Jetty-12 Restructure

Copied ee10 from hackathon branch

Co-authored-by: Greg Wilkins <gregw@webtide.com>
Co-authored-by: Jan Bartel <janb@webtide.com>
Co-authored-by: Joakim Erdfelt <joakim@webtide.com>
Co-authored-by: Lachlan Roberts <lachlan@webtide.com>
Co-authored-by: Ludovic Orban <lorban@webtide.com>
Co-authored-by: Olivier Lamy <olamy@webtide.com>
Co-authored-by: Simone Bordet <sbordet@webtide.com>
This commit is contained in:
Greg Wilkins 2022-05-03 15:52:44 +02:00
parent 04acdb72f0
commit 0a32147e89
2264 changed files with 279102 additions and 0 deletions

View File

@ -0,0 +1,95 @@
<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.ee10</groupId>
<artifactId>jetty-ee10</artifactId>
<version>12.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ee10-annotations</artifactId>
<name>EE10 :: Jetty :: Servlet Annotations</name>
<description>Annotation support for deploying servlets in jetty.</description>
<properties>
<bundle-symbolic-name>${project.groupId}.annotations</bundle-symbolic-name>
<spotbugs.onlyAnalyze>org.eclipse.annotations.*</spotbugs.onlyAnalyze>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine} ${jetty.surefire.argLine} --add-opens org.eclipse.jetty.ee10.annotations/org.eclipse.jetty.ee10.annotations.resources=org.eclipse.jetty.ee10.plus
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>${osgi.slf4j.import.packages},org.objectweb.asm;version="[$(version;==;${asm.version}),$(version;+;${asm.version}))",*</Import-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)", osgi.serviceloader; filter:="(osgi.serviceloader=jakarta.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional
</Require-Capability>
<Provide-Capability>
osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.ee10.webapp.Configuration
</Provide-Capability>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-plus</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-webapp</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-jakarta-servlet-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jndi</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,15 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Enables Annotation scanning for deployed web applications.
[depend]
plus
[lib]
lib/jetty-annotations-${jetty.version}.jar
lib/annotations/*.jar
[jpms]
add-modules:org.objectweb.asm

View File

@ -0,0 +1,32 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration;
import org.eclipse.jetty.ee10.webapp.Configuration;
module org.eclipse.jetty.ee10.annotations
{
requires jakarta.annotation;
requires java.naming;
requires org.slf4j;
requires transitive org.eclipse.jetty.ee10.plus;
requires transitive org.objectweb.asm;
exports org.eclipse.jetty.ee10.annotations;
uses jakarta.servlet.ServletContainerInitializer;
provides Configuration with
AnnotationConfiguration;
}

View File

@ -0,0 +1,37 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
/**
* DiscoverableAnnotationHandler
*
* Base class for handling the discovery of an annotation.
*/
public abstract class AbstractDiscoverableAnnotationHandler extends AnnotationParser.AbstractHandler
{
protected WebAppContext _context;
public AbstractDiscoverableAnnotationHandler(WebAppContext context)
{
_context = context;
}
public void addAnnotation(DiscoveredAnnotation a)
{
_context.getMetaData().addDiscoveredAnnotation(a);
}
}

View File

@ -0,0 +1,84 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.Objects;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.Decorator;
/**
* AnnotationDecorator
*/
public class AnnotationDecorator implements Decorator
{
protected AnnotationIntrospector _introspector;
protected WebAppContext _context;
public AnnotationDecorator(WebAppContext context)
{
_context = Objects.requireNonNull(context);
_introspector = new AnnotationIntrospector(_context);
registerHandlers();
}
private void registerHandlers()
{
_introspector.registerHandler(new ResourceAnnotationHandler(_context));
_introspector.registerHandler(new ResourcesAnnotationHandler(_context));
_introspector.registerHandler(new RunAsAnnotationHandler(_context));
_introspector.registerHandler(new PostConstructAnnotationHandler(_context));
_introspector.registerHandler(new PreDestroyAnnotationHandler(_context));
_introspector.registerHandler(new DeclareRolesAnnotationHandler(_context));
_introspector.registerHandler(new MultiPartConfigAnnotationHandler(_context));
_introspector.registerHandler(new ServletSecurityAnnotationHandler(_context));
}
/**
* Look for annotations that can be discovered with introspection:
* <ul>
* <li> Resource </li>
* <li> Resources </li>
* <li> RunAs </li>
* <li> PostConstruct </li>
* <li> PreDestroy </li>
* <li> DeclareRoles </li>
* <li> MultiPart </li>
* <li> ServletSecurity</li>
* </ul>
*
* @param o the object to introspect
* @param metaInfo information about the object to introspect
*/
protected void introspect(Object o, Object metaInfo)
{
if (o == null)
return;
_introspector.introspect(o, metaInfo);
}
@Override
public Object decorate(Object o)
{
introspect(o, DecoratedObjectFactory.getAssociatedInfo());
return o;
}
@Override
public void destroy(Object o)
{
}
}

View File

@ -0,0 +1,223 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.eclipse.jetty.ee10.servlet.BaseHolder;
import org.eclipse.jetty.ee10.servlet.Source.Origin;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebDescriptor;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AnnotationIntrospector
* Introspects a class to find various types of
* annotations as defined by the servlet specification.
*/
public class AnnotationIntrospector
{
private static final Logger LOG = LoggerFactory.getLogger(AnnotationIntrospector.class);
private final AutoLock _lock = new AutoLock();
private final Set<Class<?>> _introspectedClasses = new HashSet<>();
private final List<IntrospectableAnnotationHandler> _handlers = new ArrayList<IntrospectableAnnotationHandler>();
private final WebAppContext _context;
/**
* IntrospectableAnnotationHandler
*
* Interface for all handlers that wish to introspect a class to find a particular annotation
*/
public interface IntrospectableAnnotationHandler
{
public void handle(Class<?> clazz);
}
/**
* AbstractIntrospectableAnnotationHandler
*
* Base class for handlers that introspect a class to find a particular annotation.
* A handler can optionally introspect the parent hierarchy of a class.
*/
public abstract static class AbstractIntrospectableAnnotationHandler implements IntrospectableAnnotationHandler
{
protected boolean _introspectAncestors;
protected WebAppContext _context;
public abstract void doHandle(Class<?> clazz);
public AbstractIntrospectableAnnotationHandler(boolean introspectAncestors, WebAppContext context)
{
_context = Objects.requireNonNull(context);
_introspectAncestors = introspectAncestors;
}
@Override
public void handle(Class<?> clazz)
{
Class<?> c = clazz;
//process the whole inheritance hierarchy for the class
while (c != null && (!c.equals(Object.class)))
{
doHandle(c);
if (!_introspectAncestors)
break;
c = c.getSuperclass();
}
}
public WebAppContext getContext()
{
return _context;
}
}
public AnnotationIntrospector(WebAppContext context)
{
_context = Objects.requireNonNull(context);
}
public void registerHandler(IntrospectableAnnotationHandler handler)
{
_handlers.add(handler);
}
/**
* Test if an object should be introspected for some specific types of annotations
* like PostConstruct/PreDestroy/MultiPart etc etc.
*
* According to servlet 4.0, these types of annotations should only be evaluated iff any
* of the following are true:
* <ol>
* <li>the object was created by the jakarta.servlet.ServletContext.createServlet/Filter/Listener method</li>
* <li>the object comes either from a discovered annotation (WebServlet/Filter/Listener) or a declaration
* in a descriptor AND web.xml is NOT metadata-complete AND any web-fragment.xml associated with the location of
* the class is NOT metadata-complete</li>
* </ol>
*
* We also support evaluations of these types of annotations for objects that were created directly
* by the jetty api.
*
* @param o the object to check for its ability to be introspected for annotations
* @param metaInfo meta information about the object to be introspected
* @return true if it can be introspected according to servlet 4.0 rules
*/
public boolean isIntrospectable(Object o, Object metaInfo)
{
if (o == null)
return false; //nothing to introspect
if (metaInfo == null)
return true; //no information about the object to introspect, assume introspectable
@SuppressWarnings("rawtypes")
BaseHolder holder = null;
try
{
holder = (BaseHolder)metaInfo;
}
catch (ClassCastException e)
{
LOG.warn("Not introspectable {}", metaInfo.getClass().getName(), e);
return true; //not the type of information we were expecting, assume introspectable
}
Origin origin = (holder.getSource() == null ? null : holder.getSource().getOrigin());
if (origin == null)
return true; //assume introspectable
switch (origin)
{
case EMBEDDED:
case JAKARTA_API:
{
return true; //objects created from the jetty or servlet api are always introspectable
}
case ANNOTATION:
{
return true; //we will have discovered annotations only if metadata-complete==false
}
default:
{
//must be from a descriptor. Only introspect if the descriptor with which it was associated
//is not metadata-complete
if (_context.getMetaData().isMetaDataComplete())
return false;
String descriptorLocation = holder.getSource().getResource();
if (descriptorLocation == null)
return true; //no descriptor, can't be metadata-complete
try
{
return !WebDescriptor.isMetaDataComplete(_context.getMetaData().getFragmentDescriptor(Resource.newResource(descriptorLocation)));
}
catch (IOException e)
{
LOG.warn("Unable to get Resource for descriptor {}", descriptorLocation, e);
return false; //something wrong with the descriptor
}
}
}
}
/**
*
*/
public void introspect(Object o, Object metaInfo)
{
if (!isIntrospectable(o, metaInfo))
return;
Class<?> clazz = o.getClass();
try (AutoLock l = _lock.lock())
{
// Lock to ensure that only 1 thread can be introspecting, and that
// thread must have fully finished generating the products of
// the introspection before another thread is allowed in.
// We remember the classes that we have introspected to avoid
// reprocessing the same class.
if (_introspectedClasses.add(clazz))
{
for (IntrospectableAnnotationHandler handler : _handlers)
{
try
{
handler.handle(clazz);
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
}
}
}

View File

@ -0,0 +1,978 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.util.JavaVersion;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiReleaseJarFile;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AnnotationParser
* <p>
* Use asm to scan classes for annotations. A SAX-style parsing is done.
* Handlers are registered which will be called back when various types of
* entity are encountered, eg a class, a method, a field.
* <p>
* Handlers are not called back in any particular order and are assumed
* to be order-independent.
* <p>
* As a registered Handler will be called back for each annotation discovered
* on a class, a method, a field, the Handler should test to see if the annotation
* is one that it is interested in.
* <p>
* For the servlet spec, we are only interested in annotations on classes, methods and fields,
* so the callbacks for handling finding a class, a method a field are themselves
* not fully implemented.
*/
public class AnnotationParser
{
private static final Logger LOG = LoggerFactory.getLogger(AnnotationParser.class);
private static final int ASM_VERSION = asmVersion();
/**
* Map of classnames scanned and the first location from which scan occurred
*/
protected Map<String, Resource> _parsedClassNames = new ConcurrentHashMap<>();
private final int _javaPlatform;
private final int _asmVersion;
/**
* Determine the runtime version of asm.
*
* @return the {@link org.objectweb.asm.Opcodes} ASM value matching the runtime version of asm.
*/
private static int asmVersion()
{
// Find the highest available ASM version on the runtime/classpath, because
// if we run with a lower than available ASM version, against a class with
// new language features we'll get an UnsupportedOperationException, even if
// the ASM version supports the new language features.
// Also, if we run with a higher than available ASM version, we'll get
// an IllegalArgumentException from org.objectweb.asm.ClassVisitor.
// So must find exactly the maximum ASM api version available.
Optional<Integer> asmVersion = Arrays.stream(Opcodes.class.getFields()).sequential()
.filter((f) -> f.getName().matches("ASM[0-9]+"))
.map((f) -> f.getName().substring(3))
.map(Integer::parseInt)
.max(Integer::compareTo);
if (!asmVersion.isPresent())
throw new IllegalStateException("Invalid " + Opcodes.class.getName());
int asmFieldId = asmVersion.get();
try
{
String fieldName = "ASM" + asmFieldId;
if (LOG.isDebugEnabled())
LOG.debug("Using ASM API from {}.{}", Opcodes.class.getName(), fieldName);
return (int)Opcodes.class.getField(fieldName).get(null);
}
catch (Throwable e)
{
throw new IllegalStateException(e);
}
}
/**
* Convert internal name to simple name
*
* @param name the internal name
* @return the simple name
*/
public static String normalize(String name)
{
if (name == null)
return null;
if (name.startsWith("L") && name.endsWith(";"))
name = name.substring(1, name.length() - 1);
if (name.endsWith(".class"))
name = name.substring(0, name.length() - ".class".length());
return StringUtil.replace(name, '/', '.');
}
/**
* Convert internal names to simple names.
*
* @param list the list of internal names
* @return the list of simple names
*/
public static String[] normalize(String[] list)
{
if (list == null)
return null;
String[] normalList = new String[list.length];
int i = 0;
for (String s : list)
{
normalList[i++] = normalize(s);
}
return normalList;
}
/**
* Immutable information gathered by parsing class header.
*/
public static class ClassInfo
{
final Resource _containingResource;
final String _className;
final int _version;
final int _access;
final String _signature;
final String _superName;
final String[] _interfaces;
public ClassInfo(Resource resource, String className, int version, int access, String signature, String superName, String[] interfaces)
{
super();
_containingResource = resource;
_className = className;
_version = version;
_access = access;
_signature = signature;
_superName = superName;
_interfaces = interfaces;
}
public String getClassName()
{
return _className;
}
public int getVersion()
{
return _version;
}
public int getAccess()
{
return _access;
}
public String getSignature()
{
return _signature;
}
public String getSuperName()
{
return _superName;
}
public String[] getInterfaces()
{
return _interfaces;
}
public Resource getContainingResource()
{
return _containingResource;
}
}
/**
* Immutable information gathered by parsing a method on a class.
*/
public static class MethodInfo
{
final ClassInfo _classInfo;
final String _methodName;
final int _access;
final String _desc;
final String _signature;
final String[] _exceptions;
public MethodInfo(ClassInfo classInfo, String methodName, int access, String desc, String signature, String[] exceptions)
{
super();
_classInfo = classInfo;
_methodName = methodName;
_access = access;
_desc = desc;
_signature = signature;
_exceptions = exceptions;
}
public ClassInfo getClassInfo()
{
return _classInfo;
}
public String getMethodName()
{
return _methodName;
}
public int getAccess()
{
return _access;
}
public String getDesc()
{
return _desc;
}
public String getSignature()
{
return _signature;
}
public String[] getExceptions()
{
return _exceptions;
}
}
/**
* Immutable information gathered by parsing a field on a class.
*/
public static class FieldInfo
{
final ClassInfo _classInfo;
final String _fieldName;
final int _access;
final String _fieldType;
final String _signature;
final Object _value;
public FieldInfo(ClassInfo classInfo, String fieldName, int access, String fieldType, String signature, Object value)
{
super();
_classInfo = classInfo;
_fieldName = fieldName;
_access = access;
_fieldType = fieldType;
_signature = signature;
_value = value;
}
public ClassInfo getClassInfo()
{
return _classInfo;
}
public String getFieldName()
{
return _fieldName;
}
public int getAccess()
{
return _access;
}
public String getFieldType()
{
return _fieldType;
}
public String getSignature()
{
return _signature;
}
public Object getValue()
{
return _value;
}
}
/**
* Signature for all handlers that respond to parsing class files.
*/
public static interface Handler
{
public void handle(ClassInfo classInfo);
public void handle(MethodInfo methodInfo);
public void handle(FieldInfo fieldInfo);
public void handle(ClassInfo info, String annotationName);
public void handle(MethodInfo info, String annotationName);
public void handle(FieldInfo info, String annotationName);
}
/**
* Convenience base class to provide no-ops for all Handler methods.
*/
public abstract static class AbstractHandler implements Handler
{
@Override
public void handle(ClassInfo classInfo)
{
}
@Override
public void handle(MethodInfo methodInfo)
{
}
@Override
public void handle(FieldInfo fieldInfo)
{
}
@Override
public void handle(ClassInfo info, String annotationName)
{
}
@Override
public void handle(MethodInfo info, String annotationName)
{
}
@Override
public void handle(FieldInfo info, String annotationName)
{
}
}
/**
* ASM Visitor for parsing a method. We are only interested in the annotations on methods.
*/
public static class MyMethodVisitor extends MethodVisitor
{
final MethodInfo _mi;
final Set<? extends Handler> _handlers;
public MyMethodVisitor(final Set<? extends Handler> handlers,
final ClassInfo classInfo,
final int access,
final String name,
final String methodDesc,
final String signature,
final String[] exceptions,
final int asmVersion)
{
super(asmVersion);
_handlers = handlers;
_mi = new MethodInfo(classInfo, name, access, methodDesc, signature, exceptions);
}
/**
* We are only interested in finding the annotations on methods.
*/
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
String annotationName = normalize(desc);
for (Handler h : _handlers)
{
h.handle(_mi, annotationName);
}
return null;
}
}
/**
* An ASM visitor for parsing Fields.
* We are only interested in visiting annotations on Fields.
*/
public static class MyFieldVisitor extends FieldVisitor
{
final FieldInfo _fieldInfo;
final Set<? extends Handler> _handlers;
public MyFieldVisitor(final Set<? extends Handler> handlers,
final ClassInfo classInfo,
final int access,
final String fieldName,
final String fieldType,
final String signature,
final Object value,
final int asmVersion)
{
super(asmVersion);
_handlers = handlers;
_fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value);
}
/**
* Parse an annotation found on a Field.
*/
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
String annotationName = normalize(desc);
for (Handler h : _handlers)
{
h.handle(_fieldInfo, annotationName);
}
return null;
}
}
/**
* ASM visitor for a class.
*/
public static class MyClassVisitor extends ClassVisitor
{
final int _asmVersion;
final Resource _containingResource;
final Set<? extends Handler> _handlers;
ClassInfo _ci;
public MyClassVisitor(Set<? extends Handler> handlers, Resource containingResource, int asmVersion)
{
super(asmVersion);
_asmVersion = asmVersion;
_handlers = handlers;
_containingResource = containingResource;
}
@Override
public void visit(final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces)
{
_ci = new ClassInfo(_containingResource, normalize(name), version, access, signature, normalize(superName), normalize(interfaces));
for (Handler h : _handlers)
{
h.handle(_ci);
}
}
/**
* Visit an annotation on a Class
*/
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
String annotationName = normalize(desc);
for (Handler h : _handlers)
{
h.handle(_ci, annotationName);
}
return null;
}
/**
* Visit a method to extract its annotations
*/
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String methodDesc,
final String signature,
final String[] exceptions)
{
return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions, _asmVersion);
}
/**
* Visit a field to extract its annotations
*/
@Override
public FieldVisitor visitField(final int access,
final String fieldName,
final String fieldType,
final String signature,
final Object value)
{
return new MyFieldVisitor(_handlers, _ci, access, fieldName, fieldType, signature, value, _asmVersion);
}
}
public AnnotationParser()
{
this(JavaVersion.VERSION.getPlatform());
}
/**
* @param javaPlatform The target java version or 0 for the current runtime.
*/
public AnnotationParser(int javaPlatform)
{
_asmVersion = ASM_VERSION;
if (javaPlatform == 0)
javaPlatform = JavaVersion.VERSION.getPlatform();
_javaPlatform = javaPlatform;
}
public AnnotationParser(int javaPlatform, int asmVersion)
{
if (javaPlatform == 0)
javaPlatform = JavaVersion.VERSION.getPlatform();
_javaPlatform = javaPlatform;
if (asmVersion == 0)
asmVersion = ASM_VERSION;
_asmVersion = asmVersion;
}
/**
* Add a class as having been parsed.
*
* @param classname the name of the class
* @param location the fully qualified location of the class
*/
public void addParsedClass(String classname, Resource location)
{
Resource existing = _parsedClassNames.putIfAbsent(classname, location);
if (existing != null)
LOG.warn("{} scanned from multiple locations: {}, {}", classname, existing, location);
}
/**
* Parse a given class
*
* @param handlers the set of handlers to find class
* @param className the class name to parse
* @throws Exception if unable to parse
*/
public void parse(Set<? extends Handler> handlers, String className) throws Exception
{
if (className == null)
return;
String classRef = TypeUtil.toClassReference(className);
URL resource = Loader.getResource(classRef);
if (resource != null)
{
Resource r = Resource.newResource(resource);
addParsedClass(className, r);
try (InputStream is = r.getInputStream())
{
scanClass(handlers, null, is);
}
}
}
/**
* Parse the given class, optionally walking its inheritance hierarchy
*
* @param handlers the handlers to look for class in
* @param clazz the class to look for
* @param visitSuperClasses if true, also visit super classes for parse
* @throws Exception if unable to parse class
*/
public void parse(Set<? extends Handler> handlers, Class<?> clazz, boolean visitSuperClasses) throws Exception
{
Class<?> cz = clazz;
while (cz != Object.class)
{
String nameAsResource = TypeUtil.toClassReference(cz);
URL resource = Loader.getResource(nameAsResource);
if (resource != null)
{
Resource r = Resource.newResource(resource);
addParsedClass(clazz.getName(), r);
try (InputStream is = r.getInputStream())
{
scanClass(handlers, null, is);
}
}
if (visitSuperClasses)
cz = cz.getSuperclass();
else
break;
}
}
/**
* Parse the given classes
*
* @param handlers the set of handlers to look for class in
* @param classNames the class name
* @throws Exception if unable to parse
*/
public void parse(Set<? extends Handler> handlers, String[] classNames) throws Exception
{
if (classNames == null)
return;
parse(handlers, Arrays.asList(classNames));
}
/**
* Parse the given classes
*
* @param handlers the set of handlers to look for class in
* @param classNames the class names
* @throws Exception if unable to parse
*/
public void parse(Set<? extends Handler> handlers, List<String> classNames) throws Exception
{
MultiException me = new MultiException();
for (String className : classNames)
{
try
{
String classRef = TypeUtil.toClassReference(className);
URL resource = Loader.getResource(classRef);
if (resource != null)
{
Resource r = Resource.newResource(resource);
addParsedClass(className, r);
try (InputStream is = r.getInputStream())
{
scanClass(handlers, null, is);
}
}
}
catch (Exception e)
{
me.add(new RuntimeException("Error scanning class " + className, e));
}
}
me.ifExceptionThrow();
}
/**
* Parse classes in the supplied uris.
*
* @param handlers the handlers to look for classes in
* @param uris the uris for the jars
* @throws Exception if unable to parse
*/
public void parse(final Set<? extends Handler> handlers, final URI[] uris) throws Exception
{
if (uris == null)
return;
MultiException me = new MultiException();
for (URI uri : uris)
{
try
{
parse(handlers, uri);
}
catch (Exception e)
{
me.add(new RuntimeException("Problem parsing classes from " + uri, e));
}
}
me.ifExceptionThrow();
}
/**
* Parse a particular uri
*
* @param handlers the handlers to look for classes in
* @param uri the uri for the jar
* @throws Exception if unable to parse
*/
public void parse(final Set<? extends Handler> handlers, URI uri) throws Exception
{
if (uri == null)
return;
parse(handlers, Resource.newResource(uri));
}
/**
* Parse a resource
*
* @param handlers the handlers to look for classes in
* @param r the resource to parse
* @throws Exception if unable to parse
*/
public void parse(final Set<? extends Handler> handlers, Resource r) throws Exception
{
if (r == null)
return;
if (r.exists() && r.isDirectory())
{
parseDir(handlers, r);
return;
}
String fullname = r.toString();
if (fullname.endsWith(".jar"))
{
parseJar(handlers, r);
return;
}
if (fullname.endsWith(".class"))
{
try (InputStream is = r.getInputStream())
{
scanClass(handlers, null, is);
return;
}
}
if (LOG.isDebugEnabled())
LOG.warn("Resource not scannable for classes: {}", r);
}
/**
* Parse all classes in a directory
*
* @param handlers the set of handlers to look for classes in
* @param root the resource directory to look for classes
* @throws Exception if unable to parse
*/
protected void parseDir(Set<? extends Handler> handlers, Resource root) throws Exception
{
if (!root.isDirectory() || !root.exists() || root.getName().startsWith("."))
return;
if (LOG.isDebugEnabled())
LOG.debug("Scanning dir {}", root);
File rootFile = root.getFile();
MultiException me = new MultiException();
Collection<Resource> resources = root.getAllResources();
if (resources != null)
{
for (Resource r : resources)
{
if (r.isDirectory())
continue;
File file = r.getFile();
if (isValidClassFileName((file == null ? null : file.getName())))
{
Path classpath = rootFile.toPath().relativize(file.toPath());
String str = classpath.toString();
str = str.substring(0, str.lastIndexOf(".class"));
str = StringUtil.replace(str, File.separatorChar, '.');
try
{
if (LOG.isDebugEnabled())
LOG.debug("Scanning class {}", r);
addParsedClass(str, r);
try (InputStream is = r.getInputStream())
{
scanClass(handlers, Resource.newResource(file.getParentFile()), is);
}
}
catch (Exception ex)
{
if (LOG.isDebugEnabled())
LOG.debug("Error scanning file {}", file, ex);
me.add(new RuntimeException("Error scanning file " + file, ex));
}
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Skipping scan on invalid file {}", file);
}
}
}
me.ifExceptionThrow();
}
/**
* Parse a resource that is a jar file.
*
* @param handlers the handlers to look for classes in
* @param jarResource the jar resource to parse
* @throws Exception if unable to parse
*/
protected void parseJar(Set<? extends Handler> handlers, Resource jarResource) throws Exception
{
if (jarResource == null)
return;
if (jarResource.toString().endsWith(".jar"))
{
if (LOG.isDebugEnabled())
LOG.debug("Scanning jar {}", jarResource);
MultiException me = new MultiException();
try (MultiReleaseJarFile jarFile = new MultiReleaseJarFile(jarResource.getFile(), _javaPlatform, false))
{
jarFile.stream().forEach(e ->
{
try
{
parseJarEntry(handlers, jarResource, e);
}
catch (Exception ex)
{
me.add(new RuntimeException("Error scanning entry " + e.getName() + " from jar " + jarResource, ex));
}
});
}
me.ifExceptionThrow();
}
}
/**
* Parse a single entry in a jar file
*
* @param handlers the handlers to look for classes in
* @param entry the entry in the potentially MultiRelease jar resource to parse
* @param jar the jar file
* @throws Exception if unable to parse
*/
protected void parseJarEntry(Set<? extends Handler> handlers, Resource jar, MultiReleaseJarFile.VersionedJarEntry entry)
throws Exception
{
if (jar == null || entry == null)
return;
//skip directories
if (entry.isDirectory())
return;
String name = entry.getName();
//check file is a valid class file name
if (isValidClassFileName(name) && isValidClassFilePath(name))
{
String shortName = StringUtil.replace(name, '/', '.').substring(0, name.length() - 6);
addParsedClass(shortName, Resource.newResource("jar:" + jar.getURI() + "!/" + entry.getNameInJar()));
if (LOG.isDebugEnabled())
LOG.debug("Scanning class from jar {}!/{}", jar, entry);
try (InputStream is = entry.getInputStream())
{
scanClass(handlers, jar, is);
}
}
}
/**
* Use ASM on a class
*
* @param handlers the handlers to look for classes in
* @param containingResource the dir or jar that the class is contained within, can be null if not known
* @param is the input stream to parse
* @throws IOException if unable to parse
*/
protected void scanClass(Set<? extends Handler> handlers, Resource containingResource, InputStream is) throws IOException
{
ClassReader reader = new ClassReader(is);
reader.accept(new MyClassVisitor(handlers, containingResource, _asmVersion), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
}
/**
* Remove any parsed class names.
*/
public void resetParsedClasses()
{
_parsedClassNames.clear();
}
/**
* Check that the given path represents a valid class file name.
* The check is fairly cursory, checking that:
* <ul>
* <li> the name ends with .class</li>
* <li> it isn't a dot file or in a hidden directory </li>
* <li> the name of the class at least begins with a valid identifier for a class name </li>
* </ul>
*
* @param name the class file name
* @return whether the class file name is valid
*/
public boolean isValidClassFileName(String name)
{
//no name cannot be valid
if (name == null || name.length() == 0)
return false;
//skip anything that is not a class file
String lc = name.toLowerCase(Locale.ENGLISH);
if (!lc.endsWith(".class"))
{
if (LOG.isDebugEnabled())
LOG.debug("Not a class: {}", name);
return false;
}
if (lc.equals("module-info.class"))
{
if (LOG.isDebugEnabled())
LOG.debug("Skipping module-info.class");
return false;
}
//skip any classfiles that are not a valid java identifier
int c0 = 0;
int ldir = name.lastIndexOf('/', name.length() - 6);
c0 = (ldir > -1 ? ldir + 1 : c0);
if (!Character.isJavaIdentifierStart(name.charAt(c0)))
{
if (LOG.isDebugEnabled())
LOG.debug("Not a java identifier: {}", name);
return false;
}
return true;
}
/**
* Check that the given path does not contain hidden directories
*
* @param path the class file path
* @return whether the class file path is valid
*/
public boolean isValidClassFilePath(String path)
{
//no path is not valid
if (path == null || path.length() == 0)
return false;
// skip any classfiles that are in a hidden directory
if (path.startsWith(".") || path.contains("/."))
{
if (LOG.isDebugEnabled())
LOG.debug("Contains hidden dirs: {}", path);
return false;
}
return true;
}
}

