Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #5162 CDI embedded integration improvements #5177

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
gregw marked this conversation as resolved.
Show resolved Hide resolved
{
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);
gregw marked this conversation as resolved.
Show resolved Hide resolved
}
}
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();
gregw marked this conversation as resolved.
Show resolved Hide resolved

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()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at all fond of the name isKnownDecoratable(). How about instead you remove isKnownDecoratable() and add a field list of _undecoratables, a setter for them, and in isDecoratable() you just check the list? This might also be a good idea in case CDI adds some other classes that can't be decorated?

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);
gregw marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* 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[]