Giter Club home page Giter Club logo

esa-restlight's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

esa-restlight's Issues

Improve Serializer

Motivation

Interface spec of HttpBodySerializer

    @Override
    default boolean supportsRead(MediaType mediaType, Type type) {
        return true;
    }

    @Override
    default boolean supportsWrite(MediaType mediaType, Type type) {
        return true;
    }

    byte[] serialize(Object target) throws Exception;

    <T> T deSerialize(byte[] data, Type type) throws Exception;

Problem is that the end-user do not know how to serialize/deserialize after calling supportsRead(MediaType mediaType, Type type) because parameters of MediaType and Type is not passed in serialize() and deSerialize()

Improvement

  1. Pass MediaType and Type in serialize() and deSerialize()
    It is easy to do this, but the end-user may need to do the parameters check twice, which is not good for performance.
  2. Remove supportsRead() and supportsWrite()
    This would be good for performance but it would be hard to design the serialize() and deSerialize().

The HandlerInterceptor did not properly intercept all configured restful interfaces

Expected behavior

I use "HandlerInterceptor" in its includes method internal configuration "/v1/stat/, /v1/get/,/v1/list/**" these parameters, I hope restlight can help us intercept similar "/ v1/get/xxxxx/hello, /v1/stat/12234435" such interface

Actual behavior

It can only intercept interfaces such as "/v1/stat/12234435"

Reason for analysis

There is a problem with the implementation of "neverIntersect(includes, patterns)" in the method of judging affinity in the HandlerInterceptorWrap class parseAffinity;
for (String include : includes) {
if (Arrays.stream(patterns)
.noneMatch(pattern -> PathMatcher.isPotentialIntersect(include, pattern))) {
return true;
}
}
The above piece of code will cause the logic to exit due to the disjointness of “/v1/stat/” before judging whether the interface “/v1/get/123456” will not intersect with “/v1/get/”. Finally, the affinity is -1 misjudgment

Env

  • Restlight version: 0.1.0
  • JVM version: ALL
  • OS:ALL

Readiness support

We can configure the warming up by restlight.server.warm-up.delay, which just takes a Thread.sleep(delay) before starting the Restlight server, but could not support doing some logic before starting the Restlight server asynchronously.

make bean validation pluggable

In Restlight, We have tried our best not to introduce dependencies for reducing conflicts. But the dependency named hibernate validator introduced in restlight-core is introducing a lot of dependencies(such as jboss-logging, validation-api and so on...) which makes Restlight prone to conflict. And this bean validation feature is not required by every end-user.

So, It would be better if we make the bean validation feature pluggable by SPI, which is introduced by default and it also can be excluded if it is not required or it is conflicting with other dependencies.

When use `@ExceptionHandler`, exception occurred when start the application.

Expected behavior

work normal

Actual behavior

org.springframework.beans.factory.BeanCreationException: Failed to start Restlight server.; nested exception is java.util.concurrent.ExecutionException: java.lang.NullPointerException: underlying

	at io.esastack.restlight.starter.ServerStarter.afterSingletonsInstantiated(ServerStarter.java:99)
	at io.esastack.restlight.integration.test.BaseIntegrationTest.setUp(BaseIntegrationTest.java:52)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.util.concurrent.ExecutionException: java.lang.NullPointerException: underlying
	at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1928)
	at io.esastack.restlight.starter.ServerStarter.afterSingletonsInstantiated(ServerStarter.java:95)
	... 18 more
