Merge pull request #3946 from eclipse/jetty-9.4.x-3804-Decoration-rename

Issue #3804 CDI integration rename
This commit is contained in:
Joakim Erdfelt 2019-08-09 08:43:07 -05:00 committed by GitHub
commit e7a1978556
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 353 additions and 210 deletions

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="setAttribute">
<Arg type="String">org.eclipse.jetty.cdi</Arg>
<Arg><Property name="jetty.cdi.mode" default="CdiSpiDecorator"/></Arg>
</Call>
</Configure>

View File

@ -1,8 +1,9 @@
<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<!-- =============================================================== --><!-- Mixin the Weld / CDI classes to the class loader --><!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Get class="org.eclipse.jetty.util.log.Log" name="rootLogger">
<Call name="warn"><Arg>cdi2 module is deprecated!</Arg></Call>
</Get>
<Ref refid="DeploymentManager">
<Call name="addLifeCycleBinding">
<Arg>

View File

@ -0,0 +1,18 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Configures Jetty to use the "CdiDecoratingListener" as the default
CDI integration mode that allows a webapp to register it's own CDI
decorator.
[tag]
cdi
[provides]
cdi-mode
[depend]
cdi
[ini]
jetty.cdi.mode=CdiDecoratingListener

View File

@ -1,24 +1,17 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
CDI SPI integration for CDI inside the webapp.
This module does not provide CDI, but configures jetty to look for the CDI SPI within
a webapp. If the CDI SPI is found, then a CdiDecorator will be registered to
decorate Listeners, Filters and Servlets using the standard CDI SPI.
The module indicates to the webapp that this mechanism is available by setting the
"org.eclipse.jetty.cdi" context attribute to "CdiDecorator".
This is the preferred integration for OWB.
Configures Jetty to use the "CdiSpiDecorator" that calls the CDI SPI
as the default CDI integration mode.
[tag]
cdi
[depend]
deploy
[provides]
cdi-mode
[lib]
lib/jetty-cdi-${jetty.version}.jar
lib/apache-jsp/org.mortbay.jasper.apache-el-*.jar
[depend]
cdi
[ini]
jetty.webapp.addSystemClasses+=,org.eclipse.jetty.cdi.CdiServletContainerInitializer
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.cdi.CdiServletContainerInitializer
jetty.cdi.mode=CdiSpiDecorator

View File

@ -1,10 +1,34 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Deprecated CDI module. Current depends on cdi-spi.
Support for CDI inside the webapp.
This module does not provide CDI, but configures jetty to support various
integration modes with a CDI implementation on the webapp classpath.
CDI integration modes can be selected per webapp with the "org.eclipse.jetty.cdi"
init parameter or defaults to the mode set by 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".
[tag]
cdi
[provides]
cdi
[depend]
cdi-spi
deploy
[xml]
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

@ -10,6 +10,9 @@ implementation in the webapp.
[tag]
cdi
[provides]
cdi-mode
[depend]
deploy

View File

@ -0,0 +1,36 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import org.eclipse.jetty.servlet.DecoratingListener;
import org.eclipse.jetty.servlet.ServletContextHandler;
/**
* A DecoratingListener that listens for "org.eclipse.jetty.cdi.decorator"
*/
class CdiDecoratingListener extends DecoratingListener
{
public static final String MODE = "CdiDecoratingListener";
public static final String ATTRIBUTE = "org.eclipse.jetty.cdi.decorator";
public CdiDecoratingListener(ServletContextHandler contextHandler)
{
super(contextHandler, ATTRIBUTE);
}
}

View File

@ -25,14 +25,24 @@ import javax.servlet.ServletContext;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* A {@link ServletContainerInitializer} that introspects for a CDI API
* implementation within a web application. If the CDI API is found, then
* a {@link CdiDecorator} is registered as a {@link org.eclipse.jetty.util.Decorator}
* for the context.
* <p>A {@link ServletContainerInitializer} that introspects for a CDI API
* implementation within a web application and applies an integration
* mode if CDI is found. CDI integration modes can be selected per webapp with
* the "org.eclipse.jetty.cdi" init parameter or default to the mode set by the
* "org.eclipse.jetty.cdi" server attribute. Supported modes are:</p>
* <dl>
* <dt>CdiSpiDecorator</dt>
* <dd>Jetty will call the CDI SPI within the webapp to decorate objects (default).</dd>
* <dt>CdiDecoratingLister</dt>
* <dd>The webapp may register a decorator on the context attribute
* "org.eclipse.jetty.cdi.decorator".</dd>
* </dl>
*
* @see AnnotationConfiguration.ServletContainerInitializerOrdering
*/
public class CdiServletContainerInitializer implements ServletContainerInitializer
@ -47,11 +57,40 @@ public class CdiServletContainerInitializer implements ServletContainerInitializ
{
ServletContextHandler context = ServletContextHandler.getServletContextHandler(ctx);
Objects.requireNonNull(context);
context.getObjectFactory().addDecorator(new CdiDecorator(context));
context.setAttribute(CDI_INTEGRATION_ATTRIBUTE, "CdiDecorator");
LOG.info("CdiDecorator enabled in " + ctx);
// Test if CDI is in the webapp by trying to load the CDI class.
ClassLoader loader = context.getClassLoader();
if (loader == null)
Loader.loadClass("javax.enterprise.inject.spi.CDI");
else
loader.loadClass("javax.enterprise.inject.spi.CDI");
String mode = ctx.getInitParameter(CDI_INTEGRATION_ATTRIBUTE);
if (mode == null)
{
mode = (String)context.getServer().getAttribute(CDI_INTEGRATION_ATTRIBUTE);
if (mode == null)
mode = CdiSpiDecorator.MODE;
}
switch (mode)
{
case CdiSpiDecorator.MODE:
context.getObjectFactory().addDecorator(new CdiSpiDecorator(context));
break;
case CdiDecoratingListener.MODE:
context.addEventListener(new CdiDecoratingListener(context));
break;
default:
throw new IllegalStateException(mode);
}
context.setAttribute(CDI_INTEGRATION_ATTRIBUTE, mode);
LOG.info(mode + " enabled in " + ctx);
}
catch (UnsupportedOperationException e)
catch (UnsupportedOperationException | ClassNotFoundException e)
{
if (LOG.isDebugEnabled())
LOG.debug("CDI not found in " + ctx, e);

View File

@ -30,11 +30,11 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* A Decorator invokes a CDI provider within
* a webapplication to decorate objects created by the contexts {@link org.eclipse.jetty.util.DecoratedObjectFactory}
* A Decorator that invokes the CDI provider within a webapp to decorate objects created by
* the contexts {@link org.eclipse.jetty.util.DecoratedObjectFactory}
* (typically Listeners, Filters and Servlets).
* The CDI provide is invoked using {@link MethodHandle}s to avoid any CDI instance or dependencies within the server scope.
* The code invoked is equivalent to:
* The CDI provider is invoked using {@link MethodHandle}s to avoid any CDI instance
* or dependencies within the server scope. The code invoked is equivalent to:
* <pre>
* public &lt;T&gt; T decorate(T o)
* {
@ -45,9 +45,10 @@ import org.eclipse.jetty.util.log.Logger;
* }
* </pre>
*/
public class CdiDecorator implements Decorator
public class CdiSpiDecorator implements Decorator
{
private static final Logger LOG = Log.getLogger(CdiServletContainerInitializer.class);
public static final String MODE = "CdiSpiDecorator";
private final ServletContextHandler _context;
private final Map<Object, Decorated> _decorated = new HashMap<>();
@ -61,7 +62,7 @@ public class CdiDecorator implements Decorator
private final MethodHandle _dispose;
private final MethodHandle _release;
public CdiDecorator(ServletContextHandler context) throws UnsupportedOperationException
public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperationException
{
_context = context;
ClassLoader classLoader = _context.getClassLoader();

View File

@ -1,16 +1,11 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Jetty setup to support Decoration of Listeners, Filters and Servlets within a deployed
webapp (as used by some CDI integrations).
Jetty setup to support Decoration of Listeners, Filters and Servlets within a deployed webapp.
This module uses the DecoratingListener to register an object set as a context attribute
as a dynamic decorator. This module sets the "org.eclipse.jetty.webapp.DecoratingListener"
context attribute with the name of the context attribute that will be listened to.
By default the attribute is "org.eclipse.jetty.webapp.decorator".
This is the preferred integration for Weld >= 3.1.2
[tag]
cdi
[depend]
deploy

View File

@ -140,16 +140,6 @@
<outputDirectory>${assembly-directory}/demo-base/webapps</outputDirectory>
<destFileName>async-rest.war</destFileName>
</artifactItem>
<artifactItem>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-weld-cdi-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${assembly-directory}/demo-base/webapps</outputDirectory>
<destFileName>test-weld-cdi.war</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
@ -358,7 +348,7 @@
<arguments>
<argument>jetty.home=${assembly-directory}</argument>
<argument>jetty.base=${assembly-directory}/demo-base</argument>
<argument>--add-to-startd=server,continuation,deploy,websocket,ext,resources,client,annotations,cdi2,decorate,jndi,servlets,jsp,jstl,http,https,threadpool</argument>
<argument>--add-to-startd=server,continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets,jsp,jstl,http,https,threadpool</argument>
</arguments>
</configuration>
<goals>
@ -459,13 +449,6 @@
<type>war</type>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-weld-cdi-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-documentation</artifactId>

View File

@ -36,7 +36,6 @@
<li><a href="/test-spec/">Servlet 3.1 Test</a></li>
<li><a href="/test-jaas/">JAAS Test</a></li>
<li><a href="/test-jndi/">JNDI Test</a></li>
<li><a href="/test-weld-cdi/">Weld CDI Test</a></li>
<li><a href="/async-rest/">Async Rest</a></li>
<li><a href="/oldContextPath/">Redirected Context</a></li>
</ul>

View File

@ -0,0 +1,174 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.servlet;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Objects;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import org.eclipse.jetty.util.Decorator;
/**
* A ServletContextAttributeListener that listens for a context
* attribute to obtain a decorator instance. The instance is then either
* coerced to a {@link Decorator} or reflected for decorator compatible methods
* so it can be added to the {@link ServletContextHandler#getObjectFactory()} as a
* {@link Decorator}.
*/
public class DecoratingListener implements ServletContextAttributeListener
{
private static final MethodType DECORATE_TYPE;
private static final MethodType DESTROY_TYPE;
static
{
try
{
DECORATE_TYPE = MethodType.methodType(Object.class, Object.class);
DESTROY_TYPE = MethodType.methodType(void.class, Object.class);
// Check we have the right MethodTypes for the current Decorator signatures
MethodHandles.Lookup lookup = MethodHandles.lookup();
lookup.findVirtual(Decorator.class, "decorate", DECORATE_TYPE);
lookup.findVirtual(Decorator.class, "destroy", DESTROY_TYPE);
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
private final ServletContextHandler _context;
private final String _attributeName;
private Decorator _decorator;
public DecoratingListener(ServletContextHandler context, String attributeName)
{
Objects.requireNonNull(context);
Objects.requireNonNull(attributeName);
_context = context;
_attributeName = attributeName;
Object decorator = _context.getAttribute(_attributeName);
if (decorator != null)
_context.getObjectFactory().addDecorator(asDecorator(decorator));
}
public String getAttributeName()
{
return _attributeName;
}
public ServletContext getServletContext()
{
return _context.getServletContext();
}
private Decorator asDecorator(Object object)
{
if (object == null)
return null;
if (object instanceof Decorator)
return (Decorator)object;
try
{
Class<?> clazz = object.getClass();
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle decorate = lookup.findVirtual(clazz, "decorate", DECORATE_TYPE);
MethodHandle destroy = lookup.findVirtual(clazz, "destroy", DESTROY_TYPE);
return new DynamicDecorator(object, decorate, destroy);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
@Override
public void attributeAdded(ServletContextAttributeEvent event)
{
if (_attributeName.equals(event.getName()))
{
_decorator = asDecorator(event.getValue());
_context.getObjectFactory().addDecorator(_decorator);
}
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event)
{
if (_attributeName.equals(event.getName()) && _decorator != null)
{
_context.getObjectFactory().removeDecorator(_decorator);
_decorator = null;
}
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event)
{
attributeRemoved(event);
attributeAdded(event);
}
private static class DynamicDecorator implements Decorator
{
private final Object _object;
private final MethodHandle _decorate;
private final MethodHandle _destroy;
private DynamicDecorator(Object object, MethodHandle decorate, MethodHandle destroy)
{
_object = object;
_decorate = decorate;
_destroy = destroy;
}
@Override
public <T> T decorate(T o)
{
try
{
return (T)_decorate.invoke(_object, o);
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
}
@Override
public void destroy(Object o)
{
try
{
_destroy.invoke(_object, o);
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
}
}
}

View File

@ -18,60 +18,20 @@
package org.eclipse.jetty.webapp;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Objects;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.Decorator;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* A ServletContextAttributeListener that listens for a specific context
* attribute (default "org.eclipse.jetty.webapp.decorator") to obtain a
* decorator instance from the webapp. The instance is then either coerced
* to a Decorator or reflected for decorator compatible methods so it can
* be added to the {@link WebAppContext#getObjectFactory()} as a
* {@link Decorator}.
* An extended org.eclipse.jetty.servlet.DecoratingListener.
* The context attribute "org.eclipse.jetty.webapp.DecoratingListener" if
* not set, is set to the name of the attribute this listener listens for.
*/
public class DecoratingListener implements ServletContextAttributeListener
public class DecoratingListener extends org.eclipse.jetty.servlet.DecoratingListener
{
public static final String DECORATOR_ATTRIBUTE = "org.eclipse.jetty.webapp.decorator";
private static final Logger LOG = Log.getLogger(DecoratingListener.class);
private static final MethodType DECORATE_TYPE;
private static final MethodType DESTROY_TYPE;
static
{
try
{
DECORATE_TYPE = MethodType.methodType(Object.class, Object.class);
DESTROY_TYPE = MethodType.methodType(Void.TYPE, Object.class);
// Check we have the right MethodTypes for the current Decorator signatures
MethodHandles.Lookup lookup = MethodHandles.lookup();
lookup.findVirtual(Decorator.class, "decorate", DECORATE_TYPE);
lookup.findVirtual(Decorator.class, "destroy", DESTROY_TYPE);
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
private final ServletContextHandler _context;
private final String _attributeName;
private Decorator _decorator;
public DecoratingListener()
{
this((String)null);
this(DECORATOR_ATTRIBUTE);
}
public DecoratingListener(String attributeName)
@ -81,116 +41,22 @@ public class DecoratingListener implements ServletContextAttributeListener
public DecoratingListener(ServletContextHandler context)
{
this(context, null);
this(context, DECORATOR_ATTRIBUTE);
}
public DecoratingListener(ServletContextHandler context, String attributeName)
{
_context = context;
Objects.requireNonNull(_context);
_attributeName = attributeName == null ? DECORATOR_ATTRIBUTE : attributeName;
checkAndSetAttribute();
Object decorator = _context.getAttribute(_attributeName);
if (decorator != null)
_context.getObjectFactory().addDecorator(asDecorator(decorator));
super(context, attributeName);
checkAndSetAttributeName();
}
protected void checkAndSetAttribute()
protected void checkAndSetAttributeName()
{
// If not set (by another DecoratingListener), flag the attribute that are
// listening for. If more than one DecoratingListener is used then this
// attribute reflects only the first.
if (_context.getAttribute(getClass().getName()) != null)
if (getServletContext().getAttribute(getClass().getName()) != null)
throw new IllegalStateException("Multiple DecoratingListeners detected");
_context.setAttribute(getClass().getName(), _attributeName);
}
private Decorator asDecorator(Object object)
{
if (object == null)
return null;
if (object instanceof Decorator)
return (Decorator)object;
try
{
Class<?> clazz = object.getClass();
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle decorate = lookup.findVirtual(clazz, "decorate", DECORATE_TYPE);
MethodHandle destroy = lookup.findVirtual(clazz, "destroy", DESTROY_TYPE);
return new DynamicDecorator(decorate, destroy, object);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
@Override
public void attributeAdded(ServletContextAttributeEvent event)
{
if (_attributeName.equals(event.getName()))
{
_decorator = asDecorator(event.getValue());
_context.getObjectFactory().addDecorator(_decorator);
}
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event)
{
if (_attributeName.equals(event.getName()) && _decorator != null)
{
_context.getObjectFactory().removeDecorator(_decorator);
_decorator = null;
}
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event)
{
attributeRemoved(event);
attributeAdded(event);
}
private static class DynamicDecorator implements Decorator
{
private final MethodHandle _decorate;
private final MethodHandle _destroy;
private final Object _object;
private DynamicDecorator(MethodHandle decorate, MethodHandle destroy, Object object)
{
_decorate = decorate;
_destroy = destroy;
_object = object;
}
@Override
public <T> T decorate(T o)
{
try
{
return (T)_decorate.invoke(_object, o);
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
}
@Override
public void destroy(Object o)
{
try
{
_destroy.invoke(_object, o);
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
}
getServletContext().setAttribute(getClass().getName(), getAttributeName());
}
}

View File

@ -22,8 +22,6 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Stream;
@ -57,15 +55,19 @@ public class CDITests extends AbstractDistributionTest
}
};
List<Object[]> tests = new ArrayList<>();
return Stream.of(
// -- Weld --
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
tests.add(new Object[]{"weld", "cdi-spi", null}); // Requires Weld >= 3.1.2
tests.add(new Object[]{"weld", "cdi2", null});
tests.add(new Object[]{"weld", "decorate", null}); // Requires Weld >= 3.1.2
tests.add(new Object[]{"owb", "cdi-spi", removeJettyWebXml});
tests.add(new Object[]{"owb", "cdi2", null});
// tests.add(new Object[]{"owb", "decorate", null}); // Will not be supported
return tests.stream().map(Arguments::of);
// -- Apache OpenWebBeans --
Arguments.of("owb", "cdi-spi", removeJettyWebXml),
Arguments.of("owb", "cdi2", null)
// Arguments.of("owb", "decorate", null), // Not supported
// Arguments.of("owb", "cdi-decorate", null) // Not supported
);
}
/**