View File

@ -0,0 +1,81 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ClassInheritanceHandler
*
* As asm scans for classes, remember the type hierarchy.
*/
public class ClassInheritanceHandler extends AnnotationParser.AbstractHandler
{
private static final Logger LOG = LoggerFactory.getLogger(ClassInheritanceHandler.class);
Map<String, Set<String>> _inheritanceMap;
public ClassInheritanceHandler(Map<String, Set<String>> map)
{
_inheritanceMap = map;
}
@Override
public void handle(AnnotationParser.ClassInfo classInfo)
{
try
{
//Don't scan Object
if ("java.lang.Object".equals(classInfo.getClassName()))
return;
for (int i = 0; classInfo.getInterfaces() != null && i < classInfo.getInterfaces().length; i++)
{
addToInheritanceMap(classInfo.getInterfaces()[i], classInfo.getClassName());
}
//To save memory, we don't record classes that only extend Object, as that can be assumed
if (!"java.lang.Object".equals(classInfo.getSuperName()))
{
addToInheritanceMap(classInfo.getSuperName(), classInfo.getClassName());
}
}
catch (Exception e)
{
LOG.warn("Failed to handle {}", classInfo, e);
}
}
private void addToInheritanceMap(String interfaceOrSuperClassName, String implementingOrExtendingClassName)
{
//As it is likely that the interfaceOrSuperClassName is already in the map, try getting it first
Set<String> implementingClasses = _inheritanceMap.get(interfaceOrSuperClassName);
//If it isn't in the map, then add it in, but test to make sure that someone else didn't get in
//first and add it
if (implementingClasses == null)
{
implementingClasses = ConcurrentHashMap.newKeySet();
Set<String> tmp = _inheritanceMap.putIfAbsent(interfaceOrSuperClassName, implementingClasses);
if (tmp != null)
implementingClasses = tmp;
}
implementingClasses.add(implementingOrExtendingClassName);
}
}

View File

@ -0,0 +1,104 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.Objects;
import org.eclipse.jetty.ee10.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.ee10.servlet.ServletContainerInitializerHolder;
/**
* ContainerInitializerAnnotationHandler
* <p>
* Discovers classes that contain the specified annotation, either at class or
* method level. The specified annotation is derived from an <code>&#064;HandlesTypes</code> on
* a ServletContainerInitializer class.
*/
public class ContainerInitializerAnnotationHandler extends AnnotationParser.AbstractHandler
{
final ContainerInitializer _initializer;
final ServletContainerInitializerHolder _holder;
final Class<?> _annotation;
@Deprecated
public ContainerInitializerAnnotationHandler(ContainerInitializer initializer, Class<?> annotation)
{
_holder = null;
_annotation = Objects.requireNonNull(annotation);
_initializer = initializer;
}
public ContainerInitializerAnnotationHandler(ServletContainerInitializerHolder holder, Class<?> annotation)
{
_holder = Objects.requireNonNull(holder);
_annotation = Objects.requireNonNull(annotation);
_initializer = null;
}
/**
* Handle finding a class that is annotated with the annotation we were constructed with.
*
* @see AnnotationParser.Handler#handle(AnnotationParser.ClassInfo, String)
*/
@Override
public void handle(AnnotationParser.ClassInfo info, String annotationName)
{
if (!_annotation.getName().equals(annotationName))
return;
if (_initializer != null)
_initializer.addAnnotatedTypeName(info.getClassName());
else
_holder.addStartupClasses(info.getClassName());
}
/**
* Handle finding a field that is annotated with the annotation we were constructed with.
*
* @see AnnotationParser.Handler#handle(AnnotationParser.FieldInfo, String)
*/
@Override
public void handle(AnnotationParser.FieldInfo info, String annotationName)
{
if (!_annotation.getName().equals(annotationName))
return;
if (_initializer != null)
_initializer.addAnnotatedTypeName(info.getClassInfo().getClassName());
else
_holder.addStartupClasses(info.getClassInfo().getClassName());
}
/**
* Handle finding a method that is annotated with the annotation we were constructed with.
*
* @see AnnotationParser.Handler#handle(AnnotationParser.MethodInfo, String)
*/
@Override
public void handle(AnnotationParser.MethodInfo info, String annotationName)
{
if (!_annotation.getName().equals(annotationName))
return;
if (_initializer != null)
_initializer.addAnnotatedTypeName(info.getClassInfo().getClassName());
else
_holder.addStartupClasses(info.getClassInfo().getClassName());
}
@Deprecated
public ContainerInitializer getContainerInitializer()
{
return _initializer;
}
}

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import jakarta.annotation.security.DeclareRoles;
import jakarta.servlet.Servlet;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DeclaresRolesAnnotationHandler
*/
public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(DeclareRolesAnnotationHandler.class);
public DeclareRolesAnnotationHandler(WebAppContext context)
{
super(false, context);
}
@Override
public void doHandle(Class clazz)
{
if (!Servlet.class.isAssignableFrom(clazz))
return; //only applicable on jakarta.servlet.Servlet derivatives
if (!(_context.getSecurityHandler() instanceof ConstraintAware))
{
LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing");
return;
}
DeclareRoles declareRoles = (DeclareRoles)clazz.getAnnotation(DeclareRoles.class);
if (declareRoles == null)
return;
String[] roles = declareRoles.value();
if (roles != null && roles.length > 0)
{
for (String r : roles)
{
((ConstraintSecurityHandler)_context.getSecurityHandler()).addRole(r);
_context.getMetaData().setOrigin("security-role." + r, declareRoles, clazz);
}
}
}
}

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.Servlet;
import jakarta.servlet.annotation.MultipartConfig;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.webapp.Descriptor;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
/**
* MultiPartConfigAnnotationHandler
*/
public class MultiPartConfigAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
public MultiPartConfigAnnotationHandler(WebAppContext context)
{
//TODO verify that MultipartConfig is not inheritable
super(false, context);
}
@Override
public void doHandle(Class clazz)
{
if (!Servlet.class.isAssignableFrom(clazz))
return;
MultipartConfig multi = (MultipartConfig)clazz.getAnnotation(MultipartConfig.class);
if (multi == null)
return;
MetaData metaData = _context.getMetaData();
//TODO: The MultipartConfigElement needs to be set on the ServletHolder's Registration.
//How to identify the correct Servlet? If the Servlet has no WebServlet annotation on it, does it mean that this MultipartConfig
//annotation applies to all declared instances in web.xml/programmatically?
//Assuming TRUE for now.
for (ServletHolder holder : _context.getServletHandler().getServlets(clazz))
{
Descriptor d = metaData.getOriginDescriptor(holder.getName() + ".servlet.multipart-config");
//if a descriptor has already set the value for multipart config, do not
//let the annotation override it
if (d == null)
{
metaData.setOrigin(holder.getName() + ".servlet.multipart-config", multi, clazz);
holder.getRegistration().setMultipartConfig(new MultipartConfigElement(multi));
}
}
}
}

View File

@ -0,0 +1,101 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import jakarta.annotation.PostConstruct;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallbackCollection;
import org.eclipse.jetty.ee10.plus.annotation.PostConstructCallback;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.Origin;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
public class PostConstructAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
public PostConstructAnnotationHandler(WebAppContext wac)
{
super(true, wac);
}
@Override
public void doHandle(Class clazz)
{
//Check that the PostConstruct is on a class that we're interested in
if (supportsPostConstruct(clazz))
{
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++)
{
Method m = (Method)methods[i];
if (m.isAnnotationPresent(PostConstruct.class))
{
if (m.getParameterCount() != 0)
throw new IllegalStateException(m + " has parameters");
if (m.getReturnType() != Void.TYPE)
throw new IllegalStateException(m + " is not void");
if (m.getExceptionTypes().length != 0)
throw new IllegalStateException(m + " throws checked exceptions");
if (Modifier.isStatic(m.getModifiers()))
throw new IllegalStateException(m + " is static");
//ServletSpec 3.0 p80 If web.xml declares even one post-construct then all post-constructs
//in fragments must be ignored. Otherwise, they are additive.
MetaData metaData = _context.getMetaData();
Origin origin = metaData.getOrigin("post-construct");
if (origin != null &&
(origin == Origin.WebXml ||
origin == Origin.WebDefaults ||
origin == Origin.WebOverride))
return;
PostConstructCallback callback = new PostConstructCallback(clazz, m.getName());
LifeCycleCallbackCollection lifecycles = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
if (lifecycles == null)
{
lifecycles = new LifeCycleCallbackCollection();
_context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, lifecycles);
}
lifecycles.add(callback);
}
}
}
}
/**
* Check if the given class is permitted to have PostConstruct annotation.
*
* @param c the class
* @return true if the spec permits the class to have PostConstruct, false otherwise
*/
public boolean supportsPostConstruct(Class c)
{
if (jakarta.servlet.Servlet.class.isAssignableFrom(c) ||
jakarta.servlet.Filter.class.isAssignableFrom(c) ||
jakarta.servlet.ServletContextListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
jakarta.servlet.AsyncListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
return true;
return false;
}
}

View File

@ -0,0 +1,103 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import jakarta.annotation.PreDestroy;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallbackCollection;
import org.eclipse.jetty.ee10.plus.annotation.PreDestroyCallback;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.Origin;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
public class PreDestroyAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
public PreDestroyAnnotationHandler(WebAppContext wac)
{
super(true, wac);
}
@Override
public void doHandle(Class clazz)
{
//Check that the PreDestroy is on a class that we're interested in
if (supportsPreDestroy(clazz))
{
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++)
{
Method m = (Method)methods[i];
if (m.isAnnotationPresent(PreDestroy.class))
{
if (m.getParameterCount() != 0)
throw new IllegalStateException(m + " has parameters");
if (m.getReturnType() != Void.TYPE)
throw new IllegalStateException(m + " is not void");
if (m.getExceptionTypes().length != 0)
throw new IllegalStateException(m + " throws checked exceptions");
if (Modifier.isStatic(m.getModifiers()))
throw new IllegalStateException(m + " is static");
//ServletSpec 3.0 p80 If web.xml declares even one predestroy then all predestroys
//in fragments must be ignored. Otherwise, they are additive.
MetaData metaData = _context.getMetaData();
Origin origin = metaData.getOrigin("pre-destroy");
if (origin != null &&
(origin == Origin.WebXml ||
origin == Origin.WebDefaults ||
origin == Origin.WebOverride))
return;
PreDestroyCallback callback = new PreDestroyCallback(clazz, m.getName());
LifeCycleCallbackCollection lifecycles = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
if (lifecycles == null)
{
lifecycles = new LifeCycleCallbackCollection();
_context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, lifecycles);
}
lifecycles.add(callback);
}
}
}
}
/**
* Check if the spec permits the given class to use the PreDestroy annotation.
*
* @param c the class
* @return true if permitted, false otherwise
*/
public boolean supportsPreDestroy(Class c)
{
if (jakarta.servlet.Servlet.class.isAssignableFrom(c) ||
jakarta.servlet.Filter.class.isAssignableFrom(c) ||
jakarta.servlet.ServletContextListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
jakarta.servlet.AsyncListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
return true;
return false;
}
}

View File

@ -0,0 +1,394 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import jakarta.annotation.Resource;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.plus.annotation.Injection;
import org.eclipse.jetty.ee10.plus.annotation.InjectionCollection;
import org.eclipse.jetty.ee10.plus.jndi.NamingEntryUtil;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(ResourceAnnotationHandler.class);
protected static final List<Class<?>> ENV_ENTRY_TYPES =
Arrays.asList(new Class[]
{
String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class,
Float.class
});
public ResourceAnnotationHandler(WebAppContext wac)
{
super(true, wac);
}
/**
* Class level Resource annotations declare a name in the
* environment that will be looked up at runtime. They do
* not specify an injection.
*/
@Override
public void doHandle(Class<?> clazz)
{
if (supportsResourceInjection(clazz))
{
handleClass(clazz);
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++)
{
handleMethod(clazz, methods[i]);
}
Field[] fields = clazz.getDeclaredFields();
//For each field, get all of it's annotations
for (int i = 0; i < fields.length; i++)
{
handleField(clazz, fields[i]);
}
}
}
public void handleClass(Class<?> clazz)
{
Resource resource = (Resource)clazz.getAnnotation(Resource.class);
if (resource != null)
{
String name = resource.name();
String mappedName = resource.mappedName();
if (name == null || name.trim().equals(""))
throw new IllegalStateException("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)");
try
{
if (!NamingEntryUtil.bindToENC(_context, name, mappedName))
if (!NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName))
throw new IllegalStateException("No resource at " + (mappedName == null ? name : mappedName));
}
catch (NamingException e)
{
LOG.warn("Unable to bind name {} to {} from class {}", name, mappedName, clazz, e);
}
}
}
public void handleField(Class<?> clazz, Field field)
{
Resource resource = (Resource)field.getAnnotation(Resource.class);
if (resource != null)
{
//JavaEE Spec 5.2.3: Field cannot be static
if (Modifier.isStatic(field.getModifiers()))
{
LOG.warn("Skipping Resource annotation on {}.{}: cannot be static", clazz.getName(), field.getName());
return;
}
//JavaEE Spec 5.2.3: Field cannot be final
if (Modifier.isFinal(field.getModifiers()))
{
LOG.warn("Skipping Resource annotation on {}.{}: cannot be final", clazz.getName(), field.getName());
return;
}
//work out default name
String name = clazz.getName() + "/" + field.getName();
//allow @Resource name= to override the field name
name = (resource.name() != null && !resource.name().trim().equals("") ? resource.name() : name);
String mappedName = (resource.mappedName() != null && !resource.mappedName().trim().equals("") ? resource.mappedName() : null);
//get the type of the Field
Class<?> type = field.getType();
//Servlet Spec 3.0 p. 76
//If a descriptor has specified at least 1 injection target for this
//resource, then it overrides this annotation
MetaData metaData = _context.getMetaData();
if (metaData.getOriginDescriptor("resource-ref." + name + ".injection") != null)
{
//at least 1 injection was specified for this resource by a descriptor, so
//it overrides this annotation
return;
}
//No injections for this resource in any descriptors, so we can add it
//Does the injection already exist?
InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
if (injections == null)
{
injections = new InjectionCollection();
_context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
}
Injection injection = injections.getInjection(name, clazz, field);
if (injection == null)
{
//No injection has been specified, add it
try
{
boolean bound = NamingEntryUtil.bindToENC(_context, name, mappedName);
if (!bound)
bound = NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName);
if (!bound)
bound = NamingEntryUtil.bindToENC(null, name, mappedName);
if (!bound)
{
//see if there is an env-entry value been bound
try
{
InitialContext ic = new InitialContext();
String nameInEnvironment = (mappedName != null ? mappedName : name);
ic.lookup("java:comp/env/" + nameInEnvironment);
bound = true;
}
catch (NameNotFoundException e)
{
bound = false;
}
}
//Check there is a JNDI entry for this annotation
if (bound)
{
LOG.debug("Bound {} as {}", (mappedName == null ? name : mappedName), name);
// Make the Injection for it if the binding succeeded
injection = new Injection(clazz, field, type, name, mappedName);
injections.add(injection);
//TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination
metaData.setOrigin("resource-ref." + name + ".injection", resource, clazz);
}
else if (!isEnvEntryType(type))
{
//if this is an env-entry type resource and there is no value bound for it, it isn't
//an error, it just means that perhaps the code will use a default value instead
// JavaEE Spec. sec 5.4.1.3
throw new IllegalStateException("No resource at " + (mappedName == null ? name : mappedName));
}
}
catch (NamingException e)
{
//if this is an env-entry type resource and there is no value bound for it, it isn't
//an error, it just means that perhaps the code will use a default value instead
// JavaEE Spec. sec 5.4.1.3
if (!isEnvEntryType(type))
throw new IllegalStateException(e);
}
}
}
}
/**
* Process a Resource annotation on a Method.
* <p>
* This will generate a JNDI entry, and an Injection to be
* processed when an instance of the class is created.
*
* @param clazz the class to process
* @param method the method to process
*/
public void handleMethod(Class<?> clazz, Method method)
{
Resource resource = (Resource)method.getAnnotation(Resource.class);
if (resource != null)
{
/*
* Commons Annotations Spec 2.3
* " The Resource annotation is used to declare a reference to a resource.
* It can be specified on a class, methods or on fields. When the
* annotation is applied on a field or method, the container will
* inject an instance of the requested resource into the application
* when the application is initialized... Even though this annotation
* is not marked Inherited, if used all superclasses MUST be examined
* to discover all uses of this annotation. All such annotation instances
* specify resources that are needed by the application. Note that this
* annotation may appear on private fields and methods of the superclasses.
* Injection of the declared resources needs to happen in these cases as
* well, even if a method with such an annotation is overridden by a subclass."
*
* Which IMHO, put more succinctly means "If you find a @Resource on any method
* or field, inject it!".
*/
//JavaEE Spec 5.2.3: Method cannot be static
if (Modifier.isStatic(method.getModifiers()))
{
LOG.warn("Skipping Resource annotation on {}.{}: cannot be static", clazz.getName(), method.getName());
return;
}
// Check it is a valid javabean: must be void return type, the name must start with "set" and it must have
// only 1 parameter
if (!method.getName().startsWith("set"))
{
LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, does not start with 'set'", clazz.getName(), method.getName());
return;
}
if (method.getParameterCount() != 1)
{
LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, not single argument to method", clazz.getName(), method.getName());
return;
}
if (Void.TYPE != method.getReturnType())
{
LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, not void", clazz.getName(), method.getName());
return;
}
//default name is the javabean property name
String name = method.getName().substring(3);
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
name = clazz.getName() + "/" + name;
name = (resource.name() != null && !resource.name().trim().equals("") ? resource.name() : name);
String mappedName = (resource.mappedName() != null && !resource.mappedName().trim().equals("") ? resource.mappedName() : null);
Class<?> paramType = method.getParameterTypes()[0];
Class<?> resourceType = resource.type();
//Servlet Spec 3.0 p. 76
//If a descriptor has specified at least 1 injection target for this
//resource, then it overrides this annotation
MetaData metaData = _context.getMetaData();
if (metaData.getOriginDescriptor("resource-ref." + name + ".injection") != null)
{
//at least 1 injection was specified for this resource by a descriptor, so
//it overrides this annotation
return;
}
//check if an injection has already been setup for this target by web.xml
InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
if (injections == null)
{
injections = new InjectionCollection();
_context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
}
Injection injection = injections.getInjection(name, clazz, method, paramType);
if (injection == null)
{
try
{
//try binding name to environment
//try the webapp's environment first
boolean bound = NamingEntryUtil.bindToENC(_context, name, mappedName);
//try the server's environment
if (!bound)
bound = NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName);
//try the jvm's environment
if (!bound)
bound = NamingEntryUtil.bindToENC(null, name, mappedName);
//TODO if it is an env-entry from web.xml it can be injected, in which case there will be no
//NamingEntry, just a value bound in java:comp/env
if (!bound)
{
try
{
InitialContext ic = new InitialContext();
String nameInEnvironment = (mappedName != null ? mappedName : name);
ic.lookup("java:comp/env/" + nameInEnvironment);
bound = true;
}
catch (NameNotFoundException e)
{
bound = false;
}
}
if (bound)
{
LOG.debug("Bound {} as {}", (mappedName == null ? name : mappedName), name);
// Make the Injection for it
injection = new Injection(clazz, method, paramType, resourceType, name, mappedName);
injections.add(injection);
//TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination
metaData.setOrigin("resource-ref." + name + ".injection", resource, clazz);
}
else if (!isEnvEntryType(paramType))
{
//if this is an env-entry type resource and there is no value bound for it, it isn't
//an error, it just means that perhaps the code will use a default value instead
// JavaEE Spec. sec 5.4.1.3
throw new IllegalStateException("No resource at " + (mappedName == null ? name : mappedName));
}
}
catch (NamingException e)
{
//if this is an env-entry type resource and there is no value bound for it, it isn't
//an error, it just means that perhaps the code will use a default value instead
// JavaEE Spec. sec 5.4.1.3
if (!isEnvEntryType(paramType))
throw new IllegalStateException(e);
}
}
}
}
/**
* Check if the given Class is one that the specification allows to have a Resource annotation.
*
* @param c the class
* @return true if Resource annotation permitted, false otherwise
*/
public boolean supportsResourceInjection(Class<?> c)
{
if (jakarta.servlet.Servlet.class.isAssignableFrom(c) ||
jakarta.servlet.Filter.class.isAssignableFrom(c) ||
jakarta.servlet.ServletContextListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
jakarta.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) ||
jakarta.servlet.AsyncListener.class.isAssignableFrom(c) ||
jakarta.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c))
return true;
return false;
}
/**
* Check if the class is one of the basic java types permitted as
* env-entries.
*
* @param clazz the class to check
* @return true if class is permitted by the spec to be an env-entry value
*/
public boolean isEnvEntryType(Class<?> clazz)
{
return ENV_ENTRY_TYPES.contains(clazz);
}
}