Caused by: java.lang.NullPointerException: underlying
	at esa.commons.Checks.checkNotNull(Checks.java:52)
	at io.esastack.restlight.core.configure.DelegatingDeployContext.<init>(DelegatingDeployContext.java:46)
	at io.esastack.restlight.core.handler.impl.HandlerContext.<init>(HandlerContext.java:41)
	at io.esastack.restlight.springmvc.resolver.exception.SpringMvcExceptionResolverFactory.lambda$extractExceptionHandlers$0(SpringMvcExceptionResolverFactory.java:120)
	at java.util.Optional.ifPresent(Optional.java:159)
	at io.esastack.restlight.springmvc.resolver.exception.SpringMvcExceptionResolverFactory.extractExceptionHandlers(SpringMvcExceptionResolverFactory.java:119)
	at io.esastack.restlight.springmvc.resolver.exception.SpringMvcExceptionResolverFactory.extractMappings(SpringMvcExceptionResolverFactory.java:109)
	at io.esastack.restlight.springmvc.resolver.exception.SpringMvcExceptionResolverFactory.createMappersFromControllerAdvice(SpringMvcExceptionResolverFactory.java:91)
	at io.esastack.restlight.core.resolver.exception.AbstractExceptionResolverFactory.initControllerAdviceToExceptionHandlerBean(AbstractExceptionResolverFactory.java:93)
	at io.esastack.restlight.core.resolver.exception.AbstractExceptionResolverFactory.init(AbstractExceptionResolverFactory.java:61)
	at io.esastack.restlight.core.resolver.exception.AbstractExceptionResolverFactory.<init>(AbstractExceptionResolverFactory.java:39)
	at io.esastack.restlight.springmvc.resolver.exception.SpringMvcExceptionResolverFactory.<init>(SpringMvcExceptionResolverFactory.java:57)
	at io.esastack.restlight.springmvc.spi.SpringMvcExceptionResolverFactoryProvider.factory(SpringMvcExceptionResolverFactoryProvider.java:38)
	at io.esastack.restlight.core.Deployments.registerRoutes(Deployments.java:1115)
	at io.esastack.restlight.core.Deployments.createRestlightHandler(Deployments.java:1619)
	at io.esastack.restlight.core.Deployments.doGetRestlightHandler(Deployments.java:1036)
	at io.esastack.restlight.core.Deployments.getRestlightHandler(Deployments.java:1611)
	at io.esastack.restlight.core.Deployments.applyDeployments(Deployments.java:1606)
	at io.esastack.restlight.core.AbstractRestlight.buildServer(AbstractRestlight.java:216)
	at io.esastack.restlight.core.AbstractRestlight.start(AbstractRestlight.java:198)
	at io.esastack.restlight.server.bootstrap.AbstractDelegatedRestlightServer.start(AbstractDelegatedRestlightServer.java:34)
	at io.esastack.restlight.starter.ServerStarter.lambda$afterSingletonsInstantiated$0(ServerStarter.java:86)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:750)

Steps to reproduce

1.add customize exceptionHandler like below.

@ControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(CustomException.class)
    public String handleCustomException(CustomException customException, HttpResponse httpResponse) {
        httpResponse.status(HttpStatus.UNAUTHORIZED.code());
        return customException.getMessage();
    }
}

2.start the application.

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:Windows 10

Follow the standard to handle charset

Motivation

In Restlight, it seems that StandardCharsets.UTF_8 is the default charset, but we should flow the HTTP standard to handle the charset such as content-type header, accept header.

And Here are some wrong uses

We should re-examine the handling of charset in this whole project

io.esastack.restlight.server.bootstrap.WebServerException: java.lang.NumberFormatException.

Expected behavior

can convert the string value to byte

Actual behavior

2022-04-14 11:25:28.657 ERROR 13796 --- [stlight-Biz-0#3] i.e.r.s.bootstrap.DispatcherHandlerImpl  : Error occurred when doing request(url=/esastack/stability/test/byte, method=POST)

io.esastack.restlight.server.bootstrap.WebServerException: java.lang.NumberFormatException: For input string: "xxxx"
	at io.esastack.restlight.server.bootstrap.WebServerException.wrap(WebServerException.java:61) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractExecution.resolveArgs(AbstractExecution.java:106) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.lambda$doHandle$0(AbstractRouteExecution.java:92) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:680) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:658) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2094) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:143) ~[na:na]
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.doHandle(AbstractRouteExecution.java:87) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.LinkedRouteFilterChain.lambda$immutable$0(LinkedRouteFilterChain.java:48) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.spi.impl.RouteTracking.routed(RouteTracking.java:55) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.LinkedRouteFilterChain.doNext(LinkedRouteFilterChain.java:60) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.handle(AbstractRouteExecution.java:78) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.bootstrap.DispatcherHandlerImpl.service(DispatcherHandlerImpl.java:82) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.ScheduledHandler.lambda$null$1(ScheduledHandler.java:83) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:883) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2251) ~[na:na]
	at io.esastack.restlight.server.schedule.ScheduledHandler.lambda$processByFixedScheduler$2(ScheduledHandler.java:74) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.RequestTaskImpl.run(RequestTaskImpl.java:45) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
	at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]
