Issue #3804 CDI integration

Rename attributes and classes to have a more regular pattern.
The DecoratingListener is now extened by the
CdiDecoratingListener which is used by the cdi-decorate module

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Greg Wilkins 2019-08-08 12:24:41 +10:00
parent dc939d753a
commit 70fcd3d145
9 changed files with 68 additions and 197 deletions

View File

@ -1,7 +1,5 @@
<?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">
<Ref refid="DeploymentManager">
<Call name="addLifeCycleBinding">

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,33 @@
# 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 "jetty.cdi.mode"
init parameter or default to the mode set by the "jetty.cdi.mode" server attribute.
Supported modes are:
CdiDecorator - Jetty will call the CDI SPI within the webapp to decorate
objects (default).
DecoratingLister - 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

@ -1,165 +0,0 @@
//
// ========================================================================
// 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 java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Map;
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 Decorator invokes a CDI provider within
* a webapplication 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:
* <pre>
* public &lt;T&gt; T decorate(T o)
* {
* BeanManager manager = CDI.current().getBeanManager();
* manager.createInjectionTarget(manager.createAnnotatedType((Class&lt;T&gt;)o.getClass()))
* .inject(o,manager.createCreationalContext(null));
* return o;
* }
* </pre>
*/
public class CdiDecorator implements Decorator
{
private static final Logger LOG = Log.getLogger(CdiServletContainerInitializer.class);
private final ServletContextHandler _context;
private final Map<Object, Decorated> _decorated = new HashMap<>();
private final MethodHandle _current;
private final MethodHandle _getBeanManager;
private final MethodHandle _createAnnotatedType;
private final MethodHandle _createInjectionTarget;
private final MethodHandle _createCreationalContext;
private final MethodHandle _inject;
private final MethodHandle _dispose;
private final MethodHandle _release;
public CdiDecorator(ServletContextHandler context) throws UnsupportedOperationException
{
_context = context;
ClassLoader classLoader = _context.getClassLoader();
try
{
Class<?> cdiClass = classLoader.loadClass("javax.enterprise.inject.spi.CDI");
Class<?> beanManagerClass = classLoader.loadClass("javax.enterprise.inject.spi.BeanManager");
Class<?> annotatedTypeClass = classLoader.loadClass("javax.enterprise.inject.spi.AnnotatedType");
Class<?> injectionTargetClass = classLoader.loadClass("javax.enterprise.inject.spi.InjectionTarget");
Class<?> creationalContextClass = classLoader.loadClass("javax.enterprise.context.spi.CreationalContext");
Class<?> contextualClass = classLoader.loadClass("javax.enterprise.context.spi.Contextual");
MethodHandles.Lookup lookup = MethodHandles.lookup();
_current = lookup.findStatic(cdiClass, "current", MethodType.methodType(cdiClass));
_getBeanManager = lookup.findVirtual(cdiClass, "getBeanManager", MethodType.methodType(beanManagerClass));
_createAnnotatedType = lookup.findVirtual(beanManagerClass, "createAnnotatedType", MethodType.methodType(annotatedTypeClass, Class.class));
_createInjectionTarget = lookup.findVirtual(beanManagerClass, "createInjectionTarget", MethodType.methodType(injectionTargetClass, annotatedTypeClass));
_createCreationalContext = lookup.findVirtual(beanManagerClass, "createCreationalContext", MethodType.methodType(creationalContextClass, contextualClass));
_inject = lookup.findVirtual(injectionTargetClass, "inject", MethodType.methodType(Void.TYPE, Object.class, creationalContextClass));
_dispose = lookup.findVirtual(injectionTargetClass, "dispose", MethodType.methodType(Void.TYPE, Object.class));
_release = lookup.findVirtual(creationalContextClass, "release", MethodType.methodType(Void.TYPE));
}
catch (Exception e)
{
throw new UnsupportedOperationException(e);
}
}
/**
* Decorate an object.
* <p>The signature of this method must match what is introspected for by the
* Jetty DecoratingListener class. It is invoked dynamically.</p>
*
* @param o The object to be decorated
* @param <T> The type of the object to be decorated
* @return The decorated object
*/
public <T> T decorate(T o)
{
try
{
if (LOG.isDebugEnabled())
LOG.debug("decorate {} in {}", o, _context);
_decorated.put(o, new Decorated(o));
}
catch (Throwable th)
{
LOG.warn("Unable to decorate " + o, th);
}
return o;
}
/**
* Destroy a decorated object.
* <p>The signature of this method must match what is introspected for by the
* Jetty DecoratingListener class. It is invoked dynamically.</p>
*
* @param o The object to be destroyed
*/
public void destroy(Object o)
{
try
{
Decorated decorated = _decorated.remove(o);
if (decorated != null)
decorated.destroy(o);
}
catch (Throwable th)
{
LOG.warn("Unable to destroy " + o, th);
}
}
private class Decorated
{
private final Object _injectionTarget;
private final Object _creationalContext;
Decorated(Object o) throws Throwable
{
// BeanManager manager = CDI.current().getBeanManager();
Object manager = _getBeanManager.invoke(_current.invoke());
// AnnotatedType annotatedType = manager.createAnnotatedType((Class<T>)o.getClass());
Object annotatedType = _createAnnotatedType.invoke(manager, o.getClass());
// CreationalContext creationalContext = manager.createCreationalContext(null);
_creationalContext = _createCreationalContext.invoke(manager, null);
// InjectionTarget injectionTarget = manager.createInjectionTarget();
_injectionTarget = _createInjectionTarget.invoke(manager, annotatedType);
// injectionTarget.inject(o, creationalContext);
_inject.invoke(_injectionTarget, o, _creationalContext);
}
public void destroy(Object o) throws Throwable
{
_dispose.invoke(_injectionTarget, o);
_release.invoke(_creationalContext);
}
}
}

View File

@ -31,7 +31,7 @@ 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}
* a {@link CdiSpiDecorator} is registered as a {@link org.eclipse.jetty.util.Decorator}
* for the context.
* @see AnnotationConfiguration.ServletContainerInitializerOrdering
*/
@ -47,9 +47,31 @@ 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);
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());
break;
default:
throw new IllegalStateException(mode);
}
context.setAttribute(CDI_INTEGRATION_ATTRIBUTE, mode);
LOG.info(mode + " enabled in " + ctx);
}
catch (UnsupportedOperationException e)
{

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

@ -89,13 +89,13 @@ public class DecoratingListener implements ServletContextAttributeListener
_context = context;
Objects.requireNonNull(_context);
_attributeName = attributeName == null ? DECORATOR_ATTRIBUTE : attributeName;
checkAndSetAttribute();
checkAndSetAttributeName();
Object decorator = _context.getAttribute(_attributeName);
if (decorator != null)
_context.getObjectFactory().addDecorator(asDecorator(decorator));
}
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

View File

@ -59,12 +59,14 @@ public class CDITests extends AbstractDistributionTest
List<Object[]> tests = new ArrayList<>();
tests.add(new Object[]{"weld", "cdi-spi", null}); // Requires Weld >= 3.1.2
tests.add(new Object[]{"weld", "cdi-spi", null}); // 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[]{"weld", "decorate", null}); // Weld == 3.1.2
// TODO tests.add(new Object[]{"weld", "cdi-decorate", null}); // 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
// tests.add(new Object[]{"owb", "cdi-decorate", null}); // Will not be supported
return tests.stream().map(Arguments::of);
}