View File

@ -0,0 +1,72 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import javax.naming.NamingException;
import jakarta.annotation.Resource;
import jakarta.annotation.Resources;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.plus.jndi.NamingEntryUtil;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ResourcesAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(ResourcesAnnotationHandler.class);
public ResourcesAnnotationHandler(WebAppContext wac)
{
super(true, wac);
}
@Override
public void doHandle(Class<?> clazz)
{
Resources resources = (Resources)clazz.getAnnotation(Resources.class);
if (resources != null)
{
Resource[] resArray = resources.value();
if (resArray == null || resArray.length == 0)
{
LOG.warn("Skipping empty or incorrect Resources annotation on {}", clazz.getName());
return;
}
for (int j = 0; j < resArray.length; j++)
{
String name = resArray[j].name();
String mappedName = resArray[j].mappedName();
if (name == null || name.trim().equals(""))
throw new IllegalStateException("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)");
try
{
//TODO don't ignore the shareable, auth etc etc
if (!NamingEntryUtil.bindToENC(_context, name, mappedName))
if (!NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName))
LOG.warn("Skipping Resources(Resource) annotation on {} for name {}: no resource bound at {}",
clazz.getName(), name, (mappedName == null ? name : mappedName));
}
catch (NamingException e)
{
LOG.warn("Unable to bind {} to {}", name, mappedName, e);
}
}
}
}
}

View File

@ -0,0 +1,75 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import jakarta.servlet.Servlet;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.webapp.Descriptor;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RunAsAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(RunAsAnnotationHandler.class);
public RunAsAnnotationHandler(WebAppContext wac)
{
//Introspect only the given class for a RunAs annotation, as it is a class level annotation,
//and according to Common Annotation Spec p2-6 a class-level annotation is not inheritable.
super(false, wac);
}
@Override
public void doHandle(Class clazz)
{
if (!Servlet.class.isAssignableFrom(clazz))
return;
jakarta.annotation.security.RunAs runAs = (jakarta.annotation.security.RunAs)clazz.getAnnotation(jakarta.annotation.security.RunAs.class);
if (runAs != null)
{
String role = runAs.value();
if (role != null)
{
for (ServletHolder holder : _context.getServletHandler().getServlets(clazz))
{
MetaData metaData = _context.getMetaData();
Descriptor d = metaData.getOriginDescriptor(holder.getName() + ".servlet.run-as");
//if a descriptor has already set the value for run-as, do not
//let the annotation override it
if (d == null)
{
metaData.setOrigin(holder.getName() + ".servlet.run-as", runAs, clazz);
holder.setRunAsRole(role);
}
}
}
else
LOG.warn("Bad value for @RunAs annotation on class {}", clazz.getName());
}
}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation)
{
LOG.warn("@RunAs annotation not applicable for fields: {}.{}", className, fieldName);
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation)
{
LOG.warn("@RunAs annotation ignored on method: {}.{} {}", className, methodName, signature);
}
}

View File

@ -0,0 +1,65 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.List;
import org.eclipse.jetty.ee10.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ServletContainerInitializersStarter
*
* Call the onStartup() method on all ServletContainerInitializers, after having
* found all applicable classes (if any) to pass in as args.
* @deprecated
*/
@Deprecated
public class ServletContainerInitializersStarter extends AbstractLifeCycle implements ServletContextHandler.ServletContainerInitializerCaller
{
private static final Logger LOG = LoggerFactory.getLogger(ServletContainerInitializersStarter.class);
WebAppContext _context;
public ServletContainerInitializersStarter(WebAppContext context)
{
_context = context;
}
@Override
public void doStart()
{
List<ContainerInitializer> initializers = (List<ContainerInitializer>)_context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
if (initializers == null)
return;
for (ContainerInitializer i : initializers)
{
try
{
if (LOG.isDebugEnabled())
LOG.debug("Calling ServletContainerInitializer {}", i.getTarget().getClass().getName());
i.callStartup(_context);
}
catch (Exception e)
{
LOG.warn("Failed to call startup on {}", i, e);
throw new RuntimeException(e);
}
}
}
}

View File

@ -0,0 +1,184 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.ArrayList;
import java.util.List;
import jakarta.servlet.ServletSecurityElement;
import jakarta.servlet.annotation.ServletSecurity;
import jakarta.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.util.security.Constraint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ServletSecurityAnnotationHandler
*
* Inspect a class to see if it has an <code>&#064;ServletSecurity</code> annotation on it,
* setting up the <code>&lt;security-constraint&gt;s</code>.
*
* A servlet can be defined in:
* <ul>
* <li>web.xml</li>
* <li>web-fragment.xml</li>
* <li>@WebServlet annotation discovered</li>
* <li>ServletContext.createServlet</li>
* </ul>
*
* The ServletSecurity annotation for a servlet should only be processed
* iff metadata-complete == false.
*/
public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(ServletSecurityAnnotationHandler.class);
public ServletSecurityAnnotationHandler(WebAppContext wac)
{
super(false, wac);
}
@Override
public void doHandle(Class clazz)
{
if (!(_context.getSecurityHandler() instanceof ConstraintAware))
{
LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing");
return;
}
ServletSecurity servletSecurity = (ServletSecurity)clazz.getAnnotation(ServletSecurity.class);
if (servletSecurity == null)
return;
//If there are already constraints defined (ie from web.xml) that match any
//of the url patterns defined for this servlet, then skip the security annotation.
List<ServletMapping> servletMappings = getServletMappings(clazz.getCanonicalName());
List<ConstraintMapping> constraintMappings = ((ConstraintAware)_context.getSecurityHandler()).getConstraintMappings();
if (constraintsExist(servletMappings, constraintMappings))
{
LOG.warn("Constraints already defined for {}, skipping ServletSecurity annotation", clazz.getName());
return;
}
//Make a fresh list
constraintMappings = new ArrayList<ConstraintMapping>();
ServletSecurityElement securityElement = new ServletSecurityElement(servletSecurity);
for (ServletMapping sm : servletMappings)
{
for (String url : sm.getPathSpecs())
{
_context.getMetaData().setOrigin("constraint.url." + url, servletSecurity, clazz);
constraintMappings.addAll(ConstraintSecurityHandler.createConstraintsWithMappingsForPath(clazz.getName(), url, securityElement));
}
}
//set up the security constraints produced by the annotation
ConstraintAware securityHandler = (ConstraintAware)_context.getSecurityHandler();
for (ConstraintMapping m : constraintMappings)
{
securityHandler.addConstraintMapping(m);
}
//Servlet Spec 3.1 requires paths with uncovered http methods to be reported
securityHandler.checkPathsWithUncoveredHttpMethods();
}
/**
* Make a jetty Constraint object, which represents the <code>&lt;auth-constraint&gt;</code> and
* <code>&lt;user-data-constraint&gt;</code> elements, based on the security annotation.
*
* @param servlet the servlet
* @param rolesAllowed the roles allowed
* @param permitOrDeny the role / permission semantic
* @param transport the transport guarantee
* @return the constraint
*/
protected Constraint makeConstraint(Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
{
return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport);
}
/**
* Get the ServletMappings for the servlet's class.
*
* @param className the class name
* @return the servlet mappings for the class
*/
protected List<ServletMapping> getServletMappings(String className)
{
List<ServletMapping> results = new ArrayList<ServletMapping>();
ServletMapping[] mappings = _context.getServletHandler().getServletMappings();
for (ServletMapping mapping : mappings)
{
//Check the name of the servlet that this mapping applies to, and then find the ServletHolder for it to find it's class
ServletHolder holder = _context.getServletHandler().getServlet(mapping.getServletName());
if (holder.getClassName() != null && holder.getClassName().equals(className))
results.add(mapping);
}
return results;
}
/**
* Check if there are already <code>&lt;security-constraint&gt;</code> elements defined that match the url-patterns for
* the servlet.
*
* @param servletMappings the servlet mappings
* @param constraintMappings the constraint mappings
* @return true if constraint exists
*/
protected boolean constraintsExist(List<ServletMapping> servletMappings, List<ConstraintMapping> constraintMappings)
{
boolean exists = false;
//Check to see if the path spec on each constraint mapping matches a pathSpec in the servlet mappings.
//If it does, then we should ignore the security annotations.
for (ServletMapping mapping : servletMappings)
{
//Get its url mappings
String[] pathSpecs = mapping.getPathSpecs();
if (pathSpecs == null)
continue;
//Check through the constraints to see if there are any whose pathSpecs (url mappings)
//match the servlet. If so, then we already have constraints defined for this servlet,
//and we will not be processing the annotation (ie web.xml or programmatic override).
for (int i = 0; constraintMappings != null && i < constraintMappings.size() && !exists; i++)
{
for (int j = 0; j < pathSpecs.length; j++)
{
//TODO decide if we need to check the origin
if (pathSpecs[j].equals(constraintMappings.get(i).getPathSpec()))
{
exists = true;
break;
}
}
}
}
return exists;
}
}

View File

@ -0,0 +1,208 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.ArrayList;
import java.util.EnumSet;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebInitParam;
import org.eclipse.jetty.ee10.servlet.FilterHolder;
import org.eclipse.jetty.ee10.servlet.FilterMapping;
import org.eclipse.jetty.ee10.servlet.Source;
import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.Origin;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* WebFilterAnnotation
*/
public class WebFilterAnnotation extends DiscoveredAnnotation
{
private static final Logger LOG = LoggerFactory.getLogger(WebFilterAnnotation.class);
public WebFilterAnnotation(WebAppContext context, String className)
{
super(context, className);
}
public WebFilterAnnotation(WebAppContext context, String className, Resource resource)
{
super(context, className, resource);
}
@Override
public void apply()
{
// TODO verify against rules for annotation v descriptor
Class clazz = getTargetClass();
if (clazz == null)
{
LOG.warn("{} cannot be loaded", _className);
return;
}
//Servlet Spec 8.1.2
if (!Filter.class.isAssignableFrom(clazz))
{
LOG.warn("{} is not assignable from jakarta.servlet.Filter", clazz.getName());
return;
}
MetaData metaData = _context.getMetaData();
WebFilter filterAnnotation = (WebFilter)clazz.getAnnotation(WebFilter.class);
if (filterAnnotation.value().length > 0 && filterAnnotation.urlPatterns().length > 0)
{
LOG.warn("{} defines both @WebFilter.value and @WebFilter.urlPatterns", clazz.getName());
return;
}
String name = (filterAnnotation.filterName().equals("") ? clazz.getName() : filterAnnotation.filterName());
String[] urlPatterns = filterAnnotation.value();
if (urlPatterns.length == 0)
urlPatterns = filterAnnotation.urlPatterns();
FilterHolder holder = _context.getServletHandler().getFilter(name);
if (holder == null)
{
//Filter with this name does not already exist, so add it
holder = _context.getServletHandler().newFilterHolder(new Source(Source.Origin.ANNOTATION, clazz.getName()));
holder.setName(name);
holder.setHeldClass(clazz);
metaData.setOrigin(name + ".filter.filter-class", filterAnnotation, clazz);
holder.setDisplayName(filterAnnotation.displayName());
metaData.setOrigin(name + ".filter.display-name", filterAnnotation, clazz);
for (WebInitParam ip : filterAnnotation.initParams())
{
holder.setInitParameter(ip.name(), ip.value());
metaData.setOrigin(name + ".filter.init-param." + ip.name(), ip, clazz);
}
FilterMapping mapping = new FilterMapping();
mapping.setFilterName(holder.getName());
metaData.setOrigin(name + ".filter.mapping." + Long.toHexString(mapping.hashCode()), filterAnnotation, clazz);
if (urlPatterns.length > 0)
{
ArrayList<String> paths = new ArrayList<String>();
for (String s : urlPatterns)
{
paths.add(ServletPathSpec.normalize(s));
}
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
}
if (filterAnnotation.servletNames().length > 0)
{
ArrayList<String> names = new ArrayList<String>();
for (String s : filterAnnotation.servletNames())
{
names.add(s);
}
mapping.setServletNames(names.toArray(new String[names.size()]));
}
EnumSet<DispatcherType> dispatcherSet = EnumSet.noneOf(DispatcherType.class);
for (DispatcherType d : filterAnnotation.dispatcherTypes())
{
dispatcherSet.add(d);
}
mapping.setDispatcherTypes(dispatcherSet);
metaData.setOrigin(name + ".filter.mappings", filterAnnotation, clazz);
holder.setAsyncSupported(filterAnnotation.asyncSupported());
metaData.setOrigin(name + ".filter.async-supported", filterAnnotation, clazz);
_context.getServletHandler().addFilter(holder);
_context.getServletHandler().addFilterMapping(mapping);
}
else
{
//A Filter definition for the same name already exists from web.xml
//ServletSpec 3.0 p81 if the Filter is already defined and has mappings,
//they override the annotation. If it already has DispatcherType set, that
//also overrides the annotation. Init-params are additive, but web.xml overrides
//init-params of the same name.
for (WebInitParam ip : filterAnnotation.initParams())
{
//if (holder.getInitParameter(ip.name()) == null)
if (metaData.getOrigin(name + ".filter.init-param." + ip.name()) == Origin.NotSet)
{
holder.setInitParameter(ip.name(), ip.value());
metaData.setOrigin(name + ".filter.init-param." + ip.name(), ip, clazz);
}
}
FilterMapping[] mappings = _context.getServletHandler().getFilterMappings();
boolean mappingExists = false;
if (mappings != null)
{
for (FilterMapping m : mappings)
{
if (m.getFilterName().equals(name))
{
mappingExists = true;
break;
}
}
}
//if a descriptor didn't specify at least one mapping, use the mappings from the annotation and the DispatcherTypes
//from the annotation
if (!mappingExists)
{
FilterMapping mapping = new FilterMapping();
mapping.setFilterName(holder.getName());
metaData.setOrigin(holder.getName() + ".filter.mapping." + Long.toHexString(mapping.hashCode()), filterAnnotation, clazz);
if (urlPatterns.length > 0)
{
ArrayList<String> paths = new ArrayList<String>();
for (String s : urlPatterns)
{
paths.add(ServletPathSpec.normalize(s));
}
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
}
if (filterAnnotation.servletNames().length > 0)
{
ArrayList<String> names = new ArrayList<String>();
for (String s : filterAnnotation.servletNames())
{
names.add(s);
}
mapping.setServletNames(names.toArray(new String[names.size()]));
}
EnumSet<DispatcherType> dispatcherSet = EnumSet.noneOf(DispatcherType.class);
for (DispatcherType d : filterAnnotation.dispatcherTypes())
{
dispatcherSet.add(d);
}
mapping.setDispatcherTypes(dispatcherSet);
_context.getServletHandler().addFilterMapping(mapping);
metaData.setOrigin(name + ".filter.mappings", filterAnnotation, clazz);
}
}
}
}

View File

@ -0,0 +1,57 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* WebFilterAnnotationHandler
*/
public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(WebFilterAnnotationHandler.class);
public WebFilterAnnotationHandler(WebAppContext context)
{
super(context);
}
@Override
public void handle(AnnotationParser.ClassInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebFilter".equals(annotationName))
return;
WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, info.getClassName(), info.getContainingResource());
addAnnotation(wfAnnotation);
}
@Override
public void handle(AnnotationParser.FieldInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebFilter".equals(annotationName))
return;
LOG.warn("@WebFilter not applicable for fields: {}.{}", info.getClassInfo().getClassName(), info.getFieldName());
}
@Override
public void handle(AnnotationParser.MethodInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebFilter".equals(annotationName))
return;
LOG.warn("@WebFilter not applicable for methods: {}.{} {}", info.getClassInfo().getClassName(), info.getMethodName(), info.getSignature());
}
}

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.EventListener;
import jakarta.servlet.ServletContextAttributeListener;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletRequestAttributeListener;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionIdListener;
import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.ee10.servlet.ListenerHolder;
import org.eclipse.jetty.ee10.servlet.Source;
import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.Origin;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* WebListenerAnnotation
*/
public class WebListenerAnnotation extends DiscoveredAnnotation
{
private static final Logger LOG = LoggerFactory.getLogger(WebListenerAnnotation.class);
public WebListenerAnnotation(WebAppContext context, String className)
{
super(context, className);
}
public WebListenerAnnotation(WebAppContext context, String className, Resource resource)
{
super(context, className, resource);
}
@Override
public void apply()
{
Class<? extends java.util.EventListener> clazz = (Class<? extends EventListener>)getTargetClass();
if (clazz == null)
{
LOG.warn("{} cannot be loaded", _className);
return;
}
try
{
if (ServletContextListener.class.isAssignableFrom(clazz) ||
ServletContextAttributeListener.class.isAssignableFrom(clazz) ||
ServletRequestListener.class.isAssignableFrom(clazz) ||
ServletRequestAttributeListener.class.isAssignableFrom(clazz) ||
HttpSessionListener.class.isAssignableFrom(clazz) ||
HttpSessionAttributeListener.class.isAssignableFrom(clazz) ||
HttpSessionIdListener.class.isAssignableFrom(clazz))
{
MetaData metaData = _context.getMetaData();
if (metaData.getOrigin(clazz.getName() + ".listener") == Origin.NotSet)
{
ListenerHolder h = _context.getServletHandler().newListenerHolder(new Source(Source.Origin.ANNOTATION, clazz.getName()));
h.setHeldClass(clazz);
_context.getServletHandler().addListener(h);
metaData.setOrigin(clazz.getName() + ".listener", clazz.getAnnotation(WebListener.class), clazz);
}
}
else
LOG.warn("{} does not implement one of the servlet listener interfaces", clazz.getName());
}
catch (Exception e)
{
LOG.warn("Unable to add listener {}", clazz, e);
}
}
}

View File

@ -0,0 +1,54 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(WebListenerAnnotationHandler.class);
public WebListenerAnnotationHandler(WebAppContext context)
{
super(context);
}
@Override
public void handle(AnnotationParser.ClassInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebListener".equals(annotationName))
return;
WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, info.getClassName(), info.getContainingResource());
addAnnotation(wlAnnotation);
}
@Override
public void handle(AnnotationParser.FieldInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebListener".equals(annotationName))
return;
LOG.warn("@WebListener is not applicable to fields: {}.{}", info.getClassInfo().getClassName(), info.getFieldName());
}
@Override
public void handle(AnnotationParser.MethodInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebListener".equals(annotationName))
return;
LOG.warn("@WebListener is not applicable to methods: {}.{} {}", info.getClassInfo().getClassName(), info.getMethodName(), info.getSignature());
}
}

View File