Caused by: java.lang.NumberFormatException: For input string: "xxxx"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:na]
	at java.base/java.lang.Integer.parseInt(Integer.java:652) ~[na:na]
	at java.base/java.lang.Byte.parseByte(Byte.java:152) ~[na:na]
	at java.base/java.lang.Byte.parseByte(Byte.java:178) ~[na:na]
	at io.esastack.restlight.core.util.ConverterUtils.lambda$static$1(ConverterUtils.java:69) ~[restlight-common-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.util.ConverterUtils$Strs2ArrayConverter.apply(ConverterUtils.java:279) ~[restlight-common-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.util.ConverterUtils$Str2ArrayConverter.apply(ConverterUtils.java:307) ~[restlight-common-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.util.ConverterUtils$Str2ArrayConverter.apply(ConverterUtils.java:288) ~[restlight-common-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.spi.impl.DefaultStringConverterFactory$1.fromString(DefaultStringConverterFactory.java:43) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.resolver.reqentity.FlexibleRequestEntityResolverFactory$DefaultResolver.readFrom(FlexibleRequestEntityResolverFactory.java:121) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.resolver.RequestEntityResolverContextImpl.proceed(RequestEntityResolverContextImpl.java:66) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AdvisedRequestEntityResolver.resolve(AdvisedRequestEntityResolver.java:48) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractExecution.resolveArgs(AbstractExecution.java:103) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	... 21 common frames omitted

Steps to reproduce

1.define the controller method like below

    @PostMapping("/byte")
    @ResponseBody
    public byte[] requestByte(@RequestBody byte[] bytes) {

2.request the uri use header Content-Type:text/plain

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:window 10

Filter should be applied in MockMvc

Expected behavior

@Bean
public Filter foo() {
    return new Filter() { xxx };
}
MockMvc mockMvc = MockMvcBuilders.contextSetup(context);
...

mockMvc.perform(xxx);

this foo Filter should be applied.

Actual behavior

The filter will NOT be applied

Env

  • Restlight version: 0.1.1

Exception occurred when use `@MatrixVariable`.

Expected behavior

can work normally

Actual behavior

throw Exception

Method [public io.esastack.restlight.integration.entity.UserData io.esastack.restlight.integration.cases.RestAnnotationController.matrix(org.springframework.util.MultiValueMap<java.lang.String, java.lang.String>)]
Resolved arguments: 
[0] [type=java.util.Collections$EmptyMap]

	at io.esastack.restlight.server.bootstrap.WebServerException.badRequest(WebServerException.java:81)
	at io.esastack.restlight.core.handler.impl.HandlerInvokerImpl.doInvoke(HandlerInvokerImpl.java:56)
	at io.esastack.restlight.core.handler.impl.HandlerInvokerImpl.invoke(HandlerInvokerImpl.java:46)
	at io.esastack.restlight.core.handler.impl.HandlerImpl.invoke(HandlerImpl.java:54)
	at io.esastack.restlight.core.handler.impl.AbstractExecution.invoke(AbstractExecution.java:130)
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.lambda$doHandle$1(AbstractRouteExecution.java:96)
	at java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:995)
	at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2137)
	at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:110)
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.doHandle(AbstractRouteExecution.java:96)
	at io.esastack.restlight.core.handler.LinkedRouteFilterChain.lambda$immutable$0(LinkedRouteFilterChain.java:48)
	at io.esastack.restlight.core.spi.impl.RouteTracking.routed(RouteTracking.java:55)
	at io.esastack.restlight.core.handler.LinkedRouteFilterChain.doNext(LinkedRouteFilterChain.java:60)
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.handle(AbstractRouteExecution.java:78)
	at io.esastack.restlight.server.bootstrap.DispatcherHandlerImpl.service(DispatcherHandlerImpl.java:82)
	at io.esastack.restlight.server.schedule.ScheduledHandler.lambda$null$1(ScheduledHandler.java:83)
	at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:774)
	at java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:792)
	at java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2153)
	at io.esastack.restlight.server.schedule.ScheduledHandler.lambda$processByFixedScheduler$2(ScheduledHandler.java:74)
	at io.esastack.restlight.server.schedule.RequestTaskImpl.run(RequestTaskImpl.java:45)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:750)
Caused by: java.lang.IllegalArgumentException: argument type mismatch
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at io.esastack.restlight.core.handler.impl.HandlerInvokerImpl.doInvoke(HandlerInvokerImpl.java:53)

Steps to reproduce

1.define the controller

    @GetMapping("get/matrix")
    public UserData matrix(@MatrixVariable MultiValueMap<String, String> map) {
        return UserData.Builder.aRestResult()
                .name(map.getFirst("name")).build();
    }

2.request the url: get/matrix?name=test,test2

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:Windows 10

Compose AsyncRequest and AsyncResponse

Motivation

We are always using AsyncRequest and AsyncResponse in pairs, but AsyncRequest and AsyncResponse are always separated into two parameters.

Which means

Improvement

Using a composed interface(or class) to present a REQUEST.

public interface RequestContext {
    
    AsyncRequest request();
    
    AsyncResponse response();

    // other functions
}

Advantages

  • The parameters spec would be short
  • It is easy to know that AsyncRequest and AsyncResponse is in the same lifecycle
  • It is easy to add more context for a REQUEST

Failed to start MockMvc when accesslog is enabled

Actual behavior

When I started a simple MockMvc, the application failed to start. The error describes as:

