From 70fcd3d1452dac46e6f50be12df9af3b9eb69c10 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 8 Aug 2019 12:24:41 +1000 Subject: [PATCH] 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 --- .../src/main/config/etc/cdi/jetty-cdi2.xml | 2 - jetty-cdi/src/main/config/modules/cdi-spi.mod | 21 +-- jetty-cdi/src/main/config/modules/cdi.mod | 27 ++- jetty-cdi/src/main/config/modules/cdi2.mod | 3 + .../org/eclipse/jetty/cdi/CdiDecorator.java | 165 ------------------ .../cdi/CdiServletContainerInitializer.java | 30 +++- .../src/main/config/modules/decorate.mod | 7 +- .../jetty/webapp/DecoratingListener.java | 4 +- .../jetty/tests/distribution/CDITests.java | 6 +- 9 files changed, 68 insertions(+), 197 deletions(-) delete mode 100644 jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiDecorator.java diff --git a/jetty-cdi/src/main/config/etc/cdi/jetty-cdi2.xml b/jetty-cdi/src/main/config/etc/cdi/jetty-cdi2.xml index 3dfde45c87a..8848889fc02 100644 --- a/jetty-cdi/src/main/config/etc/cdi/jetty-cdi2.xml +++ b/jetty-cdi/src/main/config/etc/cdi/jetty-cdi2.xml @@ -1,7 +1,5 @@ - - diff --git a/jetty-cdi/src/main/config/modules/cdi-spi.mod b/jetty-cdi/src/main/config/modules/cdi-spi.mod index 437b4f2fcc1..5002bf967db 100644 --- a/jetty-cdi/src/main/config/modules/cdi-spi.mod +++ b/jetty-cdi/src/main/config/modules/cdi-spi.mod @@ -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 \ No newline at end of file +jetty.cdi.mode=CdiSpiDecorator diff --git a/jetty-cdi/src/main/config/modules/cdi.mod b/jetty-cdi/src/main/config/modules/cdi.mod index 94264564a95..7b90b29080e 100644 --- a/jetty-cdi/src/main/config/modules/cdi.mod +++ b/jetty-cdi/src/main/config/modules/cdi.mod @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/jetty-cdi/src/main/config/modules/cdi2.mod b/jetty-cdi/src/main/config/modules/cdi2.mod index 48defeb252b..48f1387d978 100644 --- a/jetty-cdi/src/main/config/modules/cdi2.mod +++ b/jetty-cdi/src/main/config/modules/cdi2.mod @@ -10,6 +10,9 @@ implementation in the webapp. [tag] cdi +[provides] +cdi-mode + [depend] deploy diff --git a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiDecorator.java b/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiDecorator.java deleted file mode 100644 index 80f07f8bf42..00000000000 --- a/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/CdiDecorator.java +++ /dev/null @@ -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: - *
- * public <T> T decorate(T o)
- * {
- *   BeanManager manager = CDI.current().getBeanManager();
- *   manager.createInjectionTarget(manager.createAnnotatedType((Class<T>)o.getClass()))
- *     .inject(o,manager.createCreationalContext(null));
- *   return o;
- * }
- * 
- */ -public class CdiDecorator implements Decorator -{ - private static final Logger LOG = Log.getLogger(CdiServletContainerInitializer.class); - - private final ServletContextHandler _context; - private final Map _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. - *

The signature of this method must match what is introspected for by the - * Jetty DecoratingListener class. It is invoked dynamically.

- * - * @param o The object to be decorated - * @param The type of the object to be decorated - * @return The decorated object - */ - public 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. - *

The signature of this method must match what is introspected for by the - * Jetty DecoratingListener class. It is invoked dynamically.

- * - * @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)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); - } - } -} 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 acf3846a60d..01cdc4dd752 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 @@ -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) { diff --git a/jetty-deploy/src/main/config/modules/decorate.mod b/jetty-deploy/src/main/config/modules/decorate.mod index c24bc28ed9a..85bc367b981 100644 --- a/jetty-deploy/src/main/config/modules/decorate.mod +++ b/jetty-deploy/src/main/config/modules/decorate.mod @@ -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 diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DecoratingListener.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DecoratingListener.java index 4b2bb1c21cf..8c0f3065bfc 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DecoratingListener.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DecoratingListener.java @@ -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 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 2e73f8a0230..fe2c83ef126 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 @@ -59,12 +59,14 @@ public class CDITests extends AbstractDistributionTest List 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); }