@ -0,0 +1,269 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jakarta.servlet.Servlet;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.Source;
import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.Origin;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* WebServletAnnotation
*/
public class WebServletAnnotation extends DiscoveredAnnotation
{
private static final Logger LOG = LoggerFactory.getLogger(WebServletAnnotation.class);
public WebServletAnnotation(WebAppContext context, String className)
{
super(context, className);
}
public WebServletAnnotation(WebAppContext context, String className, Resource resource)
{
super(context, className, resource);
}
@Override
public void apply()
{
//TODO check this algorithm with new rules for applying descriptors and annotations in order
Class<? extends Servlet> clazz = (Class<? extends Servlet>)getTargetClass();
if (clazz == null)
{
LOG.warn("{} cannot be loaded", _className);
return;
}
//Servlet Spec 8.1.1
if (!HttpServlet.class.isAssignableFrom(clazz))
{
LOG.warn("{} is not assignable from jakarta.servlet.http.HttpServlet", clazz.getName());
return;
}
WebServlet annotation = (WebServlet)clazz.getAnnotation(WebServlet.class);
if (annotation.urlPatterns().length > 0 && annotation.value().length > 0)
{
LOG.warn("{} defines both @WebServlet.value and @WebServlet.urlPatterns", clazz.getName());
return;
}
String[] urlPatterns = annotation.value();
if (urlPatterns.length == 0)
urlPatterns = annotation.urlPatterns();
if (urlPatterns.length == 0)
{
LOG.warn("{} defines neither @WebServlet.value nor @WebServlet.urlPatterns", clazz.getName());
return;
}
//canonicalize the patterns
ArrayList<String> urlPatternList = new ArrayList<String>();
for (String p : urlPatterns)
{
urlPatternList.add(ServletPathSpec.normalize(p));
}
String servletName = (annotation.name().equals("") ? clazz.getName() : annotation.name());
MetaData metaData = _context.getMetaData();
ServletMapping mapping = null; //the new mapping
//Find out if a <servlet> already exists with this name
ServletHolder[] holders = _context.getServletHandler().getServlets();
ServletHolder holder = null;
if (holders != null)
{
for (ServletHolder h : holders)
{
if (h.getName() != null && servletName.equals(h.getName()))
{
holder = h;
break;
}
}
}
//handle creation/completion of a servlet
if (holder == null)
{
//No servlet of this name has already been defined, either by a descriptor
//or another annotation (which would be impossible).
Source source = new Source(Source.Origin.ANNOTATION, clazz.getName());
holder = _context.getServletHandler().newServletHolder(source);
holder.setHeldClass(clazz);
metaData.setOrigin(servletName + ".servlet.servlet-class", annotation, clazz);
holder.setName(servletName);
holder.setDisplayName(annotation.displayName());
metaData.setOrigin(servletName + ".servlet.display-name", annotation, clazz);
holder.setInitOrder(annotation.loadOnStartup());
metaData.setOrigin(servletName + ".servlet.load-on-startup", annotation, clazz);
holder.setAsyncSupported(annotation.asyncSupported());
metaData.setOrigin(servletName + ".servlet.async-supported", annotation, clazz);
for (WebInitParam ip : annotation.initParams())
{
holder.setInitParameter(ip.name(), ip.value());
metaData.setOrigin(servletName + ".servlet.init-param." + ip.name(), ip, clazz);
}
_context.getServletHandler().addServlet(holder);
mapping = new ServletMapping(source);
mapping.setServletName(holder.getName());
mapping.setPathSpecs(LazyList.toStringArray(urlPatternList));
_context.getMetaData().setOrigin(servletName + ".servlet.mapping." + Long.toHexString(mapping.hashCode()), annotation, clazz);
}
else
{
//set the class according to the servlet that is annotated, if it wasn't already
//NOTE: this may be considered as "completing" an incomplete servlet registration, and it is
//not clear from servlet 3.0 spec whether this is intended, or if only a ServletContext.addServlet() call
//can complete it, see http://java.net/jira/browse/SERVLET_SPEC-42
if (holder.getClassName() == null)
holder.setClassName(clazz.getName());
if (holder.getHeldClass() == null)
holder.setHeldClass(clazz);
//check if the existing servlet has each init-param from the annotation
//if not, add it
for (WebInitParam ip : annotation.initParams())
{
if (metaData.getOrigin(servletName + ".servlet.init-param." + ip.name()) == Origin.NotSet)
{
holder.setInitParameter(ip.name(), ip.value());
metaData.setOrigin(servletName + ".servlet.init-param." + ip.name(), ip, clazz);
}
}
//check the url-patterns
//ServletSpec 3.0 p81 If a servlet already has url mappings from a
//webxml or fragment descriptor the annotation is ignored.
//However, we want to be able to replace mappings that were given in webdefault.xml
List<ServletMapping> existingMappings = getServletMappingsForServlet(servletName);
//if any mappings for this servlet already set by a descriptor that is not webdefault.xml forget
//about processing these url mappings
if (existingMappings.isEmpty() || !containsNonDefaultMappings(existingMappings))
{
mapping = new ServletMapping(new Source(Source.Origin.ANNOTATION, clazz.getName()));
mapping.setServletName(servletName);
mapping.setPathSpecs(LazyList.toStringArray(urlPatternList));
_context.getMetaData().setOrigin(servletName + ".servlet.mapping." + Long.toHexString(mapping.hashCode()), annotation, clazz);
}
}
//We also want to be able to replace mappings that were defined in webdefault.xml
//that were for a different servlet eg a mapping in webdefault.xml for / to the jetty
//default servlet should be able to be replaced by an annotation for / to a different
//servlet
if (mapping != null)
{
//url mapping was permitted by annotation processing rules
//take a copy of the existing servlet mappings that we can iterate over and remove from. This is
//because the ServletHandler interface does not support removal of individual mappings.
List<ServletMapping> allMappings = ArrayUtil.asMutableList(_context.getServletHandler().getServletMappings());
//for each of the urls in the annotation, check if a mapping to same/different servlet exists
// if mapping exists and is from a default descriptor, it can be replaced. NOTE: we do not
// guard against duplicate path mapping here: that is the job of the ServletHandler
for (String p : urlPatternList)
{
ServletMapping existingMapping = _context.getServletHandler().getServletMapping(p);
if (existingMapping != null && existingMapping.isFromDefaultDescriptor())
{
String[] updatedPaths = ArrayUtil.removeFromArray(existingMapping.getPathSpecs(), p);
//if we removed the last path from a servletmapping, delete the servletmapping
if (updatedPaths == null || updatedPaths.length == 0)
{
boolean success = allMappings.remove(existingMapping);
if (LOG.isDebugEnabled())
LOG.debug("Removed empty mapping {} from defaults descriptor success:{}", existingMapping, success);
}
else
{
existingMapping.setPathSpecs(updatedPaths);
if (LOG.isDebugEnabled())
LOG.debug("Removed path {} from mapping {} from defaults descriptor ", p, existingMapping);
}
}
_context.getMetaData().setOrigin(servletName + ".servlet.mapping.url" + p, annotation, clazz);
}
allMappings.add(mapping);
_context.getServletHandler().setServletMappings(allMappings.toArray(new ServletMapping[allMappings.size()]));
}
}
/**
*
*/
private List<ServletMapping> getServletMappingsForServlet(String name)
{
ServletMapping[] allMappings = _context.getServletHandler().getServletMappings();
if (allMappings == null)
return Collections.emptyList();
List<ServletMapping> mappings = new ArrayList<ServletMapping>();
for (ServletMapping m : allMappings)
{
if (m.getServletName() != null && name.equals(m.getServletName()))
{
mappings.add(m);
}
}
return mappings;
}
/**
*
*/
private boolean containsNonDefaultMappings(List<ServletMapping> mappings)
{
if (mappings == null)
return false;
for (ServletMapping m : mappings)
{
if (!m.isFromDefaultDescriptor())
return true;
}
return false;
}
}

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* WebServletAnnotationHandler
*
* Process a WebServlet annotation on a class.
*/
public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(WebServletAnnotationHandler.class);
public WebServletAnnotationHandler(WebAppContext context)
{
super(context);
}
/**
* Handle discovering a WebServlet annotation.
*/
@Override
public void handle(AnnotationParser.ClassInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebServlet".equals(annotationName))
return;
WebServletAnnotation annotation = new WebServletAnnotation(_context, info.getClassName(), info.getContainingResource());
addAnnotation(annotation);
}
@Override
public void handle(AnnotationParser.FieldInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebServlet".equals(annotationName))
return;
LOG.warn("@WebServlet annotation not supported for fields");
}
@Override
public void handle(AnnotationParser.MethodInfo info, String annotationName)
{
if (annotationName == null || !"jakarta.servlet.annotation.WebServlet".equals(annotationName))
return;
LOG.warn("@WebServlet annotation not supported for methods");
}
}

View File

@ -0,0 +1,17 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
/**
* Jetty Annotations : Support for Servlet Annotations
*/
package org.eclipse.jetty.ee10.annotations;

View File

@ -0,0 +1 @@
org.eclipse.jetty.ee10.annotations.AnnotationConfiguration

View File

@ -0,0 +1,25 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.acme;
/**
* ClassOne
*/
public class ClassOne
{
public void one()
{
}
}

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
/**
* ClassA
*/
@Sample(1)
public class ClassA
{
private Integer e;
private Integer f;
private Integer g;
private Integer h;
private Integer j;
private Integer k;
public static class Foo
{
}
@Sample(7)
private Integer m;
@Sample(2)
public void a(Integer[] x)
{
System.err.println("ClassA.public");
}
@Sample(3)
protected void b(Foo[] f)
{
System.err.println("ClassA.protected");
}
@Sample(4)
void c(int[] x)
{
System.err.println("ClassA.package");
}
@Sample(5)
private void d(int x, String y)
{
System.err.println("ClassA.private");
}
@Sample(6)
protected void l()
{
System.err.println("ClassA.protected method l");
}
public Integer getE()
{
return this.e;
}
public Integer getF()
{
return this.f;
}
public Integer getG()
{
return this.g;
}
public Integer getJ()
{
return this.j;
}
public void x()
{
System.err.println("ClassA.x");
}
}

View File

@ -0,0 +1,50 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
/**
* ClassB
*/
@Sample(value = 50)
@Multi({"do", "re", "mi"})
public class ClassB extends ClassA implements InterfaceD
{
//test override of public scope method
@Sample(value = 51)
@Multi({"fa", "so", "la"})
public void a()
{
System.err.println("ClassB.public");
}
//test override of package scope method
@Sample(value = 52)
void c()
{
System.err.println("ClassB.package");
}
@Override
public void l()
{
System.err.println("Overridden method l has no annotation");
}
//test no annotation
public void z()
{
System.err.println("ClassB.z");
}
}

View File

@ -0,0 +1,78 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.io.IOException;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import jakarta.annotation.security.RunAs;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
@WebFilter(filterName = "CFilter", dispatcherTypes = {DispatcherType.REQUEST}, urlPatterns = {"/*"}, initParams = {
@WebInitParam(name = "a", value = "99")
}, asyncSupported = false)
@RunAs("admin")
public class FilterC implements Filter
{
@Resource(mappedName = "foo")
private Double foo;
@PreDestroy
public void pre()
{
}
@PostConstruct
public void post()
{
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest)arg0;
HttpServletResponse response = (HttpServletResponse)arg1;
HttpSession session = request.getSession(true);
String val = request.getParameter("action");
if (val != null)
session.setAttribute("action", val);
arg2.doFilter(request, response);
}
@Override
public void destroy()
{
}
@Override
public void init(FilterConfig arg0) throws ServletException
{
}
}

View File

@ -0,0 +1,22 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
/**
* InterfaceD
*/
public interface InterfaceD
{
}

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;
@WebListener
public class ListenerC implements ServletContextListener
{
@Override
public void contextDestroyed(ServletContextEvent arg0)
{
}
@Override
public void contextInitialized(ServletContextEvent arg0)
{
}
}

View File

@ -0,0 +1,26 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface Multi
{
String[] value();
}

View File

@ -0,0 +1,26 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface Sample
{
int value();
}

View File

@ -0,0 +1,68 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.io.IOException;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import jakarta.annotation.security.DeclareRoles;
import jakarta.annotation.security.RunAs;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.HttpConstraint;
import jakarta.servlet.annotation.HttpMethodConstraint;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.ServletSecurity;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@DeclareRoles({"alice"})
@WebServlet(urlPatterns = {"/foo/*", "/bah/*"}, name = "CServlet", initParams = {
@WebInitParam(name = "x", value = "y")
}, loadOnStartup = 2, asyncSupported = false)
@MultipartConfig(fileSizeThreshold = 1000, maxFileSize = 2000, maxRequestSize = 3000)
@RunAs("admin")
@ServletSecurity(value = @HttpConstraint(rolesAllowed = {"fred", "bill", "dorothy"}), httpMethodConstraints = {
@HttpMethodConstraint(value = "GET", rolesAllowed =
{"bob", "carol", "ted"})
})
public class ServletC extends HttpServlet
{
@Resource(mappedName = "foo", type = Double.class)
private Double foo;
@PreDestroy
public void pre()
{
}
@PostConstruct
public void post()
{
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/html");
response.getWriter().println("<h1>Annotated Servlet</h1>");
response.getWriter().println("An annotated Servlet.");
}
}

View File

@ -0,0 +1,26 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
@WebServlet(urlPatterns = {"/", "/bah/*"}, name = "DServlet", initParams = {
@WebInitParam(name = "x", value = "y")
}, loadOnStartup = 1, asyncSupported = false)
public class ServletD extends HttpServlet
{
// no op
}

View File

@ -0,0 +1,25 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import jakarta.annotation.PreDestroy;
import jakarta.servlet.http.HttpServlet;
public class ServletE extends HttpServlet
{
@PreDestroy
public void preDestroy()
{
}
}

View File

@ -0,0 +1,400 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.List;
import jakarta.servlet.ServletContainerInitializer;
import org.eclipse.jetty.ee10.webapp.RelativeOrdering;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebDescriptor;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.JAR;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
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.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestAnnotationConfiguration
{
public class TestableAnnotationConfiguration extends AnnotationConfiguration
{
public void assertAnnotationDiscovery(boolean b)
{
if (!b)
assertTrue(_discoverableAnnotationHandlers.isEmpty());
else
assertFalse(_discoverableAnnotationHandlers.isEmpty());
}
}
public File web25;
public File web31false;
public File web31true;
public File jarDir;
public File testSciJar;
public File testContainerSciJar;
public File testWebInfClassesJar;
public File unpacked;
public URLClassLoader containerLoader;
public URLClassLoader webAppLoader;
public List<Resource> classes;
public Resource targetClasses;
public Resource webInfClasses;
@BeforeEach
public void setup() throws Exception
{
web25 = MavenTestingUtils.getTestResourceFile("web25.xml");
web31false = MavenTestingUtils.getTestResourceFile("web31false.xml");
web31true = MavenTestingUtils.getTestResourceFile("web31true.xml");
// prepare an sci that will be on the webapp's classpath
jarDir = new File(MavenTestingUtils.getTestResourcesDir().getParentFile(), "jar");
testSciJar = new File(jarDir, "test-sci.jar");
assertTrue(testSciJar.exists());
testContainerSciJar = new File(jarDir, "test-sci-for-container-path.jar");
testWebInfClassesJar = new File(jarDir, "test-sci-for-webinf.jar");
// unpack some classes to pretend that are in WEB-INF/classes
unpacked = new File(MavenTestingUtils.getTargetTestingDir(), "test-sci-for-webinf");
unpacked.mkdirs();
FS.cleanDirectory(unpacked);
JAR.unpack(testWebInfClassesJar, unpacked);
webInfClasses = Resource.newResource(unpacked);
containerLoader = new URLClassLoader(new URL[]{
testContainerSciJar.toURI().toURL()
}, Thread.currentThread().getContextClassLoader());
targetClasses = Resource.newResource(MavenTestingUtils.getTargetDir().toURI()).addPath("/test-classes");
classes = Arrays.asList(new Resource[]{webInfClasses, targetClasses});
webAppLoader = new URLClassLoader(new URL[]{
testSciJar.toURI().toURL(), targetClasses.getURI().toURL(), webInfClasses.getURI().toURL()
},
containerLoader);
}
@Test
public void testAnnotationScanControl() throws Exception
{
//check that a 2.5 webapp with configurationDiscovered will discover annotations
TestableAnnotationConfiguration config25 = new TestableAnnotationConfiguration();
WebAppContext context25 = new WebAppContext();
context25.setClassLoader(Thread.currentThread().getContextClassLoader());
context25.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
context25.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
context25.setConfigurationDiscovered(false);
context25.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context25.getContext().getServletContext().setEffectiveMajorVersion(2);
context25.getContext().getServletContext().setEffectiveMinorVersion(5);
config25.configure(context25);
config25.assertAnnotationDiscovery(false);
//check that a 2.5 webapp discover annotations
TestableAnnotationConfiguration config25b = new TestableAnnotationConfiguration();
WebAppContext context25b = new WebAppContext();
context25b.setClassLoader(Thread.currentThread().getContextClassLoader());
context25b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
context25b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
context25b.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context25b.getContext().getServletContext().setEffectiveMajorVersion(2);
context25b.getContext().getServletContext().setEffectiveMinorVersion(5);
config25b.configure(context25b);
config25b.assertAnnotationDiscovery(true);
//check that a 3.x webapp with metadata true won't discover annotations
TestableAnnotationConfiguration config31 = new TestableAnnotationConfiguration();
WebAppContext context31 = new WebAppContext();
context31.setClassLoader(Thread.currentThread().getContextClassLoader());
context31.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
context31.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
context31.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true)));
context31.getContext().getServletContext().setEffectiveMajorVersion(3);
context31.getContext().getServletContext().setEffectiveMinorVersion(1);
config31.configure(context31);
config31.assertAnnotationDiscovery(false);
//check that a 3.x webapp with metadata false will discover annotations
TestableAnnotationConfiguration config31b = new TestableAnnotationConfiguration();
WebAppContext context31b = new WebAppContext();
context31b.setClassLoader(Thread.currentThread().getContextClassLoader());
context31b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
context31b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
context31b.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31false)));
context31b.getContext().getServletContext().setEffectiveMajorVersion(3);
context31b.getContext().getServletContext().setEffectiveMinorVersion(1);
config31b.configure(context31b);
config31b.assertAnnotationDiscovery(true);
}
@Test
public void testServerAndWebappSCIs() throws Exception
{
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(webAppLoader);
try
{
AnnotationConfiguration config = new AnnotationConfiguration();
WebAppContext context = new WebAppContext();
List<ServletContainerInitializer> scis;
//test 3.1 webapp loads both server and app scis
context.setClassLoader(webAppLoader);
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true)));
context.getMetaData().setWebInfClassesResources(classes);
context.getContext().getServletContext().setEffectiveMajorVersion(3);
context.getContext().getServletContext().setEffectiveMinorVersion(1);
scis = config.getNonExcludedInitializers(context);
assertNotNull(scis);
assertEquals(3, scis.size());
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path
assertEquals("com.acme.webinf.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); // web-inf
assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); //web-inf jar no web-fragment
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
@Test
public void testClassScanHandlersForSCIs() throws Exception
{
//test that SCIs with a @HandlesTypes that is an annotation registers
//handlers for the scanning phase that will capture the class hierarchy,
//and also capture all classes that contain the annotation
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(webAppLoader);
try
{
class MyAnnotationConfiguration extends AnnotationConfiguration
{
@Override
public void createServletContainerInitializerAnnotationHandlers(WebAppContext context, List<ServletContainerInitializer> scis) throws Exception
{
super.createServletContainerInitializerAnnotationHandlers(context, scis);
//check class hierarchy scanner handler is registered
assertNotNull(_classInheritanceHandler);
//check
assertEquals(1, _containerInitializerAnnotationHandlers.size());
ContainerInitializerAnnotationHandler handler = _containerInitializerAnnotationHandlers.get(0);
assertThat(handler._holder.toString(), containsString("com.acme.initializer.FooInitializer"));
assertEquals("com.acme.initializer.Foo", handler._annotation.getName());
}
}
MyAnnotationConfiguration config = new MyAnnotationConfiguration();
WebAppContext context = new WebAppContext();
List<ServletContainerInitializer> scis;
context.setClassLoader(webAppLoader);
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true)));
context.getMetaData().setWebInfClassesResources(classes);
context.getContext().getServletContext().setEffectiveMajorVersion(3);
context.getContext().getServletContext().setEffectiveMinorVersion(1);
scis = config.getNonExcludedInitializers(context);
assertNotNull(scis);
assertEquals(3, scis.size());
config.createServletContainerInitializerAnnotationHandlers(context, scis);
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
@Test
public void testMetaDataCompleteSCIs() throws Exception
{
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(webAppLoader);
try
{
AnnotationConfiguration config = new AnnotationConfiguration();
WebAppContext context = new WebAppContext();
List<ServletContainerInitializer> scis;
// test a 3.1 webapp with metadata-complete=false loads both server
// and webapp scis
context.setClassLoader(webAppLoader);
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31false)));
context.getMetaData().setWebInfClassesResources(classes);
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
context.getContext().getServletContext().setEffectiveMajorVersion(3);
context.getContext().getServletContext().setEffectiveMinorVersion(1);
scis = config.getNonExcludedInitializers(context);
assertNotNull(scis);
assertEquals(3, scis.size());
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); // container
// path
assertEquals("com.acme.webinf.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); // web-inf
assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); // web-inf
// jar
// no
// web-fragment
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
@Test
public void testRelativeOrderingWithSCIs() throws Exception
{
// test a 3.1 webapp with RELATIVE ORDERING loads sci from
// equivalent of WEB-INF/classes first as well as container path
ClassLoader old = Thread.currentThread().getContextClassLoader();
File orderedFragmentJar = new File(jarDir, "test-sci-with-ordering.jar");
assertTrue(orderedFragmentJar.exists());
URLClassLoader orderedLoader = new URLClassLoader(new URL[]{
orderedFragmentJar.toURI().toURL(), testSciJar.toURI().toURL(),
targetClasses.getURI().toURL(), webInfClasses.getURI().toURL()
},
containerLoader);
Thread.currentThread().setContextClassLoader(orderedLoader);
try
{
AnnotationConfiguration config = new AnnotationConfiguration();
WebAppContext context = new WebAppContext();
List<ServletContainerInitializer> scis;
context.setClassLoader(orderedLoader);
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true)));
RelativeOrdering ordering = new RelativeOrdering(context.getMetaData());
context.getMetaData().setOrdering(ordering);
context.getMetaData().addWebInfResource(Resource.newResource(orderedFragmentJar.toURI().toURL()));
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
context.getMetaData().setWebInfClassesResources(classes);
context.getMetaData().orderFragments();
context.getContext().getServletContext().setEffectiveMajorVersion(3);
context.getContext().getServletContext().setEffectiveMinorVersion(1);
scis = config.getNonExcludedInitializers(context);
assertNotNull(scis);
assertEquals(4, scis.size());
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path
assertEquals("com.acme.webinf.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); // web-inf
assertEquals("com.acme.ordering.AcmeServletContainerInitializer", scis.get(2).getClass().getName()); // first
assertEquals("com.acme.initializer.FooInitializer", scis.get(3).getClass().getName()); //other in ordering
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
@Test
public void testDiscoveredFalseWithSCIs() throws Exception
{
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(webAppLoader);
try
{
//test 2.5 webapp with configurationDiscovered=false loads only server scis
AnnotationConfiguration config = new AnnotationConfiguration();
WebAppContext context = new WebAppContext();
List<ServletContainerInitializer> scis;
context.setConfigurationDiscovered(false);
context.setClassLoader(webAppLoader);
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context.getMetaData().setWebInfClassesResources(classes);
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
context.getContext().getServletContext().setEffectiveMajorVersion(2);
context.getContext().getServletContext().setEffectiveMinorVersion(5);
scis = config.getNonExcludedInitializers(context);
assertNotNull(scis);
for (ServletContainerInitializer s : scis)
{
//should not have any of the web-inf lib scis in here
assertFalse(s.getClass().getName().equals("com.acme.ordering.AcmeServletContainerInitializer"));
assertFalse(s.getClass().getName().equals("com.acme.initializer.FooInitializer"));
//NOTE: should also not have the web-inf classes scis in here either, but due to the
//way the test is set up, the sci we're pretending is in web-inf classes will actually
//NOT be loaded by the webapp's classloader, but rather by the junit classloader, so
//it looks as if it is a container class.
}
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
@Test
public void testDiscoveredTrueWithSCIs() throws Exception
{
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(webAppLoader);
try
{
//test 2.5 webapp with configurationDiscovered=true loads both server and webapp scis
AnnotationConfiguration config = new AnnotationConfiguration();
WebAppContext context = new WebAppContext();
List<ServletContainerInitializer> scis;
context.setConfigurationDiscovered(true);
context.setClassLoader(webAppLoader);
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context.getMetaData().setWebInfClassesResources(classes);
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
context.getContext().getServletContext().setEffectiveMajorVersion(2);
context.getContext().getServletContext().setEffectiveMinorVersion(5);
scis = config.getNonExcludedInitializers(context);
assertNotNull(scis);
assertEquals(3, scis.size(), () -> scis.toString());
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path
assertEquals("com.acme.webinf.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); // web-inf
assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); //web-inf jar no web-fragment
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
}

View File

@ -0,0 +1,122 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallbackCollection;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.Source;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebDescriptor;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.resource.EmptyResource;
import org.eclipse.jetty.xml.XmlParser;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestAnnotationDecorator
{
public class TestWebDescriptor extends WebDescriptor
{
public TestWebDescriptor(MetaData.Complete metadata)
{
super(EmptyResource.INSTANCE);
_metaDataComplete = metadata;
}
@Override
public void parse(XmlParser parser) throws Exception
{
}
@Override
public void processVersion()
{
}
@Override
public void processOrdering()
{
}
@Override
public void processDistributable()
{
}
@Override
public int getMajorVersion()
{
return 4;
}
@Override
public int getMinorVersion()
{
return 0;
}
}
@Test
public void testAnnotationDecorator() throws Exception
{
assertThrows(NullPointerException.class, () ->
{
new AnnotationDecorator(null);
});
WebAppContext context = new WebAppContext();
AnnotationDecorator decorator = new AnnotationDecorator(context);
ServletE servlet = new ServletE();
//test without BaseHolder metadata
decorator.decorate(servlet);
LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
assertNotNull(callbacks);
assertFalse(callbacks.getPreDestroyCallbacks().isEmpty());
//reset
context.removeAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
//test with BaseHolder metadata, should not introspect with metdata-complete==true
context.getMetaData().setWebDescriptor(new TestWebDescriptor(MetaData.Complete.True));
assertTrue(context.getMetaData().isMetaDataComplete());
ServletHolder holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, ""));
holder.setHeldClass(ServletE.class);
context.getServletHandler().addServlet(holder);
DecoratedObjectFactory.associateInfo(holder);
decorator = new AnnotationDecorator(context);
decorator.decorate(servlet);
DecoratedObjectFactory.disassociateInfo();
callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
assertNull(callbacks);
//reset
context.removeAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
//test with BaseHolder metadata, should introspect with metadata-complete==false
context.getMetaData().setWebDescriptor(new TestWebDescriptor(MetaData.Complete.False));
DecoratedObjectFactory.associateInfo(holder);
decorator = new AnnotationDecorator(context);
decorator.decorate(servlet);
DecoratedObjectFactory.disassociateInfo();
callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
assertNotNull(callbacks);
assertFalse(callbacks.getPreDestroyCallbacks().isEmpty());
}
}

View File

@ -0,0 +1,177 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasKey;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
*
*/
public class TestAnnotationInheritance
{
List<String> classNames = new ArrayList<String>();
class SampleHandler extends AnnotationParser.AbstractHandler
{
public final List<String> annotatedClassNames = new ArrayList<String>();
public final List<String> annotatedMethods = new ArrayList<String>();
public final List<String> annotatedFields = new ArrayList<String>();
@Override
public void handle(AnnotationParser.ClassInfo info, String annotation)
{
if (annotation == null || !"org.eclipse.jetty.ee10.annotations.Sample".equals(annotation))
return;
annotatedClassNames.add(info.getClassName());
}
@Override
public void handle(AnnotationParser.FieldInfo info, String annotation)
{
if (annotation == null || !"org.eclipse.jetty.ee10.annotations.Sample".equals(annotation))
return;
annotatedFields.add(info.getClassInfo().getClassName() + "." + info.getFieldName());
}
@Override
public void handle(AnnotationParser.MethodInfo info, String annotation)
{
if (annotation == null || !"org.eclipse.jetty.ee10.annotations.Sample".equals(annotation))
return;
annotatedMethods.add(info.getClassInfo().getClassName() + "." + info.getMethodName());
}
@Override
public String toString()
{
return annotatedClassNames.toString() + annotatedMethods + annotatedFields;
}
}
@AfterEach
public void destroy() throws Exception
{
classNames.clear();
InitialContext ic = new InitialContext();
Context comp = (Context)ic.lookup("java:comp");
comp.destroySubcontext("env");
}
@Test
public void testParseClassNames() throws Exception
{
classNames.add(ClassA.class.getName());
classNames.add(ClassB.class.getName());
SampleHandler handler = new SampleHandler();
AnnotationParser parser = new AnnotationParser();
parser.parse(Collections.singleton(handler), classNames);
//check we got 2 class annotations
assertEquals(2, handler.annotatedClassNames.size());
//check we got all annotated methods on each class
assertEquals(7, handler.annotatedMethods.size());
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.a"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.b"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.c"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.d"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.l"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassB.a"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassB.c"));
//check we got all annotated fields on each class
assertEquals(1, handler.annotatedFields.size());
assertEquals("org.eclipse.jetty.ee10.annotations.ClassA.m", handler.annotatedFields.get(0));
}
@Test
public void testParseClass() throws Exception
{
SampleHandler handler = new SampleHandler();
AnnotationParser parser = new AnnotationParser();
parser.parse(Collections.singleton(handler), ClassB.class, true);
//check we got 2 class annotations
assertEquals(2, handler.annotatedClassNames.size());
//check we got all annotated methods on each class
assertEquals(7, handler.annotatedMethods.size());
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.a"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.b"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.c"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.d"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.l"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassB.a"));
assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassB.c"));
//check we got all annotated fields on each class
assertEquals(1, handler.annotatedFields.size());
assertEquals("org.eclipse.jetty.ee10.annotations.ClassA.m", handler.annotatedFields.get(0));
}
@Test
public void testTypeInheritanceHandling() throws Exception
{
Map<String, Set<String>> map = new ConcurrentHashMap<>();
AnnotationParser parser = new AnnotationParser();
ClassInheritanceHandler handler = new ClassInheritanceHandler(map);
class Foo implements InterfaceD
{
}
classNames.clear();
classNames.add(ClassA.class.getName());
classNames.add(ClassB.class.getName());
classNames.add(InterfaceD.class.getName());
classNames.add(Foo.class.getName());
parser.parse(Collections.singleton(handler), classNames);
assertNotNull(map);
assertFalse(map.isEmpty());
assertEquals(2, map.size());
assertThat(map, hasKey("org.eclipse.jetty.ee10.annotations.ClassA"));
assertThat(map, hasKey("org.eclipse.jetty.ee10.annotations.InterfaceD"));
Set<String> classes = map.get("org.eclipse.jetty.ee10.annotations.ClassA");
assertThat(classes, contains("org.eclipse.jetty.ee10.annotations.ClassB"));
classes = map.get("org.eclipse.jetty.ee10.annotations.InterfaceD");
assertThat(classes, containsInAnyOrder("org.eclipse.jetty.ee10.annotations.ClassB",
Foo.class.getName()));
}
}

