Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.
This commit is contained in:
commit
62758122c3
|
@ -33,23 +33,12 @@
|
|||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<!--
|
||||
Eclipse Jetty Specific.
|
||||
===========================================================================================
|
||||
-->
|
||||
|
||||
<module name="SuppressionCommentFilter">
|
||||
<property name="offCommentFormat" value="@checkstyle-disable-check : ([\w\|]+)"/>
|
||||
<property name="onCommentFormat" value="@checkstyle-enable-check : ([\w\|]+)"/>
|
||||
<property name="checkFormat" value="$1"/>
|
||||
</module>
|
||||
|
||||
<!-- Check abbreviations(consecutive capital letters) length in identifier name -->
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="ignoreFinal" value="true"/>
|
||||
<property name="allowedAbbreviations" value="ALPN, ASCII, AWT, CRLDP, CRLF, FCGI, GZIP, HTTP, HTTPS, ID, IP, ISO8859, JAAS, JDBC, JMXRMI, JNDI, JPMS, JSON, JSTL, LDAP, PROXY, RFC, SPNEGO, URI, URL, UTF8, XML"/>
|
||||
</module>
|
||||
|
||||
<!-- Location of Annotations -->
|
||||
<module name="AnnotationLocation">
|
||||
<property name="allowSamelineSingleParameterlessAnnotation" value="false"/>
|
||||
|
@ -90,7 +79,7 @@
|
|||
|
||||
<!-- Indentation Rules -->
|
||||
<module name="Indentation">
|
||||
<property name="throwsIndent" value="0"/>
|
||||
<property name="arrayInitIndent" value="8"/>
|
||||
</module>
|
||||
|
||||
<!-- Interface Type Parameter Name -->
|
||||
|
@ -215,7 +204,9 @@
|
|||
</module>
|
||||
|
||||
<!-- all switch statements should have "default" label declared -->
|
||||
<!-- Disabled: Is super noisy
|
||||
<module name="MissingSwitchDefault"/>
|
||||
-->
|
||||
|
||||
<!-- prevent line wrapping of import / package statements -->
|
||||
<module name="NoLineWrap"/>
|
||||
|
@ -226,9 +217,6 @@
|
|||
<!-- Filename and Classname match -->
|
||||
<module name="OuterTypeFilename"/>
|
||||
|
||||
<!-- Checks that overload methods are grouped together -->
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
|
||||
<!--
|
||||
Checks based on the Java Language Specification recommendations.
|
||||
https://docs.oracle.com/javase/specs/jls/se8/html/index.html
|
||||
|
@ -284,8 +272,5 @@
|
|||
<module name="UpperEll"/>
|
||||
<!-- TODO: look for float / double version of above -->
|
||||
|
||||
<!-- Checks the distance between declaration of variable and its first usage -->
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
|
||||
</module>
|
||||
</module>
|
||||
|
|
|
@ -188,11 +188,12 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
|
||||
/**
|
||||
* ServletContainerInitializerOrdering
|
||||
*
|
||||
* A list of classnames of ServletContainerInitializers in the order in which
|
||||
* they are to be called back. One name only in the list can be "*", which is a
|
||||
* <p>Applies an ordering to the {@link ServletContainerInitializer}s for the context, using
|
||||
* the value of the "org.eclipse.jetty.containerInitializerOrder" context attribute.
|
||||
* The attribute value is a list of classnames of ServletContainerInitializers in the order in which
|
||||
* they are to be called. One name only in the list can be "*", which is a
|
||||
* wildcard which matches any other ServletContainerInitializer name not already
|
||||
* matched.
|
||||
* matched.</p>
|
||||
*/
|
||||
public class ServletContainerInitializerOrdering
|
||||
{
|
||||
|
|
|
@ -5,14 +5,34 @@
|
|||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
<artifactId>cdi-2</artifactId>
|
||||
<name>Jetty :: CDI 2</name>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-cdi</artifactId>
|
||||
<name>Jetty :: CDI</name>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.cdi2</bundle-symbolic-name>
|
||||
<bundle-symbolic-name>${project.groupId}.cdi</bundle-symbolic-name>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-annotations</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?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>
|
|
@ -0,0 +1,17 @@
|
|||
<?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">
|
||||
<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>
|
||||
<New class="org.eclipse.jetty.deploy.bindings.GlobalWebappConfigBinding">
|
||||
<Set name="jettyXml"><Property name="jetty.home" default="." />/etc/cdi/jetty-web-cdi2.xml
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Ref>
|
||||
</Configure>
|
|
@ -1,9 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<Set name="contextPath">/demo</Set>
|
||||
<Set name="war"><Property name="jetty.webapps"/>/demo/</Set>
|
||||
<Get name="serverClassMatcher">
|
||||
<Call name="add">
|
||||
<Arg>-org.eclipse.jetty.util.Decorator</Arg>
|
|
@ -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
|
|
@ -0,0 +1,17 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Configures Jetty to use the "CdiSpiDecorator" that calls the CDI SPI
|
||||
as the default CDI integration mode.
|
||||
|
||||
[tag]
|
||||
cdi
|
||||
|
||||
[provides]
|
||||
cdi-mode
|
||||
|
||||
[depend]
|
||||
cdi
|
||||
|
||||
[ini]
|
||||
jetty.cdi.mode=CdiSpiDecorator
|
|
@ -1,7 +1,34 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Jetty setup to support Weld/CDI2 with WELD inside the webapp
|
||||
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]
|
||||
cdi2
|
||||
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
|
|
@ -1,20 +1,23 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Jetty setup to support Weld/CDI2 with WELD inside the webapp
|
||||
Deprecated support for CDI integrations inside the webapp.
|
||||
This module does not provide CDI, but configures jetty so that a CDI implementation
|
||||
can enable itself as a decorator for Filters, Servlets and Listeners.
|
||||
This modules uses the deprecated technique of exposing private Jetty decorate APIs to the CDI
|
||||
implementation in the webapp.
|
||||
|
||||
[tag]
|
||||
cdi
|
||||
|
||||
[provides]
|
||||
cdi-mode
|
||||
|
||||
[depend]
|
||||
annotations
|
||||
deploy
|
||||
|
||||
[lib]
|
||||
lib/apache-jsp/org.mortbay.jasper.apache-el-*.jar
|
||||
|
||||
[ini]
|
||||
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.util.Decorator,-org.eclipse.jetty.util.DecoratedObjectFactory
|
||||
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.server.handler.ContextHandler.,-org.eclipse.jetty.server.handler.ContextHandler,-org.eclipse.jetty.servlet.ServletContextHandler
|
||||
|
||||
|
||||
[license]
|
||||
Weld is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
http://weld.cdi-spec.org/
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
[xml]
|
||||
etc/cdi/jetty-cdi2.xml
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.util.Objects;
|
||||
import java.util.Set;
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
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;
|
||||
|
||||
/**
|
||||
* <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
|
||||
{
|
||||
public static final String CDI_INTEGRATION_ATTRIBUTE = "org.eclipse.jetty.cdi";
|
||||
private static final Logger LOG = Log.getLogger(CdiServletContainerInitializer.class);
|
||||
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx)
|
||||
{
|
||||
try
|
||||
{
|
||||
ServletContextHandler context = ServletContextHandler.getServletContextHandler(ctx);
|
||||
Objects.requireNonNull(context);
|
||||
|
||||
// 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 | ClassNotFoundException e)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("CDI not found in " + ctx, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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 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 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 <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;
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
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<>();
|
||||
|
||||
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 CdiSpiDecorator(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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.cdi.CdiServletContainerInitializer
|
|
@ -0,0 +1,15 @@
|
|||
<?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">
|
||||
<!-- Bind the jetty-web-decorate.xml to every deployed webapp -->
|
||||
<Ref refid="DeploymentManager">
|
||||
<Call name="addLifeCycleBinding">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.deploy.bindings.GlobalWebappConfigBinding">
|
||||
<Set name="jettyXml"><Property name="jetty.home" default="." />/etc/jetty-web-decorate.xml
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Ref>
|
||||
</Configure>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext" id="context">
|
||||
|
||||
<Get class="org.eclipse.jetty.webapp.DecoratingListener" name="DECORATOR_ATTRIBUTE" id="decoratorAttribute"/>
|
||||
|
||||
<!-- Add the DecoratingListener to the webapp to look for dynamic decorators -->
|
||||
<Call name="addEventListener">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.webapp.DecoratingListener">
|
||||
<Arg><Ref refid="context" /></Arg>
|
||||
<Arg type="String"><Ref refid="decoratorAttribute"/></Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Configure>
|
|
@ -0,0 +1,14 @@
|
|||
# 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.
|
||||
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".
|
||||
|
||||
[depend]
|
||||
deploy
|
||||
|
||||
[xml]
|
||||
etc/jetty-decorate.xml
|
|
@ -30,13 +30,13 @@
|
|||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<h2>examples ...</h2>
|
||||
<h2>tests ...</h2>
|
||||
<ul>
|
||||
<li><a href="/test/">Test Jetty Webapp</a></li>
|
||||
<li><a href="/async-rest/">Async Rest</a></li>
|
||||
<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-spec/">Servlet 3.1 Test</a></li>
|
||||
<li><a href="/async-rest/">Async Rest</a></li>
|
||||
<li><a href="/oldContextPath/">Redirected Context</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
|
|
|
@ -689,8 +689,8 @@
|
|||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
<artifactId>cdi-2</artifactId>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-cdi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -31,9 +31,6 @@
|
|||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.weld.servlet</groupId>
|
||||
<artifactId>weld-servlet</artifactId>
|
||||
<version>@weld.version@</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.weld.servlet</groupId>
|
||||
<artifactId>weld-servlet</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-servlet-api</artifactId>
|
||||
|
|
|
@ -58,6 +58,7 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletMapping;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpUpgradeHandler;
|
||||
|
@ -484,57 +485,30 @@ public class Request implements HttpServletRequest
|
|||
{
|
||||
try
|
||||
{
|
||||
int maxFormContentSize = -1;
|
||||
int maxFormKeys = -1;
|
||||
int maxFormContentSize = ContextHandler.DEFAULT_MAX_FORM_CONTENT_SIZE;
|
||||
int maxFormKeys = ContextHandler.DEFAULT_MAX_FORM_KEYS;
|
||||
|
||||
if (_context != null)
|
||||
{
|
||||
maxFormContentSize = _context.getContextHandler().getMaxFormContentSize();
|
||||
maxFormKeys = _context.getContextHandler().getMaxFormKeys();
|
||||
ContextHandler contextHandler = _context.getContextHandler();
|
||||
maxFormContentSize = contextHandler.getMaxFormContentSize();
|
||||
maxFormKeys = contextHandler.getMaxFormKeys();
|
||||
}
|
||||
|
||||
if (maxFormContentSize < 0)
|
||||
else
|
||||
{
|
||||
Object obj = _channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize");
|
||||
if (obj == null)
|
||||
maxFormContentSize = 200000;
|
||||
else if (obj instanceof Number)
|
||||
{
|
||||
Number size = (Number)obj;
|
||||
maxFormContentSize = size.intValue();
|
||||
}
|
||||
else if (obj instanceof String)
|
||||
{
|
||||
maxFormContentSize = Integer.parseInt((String)obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxFormKeys < 0)
|
||||
{
|
||||
Object obj = _channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormKeys");
|
||||
if (obj == null)
|
||||
maxFormKeys = 1000;
|
||||
else if (obj instanceof Number)
|
||||
{
|
||||
Number keys = (Number)obj;
|
||||
maxFormKeys = keys.intValue();
|
||||
}
|
||||
else if (obj instanceof String)
|
||||
{
|
||||
maxFormKeys = Integer.parseInt((String)obj);
|
||||
}
|
||||
maxFormContentSize = lookupServerAttribute(ContextHandler.MAX_FORM_CONTENT_SIZE_KEY, maxFormContentSize);
|
||||
maxFormKeys = lookupServerAttribute(ContextHandler.MAX_FORM_KEYS_KEY, maxFormKeys);
|
||||
}
|
||||
|
||||
int contentLength = getContentLength();
|
||||
if (contentLength > maxFormContentSize && maxFormContentSize > 0)
|
||||
{
|
||||
throw new IllegalStateException("Form too large: " + contentLength + " > " + maxFormContentSize);
|
||||
}
|
||||
if (maxFormContentSize >= 0 && contentLength > maxFormContentSize)
|
||||
throw new IllegalStateException("Form is larger than max length " + maxFormContentSize);
|
||||
|
||||
InputStream in = getInputStream();
|
||||
if (_input.isAsync())
|
||||
throw new IllegalStateException("Cannot extract parameters with async IO");
|
||||
|
||||
UrlEncoded.decodeTo(in, params, getCharacterEncoding(), contentLength < 0 ? maxFormContentSize : -1, maxFormKeys);
|
||||
UrlEncoded.decodeTo(in, params, getCharacterEncoding(), maxFormContentSize, maxFormKeys);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
@ -543,6 +517,16 @@ public class Request implements HttpServletRequest
|
|||
}
|
||||
}
|
||||
|
||||
private int lookupServerAttribute(String key, int dftValue)
|
||||
{
|
||||
Object attribute = _channel.getServer().getAttribute(key);
|
||||
if (attribute instanceof Number)
|
||||
return ((Number)attribute).intValue();
|
||||
else if (attribute instanceof String)
|
||||
return Integer.parseInt((String)attribute);
|
||||
return dftValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncContext getAsyncContext()
|
||||
{
|
||||
|
@ -2137,7 +2121,7 @@ public class Request implements HttpServletRequest
|
|||
AsyncContextEvent event = new AsyncContextEvent(_context, _async, state, this, servletRequest, servletResponse);
|
||||
event.setDispatchContext(getServletContext());
|
||||
|
||||
String uri = ((HttpServletRequest)servletRequest).getRequestURI();
|
||||
String uri = unwrap(servletRequest).getRequestURI();
|
||||
if (_contextPath != null && uri.startsWith(_contextPath))
|
||||
uri = uri.substring(_contextPath.length());
|
||||
else
|
||||
|
@ -2149,6 +2133,19 @@ public class Request implements HttpServletRequest
|
|||
return _async;
|
||||
}
|
||||
|
||||
public static HttpServletRequest unwrap(ServletRequest servletRequest)
|
||||
{
|
||||
if (servletRequest instanceof HttpServletRequestWrapper)
|
||||
{
|
||||
return (HttpServletRequestWrapper)servletRequest;
|
||||
}
|
||||
if (servletRequest instanceof ServletRequestWrapper)
|
||||
{
|
||||
return unwrap(((ServletRequestWrapper)servletRequest).getRequest());
|
||||
}
|
||||
return ((HttpServletRequest)servletRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -32,8 +32,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
import java.util.function.Supplier;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.ServletResponseWrapper;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.eclipse.jetty.http.CookieCompliance;
|
||||
|
@ -1292,6 +1295,19 @@ public class Response implements HttpServletResponse
|
|||
}
|
||||
}
|
||||
|
||||
public static HttpServletResponse unwrap(ServletResponse servletResponse)
|
||||
{
|
||||
if (servletResponse instanceof HttpServletResponseWrapper)
|
||||
{
|
||||
return (HttpServletResponseWrapper)servletResponse;
|
||||
}
|
||||
if (servletResponse instanceof ServletResponseWrapper)
|
||||
{
|
||||
return unwrap(((ServletResponseWrapper)servletResponse).getResponse());
|
||||
}
|
||||
return (HttpServletResponse)servletResponse;
|
||||
}
|
||||
|
||||
private static class HttpFieldsSupplier implements Supplier<HttpFields>
|
||||
{
|
||||
private final Supplier<Map<String, String>> _supplier;
|
||||
|
|
|
@ -558,8 +558,8 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
}
|
||||
|
||||
final String target = baseRequest.getPathInfo();
|
||||
final HttpServletRequest request = (HttpServletRequest)event.getSuppliedRequest();
|
||||
final HttpServletResponse response = (HttpServletResponse)event.getSuppliedResponse();
|
||||
final HttpServletRequest request = Request.unwrap(event.getSuppliedRequest());
|
||||
final HttpServletResponse response = Response.unwrap(event.getSuppliedResponse());
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} {} {} on {}", request.getDispatcherType(), request.getMethod(), target, channel);
|
||||
|
|
|
@ -136,6 +136,11 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
|
||||
public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes";
|
||||
|
||||
public static final String MAX_FORM_KEYS_KEY = "org.eclipse.jetty.server.Request.maxFormKeys";
|
||||
public static final String MAX_FORM_CONTENT_SIZE_KEY = "org.eclipse.jetty.server.Request.maxFormContentSize";
|
||||
public static final int DEFAULT_MAX_FORM_KEYS = 1000;
|
||||
public static final int DEFAULT_MAX_FORM_CONTENT_SIZE = 200000;
|
||||
|
||||
/**
|
||||
* Get the current ServletContext implementation.
|
||||
*
|
||||
|
@ -188,8 +193,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
|
||||
private Logger _logger;
|
||||
private boolean _allowNullPathInfo;
|
||||
private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys", -1).intValue();
|
||||
private int _maxFormContentSize = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormContentSize", -1).intValue();
|
||||
private int _maxFormKeys = Integer.getInteger(MAX_FORM_KEYS_KEY, DEFAULT_MAX_FORM_KEYS);
|
||||
private int _maxFormContentSize = Integer.getInteger(MAX_FORM_CONTENT_SIZE_KEY, DEFAULT_MAX_FORM_CONTENT_SIZE);
|
||||
private boolean _compactPath = false;
|
||||
private boolean _usingSecurityManager = System.getSecurityManager() != null;
|
||||
|
||||
|
@ -784,9 +789,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
throw new IllegalStateException("Null contextPath");
|
||||
|
||||
if (_logger == null)
|
||||
{
|
||||
_logger = Log.getLogger(ContextHandler.class.getName() + getLogNameSuffix());
|
||||
}
|
||||
|
||||
ClassLoader oldClassloader = null;
|
||||
Thread currentThread = null;
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.server;
|
||||
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
public class ServletRequestWrapperTest
|
||||
{
|
||||
private Server server;
|
||||
private LocalConnector connector;
|
||||
private RequestHandler handler;
|
||||
|
||||
@BeforeEach
|
||||
public void init() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new LocalConnector(server, new HttpConnectionFactory());
|
||||
server.addConnector(connector);
|
||||
|
||||
handler = new RequestHandler();
|
||||
server.setHandler(handler);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServletRequestWrapper() throws Exception
|
||||
{
|
||||
String request = "GET / HTTP/1.1\r\n" +
|
||||
"Host: whatever\r\n" +
|
||||
"\n";
|
||||
|
||||
String response = connector.getResponse(request);
|
||||
assertThat("Response", response, containsString("200"));
|
||||
}
|
||||
|
||||
private class RequestWrapper extends ServletRequestWrapper
|
||||
{
|
||||
public RequestWrapper(ServletRequest request)
|
||||
{
|
||||
super(request);
|
||||
}
|
||||
}
|
||||
|
||||
private class RequestHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
RequestWrapper requestWrapper = new RequestWrapper(request);
|
||||
AsyncContext context = request.startAsync(requestWrapper, response);
|
||||
context.complete();
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||
import org.eclipse.jetty.client.util.FormContentProvider;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class FormTest
|
||||
{
|
||||
private static final int MAX_FORM_CONTENT_SIZE = 128;
|
||||
private static final int MAX_FORM_KEYS = 4;
|
||||
|
||||
private Server server;
|
||||
private ServerConnector connector;
|
||||
private HttpClient client;
|
||||
private String contextPath = "/ctx";
|
||||
private String servletPath = "/test_form";
|
||||
|
||||
private void start(Function<ServletContextHandler, HttpServlet> config) throws Exception
|
||||
{
|
||||
startServer(config);
|
||||
startClient();
|
||||
}
|
||||
|
||||
private void startServer(Function<ServletContextHandler, HttpServlet> config) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new ServerConnector(server, 1, 1);
|
||||
server.addConnector(connector);
|
||||
|
||||
ServletContextHandler handler = new ServletContextHandler(server, contextPath);
|
||||
HttpServlet servlet = config.apply(handler);
|
||||
handler.addServlet(new ServletHolder(servlet), servletPath + "/*");
|
||||
|
||||
server.start();
|
||||
}
|
||||
|
||||
private void startClient() throws Exception
|
||||
{
|
||||
client = new HttpClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dispose() throws Exception
|
||||
{
|
||||
if (client != null)
|
||||
client.stop();
|
||||
if (server != null)
|
||||
server.stop();
|
||||
}
|
||||
|
||||
public static Stream<Arguments> formContentSizeScenarios()
|
||||
{
|
||||
return Stream.of(
|
||||
Arguments.of(null, true),
|
||||
Arguments.of(null, false),
|
||||
Arguments.of(-1, true),
|
||||
Arguments.of(-1, false),
|
||||
Arguments.of(0, true),
|
||||
Arguments.of(0, false),
|
||||
Arguments.of(MAX_FORM_CONTENT_SIZE, true),
|
||||
Arguments.of(MAX_FORM_CONTENT_SIZE, false)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("formContentSizeScenarios")
|
||||
public void testMaxFormContentSizeExceeded(Integer maxFormContentSize, boolean withContentLength) throws Exception
|
||||
{
|
||||
start(handler ->
|
||||
{
|
||||
if (maxFormContentSize != null)
|
||||
handler.setMaxFormContentSize(maxFormContentSize);
|
||||
return new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
request.getParameterMap();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
byte[] key = "foo=".getBytes(StandardCharsets.US_ASCII);
|
||||
int length = (maxFormContentSize == null || maxFormContentSize < 0)
|
||||
? ContextHandler.DEFAULT_MAX_FORM_CONTENT_SIZE
|
||||
: maxFormContentSize;
|
||||
// Avoid empty value.
|
||||
length = length + 1;
|
||||
byte[] value = new byte[length];
|
||||
Arrays.fill(value, (byte)'x');
|
||||
DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(key), ByteBuffer.wrap(value));
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.method(HttpMethod.POST)
|
||||
.path(contextPath + servletPath)
|
||||
.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString())
|
||||
.content(content)
|
||||
.onRequestBegin(request ->
|
||||
{
|
||||
if (withContentLength)
|
||||
content.close();
|
||||
})
|
||||
.onRequestCommit(request ->
|
||||
{
|
||||
if (!withContentLength)
|
||||
content.close();
|
||||
})
|
||||
.send();
|
||||
|
||||
int expected = (maxFormContentSize != null && maxFormContentSize < 0)
|
||||
? HttpStatus.OK_200
|
||||
: HttpStatus.BAD_REQUEST_400;
|
||||
assertEquals(expected, response.getStatus());
|
||||
}
|
||||
|
||||
public static Stream<Integer> formKeysScenarios()
|
||||
{
|
||||
return Stream.of(null, -1, 0, MAX_FORM_KEYS);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("formKeysScenarios")
|
||||
public void testMaxFormKeysExceeded(Integer maxFormKeys) throws Exception
|
||||
{
|
||||
start(handler ->
|
||||
{
|
||||
if (maxFormKeys != null)
|
||||
handler.setMaxFormKeys(maxFormKeys);
|
||||
return new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
request.getParameterMap();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
int keys = (maxFormKeys == null || maxFormKeys < 0)
|
||||
? ContextHandler.DEFAULT_MAX_FORM_KEYS
|
||||
: maxFormKeys;
|
||||
// Have at least one key.
|
||||
keys = keys + 1;
|
||||
Fields formParams = new Fields();
|
||||
for (int i = 0; i < keys; ++i)
|
||||
{
|
||||
formParams.add("key_" + i, "value_" + i);
|
||||
}
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.method(HttpMethod.POST)
|
||||
.path(contextPath + servletPath)
|
||||
.content(new FormContentProvider(formParams))
|
||||
.send();
|
||||
|
||||
int expected = (maxFormKeys != null && maxFormKeys < 0)
|
||||
? HttpStatus.OK_200
|
||||
: HttpStatus.BAD_REQUEST_400;
|
||||
assertEquals(expected, response.getStatus());
|
||||
}
|
||||
}
|
|
@ -51,7 +51,13 @@ public class DecoratedObjectFactory implements Iterable<Decorator>
|
|||
public void addDecorator(Decorator decorator)
|
||||
{
|
||||
LOG.debug("Adding Decorator: {}", decorator);
|
||||
this.decorators.add(decorator);
|
||||
decorators.add(decorator);
|
||||
}
|
||||
|
||||
public boolean removeDecorator(Decorator decorator)
|
||||
{
|
||||
LOG.debug("Remove Decorator: {}", decorator);
|
||||
return decorators.remove(decorator);
|
||||
}
|
||||
|
||||
public void clear()
|
||||
|
|
|
@ -155,7 +155,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
boolean delim = false;
|
||||
for (Map.Entry<String, List<String>> entry : map.entrySet())
|
||||
{
|
||||
String key = entry.getKey().toString();
|
||||
String key = entry.getKey();
|
||||
List<String> list = entry.getValue();
|
||||
int s = list.size();
|
||||
|
||||
|
@ -181,7 +181,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
|
||||
if (val != null)
|
||||
{
|
||||
String str = val.toString();
|
||||
String str = val;
|
||||
if (str.length() > 0)
|
||||
{
|
||||
result.append('=');
|
||||
|
@ -232,7 +232,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
synchronized (map)
|
||||
{
|
||||
String key = null;
|
||||
String value = null;
|
||||
String value;
|
||||
int mark = -1;
|
||||
boolean encoded = false;
|
||||
for (int i = 0; i < content.length(); i++)
|
||||
|
@ -269,8 +269,6 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
case '%':
|
||||
encoded = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,146 +291,6 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoded parameters to Map.
|
||||
*
|
||||
* @param in the stream containing the encoded parameters
|
||||
* @param map the MultiMap to decode into
|
||||
* @param charset the charset to use for decoding
|
||||
* @param maxLength the maximum length of the form to decode
|
||||
* @param maxKeys the maximum number of keys to decode
|
||||
* @throws IOException if unable to decode input stream
|
||||
*/
|
||||
public static void decodeTo(InputStream in, MultiMap<String> map, String charset, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
{
|
||||
if (charset == null)
|
||||
{
|
||||
if (ENCODING.equals(StandardCharsets.UTF_8))
|
||||
decodeUtf8To(in, map, maxLength, maxKeys);
|
||||
else
|
||||
decodeTo(in, map, ENCODING, maxLength, maxKeys);
|
||||
}
|
||||
else if (StringUtil.__UTF8.equalsIgnoreCase(charset))
|
||||
decodeUtf8To(in, map, maxLength, maxKeys);
|
||||
else if (StringUtil.__ISO_8859_1.equalsIgnoreCase(charset))
|
||||
decode88591To(in, map, maxLength, maxKeys);
|
||||
else if (StringUtil.__UTF16.equalsIgnoreCase(charset))
|
||||
decodeUtf16To(in, map, maxLength, maxKeys);
|
||||
else
|
||||
decodeTo(in, map, Charset.forName(charset), maxLength, maxKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoded parameters to Map.
|
||||
*
|
||||
* @param in the stream containing the encoded parameters
|
||||
* @param map the MultiMap to decode into
|
||||
* @param charset the charset to use for decoding
|
||||
* @param maxLength the maximum length of the form to decode
|
||||
* @param maxKeys the maximum number of keys to decode
|
||||
* @throws IOException if unable to decode input stream
|
||||
*/
|
||||
public static void decodeTo(InputStream in, MultiMap<String> map, Charset charset, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
{
|
||||
//no charset present, use the configured default
|
||||
if (charset == null)
|
||||
charset = ENCODING;
|
||||
|
||||
if (StandardCharsets.UTF_8.equals(charset))
|
||||
{
|
||||
decodeUtf8To(in, map, maxLength, maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StandardCharsets.ISO_8859_1.equals(charset))
|
||||
{
|
||||
decode88591To(in, map, maxLength, maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StandardCharsets.UTF_16.equals(charset)) // Should be all 2 byte encodings
|
||||
{
|
||||
decodeUtf16To(in, map, maxLength, maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (map)
|
||||
{
|
||||
String key = null;
|
||||
String value = null;
|
||||
|
||||
int c;
|
||||
|
||||
int totalLength = 0;
|
||||
|
||||
try (ByteArrayOutputStream2 output = new ByteArrayOutputStream2();)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
while ((c = in.read()) > 0)
|
||||
{
|
||||
switch ((char)c)
|
||||
{
|
||||
case '&':
|
||||
size = output.size();
|
||||
value = size == 0 ? "" : output.toString(charset);
|
||||
output.setCount(0);
|
||||
if (key != null)
|
||||
{
|
||||
map.add(key, value);
|
||||
}
|
||||
else if (value != null && value.length() > 0)
|
||||
{
|
||||
map.add(value, "");
|
||||
}
|
||||
key = null;
|
||||
value = null;
|
||||
if (maxKeys > 0 && map.size() > maxKeys)
|
||||
throw new IllegalStateException(String.format("Form with too many keys [%d > %d]", map.size(), maxKeys));
|
||||
break;
|
||||
case '=':
|
||||
if (key != null)
|
||||
{
|
||||
output.write(c);
|
||||
break;
|
||||
}
|
||||
size = output.size();
|
||||
key = size == 0 ? "" : output.toString(charset);
|
||||
output.setCount(0);
|
||||
break;
|
||||
case '+':
|
||||
output.write(' ');
|
||||
break;
|
||||
case '%':
|
||||
int code0 = in.read();
|
||||
int code1 = in.read();
|
||||
output.write(decodeHexChar(code0, code1));
|
||||
break;
|
||||
default:
|
||||
output.write(c);
|
||||
break;
|
||||
}
|
||||
|
||||
totalLength++;
|
||||
if (maxLength >= 0 && totalLength > maxLength)
|
||||
throw new IllegalStateException("Form is too large");
|
||||
}
|
||||
|
||||
size = output.size();
|
||||
if (key != null)
|
||||
{
|
||||
value = size == 0 ? "" : output.toString(charset);
|
||||
output.setCount(0);
|
||||
map.add(key, value);
|
||||
}
|
||||
else if (size > 0)
|
||||
map.add(output.toString(charset), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void decodeUtf8To(String query, MultiMap<String> map)
|
||||
{
|
||||
decodeUtf8To(query, 0, query.length(), map);
|
||||
|
@ -521,14 +379,96 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoded parameters to MultiMap, using ISO8859-1 encodings.
|
||||
*
|
||||
* @param in InputSteam to read
|
||||
* @param map MultiMap to add parameters to
|
||||
* @param maxLength maximum length of form to read or -1 for no limit
|
||||
* @param maxKeys maximum number of keys to read or -1 for no limit
|
||||
* @throws IOException if unable to decode the InputStream as ISO8859-1
|
||||
*/
|
||||
public static void decode88591To(InputStream in, MultiMap<String> map, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
{
|
||||
synchronized (map)
|
||||
{
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String key = null;
|
||||
String value = null;
|
||||
|
||||
int b;
|
||||
|
||||
int totalLength = 0;
|
||||
while ((b = in.read()) >= 0)
|
||||
{
|
||||
switch ((char)b)
|
||||
{
|
||||
case '&':
|
||||
value = buffer.length() == 0 ? "" : buffer.toString();
|
||||
buffer.setLength(0);
|
||||
if (key != null)
|
||||
{
|
||||
map.add(key, value);
|
||||
}
|
||||
else if (value.length() > 0)
|
||||
{
|
||||
map.add(value, "");
|
||||
}
|
||||
key = null;
|
||||
value = null;
|
||||
checkMaxKeys(map, maxKeys);
|
||||
break;
|
||||
|
||||
case '=':
|
||||
if (key != null)
|
||||
{
|
||||
buffer.append((char)b);
|
||||
break;
|
||||
}
|
||||
key = buffer.toString();
|
||||
buffer.setLength(0);
|
||||
break;
|
||||
|
||||
case '+':
|
||||
buffer.append(' ');
|
||||
break;
|
||||
|
||||
case '%':
|
||||
int code0 = in.read();
|
||||
int code1 = in.read();
|
||||
buffer.append(decodeHexChar(code0, code1));
|
||||
break;
|
||||
|
||||
default:
|
||||
buffer.append((char)b);
|
||||
break;
|
||||
}
|
||||
checkMaxLength(++totalLength, maxLength);
|
||||
}
|
||||
|
||||
if (key != null)
|
||||
{
|
||||
value = buffer.length() == 0 ? "" : buffer.toString();
|
||||
buffer.setLength(0);
|
||||
map.add(key, value);
|
||||
}
|
||||
else if (buffer.length() > 0)
|
||||
{
|
||||
map.add(buffer.toString(), "");
|
||||
}
|
||||
checkMaxKeys(map, maxKeys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoded parameters to Map.
|
||||
*
|
||||
* @param in InputSteam to read
|
||||
* @param map MultiMap to add parameters to
|
||||
* @param maxLength maximum form length to decode
|
||||
* @param maxLength maximum form length to decode or -1 for no limit
|
||||
* @param maxKeys the maximum number of keys to read or -1 for no limit
|
||||
* @throws IOException if unable to decode input stream
|
||||
* @throws IOException if unable to decode the input stream
|
||||
*/
|
||||
public static void decodeUtf8To(InputStream in, MultiMap<String> map, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
|
@ -559,8 +499,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
}
|
||||
key = null;
|
||||
value = null;
|
||||
if (maxKeys > 0 && map.size() > maxKeys)
|
||||
throw new IllegalStateException(String.format("Form with too many keys [%d > %d]", map.size(), maxKeys));
|
||||
checkMaxKeys(map, maxKeys);
|
||||
break;
|
||||
|
||||
case '=':
|
||||
|
@ -587,8 +526,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
buffer.append((byte)b);
|
||||
break;
|
||||
}
|
||||
if (maxLength >= 0 && (++totalLength > maxLength))
|
||||
throw new IllegalStateException("Form is too large");
|
||||
checkMaxLength(++totalLength, maxLength);
|
||||
}
|
||||
|
||||
if (key != null)
|
||||
|
@ -601,6 +539,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
{
|
||||
map.add(buffer.toReplacedString(), "");
|
||||
}
|
||||
checkMaxKeys(map, maxKeys);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,88 +554,157 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
|
|||
}
|
||||
|
||||
/**
|
||||
* Decoded parameters to MultiMap, using ISO8859-1 encodings.
|
||||
* Decoded parameters to Map.
|
||||
*
|
||||
* @param in InputSteam to read
|
||||
* @param map MultiMap to add parameters to
|
||||
* @param maxLength maximum length of form to read
|
||||
* @param maxKeys maximum number of keys to read or -1 for no limit
|
||||
* @throws IOException if unable to decode inputstream as ISO8859-1
|
||||
* @param in the stream containing the encoded parameters
|
||||
* @param map the MultiMap to decode into
|
||||
* @param charset the charset to use for decoding
|
||||
* @param maxLength the maximum length of the form to decode or -1 for no limit
|
||||
* @param maxKeys the maximum number of keys to decode or -1 for no limit
|
||||
* @throws IOException if unable to decode the input stream
|
||||
*/
|
||||
public static void decode88591To(InputStream in, MultiMap<String> map, int maxLength, int maxKeys)
|
||||
public static void decodeTo(InputStream in, MultiMap<String> map, String charset, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
{
|
||||
if (charset == null)
|
||||
{
|
||||
if (ENCODING.equals(StandardCharsets.UTF_8))
|
||||
decodeUtf8To(in, map, maxLength, maxKeys);
|
||||
else
|
||||
decodeTo(in, map, ENCODING, maxLength, maxKeys);
|
||||
}
|
||||
else if (StringUtil.__UTF8.equalsIgnoreCase(charset))
|
||||
decodeUtf8To(in, map, maxLength, maxKeys);
|
||||
else if (StringUtil.__ISO_8859_1.equalsIgnoreCase(charset))
|
||||
decode88591To(in, map, maxLength, maxKeys);
|
||||
else if (StringUtil.__UTF16.equalsIgnoreCase(charset))
|
||||
decodeUtf16To(in, map, maxLength, maxKeys);
|
||||
else
|
||||
decodeTo(in, map, Charset.forName(charset), maxLength, maxKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoded parameters to Map.
|
||||
*
|
||||
* @param in the stream containing the encoded parameters
|
||||
* @param map the MultiMap to decode into
|
||||
* @param charset the charset to use for decoding
|
||||
* @param maxLength the maximum length of the form to decode
|
||||
* @param maxKeys the maximum number of keys to decode
|
||||
* @throws IOException if unable to decode input stream
|
||||
*/
|
||||
public static void decodeTo(InputStream in, MultiMap<String> map, Charset charset, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
{
|
||||
//no charset present, use the configured default
|
||||
if (charset == null)
|
||||
charset = ENCODING;
|
||||
|
||||
if (StandardCharsets.UTF_8.equals(charset))
|
||||
{
|
||||
decodeUtf8To(in, map, maxLength, maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StandardCharsets.ISO_8859_1.equals(charset))
|
||||
{
|
||||
decode88591To(in, map, maxLength, maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StandardCharsets.UTF_16.equals(charset)) // Should be all 2 byte encodings
|
||||
{
|
||||
decodeUtf16To(in, map, maxLength, maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (map)
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
String key = null;
|
||||
String value = null;
|
||||
|
||||
int b;
|
||||
int c;
|
||||
|
||||
int totalLength = 0;
|
||||
while ((b = in.read()) >= 0)
|
||||
|
||||
try (ByteArrayOutputStream2 output = new ByteArrayOutputStream2())
|
||||
{
|
||||
switch ((char)b)
|
||||
int size = 0;
|
||||
|
||||
while ((c = in.read()) > 0)
|
||||
{
|
||||
case '&':
|
||||
value = buffer.length() == 0 ? "" : buffer.toString();
|
||||
buffer.setLength(0);
|
||||
if (key != null)
|
||||
{
|
||||
map.add(key, value);
|
||||
}
|
||||
else if (value != null && value.length() > 0)
|
||||
{
|
||||
map.add(value, "");
|
||||
}
|
||||
key = null;
|
||||
value = null;
|
||||
if (maxKeys > 0 && map.size() > maxKeys)
|
||||
throw new IllegalStateException(String.format("Form with too many keys [%d > %d]", map.size(), maxKeys));
|
||||
break;
|
||||
|
||||
case '=':
|
||||
if (key != null)
|
||||
{
|
||||
buffer.append((char)b);
|
||||
switch ((char)c)
|
||||
{
|
||||
case '&':
|
||||
size = output.size();
|
||||
value = size == 0 ? "" : output.toString(charset);
|
||||
output.setCount(0);
|
||||
if (key != null)
|
||||
{
|
||||
map.add(key, value);
|
||||
}
|
||||
else if (value != null && value.length() > 0)
|
||||
{
|
||||
map.add(value, "");
|
||||
}
|
||||
key = null;
|
||||
value = null;
|
||||
checkMaxKeys(map, maxKeys);
|
||||
break;
|
||||
}
|
||||
key = buffer.toString();
|
||||
buffer.setLength(0);
|
||||
break;
|
||||
|
||||
case '+':
|
||||
buffer.append(' ');
|
||||
break;
|
||||
|
||||
case '%':
|
||||
int code0 = in.read();
|
||||
int code1 = in.read();
|
||||
buffer.append(decodeHexChar(code0, code1));
|
||||
break;
|
||||
|
||||
default:
|
||||
buffer.append((char)b);
|
||||
break;
|
||||
case '=':
|
||||
if (key != null)
|
||||
{
|
||||
output.write(c);
|
||||
break;
|
||||
}
|
||||
size = output.size();
|
||||
key = size == 0 ? "" : output.toString(charset);
|
||||
output.setCount(0);
|
||||
break;
|
||||
case '+':
|
||||
output.write(' ');
|
||||
break;
|
||||
case '%':
|
||||
int code0 = in.read();
|
||||
int code1 = in.read();
|
||||
output.write(decodeHexChar(code0, code1));
|
||||
break;
|
||||
default:
|
||||
output.write(c);
|
||||
break;
|
||||
}
|
||||
checkMaxLength(++totalLength, maxLength);
|
||||
}
|
||||
if (maxLength >= 0 && (++totalLength > maxLength))
|
||||
throw new IllegalStateException(String.format("Form with too many keys [%d > %d]", map.size(), maxKeys));
|
||||
}
|
||||
|
||||
if (key != null)
|
||||
{
|
||||
value = buffer.length() == 0 ? "" : buffer.toString();
|
||||
buffer.setLength(0);
|
||||
map.add(key, value);
|
||||
}
|
||||
else if (buffer.length() > 0)
|
||||
{
|
||||
map.add(buffer.toString(), "");
|
||||
size = output.size();
|
||||
if (key != null)
|
||||
{
|
||||
value = size == 0 ? "" : output.toString(charset);
|
||||
output.setCount(0);
|
||||
map.add(key, value);
|
||||
}
|
||||
else if (size > 0)
|
||||
{
|
||||
map.add(output.toString(charset), "");
|
||||
}
|
||||
checkMaxKeys(map, maxKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkMaxKeys(MultiMap<String> map, int maxKeys)
|
||||
{
|
||||
int size = map.size();
|
||||
if (maxKeys >= 0 && size > maxKeys)
|
||||
throw new IllegalStateException(String.format("Form with too many keys [%d > %d]", size, maxKeys));
|
||||
}
|
||||
|
||||
private static void checkMaxLength(int length, int maxLength)
|
||||
{
|
||||
if (maxLength >= 0 && length > maxLength)
|
||||
throw new IllegalStateException("Form is larger than max length " + maxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode String with % encoding.
|
||||
* This method makes the assumption that the majority of calls
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.webapp;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
|
||||
/**
|
||||
* 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 extends org.eclipse.jetty.servlet.DecoratingListener
|
||||
{
|
||||
public static final String DECORATOR_ATTRIBUTE = "org.eclipse.jetty.webapp.decorator";
|
||||
|
||||
public DecoratingListener()
|
||||
{
|
||||
this(DECORATOR_ATTRIBUTE);
|
||||
}
|
||||
|
||||
public DecoratingListener(String attributeName)
|
||||
{
|
||||
this(WebAppContext.getCurrentWebAppContext(), attributeName);
|
||||
}
|
||||
|
||||
public DecoratingListener(ServletContextHandler context)
|
||||
{
|
||||
this(context, DECORATOR_ATTRIBUTE);
|
||||
}
|
||||
|
||||
public DecoratingListener(ServletContextHandler context, String attributeName)
|
||||
{
|
||||
super(context, attributeName);
|
||||
checkAndSetAttributeName();
|
||||
}
|
||||
|
||||
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 (getServletContext().getAttribute(getClass().getName()) != null)
|
||||
throw new IllegalStateException("Multiple DecoratingListeners detected");
|
||||
getServletContext().setAttribute(getClass().getName(), getAttributeName());
|
||||
}
|
||||
}
|
|
@ -946,7 +946,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
new DumpableCollection("Serverclasses " + name, serverClasses),
|
||||
new DumpableCollection("Configurations " + name, _configurations),
|
||||
new DumpableCollection("Handler attributes " + name, ((AttributesMap)getAttributes()).getAttributeEntrySet()),
|
||||
new DumpableCollection("Context attributes " + name, ((Context)getServletContext()).getAttributeEntrySet()),
|
||||
new DumpableCollection("Context attributes " + name, getServletContext().getAttributeEntrySet()),
|
||||
new DumpableCollection("EventListeners " + this, Arrays.asList(getEventListeners())),
|
||||
new DumpableCollection("Initparams " + name, getInitParams().entrySet())
|
||||
);
|
||||
}
|
||||
|
|
15
pom.xml
15
pom.xml
|
@ -37,7 +37,7 @@
|
|||
<junit.version>5.5.1</junit.version>
|
||||
<maven.version>3.6.0</maven.version>
|
||||
<maven.resolver.version>1.3.1</maven.resolver.version>
|
||||
<weld.version>2.4.5.Final</weld.version>
|
||||
<weld.version>3.1.2.Final</weld.version>
|
||||
<jetty.perf-helper.version>1.0.5</jetty.perf-helper.version>
|
||||
<unix.socket.tmp></unix.socket.tmp>
|
||||
<!-- enable or not TestTracker junit5 extension i.e log message when test method is starting -->
|
||||
|
@ -63,7 +63,7 @@
|
|||
otherwise depending on Spring Boot might be chicken and egg issue :) -->
|
||||
<springboot.version>2.1.1.RELEASE</springboot.version>
|
||||
<annotation-api.version>1.3.4</annotation-api.version>
|
||||
<jackson-databind.version>2.9.7</jackson-databind.version>
|
||||
<jackson-databind.version>2.9.9</jackson-databind.version>
|
||||
<localRepoPath>${project.build.directory}/local-repo</localRepoPath>
|
||||
<settingsPath>src/it/settings.xml</settingsPath>
|
||||
|
||||
|
@ -304,7 +304,7 @@
|
|||
<id>attach-sources</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<archive>
|
||||
|
@ -449,7 +449,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>build-resources</artifactId>
|
||||
<version>10.0.0-alpha0</version>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
|
@ -547,6 +547,11 @@
|
|||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
|
@ -1261,6 +1266,7 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- already part of the release-jetty.sh script
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
|
@ -1275,6 +1281,7 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
-->
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
|
|
@ -167,7 +167,7 @@ if proceedyn "Are you sure you want to release using above? (y/N)" n; then
|
|||
# This is equivalent to 'mvn release:perform'
|
||||
if proceedyn "Build/Deploy from tag $TAG_NAME? (Y/n)" y; then
|
||||
git checkout $TAG_NAME
|
||||
mvn clean package source:jar javadoc:jar gpg:sign deploy \
|
||||
mvn clean package gpg:sign javadoc:aggregate-jar deploy \
|
||||
-Peclipse-release $DEPLOY_OPTS
|
||||
reportMavenTestFailures
|
||||
git checkout $GIT_BRANCH_ID
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-cdi2-webapp</artifactId>
|
||||
<artifactId>test-weld-cdi-webapp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<scope>test</scope>
|
||||
|
|
|
@ -127,6 +127,16 @@ public class DistributionTester
|
|||
return start(Arrays.asList(args));
|
||||
}
|
||||
|
||||
public Path getJettyBase()
|
||||
{
|
||||
return config.jettyBase;
|
||||
}
|
||||
|
||||
public Path getJettyHome()
|
||||
{
|
||||
return config.jettyHome;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the distribution with the arguments
|
||||
*
|
||||
|
|
|
@ -19,85 +19,64 @@
|
|||
package org.eclipse.jetty.tests.distribution;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Disabled("Until issue is fixed at weld -> https://github.com/eclipse/jetty.project/issues/3803")
|
||||
public class CDITests extends AbstractDistributionTest
|
||||
{
|
||||
/**
|
||||
* Tests a WAR file that is CDI complete as it includes the weld
|
||||
* library in its WEB-INF/lib directory.
|
||||
*/
|
||||
@Test
|
||||
public void testCDI2_IncludedInWebapp() throws Exception
|
||||
// Tests from here use these parameters
|
||||
public static Stream<Arguments> tests()
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
.jettyVersion(jettyVersion)
|
||||
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
|
||||
.build();
|
||||
|
||||
String[] args1 = {
|
||||
"--create-startd",
|
||||
"--approve-all-licenses",
|
||||
"--add-to-start=http,deploy,annotations,jsp"
|
||||
};
|
||||
try (DistributionTester.Run run1 = distribution.start(args1))
|
||||
Consumer<DistributionTester> removeJettyWebXml = d ->
|
||||
{
|
||||
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
|
||||
assertEquals(0, run1.getExitValue());
|
||||
|
||||
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-cdi2-webapp:war:" + jettyVersion);
|
||||
distribution.installWarFile(war, "demo");
|
||||
|
||||
distribution.installBaseResource("cdi/demo_context.xml", "webapps/demo.xml");
|
||||
|
||||
int port = distribution.freePort();
|
||||
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
|
||||
try
|
||||
{
|
||||
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
|
||||
|
||||
startHttpClient();
|
||||
ContentResponse response = client.GET("http://localhost:" + port + "/demo/greetings");
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
// Confirm Servlet based CDI
|
||||
assertThat(response.getContentAsString(), containsString("Hello GreetingsServlet"));
|
||||
// Confirm Listener based CDI (this has been a problem in the past, keep this for regression testing!)
|
||||
assertThat(response.getHeaders().get("Server"), containsString("CDI-Demo-org.eclipse.jetty.test"));
|
||||
|
||||
run2.stop();
|
||||
assertTrue(run2.awaitFor(5, TimeUnit.SECONDS));
|
||||
Path jettyWebXml = d.getJettyBase().resolve("webapps/demo/WEB-INF/jetty-web.xml");
|
||||
Files.deleteIfExists(jettyWebXml);
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
// -- 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
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a WAR file that is expects CDI to be configured by the server.
|
||||
*
|
||||
* <p>
|
||||
* This means the WAR has the weld libs in its
|
||||
* WEB-INF/lib directory.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The expectation is that a context xml deployable file is not
|
||||
* required when using this `cdi2` module, and the appropriate
|
||||
* server side libs are made available to allow weld to function.
|
||||
* (the required server side javax.el support comes from the cdi2 module)
|
||||
* </p>
|
||||
* Tests a WAR file that includes the CDI
|
||||
* library in its WEB-INF/lib directory.
|
||||
*/
|
||||
@Test
|
||||
public void testCDI2_ConfiguredByServer() throws Exception
|
||||
@ParameterizedTest
|
||||
@MethodSource("tests")
|
||||
public void testCDIIncludedInWebapp(String implementation, String integration, Consumer<DistributionTester> configure) throws Exception
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
|
@ -108,18 +87,17 @@ public class CDITests extends AbstractDistributionTest
|
|||
String[] args1 = {
|
||||
"--create-startd",
|
||||
"--approve-all-licenses",
|
||||
// standard entries
|
||||
"--add-to-start=http,deploy,annotations",
|
||||
// cdi2 specific entry (should transitively pull in what it needs, the user should not be expected to know the transitive entries)
|
||||
"--add-to-start=cdi2"
|
||||
"--add-to-start=http,deploy,annotations,jsp,"+integration
|
||||
};
|
||||
try (DistributionTester.Run run1 = distribution.start(args1))
|
||||
{
|
||||
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
|
||||
assertEquals(0, run1.getExitValue());
|
||||
|
||||
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-cdi2-webapp:war:" + jettyVersion);
|
||||
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-" + implementation + "-cdi-webapp:war:" + jettyVersion);
|
||||
distribution.installWarFile(war, "demo");
|
||||
if (configure != null)
|
||||
configure.accept(distribution);
|
||||
|
||||
int port = distribution.freePort();
|
||||
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
<module>test-http2-webapp</module>
|
||||
<module>test-simple-webapp</module>
|
||||
<module>test-felix-webapp</module>
|
||||
<module>test-cdi2-webapp</module>
|
||||
<module>test-cdi-common-webapp</module>
|
||||
<module>test-weld-cdi-webapp</module>
|
||||
<module>test-owb-cdi-webapp</module>
|
||||
</modules>
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-webapps-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>test-cdi-common-webapp</artifactId>
|
||||
<name>Test :: CDI :: Common Demo Webapp</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.cdi.common</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!-- provided by container -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<version>1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.enterprise</groupId>
|
||||
<artifactId>cdi-api</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -37,6 +37,8 @@ public class GreetingsServlet extends HttpServlet
|
|||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
|
||||
{
|
||||
resp.setContentType("text/plain");
|
||||
resp.getWriter().println("[" + greetings.getGreeting() + "]");
|
||||
resp.getWriter().print(greetings.getGreeting());
|
||||
resp.getWriter().print(" from ");
|
||||
resp.getWriter().println(getServletContext().getAttribute("ServerID"));
|
||||
}
|
||||
}
|
|
@ -21,8 +21,11 @@ package org.eclipse.jetty.test;
|
|||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Set;
|
||||
import javax.enterprise.inject.Any;
|
||||
import javax.enterprise.inject.spi.Bean;
|
||||
import javax.enterprise.inject.spi.BeanManager;
|
||||
import javax.enterprise.inject.spi.InjectionPoint;
|
||||
import javax.enterprise.util.AnnotationLiteral;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
@ -43,10 +46,12 @@ public class InfoServlet extends HttpServlet
|
|||
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.println("Bean Manager: " + beanManager);
|
||||
Set<Bean<?>> beans = beanManager.getBeans("");
|
||||
Set<Bean<?>> beans = beanManager.getBeans(Object.class, new AnnotationLiteral<Any>() {});
|
||||
for (Bean<?> bean : beans)
|
||||
{
|
||||
out.println(" " + bean);
|
||||
out.printf("%16s => %s%n", bean.getName(), bean);
|
||||
for (InjectionPoint ij : bean.getInjectionPoints())
|
||||
out.printf("%16s -> %s%n", "", ij);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ public class MyContextListener implements ServletContextListener
|
|||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
if (serverId == null)
|
||||
throw new IllegalStateException("CDI did not inject!");
|
||||
sce.getServletContext().setAttribute("ServerID", serverId.get());
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<H1>OWB CDI Test Webapp</H1>
|
||||
|
||||
<H2>CDI Info</H2>
|
||||
<iframe src="info" width="100%" height="60%"></iframe>
|
||||
|
||||
<H2>Greetings test</H2>
|
||||
<iframe src="greetings" width="100%"></iframe>
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-webapps-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>test-owb-cdi-webapp</artifactId>
|
||||
<name>Test :: CDI On Jetty :: OWB Demo Webapp</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.cdi.owb</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<finalName>weld-owb-demo</finalName>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-cdi-common-webapp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- included in webapp -->
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-annotation_1.3_spec</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-jcdi_2.0_spec</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-atinject_1.0_spec</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-interceptor_1.2_spec</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.openwebbeans</groupId>
|
||||
<artifactId>openwebbeans-web</artifactId>
|
||||
<!-- TODO remove OwbServletContainerInitializer when updated to a version with https://issues.apache.org/jira/browse/OWB-1296 -->
|
||||
<version>2.0.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.openwebbeans</groupId>
|
||||
<artifactId>openwebbeans-jetty9</artifactId>
|
||||
<version>2.0.11</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.owb;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.webbeans.servlet.WebBeansConfigurationListener;
|
||||
|
||||
/**
|
||||
* @deprecated This class will not be required once https://issues.apache.org/jira/browse/OWB-1296 is available
|
||||
*/
|
||||
@Deprecated
|
||||
public class OwbServletContainerInitializer implements ServletContainerInitializer
|
||||
{
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException
|
||||
{
|
||||
Listener listener = new Listener();
|
||||
listener.preInitialize(new ServletContextEvent(ctx));
|
||||
ctx.addListener(listener);
|
||||
}
|
||||
|
||||
public static class Listener extends WebBeansConfigurationListener
|
||||
{
|
||||
void preInitialize(ServletContextEvent event)
|
||||
{
|
||||
super.contextInitialized(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent event)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.cdi.owb.OwbServletContainerInitializer
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure id="webAppCtx" class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<New id="BeanManager" class="org.eclipse.jetty.plus.jndi.Resource">
|
||||
<Arg>
|
||||
<Ref refid="webAppCtx"/>
|
||||
</Arg>
|
||||
<Arg>BeanManager</Arg>
|
||||
<Arg>
|
||||
<New class="javax.naming.Reference">
|
||||
<Arg>javax.enterprise.inject.spi.BeanManager</Arg>
|
||||
<Arg>org.apache.webbeans.container.ManagerObjectFactory</Arg>
|
||||
<Arg/>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Configure>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure id="wac" class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<!-- This file is only needed for cdi2 integration and should be removed if using the cdi module -->
|
||||
<Get id="wal" name="classLoader"/>
|
||||
<Get id="objf" name="objectFactory">
|
||||
<Call name="addDecorator">
|
||||
<Arg>
|
||||
<New class="org.apache.webbeans.web.jetty9.JettyDecorator">
|
||||
<Arg><Ref refid="wal"/></Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Get>
|
||||
</Configure>
|
|
@ -3,11 +3,9 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
|
||||
version="3.1">
|
||||
<display-name>CDI Integration Test WebApp</display-name>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
|
||||
</listener>
|
||||
<display-name>OWB CDI Integration Test WebApp</display-name>
|
||||
|
||||
<!-- OWB Listener is added by OwbServletContainerInitializer -->
|
||||
|
||||
<resource-env-ref>
|
||||
<description>Object factory for the CDI Bean Manager</description>
|
|
@ -19,8 +19,6 @@
|
|||
package com.acme.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -63,17 +61,13 @@ public class MultiPartTest extends HttpServlet
|
|||
out.println("<p>");
|
||||
|
||||
Collection<Part> parts = request.getParts();
|
||||
out.println("<b>Parts:</b> " + parts.size());
|
||||
out.println("<b>Parts:</b> " + parts.size() + "<br>");
|
||||
for (Part p : parts)
|
||||
{
|
||||
out.println("<h3>" + p.getName() + "</h3>");
|
||||
out.println("<b>Size:</b> " + p.getSize());
|
||||
if (p.getContentType() == null || p.getContentType().startsWith("text/plain"))
|
||||
{
|
||||
out.println("<p>");
|
||||
copy(p.getInputStream(), out);
|
||||
out.println("</p>");
|
||||
}
|
||||
out.println("<br><b>PartName:</b> " + sanitizeXmlString(p.getName()));
|
||||
out.println("<br><b>Size:</b> " + p.getSize());
|
||||
String contentType = p.getContentType();
|
||||
out.println("<br><b>ContentType:</b> " + contentType);
|
||||
}
|
||||
out.println("</body>");
|
||||
out.println("</html>");
|
||||
|
@ -109,20 +103,67 @@ public class MultiPartTest extends HttpServlet
|
|||
}
|
||||
}
|
||||
|
||||
// TODO remove inline once 9.4.19 is released with a fix for #3726
|
||||
public static void copy(InputStream in,
|
||||
OutputStream out)
|
||||
throws IOException
|
||||
public static String sanitizeXmlString(String html)
|
||||
{
|
||||
int bufferSize = 8192;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
if (html == null)
|
||||
return null;
|
||||
|
||||
while (true)
|
||||
int i = 0;
|
||||
|
||||
// Are there any characters that need sanitizing?
|
||||
loop:
|
||||
for (; i < html.length(); i++)
|
||||
{
|
||||
int len = in.read(buffer, 0, bufferSize);
|
||||
if (len < 0)
|
||||
break;
|
||||
out.write(buffer, 0, len);
|
||||
char c = html.charAt(i);
|
||||
switch (c)
|
||||
{
|
||||
case '&':
|
||||
case '<':
|
||||
case '>':
|
||||
case '\'':
|
||||
case '"':
|
||||
break loop;
|
||||
default:
|
||||
if (Character.isISOControl(c) && !Character.isWhitespace(c))
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
// No characters need sanitizing, so return original string
|
||||
if (i == html.length())
|
||||
return html;
|
||||
|
||||
// Create builder with OK content so far
|
||||
StringBuilder out = new StringBuilder(html.length() * 4 / 3);
|
||||
out.append(html, 0, i);
|
||||
|
||||
// sanitize remaining content
|
||||
for (; i < html.length(); i++)
|
||||
{
|
||||
char c = html.charAt(i);
|
||||
switch (c)
|
||||
{
|
||||
case '&':
|
||||
out.append("&");
|
||||
break;
|
||||
case '<':
|
||||
out.append("<");
|
||||
break;
|
||||
case '>':
|
||||
out.append(">");
|
||||
break;
|
||||
case '\'':
|
||||
out.append("'");
|
||||
break;
|
||||
case '"':
|
||||
out.append(""");
|
||||
break;
|
||||
default:
|
||||
if (Character.isISOControl(c) && !Character.isWhitespace(c))
|
||||
out.append('?');
|
||||
else
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,30 +7,32 @@
|
|||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>test-cdi2-webapp</artifactId>
|
||||
<name>Test :: CDI2 On Jetty :: Included in WebApp</name>
|
||||
<artifactId>test-weld-cdi-webapp</artifactId>
|
||||
<name>Test :: CDI On Jetty :: Weld Demo Webapp</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.cdi2.webapp</bundle-symbolic-name>
|
||||
<bundle-symbolic-name>${project.groupId}.cdi.weld</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<finalName>cdi2-demo</finalName>
|
||||
<finalName>weld-cdi-demo</finalName>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<!-- provided by container -->
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>provided</scope>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-cdi-common-webapp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- included in webapp -->
|
||||
<dependency>
|
||||
<groupId>org.jboss.weld.servlet</groupId>
|
||||
<artifactId>weld-servlet</artifactId>
|
||||
<artifactId>weld-servlet-core</artifactId>
|
||||
<version>${weld.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
|
@ -15,4 +15,4 @@
|
|||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Configure>
|
||||
</Configure>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0"?>
|
||||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
|
||||
version="3.1">
|
||||
<display-name>Weld CDI Integration Test WebApp</display-name>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
|
||||
</listener>
|
||||
|
||||
<resource-env-ref>
|
||||
<description>Object factory for the CDI Bean Manager</description>
|
||||
<resource-env-ref-name>BeanManager</resource-env-ref-name>
|
||||
<resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
|
||||
</resource-env-ref>
|
||||
</web-app>
|
Loading…
Reference in New Issue