java.lang.IllegalStateException: Failed to load ApplicationContext

	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118)
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
	at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'oldAccessLogFilter' defined in class path resource [esa/restlight/ext/filter/starter/autoconfigurer/RestlightExtFilterAutoConfiguration.class]: Unsatisfied dependency expressed through method 'oldAccessLogFilter' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'esa.restlight.starter.autoconfigure.AutoRestlightServerOptions' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:509)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1288)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
	... 25 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'esa.restlight.starter.autoconfigure.AutoRestlightServerOptions' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1651)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1210)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1164)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
	... 43 more

and the application.properties configured as:

restlight.server.port=9997
restlight.server.ext.accesslog.enable=true

Env

  • Restlight version: 0.1.1
  • JVM version: openjdk version "1.8.0_252"
  • OS: Win10

test by yangtao

esa-restlight/LICENSE

Lines 21 to 24 in ce8fe48

outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.

Protostuff support is wanted

As we all know, protostuff is also an excellent serialization format and
has been used widely. So why not considering to support it ?

Config improvement

Motivation

Now we are using biz threads configuration like

restlight.server.blocking-queue-length=128
restlight.server.keep-alive-time-seconds=60

But it is hard to understand what restlight.server.keep-alive-time-seconds and restlight.server.blocking-queue-length means.
Same with restlight.server.core-biz-threads and restlight.server.max-biz-threads that are easier to understand than above because of the suffix of biz-threads.

Improvement

It would be clear if we change the configuration to this

restlight.server.biz-threads.core=1
restlight.server.biz-threads.max=2
restlight.server.biz-threads.blocking-queue-length=128
restlight.server.biz-threads.keep-alive-time-seconds=60

`@QueryBean` and `@RequestBean` cann't assign the value to pojo sometime.

Expected behavior

can assign the value to the pojo.

Actual behavior

sometime can, sometime can not.

Steps to reproduce

1.define the controller like below.

    @GetMapping("get/querybean")
    public UserData queryBean(@QueryBean UserData user) {
        return user;
    }

    @GetMapping("get/requestbean")
    public UserData requestBean(@RequestBean UserData user) {
        return user;
    }

 pojo like below.
public class UserData {

    private String name;

    private Integer age;

    private BigDecimal weight;

    public BigDecimal getWeight() {
        return weight;
    }

    private Date birthDay;

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public Date getBirthDay() {
        return birthDay;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setWeight(BigDecimal weight) {
        this.weight = weight;
    }

    public void setBirthDay(Date birthDay) {
        this.birthDay = birthDay;
    }

    @Override
    public String toString() {
        return "UserData{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", weight=" + weight +
                ", birthDay=" + birthDay +
                '}';
    }

    public static final class Builder {
        private String name;
        private Integer age;
        private BigDecimal weight;
        private Date birthDay;

        private Builder() {
        }

        public static Builder aRestResult() {
            return new Builder();
        }

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder age(Integer age) {
            this.age = age;
            return this;
        }

        public Builder weight(BigDecimal weight) {
            this.weight = weight;
            return this;
        }

        public Builder birthDay(Date birthDay) {
            this.birthDay = birthDay;
            return this;
        }

        public UserData build() {
            UserData userData = new UserData();
            userData.name = this.name;
            userData.weight = this.weight;
            userData.birthDay = this.birthDay;
            userData.age = this.age;
            return userData;
        }
    }
}

2.request the url with param name=test.

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:Windows 10

When I use `@NotEmpty` to mark the `@QueryParam`, the request timeout.

Expected behavior

request normal.

Actual behavior

client timeout.

java.util.concurrent.ExecutionException: java.net.SocketTimeoutException: Request: http://127.0.0.1:53867/integration/test/validation/param exceeds read timeout: 6000ms

	at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
	at io.esastack.restlight.integration.jaxrs.test.ValidationTest.testParam(ValidationTest.java:31)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.net.SocketTimeoutException: Request: http://127.0.0.1:53867/integration/test/validation/param exceeds read timeout: 6000ms
	at io.esastack.httpclient.core.netty.ReadTimeoutTask.lambda$run$76(ReadTimeoutTask.java:49)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:750)

Steps to reproduce

1.define the resource like below.

@Controller
@Path("/validation/")
public class ValidationResource {

    @GET
    @Path("param")
    public UserData param(@NotEmpty @QueryParam("name") String name) {
        return UserData.Builder.anUserData()
                .name(name).build();
    }
}

2.request it like below.

RestResponseBase responseBase = restClient.get(domain + "/validation/param").execute().toCompletableFuture().get();
Assert.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.code(), responseBase.status());

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS: windows 10

Stop using origin netty interfaces directly

We are using the netty interfaces(or classes) in interfaces that would be used by the end-user such as io.netty.buffer.ByteBuf, io.netty.handler.codec.http.HttpHeaders, io.netty.handler.codec.http.cookie.Cookie and so on...
And this time we want to replace these interfaces(or classes) with our own interfaces(or classes) such as esa.commons.netty.core.Buffer, esa.commons.http.HttpHeaders, esa.commons.http.Cookie and so on...