View File

@ -0,0 +1,93 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.io.File;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.Source;
import org.eclipse.jetty.ee10.webapp.FragmentDescriptor;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebDescriptor;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestAnnotationIntrospector
{
@Test
public void testIsIntrospectable() throws Exception
{
try (StacklessLogging ignore = new StacklessLogging(AnnotationIntrospector.class))
{
WebAppContext wac = new WebAppContext();
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
//can't introspect nothing
assertFalse(introspector.isIntrospectable(null, null));
//can introspect if no metadata to say otherwise
assertTrue(introspector.isIntrospectable(new Object(), null));
//can introspect if metdata isn't a BaseHolder
assertTrue(introspector.isIntrospectable(new Object(), new Object()));
//an EMBEDDED sourced servlet can be introspected
ServletHolder holder = new ServletHolder();
holder.setHeldClass(ServletE.class);
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
//a JAVAX API sourced servlet can be introspected
holder = new ServletHolder(Source.JAVAX_API);
holder.setHeldClass(ServletE.class);
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
//an ANNOTATION sourced servlet can be introspected
holder = new ServletHolder(new Source(Source.Origin.ANNOTATION, ServletE.class.getName()));
holder.setHeldClass(ServletE.class);
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
//a DESCRIPTOR sourced servlet can be introspected if web.xml metdata-complete==false
File file = MavenTestingUtils.getTestResourceFile("web31false.xml");
Resource resource = Resource.newResource(file);
wac.getMetaData().setWebDescriptor(new WebDescriptor(resource));
holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString()));
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
//a DESCRIPTOR sourced servlet can be introspected if web-fragment.xml medata-complete==false && web.xml metadata-complete==false
file = MavenTestingUtils.getTestResourceFile("web-fragment4false.xml");
resource = Resource.newResource(file);
wac.getMetaData().addFragmentDescriptor(Resource.newResource(file.getParentFile()), new FragmentDescriptor(resource));
holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString()));
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
//a DESCRIPTOR sourced servlet cannot be introspected if web-fragment.xml medata-complete==true (&& web.xml metadata-complete==false)
file = MavenTestingUtils.getTestResourceFile("web-fragment4true.xml");
resource = Resource.newResource(file);
wac.getMetaData().addFragmentDescriptor(Resource.newResource(file.getParentFile()), new FragmentDescriptor(resource));
holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString()));
assertFalse(introspector.isIntrospectable(new ServletE(), holder));
//a DESCRIPTOR sourced servlet cannot be introspected if web.xml medata-complete==true
file = MavenTestingUtils.getTestResourceFile("web31true.xml");
resource = Resource.newResource(file);
wac.getMetaData().setWebDescriptor(new WebDescriptor(resource));
holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString()));
assertFalse(introspector.isIntrospectable(new ServletE(), holder));
}
}
}

View File

@ -0,0 +1,298 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.in;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(WorkDirExtension.class)
public class TestAnnotationParser
{
public static class TrackingAnnotationHandler extends AnnotationParser.AbstractHandler
{
private final String annotationName;
public final Set<String> foundClasses;
public TrackingAnnotationHandler(String annotationName)
{
this.annotationName = annotationName;
this.foundClasses = new HashSet<>();
}
@Override
public void handle(AnnotationParser.ClassInfo info, String annotation)
{
if (annotation == null || !annotationName.equals(annotation))
return;
foundClasses.add(info.getClassName());
}
}
public static class DuplicateClassScanHandler extends AnnotationParser.AbstractHandler
{
private Map<String, List<String>> _classMap = new ConcurrentHashMap();
@Override
public void handle(AnnotationParser.ClassInfo info)
{
List<String> list = new CopyOnWriteArrayList<>();
Resource r = info.getContainingResource();
list.add((r == null ? "" : r.toString()));
List<String> existing = _classMap.putIfAbsent(info.getClassName(), list);
if (existing != null)
{
existing.addAll(list);
}
}
public List<String> getParsedList(String classname)
{
return _classMap.get(classname);
}
}
public WorkDir testdir;
@Test
public void testSampleAnnotation() throws Exception
{
String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassA"};
AnnotationParser parser = new AnnotationParser();
class SampleAnnotationHandler extends AnnotationParser.AbstractHandler
{
private List<String> methods = Arrays.asList("a", "b", "c", "d", "l");
@Override
public void handle(AnnotationParser.ClassInfo info, String annotation)
{
if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation))
return;
assertEquals("org.eclipse.jetty.annotations.ClassA", info.getClassName());
}
@Override
public void handle(AnnotationParser.FieldInfo info, String annotation)
{
if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation))
return;
assertEquals("m", info.getFieldName());
assertEquals(org.objectweb.asm.Type.OBJECT, org.objectweb.asm.Type.getType(info.getFieldType()).getSort());
}
@Override
public void handle(AnnotationParser.MethodInfo info, String annotation)
{
if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation))
return;
assertEquals("org.eclipse.jetty.annotations.ClassA", info.getClassInfo().getClassName());
assertThat(info.getMethodName(), is(in(methods)));
assertEquals("org.eclipse.jetty.annotations.Sample", annotation);
}
}
//long start = System.currentTimeMillis();
parser.parse(Collections.singleton(new SampleAnnotationHandler()), classNames);
//long end = System.currentTimeMillis();
//System.err.println("Time to parse class: " + ((end - start)));
}
@Test
public void testMultiAnnotation() throws Exception
{
String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassB"};
AnnotationParser parser = new AnnotationParser();
class MultiAnnotationHandler extends AnnotationParser.AbstractHandler
{
@Override
public void handle(AnnotationParser.ClassInfo info, String annotation)
{
if (annotation == null || !"org.eclipse.jetty.annotations.Multi".equals(annotation))
return;
assertTrue("org.eclipse.jetty.annotations.ClassB".equals(info.getClassName()));
}
@Override
public void handle(AnnotationParser.FieldInfo info, String annotation)
{
assertTrue(annotation == null || !"org.eclipse.jetty.annotations.Multi".equals(annotation),
"There should not be any");
}
@Override
public void handle(AnnotationParser.MethodInfo info, String annotation)
{
if (annotation == null || !"org.eclipse.jetty.annotations.Multi".equals(annotation))
return;
assertTrue("org.eclipse.jetty.annotations.ClassB".equals(info.getClassInfo().getClassName()));
assertTrue("a".equals(info.getMethodName()));
}
}
parser.parse(Collections.singleton(new MultiAnnotationHandler()), classNames);
}
@Test
public void testHiddenFilesInJar() throws Exception
{
File badClassesJar = MavenTestingUtils.getTestResourceFile("bad-classes.jar");
AnnotationParser parser = new AnnotationParser();
Set<AnnotationParser.Handler> emptySet = Collections.emptySet();
parser.parse(emptySet, badClassesJar.toURI());
// only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here
}
@Test
public void testModuleInfoClassInJar() throws Exception
{
File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/slf4j-api-1.8.0-alpha2.jar");
AnnotationParser parser = new AnnotationParser();
Set<AnnotationParser.Handler> emptySet = Collections.emptySet();
parser.parse(emptySet, badClassesJar.toURI());
// Should throw no exceptions, and happily skip the module-info.class files
}
@Test
public void testJep238MultiReleaseInJar() throws Exception
{
File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/log4j-api-2.9.0.jar");
AnnotationParser parser = new AnnotationParser();
Set<AnnotationParser.Handler> emptySet = Collections.emptySet();
parser.parse(emptySet, badClassesJar.toURI());
// Should throw no exceptions, and skip the META-INF/versions/9/* files
}
@Test
public void testJep238MultiReleaseInJarJDK10() throws Exception
{
File jdk10Jar = MavenTestingUtils.getTestResourceFile("jdk10/multirelease-10.jar");
AnnotationParser parser = new AnnotationParser();
DuplicateClassScanHandler handler = new DuplicateClassScanHandler();
Set<AnnotationParser.Handler> handlers = Collections.singleton(handler);
parser.parse(handlers, new PathResource(jdk10Jar));
// Should throw no exceptions
}
@Test
public void testBasedirExclusion() throws Exception
{
// Build up basedir, which itself has a path segment that violates java package and classnaming.
// The basedir should have no effect on annotation scanning.
// Intentionally using a base director name that starts with a "."
// This mimics what you see in jenkins, hudson, hadoop, solr, camel, and selenium for their
// installed and/or managed webapps
File basedir = testdir.getPathFile(".base/workspace/classes").toFile();
FS.ensureEmpty(basedir);
// Copy in class that is known to have annotations.
copyClass(ClassA.class, basedir);
// Setup Tracker
TrackingAnnotationHandler tracker = new TrackingAnnotationHandler(Sample.class.getName());
// Setup annotation scanning
AnnotationParser parser = new AnnotationParser();
// Parse
parser.parse(Collections.singleton(tracker), basedir.toURI());
// Validate
assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName()));
}
@Test
public void testScanDuplicateClassesInJars() throws Exception
{
Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar"));
Resource testJar2 = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest_copy.jar"));
AnnotationParser parser = new AnnotationParser();
DuplicateClassScanHandler handler = new DuplicateClassScanHandler();
Set<AnnotationParser.Handler> handlers = Collections.singleton(handler);
parser.parse(handlers, testJar);
parser.parse(handlers, testJar2);
List<String> locations = handler.getParsedList("org.acme.ClassOne");
assertNotNull(locations);
assertEquals(2, locations.size());
assertTrue(!(locations.get(0).equals(locations.get(1))));
}
@Test
public void testScanDuplicateClasses() throws Exception
{
Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar"));
File testClasses = new File(MavenTestingUtils.getTargetDir(), "test-classes");
AnnotationParser parser = new AnnotationParser();
DuplicateClassScanHandler handler = new DuplicateClassScanHandler();
Set<AnnotationParser.Handler> handlers = Collections.singleton(handler);
parser.parse(handlers, testJar);
parser.parse(handlers, Resource.newResource(testClasses));
List<String> locations = handler.getParsedList("org.acme.ClassOne");
assertNotNull(locations);
assertEquals(2, locations.size());
assertTrue(!(locations.get(0).equals(locations.get(1))));
}
private void copyClass(Class<?> clazz, File basedir) throws IOException
{
String classRef = TypeUtil.toClassReference(clazz);
URL url = this.getClass().getResource('/' + classRef);
assertThat("URL for: " + classRef, url, notNullValue());
Path outputFile = basedir.toPath().resolve(classRef);
FS.ensureDirExists(outputFile.getParent());
try (InputStream in = url.openStream();
OutputStream out = Files.newOutputStream(outputFile))
{
IO.copy(in, out);
}
}
}

View File

@ -0,0 +1,98 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.HandlesTypes;
import org.eclipse.jetty.ee10.servlet.Source;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
public class TestDiscoveredServletContainerInitializerHolder
{
/**
* A marker type that is passed as an arg to @HandlesTypes
*/
interface Ordinary
{
}
/**
* An class with an annotation (that is listed in @HandlesTypes)
*/
@Sample(value = 1)
public static class ASample
{
}
/**
* A class that extends a class with an annotation
*/
public static class BSample extends ASample
{
}
@HandlesTypes({Sample.class})
public static class SampleServletContainerInitializer implements ServletContainerInitializer
{
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException
{
}
}
@Test
public void test() throws Exception
{
//SCI with @HandlesTypes[Ordinary, Sample]
SampleServletContainerInitializer sci = new SampleServletContainerInitializer();
AnnotationConfiguration.DiscoveredServletContainerInitializerHolder holder =
new AnnotationConfiguration.DiscoveredServletContainerInitializerHolder(new Source(Source.Origin.ANNOTATION, sci.getClass().getName()),
sci);
//add the @HandlesTypes to the holder
holder.addStartupClasses(Ordinary.class, Sample.class);
//pretend scanned and discovered that ASample has the Sample annotation
holder.addStartupClasses(ASample.class.getName());
//pretend we scanned the entire class hierarchy and found:
// com.acme.tom and com.acme.dick both extend Ordinary
// ASample has subclass BSample
Map<String, Set<String>> classMap = new HashMap<>();
classMap.put(Ordinary.class.getName(), new HashSet(Arrays.asList("com.acme.tom", "com.acme.dick")));
classMap.put(ASample.class.getName(), new HashSet(Arrays.asList(BSample.class.getName())));
holder.resolveClasses(classMap);
//we should now have the following classes that will be passed to the SampleServletContainerInitializer.onStartup
String toString = holder.toString();
assertThat(toString, containsString("com.acme.tom"));
assertThat(toString, containsString("com.acme.dick"));
assertThat(toString, containsString(ASample.class.getName()));
assertThat(toString, containsString(BSample.class.getName()));
assertThat(toString, containsString("applicable=[],annotated=[]"));
}
}

View File

@ -0,0 +1,58 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.io.File;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebDescriptor;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestRunAsAnnotation
{
@Test
public void testRunAsAnnotation() throws Exception
{
WebAppContext wac = new WebAppContext();
//pre-add a servlet but not by descriptor
ServletHolder holder = new ServletHolder();
holder.setName("foo1");
holder.setHeldClass(ServletC.class);
holder.setInitOrder(1); //load on startup
wac.getServletHandler().addServletWithMapping(holder, "/foo/*");
//add another servlet of the same class, but as if by descriptor
ServletHolder holder2 = new ServletHolder();
holder2.setName("foo2");
holder2.setHeldClass(ServletC.class);
holder2.setInitOrder(1);
wac.getServletHandler().addServletWithMapping(holder2, "/foo2/*");
Resource fakeXml = Resource.newResource(new File(MavenTestingUtils.getTargetTestingDir("run-as"), "fake.xml"));
wac.getMetaData().setOrigin(holder2.getName() + ".servlet.run-as", new WebDescriptor(fakeXml));
AnnotationIntrospector parser = new AnnotationIntrospector(wac);
RunAsAnnotationHandler handler = new RunAsAnnotationHandler(wac);
parser.registerHandler(handler);
parser.introspect(new ServletC(), null);
assertEquals("admin", holder.getRunAsRole());
assertEquals(null, holder2.getRunAsRole());
}
}

View File

@ -0,0 +1,340 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.Arrays;
import java.util.List;
import jakarta.servlet.annotation.HttpConstraint;
import jakarta.servlet.annotation.HttpMethodConstraint;
import jakarta.servlet.annotation.ServletSecurity;
import jakarta.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee;
import jakarta.servlet.http.HttpServlet;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.util.security.Constraint;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public class TestSecurityAnnotationConversions
{
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.DENY))
public static class DenyServlet extends HttpServlet
{
}
@ServletSecurity
public static class PermitServlet extends HttpServlet
{
}
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed =
{
"tom", "dick", "harry"
}))
public static class RolesServlet extends HttpServlet
{
}
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed =
{
"tom", "dick", "harry"
}), httpMethodConstraints = {@HttpMethodConstraint(value = "GET")})
public static class Method1Servlet extends HttpServlet
{
}
@ServletSecurity(
value = @HttpConstraint(
value = EmptyRoleSemantic.PERMIT,
transportGuarantee = TransportGuarantee.CONFIDENTIAL,
rolesAllowed = {
"tom", "dick", "harry"
}),
httpMethodConstraints = {
@HttpMethodConstraint(value = "GET", transportGuarantee = TransportGuarantee.CONFIDENTIAL)
})
public static class Method2Servlet extends HttpServlet
{
}
public void setUp()
{
}
@Test
public void testDenyAllOnClass() throws Exception
{
WebAppContext wac = makeWebAppContext(DenyServlet.class.getCanonicalName(), "denyServlet", new String[]{
"/foo/*", "*.foo"
});
//Assume we found 1 servlet with a @HttpConstraint with value=EmptyRoleSemantic.DENY security annotation
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
introspector.registerHandler(annotationHandler);
//set up the expected outcomes:
//1 ConstraintMapping per ServletMapping pathSpec
Constraint expectedConstraint = new Constraint();
expectedConstraint.setAuthenticate(true);
expectedConstraint.setDataConstraint(Constraint.DC_NONE);
ConstraintMapping[] expectedMappings = new ConstraintMapping[2];
expectedMappings[0] = new ConstraintMapping();
expectedMappings[0].setConstraint(expectedConstraint);
expectedMappings[0].setPathSpec("/foo/*");
expectedMappings[1] = new ConstraintMapping();
expectedMappings[1].setConstraint(expectedConstraint);
expectedMappings[1].setPathSpec("*.foo");
introspector.introspect(new DenyServlet(), null);
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
}
@Test
public void testPermitAll() throws Exception
{
//Assume we found 1 servlet with a @ServletSecurity security annotation
WebAppContext wac = makeWebAppContext(PermitServlet.class.getCanonicalName(), "permitServlet", new String[]{
"/foo/*", "*.foo"
});
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
introspector.registerHandler(annotationHandler);
//set up the expected outcomes - no constraints at all as per Servlet Spec 3.1 pg 129
//1 ConstraintMapping per ServletMapping pathSpec
ConstraintMapping[] expectedMappings = new ConstraintMapping[]{};
PermitServlet permit = new PermitServlet();
introspector.introspect(permit, null);
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
}
@Test
public void testRolesAllowedWithTransportGuarantee() throws Exception
{
//Assume we found 1 servlet with annotation with roles defined and
//and a TransportGuarantee
WebAppContext wac = makeWebAppContext(RolesServlet.class.getCanonicalName(), "rolesServlet", new String[]{
"/foo/*", "*.foo"
});
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
introspector.registerHandler(annotationHandler);
//set up the expected outcomes:compareResults
//1 ConstraintMapping per ServletMapping
Constraint expectedConstraint = new Constraint();
expectedConstraint.setAuthenticate(true);
expectedConstraint.setRoles(new String[]{"tom", "dick", "harry"});
expectedConstraint.setDataConstraint(Constraint.DC_CONFIDENTIAL);
ConstraintMapping[] expectedMappings = new ConstraintMapping[2];
expectedMappings[0] = new ConstraintMapping();
expectedMappings[0].setConstraint(expectedConstraint);
expectedMappings[0].setPathSpec("/foo/*");
expectedMappings[1] = new ConstraintMapping();
expectedMappings[1].setConstraint(expectedConstraint);
expectedMappings[1].setPathSpec("*.foo");
introspector.introspect(new RolesServlet(), null);
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
}
@Test
public void testMethodAnnotation() throws Exception
{
//ServletSecurity annotation with HttpConstraint of TransportGuarantee.CONFIDENTIAL, and a list of rolesAllowed, and
//an HttpMethodConstraint for GET method that permits all and has TransportGuarantee.NONE (ie is default)
WebAppContext wac = makeWebAppContext(Method1Servlet.class.getCanonicalName(), "method1Servlet", new String[]{
"/foo/*", "*.foo"
});
//set up the expected outcomes: - a Constraint for the RolesAllowed on the class
//with userdata constraint of DC_CONFIDENTIAL
//and mappings for each of the pathSpecs
Constraint expectedConstraint1 = new Constraint();
expectedConstraint1.setAuthenticate(true);
expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"});
expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL);
//a Constraint for the PermitAll on the doGet method with a userdata
//constraint of DC_CONFIDENTIAL inherited from the class
Constraint expectedConstraint2 = new Constraint();
expectedConstraint2.setDataConstraint(Constraint.DC_NONE);
ConstraintMapping[] expectedMappings = new ConstraintMapping[4];
expectedMappings[0] = new ConstraintMapping();
expectedMappings[0].setConstraint(expectedConstraint1);
expectedMappings[0].setPathSpec("/foo/*");
expectedMappings[0].setMethodOmissions(new String[]{"GET"});
expectedMappings[1] = new ConstraintMapping();
expectedMappings[1].setConstraint(expectedConstraint1);
expectedMappings[1].setPathSpec("*.foo");
expectedMappings[1].setMethodOmissions(new String[]{"GET"});
expectedMappings[2] = new ConstraintMapping();
expectedMappings[2].setConstraint(expectedConstraint2);
expectedMappings[2].setPathSpec("/foo/*");
expectedMappings[2].setMethod("GET");
expectedMappings[3] = new ConstraintMapping();
expectedMappings[3].setConstraint(expectedConstraint2);
expectedMappings[3].setPathSpec("*.foo");
expectedMappings[3].setMethod("GET");
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
introspector.registerHandler(annotationHandler);
introspector.introspect(new Method1Servlet(), null);
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
}
@Test
public void testMethodAnnotation2() throws Exception
{
//A ServletSecurity annotation that has HttpConstraint of CONFIDENTIAL with defined roles, but a
//HttpMethodConstraint for GET that permits all, but also requires CONFIDENTIAL
WebAppContext wac = makeWebAppContext(Method2Servlet.class.getCanonicalName(), "method2Servlet", new String[]{
"/foo/*", "*.foo"
});
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
introspector.registerHandler(annotationHandler);
//set up the expected outcomes: - a Constraint for the RolesAllowed on the class
//with userdata constraint of DC_CONFIDENTIAL
//and mappings for each of the pathSpecs
Constraint expectedConstraint1 = new Constraint();
expectedConstraint1.setAuthenticate(true);
expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"});
expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL);
//a Constraint for the Permit on the GET method with a userdata
//constraint of DC_CONFIDENTIAL
Constraint expectedConstraint2 = new Constraint();
expectedConstraint2.setDataConstraint(Constraint.DC_CONFIDENTIAL);
ConstraintMapping[] expectedMappings = new ConstraintMapping[4];
expectedMappings[0] = new ConstraintMapping();
expectedMappings[0].setConstraint(expectedConstraint1);
expectedMappings[0].setPathSpec("/foo/*");
expectedMappings[0].setMethodOmissions(new String[]{"GET"});
expectedMappings[1] = new ConstraintMapping();
expectedMappings[1].setConstraint(expectedConstraint1);
expectedMappings[1].setPathSpec("*.foo");
expectedMappings[1].setMethodOmissions(new String[]{"GET"});
expectedMappings[2] = new ConstraintMapping();
expectedMappings[2].setConstraint(expectedConstraint2);
expectedMappings[2].setPathSpec("/foo/*");
expectedMappings[2].setMethod("GET");
expectedMappings[3] = new ConstraintMapping();
expectedMappings[3].setConstraint(expectedConstraint2);
expectedMappings[3].setPathSpec("*.foo");
expectedMappings[3].setMethod("GET");
introspector.introspect(new Method2Servlet(), null);
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
}
private void compareResults(ConstraintMapping[] expectedMappings, List<ConstraintMapping> actualMappings)
{
assertNotNull(actualMappings);
assertEquals(expectedMappings.length, actualMappings.size());
for (int k = 0; k < actualMappings.size(); k++)
{
ConstraintMapping am = actualMappings.get(k);
boolean matched = false;
for (int i = 0; i < expectedMappings.length && !matched; i++)
{
ConstraintMapping em = expectedMappings[i];
if (em.getPathSpec().equals(am.getPathSpec()))
{
if ((em.getMethod() == null && am.getMethod() == null) || em.getMethod() != null && em.getMethod().equals(am.getMethod()))
{
matched = true;
assertEquals(em.getConstraint().getAuthenticate(), am.getConstraint().getAuthenticate());
assertEquals(em.getConstraint().getDataConstraint(), am.getConstraint().getDataConstraint());
if (em.getMethodOmissions() == null)
{
assertNull(am.getMethodOmissions());
}
else
{
assertTrue(Arrays.equals(am.getMethodOmissions(), em.getMethodOmissions()));
}
if (em.getConstraint().getRoles() == null)
{
assertNull(am.getConstraint().getRoles());
}
else
{
assertTrue(Arrays.equals(em.getConstraint().getRoles(), am.getConstraint().getRoles()));
}
}
}
}
if (!matched)
fail("No expected ConstraintMapping matching method:" + am.getMethod() + " pathSpec: " + am.getPathSpec());
}
}
private WebAppContext makeWebAppContext(String className, String servletName, String[] paths)
{
WebAppContext wac = new WebAppContext();
ServletHolder[] holders = new ServletHolder[1];
holders[0] = new ServletHolder();
holders[0].setClassName(className);
holders[0].setName(servletName);
holders[0].setServletHandler(wac.getServletHandler());
wac.getServletHandler().setServlets(holders);
wac.setSecurityHandler(new ConstraintSecurityHandler());
ServletMapping[] servletMappings = new ServletMapping[1];
servletMappings[0] = new ServletMapping();
servletMappings[0].setPathSpecs(paths);
servletMappings[0].setServletName(servletName);
wac.getServletHandler().setServletMappings(servletMappings);
return wac;
}
}

