diff --git a/lighty-modules/lighty-aaa-aggregator/lighty-aaa/src/test/java/io/lighty/aaa/LocalHttpServerTest.java b/lighty-modules/lighty-aaa-aggregator/lighty-aaa/src/test/java/io/lighty/aaa/LocalHttpServerTest.java index dd91cb80be..a97c14d108 100644 --- a/lighty-modules/lighty-aaa-aggregator/lighty-aaa/src/test/java/io/lighty/aaa/LocalHttpServerTest.java +++ b/lighty-modules/lighty-aaa-aggregator/lighty-aaa/src/test/java/io/lighty/aaa/LocalHttpServerTest.java @@ -8,12 +8,16 @@ package io.lighty.aaa; import io.lighty.server.LightyServerBuilder; +import java.lang.reflect.Field; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.security.AccessController; +import java.security.PrivilegedAction; import javax.servlet.Servlet; import org.eclipse.jetty.server.Server; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.opendaylight.aaa.web.jetty.JettyWebServer; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -34,16 +38,36 @@ public void init() { public void initLocalHttpServerTest() throws Exception { InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLocalHost(), 8888); LightyServerBuilder serverBuilder = new LightyServerBuilder(socketAddress); - Server server = serverBuilder.build(); + JettyWebServer server = serverBuilder.build(); + Server jettyServer = null; + try { + // Use AccessController.doPrivileged to allow access to the private field + Field serverField = AccessController.doPrivileged((PrivilegedAction) () -> { + try { + Field field = JettyWebServer.class.getDeclaredField("server"); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + throw new RuntimeException("Field not found", e); + } + }); + + jettyServer = (Server) serverField.get(server); + + // Only set the handler if no handler is already set by JaxRsEndpoint + + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to set handler on JettyWebServer", e); + } LocalHttpServer localHttpServer = new LocalHttpServer(serverBuilder); localHttpServer.registerServlet(TEST_SERVLET, servlet, null); localHttpServer.unregister(TEST_SERVLET); server.start(); - Assert.assertTrue(server.isStarted()); - Assert.assertTrue(server.isRunning()); + Assert.assertTrue(jettyServer.isStarted()); + Assert.assertTrue(jettyServer.isRunning()); server.stop(); - Assert.assertFalse(server.isStarted()); - Assert.assertFalse(server.isRunning()); + Assert.assertFalse(jettyServer.isStarted()); + Assert.assertFalse(jettyServer.isRunning()); } } diff --git a/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/Http2LightyServerBuilder.java b/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/Http2LightyServerBuilder.java index 6ea4b14500..83717a7c28 100644 --- a/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/Http2LightyServerBuilder.java +++ b/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/Http2LightyServerBuilder.java @@ -8,7 +8,10 @@ package io.lighty.server; import io.lighty.server.config.SecurityConfig; +import java.lang.reflect.Field; import java.net.InetSocketAddress; +import java.security.AccessController; +import java.security.PrivilegedAction; import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; @@ -17,6 +20,7 @@ import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; +import org.opendaylight.aaa.web.jetty.JettyWebServer; public class Http2LightyServerBuilder extends LightyServerBuilder { @@ -28,9 +32,10 @@ public Http2LightyServerBuilder(final InetSocketAddress inetSocketAddress, final } @Override - public Server build() { - super.server = new Server(); + public JettyWebServer build() { + super.server = new JettyWebServer(this.inetSocketAddress.getPort()); final var server = super.build(); + // HTTPS Configuration final var httpsConfig = new HttpConfiguration(); httpsConfig.setSecureScheme(HttpScheme.HTTPS.asString()); @@ -48,9 +53,27 @@ public Server build() { final var ssl = securityConfig.getSslConnectionFactory(alpn.getProtocol()); // HTTP/2 Connector - final var sslConnector = new ServerConnector(server, ssl, alpn, h2, new HttpConnectionFactory(httpsConfig)); - sslConnector.setPort(this.inetSocketAddress.getPort()); - server.addConnector(sslConnector); + try { + // Use AccessController.doPrivileged to allow access to the private field + Field serverField = AccessController.doPrivileged((PrivilegedAction) () -> { + try { + Field field = JettyWebServer.class.getDeclaredField("server"); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + throw new RuntimeException("Field not found", e); + } + }); + + Server jettyServer = (Server) serverField.get(server); + final var sslConnector = new ServerConnector( + jettyServer, ssl, alpn, h2, new HttpConnectionFactory(httpsConfig)); + sslConnector.setPort(this.inetSocketAddress.getPort()); + jettyServer.addConnector(sslConnector); + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to set handler on JettyWebServer", e); + } + return server; } } diff --git a/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/HttpsLightyServerBuilder.java b/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/HttpsLightyServerBuilder.java index fee52f5239..b57befe2c8 100644 --- a/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/HttpsLightyServerBuilder.java +++ b/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/HttpsLightyServerBuilder.java @@ -8,7 +8,10 @@ package io.lighty.server; import io.lighty.server.config.SecurityConfig; +import java.lang.reflect.Field; import java.net.InetSocketAddress; +import java.security.AccessController; +import java.security.PrivilegedAction; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -16,25 +19,45 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; +import org.opendaylight.aaa.web.jetty.JettyWebServer; public class HttpsLightyServerBuilder extends LightyServerBuilder { private final SecurityConfig securityConfig; public HttpsLightyServerBuilder(final InetSocketAddress inetSocketAddress, final SecurityConfig securityConfig) { super(inetSocketAddress); - this.server = new Server(); this.securityConfig = securityConfig; } @Override - public Server build() { - final Server server = super.build(); + public JettyWebServer build() { + super.server = new JettyWebServer(this.inetSocketAddress.getPort()); + final JettyWebServer server = super.build(); + + // HTTPS Configuration final SslConnectionFactory ssl = securityConfig.getSslConnectionFactory(HttpVersion.HTTP_1_1.asString()); - final ServerConnector sslConnector = new ServerConnector(server, - ssl, httpConfiguration(this.inetSocketAddress)); - sslConnector.setPort(this.inetSocketAddress.getPort()); + final ServerConnector sslConnector; + + try { + // Use AccessController.doPrivileged to allow access to the private field + Field serverField = AccessController.doPrivileged((PrivilegedAction) () -> { + try { + Field field = JettyWebServer.class.getDeclaredField("server"); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + throw new RuntimeException("Field not found", e); + } + }); + + Server jettyServer = (Server) serverField.get(server); + sslConnector = new ServerConnector(jettyServer, ssl, httpConfiguration(this.inetSocketAddress)); + sslConnector.setPort(this.inetSocketAddress.getPort()); + jettyServer.addConnector(sslConnector); + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to set handler on JettyWebServer", e); + } - server.addConnector(sslConnector); return server; } diff --git a/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/LightyServerBuilder.java b/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/LightyServerBuilder.java index 819d0fef5f..f2f3a71034 100644 --- a/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/LightyServerBuilder.java +++ b/lighty-modules/lighty-jetty-server/src/main/java/io/lighty/server/LightyServerBuilder.java @@ -7,7 +7,10 @@ */ package io.lighty.server; +import java.lang.reflect.Field; import java.net.InetSocketAddress; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.EnumSet; import java.util.EventListener; @@ -18,8 +21,10 @@ import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.opendaylight.aaa.web.jetty.JettyWebServer; /** * Allows user to build jetty server. @@ -31,13 +36,8 @@ public class LightyServerBuilder { protected final InetSocketAddress inetSocketAddress; protected final List contexts; - protected Server server; + protected JettyWebServer server; - /** - * Init new jetty server on specific port and address wrapped into {@link InetSocketAddress}. - * - * @param inetSocketAddress - port and address of server - */ public LightyServerBuilder(final InetSocketAddress inetSocketAddress) { this.inetSocketAddress = inetSocketAddress; this.filters = new HashMap<>(); @@ -46,80 +46,106 @@ public LightyServerBuilder(final InetSocketAddress inetSocketAddress) { this.contexts = new ArrayList<>(); } - /** - * Init jetty server with existing ones. - * - * @param server - jetty server - */ - public LightyServerBuilder(final Server server) { + public LightyServerBuilder(final JettyWebServer server) { this(new InetSocketAddress(0)); this.server = server; } - /** - * Add filter for handlers. - * - * @param filterHolder - filter holder - * @param path - path - * @return instance of {@link LightyServerBuilder} - */ public LightyServerBuilder addCommonFilter(final FilterHolder filterHolder, final String path) { this.filters.put(filterHolder, path); return this; } - /** - * Add listener for handlers. - * - * @param eventListener - event listener - * @return instance of {@link LightyServerBuilder} - */ public LightyServerBuilder addCommonEventListener(final EventListener eventListener) { this.listeners.add(eventListener); return this; } - /** - * Add init parameters for handlers. - * - * @param key - key of init parameters - * @param value - value of init parameters - * @return instance of {@link LightyServerBuilder} - */ public LightyServerBuilder addCommonInitParameter(final String key, final String value) { this.parameters.put(key, value); return this; } - /** - * Add specific handler for server to handle incoming HTTP requests. - * - * @param handler - specific handler - * @return instance of {@link LightyServerBuilder} - */ public LightyServerBuilder addContextHandler(final Handler handler) { this.contexts.add(handler); return this; } - /** - * Build jetty server with specific settings (filters, init params, event listeners, handlers). - * - * @return instance of jetty server - */ - public Server build() { + public JettyWebServer build() { if (this.server == null) { - this.server = new Server(this.inetSocketAddress); + this.server = new JettyWebServer(this.inetSocketAddress.getPort()); } + final ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); - this.contexts.forEach((contextHandler) -> { + this.contexts.forEach(contextHandler -> { addFilters(contextHandler); contextHandlerCollection.addHandler(contextHandler); }); - this.server.setHandler(contextHandlerCollection); + + try { + Field serverField = AccessController.doPrivileged((PrivilegedAction) () -> { + try { + Field field = JettyWebServer.class.getDeclaredField("server"); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + throw new RuntimeException("Field not found", e); + } + }); + + Server jettyServer = (Server) serverField.get(this.server); + Handler currentHandler = jettyServer.getHandler(); + + if (currentHandler == null) { + jettyServer.setHandler(contextHandlerCollection); + } else if (currentHandler instanceof HandlerCollection) { + HandlerCollection handlerCollection = (HandlerCollection) currentHandler; + handlerCollection.addHandler(contextHandlerCollection); + } else { + HandlerCollection handlerCollection = new HandlerCollection(); + handlerCollection.addHandler(currentHandler); + handlerCollection.addHandler(contextHandlerCollection); + jettyServer.setHandler(handlerCollection); + } + + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to set handler on JettyWebServer", e); + } + return this.server; } + @SuppressWarnings("IllegalCatch") + public void addHandlerAtRuntime(ContextHandlerCollection newHandler) { + try { + Field serverField = AccessController.doPrivileged((PrivilegedAction) () -> { + try { + Field field = JettyWebServer.class.getDeclaredField("server"); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + throw new RuntimeException("Field not found", e); + } + }); + + Server jettyServer = (Server) serverField.get(this.server); + Handler currentHandler = jettyServer.getHandler(); + + if (currentHandler instanceof HandlerCollection) { + HandlerCollection handlerCollection = (HandlerCollection) currentHandler; + handlerCollection.addHandler(newHandler); + newHandler.start(); + } else { + throw new IllegalStateException("Current handler is not a HandlerCollection"); + } + + } catch (RuntimeException e) { + throw new RuntimeException("Failed to add handler at runtime", e); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + void addFilters(final Handler contextHandler) { if (contextHandler instanceof ContextHandlerCollection) { final ContextHandlerCollection sch = (ContextHandlerCollection) contextHandler; diff --git a/lighty-modules/lighty-jetty-server/src/test/java/io/lighty/server/LightyServerBuilderTest.java b/lighty-modules/lighty-jetty-server/src/test/java/io/lighty/server/LightyServerBuilderTest.java index d633ad1265..84860879da 100644 --- a/lighty-modules/lighty-jetty-server/src/test/java/io/lighty/server/LightyServerBuilderTest.java +++ b/lighty-modules/lighty-jetty-server/src/test/java/io/lighty/server/LightyServerBuilderTest.java @@ -12,9 +12,9 @@ import io.lighty.server.util.LightyServerConfigUtils; import java.net.InetSocketAddress; import java.util.EventListener; -import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.servlet.FilterHolder; +import org.opendaylight.aaa.web.jetty.JettyWebServer; import org.testng.annotations.Test; public class LightyServerBuilderTest { @@ -68,7 +68,7 @@ public void testHttpsCustomServerBuilder() throws Exception { assertNotNull(server); } - private static Server initLightyServer(final LightyServerBuilder serverBuilder) { + private static JettyWebServer initLightyServer(final LightyServerBuilder serverBuilder) { final var filterHolder = new FilterHolder(); final var contexts = new ContextHandlerCollection(); serverBuilder.addCommonEventListener(new EventListener(){}); diff --git a/lighty-modules/lighty-openapi/src/main/java/io/lighty/openapi/OpenApiLighty.java b/lighty-modules/lighty-openapi/src/main/java/io/lighty/openapi/OpenApiLighty.java index 0b1d2caac0..7b8afae89c 100644 --- a/lighty-modules/lighty-openapi/src/main/java/io/lighty/openapi/OpenApiLighty.java +++ b/lighty-modules/lighty-openapi/src/main/java/io/lighty/openapi/OpenApiLighty.java @@ -77,7 +77,7 @@ public Set getSingletons() { addStaticResources(mainHandler, "/explorer", "static-content"); LOG.info("adding context handler ..."); - jettyServerBuilder.addContextHandler(contexts); + jettyServerBuilder.addHandlerAtRuntime(contexts); return true; } diff --git a/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConf.java b/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConf.java index edb1013ca2..6cd4bcb10d 100644 --- a/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConf.java +++ b/lighty-modules/lighty-restconf-nb-community/src/main/java/io/lighty/modules/northbound/restconf/community/impl/CommunityRestConf.java @@ -62,7 +62,7 @@ public class CommunityRestConf extends AbstractLightyModule { private final int httpPort; private final InetAddress inetAddress; private final String restconfServletContextPath; - private Server jettyServer; + private JettyWebServer jettyServer; private LightyServerBuilder lightyServerBuilder; private JaxRsEndpoint jaxRsEndpoint; @@ -102,51 +102,19 @@ protected boolean initProcedure() throws ServletException { LOG.info("Starting RestconfApplication with configuration {}", streamsConfiguration); final MdsalDatabindProvider databindProvider = new MdsalDatabindProvider(domSchemaService); - final var server = new MdsalRestconfServer(databindProvider, domDataBroker, domRpcService, domActionService, - domMountPointService); - - this.jaxRsEndpoint = new JaxRsEndpoint(new JettyWebServer(httpPort), new LightyWebContextSecurer(), - new JerseyServletSupport(), new CustomFilterAdapterConfigurationImpl(), server, + final var server = new MdsalRestconfServer(databindProvider, domDataBroker, domRpcService, + domActionService, domMountPointService); + + this.jettyServer = this.lightyServerBuilder.build(); + this.jaxRsEndpoint = new JaxRsEndpoint( + jettyServer, + new LightyWebContextSecurer(), + new JerseyServletSupport(), + new CustomFilterAdapterConfigurationImpl(), + server, new MdsalRestconfStreamRegistry(new JaxRsLocationProvider(), domDataBroker), - JaxRsEndpoint.props(streamsConfiguration)); - - final ServletContainer servletContainer8040 = new ServletContainer(ResourceConfig - .forApplication(new Application() { - @Override - public Set getSingletons() { - return Set.of( - new JsonJaxRsFormattableBodyWriter(), new XmlJaxRsFormattableBodyWriter(), - new JaxRsRestconf(server, new MdsalRestconfStreamRegistry(new JaxRsLocationProvider(), - domDataBroker), jaxRsEndpoint, ErrorTagMapping.RFC8040, PrettyPrintParam.FALSE)); - } - })); - - final ServletHolder jaxrs = new ServletHolder(servletContainer8040); - - LOG.info("RestConf init complete, starting Jetty"); - LOG.info("http address:port {}:{}, url prefix: {}", this.inetAddress.toString(), this.httpPort, - this.restconfServletContextPath); - final InetSocketAddress inetSocketAddress = new InetSocketAddress(this.inetAddress, this.httpPort); - final ContextHandlerCollection contexts = new ContextHandlerCollection(); - final ServletContextHandler mainHandler = - new ServletContextHandler(contexts, this.restconfServletContextPath, true, false); - mainHandler.addServlet(jaxrs, "/*"); - - final ServletContextHandler rrdHandler = - new ServletContextHandler(contexts, "/.well-known", true, false); - final RootFoundApplication rootDiscoveryApp = new RootFoundApplication(restconfServletContextPath); - rrdHandler.addServlet(new ServletHolder(new ServletContainer(ResourceConfig - .forApplication(rootDiscoveryApp))), "/*"); - - boolean startDefault = false; - if (this.lightyServerBuilder == null) { - this.lightyServerBuilder = new LightyServerBuilder(inetSocketAddress); - startDefault = true; - } - this.lightyServerBuilder.addContextHandler(contexts); - if (startDefault) { - startServer(); - } + JaxRsEndpoint.props(streamsConfiguration) + ); LOG.info("Lighty RestConf started in {}", stopwatch.stop()); return true; @@ -183,11 +151,8 @@ protected boolean stopProcedure() { @SuppressWarnings("checkstyle:illegalCatch") public void startServer() { - if (this.jettyServer != null && !this.jettyServer.isStopped()) { - return; - } try { - this.jettyServer = this.lightyServerBuilder.build(); + lightyServerBuilder.build(); this.jettyServer.start(); } catch (final Exception e) { Throwables.throwIfUnchecked(e);