Try to prevent the end-user from using origin netty interfaces directly, and let end-user use our own interfaces(or classes).

Graceful shutdown with spring

Expected behavior

Restlight should be shut down before closing Spring context.

Actual behavior

Restlight does the graceful shutdown by adding an individual shutdown hook, which has a different lifecycle from Spring's shutdown hook. This would cause errors when invoking the controller methods because the beans these methods belong to would have been destroyed in Spring's shutdown hook if Spring's shutdown hook is executed before Restlight's shutdown hook.

Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown, "shutdown-hook-thread"));

Maybe we should bind the shutdown hook lifecycle with Spring context(if it is in Spring env).

Steps to reproduce

see above

Env

  • Restlight version: 0.1.0

Invalid @RestControllerAdvice

Hi, maybe a bug have been found out. The specific scene is:
when a customize aop arounds the bean which is
annotated with @RestControllerAdvice and then it's
internal methods which are expected to handle the
exception will make no effect.

Env

  • Restlight version: 0.0.1-SNAPSHOT
  • JVM version: openjdk version "1.8.0_252"
  • OS: Window10

Failed to start the server when there are ext-filter-starter and starter-actuator at the same time

Actual behavior

When I added restlight-ext-filter-starter and restlight-starter-actuator dependencies at the same time, it's failed
to start the server successfully. The error describes as:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method oldAccessLogFilter in esa.restlight.ext.filter.starter.autoconfigurer.RestlightExtFilterAutoConfiguration required a single bean, but 2 were found:
	- restlight.server-esa.restlight.starter.autoconfigure.AutoRestlightServerOptions: defined in null
	- management.server.restlight-esa.restlight.starter.actuator.autoconfigurer.ManagementOptions: defined in null

Env

  • Restlight version: 0.1.0
  • JVM version: openjdk version "1.8.0_252"
  • OS: Win10

JacksonSerializer's date format pattern is not `yyyy-MM-dd HH:mm:ss`

Expected behavior

support the date format pattern yyyy-MM-dd HH:mm:ss as default.

Actual behavior

throw exception when I input date time like yyyy-MM-dd HH:mm:ss.

Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2022-04-11 19:33:00": not a valid representation (error: Failed to parse Date value '2022-04-11 19:33:00': Cannot parse date "2022-04-11 19:33:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null))
 at [Source: (byte[])"{
  "id": 1,
  "userName": "张三",
  "age": 20,
  "sex": "M",
  "time": 1649676789851,
  "amount": 100.332,
  "weight": 75,
  "createTime": "2022-04-11 19:33:00"
}"; line: 9, column: 17] (through reference chain: io.esastack.stability.test.controller.StabilityController$Pojo["createTime"])
	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1676) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:932) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseDate(StdDeserializer.java:550) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseDate(StdDeserializer.java:491) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateBasedDeserializer._parseDate(DateDeserializers.java:195) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:285) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:268) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3300) ~[jackson-databind-2.10.1.jar:2.10.1]
	at io.esastack.restlight.core.serialize.JacksonSerializer.deserialize(JacksonSerializer.java:54) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.serialize.BaseHttpBodySerializer.doDeserialize(BaseHttpBodySerializer.java:54) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.serialize.BaseHttpBodySerializer.deserialize(BaseHttpBodySerializer.java:34) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.resolver.reqentity.FlexibleRequestEntityResolverFactory$DefaultResolver.readFrom(FlexibleRequestEntityResolverFactory.java:127) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.resolver.RequestEntityResolverContextImpl.proceed(RequestEntityResolverContextImpl.java:66) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AdvisedRequestEntityResolver.resolve(AdvisedRequestEntityResolver.java:48) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractExecution.resolveArgs(AbstractExecution.java:103) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	... 21 common frames omitted

Steps to reproduce

1.add a date property in pojo.
2.request the url use date pattern yyyy-MM-dd HH:mm:ss .

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:windows 10

The CI and unit test can not pass now.

Expected behavior

CI and unit test can pass.

Actual behavior

The CI and unit test can not pass now.

Steps to reproduce

see the workflow in PR.

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:Windows 10

Customize `ReaderInterceptor` does not work.

Expected behavior

can work

Actual behavior

don't work.

Steps to reproduce

1.define the customize ReaderInterceptor.

@Provider
@Component
public class RequestInterceptor implements ReaderInterceptor {

    @Override
    public Object aroundReadFrom(ReaderInterceptorContext context) throws WebApplicationException, IOException {
        System.out.println(context.getType());
        return context.proceed();
    }
}

2.request the rest api.

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS: Windows 10

Considering improving the PathMatcher

make the PathMatcher performance up

why

Now, We are using regex to match the path variables of the URI. eg. /foo/{bar}/baz

Maybe we should stop using regex and try to resolve the path variables directly for a nice performance.

boolean matchStrings(String str, Map<String, String> uriTemplateVariables) {
java.util.regex.Matcher matcher = this.pattern.matcher(str);
if (matcher.matches()) {
if (uriTemplateVariables != null) {
// SPR-8455
if (this.variableNames.size() != matcher.groupCount()) {
throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
this.pattern + " does not match the number of URI template variables it defines, " +
"which can occur if capturing groups are used in a URI template regex. " +
"Use non-capturing groups instead.");
}
for (int i = 1; i <= matcher.groupCount(); i++) {
String name = this.variableNames.get(i - 1);
String value = matcher.group(i);
uriTemplateVariables.put(name, value);
}
}
return true;
} else {
return false;
}
}

There is no suitable resolver to handle param: [MethodParam: io.esastack.restlight.starter.actuator.adapt.OperationHandler=>handle@body]

Expected behavior

application start successfully.

Actual behavior

org.springframework.beans.factory.BeanCreationException: Failed to start Restlight server.; nested exception is java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: There is no suitable resolver to handle param: [MethodParam: io.esastack.restlight.starter.actuator.adapt.OperationHandler=>handle@body]
	at io.esastack.restlight.starter.ServerStarter.afterSingletonsInstantiated(ServerStarter.java:99) ~[restlight-starter-1.0.0-SNAPSHOT.jar:na]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:863) ~[spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863) ~[spring-context-5.1.3.RELEASE.jar:5.1.3.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546) ~[spring-context-5.1.3.RELEASE.jar:5.1.3.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE]
	at io.esastack.demo.DemoApplication.main(DemoApplication.java:10) ~[classes/:na]
Caused by: java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: There is no suitable resolver to handle param: [MethodParam: io.esastack.restlight.starter.actuator.adapt.OperationHandler=>handle@body]
	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2022) ~[na:na]
	at io.esastack.restlight.starter.ServerStarter.afterSingletonsInstantiated(ServerStarter.java:95) ~[restlight-starter-1.0.0-SNAPSHOT.jar:na]
	... 9 common frames omitted
Caused by: java.lang.IllegalArgumentException: There is no suitable resolver to handle param: [MethodParam: io.esastack.restlight.starter.actuator.adapt.OperationHandler=>handle@body]
	at io.esastack.restlight.core.handler.impl.HandlerMethodAdapter.getResolverWrap(HandlerMethodAdapter.java:123) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.HandlerMethodAdapter.buildParamResolvers(HandlerMethodAdapter.java:103) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.HandlerMethodAdapter.<init>(HandlerMethodAdapter.java:54) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.RouteHandlerMethodAdapter.<init>(RouteHandlerMethodAdapter.java:61) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.SingletonRouteMethod.<init>(SingletonRouteMethod.java:43) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.util.RouteUtils.extractRoute(RouteUtils.java:178) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.configure.HandlerRegistryImpl.lambda$addHandlerMappings$7(HandlerRegistryImpl.java:202) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
	at io.esastack.restlight.core.configure.HandlerRegistryImpl.addHandlerMappings(HandlerRegistryImpl.java:202) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.Deployments.lambda$registerHandlers$11(Deployments.java:1156) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
	at io.esastack.restlight.core.Deployments.registerHandlers(Deployments.java:1153) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.Deployments.registerRoutes(Deployments.java:1128) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.Deployments.createRestlightHandler(Deployments.java:1594) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.Deployments.doGetRestlightHandler(Deployments.java:1033) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.Deployments.getRestlightHandler(Deployments.java:1586) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.Deployments.applyDeployments(Deployments.java:1581) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.AbstractRestlight.buildServer(AbstractRestlight.java:217) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.AbstractRestlight.start(AbstractRestlight.java:199) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.bootstrap.AbstractDelegatedRestlightServer.start(AbstractDelegatedRestlightServer.java:34) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.starter.ServerStarter.lambda$afterSingletonsInstantiated$0(ServerStarter.java:86) ~[restlight-starter-1.0.0-SNAPSHOT.jar:na]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

Steps to reproduce

1.import restlight-starter-actuator

        <dependency>
            <groupId>io.esastack</groupId>
            <artifactId>restlight-starter-actuator</artifactId>
            <version>${restlight.version}</version>
        </dependency>

2.start application.

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 11
  • OS:windows 10

Can we support not provide the annotation like @RequestParam in controller.

Expected behavior

not contains the @RequestParam, the application still start normally.

Actual behavior

throw exception

Caused by: java.lang.IllegalArgumentException: There is no suitable resolver to handle param: [MethodParam: io.esastack.demo.controller.HelloController=>hello@name]

Steps to reproduce

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:windows 10

customize `ExceptionResolver` can't work.