View File

@ -0,0 +1,290 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* TestServletAnnotations
*/
public class TestServletAnnotations
{
public class TestWebServletAnnotationHandler extends WebServletAnnotationHandler
{
List<DiscoveredAnnotation> _list = null;
public TestWebServletAnnotationHandler(WebAppContext context, List<DiscoveredAnnotation> list)
{
super(context);
_list = list;
}
@Override
public void addAnnotation(DiscoveredAnnotation a)
{
super.addAnnotation(a);
_list.add(a);
}
}
@Test
public void testServletAnnotation() throws Exception
{
List<String> classes = new ArrayList<String>();
classes.add("org.eclipse.jetty.ee10.annotations.ServletC");
AnnotationParser parser = new AnnotationParser();
WebAppContext wac = new WebAppContext();
List<DiscoveredAnnotation> results = new ArrayList<DiscoveredAnnotation>();
TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results);
parser.parse(Collections.singleton(handler), classes);
assertEquals(1, results.size());
assertTrue(results.get(0) instanceof WebServletAnnotation);
results.get(0).apply();
ServletHolder[] holders = wac.getServletHandler().getServlets();
assertNotNull(holders);
assertEquals(1, holders.length);
// Verify servlet annotations
ServletHolder cholder = holders[0];
assertThat("Servlet Name", cholder.getName(), is("CServlet"));
assertThat("InitParameter[x]", cholder.getInitParameter("x"), is("y"));
assertThat("Init Order", cholder.getInitOrder(), is(2));
assertThat("Async Supported", cholder.isAsyncSupported(), is(false));
// Verify mappings
ServletMapping[] mappings = wac.getServletHandler().getServletMappings();
assertNotNull(mappings);
assertEquals(1, mappings.length);
String[] paths = mappings[0].getPathSpecs();
assertNotNull(paths);
assertEquals(2, paths.length);
}
@Test
public void testWebServletAnnotationOverrideDefault() throws Exception
{
//if the existing servlet mapping TO A DIFFERENT SERVLET IS from a default descriptor we
//DO allow the annotation to replace the mapping.
WebAppContext wac = new WebAppContext();
ServletHolder defaultServlet = new ServletHolder();
defaultServlet.setClassName("org.eclipse.jetty.ee10.servlet.DefaultServlet");
defaultServlet.setName("default");
wac.getServletHandler().addServlet(defaultServlet);
ServletMapping m = new ServletMapping();
m.setPathSpec("/");
m.setServletName("default");
m.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor
wac.getServletHandler().addServletMapping(m);
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee10.annotations.ServletD", null);
annotation.apply();
//test that as the original servlet mapping had only 1 pathspec, then the whole
//servlet mapping should be deleted as that pathspec will be remapped to the DServlet
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
assertNotNull(resultMappings);
assertEquals(1, resultMappings.length);
assertEquals(2, resultMappings[0].getPathSpecs().length);
resultMappings[0].getServletName().equals("DServlet");
for (String s : resultMappings[0].getPathSpecs())
{
assertThat(s, anyOf(is("/"), is("/bah/*")));
}
}
@Test
public void testWebServletAnnotationReplaceDefault() throws Exception
{
//if the existing servlet mapping TO A DIFFERENT SERVLET IS from a default descriptor we
//DO allow the annotation to replace the mapping.
WebAppContext wac = new WebAppContext();
ServletHolder defaultServlet = new ServletHolder();
defaultServlet.setClassName("org.eclipse.jetty.ee10.servlet.DefaultServlet");
defaultServlet.setName("default");
wac.getServletHandler().addServlet(defaultServlet);
ServletMapping m = new ServletMapping();
m.setPathSpec("/");
m.setServletName("default");
m.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor
wac.getServletHandler().addServletMapping(m);
ServletMapping m2 = new ServletMapping();
m2.setPathSpec("/other");
m2.setServletName("default");
m2.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor
wac.getServletHandler().addServletMapping(m2);
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee10.annotations.ServletD", null);
annotation.apply();
//test that only the mapping for "/" was removed from the mappings to the default servlet
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
assertNotNull(resultMappings);
assertEquals(2, resultMappings.length);
for (ServletMapping r : resultMappings)
{
if (r.getServletName().equals("default"))
{
assertEquals(1, r.getPathSpecs().length);
assertEquals("/other", r.getPathSpecs()[0]);
}
else if (r.getServletName().equals("DServlet"))
{
assertEquals(2, r.getPathSpecs().length);
for (String p : r.getPathSpecs())
{
if (!p.equals("/") && !p.equals("/bah/*"))
fail("Unexpected path");
}
}
else
fail("Unexpected servlet mapping: " + r);
}
}
@Test
public void testWebServletAnnotationNotOverride() throws Exception
{
//if the existing servlet mapping TO A DIFFERENT SERVLET IS NOT from a default descriptor we
//DO NOT allow the annotation to replace the mapping
WebAppContext wac = new WebAppContext();
ServletHolder servlet = new ServletHolder();
servlet.setClassName("org.eclipse.jetty.ee10.servlet.FooServlet");
servlet.setName("foo");
wac.getServletHandler().addServlet(servlet);
ServletMapping m = new ServletMapping();
m.setPathSpec("/");
m.setServletName("foo");
wac.getServletHandler().addServletMapping(m);
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee10.annotations.ServletD", null);
annotation.apply();
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
assertEquals(2, resultMappings.length);
for (ServletMapping r : resultMappings)
{
if (r.getServletName().equals("DServlet"))
{
assertEquals(2, r.getPathSpecs().length);
}
else if (r.getServletName().equals("foo"))
{
assertEquals(1, r.getPathSpecs().length);
}
else
fail("Unexpected servlet name: " + r);
}
}
@Test
public void testWebServletAnnotationIgnore() throws Exception
{
//an existing servlet OF THE SAME NAME has even 1 non-default mapping we can't use
//any of the url mappings in the annotation
WebAppContext wac = new WebAppContext();
ServletHolder servlet = new ServletHolder();
servlet.setClassName("org.eclipse.jetty.ee10.servlet.OtherDServlet");
servlet.setName("DServlet");
wac.getServletHandler().addServlet(servlet);
ServletMapping m = new ServletMapping();
m.setPathSpec("/default");
m.setFromDefaultDescriptor(true);
m.setServletName("DServlet");
wac.getServletHandler().addServletMapping(m);
ServletMapping m2 = new ServletMapping();
m2.setPathSpec("/other");
m2.setServletName("DServlet");
wac.getServletHandler().addServletMapping(m2);
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee10.annotations.ServletD", null);
annotation.apply();
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
assertEquals(2, resultMappings.length);
for (ServletMapping r : resultMappings)
{
assertEquals(1, r.getPathSpecs().length);
if (!r.getPathSpecs()[0].equals("/default") && !r.getPathSpecs()[0].equals("/other"))
fail("Unexpected path in mapping: " + r);
}
}
@Test
public void testWebServletAnnotationNoMappings() throws Exception
{
//an existing servlet OF THE SAME NAME has no mappings, therefore all mappings in the annotation
//should be accepted
WebAppContext wac = new WebAppContext();
ServletHolder servlet = new ServletHolder();
servlet.setName("foo");
wac.getServletHandler().addServlet(servlet);
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee10.annotations.ServletD", null);
annotation.apply();
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
assertEquals(1, resultMappings.length);
assertEquals(2, resultMappings[0].getPathSpecs().length);
for (String s : resultMappings[0].getPathSpecs())
{
assertThat(s, anyOf(is("/"), is("/bah/*")));
}
}
@Test
public void testDeclareRoles()
throws Exception
{
WebAppContext wac = new WebAppContext();
ConstraintSecurityHandler sh = new ConstraintSecurityHandler();
wac.setSecurityHandler(sh);
sh.setRoles(new HashSet<String>(Arrays.asList(new String[]{"humpty", "dumpty"})));
DeclareRolesAnnotationHandler handler = new DeclareRolesAnnotationHandler(wac);
handler.doHandle(ServletC.class);
assertThat(sh.getRoles(), containsInAnyOrder("humpty", "alice", "dumpty"));
}
}

View File

@ -0,0 +1,115 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations.resources;
import java.io.IOException;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
/**
* ResourceA
*/
public class ResourceA implements jakarta.servlet.Servlet
{
private Integer e;
private Integer h;
private Integer k;
@Resource(name = "myf", mappedName = "resB") //test giving both a name and mapped name from the environment
private Integer f; //test a non inherited field that needs injection
@Resource(mappedName = "resA") //test the default naming scheme but using a mapped name from the environment
private Integer g;
@Resource(name = "resA") //test using the given name as the name from the environment
private Integer j;
@Resource(mappedName = "resB") //test using the default name on an inherited field
protected Integer n; //TODO - if it's inherited, is it supposed to use the classname of the class it is inherited by?
@Resource(name = "mye", mappedName = "resA", type = Integer.class)
public void setE(Integer e)
{
this.e = e;
}
public Integer getE()
{
return this.e;
}
public Integer getF()
{
return this.f;
}
public Integer getG()
{
return this.g;
}
public Integer getJ()
{
return this.j;
}
@Resource(mappedName = "resA")
public void setH(Integer h)
{
this.h = h;
}
@Resource(name = "resA")
public void setK(Integer k)
{
this.k = k;
}
public void x()
{
System.err.println("ResourceA.x");
}
@Override
public void destroy()
{
}
@Override
public ServletConfig getServletConfig()
{
return null;
}
@Override
public String getServletInfo()
{
return null;
}
@Override
public void init(ServletConfig arg0) throws ServletException
{
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
}
}

View File

@ -0,0 +1,39 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations.resources;
import jakarta.annotation.Resource;
import jakarta.annotation.Resources;
/**
* ResourceB
*/
@Resources({
@Resource(name = "peach", mappedName = "resA"),
@Resource(name = "pear", mappedName = "resB")
})
public class ResourceB extends ResourceA
{
@Resource(mappedName = "resB")
private Integer f; //test no inheritance of private fields
@Resource
private Integer p = 8; //test no injection because no value
//test no annotation
public void z()
{
System.err.println("ResourceB.z");
}
}

View File

@ -0,0 +1,168 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.annotations.resources;
import java.lang.reflect.Field;
import java.util.Set;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector;
import org.eclipse.jetty.ee10.annotations.ResourceAnnotationHandler;
import org.eclipse.jetty.ee10.annotations.ResourcesAnnotationHandler;
import org.eclipse.jetty.ee10.plus.annotation.Injection;
import org.eclipse.jetty.ee10.plus.annotation.InjectionCollection;
import org.eclipse.jetty.ee10.plus.jndi.EnvEntry;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.server.Server;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class TestResourceAnnotations
{
private Server server;
private WebAppContext wac;
private InjectionCollection injections;
private Context comp;
private Context env;
private Object objA = 1000;
private Object objB = 2000;
@BeforeEach
public void init() throws Exception
{
server = new Server();
wac = new WebAppContext();
wac.setServer(server);
injections = new InjectionCollection();
wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
InitialContext ic = new InitialContext();
comp = (Context)ic.lookup("java:comp");
env = comp.createSubcontext("env");
}
@AfterEach
public void destroy() throws Exception
{
comp.destroySubcontext("env");
}
@Test
public void testResourceAnnotations()
throws Exception
{
new EnvEntry(server, "resA", objA, false);
new EnvEntry(server, "resB", objB, false);
AnnotationIntrospector parser = new AnnotationIntrospector(wac);
ResourceAnnotationHandler handler = new ResourceAnnotationHandler(wac);
parser.registerHandler(handler);
ResourceA resourceA = new ResourceA();
ResourceB resourceB = new ResourceB();
parser.introspect(resourceA, null);
parser.introspect(resourceB, null);
//processing classA should give us these jndi name bindings:
// java:comp/env/myf
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/g
// java:comp/env/mye
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/h
// java:comp/env/resA
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceB/f
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/n
//
assertEquals(objB, env.lookup("myf"));
assertEquals(objA, env.lookup("mye"));
assertEquals(objA, env.lookup("resA"));
assertEquals(objA, env.lookup("org.eclipse.jetty.ee10.annotations.resources.ResourceA/g"));
assertEquals(objA, env.lookup("org.eclipse.jetty.ee10.annotations.resources.ResourceA/h"));
assertEquals(objB, env.lookup("org.eclipse.jetty.ee10.annotations.resources.ResourceB/f"));
assertEquals(objB, env.lookup("org.eclipse.jetty.ee10.annotations.resources.ResourceA/n"));
//we should have Injections
assertNotNull(injections);
Set<Injection> resBInjections = injections.getInjections(ResourceB.class.getName());
assertNotNull(resBInjections);
//only 1 field injection because the other has no Resource mapping
assertEquals(1, resBInjections.size());
Injection fi = resBInjections.iterator().next();
assertEquals("f", fi.getTarget().getName());
//3 method injections on class ResourceA, 4 field injections
Set<Injection> resAInjections = injections.getInjections(ResourceA.class.getName());
assertNotNull(resAInjections);
assertEquals(7, resAInjections.size());
int fieldCount = 0;
int methodCount = 0;
for (Injection x : resAInjections)
{
if (x.isField())
fieldCount++;
else
methodCount++;
}
assertEquals(4, fieldCount);
assertEquals(3, methodCount);
//test injection
ResourceB binst = new ResourceB();
injections.inject(binst);
//check injected values
Field f = ResourceB.class.getDeclaredField("f");
f.setAccessible(true);
assertEquals(objB, f.get(binst));
//@Resource(mappedName="resA") //test the default naming scheme but using a mapped name from the environment
f = ResourceA.class.getDeclaredField("g");
f.setAccessible(true);
assertEquals(objA, f.get(binst));
//@Resource(name="resA") //test using the given name as the name from the environment
f = ResourceA.class.getDeclaredField("j");
f.setAccessible(true);
assertEquals(objA, f.get(binst));
//@Resource(mappedName="resB") //test using the default name on an inherited field
f = ResourceA.class.getDeclaredField("n");
f.setAccessible(true);
assertEquals(objB, f.get(binst));
}
@Test
public void testResourcesAnnotation()
throws Exception
{
new EnvEntry(server, "resA", objA, false);
new EnvEntry(server, "resB", objB, false);
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
ResourcesAnnotationHandler handler = new ResourcesAnnotationHandler(wac);
introspector.registerHandler(handler);
ResourceA resourceA = new ResourceA();
ResourceB resourceB = new ResourceB();
introspector.introspect(resourceA, null);
introspector.introspect(resourceB, null);
assertEquals(objA, env.lookup("peach"));
assertEquals(objB, env.lookup("pear"));
}
}

View File

@ -0,0 +1,3 @@
# Jetty Logging using jetty-slf4j-impl
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.annotations.LEVEL=DEBUG

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment 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-fragment_4_0.xsd" version="4.0">
<name>ardvaark</name>
</web-fragment>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment 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-fragment_4_0.xsd" metadata-complete="true" version="4.0">
<name>badger</name>
</web-fragment>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Test 2.5 WebApp</display-name>
</web-app>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<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"
metadata-complete="false"
version="3.1">
<display-name>Test 31 WebApp</display-name>
</web-app>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<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"
metadata-complete="true"
version="3.1">
<display-name>Test 31 WebApp</display-name>
</web-app>

View File

@ -0,0 +1,96 @@
<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.ee10</groupId>
<artifactId>jetty-ee10</artifactId>
<version>12.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ee10-ant</artifactId>
<packaging>jar</packaging>
<name>EE10 :: Jetty :: Ant Plugin</name>
<properties>
<bundle-symbolic-name>${project.groupId}.ant</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Require-Capability>
osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"
</Require-Capability>
<Provide-Capability>
osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.ee10.webapp.Configuration
</Provide-Capability>
</instructions>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-lib-deps</id>
<phase>process-test-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.websocket</excludeGroupIds>
<excludeArtifactIds>jetty-start</excludeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${project.build.directory}/test-lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-plus</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,81 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.apache.tools.ant.AntClassLoader;
import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
public class AntMetaInfConfiguration extends MetaInfConfiguration
{
@Override
public Class<? extends Configuration> replaces()
{
return MetaInfConfiguration.class;
}
@Override
public void findAndFilterContainerPaths(WebAppContext context) throws Exception
{
// TODO Auto-generated method stub
super.findAndFilterContainerPaths(context);
}
@Override
protected List<URI> getAllContainerJars(final WebAppContext context) throws URISyntaxException
{
List<URI> uris = new ArrayList<>();
if (context.getClassLoader() != null)
{
ClassLoader loader = context.getClassLoader().getParent();
while (loader != null)
{
if (loader instanceof URLClassLoader)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
for (URL url : urls)
{
uris.add(new URI(url.toString().replaceAll(" ", "%20")));
}
}
else if (loader instanceof AntClassLoader)
{
AntClassLoader antLoader = (AntClassLoader)loader;
String[] paths = antLoader.getClasspath().split(new String(new char[]{File.pathSeparatorChar}));
if (paths != null)
{
for (String p : paths)
{
File f = new File(p);
uris.add(f.toURI());
}
}
}
loader = loader.getParent();
}
}
return uris;
}
}

View File

