Skip to content

Commit

Permalink
Issue #5162 CDI embedded integration improvements
Browse files Browse the repository at this point in the history
Clean up CDI integration and documentation to better support embedded usage.
 + made listener public
 + added utility class for SCIs
  • Loading branch information
gregw committed Aug 20, 2020
1 parent ff3ebef commit 67c373c
Show file tree
Hide file tree
Showing 11 changed files with 627 additions and 111 deletions.
Expand Up @@ -24,13 +24,14 @@
/**
* 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";

public CdiDecoratingListener(ServletContextHandler contextHandler)
{
super(contextHandler, ATTRIBUTE);
contextHandler.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE);
}
}
Expand Up @@ -86,8 +86,6 @@ public void onStartup(Set<Class<?>> c, ServletContext ctx)
default:
throw new IllegalStateException(mode);
}

context.setAttribute(CDI_INTEGRATION_ATTRIBUTE, mode);
LOG.info(mode + " enabled in " + ctx);
}
catch (UnsupportedOperationException | ClassNotFoundException e)
Expand Down
Expand Up @@ -65,7 +65,10 @@ public class CdiSpiDecorator implements Decorator
public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperationException
{
_context = context;
context.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE);
ClassLoader classLoader = _context.getClassLoader();
if (classLoader == null)
classLoader = this.getClass().getClassLoader();

try
{
Expand All @@ -92,6 +95,35 @@ public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperatio
}
}

/**
* Test if a class can be decorated.
* @implNote The default implementation calls {@link #isKnownUndecoratable(String) }
* on the class and all it's super classes.
* @param clazz The class to check
* @return True if the class and all it's super classes can be decorated
*/
protected boolean isDecoratable(Class<?> clazz)
{
if (Object.class == clazz)
return true;
if (isKnownUndecoratable(clazz.getName()))
return false;
return isDecoratable(clazz.getSuperclass());
}

/**
* Test if a specific class name is known to not be decoratable.
* @implNote default implementation checks for well known classes that are used to
* setup CDI itself, and thus cannot themselves be decorated.
* @see #isDecoratable(Class)
* @param className The name of the class to check
* @return True if the class is known not to be decoratable
*/
protected boolean isKnownUndecoratable(String className)
{
return "org.jboss.weld.environment.servlet.Listener".equals(className);
}

/**
* Decorate an object.
* <p>The signature of this method must match what is introspected for by the
Expand All @@ -108,7 +140,8 @@ public <T> T decorate(T o)
if (LOG.isDebugEnabled())
LOG.debug("decorate {} in {}", o, _context);

_decorated.put(o, new Decorated(o));
if (isDecoratable(o.getClass()))
_decorated.put(o, new Decorated(o));
}
catch (Throwable th)
{
Expand Down
133 changes: 133 additions & 0 deletions jetty-documentation/src/main/asciidoc/development/frameworks/cdi.adoc
@@ -0,0 +1,133 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
// ========================================================================
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//

[[framework-cdi]]
=== CDI

Contexts and Dependency Injection for Java EE (http://www.cdi-spec.org/[CDI])
is a standard implemented by frameworks such as
http://seamframework.org/Weld[Weld] and
https://openwebbeans.apache.org/[Apache OpenWebBeans]. This is a common way to
assemble and configure webapplications by a process often referred to as
'decoration'.

Jetty integration of CDI frameworks allows CDI to be used to inject the
Filters, Servlets and Listeners created within a Servlet Context. There are
two approaches to integration:

* CDI implementation can integrate with Jetty. This requires the CDI
implementation to have Jetty specific code. Since Jetty-9.4.20 a loosely
bound mechanism has been available for CDI implementations to extends
the Jetty `DecoratedObjectFactory` without hard API dependencies.
Prior to that, CDI implementations directly called jetty APIs that need
to be explicitly exposed to the webapp.

* Alternately, Jetty can integrate with CDI implementations by using standard
CDI SPIs.

==== Jetty CDI Modules

The Jetty distribution come with several CDI modules. These modules do not provide CDI,
but instead enable one of more integration mechanisms.

===== Jetty `cdi` Module
The `cdi` module supports either two modes of CDI integration which can be
selected either by the "org.eclipse.jetty.cdi" context init parameter or
the "org.eclipse.jetty.cdi" server attribute (which is initialised from
the "jetty.cdi.mode" start property). Supported modes are:

* `CdiSpiDecorator` Jetty will call the CDI SPI within the webapp to decorate
objects (default).

* `CdiDecoratingLister` The webapp may register a decorator on the context attribute
"org.eclipse.jetty.cdi.decorator".
-------------------------
cd $JETTY_BASE
java -jar $JETTY_HOME/start.jar --add-to-start=cdi
-------------------------


===== Jetty `cdi-decorate` Module
This module depends on the `cdi` module and sets the default mode to `CdiDecoratingListener`.
This is the preferred mode for Weld integration.
-------------------------
cd $JETTY_BASE
java -jar $JETTY_HOME/start.jar --add-to-start=cdi-decorate
-------------------------

===== Jetty `cdi-spi` Module
This module depends on the `cdi` module and sets the default mode to `CdiSpiDecorator`.
This is the preferred mode for Open Web Beans integration.
-------------------------
cd $JETTY_BASE
java -jar $JETTY_HOME/start.jar --add-to-start=cdi-spi
-------------------------

===== Jetty `cdi2` Module
This module supports the *deprecated* technique of exposing private Jetty decorate APIs to the CDI
implementation in the webapp.

-------------------------
cd $JETTY_BASE
java -jar $JETTY_HOME/start.jar --add-to-start=cdi2
-------------------------

This module is equivalent to directly modifying the class path configuration with a `jetty-web.xml` like:

[source.XML, xml]
-------------------------------------------------------------
<?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 Servlet Container Initializers may be discovered.

The details for embedding CDI will be explain in the Embedded Jetty with Weld section, but
can be adapted to other CDI frameworks.
Expand Up @@ -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[]

0 comments on commit 67c373c

Please sign in to comment.