From 67c373c7d986b73e03ffb137f860298c891cdc0a Mon Sep 17 00:00:00 2001 From: gregw Date: Thu, 20 Aug 2020 10:29:06 +0200 Subject: [PATCH 01/11] Issue #5162 CDI embedded integration improvements Clean up CDI integration and documentation to better support embedded usage. + made listener public + added utility class for SCIs --- .../jetty/cdi/CdiDecoratingListener.java | 3 +- .../cdi/CdiServletContainerInitializer.java | 2 - .../eclipse/jetty/cdi/CdiSpiDecorator.java | 35 ++- .../asciidoc/development/frameworks/cdi.adoc | 133 ++++++++++ .../development/frameworks/chapter.adoc | 3 +- .../asciidoc/development/frameworks/weld.adoc | 228 ++++++++++------- .../plus/annotation/ContainerInitializer.java | 4 - .../jetty/servlet/ServletContextHandler.java | 65 ++++- .../jetty/tests/distribution/CDITests.java | 2 +- .../test-webapps/test-weld-cdi-webapp/pom.xml | 22 +- .../jetty/cdi/weld/EmbeddedWeldTest.java | 241 ++++++++++++++++++ 11 files changed, 627 insertions(+), 111 deletions(-) create mode 100644 jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc create mode 100644 tests/test-webapps/test-weld-cdi-webapp/src/test/java/org/eclipse/jetty/cdi/weld/EmbeddedWeldTest.java diff --git a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiDecoratingListener.java b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiDecoratingListener.java index 674591b8d80e..dbdafb889275 100644 --- a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiDecoratingListener.java +++ b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiDecoratingListener.java @@ -24,7 +24,7 @@ /** * A DecoratingListener that listens for "org.eclipse.jetty.cdi.decorator" */ -class CdiDecoratingListener extends DecoratingListener +public class CdiDecoratingListener extends DecoratingListener { public static final String MODE = "CdiDecoratingListener"; public static final String ATTRIBUTE = "org.eclipse.jetty.cdi.decorator"; @@ -32,5 +32,6 @@ class CdiDecoratingListener extends DecoratingListener public CdiDecoratingListener(ServletContextHandler contextHandler) { super(contextHandler, ATTRIBUTE); + contextHandler.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE); } } diff --git a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiServletContainerInitializer.java b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiServletContainerInitializer.java index ef99c3aa3587..3a72a740525e 100644 --- a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiServletContainerInitializer.java +++ b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiServletContainerInitializer.java @@ -86,8 +86,6 @@ public void onStartup(Set> c, ServletContext ctx) default: throw new IllegalStateException(mode); } - - context.setAttribute(CDI_INTEGRATION_ATTRIBUTE, mode); LOG.info(mode + " enabled in " + ctx); } catch (UnsupportedOperationException | ClassNotFoundException e) diff --git a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java index 4198a49407d8..ad6983e38602 100644 --- a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java +++ b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java @@ -65,7 +65,10 @@ public class CdiSpiDecorator implements Decorator public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperationException { _context = context; + context.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE); ClassLoader classLoader = _context.getClassLoader(); + if (classLoader == null) + classLoader = this.getClass().getClassLoader(); try { @@ -92,6 +95,35 @@ public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperatio } } + /** + * Test if a class can be decorated. + * @implNote The default implementation calls {@link #isKnownUndecoratable(String) } + * on the class and all it's super classes. + * @param clazz The class to check + * @return True if the class and all it's super classes can be decorated + */ + protected boolean isDecoratable(Class clazz) + { + if (Object.class == clazz) + return true; + if (isKnownUndecoratable(clazz.getName())) + return false; + return isDecoratable(clazz.getSuperclass()); + } + + /** + * Test if a specific class name is known to not be decoratable. + * @implNote default implementation checks for well known classes that are used to + * setup CDI itself, and thus cannot themselves be decorated. + * @see #isDecoratable(Class) + * @param className The name of the class to check + * @return True if the class is known not to be decoratable + */ + protected boolean isKnownUndecoratable(String className) + { + return "org.jboss.weld.environment.servlet.Listener".equals(className); + } + /** * Decorate an object. *

The signature of this method must match what is introspected for by the @@ -108,7 +140,8 @@ public T decorate(T o) if (LOG.isDebugEnabled()) LOG.debug("decorate {} in {}", o, _context); - _decorated.put(o, new Decorated(o)); + if (isDecoratable(o.getClass())) + _decorated.put(o, new Decorated(o)); } catch (Throwable th) { diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc new file mode 100644 index 000000000000..b586db93e5a8 --- /dev/null +++ b/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc @@ -0,0 +1,133 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// ======================================================================== +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +[[framework-cdi]] +=== CDI + +Contexts and Dependency Injection for Java EE (http://www.cdi-spec.org/[CDI]) +is a standard implemented by frameworks such as +http://seamframework.org/Weld[Weld] and +https://openwebbeans.apache.org/[Apache OpenWebBeans]. This is a common way to +assemble and configure webapplications by a process often referred to as +'decoration'. + +Jetty integration of CDI frameworks allows CDI to be used to inject the +Filters, Servlets and Listeners created within a Servlet Context. There are +two approaches to integration: + + * CDI implementation can integrate with Jetty. This requires the CDI + implementation to have Jetty specific code. Since Jetty-9.4.20 a loosely + bound mechanism has been available for CDI implementations to extends + the Jetty `DecoratedObjectFactory` without hard API dependencies. + Prior to that, CDI implementations directly called jetty APIs that need + to be explicitly exposed to the webapp. + + * Alternately, Jetty can integrate with CDI implementations by using standard + CDI SPIs. + +==== Jetty CDI Modules + +The Jetty distribution come with several CDI modules. These modules do not provide CDI, +but instead enable one of more integration mechanisms. + +===== Jetty `cdi` Module +The `cdi` module supports either two modes of CDI integration which can be +selected either by the "org.eclipse.jetty.cdi" context init parameter or +the "org.eclipse.jetty.cdi" server attribute (which is initialised from +the "jetty.cdi.mode" start property). Supported modes are: + + * `CdiSpiDecorator` Jetty will call the CDI SPI within the webapp to decorate +objects (default). + + * `CdiDecoratingLister` The webapp may register a decorator on the context attribute +"org.eclipse.jetty.cdi.decorator". +------------------------- +cd $JETTY_BASE +java -jar $JETTY_HOME/start.jar --add-to-start=cdi +------------------------- + + +===== Jetty `cdi-decorate` Module +This module depends on the `cdi` module and sets the default mode to `CdiDecoratingListener`. +This is the preferred mode for Weld integration. +------------------------- +cd $JETTY_BASE +java -jar $JETTY_HOME/start.jar --add-to-start=cdi-decorate +------------------------- + +===== Jetty `cdi-spi` Module +This module depends on the `cdi` module and sets the default mode to `CdiSpiDecorator`. +This is the preferred mode for Open Web Beans integration. +------------------------- +cd $JETTY_BASE +java -jar $JETTY_HOME/start.jar --add-to-start=cdi-spi +------------------------- + +===== Jetty `cdi2` Module +This module supports the *deprecated* technique of exposing private Jetty decorate APIs to the CDI +implementation in the webapp. + +------------------------- +cd $JETTY_BASE +java -jar $JETTY_HOME/start.jar --add-to-start=cdi2 +------------------------- + +This module is equivalent to directly modifying the class path configuration with a `jetty-web.xml` like: + +[source.XML, xml] +------------------------------------------------------------- + + + + + -org.eclipse.jetty.util.Decorator + + + -org.eclipse.jetty.util.DecoratedObjectFactory + + + -org.eclipse.jetty.server.handler.ContextHandler. + + + -org.eclipse.jetty.server.handler.ContextHandler + + + -org.eclipse.jetty.servlet.ServletContextHandler + + +------------------------------------------------------------- + +____ +[TIP] +The `cdi2` module or directly modifying the web application classpath will not work for Jetty 10.0.0 and later. +It should only be used for versions prior to Jetty 9.4.20 and/or Weld 3.1.2.Final +____ + + +[[cdi-embedded]] +==== Embedded Jetty with CDI +When starting embedded Jetty programmatically from the `main` method, to use CDI it may be +necessary: + * enable a Jetty CDI integration mode + * and/or enable a CDI frame integration. + +However, depending on the exact configuration of the embedded server, either or both steps +may not be required as Servlet Container Initializers may be discovered. + +The details for embedding CDI will be explain in the Embedded Jetty with Weld section, but +can be adapted to other CDI frameworks. diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/chapter.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/chapter.adoc index 0f21a388b3ff..65565050b5f0 100644 --- a/jetty-documentation/src/main/asciidoc/development/frameworks/chapter.adoc +++ b/jetty-documentation/src/main/asciidoc/development/frameworks/chapter.adoc @@ -19,7 +19,8 @@ [[frameworks]] == Frameworks +include::cdi.adoc[] +include::weld.adoc[] include::spring-usage.adoc[] include::osgi.adoc[] -include::weld.adoc[] include::metro.adoc[] diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc index bd937ab78746..c76e3dff3e67 100644 --- a/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc +++ b/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc @@ -26,124 +26,70 @@ It is easily configured with Jetty 9. ==== Weld Setup The easiest way to configure weld is within the Jetty distribution itself. -This can be accomplished either by enabling one of the startup link:#startup-modules[modules] for Weld, or by creating/editing a `jetty-web.xml` descriptor (see also https://www.eclipse.org/jetty/documentation/current/jetty-web-xml-config.html[Jetty XML Reference]). +This can be accomplished either by enabling one of the +startup link:#startup-modules[modules] described in link:#framework-cdi[CDI Framework]: -===== Jetty Weld Modules + * the `cdi-decorate` module is the preferred Weld integration. The activation +of this module by Weld can be confirmed by the following Weld log: +[source, screen, subs="{sub-order}"] +.... +INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters. +.... + * the `cdi-spi` module works with Weld, but may restrict some non standard features.The activation +of this module by Weld can be confirmed by the following Weld log: +[source, screen, subs="{sub-order}"] +.... +INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters. +.... + * the deprecated `cdi2` module works with Weld prior to 3.1.2.Final. The activation +of this module by Weld can be confirmed by the following Weld log: +[source, screen, subs="{sub-order}"] +.... +INFO: WELD-ENV-001201: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported. +.... -====== Jetty `cdi-decorate` Module -Since Jetty 9.4.20 and Weld 3.1.2.Final, the Weld/Jetty integration uses the jetty `cdi-decorate` module. -To activate this module in Jetty the `cdi-decorate` module needs activated on the command line, which can be done as follows: +To activate the preferred `cdi-decorate` module use: ------------------------- cd $JETTY_BASE java -jar $JETTY_HOME/start.jar --add-to-start=cdi-decorate ------------------------- -====== Jetty `cdi2` Module - -For versions prior to Jetty 9.4.20 and Weld 3.1.2, the Weld/Jetty integration required some internal Jetty APIs to be made visible to the web application. -This can be done using the deprecated `cdi2` module either by activating the `cdi2` module: - -------------------------- -cd $JETTY_BASE -java -jar $JETTY_HOME/start.jar --add-to-start=cdi2 -------------------------- - - -===== Jetty-Web XML - - -[source.XML, xml] -------------------------------------------------------------- - - - - - -org.eclipse.jetty.util.Decorator - - - -org.eclipse.jetty.util.DecoratedObjectFactory - - - -org.eclipse.jetty.server.handler.ContextHandler. - - - -org.eclipse.jetty.server.handler.ContextHandler - - - -org.eclipse.jetty.servlet.ServletContextHandler - - -------------------------------------------------------------- ____ [TIP] -Directly modifying the web application classpath via `jetty-web.xml` will not work for Jetty 10.0.0 and later. +For use with the jetty-maven-plugin, the best idea is to make the org.jboss.weld.servlet:weld-servlet and jetty-cdi artifacts _plugin_ dependencies (__not__ a webapp dependency). ____ -===== Jetty `cdi-spi` Module -Since Jetty 9.4.20 the Jetty `cdi-spi` module has been available that integrates any compliant CDI implementation by directly calling the CDI SPI. -Since Weld support specific Jetty integration, it is not recommended to use this module with Weld. +[[weld-embedded]] +==== Embedded Jetty -When you start up your Jetty distribution with the webapp you should see output similar to the following (providing your logging is the default configuration): +When starting embedded Jetty programmatically from the `main` method it is necessary to: -[source, screen, subs="{sub-order}"] -.... -2015-06-18 12:13:54.924:INFO::main: Logging initialized @485ms -2015-06-18 12:13:55.231:INFO:oejs.Server:main: jetty-{VERSION} -2015-06-18 12:13:55.264:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/cdi-demo/webapps/] at interval 1 -2015-06-18 12:13:55.607:WARN:oeja.AnnotationConfiguration:main: ServletContainerInitializers: detected. Class hierarchy: empty -Jun 18, 2015 12:13:55 PM org.jboss.weld.environment.servlet.EnhancedListener onStartup -INFO: WELD-ENV-001008: Initialize Weld using ServletContainerInitializer -Jun 18, 2015 12:13:55 PM org.jboss.weld.bootstrap.WeldStartup -INFO: WELD-000900: 2.2.9 (Final) -Jun 18, 2015 12:13:55 PM org.jboss.weld.environment.servlet.deployment.WebAppBeanArchiveScanner scan -WARN: WELD-ENV-001004: Found both WEB-INF/beans.xml and WEB-INF/classes/META-INF/beans.xml. It's not portable to use both locations at the same time. Weld is going to use file:/tmp/jetty-0.0.0.0-8080-cdi-webapp.war-_cdi-webapp-any-8161614308407422636.dir/webapp/WEB-INF/beans.xml. -Jun 18, 2015 12:13:55 PM org.jboss.weld.bootstrap.WeldStartup startContainer -INFO: WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously. -Jun 18, 2015 12:13:55 PM org.jboss.weld.interceptor.util.InterceptionTypeRegistry -WARN: WELD-001700: Interceptor annotation class javax.ejb.PostActivate not found, interception based on it is not enabled -Jun 18, 2015 12:13:55 PM org.jboss.weld.interceptor.util.InterceptionTypeRegistry -WARN: WELD-001700: Interceptor annotation class javax.ejb.PrePassivate not found, interception based on it is not enabled -Jun 18, 2015 12:13:56 PM org.jboss.weld.bootstrap.MissingDependenciesRegistry handleResourceLoadingException -Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.WeldServletLifecycle findContainer -INFO: WELD-ENV-001002: Container detection skipped - custom container class loaded: org.jboss.weld.environment.jetty.JettyContainer. -Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.jetty.JettyContainer initialize -INFO: WELD-ENV-001200: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners should work on Jetty 9.1.1 and newer. -Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.Listener contextInitialized -INFO: WELD-ENV-001006: org.jboss.weld.environment.servlet.EnhancedListener used for ServletContext notifications -Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.EnhancedListener contextInitialized -INFO: WELD-ENV-001009: org.jboss.weld.environment.servlet.Listener used for ServletRequest and HttpSession notifications -2015-06-18 12:13:56.535:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@6574b225{/cdi-webapp,file:///tmp/jetty-0.0.0.0-8080-cdi-webapp.war-_cdi-webapp-any-8161614308407422636.dir/webapp/,AVAILABLE}{/cdi-webapp.war} -2015-06-18 12:13:56.554:INFO:oejs.ServerConnector:main: Started ServerConnector@7112f81c{HTTP/1.1,[http/1.1]}{0.0.0.0:8080} -2015-06-18 12:13:56.587:INFO:oejus.SslContextFactory:main: x509={jetty.eclipse.org=jetty} wild={} alias=null for SslContextFactory@3214ee6(file:///tmp/cdi-demo/etc/keystore,file:///tmp/cdi-demo/etc/keystore) -2015-06-18 12:13:56.821:INFO:oejs.ServerConnector:main: Started ServerConnector@69176a9b{SSL,[ssl, http/1.1]}{0.0.0.0:8443} -2015-06-18 12:13:56.822:INFO:oejs.Server:main: Started @2383ms + * enable a jetty CDI integration mode by registering a `Listener` or `ServletContainerInitializer` -.... + * enable Weld by registering either its `Listener` or `ServletContainerInitializer` -For use with the jetty-maven-plugin, the best idea is to make the org.jboss.weld.servlet:weld-servlet artifact a _plugin_ dependency (__not__ a webapp dependency). -[[weld-embedded]] -==== Embedded Jetty +===== Using a `ServletContextHandler` -When starting embedded Jetty programmatically from the `main` method it is necessary to register Weld's listener: +Embedded usage often uses a `ServletContextHandler` which is the base class of `WebappContext` and +lacks the features of "web.xml" configuration and must be configured directly. The examples in this section +based on a server and context set up as follows: [source.JAVA, java] ---- public class Main { - public static void main(String[] args) throws Exception { Server jetty = new Server(8080); - WebAppContext context = new WebAppContext(); + ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); - context.setResourceBase("src/main/resources"); - jetty.setHandler(context); + server.setHandler(context); + context.addServlet(HelloWorldServlet.class, "/*"); - context.addEventListener(new DecoratingListener()); # <1> - context.addEventListener(new Listener()); # <2> + /* CDI enabling goes here. See options below */ jetty.start(); jetty.join(); @@ -159,7 +105,107 @@ public class Main { } } } +---- + +====== Initialize Weld with `ServletContainerInitializers` +The best way to initialize both Jetty Weld integration is to use their respective `ServletContainerInitializers`: +[source.JAVA, java] +---- + import org.eclipse.jetty.cdi.CdiServletContainerInitializer; + import org.eclipse.jetty.cdi.CdiDecoratingListener; + import org.jboss.weld.environment.servlet.EnhancedListener; + // ... + context.setInitParameter( + CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, + CdiDecoratingListener.MODE); + context.addBean(new ServletContextHandler.Initializer(context, + new EnhancedListener())); + context.addBean(new ServletContextHandler.Initializer(context, + new CdiServletContainerInitializer())); +---- +This code uses the `ServletContextHandler.Initializer` utility class added in Jetty-9.4.30. Prior +to that the same effect can be achieved with a custom implementation of +`ServletContextHandler.ServletContainerInitializerCaller`. + +====== Initialize Weld with Listeners +Jetty Weld integration can also be initialized by directly adding the listeners +required: +[source.JAVA, java] +---- + import org.eclipse.jetty.cdi.CdiDecoratingListener; + import org.jboss.weld.environment.servlet.Listener; + // ... + context.addEventListener(new CdiDecoratingListener(context)); + context.addEventListener(new Listener()); +---- + +====== Other Weld initializations +When running embedded without a context classloader, it is not actually required +to initialize Jetty at all. If just Weld is initialized then it will disover the +Jetty APIs and use the deprecated integration: +[source.JAVA, java] +---- + import org.jboss.weld.environment.servlet.Listener; + // ... + context.addEventListener(new Listener()); +---- +However, this results in only a partially functional integration and the +following warning: +---- +INFO: WELD-ENV-001201: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported. +---- + +Jetty can also be initialized by adding the `org.eclipse.jetty.webapp.DecoratingListener` listener instead +of the `org.eclipse.jetty.cdi.CdiDecoratingListener`. However, this introduces a needless +dependency on `jetty-webapp` and is not the preferred method. + +====== Initialize Weld with `WebappContext` +Some embedded usage still makes use of the `WebappContext` class for the convention-over-configuration +benefits. The methods described for `ServletContextHandler` will work for `WebappContext`: -<1> Jetty's `org.eclipse.jetty.webapp.DecoratingListener` registered programmatically (since Jetty-9.4.20) -<2> Weld's `org.jboss.weld.environment.servlet.Listener` registered programmatically +[source.JAVA, java] +---- + import org.eclipse.jetty.cdi.CdiServletContainerInitializer; + import org.eclipse.jetty.cdi.CdiDecoratingListener; + import org.jboss.weld.environment.servlet.EnhancedListener; + // ... + Server server = new Server(8080); + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + server.setHandler(webapp); + + webapp.setInitParameter( + CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, + CdiDecoratingListener.MODE); + webapp.addBean(new ServletContextHandler.Initializer(webapp, + new CdiServletContainerInitializer())); + webapp.addBean(new ServletContextHandler.Initializer(webapp, + new EnhancedListener())); + + // ... +---- + +Alternately the webapp can be configured to discover the SCIs: + +[source.JAVA, java] ---- + Server server = new Server(8080); + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + server.setHandler(webapp); + + webapp.setInitParameter( + CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, + CdiDecoratingListener.MODE); + + // Need the AnnotationConfiguration to detect SCIs + Configuration.ClassList.setServerDefault(server).addBefore( + JettyWebXmlConfiguration.class.getName(), + AnnotationConfiguration.class.getName()); + + // Need to expose our SCI class. + webapp.getServerClasspathPattern().add("-" + CdiServletContainerInitializer.class.getName()); + webapp.getSystemClasspathPattern().add(CdiServletContainerInitializer.class.getName()); + + // ... +---- \ No newline at end of file diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java index 75c858eebba3..91acd211eba5 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java @@ -124,9 +124,6 @@ public void callStartup(WebAppContext context) { Set> classes = new HashSet>(); - ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(context.getClassLoader()); - try { for (String s : _applicableTypeNames) @@ -147,7 +144,6 @@ public void callStartup(WebAppContext context) finally { context.getServletContext().setExtendedListenerTypes(false); - Thread.currentThread().setContextClassLoader(oldLoader); } } } diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java index 8d4ac4c6dd43..2793b5cc7712 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java @@ -34,6 +34,7 @@ import javax.servlet.FilterRegistration; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; +import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; @@ -68,6 +69,7 @@ import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -336,9 +338,15 @@ protected ServletHandler newServletHandler() @Override protected void startContext() throws Exception { - ServletContainerInitializerCaller sciBean = getBean(ServletContainerInitializerCaller.class); - if (sciBean != null) - sciBean.start(); + for (ServletContainerInitializerCaller sci : getBeans(ServletContainerInitializerCaller.class)) + { + if (sci.isStopped()) + { + sci.start(); + if (isAuto(sci)) + manage(sci); + } + } if (_servletHandler != null) { @@ -749,13 +757,13 @@ public static ServletContextHandler getServletContextHandler(ServletContext cont public static class JspPropertyGroup implements JspPropertyGroupDescriptor { - private List _urlPatterns = new ArrayList<>(); + private final List _urlPatterns = new ArrayList<>(); private String _elIgnored; private String _pageEncoding; private String _scriptingInvalid; private String _isXml; - private List _includePreludes = new ArrayList<>(); - private List _includeCodas = new ArrayList<>(); + private final List _includePreludes = new ArrayList<>(); + private final List _includeCodas = new ArrayList<>(); private String _deferredSyntaxAllowedAsLiteral; private String _trimDirectiveWhitespaces; private String _defaultContentType; @@ -1001,8 +1009,8 @@ public String toString() public static class JspConfig implements JspConfigDescriptor { - private List _taglibs = new ArrayList<>(); - private List _jspPropertyGroups = new ArrayList<>(); + private final List _taglibs = new ArrayList<>(); + private final List _jspPropertyGroups = new ArrayList<>(); public JspConfig() { @@ -1474,7 +1482,7 @@ public interface Decorator extends org.eclipse.jetty.util.Decorator */ private static class LegacyDecorator implements Decorator { - private org.eclipse.jetty.util.Decorator decorator; + private final org.eclipse.jetty.util.Decorator decorator; public LegacyDecorator(org.eclipse.jetty.util.Decorator decorator) { @@ -1493,4 +1501,43 @@ public void destroy(Object o) decorator.destroy(o); } } + + /** + * A utility class to hold a {@link ServletContainerInitializer} and implement the + * {@link ServletContainerInitializerCaller} interface so that the SCI is correctly + * started if an instance of this class is added as a bean to a {@link ServletContextHandler}. + */ + public static class Initializer extends AbstractLifeCycle implements ServletContainerInitializerCaller + { + private final ServletContextHandler _context; + private final ServletContainerInitializer _sci; + private final Set> _classes; + + public Initializer(ServletContextHandler context, ServletContainerInitializer sci, Set> classes) + { + _context = context; + _sci = sci; + _classes = classes; + } + + public Initializer(ServletContextHandler context, ServletContainerInitializer sci) + { + this(context, sci, Collections.emptySet()); + } + + @Override + protected void doStart() throws Exception + { + boolean oldExtended = _context.getServletContext().isExtendedListenerTypes(); + try + { + _context.getServletContext().setExtendedListenerTypes(true); + _sci.onStartup(_classes, _context.getServletContext()); + } + finally + { + _context.getServletContext().setExtendedListenerTypes(oldExtended); + } + } + } } diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java index fd5ef687a16f..ebdac3c9d836 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java @@ -61,7 +61,7 @@ public static Stream tests() Arguments.of("weld", "cdi2", null), Arguments.of("weld", "cdi-spi", null), // Weld >= 3.1.2 Arguments.of("weld", "decorate", null), // Weld >= 3.1.2 - // TODO Arguments.of("weld", "cdi-decorate", null), // Weld >= 3.1.3 + Arguments.of("weld", "cdi-decorate", null), // Weld >= 3.1.3 // -- Apache OpenWebBeans -- Arguments.of("owb", "cdi-spi", null), diff --git a/tests/test-webapps/test-weld-cdi-webapp/pom.xml b/tests/test-webapps/test-weld-cdi-webapp/pom.xml index 00f6f2ab45df..2ee653ba8be6 100644 --- a/tests/test-webapps/test-weld-cdi-webapp/pom.xml +++ b/tests/test-webapps/test-weld-cdi-webapp/pom.xml @@ -26,7 +26,7 @@ test-cdi-common-webapp ${project.version} war - runtime + test @@ -35,5 +35,25 @@ weld-servlet-core ${weld.version} + + + + org.eclipse.jetty + jetty-servlet + ${project.version} + test + + + org.eclipse.jetty + jetty-cdi + ${project.version} + test + + + org.eclipse.jetty + jetty-annotations + ${project.version} + test + diff --git a/tests/test-webapps/test-weld-cdi-webapp/src/test/java/org/eclipse/jetty/cdi/weld/EmbeddedWeldTest.java b/tests/test-webapps/test-weld-cdi-webapp/src/test/java/org/eclipse/jetty/cdi/weld/EmbeddedWeldTest.java new file mode 100644 index 000000000000..31818b656fbb --- /dev/null +++ b/tests/test-webapps/test-weld-cdi-webapp/src/test/java/org/eclipse/jetty/cdi/weld/EmbeddedWeldTest.java @@ -0,0 +1,241 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.cdi.weld; + +import java.io.IOException; +import java.util.EnumSet; +import javax.enterprise.inject.spi.BeanManager; +import javax.inject.Inject; +import javax.servlet.DispatcherType; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.cdi.CdiServletContainerInitializer; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ListenerHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.test.GreetingsServlet; +import org.eclipse.jetty.test.MyContextListener; +import org.eclipse.jetty.test.ServerIDFilter; +import org.eclipse.jetty.webapp.Configuration; +import org.eclipse.jetty.webapp.JettyWebXmlConfiguration; +import org.eclipse.jetty.webapp.WebAppContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +public class EmbeddedWeldTest +{ + public static Server createServerWithServletContext(int mode) + { + Server server = new Server(); + server.addConnector(new LocalConnector(server)); + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + server.setHandler(context); + + // Setup context + context.addServlet(GreetingsServlet.class, "/"); + context.addServlet(BeanServlet.class, "/beans"); + context.addFilter(ServerIDFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + + // Setup Jetty weld integration + switch (mode) + { + case 0: // Do nothing, let weld work it out. + // Expect:INFO: WELD-ENV-001201: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported. + context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); + break; + + case 1: // Deprecated use of Decorating Listener + // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters. + context.addEventListener(new org.eclipse.jetty.webapp.DecoratingListener(context)); + context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); + break; + + case 2: // CDI Decorating Listener + // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters. + context.addEventListener(new org.eclipse.jetty.cdi.CdiDecoratingListener(context)); + context.addEventListener(new org.jboss.weld.environment.servlet.Listener()); + break; + + case 3: // CDI SPI + // Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters. + context.getObjectFactory().addDecorator(new org.eclipse.jetty.cdi.CdiSpiDecorator(context)); + context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); + break; + + case 4: // SCI invocation with no mode selected + // Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters. + context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); + context.addEventListener(new org.jboss.weld.environment.servlet.Listener()); + // context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); + break; + + case 5: // SCI invocation with mode selected + // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters + context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE); + context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); + context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); + break; + + case 6: // direct SCI invocation of jetty and Weld SCI + // Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters. + context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener())); + context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); + + // Can decorate MyContextListener in this setup + context.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + break; + + case 7: // direct SCI invocation of jetty and Weld SCI with mode selected + // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters + context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE); + context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener())); + context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); + + // Can decorate MyContextListener in this setup + context.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + break; + + case 8: // direct SCI invocation of jetty and Weld SCI with mode selected - check order independent + // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters + context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE); + context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); + context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener())); + + // Can decorate MyContextListener in this setup + context.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + break; + } + + return server; + } + + @ParameterizedTest() + @ValueSource(ints = {0, 1, 2, 3, 4, 5, 6, 7, 8}) + public void testServletContext(int mode) throws Exception + { + Server server = createServerWithServletContext(mode); + server.start(); + LocalConnector connector = server.getBean(LocalConnector.class); + String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n"); + assertThat(response, containsString("HTTP/1.1 200 OK")); + assertThat(response, containsString("Hello GreetingsServlet")); + if (mode >= 6) + assertThat(response, containsString(" from CDI-Demo-org.eclipse.jetty.test")); + + response = connector.getResponse("GET /beans HTTP/1.0\r\n\r\n"); + assertThat(response, containsString("Beans from Weld BeanManager for ")); + + server.stop(); + } + + @Test + public void testWebappContext() throws Exception + { + Server server = new Server(8080); + server.addConnector(new LocalConnector(server)); + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + webapp.setResourceBase("src/test/resources"); + server.setHandler(webapp); + + webapp.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE); + webapp.addBean(new ServletContextHandler.Initializer(webapp, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); + webapp.addBean(new ServletContextHandler.Initializer(webapp, new org.jboss.weld.environment.servlet.EnhancedListener())); + + // This is ugly but needed for maven for testing in a overlaid war pom + webapp.getServerClasspathPattern().add("-org.eclipse.jetty.test."); + webapp.getSystemClasspathPattern().add("org.eclipse.jetty.test."); + + webapp.addServlet(GreetingsServlet.class, "/"); + webapp.addFilter(ServerIDFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + + server.start(); + + LocalConnector connector = server.getBean(LocalConnector.class); + String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n"); + System.err.println(response); + assertThat(response, containsString("HTTP/1.1 200 OK")); + assertThat(response, containsString("Hello GreetingsServlet")); + assertThat(response, containsString(" from CDI-Demo-org.eclipse.jetty.test")); + server.stop(); + + } + + @Test + public void testWebappContextDiscovered() throws Exception + { + Server server = new Server(8080); + server.addConnector(new LocalConnector(server)); + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + webapp.setResourceBase("src/test/resources"); + server.setHandler(webapp); + + // Need the AnnotationConfiguration to detect SCIs + Configuration.ClassList.setServerDefault(server).addBefore(JettyWebXmlConfiguration.class.getName(), + AnnotationConfiguration.class.getName()); + + // Need to expose our SCI. This is ugly could be made better in jetty-10 with a CdiConfiguration + webapp.getServerClasspathPattern().add("-" + CdiServletContainerInitializer.class.getName()); + webapp.getSystemClasspathPattern().add(CdiServletContainerInitializer.class.getName()); + + // This is ugly but needed for maven for testing in a overlaid war pom + webapp.getServerClasspathPattern().add("-org.eclipse.jetty.test."); + webapp.getSystemClasspathPattern().add("org.eclipse.jetty.test."); + + webapp.addServlet(GreetingsServlet.class, "/"); + webapp.addFilter(ServerIDFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + + server.start(); + + LocalConnector connector = server.getBean(LocalConnector.class); + String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n"); + System.err.println(response); + assertThat(response, containsString("HTTP/1.1 200 OK")); + assertThat(response, containsString("Hello GreetingsServlet")); + assertThat(response, containsString(" from CDI-Demo-org.eclipse.jetty.test")); + server.stop(); + + } + + public static class BeanServlet extends HttpServlet + { + @Inject + BeanManager manager; + + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.setContentType("text/plain"); + resp.getWriter().append("Beans from " + manager); + } + } + +} From ed0b344a1cfd99b8092fcb99cc394946181be28c Mon Sep 17 00:00:00 2001 From: gregw Date: Mon, 24 Aug 2020 12:47:58 +0200 Subject: [PATCH 02/11] Issue #5162 CDI embedded integration improvements Clean up CDI integration and documentation to better support embedded usage. + moved EmbeddedWeldTest to jetty-embedded --- examples/embedded/pom.xml | 12 ++ .../jetty/embedded}/EmbeddedWeldTest.java | 199 +++++++++++++----- .../src/test/resources/META-INF/beans.xml | 4 + .../jetty/util/component/Container.java | 13 +- .../util/component/ContainerLifeCycle.java | 10 - 5 files changed, 169 insertions(+), 69 deletions(-) rename {tests/test-webapps/test-weld-cdi-webapp/src/test/java/org/eclipse/jetty/cdi/weld => examples/embedded/src/test/java/org/eclipse/jetty/embedded}/EmbeddedWeldTest.java (65%) create mode 100644 examples/embedded/src/test/resources/META-INF/beans.xml diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml index 8bef9fafb889..38a9012625e3 100644 --- a/examples/embedded/pom.xml +++ b/examples/embedded/pom.xml @@ -50,6 +50,11 @@ jetty-jmx ${project.version} + + org.eclipse.jetty + jetty-cdi + ${project.version} + org.eclipse.jetty.websocket javax-websocket-server-impl @@ -153,6 +158,13 @@ tar.gz test + + org.jboss.weld.servlet + weld-servlet-core + ${weld.version} + test + + diff --git a/tests/test-webapps/test-weld-cdi-webapp/src/test/java/org/eclipse/jetty/cdi/weld/EmbeddedWeldTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/EmbeddedWeldTest.java similarity index 65% rename from tests/test-webapps/test-weld-cdi-webapp/src/test/java/org/eclipse/jetty/cdi/weld/EmbeddedWeldTest.java rename to examples/embedded/src/test/java/org/eclipse/jetty/embedded/EmbeddedWeldTest.java index 31818b656fbb..5c2dc1a1ba22 100644 --- a/tests/test-webapps/test-weld-cdi-webapp/src/test/java/org/eclipse/jetty/cdi/weld/EmbeddedWeldTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/EmbeddedWeldTest.java @@ -16,14 +16,24 @@ // ======================================================================== // -package org.eclipse.jetty.cdi.weld; +package org.eclipse.jetty.embedded; import java.io.IOException; import java.util.EnumSet; +import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionPoint; import javax.inject.Inject; +import javax.inject.Named; import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -34,9 +44,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ListenerHolder; import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.test.GreetingsServlet; -import org.eclipse.jetty.test.MyContextListener; -import org.eclipse.jetty.test.ServerIDFilter; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.JettyWebXmlConfiguration; import org.eclipse.jetty.webapp.WebAppContext; @@ -49,86 +56,77 @@ public class EmbeddedWeldTest { - public static Server createServerWithServletContext(int mode) + public static Server createServerWithServletContext(String mode) { Server server = new Server(); server.addConnector(new LocalConnector(server)); ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); + context.setResourceBase("src/test/resources/weldtest"); server.setHandler(context); // Setup context + context.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); context.addServlet(GreetingsServlet.class, "/"); - context.addServlet(BeanServlet.class, "/beans"); - context.addFilter(ServerIDFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + context.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); // Setup Jetty weld integration switch (mode) { - case 0: // Do nothing, let weld work it out. + case "none" : // Do nothing, let weld work it out. // Expect:INFO: WELD-ENV-001201: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported. context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); break; - case 1: // Deprecated use of Decorating Listener + case "DecoratingListener+Listener": // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters. context.addEventListener(new org.eclipse.jetty.webapp.DecoratingListener(context)); context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); break; - case 2: // CDI Decorating Listener + case "CdiDecoratingListener+Listener": // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters. context.addEventListener(new org.eclipse.jetty.cdi.CdiDecoratingListener(context)); context.addEventListener(new org.jboss.weld.environment.servlet.Listener()); break; - case 3: // CDI SPI + case "CdiSpiDecorator+Listener": // Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters. context.getObjectFactory().addDecorator(new org.eclipse.jetty.cdi.CdiSpiDecorator(context)); context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); break; - case 4: // SCI invocation with no mode selected + case "CdiServletContainerInitializer+Listener": // Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters. context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); context.addEventListener(new org.jboss.weld.environment.servlet.Listener()); - // context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); break; - case 5: // SCI invocation with mode selected + case "CdiServletContainerInitializer(CdiDecoratingListener)+Listener": // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE); context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); - context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class)); + context.addEventListener(new org.jboss.weld.environment.servlet.Listener()); break; - case 6: // direct SCI invocation of jetty and Weld SCI + case "CdiServletContainerInitializer+EnhancedListener": // Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters. - context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener())); context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); - - // Can decorate MyContextListener in this setup - context.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener())); break; - case 7: // direct SCI invocation of jetty and Weld SCI with mode selected + case "CdiServletContainerInitializer(CdiDecoratingListener)+EnhancedListener": // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE); - context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener())); context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); - - // Can decorate MyContextListener in this setup - context.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener())); break; - case 8: // direct SCI invocation of jetty and Weld SCI with mode selected - check order independent + case "EnhancedListener+CdiServletContainerInitializer(CdiDecoratingListener)": // Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE); - context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener())); - - // Can decorate MyContextListener in this setup - context.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); break; } @@ -136,19 +134,31 @@ public static Server createServerWithServletContext(int mode) } @ParameterizedTest() - @ValueSource(ints = {0, 1, 2, 3, 4, 5, 6, 7, 8}) - public void testServletContext(int mode) throws Exception + @ValueSource(strings = + { + "none", + "DecoratingListener+Listener", + "CdiDecoratingListener+Listener", + "CdiSpiDecorator+Listener", + "CdiServletContainerInitializer+Listener", + "CdiServletContainerInitializer(CdiDecoratingListener)+Listener", + "CdiServletContainerInitializer+EnhancedListener", + "CdiServletContainerInitializer(CdiDecoratingListener)+EnhancedListener" + }) + public void testServletContext(String mode) throws Exception { Server server = createServerWithServletContext(mode); server.start(); LocalConnector connector = server.getBean(LocalConnector.class); String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n"); assertThat(response, containsString("HTTP/1.1 200 OK")); - assertThat(response, containsString("Hello GreetingsServlet")); - if (mode >= 6) - assertThat(response, containsString(" from CDI-Demo-org.eclipse.jetty.test")); + assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager ")); + assertThat(response, containsString("Beans from Weld BeanManager ")); + if (mode.contains("EnhancedListener")) + assertThat(response, containsString("Listener saw Weld BeanManager")); + else + assertThat(response, containsString("Listener saw null")); - response = connector.getResponse("GET /beans HTTP/1.0\r\n\r\n"); assertThat(response, containsString("Beans from Weld BeanManager for ")); server.stop(); @@ -161,31 +171,29 @@ public void testWebappContext() throws Exception server.addConnector(new LocalConnector(server)); WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); - webapp.setResourceBase("src/test/resources"); + webapp.setResourceBase("src/test/resources/weldtest"); server.setHandler(webapp); webapp.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE); webapp.addBean(new ServletContextHandler.Initializer(webapp, new org.eclipse.jetty.cdi.CdiServletContainerInitializer())); webapp.addBean(new ServletContextHandler.Initializer(webapp, new org.jboss.weld.environment.servlet.EnhancedListener())); - // This is ugly but needed for maven for testing in a overlaid war pom - webapp.getServerClasspathPattern().add("-org.eclipse.jetty.test."); - webapp.getSystemClasspathPattern().add("org.eclipse.jetty.test."); + webapp.getServerClasspathPattern().add("-org.eclipse.jetty.embedded."); + webapp.getSystemClasspathPattern().add("org.eclipse.jetty.embedded."); webapp.addServlet(GreetingsServlet.class, "/"); - webapp.addFilter(ServerIDFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); server.start(); LocalConnector connector = server.getBean(LocalConnector.class); String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n"); - System.err.println(response); assertThat(response, containsString("HTTP/1.1 200 OK")); - assertThat(response, containsString("Hello GreetingsServlet")); - assertThat(response, containsString(" from CDI-Demo-org.eclipse.jetty.test")); + assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager ")); + assertThat(response, containsString("Beans from Weld BeanManager ")); + assertThat(response, containsString("Listener saw Weld BeanManager")); server.stop(); - } @Test @@ -195,7 +203,7 @@ public void testWebappContextDiscovered() throws Exception server.addConnector(new LocalConnector(server)); WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); - webapp.setResourceBase("src/test/resources"); + webapp.setResourceBase("src/test/resources/weldtest"); server.setHandler(webapp); // Need the AnnotationConfiguration to detect SCIs @@ -207,12 +215,12 @@ public void testWebappContextDiscovered() throws Exception webapp.getSystemClasspathPattern().add(CdiServletContainerInitializer.class.getName()); // This is ugly but needed for maven for testing in a overlaid war pom - webapp.getServerClasspathPattern().add("-org.eclipse.jetty.test."); - webapp.getSystemClasspathPattern().add("org.eclipse.jetty.test."); + webapp.getServerClasspathPattern().add("-org.eclipse.jetty.embedded."); + webapp.getSystemClasspathPattern().add("org.eclipse.jetty.embedded."); - webapp.addServlet(GreetingsServlet.class, "/"); - webapp.addFilter(ServerIDFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); + webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + webapp.addServlet(GreetingsServlet.class, "/"); server.start(); @@ -220,21 +228,104 @@ public void testWebappContextDiscovered() throws Exception String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n"); System.err.println(response); assertThat(response, containsString("HTTP/1.1 200 OK")); - assertThat(response, containsString("Hello GreetingsServlet")); - assertThat(response, containsString(" from CDI-Demo-org.eclipse.jetty.test")); + assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager ")); + assertThat(response, containsString("Beans from Weld BeanManager ")); + assertThat(response, containsString("Listener saw Weld BeanManager")); server.stop(); } - public static class BeanServlet extends HttpServlet + public static class MyContextListener implements ServletContextListener + { + @Inject + BeanManager manager; + + @Override + public void contextInitialized(ServletContextEvent sce) + { + sce.getServletContext().setAttribute("listener", manager); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + + } + } + + public static class MyFilter implements Filter + { + @Inject + BeanManager manager; + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + if (manager == null) + throw new IllegalStateException(); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException + { + // copy attribute from MyListener to see if it was decorated. + request.setAttribute("filter", manager); + chain.doFilter(request, response); + } + + @Override + public void destroy() + { + + } + } + + public static class GreetingsServlet extends HttpServlet { + @Inject + @Named("friendly") + public Greetings greetings; + @Inject BeanManager manager; + @Override + public void init() + { + if (manager == null) + throw new IllegalStateException(); + } + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); - resp.getWriter().append("Beans from " + manager); + resp.getWriter().print(greetings == null ? "NULL" : greetings.getGreeting()); + resp.getWriter().print(" filtered by "); + resp.getWriter().println(req.getAttribute("filter")); + resp.getWriter().println("Beans from " + manager); + resp.getWriter().println("Listener saw " + req.getServletContext().getAttribute("listener")); + } + } + + public interface Greetings + { + String getGreeting(); + } + + public static class FriendlyGreetings + { + @Produces + @Named("friendly") + public Greetings friendly(InjectionPoint ip) + { + return () -> "Hello " + ip.getMember().getDeclaringClass().getSimpleName(); + } + + @Produces + @Named("old") + public Greetings old() + { + return () -> "Salutations!"; } } diff --git a/examples/embedded/src/test/resources/META-INF/beans.xml b/examples/embedded/src/test/resources/META-INF/beans.xml new file mode 100644 index 000000000000..5fd351cfd218 --- /dev/null +++ b/examples/embedded/src/test/resources/META-INF/beans.xml @@ -0,0 +1,4 @@ + +o \ No newline at end of file diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java index 16936d43cef5..ff8010912c9f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java @@ -25,7 +25,6 @@ */ public interface Container { - /** * Add a bean. If the bean is-a {@link Listener}, then also do an implicit {@link #addEventListener(Listener)}. * @@ -35,7 +34,8 @@ public interface Container boolean addBean(Object o); /** - * @return the list of beans known to this aggregate + * @return the collection of beans known to this aggregate, in the order they were added. + * @implSpec The return type is ordered and should be a {@link java.util.List} * @see #getBean(Class) */ Collection getBeans(); @@ -43,7 +43,8 @@ public interface Container /** * @param clazz the class of the beans * @param the Bean type - * @return a list of beans of the given class (or subclass) + * @return a list of beans of the given class (or subclass), in the order they were added. + * @implSpec The return type is ordered and should be a {@link java.util.List} * @see #getBeans() * @see #getContainedBeans(Class) */ @@ -52,7 +53,7 @@ public interface Container /** * @param clazz the class of the bean * @param the Bean type - * @return the first bean of a specific class (or subclass), or null if no such bean exist + * @return the first bean (in order added) of a specific class (or subclass), or null if no such bean exist */ T getBean(Class clazz); @@ -138,7 +139,9 @@ interface InheritedListener extends Listener /** * @param clazz the class of the beans * @param the Bean type - * @return the list of beans of the given class from the entire Container hierarchy + * @return the list of beans of the given class from the entire Container hierarchy. The order is primarily depth first + * and secondarily added order. + * @implSpec The return type is ordered and should be a {@link java.util.List} */ Collection getContainedBeans(Class clazz); } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java index 3051a0a7dce8..5e80d437cce4 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java @@ -915,11 +915,6 @@ public void updateBeans(Object[] oldBeans, final Object[] newBeans) } } - /** - * @param clazz the class of the beans - * @param the Bean type - * @return the list of beans of the given class from the entire Container hierarchy - */ @Override public Collection getContainedBeans(Class clazz) { @@ -928,11 +923,6 @@ public Collection getContainedBeans(Class clazz) return beans; } - /** - * @param clazz the class of the beans - * @param the Bean type - * @param beans the collection to add beans of the given class from the entire Container hierarchy - */ protected void getContainedBeans(Class clazz, Collection beans) { beans.addAll(getBeans(clazz)); From b2fd5af3eddf4e0a6bdbd3169e6deb7590456e82 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 24 Aug 2020 16:36:00 +0200 Subject: [PATCH 03/11] fix javadoc Signed-off-by: Greg Wilkins --- .../java/org/eclipse/jetty/util/component/Container.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java index ff8010912c9f..02dd61939b0b 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java @@ -35,7 +35,7 @@ public interface Container /** * @return the collection of beans known to this aggregate, in the order they were added. - * @implSpec The return type is ordered and should be a {@link java.util.List} + * @apiNote The return type is ordered and should be a {@link java.util.List} * @see #getBean(Class) */ Collection getBeans(); @@ -44,7 +44,7 @@ public interface Container * @param clazz the class of the beans * @param the Bean type * @return a list of beans of the given class (or subclass), in the order they were added. - * @implSpec The return type is ordered and should be a {@link java.util.List} + * @apiNote The return type is ordered and should be a {@link java.util.List} * @see #getBeans() * @see #getContainedBeans(Class) */ @@ -141,7 +141,7 @@ interface InheritedListener extends Listener * @param the Bean type * @return the list of beans of the given class from the entire Container hierarchy. The order is primarily depth first * and secondarily added order. - * @implSpec The return type is ordered and should be a {@link java.util.List} + * @apiNote The return type is ordered and should be a {@link java.util.List} */ Collection getContainedBeans(Class clazz); } From e4d7712863b366208c0df6c2c7e5762ab6fe132d Mon Sep 17 00:00:00 2001 From: gregw Date: Mon, 24 Aug 2020 17:33:19 +0200 Subject: [PATCH 04/11] Issue #5162 CDI embedded integration improvements ventilated text --- .../asciidoc/development/frameworks/cdi.adoc | 60 +++++++------------ .../asciidoc/development/frameworks/weld.adoc | 49 ++++++--------- 2 files changed, 42 insertions(+), 67 deletions(-) diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc index b586db93e5a8..4b9e2bf65ef3 100644 --- a/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc +++ b/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc @@ -19,49 +19,36 @@ [[framework-cdi]] === CDI -Contexts and Dependency Injection for Java EE (http://www.cdi-spec.org/[CDI]) -is a standard implemented by frameworks such as -http://seamframework.org/Weld[Weld] and -https://openwebbeans.apache.org/[Apache OpenWebBeans]. This is a common way to -assemble and configure webapplications by a process often referred to as -'decoration'. - -Jetty integration of CDI frameworks allows CDI to be used to inject the -Filters, Servlets and Listeners created within a Servlet Context. There are -two approaches to integration: - - * CDI implementation can integrate with Jetty. This requires the CDI - implementation to have Jetty specific code. Since Jetty-9.4.20 a loosely - bound mechanism has been available for CDI implementations to extends - the Jetty `DecoratedObjectFactory` without hard API dependencies. - Prior to that, CDI implementations directly called jetty APIs that need - to be explicitly exposed to the webapp. - - * Alternately, Jetty can integrate with CDI implementations by using standard - CDI SPIs. +Contexts and Dependency Injection for Java EE (http://www.cdi-spec.org/[CDI]) is a standard implemented by frameworks such as http://seamframework.org/Weld[Weld] and https://openwebbeans.apache.org/[Apache OpenWebBeans]. +This is a common way to assemble and configure webapplications by a process often referred to as 'decoration'. + +Jetty integration of CDI frameworks allows CDI to be used to inject the Filters, Servlets and Listeners created within a Servlet Context. +There are two approaches to integration: + + * CDI implementation can integrate with Jetty. + This requires the CDI implementation to have Jetty specific code. + Since Jetty-9.4.20 a loosely bound mechanism has been available for CDI implementations to extends the Jetty `DecoratedObjectFactory` without hard API dependencies. + Prior to that, CDI implementations directly called jetty APIs that need to be explicitly exposed to the webapp. + + * Alternately, Jetty can integrate with CDI implementations by using standard CDI SPIs. ==== Jetty CDI Modules -The Jetty distribution come with several CDI modules. These modules do not provide CDI, -but instead enable one of more integration mechanisms. +The Jetty distribution come with several CDI modules. +These modules do not provide CDI, but instead enable one of more integration mechanisms. ===== Jetty `cdi` Module -The `cdi` module supports either two modes of CDI integration which can be -selected either by the "org.eclipse.jetty.cdi" context init parameter or -the "org.eclipse.jetty.cdi" server attribute (which is initialised from -the "jetty.cdi.mode" start property). Supported modes are: +The `cdi` module supports either two modes of CDI integration which can be selected either by the "org.eclipse.jetty.cdi" context init parameter or the "org.eclipse.jetty.cdi" server attribute (which is initialised from the "jetty.cdi.mode" start property). +Supported modes are: - * `CdiSpiDecorator` Jetty will call the CDI SPI within the webapp to decorate -objects (default). + * `CdiSpiDecorator` Jetty will call the CDI SPI within the webapp to decorate objects (default). - * `CdiDecoratingLister` The webapp may register a decorator on the context attribute -"org.eclipse.jetty.cdi.decorator". + * `CdiDecoratingLister` The webapp may register a decorator on the context attribute "org.eclipse.jetty.cdi.decorator". ------------------------- cd $JETTY_BASE java -jar $JETTY_HOME/start.jar --add-to-start=cdi ------------------------- - ===== Jetty `cdi-decorate` Module This module depends on the `cdi` module and sets the default mode to `CdiDecoratingListener`. This is the preferred mode for Weld integration. @@ -79,8 +66,7 @@ java -jar $JETTY_HOME/start.jar --add-to-start=cdi-spi ------------------------- ===== Jetty `cdi2` Module -This module supports the *deprecated* technique of exposing private Jetty decorate APIs to the CDI -implementation in the webapp. +This module supports the *deprecated* technique of exposing private Jetty decorate APIs to the CDI implementation in the webapp. ------------------------- cd $JETTY_BASE @@ -123,11 +109,11 @@ ____ ==== Embedded Jetty with CDI When starting embedded Jetty programmatically from the `main` method, to use CDI it may be necessary: + * enable a Jetty CDI integration mode + * and/or enable a CDI frame integration. -However, depending on the exact configuration of the embedded server, either or both steps -may not be required as Servlet Container Initializers may be discovered. +However, depending on the exact configuration of the embedded server, either or both steps may not be required as Servlet Container Initializers may be discovered. -The details for embedding CDI will be explain in the Embedded Jetty with Weld section, but -can be adapted to other CDI frameworks. +The details for embedding CDI will be explain in the Embedded Jetty with Weld section, but can be adapted to other CDI frameworks. diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc index c76e3dff3e67..23f9d382c686 100644 --- a/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc +++ b/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc @@ -26,37 +26,33 @@ It is easily configured with Jetty 9. ==== Weld Setup The easiest way to configure weld is within the Jetty distribution itself. -This can be accomplished either by enabling one of the -startup link:#startup-modules[modules] described in link:#framework-cdi[CDI Framework]: +This can be accomplished either by enabling one of the startup link:#startup-modules[modules] described in link:#framework-cdi[CDI Framework]: - * the `cdi-decorate` module is the preferred Weld integration. The activation -of this module by Weld can be confirmed by the following Weld log: + * the `cdi-decorate` module is the preferred Weld integration. + The activation of this module by Weld can be confirmed by the following Weld log: [source, screen, subs="{sub-order}"] .... INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters. .... - * the `cdi-spi` module works with Weld, but may restrict some non standard features.The activation -of this module by Weld can be confirmed by the following Weld log: + * the `cdi-spi` module works with Weld, but may restrict some non standard features. + The activation of this module by Weld can be confirmed by the following Weld log: [source, screen, subs="{sub-order}"] .... INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters. .... - * the deprecated `cdi2` module works with Weld prior to 3.1.2.Final. The activation -of this module by Weld can be confirmed by the following Weld log: + * the deprecated `cdi2` module works with Weld prior to 3.1.2.Final. + The activation of this module by Weld can be confirmed by the following Weld log: [source, screen, subs="{sub-order}"] .... INFO: WELD-ENV-001201: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported. .... - To activate the preferred `cdi-decorate` module use: - ------------------------- cd $JETTY_BASE java -jar $JETTY_HOME/start.jar --add-to-start=cdi-decorate ------------------------- - ____ [TIP] For use with the jetty-maven-plugin, the best idea is to make the org.jboss.weld.servlet:weld-servlet and jetty-cdi artifacts _plugin_ dependencies (__not__ a webapp dependency). @@ -71,12 +67,10 @@ When starting embedded Jetty programmatically from the `main` method it is neces * enable Weld by registering either its `Listener` or `ServletContainerInitializer` - ===== Using a `ServletContextHandler` -Embedded usage often uses a `ServletContextHandler` which is the base class of `WebappContext` and -lacks the features of "web.xml" configuration and must be configured directly. The examples in this section -based on a server and context set up as follows: +Embedded usage often uses a `ServletContextHandler` which is the base class of `WebappContext` and lacks the features of "web.xml" configuration and must be configured directly. +The examples in this section based on a server and context set up as follows: [source.JAVA, java] ---- @@ -123,13 +117,11 @@ The best way to initialize both Jetty Weld integration is to use their respectiv context.addBean(new ServletContextHandler.Initializer(context, new CdiServletContainerInitializer())); ---- -This code uses the `ServletContextHandler.Initializer` utility class added in Jetty-9.4.30. Prior -to that the same effect can be achieved with a custom implementation of -`ServletContextHandler.ServletContainerInitializerCaller`. +This code uses the `ServletContextHandler.Initializer` utility class added in Jetty-9.4.30. +Prior to that the same effect can be achieved with a custom implementation of `ServletContextHandler.ServletContainerInitializerCaller`. ====== Initialize Weld with Listeners -Jetty Weld integration can also be initialized by directly adding the listeners -required: +Jetty Weld integration can also be initialized by directly adding the listeners required: [source.JAVA, java] ---- import org.eclipse.jetty.cdi.CdiDecoratingListener; @@ -140,28 +132,25 @@ required: ---- ====== Other Weld initializations -When running embedded without a context classloader, it is not actually required -to initialize Jetty at all. If just Weld is initialized then it will disover the -Jetty APIs and use the deprecated integration: +When running embedded without a context classloader, it is not actually required to initialize Jetty at all. +If just Weld is initialized then it will disover the Jetty APIs and use the deprecated integration: [source.JAVA, java] ---- import org.jboss.weld.environment.servlet.Listener; // ... context.addEventListener(new Listener()); ---- -However, this results in only a partially functional integration and the -following warning: +However, this results in only a partially functional integration and the following warning: ---- INFO: WELD-ENV-001201: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported. ---- -Jetty can also be initialized by adding the `org.eclipse.jetty.webapp.DecoratingListener` listener instead -of the `org.eclipse.jetty.cdi.CdiDecoratingListener`. However, this introduces a needless -dependency on `jetty-webapp` and is not the preferred method. +Jetty can also be initialized by adding the `org.eclipse.jetty.webapp.DecoratingListener` listener instead of the `org.eclipse.jetty.cdi.CdiDecoratingListener`. +However, this introduces a needless dependency on `jetty-webapp` and is not the preferred method. ====== Initialize Weld with `WebappContext` -Some embedded usage still makes use of the `WebappContext` class for the convention-over-configuration -benefits. The methods described for `ServletContextHandler` will work for `WebappContext`: +Some embedded usage still makes use of the `WebappContext` class for the convention-over-configuration benefits. +The methods described for `ServletContextHandler` will work for `WebappContext`: [source.JAVA, java] ---- From f923d9b8b337d68243fb61ee719916faff5efcb8 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 24 Aug 2020 19:47:29 +0200 Subject: [PATCH 05/11] fix test pom Signed-off-by: Greg Wilkins --- .../test-webapps/test-weld-cdi-webapp/pom.xml | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/tests/test-webapps/test-weld-cdi-webapp/pom.xml b/tests/test-webapps/test-weld-cdi-webapp/pom.xml index 2ee653ba8be6..00f6f2ab45df 100644 --- a/tests/test-webapps/test-weld-cdi-webapp/pom.xml +++ b/tests/test-webapps/test-weld-cdi-webapp/pom.xml @@ -26,7 +26,7 @@ test-cdi-common-webapp ${project.version} war - test + runtime @@ -35,25 +35,5 @@ weld-servlet-core ${weld.version} - - - - org.eclipse.jetty - jetty-servlet - ${project.version} - test - - - org.eclipse.jetty - jetty-cdi - ${project.version} - test - - - org.eclipse.jetty - jetty-annotations - ${project.version} - test - From d3c4bd8c35f0e13fce939b429d260ec97400bdad Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 25 Aug 2020 12:37:31 +0200 Subject: [PATCH 06/11] Fixed javadoc --- .../main/java/org/eclipse/jetty/util/component/Container.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java index 02dd61939b0b..130afe4c4115 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Container.java @@ -35,7 +35,6 @@ public interface Container /** * @return the collection of beans known to this aggregate, in the order they were added. - * @apiNote The return type is ordered and should be a {@link java.util.List} * @see #getBean(Class) */ Collection getBeans(); @@ -44,7 +43,6 @@ public interface Container * @param clazz the class of the beans * @param the Bean type * @return a list of beans of the given class (or subclass), in the order they were added. - * @apiNote The return type is ordered and should be a {@link java.util.List} * @see #getBeans() * @see #getContainedBeans(Class) */ @@ -141,7 +139,6 @@ interface InheritedListener extends Listener * @param the Bean type * @return the list of beans of the given class from the entire Container hierarchy. The order is primarily depth first * and secondarily added order. - * @apiNote The return type is ordered and should be a {@link java.util.List} */ Collection getContainedBeans(Class clazz); } From 400838503972b3aa5accc7b9f1f9ab95dee0c658 Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 25 Aug 2020 14:12:46 +0200 Subject: [PATCH 07/11] Fixed javadoc --- .../src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java index ad6983e38602..dd63b9d76b27 100644 --- a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java +++ b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java @@ -97,7 +97,7 @@ public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperatio /** * Test if a class can be decorated. - * @implNote The default implementation calls {@link #isKnownUndecoratable(String) } + * The default implementation calls {@link #isKnownUndecoratable(String) } * on the class and all it's super classes. * @param clazz The class to check * @return True if the class and all it's super classes can be decorated @@ -113,7 +113,7 @@ protected boolean isDecoratable(Class clazz) /** * Test if a specific class name is known to not be decoratable. - * @implNote default implementation checks for well known classes that are used to + * The default implementation checks for well known classes that are used to * setup CDI itself, and thus cannot themselves be decorated. * @see #isDecoratable(Class) * @param className The name of the class to check From 9a6f48ce74c45f15348bff075f1e5342f9d5f535 Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 25 Aug 2020 15:26:39 +0200 Subject: [PATCH 08/11] Issue #5162 CDI embedded integration improvements Moved tests to jetty-cdi to avoid consequences to other tests in embedded --- examples/embedded/pom.xml | 12 ------------ jetty-cdi/pom.xml | 6 ++++++ .../org/eclipse/jetty/embedded/EmbeddedWeldTest.java | 0 .../src/test/resources/META-INF/beans.xml | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) rename {examples/embedded => jetty-cdi}/src/test/java/org/eclipse/jetty/embedded/EmbeddedWeldTest.java (100%) rename {examples/embedded => jetty-cdi}/src/test/resources/META-INF/beans.xml (96%) diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml index 38a9012625e3..8bef9fafb889 100644 --- a/examples/embedded/pom.xml +++ b/examples/embedded/pom.xml @@ -50,11 +50,6 @@ jetty-jmx ${project.version} - - org.eclipse.jetty - jetty-cdi - ${project.version} - org.eclipse.jetty.websocket javax-websocket-server-impl @@ -158,13 +153,6 @@ tar.gz test - - org.jboss.weld.servlet - weld-servlet-core - ${weld.version} - test - - diff --git a/jetty-cdi/pom.xml b/jetty-cdi/pom.xml index c06cc91e72a9..f1dd8a4b8a19 100644 --- a/jetty-cdi/pom.xml +++ b/jetty-cdi/pom.xml @@ -32,6 +32,12 @@ ${project.version} compile + + org.jboss.weld.servlet + weld-servlet-core + ${weld.version} + test + diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/EmbeddedWeldTest.java b/jetty-cdi/src/test/java/org/eclipse/jetty/embedded/EmbeddedWeldTest.java similarity index 100% rename from examples/embedded/src/test/java/org/eclipse/jetty/embedded/EmbeddedWeldTest.java rename to jetty-cdi/src/test/java/org/eclipse/jetty/embedded/EmbeddedWeldTest.java diff --git a/examples/embedded/src/test/resources/META-INF/beans.xml b/jetty-cdi/src/test/resources/META-INF/beans.xml similarity index 96% rename from examples/embedded/src/test/resources/META-INF/beans.xml rename to jetty-cdi/src/test/resources/META-INF/beans.xml index 5fd351cfd218..abb1108351b5 100644 --- a/examples/embedded/src/test/resources/META-INF/beans.xml +++ b/jetty-cdi/src/test/resources/META-INF/beans.xml @@ -1,4 +1,4 @@ -o \ No newline at end of file + \ No newline at end of file From 16bae47a73b4916928ade19fd00612d954150b50 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 26 Aug 2020 14:37:45 +0200 Subject: [PATCH 09/11] trailing new line Signed-off-by: Greg Wilkins --- jetty-cdi/src/test/resources/META-INF/beans.xml | 2 +- .../src/main/resources/META-INF/beans.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-cdi/src/test/resources/META-INF/beans.xml b/jetty-cdi/src/test/resources/META-INF/beans.xml index abb1108351b5..f85327832c60 100644 --- a/jetty-cdi/src/test/resources/META-INF/beans.xml +++ b/jetty-cdi/src/test/resources/META-INF/beans.xml @@ -1,4 +1,4 @@ - \ No newline at end of file + diff --git a/tests/test-webapps/test-cdi-common-webapp/src/main/resources/META-INF/beans.xml b/tests/test-webapps/test-cdi-common-webapp/src/main/resources/META-INF/beans.xml index abb1108351b5..f85327832c60 100644 --- a/tests/test-webapps/test-cdi-common-webapp/src/main/resources/META-INF/beans.xml +++ b/tests/test-webapps/test-cdi-common-webapp/src/main/resources/META-INF/beans.xml @@ -1,4 +1,4 @@ - \ No newline at end of file + From b665a6ed05d607f8f928f8084e5f4c613f0fe864 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 2 Sep 2020 12:08:26 +0200 Subject: [PATCH 10/11] updates from review Signed-off-by: Greg Wilkins --- .../eclipse/jetty/cdi/CdiSpiDecorator.java | 42 +++++++++++++++---- .../asciidoc/development/frameworks/cdi.adoc | 4 +- .../asciidoc/development/frameworks/weld.adoc | 2 +- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java index dd63b9d76b27..3b71043e3ca7 100644 --- a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java +++ b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java @@ -21,8 +21,12 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.Decorator; @@ -61,6 +65,7 @@ public class CdiSpiDecorator implements Decorator private final MethodHandle _inject; private final MethodHandle _dispose; private final MethodHandle _release; + private final Set _undecorated = new HashSet<>(Collections.singletonList("org.jboss.weld.environment.servlet.Listener")); public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperationException { @@ -97,7 +102,7 @@ public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperatio /** * Test if a class can be decorated. - * The default implementation calls {@link #isKnownUndecoratable(String) } + * The default implementation checks the set from {@link #getUndecoratedClasses()} * on the class and all it's super classes. * @param clazz The class to check * @return True if the class and all it's super classes can be decorated @@ -106,22 +111,41 @@ protected boolean isDecoratable(Class clazz) { if (Object.class == clazz) return true; - if (isKnownUndecoratable(clazz.getName())) + if (getUndecoratedClasses().contains(clazz.getName())) return false; return isDecoratable(clazz.getSuperclass()); } /** - * Test if a specific class name is known to not be decoratable. - * The default implementation checks for well known classes that are used to - * setup CDI itself, and thus cannot themselves be decorated. + * Get the set of classes that will not be decorated. The default set includes the listener from Weld that will itself + * setup decoration. + * @return The modifiable set of class names that will not be decorated (ie {@link #isDecoratable(Class)} will return false. * @see #isDecoratable(Class) - * @param className The name of the class to check - * @return True if the class is known not to be decoratable */ - protected boolean isKnownUndecoratable(String className) + public Set getUndecoratedClasses() { - return "org.jboss.weld.environment.servlet.Listener".equals(className); + return _undecorated; + } + + /** + * @param undecorated The set of class names that will not be decorated. + * @see #isDecoratable(Class) + */ + public void setUndecoratedClasses(Set undecorated) + { + _undecorated.clear(); + if (undecorated != null) + _undecorated.addAll(undecorated); + } + + /** + * @param undecorated A class name that will be added to the undecoratable classes set. + * @see #getUndecoratedClasses() + * @see #isDecoratable(Class) + */ + public void addUndecoratedClass(String... undecorated) + { + _undecorated.addAll(Arrays.asList()); } /** diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc index 4b9e2bf65ef3..a47bac65216b 100644 --- a/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc +++ b/jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc @@ -114,6 +114,6 @@ necessary: * and/or enable a CDI frame integration. -However, depending on the exact configuration of the embedded server, either or both steps may not be required as Servlet Container Initializers may be discovered. +However, depending on the exact configuration of the embedded server, either or both steps may not be required as `ServletContainerInitializer`s may be discovered. -The details for embedding CDI will be explain in the Embedded Jetty with Weld section, but can be adapted to other CDI frameworks. +The details for embedding CDI is explained in the link:#weld-embedded[Embedded Jetty with Weld] section, which can also be adapted to other CDI frameworks. diff --git a/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc b/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc index 23f9d382c686..19c68531adab 100644 --- a/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc +++ b/jetty-documentation/src/main/asciidoc/development/frameworks/weld.adoc @@ -59,7 +59,7 @@ For use with the jetty-maven-plugin, the best idea is to make the org.jboss.weld ____ [[weld-embedded]] -==== Embedded Jetty +==== Weld with Embedded Jetty When starting embedded Jetty programmatically from the `main` method it is necessary to: From 2bd8e1c7cae83f18386303428b12c29966521a8a Mon Sep 17 00:00:00 2001 From: gregw Date: Wed, 2 Sep 2020 13:13:46 +0200 Subject: [PATCH 11/11] Feedback from review --- .../eclipse/jetty/cdi/CdiSpiDecorator.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java index 3b71043e3ca7..413eacaf50bd 100644 --- a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java +++ b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiSpiDecorator.java @@ -102,7 +102,7 @@ public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperatio /** * Test if a class can be decorated. - * The default implementation checks the set from {@link #getUndecoratedClasses()} + * The default implementation checks the set from {@link #getUndecoratable()} * on the class and all it's super classes. * @param clazz The class to check * @return True if the class and all it's super classes can be decorated @@ -111,7 +111,7 @@ protected boolean isDecoratable(Class clazz) { if (Object.class == clazz) return true; - if (getUndecoratedClasses().contains(clazz.getName())) + if (getUndecoratable().contains(clazz.getName())) return false; return isDecoratable(clazz.getSuperclass()); } @@ -122,28 +122,28 @@ protected boolean isDecoratable(Class clazz) * @return The modifiable set of class names that will not be decorated (ie {@link #isDecoratable(Class)} will return false. * @see #isDecoratable(Class) */ - public Set getUndecoratedClasses() + public Set getUndecoratable() { return _undecorated; } /** - * @param undecorated The set of class names that will not be decorated. + * @param classnames The set of class names that will not be decorated. * @see #isDecoratable(Class) */ - public void setUndecoratedClasses(Set undecorated) + public void setUndecoratable(Set classnames) { _undecorated.clear(); - if (undecorated != null) - _undecorated.addAll(undecorated); + if (classnames != null) + _undecorated.addAll(classnames); } /** - * @param undecorated A class name that will be added to the undecoratable classes set. - * @see #getUndecoratedClasses() + * @param classname A class name that will be added to the undecoratable classes set. + * @see #getUndecoratable() * @see #isDecoratable(Class) */ - public void addUndecoratedClass(String... undecorated) + public void addUndecoratable(String... classname) { _undecorated.addAll(Arrays.asList()); }