org.apache.myfaces.spi.impl.NoInjectionAnnotationInjectionProvider Maven / Gradle / Ivy
/*
* 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.myfaces.spi.impl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import javax.naming.NamingException;
import org.apache.myfaces.util.lang.ClassUtils;
import org.apache.myfaces.spi.InjectionProvider;
import org.apache.myfaces.spi.InjectionProviderException;
/**
* See SRV.14.5 Servlet Specification Version 2.5 JSR 154
* and Common Annotations for the Java Platform JSR 250
*/
public class NoInjectionAnnotationInjectionProvider extends InjectionProvider
{
/**
* Cache the Method instances per ClassLoader using the Class-Name.
* NOTE that we do it this way, because the only other valid way in order to support a shared
* classloader scenario would be to use a WeakHashMap, Method[]>, but this
* creates a cyclic reference between the key and the value of the WeakHashMap which will
* most certainly cause a memory leak! Furthermore we can manually cleanup the Map when
* the webapp is undeployed just by removing the Map for the current ClassLoader.
*/
private volatile static WeakHashMap > declaredMethodBeans =
new WeakHashMap>();
private static Map getDeclaredMethodBeansMap()
{
ClassLoader cl = ClassUtils.getContextClassLoader();
Map metadata = declaredMethodBeans.get(cl);
if (metadata == null)
{
// Ensure thread-safe put over _metadata, and only create one map
// per classloader to hold metadata.
synchronized (declaredMethodBeans)
{
metadata = createDeclaredMethodBeansMap(cl, metadata);
}
}
return metadata;
}
private static Map createDeclaredMethodBeansMap(
ClassLoader cl, Map metadata)
{
metadata = declaredMethodBeans.get(cl);
if (metadata == null)
{
metadata = new HashMap();
declaredMethodBeans.put(cl, metadata);
}
return metadata;
}
@Override
public Object inject(Object instance) throws InjectionProviderException
{
try
{
processAnnotations(instance);
}
catch (IllegalAccessException | InvocationTargetException | NamingException ex)
{
throw new InjectionProviderException(ex);
}
return null;
}
Method[] getDeclaredMethods(Class clazz)
{
Map declaredMethodBeansMap = getDeclaredMethodBeansMap();
Method[] methods = declaredMethodBeansMap.get(clazz);
if (methods == null)
{
methods = clazz.getDeclaredMethods();
synchronized(declaredMethodBeansMap)
{
declaredMethodBeansMap.put(clazz, methods);
}
}
return methods;
}
/**
* Call postConstruct method on the specified instance.
*/
@Override
public void postConstruct(Object instance, Object creationMetaData) throws InjectionProviderException
{
// TODO the servlet spec is not clear about searching in superclass??
Class clazz = instance.getClass();
Method[] methods = getDeclaredMethods(clazz);
if (methods == null)
{
methods = clazz.getDeclaredMethods();
Map declaredMethodBeansMap = getDeclaredMethodBeansMap();
synchronized(declaredMethodBeansMap)
{
declaredMethodBeansMap.put(clazz, methods);
}
}
Method postConstruct = null;
for (int i = 0; i < methods.length; i++)
{
Method method = methods[i];
if (method.isAnnotationPresent(PostConstruct.class))
{
// a method that does not take any arguments
// the method must not be static
// must not throw any checked expections
// the return value must be void
// the method may be public, protected, package private or private
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 = method;
}
}
try
{
invokeAnnotatedMethod(postConstruct, instance);
}
catch (IllegalAccessException | InvocationTargetException ex)
{
throw new InjectionProviderException(ex);
}
}
@Override
public void preDestroy(Object instance, Object creationMetaData) throws InjectionProviderException
{
// TODO the servlet spec is not clear about searching in superclass??
// May be only check non private fields and methods
Class clazz = instance.getClass();
Method[] methods = getDeclaredMethods(clazz);
Method preDestroy = null;
for (int i = 0; i < methods.length; i++)
{
Method method = methods[i];
if (method.isAnnotationPresent(PreDestroy.class))
{
// must not throw any checked expections
// the method must not be static
// must not throw any checked expections
// the return value must be void
// the method may be public, protected, package private or private
if ((preDestroy != null)
|| (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;
}
}
try
{
invokeAnnotatedMethod(preDestroy, instance);
}
catch (IllegalAccessException | InvocationTargetException ex)
{
throw new InjectionProviderException(ex);
}
}
private void invokeAnnotatedMethod(Method method, Object instance)
throws IllegalAccessException, InvocationTargetException
{
// At the end the annotated
// method is invoked
if (method != null)
{
boolean accessibility = method.isAccessible();
method.setAccessible(true);
method.invoke(instance);
method.setAccessible(accessibility);
}
}
/**
* Inject resources in specified instance.
*/
protected void processAnnotations(Object instance)
throws IllegalAccessException, InvocationTargetException, NamingException
{
}
}