Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x

Signed-off-by: gregw <gregw@webtide.com>
This commit is contained in:
gregw 2020-09-07 15:59:27 +02:00
commit d7b3d62982
19 changed files with 770 additions and 146 deletions

View File

@ -41,6 +41,12 @@
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-core</artifactId>
<version>${weld.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -27,8 +27,3 @@ etc/cdi/jetty-cdi.xml
[lib]
lib/jetty-cdi-${jetty.version}.jar
lib/apache-jsp/org.mortbay.jasper.apache-el-*.jar
[ini]
jetty.webapp.addSystemClasses+=,org.eclipse.jetty.cdi.CdiServletContainerInitializer
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.cdi.CdiServletContainerInitializer

View File

@ -0,0 +1,43 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.cdi;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>CDI Configuration</p>
* <p>This configuration configures the WebAppContext server/system classes to
* be able to see the {@link CdiServletContainerInitializer}.
* </p>
*/
public class CdiConfiguration extends AbstractConfiguration
{
private static final Logger LOG = LoggerFactory.getLogger(CdiConfiguration.class);
public CdiConfiguration()
{
protectAndExpose("org.eclipse.jetty.cdi.CdiServletContainerInitializer");
addDependents(AnnotationConfiguration.class, PlusConfiguration.class);
}
}

View File

@ -24,7 +24,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
/**
* 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);
}
}

View File

@ -86,8 +86,6 @@ public class CdiServletContainerInitializer implements ServletContainerInitializ
default:
throw new IllegalStateException(mode);
}
context.setAttribute(CDI_INTEGRATION_ATTRIBUTE, mode);
LOG.info("{} enabled in {}", mode, ctx);
}
catch (UnsupportedOperationException | ClassNotFoundException e)

View File

@ -21,8 +21,12 @@ package org.eclipse.jetty.cdi;
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,11 +65,15 @@ public class CdiSpiDecorator implements Decorator
private final MethodHandle _inject;
private final MethodHandle _dispose;
private final MethodHandle _release;
private final Set<String> _undecorated = new HashSet<>(Collections.singletonList("org.jboss.weld.environment.servlet.Listener"));
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 +100,54 @@ public class CdiSpiDecorator implements Decorator
}
}
/**
* Test if a class can be decorated.
* 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
*/
protected boolean isDecoratable(Class<?> clazz)
{
if (Object.class == clazz)
return true;
if (getUndecoratable().contains(clazz.getName()))
return false;
return isDecoratable(clazz.getSuperclass());
}
/**
* 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)
*/
public Set<String> getUndecoratable()
{
return _undecorated;
}
/**
* @param classnames The set of class names that will not be decorated.
* @see #isDecoratable(Class)
*/
public void setUndecoratable(Set<String> classnames)
{
_undecorated.clear();
if (classnames != null)
_undecorated.addAll(classnames);
}
/**
* @param classname A class name that will be added to the undecoratable classes set.
* @see #getUndecoratable()
* @see #isDecoratable(Class)
*/
public void addUndecoratable(String... classname)
{
_undecorated.addAll(Arrays.asList());
}
/**
* Decorate an object.
* <p>The signature of this method must match what is introspected for by the
@ -108,6 +164,7 @@ public class CdiSpiDecorator implements Decorator
if (LOG.isDebugEnabled())
LOG.debug("decorate {} in {}", o, _context);
if (isDecoratable(o.getClass()))
_decorated.put(o, new Decorated(o));
}
catch (Throwable th)

View File

@ -0,0 +1,2 @@
org.eclipse.jetty.cdi.CdiConfiguration

View File

@ -0,0 +1,331 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
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;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.cdi.CdiConfiguration;
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.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(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.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
// Setup Jetty weld integration
switch (mode)
{
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 "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 "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 "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 "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());
break;
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.addEventListener(new org.jboss.weld.environment.servlet.Listener());
break;
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.eclipse.jetty.cdi.CdiServletContainerInitializer()));
context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener()));
break;
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.eclipse.jetty.cdi.CdiServletContainerInitializer()));
context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener()));
break;
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.jboss.weld.environment.servlet.EnhancedListener()));
context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer()));
break;
}
return server;
}
@ParameterizedTest()
@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 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"));
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/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()));
webapp.getServerClassMatcher().add("-org.eclipse.jetty.embedded.");
webapp.getSystemClassMatcher().add("org.eclipse.jetty.embedded.");
webapp.addServlet(GreetingsServlet.class, "/");
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");
assertThat(response, containsString("HTTP/1.1 200 OK"));
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
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/weldtest");
server.setHandler(webapp);
// Need the AnnotationConfiguration to detect SCIs
webapp.addConfiguration(new AnnotationConfiguration());
// Need to expose our SCI. This is ugly could be made better in jetty-10 with a CdiConfiguration
webapp.addConfiguration(new CdiConfiguration());
// This is ugly but needed for maven for testing in a overlaid war pom
webapp.getServerClassMatcher().add("-org.eclipse.jetty.embedded.");
webapp.getSystemClassMatcher().add("org.eclipse.jetty.embedded.");
webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
webapp.addServlet(GreetingsServlet.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 filtered by Weld BeanManager "));
assertThat(response, containsString("Beans from Weld BeanManager "));
assertThat(response, containsString("Listener saw Weld BeanManager"));
server.stop();
}
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().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!";
}
}
}

View File

@ -0,0 +1,4 @@
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="all">
</beans>

View File

@ -0,0 +1,119 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
[[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]
-------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.util.Decorator</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.util.DecoratedObjectFactory</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler.</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.servlet.ServletContextHandler</Arg>
</Call>
</Configure>
-------------------------------------------------------------
____
[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 `ServletContainerInitializer`s may be discovered.
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.

View File

@ -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[]

View File

@ -26,125 +26,64 @@ 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]).
===== Jetty Weld Modules
====== 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:
-------------------------
$ 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]
-------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.util.Decorator</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.util.DecoratedObjectFactory</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler.</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.servlet.ServletContextHandler</Arg>
</Call>
</Configure>
-------------------------------------------------------------
//TODO Fix for 10
____
[TIP]
Directly modifying the web application classpath via `jetty-web.xml` will not work for Jetty 10.0.0 and later.
____
===== 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.
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):
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:
[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 <clinit>
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 <clinit>
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 <clinit>
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
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.
....
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).
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).
____
[[weld-embedded]]
==== Embedded Jetty
==== Weld with Embedded Jetty
When starting embedded Jetty programmatically from the `main` method it is necessary to register Weld's listener:
When starting embedded Jetty programmatically from the `main` method it is necessary to:
* enable a jetty CDI integration mode by registering a `Listener` or `ServletContainerInitializer`
* 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:
[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();
@ -160,7 +99,102 @@ public class Main {
}
}
}
<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
----
====== 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`:
[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());
// ...
----

View File

@ -124,9 +124,6 @@ public class ContainerInitializer
{
Set<Class<?>> classes = new HashSet<Class<?>>();
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
try
{
for (String s : _applicableTypeNames)
@ -147,7 +144,6 @@ public class ContainerInitializer
finally
{
context.getServletContext().setExtendedListenerTypes(false);
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
}

View File

@ -36,6 +36,7 @@ import javax.servlet.Filter;
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;
@ -71,6 +72,7 @@ import org.eclipse.jetty.util.DeprecationWarning;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -353,9 +355,15 @@ public class ServletContextHandler extends ContextHandler
@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)
{
@ -709,13 +717,13 @@ public class ServletContextHandler extends ContextHandler
public static class JspPropertyGroup implements JspPropertyGroupDescriptor
{
private List<String> _urlPatterns = new ArrayList<>();
private final List<String> _urlPatterns = new ArrayList<>();
private String _elIgnored;
private String _pageEncoding;
private String _scriptingInvalid;
private String _isXml;
private List<String> _includePreludes = new ArrayList<>();
private List<String> _includeCodas = new ArrayList<>();
private final List<String> _includePreludes = new ArrayList<>();
private final List<String> _includeCodas = new ArrayList<>();
private String _deferredSyntaxAllowedAsLiteral;
private String _trimDirectiveWhitespaces;
private String _defaultContentType;
@ -919,8 +927,8 @@ public class ServletContextHandler extends ContextHandler
public static class JspConfig implements JspConfigDescriptor
{
private List<TaglibDescriptor> _taglibs = new ArrayList<>();
private List<JspPropertyGroupDescriptor> _jspPropertyGroups = new ArrayList<>();
private final List<TaglibDescriptor> _taglibs = new ArrayList<>();
private final List<JspPropertyGroupDescriptor> _jspPropertyGroups = new ArrayList<>();
public JspConfig()
{
@ -1526,4 +1534,43 @@ public class ServletContextHandler extends ContextHandler
setDefaultResponseCharacterEncoding(encoding);
}
}
/**
* 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<Class<?>> _classes;
public Initializer(ServletContextHandler context, ServletContainerInitializer sci, Set<Class<?>> 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);
}
}
}
}

View File

@ -47,15 +47,14 @@ public interface Container
boolean addBean(Object o, boolean managed);
/**
* @return the list of beans known to this aggregate
* @see #getBean(Class)
* @return the collection of beans known to this aggregate, in the order they were added.
*/
Collection<Object> getBeans();
/**
* @param clazz the class of the beans
* @param <T> 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.
* @see #getBeans()
* @see #getContainedBeans(Class)
*/
@ -76,7 +75,7 @@ public interface Container
/**
* @param clazz the class of the bean
* @param <T> 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> T getBean(Class<T> clazz);
@ -137,7 +136,8 @@ public interface Container
/**
* @param clazz the class of the beans
* @param <T> 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 by depth first and then the order beans were added.
*/
<T> Collection<T> getContainedBeans(Class<T> clazz);

View File

@ -855,11 +855,6 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
}
}
/**
* @param clazz the class of the beans
* @param <T> the Bean type
* @return the list of beans of the given class from the entire Container hierarchy
*/
@Override
public <T> Collection<T> getContainedBeans(Class<T> clazz)
{
@ -868,11 +863,6 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
return beans;
}
/**
* @param clazz the class of the beans
* @param <T> the Bean type
* @param beans the collection to add beans of the given class from the entire Container hierarchy
*/
protected <T> void getContainedBeans(Class<T> clazz, Collection<T> beans)
{
beans.addAll(getBeans(clazz));

View File

@ -37,7 +37,7 @@
<junit.version>5.6.2</junit.version>
<maven.version>3.6.0</maven.version>
<maven.resolver.version>1.3.1</maven.resolver.version>
<weld.version>3.1.2.Final</weld.version>
<weld.version>3.1.3.Final</weld.version>
<jetty.perf-helper.version>1.0.5</jetty.perf-helper.version>
<unix.socket.tmp></unix.socket.tmp>
<!-- enable or not TestTracker junit5 extension i.e log message when test method is starting -->

View File

@ -60,7 +60,7 @@ public class CDITests extends AbstractDistributionTest
// -- Weld --
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)