@ -0,0 +1,691 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.jar.Manifest;
import jakarta.servlet.Servlet;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;
import org.eclipse.jetty.ee10.ant.types.Attribute;
import org.eclipse.jetty.ee10.ant.types.Attributes;
import org.eclipse.jetty.ee10.ant.types.FileMatchingConfiguration;
import org.eclipse.jetty.ee10.ant.utils.TaskLog;
import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.ee10.servlet.FilterHolder;
import org.eclipse.jetty.ee10.servlet.FilterMapping;
import org.eclipse.jetty.ee10.servlet.ServletHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.Source;
import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration;
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Extension of WebAppContext to allow configuration via Ant environment.
*/
public class AntWebAppContext extends WebAppContext
{
private static final Logger LOG = LoggerFactory.getLogger(WebAppContext.class);
public static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN =
".*/.*jsp-api-[^/]*\\.jar$|.*/.*jsp-[^/]*\\.jar$|.*/.*taglibs[^/]*\\.jar$|.*/.*jstl[^/]*\\.jar$|.*/.*jsf-impl-[^/]*\\.jar$|.*/.*javax.faces-[^/]*\\.jar$|.*/.*myfaces-impl-[^/]*\\.jar$";
/**
* Location of jetty-env.xml file.
*/
private File jettyEnvXml;
/**
* List of web application libraries.
*/
private List<FileSet> libraries = new ArrayList<>();
/**
* List of web application class directories.
*/
private List<FileSet> classes = new ArrayList<>();
/**
* context xml file to apply to the webapp
*/
private File contextXml;
/**
* List of extra scan targets for this web application.
*/
private FileSet scanTargets;
/**
* context attributes to set
**/
private Attributes attributes;
private Project project;
private List<File> scanFiles;
private FileMatchingConfiguration librariesConfiguration;
public static void dump(ClassLoader loader)
{
while (loader != null)
{
System.err.println(loader);
if (loader instanceof URLClassLoader)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
for (URL u : urls)
{
System.err.println("\t" + u + "\n");
}
}
}
loader = loader.getParent();
}
}
/**
* AntURLClassLoader
*
* Adapt the AntClassLoader which is not a URLClassLoader - this is needed for
* jsp to be able to search the classpath.
*/
public static class AntURLClassLoader extends URLClassLoader
{
private AntClassLoader antLoader;
public AntURLClassLoader(AntClassLoader antLoader)
{
super(new URL[]{}, antLoader);
this.antLoader = antLoader;
}
@Override
public InputStream getResourceAsStream(String name)
{
return super.getResourceAsStream(name);
}
@Override
public void close() throws IOException
{
super.close();
}
@Override
protected void addURL(URL url)
{
super.addURL(url);
}
@Override
public URL[] getURLs()
{
Set<URL> urls = new HashSet<URL>();
//convert urls from antLoader
String[] paths = antLoader.getClasspath().split(new String(new char[]{File.pathSeparatorChar}));
if (paths != null)
{
for (String p : paths)
{
File f = new File(p);
try
{
urls.add(f.toURI().toURL());
}
catch (Exception e)
{
LOG.trace("IGNORED", e);
}
}
}
//add in any that may have been added to us as a URL directly
URL[] ourURLS = super.getURLs();
if (ourURLS != null)
{
for (URL u : ourURLS)
{
urls.add(u);
}
}
return urls.toArray(new URL[urls.size()]);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
return super.findClass(name);
}
@Override
public URL findResource(String name)
{
return super.findResource(name);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException
{
return super.findResources(name);
}
@Override
protected PermissionCollection getPermissions(CodeSource codesource)
{
return super.getPermissions(codesource);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
return super.loadClass(name);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
return super.loadClass(name, resolve);
}
@Override
protected Object getClassLoadingLock(String className)
{
return super.getClassLoadingLock(className);
}
@Override
public URL getResource(String name)
{
return super.getResource(name);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
return super.getResources(name);
}
@Override
protected Package definePackage(String name, Manifest man, URL url) throws IllegalArgumentException
{
return super.definePackage(name, man, url);
}
@Override
protected Package definePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion,
String implVendor, URL sealBase) throws IllegalArgumentException
{
return super.definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
}
@Override
protected Package[] getPackages()
{
return super.getPackages();
}
@Override
protected String findLibrary(String libname)
{
return super.findLibrary(libname);
}
@Override
public void setDefaultAssertionStatus(boolean enabled)
{
super.setDefaultAssertionStatus(enabled);
}
@Override
public void setPackageAssertionStatus(String packageName, boolean enabled)
{
super.setPackageAssertionStatus(packageName, enabled);
}
@Override
public void setClassAssertionStatus(String className, boolean enabled)
{
super.setClassAssertionStatus(className, enabled);
}
@Override
public void clearAssertionStatus()
{
super.clearAssertionStatus();
}
}
/**
* AntServletHolder
*/
public static class AntServletHolder extends ServletHolder
{
public AntServletHolder()
{
super();
}
public AntServletHolder(Class<? extends Servlet> servlet)
{
super(servlet);
}
public AntServletHolder(Servlet servlet)
{
super(servlet);
}
public AntServletHolder(String name, Class<? extends Servlet> servlet)
{
super(name, servlet);
}
public AntServletHolder(String name, Servlet servlet)
{
super(name, servlet);
}
protected String getSystemClassPath(ClassLoader loader) throws Exception
{
StringBuilder classpath = new StringBuilder();
while (loader != null)
{
if (loader instanceof URLClassLoader)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
for (int i = 0; i < urls.length; i++)
{
Resource resource = Resource.newResource(urls[i]);
File file = resource.getFile();
if (file != null && file.exists())
{
if (classpath.length() > 0)
classpath.append(File.pathSeparatorChar);
classpath.append(file.getAbsolutePath());
}
}
}
}
else if (loader instanceof AntClassLoader)
{
classpath.append(((AntClassLoader)loader).getClasspath());
}
loader = loader.getParent();
}
return classpath.toString();
}
}
/**
* AntServletHandler
*/
public static class AntServletHandler extends ServletHandler
{
@Override
public ServletHolder newServletHolder(Source source)
{
return new AntServletHolder();
}
}
/**
* Default constructor. Takes project as an argument
*
* @param project the project.
* @throws Exception if unable to create webapp context
*/
public AntWebAppContext(Project project) throws Exception
{
super();
this.project = project;
setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN);
setParentLoaderPriority(true);
addConfiguration(new AntWebInfConfiguration(), new AntWebXmlConfiguration(), new AntMetaInfConfiguration());
}
/**
* Adds a new Ant's attributes tag object if it have not been created yet.
*
* @param atts the attributes
*/
public void addAttributes(Attributes atts)
{
if (this.attributes != null)
{
throw new BuildException("Only one <attributes> tag is allowed!");
}
this.attributes = atts;
}
public void addLib(FileSet lib)
{
libraries.add(lib);
}
public void addClasses(FileSet classes)
{
this.classes.add(classes);
}
@Override
protected ServletHandler newServletHandler()
{
return new AntServletHandler();
}
public void setJettyEnvXml(File jettyEnvXml)
{
this.jettyEnvXml = jettyEnvXml;
TaskLog.log("jetty-env.xml file: = " + (jettyEnvXml == null ? null : jettyEnvXml.getAbsolutePath()));
}
public File getJettyEnvXml()
{
return this.jettyEnvXml;
}
public List<File> getLibraries()
{
return librariesConfiguration.getBaseDirectories();
}
public void addScanTargets(FileSet scanTargets)
{
if (this.scanTargets != null)
{
throw new BuildException("Only one <scanTargets> tag is allowed!");
}
this.scanTargets = scanTargets;
}
public List<File> getScanTargetFiles()
{
if (this.scanTargets == null)
return null;
FileMatchingConfiguration configuration = new FileMatchingConfiguration();
configuration.addDirectoryScanner(scanTargets.getDirectoryScanner(project));
return configuration.getBaseDirectories();
}
public List<File> getScanFiles()
{
if (scanFiles == null)
scanFiles = initScanFiles();
return scanFiles;
}
public boolean isScanned(File file)
{
List<File> files = getScanFiles();
if (files == null || files.isEmpty())
return false;
return files.contains(file);
}
public List<File> initScanFiles()
{
List<File> scanList = new ArrayList<File>();
if (getDescriptor() != null)
{
try (Resource r = Resource.newResource(getDescriptor());)
{
scanList.add(r.getFile());
}
catch (IOException e)
{
throw new BuildException(e);
}
}
if (getJettyEnvXml() != null)
{
try (Resource r = Resource.newResource(getJettyEnvXml());)
{
scanList.add(r.getFile());
}
catch (IOException e)
{
throw new BuildException("Problem configuring scanner for jetty-env.xml", e);
}
}
if (getDefaultsDescriptor() != null)
{
try (Resource r = Resource.newResource(getDefaultsDescriptor());)
{
if (!WebAppContext.WEB_DEFAULTS_XML.equals(getDefaultsDescriptor()))
{
scanList.add(r.getFile());
}
}
catch (IOException e)
{
throw new BuildException("Problem configuring scanner for webdefaults.xml", e);
}
}
if (getOverrideDescriptor() != null)
{
try
{
Resource r = Resource.newResource(getOverrideDescriptor());
scanList.add(r.getFile());
}
catch (IOException e)
{
throw new BuildException("Problem configuring scanner for webdefaults.xml", e);
}
}
//add any extra classpath and libs
List<File> cpFiles = getClassPathFiles();
if (cpFiles != null)
scanList.addAll(cpFiles);
//any extra scan targets
List<File> scanFiles = (List<File>)getScanTargetFiles();
if (scanFiles != null)
scanList.addAll(scanFiles);
return scanList;
}
@Override
public void setWar(String path)
{
super.setWar(path);
try
{
Resource war = Resource.newResource(path);
if (war.exists() && war.isDirectory() && getDescriptor() == null)
{
Resource webXml = war.addPath("WEB-INF/web.xml");
setDescriptor(webXml.toString());
}
}
catch (IOException e)
{
throw new BuildException(e);
}
}
@Override
public void doStart()
{
try
{
TaskLog.logWithTimestamp("Starting web application " + this.getDescriptor());
if (jettyEnvXml != null && jettyEnvXml.exists())
getConfiguration(EnvConfiguration.class).setJettyEnvResource(new PathResource(jettyEnvXml));
ClassLoader parentLoader = this.getClass().getClassLoader();
if (parentLoader instanceof AntClassLoader)
parentLoader = new AntURLClassLoader((AntClassLoader)parentLoader);
setClassLoader(new WebAppClassLoader(parentLoader, this));
if (attributes != null && attributes.getAttributes() != null)
{
for (Attribute a : attributes.getAttributes())
{
setAttribute(a.getName(), a.getValue());
}
}
//apply a context xml file if one was supplied
if (contextXml != null)
{
XmlConfiguration xmlConfiguration = new XmlConfiguration(new PathResource(contextXml));
TaskLog.log("Applying context xml file " + contextXml);
xmlConfiguration.configure(this);
}
super.doStart();
}
catch (Exception e)
{
TaskLog.log(e.toString());
}
}
@Override
public void doStop()
{
try
{
scanFiles = null;
TaskLog.logWithTimestamp("Stopping web application " + this);
Thread.currentThread().sleep(500L);
super.doStop();
// remove all filters and servlets. They will be recreated
// either via application of a context xml file or web.xml or annotation or servlet api.
// Event listeners are reset in ContextHandler.doStop()
getServletHandler().setFilters(new FilterHolder[0]);
getServletHandler().setFilterMappings(new FilterMapping[0]);
getServletHandler().setServlets(new ServletHolder[0]);
getServletHandler().setServletMappings(new ServletMapping[0]);
}
catch (InterruptedException e)
{
TaskLog.log(e.toString());
}
catch (Exception e)
{
TaskLog.log(e.toString());
}
}
/**
* @return a list of classpath files (libraries and class directories).
*/
public List<File> getClassPathFiles()
{
List<File> classPathFiles = new ArrayList<File>();
Iterator<FileSet> classesIterator = classes.iterator();
while (classesIterator.hasNext())
{
FileSet clazz = classesIterator.next();
classPathFiles.add(clazz.getDirectoryScanner(project).getBasedir());
}
Iterator<FileSet> iterator = libraries.iterator();
while (iterator.hasNext())
{
FileSet library = iterator.next();
String[] includedFiles = library.getDirectoryScanner(project).getIncludedFiles();
File baseDir = library.getDirectoryScanner(project).getBasedir();
for (int i = 0; i < includedFiles.length; i++)
{
classPathFiles.add(new File(baseDir, includedFiles[i]));
}
}
return classPathFiles;
}
/**
* @return a <code>FileMatchingConfiguration</code> object describing the
* configuration of all libraries added to this particular web app
* (both classes and libraries).
*/
public FileMatchingConfiguration getLibrariesConfiguration()
{
FileMatchingConfiguration config = new FileMatchingConfiguration();
Iterator<FileSet> classesIterator = classes.iterator();
while (classesIterator.hasNext())
{
FileSet clazz = classesIterator.next();
config.addDirectoryScanner(clazz.getDirectoryScanner(project));
}
Iterator<FileSet> librariesIterator = libraries.iterator();
while (librariesIterator.hasNext())
{
FileSet library = librariesIterator.next();
config.addDirectoryScanner(library.getDirectoryScanner(project));
}
return config;
}
public File getContextXml()
{
return contextXml;
}
public void setContextXml(File contextXml)
{
this.contextXml = contextXml;
}
}

View File

@ -0,0 +1,59 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.io.File;
import java.util.List;
import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebInfConfiguration;
import org.eclipse.jetty.ee10.webapp.WebXmlConfiguration;
public class AntWebInfConfiguration extends WebInfConfiguration
{
@Override
public Class<? extends Configuration> replaces()
{
return WebInfConfiguration.class;
}
/**
* Adds classpath files into web application classloader, and
* sets web.xml and base directory for the configured web application.
*
* @see WebXmlConfiguration#configure(WebAppContext)
*/
@Override
public void configure(WebAppContext context) throws Exception
{
if (context instanceof AntWebAppContext)
{
List<File> classPathFiles = ((AntWebAppContext)context).getClassPathFiles();
if (classPathFiles != null)
{
for (File cpFile : classPathFiles)
{
if (cpFile.exists())
{
((WebAppClassLoader)context.getClassLoader()).addClassPath(cpFile.getCanonicalPath());
}
}
}
}
super.configure(context);
}
}

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.io.File;
import java.util.List;
import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.WebXmlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This configuration object provides additional way to inject application
* properties into the configured web application. The list of classpath files,
* the application base directory and web.xml file could be specified in this
* way.
*/
public class AntWebXmlConfiguration extends WebXmlConfiguration
{
private static final Logger LOG = LoggerFactory.getLogger(WebXmlConfiguration.class);
/**
* List of classpath files.
*/
private List classPathFiles;
/**
* Web application root directory.
*/
private File webAppBaseDir;
public AntWebXmlConfiguration()
{
super();
}
@Override
public Class<? extends Configuration> replaces()
{
return WebXmlConfiguration.class;
}
public void setClassPathFiles(List classPathFiles)
{
this.classPathFiles = classPathFiles;
}
public void setWebAppBaseDir(File webAppBaseDir)
{
this.webAppBaseDir = webAppBaseDir;
}
}

View File

@ -0,0 +1,310 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Property;
import org.eclipse.jetty.ee10.ant.types.Connector;
import org.eclipse.jetty.ee10.ant.types.Connectors;
import org.eclipse.jetty.ee10.ant.types.ContextHandlers;
import org.eclipse.jetty.ee10.ant.types.LoginServices;
import org.eclipse.jetty.ee10.ant.types.SystemProperties;
import org.eclipse.jetty.ee10.ant.utils.TaskLog;
import org.eclipse.jetty.ee10.servlet.security.LoginService;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.server.RequestLog;
/**
* Ant task for running a Jetty server.
*/
public class JettyRunTask extends Task
{
private int scanIntervalSeconds;
/**
* Temporary files directory.
*/
private File tempDirectory;
/**
* List of web applications to be deployed.
*/
private List<AntWebAppContext> webapps = new ArrayList<>();
/**
* Location of jetty.xml file.
*/
private File jettyXml;
/**
* List of server connectors.
*/
private Connectors connectors = null;
/**
* Server request logger object.
*/
private RequestLog requestLog;
/**
* List of login services.
*/
private LoginServices loginServices;
/**
* List of system properties to be set.
*/
private SystemProperties systemProperties;
/**
* List of other contexts to deploy
*/
private ContextHandlers contextHandlers;
/**
* Port Jetty will use for the default connector
*/
private int jettyPort = 8080;
private int stopPort;
private String stopKey;
private boolean daemon;
public JettyRunTask()
{
TaskLog.setTask(this);
}
/**
* Creates a new <code>WebApp</code> Ant object.
*
* @param webapp the webapp context
*/
public void addWebApp(AntWebAppContext webapp)
{
webapps.add(webapp);
}
/**
* Adds a new Ant's connector tag object if it have not been created yet.
*
* @param connectors the connectors
*/
public void addConnectors(Connectors connectors)
{
if (this.connectors != null)
throw new BuildException("Only one <connectors> tag is allowed!");
this.connectors = connectors;
}
public void addLoginServices(LoginServices services)
{
if (this.loginServices != null)
throw new BuildException("Only one <loginServices> tag is allowed!");
this.loginServices = services;
}
public void addSystemProperties(SystemProperties systemProperties)
{
if (this.systemProperties != null)
throw new BuildException("Only one <systemProperties> tag is allowed!");
this.systemProperties = systemProperties;
}
public void addContextHandlers(ContextHandlers handlers)
{
if (this.contextHandlers != null)
throw new BuildException("Only one <contextHandlers> tag is allowed!");
this.contextHandlers = handlers;
}
public File getTempDirectory()
{
return tempDirectory;
}
public void setTempDirectory(File tempDirectory)
{
this.tempDirectory = tempDirectory;
}
public File getJettyXml()
{
return jettyXml;
}
public void setJettyXml(File jettyXml)
{
this.jettyXml = jettyXml;
}
public void setRequestLog(String className)
{
try
{
this.requestLog = (RequestLog)Class.forName(className).getDeclaredConstructor().newInstance();
}
catch (ClassNotFoundException e)
{
throw new BuildException("Unknown request logger class: " + className);
}
catch (Exception e)
{
throw new BuildException("Request logger instantiation exception: " + e);
}
}
public String getRequestLog()
{
if (requestLog != null)
{
return requestLog.getClass().getName();
}
return "";
}
/**
* Sets the port Jetty uses for the default connector.
*
* @param jettyPort The port Jetty will use for the default connector
*/
public void setJettyPort(final int jettyPort)
{
this.jettyPort = jettyPort;
}
/**
* Executes this Ant task. The build flow is being stopped until Jetty
* server stops.
*
* @throws BuildException if unable to build
*/
@Override
public void execute() throws BuildException
{
TaskLog.log("Configuring Jetty for project: " + getProject().getName());
setSystemProperties();
List<Connector> connectorsList = null;
if (connectors != null)
connectorsList = connectors.getConnectors();
else
connectorsList = new Connectors(jettyPort, 30000).getDefaultConnectors();
List<LoginService> loginServicesList = (loginServices != null ? loginServices.getLoginServices() : new ArrayList<LoginService>());
ServerProxyImpl server = new ServerProxyImpl();
server.setConnectors(connectorsList);
server.setLoginServices(loginServicesList);
server.setRequestLog(requestLog);
server.setJettyXml(jettyXml);
server.setDaemon(daemon);
server.setStopPort(stopPort);
server.setStopKey(stopKey);
server.setContextHandlers(contextHandlers);
server.setTempDirectory(tempDirectory);
server.setScanIntervalSecs(scanIntervalSeconds);
try
{
for (WebAppContext webapp : webapps)
{
server.addWebApplication((AntWebAppContext)webapp);
}
}
catch (Exception e)
{
throw new BuildException(e);
}
server.start();
}
public int getStopPort()
{
return stopPort;
}
public void setStopPort(int stopPort)
{
this.stopPort = stopPort;
TaskLog.log("stopPort=" + stopPort);
}
public String getStopKey()
{
return stopKey;
}
public void setStopKey(String stopKey)
{
this.stopKey = stopKey;
TaskLog.log("stopKey=" + stopKey);
}
/**
* @return the daemon
*/
public boolean isDaemon()
{
return daemon;
}
/**
* @param daemon the daemon to set
*/
public void setDaemon(boolean daemon)
{
this.daemon = daemon;
TaskLog.log("Daemon=" + daemon);
}
public int getScanIntervalSeconds()
{
return scanIntervalSeconds;
}
public void setScanIntervalSeconds(int secs)
{
scanIntervalSeconds = secs;
TaskLog.log("scanIntervalSecs=" + secs);
}
/**
* Sets the system properties.
*/
private void setSystemProperties()
{
if (systemProperties != null)
{
Iterator propertiesIterator = systemProperties.getSystemProperties().iterator();
while (propertiesIterator.hasNext())
{
Property property = ((Property)propertiesIterator.next());
SystemProperties.setIfNotSetAlready(property);
}
}
}
}

View File

@ -0,0 +1,114 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.Socket;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.eclipse.jetty.ee10.ant.utils.TaskLog;
/**
* JettyStopTask
*/
public class JettyStopTask extends Task
{
private int stopPort;
private String stopKey;
private int stopWait;
/**
*
*/
public JettyStopTask()
{
TaskLog.setTask(this);
}
@Override
public void execute() throws BuildException
{
try
{
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), stopPort);
if (stopWait > 0)
s.setSoTimeout(stopWait * 1000);
try
{
OutputStream out = s.getOutputStream();
out.write((stopKey + "\r\nstop\r\n").getBytes());
out.flush();
if (stopWait > 0)
{
TaskLog.log("Waiting" + (stopWait > 0 ? (" " + stopWait + "sec") : "") + " for jetty to stop");
LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
String response = lin.readLine();
if ("Stopped".equals(response))
System.err.println("Stopped");
}
}
finally
{
s.close();
}
}
catch (ConnectException e)
{
TaskLog.log("Jetty not running!");
}
catch (Exception e)
{
TaskLog.log(e.getMessage());
}
}
public int getStopPort()
{
return stopPort;
}
public void setStopPort(int stopPort)
{
this.stopPort = stopPort;
}
public String getStopKey()
{
return stopKey;
}
public void setStopKey(String stopKey)
{
this.stopKey = stopKey;
}
public int getStopWait()
{
return stopWait;
}
public void setStopWait(int stopWait)
{
this.stopWait = stopWait;
}
}

View File

