io.github.factoryfx.jetty.builder.JettyServerBuilder Maven / Gradle / Ivy
package io.github.factoryfx.jetty.builder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.glassfish.jersey.logging.LoggingFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.factoryfx.factory.FactoryBase;
import io.github.factoryfx.factory.builder.FactoryContext;
import io.github.factoryfx.factory.builder.FactoryTemplateId;
import io.github.factoryfx.factory.builder.FactoryTreeBuilder;
import io.github.factoryfx.factory.builder.NestedBuilder;
import io.github.factoryfx.factory.builder.Scope;
import io.github.factoryfx.jetty.GzipHandlerFactory;
import io.github.factoryfx.jetty.HandlerCollectionFactory;
import io.github.factoryfx.jetty.HttpConfigurationFactory;
import io.github.factoryfx.jetty.HttpServerConnectorFactory;
import io.github.factoryfx.jetty.JettyServerFactory;
import io.github.factoryfx.jetty.ServletAndPathFactory;
import io.github.factoryfx.jetty.ServletContextHandlerFactory;
import io.github.factoryfx.jetty.ServletFilterAndPathFactory;
import io.github.factoryfx.jetty.Slf4jRequestLogFactory;
import io.github.factoryfx.jetty.ThreadPoolFactory;
import io.github.factoryfx.jetty.UpdateableServletFactory;
import jakarta.servlet.Filter;
import jakarta.servlet.Servlet;
import jakarta.ws.rs.ext.ExceptionMapper;
/**
* The builder builds the factory structure for a jetty server not the jetty liveobject
* The factory structure matches the jetty internal architecture and the JettyServerBuilder creates a default configuration for that.
*
* {@code jetty builder==>jetty factories=>real jetty server}
*
* @see https://dzone.com/refcardz/jetty?chapter=2
* @param server root
*/
public class JettyServerBuilder, JR extends JettyServerFactory> implements NestedBuilder {
private final List> additionalServletBuilders = new ArrayList<>();
private final List> servletFilterBuilders = new ArrayList<>();
private FactoryBase firstHandler;
private int threadPoolSize=200;
private boolean enabledRequestLog=true;
private final FactoryTemplateId rootTemplateId;
private ServerConnectorBuilder defaultServerConnector;
private final List> serverConnectorBuilders =new ArrayList<>();
private ResourceBuilder resourceBuilder;
private final List> resourceBuilders =new ArrayList<>();
private Consumer additionalConfiguration = jr-> {};
private final FactoryTemplateId> threadPoolFactoryTemplateId;
private final FactoryTemplateId> requestLogerTemplateId;
private final FactoryTemplateId> handlerCollectionFactoryTemplateId;
private final FactoryTemplateId> gzipHandlerFactoryTemplateId;
private final FactoryTemplateId> servletContextHandlerFactoryTemplateId;
private final FactoryTemplateId> updateableServletFactoryTemplateId;
private final FactoryTemplateId> defaultServerConnectorTemplateId;
private Consumer> gzipHandlerCustomizer=(gzipHandler)->{};
private final Supplier jettyRootCreator;
public JettyServerBuilder(FactoryTemplateId rootTemplateId, Supplier jettyRootCreator){
this.rootTemplateId=rootTemplateId;
this.jettyRootCreator=jettyRootCreator;
this.threadPoolFactoryTemplateId=new FactoryTemplateId<>(rootTemplateId.name, ThreadPoolFactory.class);
this.requestLogerTemplateId=new FactoryTemplateId<>(rootTemplateId.name, Slf4jRequestLogFactory.class);
this.handlerCollectionFactoryTemplateId=new FactoryTemplateId<>(rootTemplateId.name, HandlerCollectionFactory.class);
this.gzipHandlerFactoryTemplateId=new FactoryTemplateId<>(rootTemplateId.name, GzipHandlerFactory.class);
this.servletContextHandlerFactoryTemplateId=new FactoryTemplateId<>(rootTemplateId.name, ServletContextHandlerFactory.class);
this.updateableServletFactoryTemplateId=new FactoryTemplateId<>(rootTemplateId.name, UpdateableServletFactory.class);
this.defaultServerConnectorTemplateId=new FactoryTemplateId<>(rootTemplateId.name, HttpServerConnectorFactory.class);
}
/**
* add a jetty connector, useful for jetty on multiple ports or support both http and ssl
*
* @param connectorBuilderSetup connectorBuilderSetup e.g. {@code withAdditionalConnector((connector)->connector.withPort(8080),"con1")}
* @param name builder unique name, used in the builder to associate match the templates
* @return builder
*/
public JettyServerBuilder withAdditionalConnector(Consumer> connectorBuilderSetup, FactoryTemplateName name){
ServerConnectorBuilder resourceBuilder = new ServerConnectorBuilder<>(new FactoryTemplateId<>(this.rootTemplateId.name+name.name, ServletAndPathFactory.class));
connectorBuilderSetup.accept(resourceBuilder);
serverConnectorBuilders.add(resourceBuilder);
return this;
}
/**
* add a jetty connector, useful for jetty on multiple ports or support both http and ssl
*
* @param connectorBuilderSetup connectorBuilderSetup e.g. {@code withAdditionalConnector((connector)->connector.withPort(8080),"con1")}
* @param name builder unique name, used in the builder to associate match the templates
* @return builder
*/
public JettyServerBuilder setAdditionalConnector(Consumer> connectorBuilderSetup, FactoryTemplateName name){
serverConnectorBuilders.clear();
ServerConnectorBuilder resourceBuilder = new ServerConnectorBuilder<>(new FactoryTemplateId<>(this.rootTemplateId.name+name.name, ServletAndPathFactory.class));
connectorBuilderSetup.accept(resourceBuilder);
serverConnectorBuilders.add(resourceBuilder);
return this;
}
/**
* add a new jersey servlet with REST Resources
* this can be used to support different ObjectMappers/ExceptionHandler
*
* @param resourceBuilderSetup resourceBuilderSetup
* @param name builder unique name, used in the builder to associate match the templates
* @return builder
*/
public JettyServerBuilder withJersey(Consumer> resourceBuilderSetup, FactoryTemplateName name){
ResourceBuilder resourceBuilder = new ResourceBuilder<>(new FactoryTemplateId<>(this.rootTemplateId.name+name.name, ServletAndPathFactory.class));
resourceBuilderSetup.accept(resourceBuilder);
addResourceBuilder(resourceBuilder);
return this;
}
/**
* adds a servlet
* @param servlet servlet
* @param pathSpec pathSpec
* @param name builder unique name, used in the builder to associate match the templates
* @return builder
*/
public JettyServerBuilder withServlet(FactoryBase extends Servlet, R> servlet, String pathSpec, FactoryTemplateName name){
additionalServletBuilders.add(new ServletBuilder<>(new FactoryTemplateId<>(null,name.name),pathSpec,servlet));
return this;
}
/**
* adds a servlet
* @param templateId templateId for the servlet
* @param pathSpec pathSpec
* @param servlet servlet
* @return builder
*/
public JettyServerBuilder withServlet(FactoryTemplateId> templateId, String pathSpec, FactoryBase extends Servlet, R> servlet){
additionalServletBuilders.add(new ServletBuilder<>(templateId,pathSpec,servlet));
return this;
}
/**
* adds a servlet filter
* @param filter filter
* @param pathSpec pathSpec
* @param name builder unique name, used in the builder to associate match the templates
* @return builder
*/
public JettyServerBuilder withServletFilter(FactoryBase extends Filter, R> filter, String pathSpec, FactoryTemplateName name){
servletFilterBuilders.add(new ServletFilterBuilder<>(new FactoryTemplateId<>(null,name.name),pathSpec,filter));
return this;
}
/**
* adds a servlet filter
* @param templateId templateId for the filter
* @param pathSpec pathSpec
* @param filter filter
* @return builder
*/
public JettyServerBuilder withServletFilter(FactoryTemplateId> templateId, String pathSpec, FactoryBase extends Filter, R> filter){
servletFilterBuilders.add(new ServletFilterBuilder<>(templateId,pathSpec,filter));
return this;
}
/**
* default is 200
* @param size jetty thread pool size
* @return builder
*/
public JettyServerBuilder withThreadPoolSize(int size){
this.threadPoolSize=size;
return this;
}
/**
* disable the jetty request log
* @return builder
*/
public JettyServerBuilder withDisabledRequestLog(){
this.enabledRequestLog=false;
return this;
}
/**
* adds a handler ad the first HandlerCollection position (with means handler is executed before the default)
* @param firstHandler creator
* @return builder
*/
public JettyServerBuilder withHandlerFirst(FactoryBase firstHandler) {
this.firstHandler=firstHandler;
return this;
}
/**
* customize the gzipHandler setup
* @param gzipHandlerCustomizer customizer consumer
* @return builder
*/
public JettyServerBuilder withGzipHandlerCustomizer(Consumer> gzipHandlerCustomizer) {
this.gzipHandlerCustomizer = gzipHandlerCustomizer;
return this;
}
/**
* customize the jetty server
* @param config customizer consumer
* @return builder
*/
public JettyServerBuilder withSpecificConfiguration(Consumer config) {
this.additionalConfiguration = config;
return this;
}
private > void addFactory(FactoryTreeBuilder,R> builder, FactoryTemplateId templateId, Scope scope, Function, F> creator){
builder.removeFactory(templateId);
builder.addFactory(templateId,scope,creator);
}
/**
* internal method
* @param builder tree builder to add
*/
@Override
public void internal_build(FactoryTreeBuilder,R> builder){
addFactory(builder,rootTemplateId, Scope.SINGLETON, (FactoryContext ctx) ->{
JR jettyServerFactory=jettyRootCreator.get();
additionalConfiguration.accept(jettyServerFactory);
jettyServerFactory.threadPool.set(ctx.get(threadPoolFactoryTemplateId));
if (enabledRequestLog) {
jettyServerFactory.requestLog.set(ctx.get(requestLogerTemplateId));
}
for (ServerConnectorBuilder connectorBuilder : serverConnectorBuilders) {
jettyServerFactory.connectors.add( ctx.get(connectorBuilder.getTemplateId()));
}
jettyServerFactory.handler.set(ctx.get(handlerCollectionFactoryTemplateId));
return jettyServerFactory;
});
if (enabledRequestLog) {
addFactory(builder,requestLogerTemplateId, Scope.SINGLETON, (ctx) -> {
return new Slf4jRequestLogFactory<>();
});
}
addFactory(builder,threadPoolFactoryTemplateId, Scope.SINGLETON, (ctx)->{
ThreadPoolFactory threadPoolFactory=new ThreadPoolFactory<>();
threadPoolFactory.poolSize.set(threadPoolSize);
return threadPoolFactory;
});
for (ServerConnectorBuilder connectorBuilder : serverConnectorBuilders) {
connectorBuilder.build(builder);
}
addFactory(builder,handlerCollectionFactoryTemplateId, Scope.SINGLETON, (ctx)->{
HandlerCollectionFactory handlerCollection = new HandlerCollectionFactory<>();
if (firstHandler!=null) {
handlerCollection.handlers.add(firstHandler);
}
handlerCollection.handlers.add(ctx.get(gzipHandlerFactoryTemplateId));
return handlerCollection;
});
addFactory(builder,gzipHandlerFactoryTemplateId, Scope.SINGLETON, (ctx)->{
GzipHandlerFactory gzipHandler = new GzipHandlerFactory<>();
gzipHandler.minGzipSize.set(23);
gzipHandler.inflateBufferSize.set(-1);
gzipHandler.syncFlush.set(false);
gzipHandler.handler.set(ctx.get(servletContextHandlerFactoryTemplateId));
gzipHandlerCustomizer.accept(gzipHandler);
return gzipHandler;
});
addFactory(builder,servletContextHandlerFactoryTemplateId, Scope.SINGLETON, (ctx)->{
ServletContextHandlerFactory servletContextHandlerFactory = new ServletContextHandlerFactory<>();
servletContextHandlerFactory.updatableRootServlet.set(ctx.get(updateableServletFactoryTemplateId));
for (ServletFilterBuilder servletFilterBuilder : servletFilterBuilders) {
servletContextHandlerFactory.servletFilters.add(ctx.get(servletFilterBuilder.getTemplateId()));
}
return servletContextHandlerFactory;
});
addFactory(builder,updateableServletFactoryTemplateId, Scope.SINGLETON, (ctx)->{
UpdateableServletFactory updateableServletFactory = new UpdateableServletFactory<>();
for (ResourceBuilder resourceBuilder : resourceBuilders) {
updateableServletFactory.servletAndPaths.add(ctx.get(resourceBuilder.getServletAndPathFactoryTemplateId()));
}
for (ServletBuilder servletBuilder : additionalServletBuilders) {
updateableServletFactory.servletAndPaths.add(ctx.get(servletBuilder.getTemplateId()));
}
return updateableServletFactory;
});
for (ResourceBuilder resourceBuilder : resourceBuilders) {
resourceBuilder.build(builder);
}
for (ServletBuilder servletBuilder : additionalServletBuilders) {
servletBuilder.build(builder);
}
for (ServletFilterBuilder filterBuilder : servletFilterBuilders) {
filterBuilder.build(builder);
}
}
private void addResourceBuilder(ResourceBuilder resourceBuilder){
for (ResourceBuilder builder : resourceBuilders) {
if (builder.match(resourceBuilder)){
throw new IllegalStateException("can't add multiple jersey servlets with the same patchSpec");
}
}
resourceBuilders.add(resourceBuilder);
}
private ServerConnectorBuilder getDefaultServerConnector(){
if (this.defaultServerConnector==null){
this.defaultServerConnector=new ServerConnectorBuilder<>(defaultServerConnectorTemplateId);
serverConnectorBuilders.add(defaultServerConnector);
}
return this.defaultServerConnector;
}
/**
* @see ServerConnectorBuilder#withPort(int)
* @param port port
* @return builder
*/
public JettyServerBuilder withPort(int port){
getDefaultServerConnector().withPort(port);
return this;
}
/**
* @see ServerConnectorBuilder#withHost(String)
* @param host host
* @return builder
*/
public JettyServerBuilder withHost(String host){
getDefaultServerConnector().withHost(host);
return this;
}
/**
* @see ServerConnectorBuilder#withHostWildcard()
* @return builder
*/
public JettyServerBuilder withHostWildcard(){
getDefaultServerConnector().withHostWildcard();
return this;
}
/**
* @see ServerConnectorBuilder#withSsl(FactoryBase)
* @param ssl ssl
* @return builder
*/
public JettyServerBuilder withSsl(FactoryBase ssl) {
getDefaultServerConnector().withSsl(ssl);
return this;
}
/**
* @see ServerConnectorBuilder#withHttpConfiguration(HttpConfigurationFactory)
* @param httpConfiguration httpConfiguration
* @return builder
*/
public JettyServerBuilder withHttpConfiguration(HttpConfigurationFactory httpConfiguration) {
getDefaultServerConnector().withHttpConfiguration(httpConfiguration);
return this;
}
/**
* @see ServerConnectorBuilder#withRandomPort()
* @return builder
*/
public JettyServerBuilder withRandomPort(){
getDefaultServerConnector().withRandomPort();
return this;
}
private ResourceBuilder getDefaultJersey(){
if (this.resourceBuilder==null){
this.resourceBuilder=new ResourceBuilder<>(new FactoryTemplateId<>(this.rootTemplateId.name+"DefaultResource", ServletAndPathFactory.class));
addResourceBuilder(resourceBuilder);
}
return this.resourceBuilder;
}
/**
* @see ResourceBuilder#withPathSpec(String)
* @param pathSpec pathSpec
* @return builder
*/
public JettyServerBuilder withPathSpec(String pathSpec){
getDefaultJersey().withPathSpec(pathSpec);
return this;
}
/**
* add a new Resource to the default jersey servlet
* @see ResourceBuilder#withResource(FactoryBase)
* @param resource resource
* @return builder
*/
public JettyServerBuilder withResource(FactoryBase,R> resource){
getDefaultJersey().withResource(resource);
return this;
}
/**
* @see ResourceBuilder#withJaxrsComponent(FactoryBase)
* @param jaxrsComponent jaxrsComponent
* @return builder
*/
public JettyServerBuilder withJaxrsComponent(FactoryBase,R> jaxrsComponent){
getDefaultJersey().withJaxrsComponent(jaxrsComponent);
return this;
}
/**
* @see ResourceBuilder#withLoggingFeature(FactoryBase)
* @param loggingFeature loggingFeature
* @return builder
*/
public JettyServerBuilder withLoggingFeature(FactoryBase loggingFeature){
getDefaultJersey().withLoggingFeature(loggingFeature);
return this;
}
/**
* @see ResourceBuilder#withObjectMapper(FactoryBase)
* @param objectMapper objectMapper
* @return builder
*/
public JettyServerBuilder withObjectMapper(FactoryBase objectMapper){
getDefaultJersey().withObjectMapper(objectMapper);
return this;
}
/**
* @see ResourceBuilder#withExceptionMapper(FactoryBase)
* @param exceptionMapper exceptionMapper
* @return builder
*/
public JettyServerBuilder withExceptionMapper(FactoryBase,R> exceptionMapper) {
getDefaultJersey().withExceptionMapper(exceptionMapper);
return this;
}
/**
* @see ResourceBuilder#withJerseyProperties(Map)
* @param jerseyProperties jerseyProperties
* @return builder
*/
public JettyServerBuilder withJerseyProperties(Map jerseyProperties) {
getDefaultJersey().withJerseyProperties(jerseyProperties);
return this;
}
/**
* @see ServerConnectorBuilder#withHttp2()
* @return builder
*/
public JettyServerBuilder withHttp2() {
getDefaultServerConnector().withHttp2();
return this;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy