introduce settings for module instantiation

This commit is contained in:
Jörg Prante 2023-03-09 18:18:32 +01:00
parent 9fb8da8a55
commit 760e83c035
23 changed files with 158 additions and 106 deletions

View file

@ -1,6 +1,3 @@
import org.xbib.net.http.server.ApplicationModule;
import org.xbib.net.http.server.application.config.ConfigApplicationModule;
module org.xbib.net.http.server.application.config {
uses org.xbib.config.ConfigLogger;
exports org.xbib.net.http.server.application.config;
@ -8,5 +5,4 @@ module org.xbib.net.http.server.application.config {
requires org.xbib.net.http.server;
requires org.xbib.config;
requires java.logging;
provides ApplicationModule with ConfigApplicationModule;
}

View file

@ -30,16 +30,12 @@ public class ConfigApplicationModule extends BaseApplicationModule {
private Settings settings;
public ConfigApplicationModule() {
public ConfigApplicationModule(Application application, String name, Settings settings) {
super(application, name, settings);
}
@Override
public String getName() {
return "config";
}
@Override
public void onOpen(Application application) throws Exception {
public void onOpen(Application application, Settings settings) {
String profile = System.getProperty("application.profile");
if (profile == null) {
profile = "developer";

View file

@ -1,14 +1,11 @@
import org.xbib.net.http.server.ApplicationModule;
import org.xbib.net.http.server.application.database.DatabaseApplicationModule;
module org.xbib.net.http.server.application.database {
requires org.xbib.net;
requires org.xbib.net.http;
requires org.xbib.net.http.server;
requires org.xbib.config;
requires org.xbib.jdbc.query;
requires org.xbib.jdbc.connection.pool;
requires org.xbib.datastructures.tiny;
requires java.logging;
requires java.sql;
provides ApplicationModule with DatabaseApplicationModule;
}

View file

@ -9,13 +9,14 @@ import org.xbib.net.http.server.BaseApplicationModule;
import org.xbib.net.http.server.HttpRequest;
import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.server.HttpService;
import org.xbib.settings.Settings;
import javax.sql.DataSource;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DatabaseApplicationModule extends BaseApplicationModule {
public class DatabaseApplicationModule<A extends Application> extends BaseApplicationModule {
private static final Logger logger = Logger.getLogger(DatabaseApplicationModule.class.getName());
@ -23,16 +24,12 @@ public class DatabaseApplicationModule extends BaseApplicationModule {
private DatabaseProvider databaseProvider;
public DatabaseApplicationModule() {
public DatabaseApplicationModule(Application application, String name, Settings settings) {
super(application, name, settings);
}
@Override
public String getName() {
return "database";
}
@Override
public void onOpen(Application application) throws Exception {
public void onOpen(Application application, Settings settings) throws Exception {
this.dataSource = createDataSource();
String flavor = System.getProperty("database.flavor");
this.databaseProvider = flavor != null ?

View file

@ -5,8 +5,6 @@ dependencies {
api project(':net-http-server-application-config')
api project(':net-http-template-groovy')
api libs.jdbc.query
implementation libs.settings.datastructures.json
implementation libs.settings.datastructures.yaml
implementation libs.webjars.bootstrap
implementation libs.webjars.jquery
implementation libs.webjars.fontawesome

View file

@ -9,7 +9,6 @@ module org.xbib.net.http.server.application.web {
requires org.xbib.net.http.server.netty.secure;
requires org.xbib.net.http.template.groovy;
requires org.xbib.datastructures.tiny;
requires org.xbib.datastructures.json.tiny;
requires org.xbib.jdbc.query;
requires org.xbib.config;
requires java.logging;

View file

@ -10,11 +10,15 @@ import org.xbib.net.http.server.session.IncomingSessionHandler;
import org.xbib.net.http.server.session.OutgoingSessionHandler;
import org.xbib.net.http.server.session.Session;
import org.xbib.net.http.server.session.file.FileJsonSessionCodec;
import org.xbib.settings.Settings;
public class WebApplication extends BaseApplication {
private final WebApplicationBuilder builder;
protected WebApplication(WebApplicationBuilder builder) {
super(builder);
this.builder = builder;
}
public static WebApplicationBuilder builder() {

View file

@ -1,7 +1,6 @@
package org.xbib.net.http.server.application.web;
import org.xbib.net.http.server.BaseApplicationBuilder;
import org.xbib.net.http.server.Application;
import org.xbib.settings.Settings;
@ -11,14 +10,18 @@ public class WebApplicationBuilder extends BaseApplicationBuilder {
protected String name;
protected Settings settings;
protected WebApplicationBuilder() throws Exception {
protected WebApplicationBuilder() {
super();
this.profile = System.getProperty("application.profile");
this.name = System.getProperty("application.name");
}
@Override
public WebApplicationBuilder setSettings(Settings settings) {
super.setSettings(settings);
return this;
}
public WebApplicationBuilder setProfile(String profile) {
this.profile = profile;
return this;
@ -29,13 +32,8 @@ public class WebApplicationBuilder extends BaseApplicationBuilder {
return this;
}
public WebApplicationBuilder setSettings(Settings settings) {
this.settings = settings;
return this;
}
@Override
public Application build() {
public WebApplication build() {
WebApplication webApplication = new WebApplication(this);
setupApplication(webApplication);
return webApplication;

View file

@ -23,13 +23,11 @@ public class HttpResponse extends BaseHttpResponse {
@Override
public void close() throws IOException {
//logger.log(Level.FINE, "close");
//builder.internalClose();
builder.close();
}
@Override
public void flush() throws IOException {
//logger.log(Level.FINE, "flush");
//builder.internalFlush();
builder.flush();
}
}

View file

@ -124,12 +124,17 @@ public class HttpResponseBuilder extends BaseHttpResponseBuilder {
return this;
}
public void flush() {
internalBufferWrite(Unpooled.buffer(0));
}
public void close() {
ctx.close();
}
@Override
public HttpResponse build() {
Objects.requireNonNull(ctx);
//if (shouldFlush()) {
// internalFlush();
//}
if (body != null) {
internalWrite(body);
} else if (charBuffer != null && charset != null) {
@ -141,19 +146,13 @@ public class HttpResponseBuilder extends BaseHttpResponseBuilder {
} else if (inputStream != null) {
internalWrite(inputStream, bufferSize, true);
}
if (shouldFlush()) {
// really server flush?
//flush();
}
return new HttpResponse(this);
}
//void internalFlush() {
// logger.log(Level.FINE, "internal flush");
// internalBufferWrite(Unpooled.buffer(0));
//}
//void internalClose() {
// logger.log(Level.FINE, "internal close");
// ctx.close();
//}
private void internalWrite(String body) {
internalWrite(dataBufferFactory.wrap(StandardCharsets.UTF_8.encode(body)));
}

View file

@ -1,3 +1,6 @@
dependencies {
api project(':net-http')
api libs.config
implementation libs.settings.datastructures.json
implementation libs.settings.datastructures.yaml
}

View file

@ -2,6 +2,7 @@ import org.xbib.net.http.server.ApplicationModule;
module org.xbib.net.http.server {
uses ApplicationModule;
uses org.xbib.config.ConfigLogger;
exports org.xbib.net.http.server;
exports org.xbib.net.http.server.auth;
exports org.xbib.net.http.server.cookie;
@ -23,6 +24,7 @@ module org.xbib.net.http.server {
requires org.xbib.net.mime;
requires org.xbib.net.http;
requires org.xbib.datastructures.common;
requires org.xbib.config;
requires java.logging;
requires java.naming;
requires java.sql;

View file

@ -3,6 +3,7 @@ package org.xbib.net.http.server;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.session.SessionListener;
import org.xbib.settings.Settings;
import java.io.Closeable;
import java.io.IOException;
@ -26,6 +27,8 @@ public interface Application extends SessionListener, Resolver<Path>, Closeable
String getContextPath();
Settings getSettings();
Collection<ApplicationModule> getModules();
/**

View file

@ -26,7 +26,5 @@ public interface ApplicationBuilder {
ApplicationBuilder setZoneId(ZoneId zoneId);
ApplicationBuilder addModule(ApplicationModule module);
Application build();
}

View file

@ -1,12 +1,13 @@
package org.xbib.net.http.server;
import org.xbib.net.http.server.session.Session;
import org.xbib.settings.Settings;
public interface ApplicationModule extends Comparable<ApplicationModule> {
public interface ApplicationModule {
String getName();
void onOpen(Application application) throws Exception;
void onOpen(Application application, Settings settings) throws Exception;
void onOpen(Application application, HttpServerContext httpServerContext);

View file

@ -14,6 +14,7 @@ import org.xbib.net.http.server.session.Session;
import org.xbib.net.http.server.util.BlockingThreadPoolExecutor;
import org.xbib.net.http.server.validate.HttpRequestValidator;
import org.xbib.net.util.NamedThreadFactory;
import org.xbib.settings.Settings;
import java.io.Closeable;
import java.io.IOException;
@ -55,7 +56,7 @@ public class BaseApplication implements Application {
protected BaseApplication(BaseApplicationBuilder builder) {
this.builder = builder;
this.executor = new BlockingThreadPoolExecutor(builder.blockingThreadCount, builder.blockingQueueCount,
new NamedThreadFactory("org-xbib-net-hhtp-server-application"));
new NamedThreadFactory("org-xbib-net-http-server-application"));
this.executor.setRejectedExecutionHandler((runnable, threadPoolExecutor) ->
logger.log(Level.SEVERE, "rejected " + runnable + " for thread pool executor = " + threadPoolExecutor));
this.httpRequestValidator = buildRequestValidator();
@ -86,6 +87,11 @@ public class BaseApplication implements Application {
return builder.contextPath;
}
@Override
public Settings getSettings() {
return builder.settings;
}
public HttpRouter getRouter() {
return builder.router;
}

View file

@ -1,7 +1,12 @@
package org.xbib.net.http.server;
import org.xbib.config.ConfigLoader;
import org.xbib.config.ConfigLogger;
import org.xbib.config.ConfigParams;
import org.xbib.config.SystemConfigLogger;
import org.xbib.datastructures.common.ImmutableSet;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.settings.Settings;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -9,6 +14,8 @@ import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.logging.Level;
@ -18,6 +25,17 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
private static final Logger logger = Logger.getLogger(BaseApplicationBuilder.class.getName());
private static final ConfigLogger bootLogger;
static {
// early loading of boot logger during static initialization block
ServiceLoader<ConfigLogger> serviceLoader = ServiceLoader.load(ConfigLogger.class);
Optional<ConfigLogger> optionalBootLogger = serviceLoader.findFirst();
bootLogger = optionalBootLogger.orElse(new SystemConfigLogger());
}
protected ClassLoader classLoader;
protected int blockingThreadCount;
protected int blockingQueueCount;
@ -36,11 +54,18 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected ZoneId zoneId;
protected List<ApplicationModule> applicationModuleList;
protected Set<String> staticFileSuffixes;
protected ConfigParams configParams;
protected ConfigLoader configLoader;
protected Settings settings;
protected List<ApplicationModule> applicationModuleList;
protected BaseApplicationBuilder() {
this.classLoader = getClass().getClassLoader();
this.blockingThreadCount = Runtime.getRuntime().availableProcessors();
this.blockingQueueCount = Integer.MAX_VALUE;
this.home = Paths.get(System.getProperties().containsKey("application.home") ? System.getProperty("application.home") : ".");
@ -52,6 +77,11 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
this.applicationModuleList = new ArrayList<>();
}
public BaseApplicationBuilder setSettings(Settings settings) {
this.settings = settings;
return this;
}
@Override
public BaseApplicationBuilder setThreadCount(int blockingThreadCount) {
this.blockingThreadCount = blockingThreadCount;
@ -77,19 +107,19 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
}
@Override
public BaseApplicationBuilder setSecret(String secret) {
public ApplicationBuilder setSecret(String secret) {
this.secret = secret;
return this;
}
@Override
public BaseApplicationBuilder setSessionsEnabled(boolean sessionsEnabled) {
public ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled) {
this.sessionsEnabled = sessionsEnabled;
return this;
}
@Override
public BaseApplicationBuilder setRouter(HttpRouter router) {
public ApplicationBuilder setRouter(HttpRouter router) {
this.router = router;
return this;
}
@ -106,15 +136,6 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this;
}
@Override
public ApplicationBuilder addModule(ApplicationModule module) {
if (module != null) {
applicationModuleList.add(module);
logger.log(Level.FINE, "module " + module + " added");
}
return this;
}
public ApplicationBuilder addStaticSuffixes(String... suffixes) {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (String suffix : suffixes) {
@ -124,6 +145,11 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this;
}
public ApplicationBuilder registerModule(ApplicationModule applicationModule) {
applicationModuleList.add(applicationModule);
return this;
}
@Override
public Application build() {
Application application = new BaseApplication(this);
@ -132,19 +158,41 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
}
protected void setupApplication(Application application) {
ServiceLoader<ApplicationModule> serviceLoader = ServiceLoader.load(ApplicationModule.class);
for (ApplicationModule module : serviceLoader) {
applicationModuleList.add(module);
logger.log(Level.FINE, "module " + module + " added");
String profile = System.getProperty("application.profile");
if (profile == null) {
profile = "developer";
}
applicationModuleList.forEach(module -> {
try {
module.onOpen(application);
logger.log(Level.FINE, "module " + module + " opened");
} catch (Exception e) {
throw new IllegalStateException(e);
String[] args = profile.split(";");
this.configParams = new ConfigParams()
.withArgs(args)
.withDirectoryName("application")
.withFileNamesWithoutSuffix(args[0])
.withSystemEnvironment()
.withSystemProperties();
this.configLoader = ConfigLoader.getInstance()
.withLogger(bootLogger);
this.settings = configLoader.load(configParams);
for (Map.Entry<String, Settings> entry : settings.getGroups("module").entrySet()) {
String moduleName = entry.getKey();
Settings moduleSettings = entry.getValue();
if (moduleSettings.getAsBoolean("enabled", true)) {
try {
String className = moduleSettings.get("class");
@SuppressWarnings("unchecked")
Class<ApplicationModule> clazz =
(Class<ApplicationModule>) Class.forName(className, true, classLoader);
ApplicationModule applicationModule = clazz.getConstructor(Application.class, String.class, Settings.class)
.newInstance(application, moduleName, moduleSettings);
applicationModuleList.add(applicationModule);
applicationModule.onOpen(application, moduleSettings);
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
throw new IllegalArgumentException("class not found or not loadable: " + e.getMessage());
}
} else {
logger.log(Level.WARNING, "disabled module: " + moduleName);
}
});
}
if (router != null) {
router.setApplication(application);
}

View file

@ -1,14 +1,29 @@
package org.xbib.net.http.server;
import org.xbib.net.http.server.session.Session;
import org.xbib.settings.Settings;
public abstract class BaseApplicationModule implements ApplicationModule {
public BaseApplicationModule() {
protected final Application application;
protected final String name;
protected final Settings settings;
public BaseApplicationModule(Application application, String name, Settings settings) {
this.application = application;
this.name = name;
this.settings = settings;
}
@Override
public void onOpen(Application application) throws Exception {
public String getName() {
return name != null ? name : settings.get("name", "undefined");
}
@Override
public void onOpen(Application application, Settings settings) throws Exception {
}
@Override
@ -38,9 +53,4 @@ public abstract class BaseApplicationModule implements ApplicationModule {
@Override
public void onClose(Application application) {
}
@Override
public int compareTo(ApplicationModule o) {
return getName().compareTo(o.getName());
}
}

View file

@ -80,7 +80,7 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
public void reset() {
this.version = HttpVersion.HTTP_1_1;
this.status = null;
this.status = HttpResponseStatus.OK;
this.headers = new HttpHeaders();
this.trailingHeaders = new HttpHeaders();
this.contentType = HttpHeaderValues.APPLICATION_OCTET_STREAM;
@ -302,6 +302,7 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
if (httpServerConfig != null && httpServerConfig.getServerName() != null) {
headers.add(HttpHeaderNames.SERVER, httpServerConfig.getServerName());
}
logger.log(Level.FINER, "headers built: " + headers);
}
public CharBuffer wrapHeaders() {

View file

@ -82,12 +82,14 @@ public abstract class AbstractResourceHandler implements HttpHandler {
context.response()
.addHeader(HttpHeaderNames.LOCATION, resource.getIndexFileName())
.setResponseStatus(HttpResponseStatus.TEMPORARY_REDIRECT)
.build().flush(); // flush is important
.build()
.flush(); // write headers
} else {
// send forbidden, we do not allow directory access
context.response()
.setResponseStatus(HttpResponseStatus.FORBIDDEN)
.build().flush(); // fluish is important
.build()
.flush(); // write status
}
context.done();
} else {
@ -291,7 +293,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
HttpServerContext context,
long offset, long size) throws IOException {
if (resource instanceof HttpServerResource) {
logger.log(Level.FINE, "we have a server resource, render by resource");
logger.log(Level.FINER, "we have a server resource, render by resource");
((HttpServerResource) resource).render(context);
return;
}

View file

@ -1,13 +1,10 @@
import org.xbib.net.http.server.ApplicationModule;
import org.xbib.net.http.template.groovy.GroovyTemplateApplicationModule;
module org.xbib.net.http.template.groovy {
exports org.xbib.net.http.template.groovy;
requires org.xbib.net;
requires org.xbib.net.http;
requires org.xbib.net.http.server;
requires org.xbib.config;
requires org.apache.groovy;
requires org.apache.groovy.templates;
requires java.logging;
provides ApplicationModule with GroovyTemplateApplicationModule;
}

View file

@ -8,6 +8,7 @@ import org.xbib.net.http.server.HttpServerContext;
import java.io.IOException;
import java.io.UncheckedIOException;
import org.xbib.net.http.server.HttpService;
import org.xbib.settings.Settings;
public class GroovyTemplateApplicationModule extends BaseApplicationModule {
@ -15,16 +16,12 @@ public class GroovyTemplateApplicationModule extends BaseApplicationModule {
private GroovyTemplateRenderer groovyTemplateRenderer;
public GroovyTemplateApplicationModule() {
public GroovyTemplateApplicationModule(Application application, String name, Settings settings) {
super(application, name, settings);
}
@Override
public String getName() {
return "groovy-template";
}
@Override
public void onOpen(Application application) {
public void onOpen(Application application, Settings settings) {
this.groovyMarkupTemplateHandler = new GroovyMarkupTemplateHandler(application);
this.groovyTemplateRenderer = new GroovyTemplateRenderer();
}
@ -40,7 +37,9 @@ public class GroovyTemplateApplicationModule extends BaseApplicationModule {
@Override
public void onOpen(Application application, HttpServerContext httpServerContext, HttpService httpService, HttpRequest httpRequest) {
httpServerContext.attributes().put("request", httpRequest);
httpServerContext.attributes().put("params", httpRequest.getParameter().asSingleValuedMap());
application.getModules().forEach(module -> httpServerContext.attributes().put(module.getName(), module));
}
@Override

View file

@ -98,10 +98,10 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
Template template = templates.get(templatePath);
Logger templateLogger = Logger.getLogger("template." + getName().replace('/', '.'));
Binding binding = new Binding();
binding.setVariable("variables", binding.getVariables());
httpServerContext.attributes().forEach(binding::setVariable);
binding.setVariable("logger", templateLogger);
binding.setVariable("log", templateLogger);
application.getModules().forEach(m -> binding.setVariable(m.getName(), m));
DefaultTemplateResolver templateResolver = httpServerContext.attributes().get(DefaultTemplateResolver.class, "templateresolver");
if (templateResolver != null) {
// handle programmatic locale change plus template making under lock so no other request/response can interrupt us