@ -0,0 +1,491 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.ee10.ant.types.Connector;
import org.eclipse.jetty.ee10.ant.types.ContextHandlers;
import org.eclipse.jetty.ee10.ant.utils.ServerProxy;
import org.eclipse.jetty.ee10.ant.utils.TaskLog;
import org.eclipse.jetty.ee10.servlet.security.LoginService;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.ShutdownMonitor;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.xml.sax.SAXException;
/**
* A proxy class for interaction with Jetty server object. Used to have some
* level of abstraction over standard Jetty classes.
*/
public class ServerProxyImpl implements ServerProxy
{
/**
* Proxied Jetty server object.
*/
private Server server;
/**
* Temporary files directory.
*/
private File tempDirectory;
/**
* Collection of context handlers (web application contexts).
*/
private ContextHandlerCollection contexts;
/**
* Location of jetty.xml file.
*/
private File jettyXml;
/**
* List of connectors.
*/
private List<Connector> connectors;
/**
* Request logger.
*/
private RequestLog requestLog;
/**
* User realms.
*/
private List<LoginService> loginServices;
/**
* List of added web applications.
*/
private List<AntWebAppContext> webApplications = new ArrayList<AntWebAppContext>();
/**
* other contexts to deploy
*/
private ContextHandlers contextHandlers;
/**
* scan interval for changed files
*/
private int scanIntervalSecs;
/**
* port to listen for stop command
*/
private int stopPort;
/**
* security key for stop command
*/
private String stopKey;
/**
* wait for all jetty threads to exit or continue
*/
private boolean daemon;
private boolean configured = false;
/**
* WebAppScannerListener
*
* Handle notifications that files we are interested in have changed
* during execution.
*/
public static class WebAppScannerListener implements Scanner.BulkListener
{
AntWebAppContext awc;
public WebAppScannerListener(AntWebAppContext awc)
{
this.awc = awc;
}
@Override
public void filesChanged(Set<String> changedFileNames)
{
boolean isScanned = false;
try
{
Iterator<String> itor = changedFileNames.iterator();
while (!isScanned && itor.hasNext())
{
isScanned = awc.isScanned(Resource.newResource(itor.next()).getFile());
}
if (isScanned)
{
awc.stop();
awc.start();
}
}
catch (Exception e)
{
TaskLog.log(e.getMessage());
}
}
}
/**
* Default constructor. Creates a new Jetty server with a standard connector
* listening on a given port.
*/
public ServerProxyImpl()
{
server = new Server();
server.setStopAtShutdown(true);
}
@Override
public void addWebApplication(AntWebAppContext webApp)
{
webApplications.add(webApp);
}
public int getStopPort()
{
return stopPort;
}
public void setStopPort(int stopPort)
{
this.stopPort = stopPort;
}
public String getStopKey()
{
return stopKey;
}
public void setStopKey(String stopKey)
{
this.stopKey = stopKey;
}
public File getJettyXml()
{
return jettyXml;
}
public void setJettyXml(File jettyXml)
{
this.jettyXml = jettyXml;
}
public List<Connector> getConnectors()
{
return connectors;
}
public void setConnectors(List<Connector> connectors)
{
this.connectors = connectors;
}
public RequestLog getRequestLog()
{
return requestLog;
}
public void setRequestLog(RequestLog requestLog)
{
this.requestLog = requestLog;
}
public List<LoginService> getLoginServices()
{
return loginServices;
}
public void setLoginServices(List<LoginService> loginServices)
{
this.loginServices = loginServices;
}
public List<AntWebAppContext> getWebApplications()
{
return webApplications;
}
public void setWebApplications(List<AntWebAppContext> webApplications)
{
this.webApplications = webApplications;
}
public File getTempDirectory()
{
return tempDirectory;
}
public void setTempDirectory(File tempDirectory)
{
this.tempDirectory = tempDirectory;
}
@Override
public void start()
{
try
{
configure();
configureWebApps();
server.start();
System.setProperty("jetty.ant.server.port", "" + ((ServerConnector)server.getConnectors()[0]).getLocalPort());
String host = ((ServerConnector)server.getConnectors()[0]).getHost();
if (host == null)
{
System.setProperty("jetty.ant.server.host", "localhost");
}
else
{
System.setProperty("jetty.ant.server.host", host);
}
startScanners();
TaskLog.log("Jetty AntTask Started");
if (!daemon)
server.join();
}
catch (InterruptedException e)
{
new RuntimeException(e);
}
catch (Exception e)
{
e.printStackTrace();
new RuntimeException(e);
}
}
@Override
public Object getProxiedObject()
{
return server;
}
/**
* @return the daemon
*/
public boolean isDaemon()
{
return daemon;
}
/**
* @param daemon the daemon to set
*/
public void setDaemon(boolean daemon)
{
this.daemon = daemon;
}
/**
* @return the contextHandlers
*/
public ContextHandlers getContextHandlers()
{
return contextHandlers;
}
/**
* @param contextHandlers the contextHandlers to set
*/
public void setContextHandlers(ContextHandlers contextHandlers)
{
this.contextHandlers = contextHandlers;
}
public int getScanIntervalSecs()
{
return scanIntervalSecs;
}
public void setScanIntervalSecs(int scanIntervalSecs)
{
this.scanIntervalSecs = scanIntervalSecs;
}
/**
* Configures Jetty server before adding any web applications to it.
*/
private void configure()
{
if (configured)
return;
configured = true;
if (stopPort > 0 && stopKey != null)
{
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
monitor.setPort(stopPort);
monitor.setKey(stopKey);
monitor.setExitVm(false);
}
if (tempDirectory != null && !tempDirectory.exists())
tempDirectory.mkdirs();
// Applies external configuration via jetty.xml
applyJettyXml();
// Configures connectors for this server instance.
if (connectors != null)
{
for (Connector c : connectors)
{
ServerConnector jc = new ServerConnector(server);
jc.setPort(c.getPort());
jc.setIdleTimeout(c.getMaxIdleTime());
server.addConnector(jc);
}
}
// Configures login services
if (loginServices != null)
{
for (LoginService ls : loginServices)
{
server.addBean(ls);
}
}
// Does not cache resources, to prevent Windows from locking files
Resource.setDefaultUseCaches(false);
// Set default server handlers
configureHandlers();
}
/**
*
*/
private void configureHandlers()
{
if (requestLog != null)
server.setRequestLog(requestLog);
contexts = server.getDescendant(ContextHandlerCollection.class);
if (contexts == null)
{
contexts = new ContextHandlerCollection();
Handler.Collection handlers = server.getDescendant(Handler.Collection.class);
if (handlers == null)
server.setHandler(new Handler.Collection(contexts, new DefaultHandler()));
else
handlers.addHandler(contexts);
}
//if there are any extra contexts to deploy
if (contextHandlers != null && contextHandlers.getContextHandlers() != null)
{
for (ContextHandler c : contextHandlers.getContextHandlers())
{
contexts.addHandler(c);
}
}
}
/**
* Applies jetty.xml configuration to the Jetty server instance.
*/
private void applyJettyXml()
{
if (jettyXml != null && jettyXml.exists())
{
TaskLog.log("Configuring jetty from xml configuration file = " + jettyXml.getAbsolutePath());
XmlConfiguration configuration;
try
{
configuration = new XmlConfiguration(new PathResource(jettyXml));
configuration.configure(server);
}
catch (MalformedURLException e)
{
throw new RuntimeException(e);
}
catch (SAXException e)
{
throw new RuntimeException(e);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
/**
* Starts web applications' scanners.
*/
private void startScanners() throws Exception
{
for (AntWebAppContext awc : webApplications)
{
if (scanIntervalSecs <= 0)
return;
TaskLog.log("Web application '" + awc + "': starting scanner at interval of " + scanIntervalSecs + " seconds.");
Scanner.Listener changeListener = new WebAppScannerListener(awc);
Scanner scanner = new Scanner();
scanner.setScanInterval(scanIntervalSecs);
scanner.addListener(changeListener);
scanner.setScanDirs(awc.getScanFiles());
scanner.setReportExistingFilesOnStartup(false);
scanner.start();
}
}
/**
*
*/
private void configureWebApps()
{
for (AntWebAppContext awc : webApplications)
{
awc.setAttribute(AntWebAppContext.BASETEMPDIR, tempDirectory);
if (contexts != null)
contexts.addHandler(awc);
}
}
}

View File

@ -0,0 +1,18 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
/**
* Jetty Ant : Ant Tasks and Configuration
*/
package org.eclipse.jetty.ee10.ant;

View File

@ -0,0 +1,42 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.types;
public class Attribute
{
String name;
String value;
public void setName(String name)
{
this.name = name;
}
public void setValue(String value)
{
this.value = value;
}
public String getName()
{
return name;
}
public String getValue()
{
return value;
}
}

View File

@ -0,0 +1,33 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.types;
import java.util.ArrayList;
import java.util.List;
public class Attributes
{
List<Attribute> _attributes = new ArrayList<Attribute>();
public void addAttribute(Attribute attr)
{
_attributes.add(attr);
}
public List<Attribute> getAttributes()
{
return _attributes;
}
}

View File

@ -0,0 +1,54 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.types;
/**
* Connector
*/
public class Connector
{
private int port;
private int maxIdleTime;
public Connector()
{
}
public Connector(int port, int maxIdleTime)
{
this.port = port;
this.maxIdleTime = maxIdleTime;
}
public int getPort()
{
return port;
}
public void setPort(int port)
{
this.port = port;
}
public int getMaxIdleTime()
{
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime)
{
this.maxIdleTime = maxIdleTime;
}
}

View File

@ -0,0 +1,76 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.types;
import java.util.ArrayList;
import java.util.List;
/**
* Specifies a jetty configuration <code>&lt;connectors/&gt;</code> element for Ant build file.
*/
public class Connectors
{
private List<Connector> connectors = new ArrayList<Connector>();
private List<Connector> defaultConnectors = new ArrayList<Connector>();
/**
* Default constructor.
*/
public Connectors()
{
this(8080, 30000);
}
/**
* Constructor.
*
* @param port The port that the default connector will listen on
* @param maxIdleTime The maximum idle time for the default connector
*/
public Connectors(int port, int maxIdleTime)
{
defaultConnectors.add(new Connector(port, maxIdleTime));
}
/**
* Adds a connector to the list of connectors to deploy.
*
* @param connector A connector to add to the list
*/
public void add(Connector connector)
{
connectors.add(connector);
}
/**
* Returns the list of known connectors to deploy.
*
* @return The list of known connectors
*/
public List<Connector> getConnectors()
{
return connectors;
}
/**
* Gets the default list of connectors to deploy when no connectors
* were explicitly added to the list.
*
* @return The list of default connectors
*/
public List<Connector> getDefaultConnectors()
{
return defaultConnectors;
}
}

View File

@ -0,0 +1,37 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.types;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.server.handler.ContextHandler;
/**
* Specifies <code>&lt;contextHandlers/&gt;</code> element in web app configuration.
*/
public class ContextHandlers
{
private List<ContextHandler> contextHandlers = new ArrayList<ContextHandler>();
public void add(ContextHandler handler)
{
contextHandlers.add(handler);
}
public List<ContextHandler> getContextHandlers()
{
return contextHandlers;
}
}

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.types;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.tools.ant.DirectoryScanner;
/**
* Describes set of files matched by <code>&lt;fileset/&gt;</code> elements in ant configuration
* file. It is used to group application classes, libraries, and scannedTargets
* elements.
*/
public class FileMatchingConfiguration
{
private List<DirectoryScanner> directoryScanners;
public FileMatchingConfiguration()
{
this.directoryScanners = new ArrayList<>();
}
/**
* @param directoryScanner new directory scanner retrieved from the
* <code>&lt;fileset/&gt;</code> element.
*/
public void addDirectoryScanner(DirectoryScanner directoryScanner)
{
this.directoryScanners.add(directoryScanner);
}
/**
* @return a list of base directories denoted by a list of directory
* scanners.
*/
public List<File> getBaseDirectories()
{
List<File> baseDirs = new ArrayList<>();
Iterator<DirectoryScanner> scanners = directoryScanners.iterator();
while (scanners.hasNext())
{
DirectoryScanner scanner = (DirectoryScanner)scanners.next();
baseDirs.add(scanner.getBasedir());
}
return baseDirs;
}
/**
* Checks if passed file is scanned by any of the directory scanners.
*
* @param pathToFile a fully qualified path to tested file.
* @return true if so, false otherwise.
*/
public boolean isIncluded(String pathToFile)
{
Iterator<DirectoryScanner> scanners = directoryScanners.iterator();
while (scanners.hasNext())
{
DirectoryScanner scanner = (DirectoryScanner)scanners.next();
scanner.scan();
String[] includedFiles = scanner.getIncludedFiles();
for (int i = 0; i < includedFiles.length; i++)
{
File includedFile = new File(scanner.getBasedir(), includedFiles[i]);
if (pathToFile.equalsIgnoreCase(includedFile.getAbsolutePath()))
{
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,37 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.types;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.ee10.servlet.security.LoginService;
/**
* Specifies a jetty configuration &lt;loginServices/&gt; element for Ant build file.
*/
public class LoginServices
{
private List<LoginService> loginServices = new ArrayList<LoginService>();
public void add(LoginService service)
{
loginServices.add(service);
}
public List<LoginService> getLoginServices()
{
return loginServices;
}
}

View File

@ -0,0 +1,59 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.types;
import java.util.ArrayList;
import java.util.List;
import org.apache.tools.ant.taskdefs.Property;
import org.eclipse.jetty.ee10.ant.utils.TaskLog;
/**
* SystemProperties
* <p>
* Ant &lt;systemProperties/&gt; tag definition.
*/
public class SystemProperties
{
private List systemProperties = new ArrayList();
public List getSystemProperties()
{
return systemProperties;
}
public void addSystemProperty(Property property)
{
systemProperties.add(property);
}
/**
* Set a System.property with this value if it is not already set.
*
* @param property the property to test
* @return true if property has been set
*/
public static boolean setIfNotSetAlready(Property property)
{
if (System.getProperty(property.getName()) == null)
{
System.setProperty(property.getName(), (property.getValue() == null ? "" : property.getValue()));
TaskLog.log("Setting property '" + property.getName() + "' to value '" + property.getValue() + "'");
return true;
}
return false;
}
}

View File

@ -0,0 +1,18 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
/**
* Jetty Ant : Ant Wrappers of Jetty Internals
*/
package org.eclipse.jetty.ee10.ant.types;

View File

@ -0,0 +1,34 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.utils;
import org.eclipse.jetty.ee10.ant.AntWebAppContext;
public interface ServerProxy
{
/**
* Adds a new web application to this server.
*
* @param awc a AntWebAppContext object.
*/
public void addWebApplication(AntWebAppContext awc);
/**
* Starts this server.
*/
public void start();
public Object getProxiedObject();
}

View File

@ -0,0 +1,52 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings and others.
// ------------------------------------------------------------------------
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.tools.ant.Task;
/**
* Provides logging functionality for classes without access to the Ant project
* variable.
*/
public class TaskLog
{
private static Task task;
private static final SimpleDateFormat format = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss.SSS");
public static void setTask(Task task)
{
TaskLog.task = task;
}
public static void log(String message)
{
task.log(message);
}
public static void logWithTimestamp(String message)
{
String date;
synchronized (format)
{
date = format.format(new Date());
}
task.log(date + ": " + message);
}
}

View File

@ -0,0 +1,18 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
/**
* Jetty Ant : Utility Classes
*/
package org.eclipse.jetty.ee10.ant.utils;

View File

@ -0,0 +1,2 @@
org.eclipse.jetty.ee10.ant.AntWebInfConfiguration
org.eclipse.jetty.ee10.ant.AntWebXmlConfiguration

View File

@ -0,0 +1,2 @@
jetty.run=org.eclipse.jetty.ee10.ant.JettyRunTask
jetty.stop=org.eclipse.jetty.ee10.ant.JettyStopTask

View File

@ -0,0 +1,39 @@
<!-- =======================================================================================-->
<!-- Build file for running the test-jetty-webapp from the jetty distro. -->
<!-- -->
<!-- You will need to have a full jetty-home available unpacked on your local file -->
<!-- system. We will refer to the top level directory of this distribution as $JETTY_HOME. -->
<!-- -->
<!-- To use: -->
<!-- * mkdir test-jetty-ant -->
<!-- * cp this file to test-jetty-ant -->
<!-- * cd test-jetty-ant; mkdir jetty-lib; mkdir jetty-temp -->
<!-- * copy all jars from $JETTY_HOME/lib and all subdirs flatly into ./jetty-lib -->
<!-- * copy the jetty-ant jar into ./jetty-lib -->
<!-- * copy the test.war from $JETTY_HOME/webapps.demo dir -->
<!-- -->
<!-- To run: -->
<!-- ant jetty.run -->
<!-- =======================================================================================-->
<project name="Jetty-Ant integration test" basedir=".">
<path id="jetty.plugin.classpath">
<fileset dir="jetty-lib" includes="*.jar"/>
</path>
<taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
<typedef name="hashLoginService" classname="org.eclipse.jetty.security.HashLoginService"
classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
<target name="jetty.run">
<jetty.run tempDirectory="jetty-temp">
<loginServices>
<hashLoginService name="Test Realm" config="realm.properties"/>
</loginServices>
<webApp war="test.war" contextpath="/test" >
<attributes>
<attribute name="org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern" value=".*/jetty-jakarta-servlet-api-[^/]*\.jar$"/>
</attributes>
</webApp>
</jetty.run>
</target>
</project>

View File

@ -0,0 +1,289 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
public class AntBuild
{
private Thread _process;
private String _ant;
private int _port;
private String _host;
public AntBuild(String ant)
{
_ant = ant;
}
private class AntBuildProcess implements Runnable
{
List<String[]> connList;
@Override
public void run()
{
File buildFile = new File(_ant);
Project antProject = new Project();
try
{
antProject.setBaseDir(MavenTestingUtils.getBaseDir());
antProject.setUserProperty("ant.file", buildFile.getAbsolutePath());
DefaultLogger logger = new DefaultLogger();
ConsoleParser parser = new ConsoleParser();
//connList = parser.newPattern(".*([0-9]+\\.[0-9]*\\.[0-9]*\\.[0-9]*):([0-9]*)",1);
connList = parser.newPattern("Jetty AntTask Started", 1);
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
PipedOutputStream pose = new PipedOutputStream();
PipedInputStream pise = new PipedInputStream(pose);
startPump("STDOUT", parser, pis);
startPump("STDERR", parser, pise);
logger.setErrorPrintStream(new PrintStream(pos));
logger.setOutputPrintStream(new PrintStream(pose));
logger.setMessageOutputLevel(Project.MSG_VERBOSE);
antProject.addBuildListener(logger);
antProject.fireBuildStarted();
antProject.init();
ProjectHelper helper = ProjectHelper.getProjectHelper();
antProject.addReference("ant.projectHelper", helper);
helper.parse(antProject, buildFile);
antProject.executeTarget("jetty.run");
parser.waitForDone(10000, TimeUnit.MILLISECONDS);
}
catch (Exception e)
{
antProject.fireBuildFinished(e);
}
}
public void waitForStarted() throws Exception
{
while (connList == null || connList.isEmpty())
{
Thread.sleep(10);
}
}
}
public void start() throws Exception
{
System.out.println("Starting Ant Build ...");
AntBuildProcess abp = new AntBuildProcess();
_process = new Thread(abp);
_process.start();
abp.waitForStarted();
// once this has returned we should have the connection info we need
//_host = abp.getConnectionList().get(0)[0];
//_port = Integer.parseInt(abp.getConnectionList().get(0)[1]);
}
public int getJettyPort()
{
return Integer.parseInt(System.getProperty("jetty.ant.server.port"));
}
public String getJettyHost()
{
return System.getProperty("jetty.ant.server.host");
}
/**
* Stop the jetty server
*/
public void stop()
{
System.out.println("Stopping Ant Build ...");
_process.interrupt();
}
private static class ConsoleParser
{
private List<ConsolePattern> patterns = new ArrayList<ConsolePattern>();
private CountDownLatch latch;
private int count;
public List<String[]> newPattern(String exp, int cnt)
{
ConsolePattern pat = new ConsolePattern(exp, cnt);
patterns.add(pat);
count += cnt;
return pat.getMatches();
}
public void parse(String line)
{
for (ConsolePattern pat : patterns)
{
Matcher mat = pat.getMatcher(line);
if (mat.find())
{
int num = 0;
int count = mat.groupCount();
String[] match = new String[count];
while (num++ < count)
{
match[num - 1] = mat.group(num);
}
pat.getMatches().add(match);
if (pat.getCount() > 0)
{
getLatch().countDown();
}
}
}
}
public void waitForDone(long timeout, TimeUnit unit) throws InterruptedException
{
getLatch().await(timeout, unit);
}
private CountDownLatch getLatch()
{
synchronized (this)
{
if (latch == null)
{
latch = new CountDownLatch(count);
}
}
return latch;
}
}
private static class ConsolePattern
{
private Pattern pattern;
private List<String[]> matches;
private int count;
ConsolePattern(String exp, int cnt)
{
pattern = Pattern.compile(exp);
matches = new ArrayList<String[]>();
count = cnt;
}
public Matcher getMatcher(String line)
{
return pattern.matcher(line);
}
public List<String[]> getMatches()
{
return matches;
}
public int getCount()
{
return count;
}
}
private void startPump(String mode, ConsoleParser parser, InputStream inputStream)
{
ConsoleStreamer pump = new ConsoleStreamer(mode, inputStream);
pump.setParser(parser);
Thread thread = new Thread(pump, "ConsoleStreamer/" + mode);
thread.start();
}
/**
* Simple streamer for the console output from a Process
*/
private static class ConsoleStreamer implements Runnable
{
private String mode;
private BufferedReader reader;
private ConsoleParser parser;
public ConsoleStreamer(String mode, InputStream is)
{
this.mode = mode;
this.reader = new BufferedReader(new InputStreamReader(is));
}
public void setParser(ConsoleParser connector)
{
this.parser = connector;
}
@Override
public void run()
{
String line;
//System.out.printf("ConsoleStreamer/%s initiated%n",mode);
try
{
while ((line = reader.readLine()) != (null))
{
if (parser != null)
{
parser.parse(line);
}
System.out.println("[" + mode + "] " + line);
}
}
catch (IOException ignore)
{
/* ignore */
}
finally
{
IO.close(reader);
}
//System.out.printf("ConsoleStreamer/%s finished%n",mode);
}
}
}

View File

@ -0,0 +1,66 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.ant;
import java.net.HttpURLConnection;
import java.net.URI;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class JettyAntTaskTest
{
@Test
public void testConnectorTask() throws Exception
{
AntBuild build = new AntBuild(MavenTestingUtils.getTestResourceFile("connector-test.xml").getAbsolutePath());
build.start();
URI uri = new URI("http://" + build.getJettyHost() + ":" + build.getJettyPort());
HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection();
connection.connect();
assertThat("response code is 404", connection.getResponseCode(), is(404));
build.stop();
}
@Disabled //TODO
@Test
public void testWebApp() throws Exception
{
AntBuild build = new AntBuild(MavenTestingUtils.getTestResourceFile("webapp-test.xml").getAbsolutePath());
build.start();
URI uri = new URI("http://" + build.getJettyHost() + ":" + build.getJettyPort() + "/");
HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection();
connection.connect();
assertThat("response code is 200", connection.getResponseCode(), is(200));
System.err.println("Stop build!");
build.stop();
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<project name="Jetty-Ant integration test" basedir=".">
<path id="jetty.plugin.classpath">
<fileset dir="target/test-lib" includes="*.jar"/>
</path>
<taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
<typedef name="connector" classname="org.eclipse.jetty.ee10.ant.types.Connector"
classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
<target name="jetty.run">
<jetty.run>
<connectors>
<connector port="0"/>
</connectors>
</jetty.run>
</target>
</project>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>acme</short-name>
<uri>http://www.acme.com/taglib</uri>
<description>taglib example</description>
<listener>
<listener-class>com.acme.TagListener</listener-class>
</listener>
<tag>
<name>date</name>
<tag-class>com.acme.DateTag</tag-class>
<body-content>TAGDEPENDENT</body-content>
<description>Display Date</description>
<attribute>
<name>tz</name>
<required>false</required>
</attribute>
</tag>
</taglib>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>Acme JSP2 tags</description>
<tlib-version>1.0</tlib-version>
<short-name>acme2</short-name>
<uri>http://www.acme.com/taglib2</uri>
<tag>
<description>Simple Date formatting</description>
<name>date2</name>
<tag-class>com.acme.Date2Tag</tag-class>
<body-content>scriptless</body-content>
<variable>
<description>Day of the Month</description>
<name-given>day</name-given>
</variable>
<variable>
<description>Month of the Year</description>
<name-given>month</name-given>
</variable>
<variable>
<description>Year</description>
<name-given>year</name-given>
</variable>
<attribute>
<name>format</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>

View File

@ -0,0 +1,17 @@
<%--
- Copyright (c) 2002 The Apache Software Foundation. All rights
- reserved.
--%>
<%@ attribute name="color" %>
<%@ attribute name="bgcolor" %>
<%@ attribute name="title" %>
<table border="1" bgcolor="${color}">
<tr>
<td><b>${title}</b></td>
</tr>
<tr>
<td bgcolor="${bgcolor}">
<jsp:doBody/>
</td>
</tr>
</table>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Test WebApp</display-name>
<context-param>
<param-name>org.eclipse.jetty.server.context.ManagedAttributes</param-name>
<param-value>QoSFilter,TransparentProxy.ThreadPool,TransparentProxy.HttpClient</param-value>
</context-param>
<servlet>
<servlet-name>foo.jsp</servlet-name>
<jsp-file>/jsp/foo/foo.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>foo.jsp</servlet-name>
<url-pattern>/jsp/foo/</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -0,0 +1,5 @@
<html>
<body>
<h1>INDEX!</h1>
</body>
</html>

View File

@ -0,0 +1,15 @@
<html>
<%@ page session="true"%>
<body>
<jsp:useBean id='counter' scope='session' class='com.acme.Counter' type="com.acme.Counter" />
<h1>JSP1.2 Beans: 1</h1>
Counter accessed <jsp:getProperty name="counter" property="count"/> times.<br/>
Counter last accessed by <jsp:getProperty name="counter" property="last"/><br/>
<jsp:setProperty name="counter" property="last" value="<%= request.getRequestURI()%>"/>
<a href="bean2.jsp">Goto bean2.jsp</a>
</body>
</html>

View File

@ -0,0 +1,15 @@
<html>
<%@ page session="true"%>
<body>
<jsp:useBean id='counter' scope='session' class='com.acme.Counter' type="com.acme.Counter" />
<h1>JSP1.2 Beans: 2</h1>
Counter accessed <jsp:getProperty name="counter" property="count"/> times.<br/>
Counter last accessed by <jsp:getProperty name="counter" property="last"/><br/>
<jsp:setProperty name="counter" property="last" value="<%= request.getRequestURI()%>"/>
<a href="bean1.jsp">Goto bean1.jsp</a>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More