Expected behavior

can resolve the exception.

Actual behavior

does not work.

Steps to reproduce

  1. add customize ExceptionResolver .
@Component
public class GlobalExceptionResolver implements ExceptionResolver<RuntimeException> {
    @Override
    public CompletionStage<Void> handleException(RequestContext context, RuntimeException exception) {
        context.response().status(HttpStatus.FORBIDDEN.code());
        context.response().entity(exception.getMessage());
        return CompletableFuture.completedFuture(null);
    }
}

2.add exception controller

    @GetMapping("get/exception")
    public void exception() {
        throw new RuntimeException("Forbidden");
    }

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:Windows 10

Add reference doc

We need more documents to tell users how to use ESA Httpclient

Maybe README or Wiki

Support annotations on the interface

Expect

@Path("/hello")
public interface HelloService {

    @Path("/echo")
    String echo(@QueryParam(value = "name") String name);

}

@Service
public class HelloServiceImpl implements HelloService {

    @Override
    public String echo(String name) {
        return name;
    }

}

As we show above, if we visit http://127.0.0.1:8080/hello/service?name=LiMing
then we can get 200 response code successfully.

Actual (Now)

The 404 will be returned.

Customize `MessageBodyReader` and `MessageBodyWriter` does not work.

Expected behavior

can work

Actual behavior

doesn't work.

Steps to reproduce

1.customize MessageBodyReader and MessageBodyWriter like below:

@Provider
@Component
public class BodyReader implements MessageBodyReader<MessageBodyData> {

    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public MessageBodyData readFrom(Class<MessageBodyData> type, Type genericType, Annotation[] annotations,
                                    MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
                                    InputStream entityStream) throws WebApplicationException {
        return MessageBodyData.Builder.aBodyMessageData().name("test").build();
    }
}
@Provider
@Component
public class BodyWriter implements MessageBodyWriter<MessageBodyData> {

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public void writeTo(MessageBodyData messageBodyData, Class<?> type, Type genericType, Annotation[] annotations,
                        MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
                        OutputStream entityStream) throws WebApplicationException, IOException {
        httpHeaders.add("Content-Type", mediaType.getType() + "/" + mediaType.getSubtype());
        if (messageBodyData == null) {
            entityStream.write("{\"name\": \"test\"}".getBytes(StandardCharsets.UTF_8));
        } else {
            entityStream.write(new JacksonSerializer().serialize(messageBodyData));
        }
    }
}

2.request the request api in MessageBodyResource.

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS: Windows 10

When I use pojo as the RequestParam, NPE will be thrown.

Expected behavior

work normal.

Actual behavior

throw NPE

