![JAR search and dependency download from the Maven repository](/logo.png)
net.redpipe.engine.core.Server Maven / Gradle / Ivy
package net.redpipe.engine.core;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Function;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.ws.rs.Path;
import javax.ws.rs.ext.Provider;
import org.jboss.resteasy.plugins.server.vertx.VertxResteasyDeployment;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import io.netty.handler.codec.http.cookie.ClientCookieDecoder;
import io.reactiverse.reactivecontexts.core.Context;
import io.reactivex.Completable;
import io.reactivex.Single;
import io.swagger.converter.ModelConverters;
import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.jaxrs.config.ReaderConfigUtils;
import io.swagger.jaxrs.listing.ApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;
import io.vertx.config.ConfigRetrieverOptions;
import io.vertx.config.ConfigStoreOptions;
import io.vertx.core.VertxOptions;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.net.NetServerOptions;
import io.vertx.reactivex.config.ConfigRetriever;
import io.vertx.reactivex.core.Vertx;
import io.vertx.reactivex.ext.auth.AuthProvider;
import io.vertx.reactivex.ext.auth.User;
import io.vertx.reactivex.ext.jdbc.JDBCClient;
import io.vertx.reactivex.ext.sql.SQLClient;
import io.vertx.reactivex.ext.web.Cookie;
import io.vertx.reactivex.ext.web.Router;
import io.vertx.reactivex.ext.web.RoutingContext;
import io.vertx.reactivex.ext.web.Session;
import io.vertx.reactivex.ext.web.handler.CookieHandler;
import io.vertx.reactivex.ext.web.handler.SessionHandler;
import io.vertx.reactivex.ext.web.sstore.LocalSessionStore;
import net.redpipe.engine.dispatcher.VertxPluginRequestHandler;
import net.redpipe.engine.resteasy.RedpipeServletContext;
import net.redpipe.engine.spi.Plugin;
import net.redpipe.engine.swagger.RxModelConverter;
import net.redpipe.engine.template.TemplateRenderer;
import net.redpipe.engine.util.RedpipeUtil;
public class Server {
private Vertx vertx;
protected List plugins;
private static final Logger log = LoggerFactory.getLogger(Server.class);
protected String configFile = "conf/config.json";
private AppGlobals appGlobals;
public Server(){
// System.setProperty("co.paralleluniverse.fibers.verifyInstrumentation", "true");
}
public Completable start(){
return start((JsonObject)null);
}
public Completable start(Class>... resourceOrProviderClasses){
return start(null, resourceOrProviderClasses);
}
public Completable start(JsonObject defaultConfig, Class>... resourceOrProviderClasses){
/*
* OK this sucks: since the rx hooks are static, we can start a second server and the hooks are still there,
* which means that the new server's Single flow will capture the existing current AppGlobals, so we reset it
* here, even though it's not correct because it should be in susbscribe/create but that'd be too late and that
* flow would already be polluted with our globals…
*/
AppGlobals.clear();
appGlobals = AppGlobals.init();
return Single.create(s -> {
setupLogging();
VertxOptions options = new VertxOptions();
options.setWarningExceptionTime(Long.MAX_VALUE);
// Propagate the Resteasy/Redpipe/CDI contexts
Context.load();
JsonObject config = loadFileConfig(defaultConfig);
AppGlobals.get().setConfig(config);
s.onSuccess(config);
})
.flatMap(this::initVertx)
.flatMapCompletable(vertx -> {
this.vertx = vertx;
AppGlobals.get().setVertx(this.vertx);
return setupPlugins();
})
.concatWith(setupTemplateRenderers())
.andThen(setupResteasy(resourceOrProviderClasses))
.flatMapCompletable(deployment -> {
setupSwagger(deployment);
return setupVertx(deployment);
});
}
private Single initVertx(JsonObject config)
{
VertxOptions options;
if (config != null)
{
options = new VertxOptions(config);
}
else
{
options = new VertxOptions();
}
options.setWarningExceptionTime(Long.MAX_VALUE);
if (options.isClustered())
{
return Vertx.rxClusteredVertx(options);
}
else
{
vertx = Vertx.vertx(options);
return Single.just(vertx);
}
}
private Completable setupPlugins() {
return Completable.defer(() -> {
loadPlugins();
return doOnPlugins(plugin -> plugin.preInit());
});
}
protected void loadPlugins() {
plugins = new ArrayList();
for(Plugin plugin : ServiceLoader.load(Plugin.class))
plugins.add(plugin);
}
private Completable setupTemplateRenderers() {
return Completable.defer(() -> {
List renderers = new ArrayList<>();
for(TemplateRenderer renderer : ServiceLoader.load(TemplateRenderer.class))
renderers.add(renderer);
AppGlobals.get().setTemplateRenderers(renderers);
return Completable.complete();
});
}
private Completable setupVertx(VertxResteasyDeployment deployment) {
return Completable.defer(() -> {
// Get a DB
SQLClient dbClient = createDbClient(AppGlobals.get().getConfig());
Class> mainClass = null;
for (Class> resourceClass : deployment.getActualResourceClasses()) {
if(resourceClass.getAnnotation(MainResource.class) != null){
mainClass = resourceClass;
break;
}
}
// Save our injected globals
AppGlobals globals = AppGlobals.get();
globals.setDbClient(dbClient);
globals.setMainClass(mainClass);
globals.setDeployment(deployment);
return doOnPlugins(plugin -> plugin.init())
.concatWith(startVertx(deployment));
});
}
protected SQLClient createDbClient(JsonObject config) {
return JDBCClient.createNonShared(vertx, new JsonObject()
.put("url", config.getString("db_url", "jdbc:hsqldb:file:db/wiki"))
.put("driver_class", config.getString("db_driver", "org.hsqldb.jdbcDriver"))
.put("max_pool_size", config.getInteger("db_max_pool_size", 30)));
}
private Completable doOnPlugins(Function operation){
return Completable.defer(() -> {
Completable last = Completable.complete();
for(Plugin plugin : plugins) {
last = last.concatWith(operation.apply(plugin));
}
return last;
});
}
private Completable startVertx(VertxResteasyDeployment deployment)
{
return Completable.defer(() -> {
Router router = Router.router(vertx);
AppGlobals globals = AppGlobals.get();
globals.setRouter(router);
VertxPluginRequestHandler resteasyHandler = new VertxPluginRequestHandler(vertx, deployment, plugins);
return doOnPlugins(plugin -> plugin.preRoute())
.doOnComplete(() -> {
setupRoutes(router);
router.route().handler(routingContext -> {
ResteasyProviderFactory.pushContext(RoutingContext.class, routingContext);
ResteasyProviderFactory.pushContext(io.vertx.rxjava.ext.web.RoutingContext.class,
io.vertx.rxjava.ext.web.RoutingContext.newInstance(routingContext.getDelegate()));
resteasyHandler.handle(routingContext.request());
});
}).concatWith(doOnPlugins(plugin -> plugin.postRoute()))
.concatWith(Completable.defer(() -> {
// Start the front end server using the Jax-RS controller
int port = globals.getConfig().getInteger("http_port", 9000);
String host = globals.getConfig().getString("http_host", NetServerOptions.DEFAULT_HOST);
return vertx.createHttpServer()
.requestHandler(router::accept)
.rxListen(port, host)
.doOnSuccess(server -> System.out.println("Server started on port " + server.actualPort()))
.doOnError(t -> t.printStackTrace())
.ignoreElement();
}));
});
}
protected void setupRoutes(Router router) {
router.route().handler(CookieHandler.create());
// Workaround for https://github.com/vert-x3/vertx-web/pull/880
router.route().handler(context -> {
context.addHeadersEndHandler(v -> {
Session session = context.session();
if (!session.isDestroyed()) {
final int currentStatusCode = context.response().getStatusCode();
// Store the session (only and only if there was no error)
if (currentStatusCode < 200 || currentStatusCode >= 400) {
String previousValue = context.get("__REDPIPE_SAVED_COOKIE");
if(previousValue != null) {
io.netty.handler.codec.http.cookie.Cookie nettyCookie = ClientCookieDecoder.LAX.decode(previousValue);
Cookie newCookie = Cookie.newInstance(io.vertx.ext.web.Cookie.cookie(nettyCookie));
context.addCookie(newCookie);
}
}
}
});
context.next();
});
SessionHandler sessionHandler = SessionHandler.create(LocalSessionStore.create(vertx));
router.route().handler(sessionHandler);
AuthProvider auth = setupAuthenticationRoutes();
router.route().handler(context -> {
// Workaround for https://github.com/vert-x3/vertx-web/pull/880
context.addHeadersEndHandler(v -> {
Session session = context.session();
if (!session.isDestroyed()) {
final int currentStatusCode = context.response().getStatusCode();
// Store the session (only and only if there was no error)
if (currentStatusCode < 200 || currentStatusCode >= 400) {
Cookie cookie = context.getCookie(io.vertx.ext.web.handler.SessionHandler.DEFAULT_SESSION_COOKIE_NAME);
context.put("__REDPIPE_SAVED_COOKIE", cookie.encode());
}
}
});
// rx2
ResteasyProviderFactory.pushContext(AuthProvider.class, auth);
ResteasyProviderFactory.pushContext(User.class, context.user());
ResteasyProviderFactory.pushContext(Session.class, context.session());
// rx1
ResteasyProviderFactory.pushContext(io.vertx.rxjava.ext.auth.AuthProvider.class,
auth != null ? io.vertx.rxjava.ext.auth.AuthProvider.newInstance(auth.getDelegate()) : null);
ResteasyProviderFactory.pushContext(io.vertx.rxjava.ext.auth.User.class,
context.user() != null ? io.vertx.rxjava.ext.auth.User.newInstance(context.user().getDelegate()) : null);
ResteasyProviderFactory.pushContext(io.vertx.rxjava.ext.web.Session.class,
context.session() != null ? io.vertx.rxjava.ext.web.Session.newInstance(context.session().getDelegate()) : null);
context.next();
});
}
protected AuthProvider setupAuthenticationRoutes() {
return null;
}
private JsonObject loadFileConfig(JsonObject config)
{
if (config != null)
{
return config;
}
try
{
File current = new File(".").getCanonicalFile();
// We need to use the canonical file. Without the file name is .
System.setProperty("vertx.cwd", current.getAbsolutePath());
}
catch (Exception e)
{
// Ignore it.
}
String confArg = this.configFile;
File file = new File(confArg);
System.out.println(file.getAbsolutePath());
try (Scanner scanner = new Scanner(new File(confArg)).useDelimiter("\\A"))
{
String sconf = scanner.next();
try
{
return new JsonObject(sconf);
}
catch (DecodeException e)
{
log.error("Configuration file " + sconf + " does not contain a valid JSON object");
// empty config
return new JsonObject();
}
}
catch (FileNotFoundException e)
{
return new JsonObject();
}
}
private Single loadConfig(JsonObject config) {
if(config != null) {
AppGlobals.get().setConfig(config);
return Single.just(config);
}
String path = "conf/config.json";
return vertx.fileSystem().rxExists(path)
.flatMap(exists -> {
if(exists) {
ConfigStoreOptions fileStore = new ConfigStoreOptions()
.setType("file")
.setConfig(new JsonObject().put("path", path));
ConfigRetrieverOptions configRetrieverOptions = new ConfigRetrieverOptions()
.addStore(fileStore);
ConfigRetriever retriever = ConfigRetriever.create(vertx, configRetrieverOptions);
return retriever.rxGetConfig().map(loadedConfig -> {
AppGlobals.get().setConfig(loadedConfig);
return loadedConfig;
});
} else {
// empty config
JsonObject emptyConfig = new JsonObject();
AppGlobals.get().setConfig(emptyConfig);
return Single.just(emptyConfig);
}
});
}
private void setupSwagger(VertxResteasyDeployment deployment) {
ModelConverters.getInstance().addConverter(new RxModelConverter());
// Swagger
ServletContext servletContext = new RedpipeServletContext();
AppGlobals.get().setGlobal(ServletContext.class, servletContext);
ServletConfig servletConfig = new ServletConfig(){
@Override
public String getServletName() {
return "pretend-servlet";
}
@Override
public ServletContext getServletContext() {
return servletContext;
}
@Override
public String getInitParameter(String name) {
return getServletContext().getInitParameter(name);
}
@Override
public Enumeration getInitParameterNames() {
return getServletContext().getInitParameterNames();
}
};
AppGlobals.get().setGlobal(ServletConfig.class, servletConfig);
ReaderConfigUtils.initReaderConfig(servletConfig);
BeanConfig swaggerConfig = new MyBeanConfig();
swaggerConfig.setVersion("1.0");
swaggerConfig.setSchemes(new String[]{"http"});
swaggerConfig.setHost("localhost:"+AppGlobals.get().getConfig().getInteger("http_port", 9000));
swaggerConfig.setBasePath("/");
Set resourcePackages = new HashSet<>();
for (Class> klass : deployment.getActualResourceClasses()) {
resourcePackages.add(klass.getPackage().getName());
}
swaggerConfig.setResourcePackage(String.join(",", resourcePackages));
swaggerConfig.setServletConfig(servletConfig);
swaggerConfig.setPrettyPrint(true);
swaggerConfig.setScan(true);
deployment.getRegistry().addPerInstanceResource(ApiListingResource.class);
deployment.getProviderFactory().register(SwaggerSerializers.class);
}
protected Single setupResteasy(Class>... resourceOrProviderClasses) {
return Single.defer(() -> {
// Build the Jax-RS hello world deployment
VertxResteasyDeployment deployment = new VertxResteasyDeployment();
deployment.getDefaultContextObjects().put(Vertx.class, AppGlobals.get().getVertx());
deployment.getDefaultContextObjects().put(AppGlobals.class, AppGlobals.get());
return doOnPlugins(plugin -> plugin.deployToResteasy(deployment)).toSingle(() -> {
for(Class> klass : resourceOrProviderClasses) {
if(klass.isAnnotationPresent(Path.class))
deployment.getActualResourceClasses().add(klass);
if(klass.isAnnotationPresent(Provider.class))
deployment.getActualProviderClasses().add(klass);
}
try {
deployment.start();
}catch(ExceptionInInitializerError err) {
// rxjava behaves badly on LinkageError
RedpipeUtil.rethrow(err.getCause());
}
return deployment;
}).doOnError(t -> t.printStackTrace());
});
}
private void setupLogging() {
// final ConsoleHandler consoleHandler = new ConsoleHandler();
// consoleHandler.setLevel(Level.FINEST);
// consoleHandler.setFormatter(new SimpleFormatter());
//
// final Logger app = Logger.getLogger("org.jboss.weld.vertx");
// app.setLevel(Level.FINEST);
// app.addHandler(consoleHandler);
}
public Completable close() {
return doOnPlugins(plugin -> plugin.shutdown())
.concatWith(vertx.rxClose());
}
public Vertx getVertx() {
return vertx;
}
public AppGlobals getAppGlobals() {
return appGlobals;
}
public static void main(String[] args) {
Server test = new Server();
test.start();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy