diff --git a/doc/architecture.jpg b/doc/architecture.jpg deleted file mode 100644 index e85c8a5..0000000 Binary files a/doc/architecture.jpg and /dev/null differ diff --git a/doc/lifecycle.jpg b/doc/lifecycle.jpg deleted file mode 100644 index df0cc10..0000000 Binary files a/doc/lifecycle.jpg and /dev/null differ diff --git a/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/HelloServiceApp.java b/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/HelloServiceApp.java index 9379168..9c835f0 100644 --- a/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/HelloServiceApp.java +++ b/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/HelloServiceApp.java @@ -4,12 +4,15 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Import; +import io.springside.springtime.springboot.EnableSpringTime; import io.springside.springtime.springboot.SpringTimeConfiguration; +import springfox.documentation.swagger.web.InMemorySwaggerResourcesProvider; import springfox.documentation.swagger2.annotations.EnableSwagger2; @SpringBootApplication @Import(SpringTimeConfiguration.class) @EnableSwagger2 +@EnableSpringTime public class HelloServiceApp { public static void main(String[] args) throws Exception { SpringApplication.run(HelloServiceApp.class, args); diff --git a/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/idl/GreetingService.java b/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/idl/GreetingService.java index c6d114b..8c5f986 100644 --- a/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/idl/GreetingService.java +++ b/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/idl/GreetingService.java @@ -1,7 +1,12 @@ package io.springside.springtime.examples.helloservice.idl; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + +@Api("Greeting Service") public interface GreetingService { + public static class HelloRequest { public String name; } @@ -18,8 +23,10 @@ public static class WeatherResponse { public String weather; } + @ApiOperation(value = "hello") public HelloResponse hello(HelloRequest helloRequest); + @ApiOperation(value = "weather") public WeatherResponse weather(WeatherRequest weatherRequest); } diff --git a/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/service/GreetingServiceImpl.java b/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/service/GreetingServiceImpl.java index c7417dd..9b5c03f 100644 --- a/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/service/GreetingServiceImpl.java +++ b/examples/hello-service/src/main/java/io/springside/springtime/examples/helloservice/service/GreetingServiceImpl.java @@ -1,13 +1,12 @@ package io.springside.springtime.examples.helloservice.service; -import org.springframework.stereotype.Controller; - import io.springside.springtime.examples.helloservice.idl.GreetingService; +import io.springside.springtime.springboot.SpringTimeService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @Api("greeting") -@Controller("greeting") +@SpringTimeService("greeting") public class GreetingServiceImpl implements GreetingService { @Override diff --git a/springtime/pom.xml b/springtime/pom.xml index b4fad97..7767ffc 100644 --- a/springtime/pom.xml +++ b/springtime/pom.xml @@ -48,9 +48,15 @@ io.swagger - swagger-annotations + swagger-core ${swagger.version} + + io.swagger + swagger-jaxrs + ${swagger.version} + + io.springfox springfox-swagger2 diff --git a/springtime/src/main/java/io/springside/springtime/jetty/SpringTimeHandler.java b/springtime/src/main/java/io/springside/springtime/jetty/SpringTimeHandler.java index 8e4e0ab..15b3c4a 100644 --- a/springtime/src/main/java/io/springside/springtime/jetty/SpringTimeHandler.java +++ b/springtime/src/main/java/io/springside/springtime/jetty/SpringTimeHandler.java @@ -14,9 +14,9 @@ import io.springside.springtime.serializer.JsonSerializer; import io.springside.springtime.serializer.Serializer; +import io.springside.springtime.service.ServiceBeanFactory; import io.springside.springtime.service.ServiceDispatcher; -import io.springside.springtime.service.ServiceBeanRegistry; -import io.swagger.annotations.Api; +import io.springside.springtime.springboot.SpringTimeService; /** * Jetty的Handler. @@ -32,13 +32,14 @@ public class SpringTimeHandler extends AbstractHandler { private Serializer serializer = new JsonSerializer(); public SpringTimeHandler(ApplicationContext applicationContext) { - ServiceBeanRegistry serviceRegistry = new ServiceBeanRegistry(); - Map beans = applicationContext.getBeansWithAnnotation(Api.class); + ServiceBeanFactory serviceBeanFactory = new ServiceBeanFactory(); + //TODO: 检查名称重复的方法 + Map beans = applicationContext.getBeansWithAnnotation(SpringTimeService.class); for (Entry entry : beans.entrySet()) { - serviceRegistry.add(RPC_PREFIX, entry.getKey(), entry.getValue()); + serviceBeanFactory.add(RPC_PREFIX, entry.getKey(), entry.getValue()); } - dispatcher = new ServiceDispatcher(serviceRegistry); + dispatcher = new ServiceDispatcher(serviceBeanFactory); } @Override diff --git a/springtime/src/main/java/io/springside/springtime/service/ServiceBeanRegistry.java b/springtime/src/main/java/io/springside/springtime/service/ServiceBeanFactory.java similarity index 98% rename from springtime/src/main/java/io/springside/springtime/service/ServiceBeanRegistry.java rename to springtime/src/main/java/io/springside/springtime/service/ServiceBeanFactory.java index fcecb0d..4035945 100644 --- a/springtime/src/main/java/io/springside/springtime/service/ServiceBeanRegistry.java +++ b/springtime/src/main/java/io/springside/springtime/service/ServiceBeanFactory.java @@ -11,7 +11,7 @@ /** * 保存通过cglib快速执行服务方法的MethodInvoker */ -public class ServiceBeanRegistry { +public class ServiceBeanFactory { //key: path public Map methodInvokerMap = new HashMap(); diff --git a/springtime/src/main/java/io/springside/springtime/service/ServiceDispatcher.java b/springtime/src/main/java/io/springside/springtime/service/ServiceDispatcher.java index 33d2204..983ba99 100644 --- a/springtime/src/main/java/io/springside/springtime/service/ServiceDispatcher.java +++ b/springtime/src/main/java/io/springside/springtime/service/ServiceDispatcher.java @@ -5,13 +5,13 @@ import java.io.OutputStream; import io.springside.springtime.serializer.Serializer; -import io.springside.springtime.service.ServiceBeanRegistry.MethodInvoker; +import io.springside.springtime.service.ServiceBeanFactory.MethodInvoker; public class ServiceDispatcher { - private ServiceBeanRegistry registry; + private ServiceBeanFactory registry; - public ServiceDispatcher(ServiceBeanRegistry registry) { + public ServiceDispatcher(ServiceBeanFactory registry) { this.registry = registry; } diff --git a/springtime/src/main/java/io/springside/springtime/springboot/EnableSpringTime.java b/springtime/src/main/java/io/springside/springtime/springboot/EnableSpringTime.java new file mode 100644 index 0000000..2e89357 --- /dev/null +++ b/springtime/src/main/java/io/springside/springtime/springboot/EnableSpringTime.java @@ -0,0 +1,15 @@ +package io.springside.springtime.springboot; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(value = { java.lang.annotation.ElementType.TYPE }) +@Documented +@Import({SpringTimeConfiguration.class}) +public @interface EnableSpringTime { + +} diff --git a/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeConfiguration.java b/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeConfiguration.java index b205d5c..6997f23 100644 --- a/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeConfiguration.java +++ b/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeConfiguration.java @@ -1,24 +1,15 @@ package io.springside.springtime.springboot; -import static springfox.documentation.builders.PathSelectors.*; - -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; -import com.google.common.base.Predicates; - -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; +import io.springside.springtime.swagger.SpringFoxConfiguration; @ComponentScan @Configuration +@Import(SpringFoxConfiguration.class) public class SpringTimeConfiguration { - @Bean - public Docket swaggerSpringMvcPlugin() { - return new Docket(DocumentationType.SWAGGER_2).groupName("business-api").select().paths(Predicates.not(regex("/manage.*"))) // and by paths - .build(); - } } diff --git a/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeJettyCustomizer.java b/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeJettyCustomizer.java index 1e58754..5d8b883 100644 --- a/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeJettyCustomizer.java +++ b/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeJettyCustomizer.java @@ -8,6 +8,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.util.ArrayUtil; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.springframework.beans.BeansException; import org.springframework.boot.autoconfigure.web.ServerProperties; @@ -23,7 +24,7 @@ import io.springside.springtime.jetty.SpringTimeHandler; /** - * 定制化Jetty,加入Http/Http2支持,指定专门的 + * 定制化Jetty,加入Http/Http2支持,指定专门的Handler */ @Component @ConfigurationProperties(prefix = "server.jetty", ignoreUnknownFields = true) @@ -31,7 +32,9 @@ public class SpringTimeJettyCustomizer implements EmbeddedServletContainerCustom private ApplicationContext applicationContext; - private Integer maxThreads = Runtime.getRuntime().availableProcessors() * 20; + private Integer minThreads = Math.max(8, Runtime.getRuntime().availableProcessors()); + + private Integer maxThreads = Math.max(200, Runtime.getRuntime().availableProcessors() * 20); @Override public void customize(ConfigurableEmbeddedServletContainer container) { @@ -77,34 +80,31 @@ private void customizeHttp2Connector(Server server) { } private void customizeSpringTimeHanlder(Server server) { - // add SpringTimeHandler at the beginning + // add SpringTimeHandler as first handler Handler[] oldHandlers = server.getHandlers(); SpringTimeHandler springTimeHandler = new SpringTimeHandler(applicationContext); + HandlerList handlerList = new HandlerList(); - handlerList.addHandler(springTimeHandler); - for (Handler handler : oldHandlers) { - handlerList.addHandler(handler); - } + handlerList.setHandlers(ArrayUtil.prependToArray(springTimeHandler, oldHandlers, Handler.class)); + server.setHandler(handlerList); } - }); } private void customizeThreadPool(JettyEmbeddedServletContainerFactory jettyFactory) { - QueuedThreadPool threadPool = (QueuedThreadPool) jettyFactory.getThreadPool(); - if (threadPool == null) { - threadPool = new QueuedThreadPool(); - jettyFactory.setThreadPool(threadPool); - } - threadPool.setMaxThreads(maxThreads); - threadPool.setIdleTimeout(10000); + QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, minThreads, 10000); + jettyFactory.setThreadPool(threadPool); } public void setMaxThreads(Integer maxThreads) { this.maxThreads = maxThreads; } + public void setMinThreads(Integer minThreads) { + this.minThreads = minThreads; + } + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; diff --git a/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeService.java b/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeService.java new file mode 100644 index 0000000..43fe6cb --- /dev/null +++ b/springtime/src/main/java/io/springside/springtime/springboot/SpringTimeService.java @@ -0,0 +1,23 @@ +package io.springside.springtime.springboot; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.stereotype.Component; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface SpringTimeService { + /** + * The value may indicate a suggestion for a logical component name, + * to be turned into a Spring bean in case of an autodetected component. + * @return the suggested component name, if any + */ + String value() default ""; + +} diff --git a/springtime/src/main/java/io/springside/springtime/swagger/SpringFoxConfiguration.java b/springtime/src/main/java/io/springside/springtime/swagger/SpringFoxConfiguration.java new file mode 100644 index 0000000..e6b301e --- /dev/null +++ b/springtime/src/main/java/io/springside/springtime/swagger/SpringFoxConfiguration.java @@ -0,0 +1,24 @@ +package io.springside.springtime.swagger; + +import static springfox.documentation.builders.PathSelectors.*; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import com.google.common.base.Predicates; + +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +@ComponentScan +@Configuration +public class SpringFoxConfiguration { + + //TODO: manage路径从Spring里读 + @Bean + public Docket swaggerSpringMvcPlugin() { + return new Docket(DocumentationType.SWAGGER_2).groupName("restful-api").select() + .paths(Predicates.not(regex("/manage.*"))).build(); + } +} diff --git a/springtime/src/main/java/io/springside/springtime/swagger/SpringTimeSwaggerDocsController.java b/springtime/src/main/java/io/springside/springtime/swagger/SpringTimeSwaggerDocsController.java new file mode 100644 index 0000000..f1cd243 --- /dev/null +++ b/springtime/src/main/java/io/springside/springtime/swagger/SpringTimeSwaggerDocsController.java @@ -0,0 +1,62 @@ +package io.springside.springtime.swagger; + +import static org.springframework.http.MediaType.*; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import io.springside.springtime.springboot.SpringTimeService; +import io.swagger.jaxrs.Reader; +import io.swagger.jaxrs.config.ReaderConfigUtils; +import io.swagger.models.Info; +import io.swagger.models.Swagger; +import springfox.documentation.annotations.ApiIgnore; + +@RestController +@ApiIgnore +public class SpringTimeSwaggerDocsController implements InitializingBean, ApplicationContextAware { + private Swagger swagger; + + private ApplicationContext applicationContext; + + @Override + public void afterPropertiesSet() throws Exception { + swagger = new Swagger(); + Info info = new Info(); + info.setTitle("GreetingService"); + swagger.setInfo(info); + + Map beans = applicationContext.getBeansWithAnnotation(SpringTimeService.class); + Set> classes = new HashSet>(); + for (Object bean : beans.values()) { + classes.add(bean.getClass()); + } + + Reader reader = new Reader(swagger, ReaderConfigUtils.getReaderConfig(null)); + swagger = reader.read(classes); + } + + @RequestMapping(value = "/v2/rfc-api-docs", method = RequestMethod.GET, produces = { APPLICATION_JSON_VALUE }) + public @ResponseBody Swagger getDocumentation(HttpServletRequest servletRequest) { + return swagger; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + + } + +} diff --git a/springtime/src/main/java/io/springside/springtime/swagger/SpringTimeSwaggerProvider.java b/springtime/src/main/java/io/springside/springtime/swagger/SpringTimeSwaggerProvider.java new file mode 100644 index 0000000..2d63313 --- /dev/null +++ b/springtime/src/main/java/io/springside/springtime/swagger/SpringTimeSwaggerProvider.java @@ -0,0 +1,30 @@ +package io.springside.springtime.swagger; + +import java.util.List; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +import springfox.documentation.spring.web.DocumentationCache; +import springfox.documentation.swagger.web.InMemorySwaggerResourcesProvider; +import springfox.documentation.swagger.web.SwaggerResource; + +@Component +@Primary +public class SpringTimeSwaggerProvider extends InMemorySwaggerResourcesProvider { + + public SpringTimeSwaggerProvider(DocumentationCache documentationCache) { + super(documentationCache); + } + + @Override + public List get() { + List resources = super.get(); + SwaggerResource springTimeResource = new SwaggerResource(); + springTimeResource.setName("SpringTime"); + springTimeResource.setLocation("/v2/rfc-api-docs"); + resources.add(0, springTimeResource); + return resources; + } + +}