io.esastack.restlight.server.bootstrap.WebServerException: java.lang.NullPointerException
	at io.esastack.restlight.server.bootstrap.WebServerException.wrap(WebServerException.java:61) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractExecution.resolveArgs(AbstractExecution.java:106) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.lambda$doHandle$0(AbstractRouteExecution.java:92) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:680) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:658) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2094) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:143) ~[na:na]
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.doHandle(AbstractRouteExecution.java:87) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.LinkedRouteFilterChain.lambda$immutable$0(LinkedRouteFilterChain.java:48) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.spi.impl.RouteTracking.routed(RouteTracking.java:55) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.LinkedRouteFilterChain.doNext(LinkedRouteFilterChain.java:60) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractRouteExecution.handle(AbstractRouteExecution.java:78) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.bootstrap.DispatcherHandlerImpl.service(DispatcherHandlerImpl.java:82) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.ScheduledHandler.lambda$null$1(ScheduledHandler.java:83) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:883) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2251) ~[na:na]
	at io.esastack.restlight.server.schedule.ScheduledHandler.lambda$processByFixedScheduler$2(ScheduledHandler.java:74) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.RequestTaskImpl.run(RequestTaskImpl.java:45) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at esa.commons.concurrent.DirectExecutor.execute(DirectExecutor.java:32) ~[commons-0.2.1-20220331.024855-1.jar:na]
	at io.esastack.restlight.server.schedule.ExecutorSchedulerImpl.schedule(ExecutorSchedulerImpl.java:38) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.ScheduledHandler.processByFixedScheduler(ScheduledHandler.java:88) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.ScheduledHandler.lambda$new$0(ScheduledHandler.java:48) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.ScheduledHandler.process(ScheduledHandler.java:64) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.ScheduledRestlightHandler.process(ScheduledRestlightHandler.java:116) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.ScheduledRestlightHandler.process(ScheduledRestlightHandler.java:45) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.schedule.AbstractRestlightHandler.process(AbstractRestlightHandler.java:46) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.RestlightHandlerImpl.process(RestlightHandlerImpl.java:75) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.server.bootstrap.NettyRestlightServer.lambda$null$5(NettyRestlightServer.java:254) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.httpserver.impl.BaseRequestHandle.handleEnd(BaseRequestHandle.java:272) ~[httpserver-0.1.1-20211213.125310-5.jar:na]
	at io.esastack.httpserver.impl.Http1Handler.end(Http1Handler.java:318) ~[httpserver-0.1.1-20211213.125310-5.jar:na]
	at io.esastack.httpserver.impl.Http1Handler.channelRead0(Http1Handler.java:149) ~[httpserver-0.1.1-20211213.125310-5.jar:na]
	at io.esastack.httpserver.impl.Http1Handler.channelRead0(Http1Handler.java:57) ~[httpserver-0.1.1-20211213.125310-5.jar:na]
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) ~[netty-codec-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) ~[netty-codec-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286) ~[netty-handler-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) ~[netty-codec-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) ~[netty-codec-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Caused by: java.lang.NullPointerException: null
	at io.esastack.restlight.core.spi.impl.DefaultStringConverterFactory$1.fromString(DefaultStringConverterFactory.java:43) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.resolver.nav.NameAndStringsValueResolver.resolve(NameAndStringsValueResolver.java:74) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.resolver.nav.NameAndValueResolverAdapter.resolve(NameAndValueResolverAdapter.java:42) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AdvisedParamResolver.resolve(AdvisedParamResolver.java:41) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	at io.esastack.restlight.core.handler.impl.AbstractExecution.resolveArgs(AbstractExecution.java:103) ~[restlight-core-1.0.0-SNAPSHOT.jar:na]
	... 65 common frames omitted

Steps to reproduce

1.use the @RequestParam to declare a parameter.
2.request the uri.

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: 1.8
  • OS:windows 10

Stop logging bizExceptions which will be handled by ExceptionHandler

Expected behavior

The exception shouldn't be logged when the custom ExceptionHandler
can be used to handle it normally.

Actual behavior

Even if the exception is handled by ExceptionHandler normally, it will also
be logged again.

Steps to reproduce

See more information from AbstractHandlerExecution#invoke().

Env

  • Restlight version: v0.1.1
  • JVM version: openjdk version "11.0.11"
  • OS: win10

Support Fail Fast

We expect that the request which has queued long time should
be discarded before real executing.

More specifically, we can compute the time from decode request
to executed by BizThredPool, if it's greater than the specified timeout
param, the request shoud be terminated immediately. If so, we can
avoid wasting resources to process requests that have expired.

Considering removing AsyncRequest#inputStream() and AsyncResponse#outputStream()

Motivation

AsyncRequest and AsyncResponse provide methods:

  1. AsyncRequest#inputStream() is holding the aggregated body which has been received completely, It doesn't make much sense to do this.
  2. It is hard to be compatible with the InputStream that will block until input data is available, which it may block on the IO EventloopGroup
  3. AsyncResponse#outputStream() is writing bytes asynchronously, which means it is allowed to write bytes very frequently without any check of channel's writability and ByteBufAllocator's allocatability, which may cause an OOM error.
    private void flush(boolean isLast) {
    if (byteBuf.readableBytes() == 0) {
    if (isLast) {
    byteBuf.release();
    }
    return;
    }
    if (isLast) {
    resp.write(byteBuf);
    } else {
    final ByteBuf copy = byteBuf.copy();
    try {
    resp.write(copy);
    } catch (Exception e) {
    copy.release();
    ExceptionUtils.throwException(e);
    } finally {
    byteBuf.clear();
    }
    }
    }
  4. It is hard to be compatible with the OutputStream that will block until output data is writable, which it may block on the IO EventloopGroup
  5. Reactive programing is exactly what we want

What should we do

  1. Remove AsyncRequest#inputStream() and AsyncResponse#outputStream() methods
  2. Supporting reading/writing data reactively

mediaType matching order

Expected behavior

when accpet is blank or /, return first match support media type serializer

Actual behavior

return the last

Steps to reproduce

image
no Accept
excepted json format but protostuff

Env

  • Restlight version: 0.1.1

The response body param wrong

Expected behavior

{"useName": null, "iName": "xxx"}

Actual behavior

image

Steps to reproduce

use entity

    class Hello {
        String iName;

        String userName;
    }

Env

  • Restlight version: 1.0.0-SNAPSHOT
  • JVM version: jdk11
  • OS:window 10

Binding array or list parameters by @RequestParam

Expected behavior

@GetMapping("/foo")
public void test(@RequestParam String[] bar) {
}

the bar could be bound as String[] {"a", "b", "c"} in request whoes uri is /foo?bar=a&bar=b&bar=c

Actual behavior

the bar is bound as String[] {"a"}

Env

  • Restlight version: 0.1.0-SNAPSHOT
  • JVM version: 1.8
  • OS: windows

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.