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:
parent
04acdb72f0
commit
0a32147e89
|
@ -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>
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>@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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>@ServletSecurity</code> annotation on it,
|
||||
* setting up the <code><security-constraint>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><auth-constraint></code> and
|
||||
* <code><user-data-constraint></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><security-constraint></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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.ee10.annotations.AnnotationConfiguration
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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=[]"));
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
# Jetty Logging using jetty-slf4j-impl
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.annotations.LEVEL=DEBUG
|
Binary file not shown.
Binary file not shown.
|
@ -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>
|
||||
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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><connectors/></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;
|
||||
}
|
||||
}
|
|
@ -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><contextHandlers/></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;
|
||||
}
|
||||
}
|
|
@ -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><fileset/></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><fileset/></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;
|
||||
}
|
||||
}
|
|
@ -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 <loginServices/> 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;
|
||||
}
|
||||
}
|
|
@ -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 <systemProperties/> 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
org.eclipse.jetty.ee10.ant.AntWebInfConfiguration
|
||||
org.eclipse.jetty.ee10.ant.AntWebXmlConfiguration
|
|
@ -0,0 +1,2 @@
|
|||
jetty.run=org.eclipse.jetty.ee10.ant.JettyRunTask
|
||||
jetty.stop=org.eclipse.jetty.ee10.ant.JettyStopTask
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
@ -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>
|
|
@ -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>
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
<h1>INDEX!</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -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>
|
|
@ -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
Loading…
Reference in New Issue