org.apache.tomee.catalina.JavaeeInstanceManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tomee-catalina Show documentation
Show all versions of tomee-catalina Show documentation
This module contains the classes that will be added to the catalina class loader
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomee.catalina;
import org.apache.catalina.core.DefaultInstanceManager;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.NamingResourcesImpl;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.core.ParentClassLoaderFinder;
import org.apache.openejb.core.WebContext;
import org.apache.openejb.loader.SystemInstance;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.util.descriptor.web.Injectable;
import org.apache.tomcat.util.descriptor.web.InjectionTarget;
import org.apache.webbeans.exception.WebBeansConfigurationException;
import org.apache.webbeans.exception.WebBeansCreationException;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @version $Rev$ $Date$
*/
public class JavaeeInstanceManager implements InstanceManager {
private final WebContext webContext;
private final StandardContext webapp;
private final String[] skipContainerTags;
private final String[] skipPrefixes;
private volatile InstanceManager defaultInstanceManager;
public JavaeeInstanceManager(final StandardContext webapp, final WebContext webContext) {
this.webContext = webContext;
this.webapp = webapp;
this.skipContainerTags = SystemInstance.get().getProperty(
"tomee.tomcat.instance-manager.skip-container-tags", "org.apache.taglibs.standard.,javax.servlet.jsp.jstl.").split(" *, *");
final String[] skipCdi = SystemInstance.get().getProperty("tomee.tomcat.instance-manager.skip-cdi", "").split(" *, *");
this.skipPrefixes = skipCdi.length == 1 && skipCdi[0].isEmpty() ? new String[0] : skipCdi;
}
public ServletContext getServletContext() {
return webContext == null ? null : webContext.getServletContext();
}
@Override
public Object newInstance(final Class> clazz) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException {
try {
final String name = clazz.getName();
if ("org.apache.tomcat.websocket.server.WsHttpUpgradeHandler".equals(name)
|| "org.apache.tomee.myfaces.TomEEMyFacesContextListener".equals(name)
|| "org.apache.openejb.server.httpd.EEFilter".equals(name)
|| "org.apache.catalina.servlets.DefaultServlet".equals(name)
|| "org.apache.jasper.servlet.JspServlet".equals(name)) {
return clazz.newInstance();
}
final Object object = isSkip(name, skipPrefixes) ? clazz.newInstance() : webContext.newInstance(clazz);
if (isJsp(clazz)) {
initDefaultInstanceMgr();
defaultInstanceManager.newInstance(object);
}
postConstruct(object, clazz);
return object;
} catch (final OpenEJBException | WebBeansCreationException | WebBeansConfigurationException e) {
throw (InstantiationException) new InstantiationException(e.getMessage()).initCause(e);
}
}
private void initDefaultInstanceMgr() {
if (defaultInstanceManager == null) { // lazy cause can not be needed
synchronized (this) {
if (defaultInstanceManager == null) {
defaultInstanceManager = new DefaultInstanceManager(
webapp.getNamingContextListener().getEnvContext(),
TomcatInjections.buildInjectionMap(webapp.getNamingResources()), webapp,
ParentClassLoaderFinder.Helper.get());
}
}
}
}
private boolean isJsp(final Class> type) {
return type.getSuperclass().getName().equals("org.apache.jasper.runtime.HttpJspBase");
}
public WebContext.Instance newWeakableInstance(final Class> clazz) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException {
try {
final WebContext.Instance object = webContext.newWeakableInstance(clazz);
postConstruct(object.getValue(), clazz);
return object;
} catch (final OpenEJBException | WebBeansCreationException | WebBeansConfigurationException e) {
throw (InstantiationException) new InstantiationException(e.getMessage()).initCause(e);
}
}
@Override
public Object newInstance(final String className) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {
return newInstance(className, webContext.getClassLoader());
}
@Override
public Object newInstance(final String className, final ClassLoader classLoader) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {
return newInstance(classLoader.loadClass(className));
}
@Override
public void newInstance(final Object o) throws IllegalAccessException, InvocationTargetException, NamingException {
final String name = o.getClass().getName();
if ("org.apache.tomee.webservices.CXFJAXRSFilter".equals(name)
|| "org.apache.tomcat.websocket.server.WsFilter".equals(name)
|| isSkip(name, skipContainerTags)) {
return;
}
try {
if (!isSkip(name, skipPrefixes)) {
webContext.inject(o);
}
postConstruct(o, o.getClass());
} catch (final OpenEJBException e) {
destroyInstance(o);
throw new InjectionFailedException(e);
}
}
private boolean isSkip(final String name, final String[] prefixes) {
for (final String prefix : prefixes) {
if (name.startsWith(prefix)) {
return true;
}
}
return false;
}
@Override
public void destroyInstance(final Object o) throws IllegalAccessException, InvocationTargetException {
if (o == null) {
return;
}
final String name = o.getClass().getName();
if ("org.apache.tomcat.websocket.server.WsHttpUpgradeHandler".equals(name)
|| "org.apache.tomee.myfaces.TomEEMyFacesContextListener".equals(name)
|| "org.apache.openejb.server.httpd.EEFilter".equals(name)
|| "org.apache.catalina.servlets.DefaultServlet".equals(name)
|| "org.apache.jasper.servlet.JspServlet".equals(name)) {
return;
}
final Object unwrapped = unwrap(o);
try {
if (isJsp(o.getClass())) {
defaultInstanceManager.destroyInstance(o);
}
preDestroy(unwrapped, unwrapped.getClass());
} finally {
webContext.destroy(unwrapped);
if (unwrapped != o) { // PojoEndpointServer, they create and track a cc so release it
webContext.destroy(o);
}
}
}
private Object unwrap(final Object o) {
return "org.apache.tomcat.websocket.pojo.PojoEndpointServer".equals(o.getClass().getName()) ?
WebSocketTypes.unwrapWebSocketPojo(o) : o;
}
public void inject(final Object o) {
try {
webContext.inject(o);
} catch (final OpenEJBException e) {
throw new InjectionFailedException(e);
}
}
/**
* Call postConstruct method on the specified instance recursively from deepest superclass to actual class.
*
* @param instance object to call postconstruct methods on
* @param clazz (super) class to examine for postConstruct annotation.
* @throws IllegalAccessException if postConstruct method is inaccessible.
* @throws java.lang.reflect.InvocationTargetException if call fails
*/
public void postConstruct(final Object instance, final Class> clazz)
throws IllegalAccessException, InvocationTargetException {
final Class> superClass = clazz.getSuperclass();
if (superClass != Object.class) {
postConstruct(instance, superClass);
}
final Method[] methods = clazz.getDeclaredMethods();
Method postConstruct = null;
for (final Method method : methods) {
if (method.isAnnotationPresent(PostConstruct.class)) {
if ((postConstruct != null)
|| (method.getParameterTypes().length != 0)
|| (Modifier.isStatic(method.getModifiers()))
|| (method.getExceptionTypes().length > 0)
|| (!method.getReturnType().getName().equals("void"))) {
throw new IllegalArgumentException("Invalid PostConstruct annotation. @PostConstruct methods "
+ "should respect the following constraints:\n"
+ "- no parameter (" + (method.getParameterTypes().length == 0) + ")\n"
+ "- no exception should be declared (" + (method.getExceptionTypes().length == 0) + ")\n"
+ "- should return void (" + method.getReturnType().getName().equals("void") + ")\n"
+ "- should not be static (" + !Modifier.isStatic(method.getModifiers()) + ")\n");
}
postConstruct = method;
}
}
// At the end the postconstruct annotated
// method is invoked
if (postConstruct != null) {
final boolean accessibility = postConstruct.isAccessible();
postConstruct.setAccessible(true);
postConstruct.invoke(instance);
postConstruct.setAccessible(accessibility);
}
}
/**
* Call preDestroy method on the specified instance recursively from deepest superclass to actual class.
*
* @param instance object to call preDestroy methods on
* @param clazz (super) class to examine for preDestroy annotation.
* @throws IllegalAccessException if preDestroy method is inaccessible.
* @throws java.lang.reflect.InvocationTargetException if call fails
*/
protected void preDestroy(final Object instance, final Class> clazz)
throws IllegalAccessException, InvocationTargetException {
final Class> superClass = clazz.getSuperclass();
if (superClass != Object.class) {
preDestroy(instance, superClass);
}
final Method[] methods = clazz.getDeclaredMethods();
Method preDestroy = null;
for (final Method method : methods) {
if (method.isAnnotationPresent(PreDestroy.class)) {
if ((method.getParameterTypes().length != 0)
|| (Modifier.isStatic(method.getModifiers()))
|| (method.getExceptionTypes().length > 0)
|| (!method.getReturnType().getName().equals("void"))) {
throw new IllegalArgumentException("Invalid PreDestroy annotation");
}
preDestroy = method;
break;
}
}
// At the end the postconstruct annotated
// method is invoked
if (preDestroy != null) {
final boolean accessibility = preDestroy.isAccessible();
preDestroy.setAccessible(true);
preDestroy.invoke(instance);
preDestroy.setAccessible(accessibility);
}
}
private static final class WebSocketTypes { // extracted for lazy loading
private static final WebSocketTypes WEB_SOCKET_TYPES = new WebSocketTypes();
private final Method getPojo;
private WebSocketTypes() {
Method tmp;
try {
tmp = WebSocketTypes.class.getClassLoader()
.loadClass("org.apache.tomcat.websocket.pojo.PojoEndpointBase")
.getDeclaredMethod("getPojo");
tmp.setAccessible(true);
} catch (final NoSuchMethodException e) {
if ("true".equals(SystemInstance.get().getProperty("tomee.websocket.skip", "false"))) {
tmp = null;
} else {
throw new IllegalStateException(e);
}
} catch (final ClassNotFoundException e) {
tmp = null; // no websocket support
}
getPojo = tmp;
}
private static Object unwrapWebSocketPojo(final Object o) {
try {
return WEB_SOCKET_TYPES.getPojo == null ? o : WEB_SOCKET_TYPES.getPojo.invoke(o);
} catch (final IllegalAccessException | InvocationTargetException | NullPointerException e) {
return o;
}
}
}
private static final class TomcatInjections { // load when needed
private TomcatInjections() {
// no-op
}
private static Map> buildInjectionMap(final NamingResourcesImpl namingResources) {
final Map> injectionMap = new HashMap<>();
for (final Injectable resource : namingResources.findLocalEjbs()) {
addInjectionTarget(resource, injectionMap);
}
for (final Injectable resource : namingResources.findEjbs()) {
addInjectionTarget(resource, injectionMap);
}
for (final Injectable resource : namingResources.findEnvironments()) {
addInjectionTarget(resource, injectionMap);
}
for (final Injectable resource : namingResources.findMessageDestinationRefs()) {
addInjectionTarget(resource, injectionMap);
}
for (final Injectable resource : namingResources.findResourceEnvRefs()) {
addInjectionTarget(resource, injectionMap);
}
for (final Injectable resource : namingResources.findResources()) {
addInjectionTarget(resource, injectionMap);
}
for (final Injectable resource : namingResources.findServices()) {
addInjectionTarget(resource, injectionMap);
}
return injectionMap;
}
private static void addInjectionTarget(final Injectable resource, final Map> injectionMap) {
final List injectionTargets = resource.getInjectionTargets();
if (injectionTargets != null && !injectionTargets.isEmpty()) {
final String jndiName = resource.getName();
for (final InjectionTarget injectionTarget : injectionTargets) {
final String clazz = injectionTarget.getTargetClass();
Map injections = injectionMap.get(clazz);
if (injections == null) {
injections = new HashMap<>();
injectionMap.put(clazz, injections);
}
injections.put(injectionTarget.getTargetName(), jndiName);
}
}
}
}
}