blob: 9c420ce718ce4a8587f8fe79d2d47fa83e016a48 [file] [log] [blame]
/**
* 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.cxf.common.injection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.annotation.Resources;
import org.apache.cxf.common.annotation.AbstractAnnotationVisitor;
import org.apache.cxf.common.annotation.AnnotationProcessor;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.ClassHelper;
import org.apache.cxf.common.util.ReflectionUtil;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.resource.ResourceManager;
import org.apache.cxf.resource.ResourceResolver;
/**
* injects references specified using @Resource annotation
*
*/
public class ResourceInjector extends AbstractAnnotationVisitor {
private static final Logger LOG = LogUtils.getL7dLogger(ResourceInjector.class);
private static final List<Class<? extends Annotation>> ANNOTATIONS = new ArrayList<>();
static {
ANNOTATIONS.add(Resource.class);
ANNOTATIONS.add(Resources.class);
}
private final ResourceManager resourceManager;
private final List<ResourceResolver> resourceResolvers;
public ResourceInjector(ResourceManager resMgr) {
this(resMgr, resMgr == null ? null : resMgr.getResourceResolvers());
}
public ResourceInjector(ResourceManager resMgr, List<ResourceResolver> resolvers) {
super(ANNOTATIONS);
resourceManager = resMgr;
resourceResolvers = resolvers;
}
private static Field getField(Class<?> cls, String name) {
if (cls == null) {
return null;
}
try {
Field f = ReflectionUtil.getDeclaredField(cls, name);
if (f == null) {
f = getField(cls.getSuperclass(), name);
}
return f;
} catch (Exception ex) {
return getField(cls.getSuperclass(), name);
}
}
public static boolean processable(Class<?> cls, Object o) {
if (cls.getName().startsWith("java.")
|| cls.getName().startsWith("javax.")) {
return false;
}
NoJSR250Annotations njsr = cls.getAnnotation(NoJSR250Annotations.class);
if (njsr != null) {
for (String s : njsr.unlessNull()) {
try {
Field f = getField(cls, s);
ReflectionUtil.setAccessible(f);
if (f.get(o) == null) {
return true;
}
} catch (Exception ex) {
return true;
}
}
return false;
}
return true;
}
public void inject(Object o) {
inject(o, o.getClass());
}
public void inject(Object o, Class<?> claz) {
if (processable(claz, o)) {
AnnotationProcessor processor = new AnnotationProcessor(o);
processor.accept(this, claz);
}
}
public void construct(Object o) {
setTarget(o);
if (processable(targetClass, o)) {
invokePostConstruct();
}
}
public void construct(Object o, Class<?> cls) {
setTarget(o, cls);
if (processable(targetClass, o)) {
invokePostConstruct();
}
}
public void destroy(Object o) {
setTarget(o);
if (processable(targetClass, o)) {
invokePreDestroy();
}
}
// Implementation of org.apache.cxf.common.annotation.AnnotationVisitor
@Override
public final void visitClass(final Class<?> clz, final Annotation annotation) { //NOPMD
assert annotation instanceof Resource || annotation instanceof Resources : annotation;
if (annotation instanceof Resource) {
injectResourceClassLevel((Resource)annotation);
} else if (annotation instanceof Resources) {
Resources resources = (Resources)annotation;
for (Resource resource : resources.value()) {
injectResourceClassLevel(resource);
}
}
}
private void injectResourceClassLevel(Resource res) {
if (res.name() == null || "".equals(res.name())) {
LOG.log(Level.INFO, "RESOURCE_NAME_NOT_SPECIFIED", target.getClass().getName());
return;
}
Object resource;
// first find a setter that matches this resource
Method setter = findSetterForResource(res);
if (setter != null) {
Class<?> type = getResourceType(res, setter);
resource = resolveResource(res.name(), type);
if (resource == null) {
LOG.log(Level.INFO, "RESOURCE_RESOLVE_FAILED");
return;
}
invokeSetter(setter, resource);
return;
}
Field field = findFieldForResource(res);
if (field != null) {
Class<?> type = getResourceType(res, field);
resource = resolveResource(res.name(), type);
if (resource == null) {
LOG.log(Level.INFO, "RESOURCE_RESOLVE_FAILED");
return;
}
injectField(field, resource);
return;
}
LOG.log(Level.SEVERE, "NO_SETTER_OR_FIELD_FOR_RESOURCE", getTarget().getClass().getName());
}
public final void visitField(final Field field, final Annotation annotation) {
assert annotation instanceof Resource : annotation;
Resource res = (Resource)annotation;
String name = getFieldNameForResource(res, field);
Class<?> type = getResourceType(res, field);
Object resource = resolveResource(name, type);
if (resource == null
&& "".equals(res.name())) {
resource = resolveResource(null, type);
}
if (resource != null) {
injectField(field, resource);
} else {
LOG.log(Level.INFO, "RESOURCE_RESOLVE_FAILED", name);
}
}
public final void visitMethod(final Method method, final Annotation annotation) {
assert annotation instanceof Resource : annotation;
Resource res = (Resource)annotation;
String resourceName = getResourceName(res, method);
Class<?> clz = getResourceType(res, method);
Object resource = resolveResource(resourceName, clz);
if (resource == null
&& "".equals(res.name())) {
resource = resolveResource(null, clz);
}
if (resource != null) {
invokeSetter(method, resource);
} else {
LOG.log(Level.FINE, "RESOURCE_RESOLVE_FAILED", new Object[] {resourceName, clz});
}
}
private Field findFieldForResource(Resource res) {
assert target != null;
assert res.name() != null;
for (Field field : target.getClass().getFields()) {
if (field.getName().equals(res.name())) {
return field;
}
}
for (Field field : target.getClass().getDeclaredFields()) {
if (field.getName().equals(res.name())) {
return field;
}
}
return null;
}
private Method findSetterForResource(Resource res) {
assert target != null;
String setterName = resourceNameToSetter(res.name());
Method setterMethod = null;
for (Method method : getTarget().getClass().getMethods()) {
if (setterName.equals(method.getName())) {
setterMethod = method;
break;
}
}
if (setterMethod != null && setterMethod.getParameterTypes().length != 1) {
LOG.log(Level.WARNING, "SETTER_INJECTION_WITH_INCORRECT_TYPE", setterMethod);
}
return setterMethod;
}
private static String resourceNameToSetter(String resName) {
return "set" + StringUtils.capitalize(resName);
}
private void invokeSetter(Method method, Object resource) {
try {
ReflectionUtil.setAccessible(method);
if (method.getDeclaringClass().isAssignableFrom(getTarget().getClass())) {
method.invoke(getTarget(), resource);
} else { // deal with the proxy setter method
Method targetMethod = getTarget().getClass().getMethod(method.getName(),
method.getParameterTypes());
targetMethod.invoke(getTarget(), resource);
}
} catch (IllegalAccessException e) {
LOG.log(Level.SEVERE, "INJECTION_SETTER_NOT_VISIBLE", method);
} catch (InvocationTargetException | SecurityException e) {
LogUtils.log(LOG, Level.SEVERE, "INJECTION_SETTER_RAISED_EXCEPTION", e, method);
} catch (NoSuchMethodException e) {
LOG.log(Level.SEVERE, "INJECTION_SETTER_METHOD_NOT_FOUND", new Object[] {method.getName()});
}
}
private String getResourceName(Resource res, Method method) {
assert method != null;
assert res != null;
assert method.getName().startsWith("set") : method;
if (res.name() == null || res.name().isEmpty()) {
String name = method.getName().substring(3);
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
return method.getDeclaringClass().getCanonicalName() + '/' + name;
}
return res.name();
}
private void injectField(Field field, Object resource) {
assert field != null;
assert resource != null;
boolean accessible = field.isAccessible();
try {
if (field.getType().isAssignableFrom(resource.getClass())) {
ReflectionUtil.setAccessible(field);
field.set(ClassHelper.getRealObject(getTarget()), resource);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
LOG.severe("FAILED_TO_INJECT_FIELD");
} finally {
ReflectionUtil.setAccessible(field, accessible);
}
}
public void invokePostConstruct() {
boolean accessible = false;
for (Method method : getPostConstructMethods()) {
PostConstruct pc = method.getAnnotation(PostConstruct.class);
if (pc != null) {
try {
ReflectionUtil.setAccessible(method);
method.invoke(target);
} catch (IllegalAccessException e) {
LOG.log(Level.WARNING, "INJECTION_COMPLETE_NOT_VISIBLE", method);
} catch (InvocationTargetException e) {
LOG.log(Level.WARNING, "INJECTION_COMPLETE_THREW_EXCEPTION", e);
} finally {
ReflectionUtil.setAccessible(method, accessible);
}
}
}
}
public void invokePreDestroy() {
boolean accessible = false;
for (Method method : getPreDestroyMethods()) {
PreDestroy pd = method.getAnnotation(PreDestroy.class);
if (pd != null) {
try {
ReflectionUtil.setAccessible(method);
method.invoke(target);
} catch (IllegalAccessException e) {
LOG.log(Level.WARNING, "PRE_DESTROY_NOT_VISIBLE", method);
} catch (InvocationTargetException e) {
LOG.log(Level.WARNING, "PRE_DESTROY_THREW_EXCEPTION", e);
} finally {
ReflectionUtil.setAccessible(method, accessible);
}
}
}
}
private Collection<Method> getPostConstructMethods() {
return getAnnotatedMethods(PostConstruct.class);
}
private Collection<Method> getPreDestroyMethods() {
return getAnnotatedMethods(PreDestroy.class);
}
private Collection<Method> getAnnotatedMethods(Class<? extends Annotation> acls) {
Collection<Method> methods = new LinkedList<>();
addAnnotatedMethods(acls, getTarget().getClass().getMethods(), methods);
addAnnotatedMethods(acls, ReflectionUtil.getDeclaredMethods(getTarget().getClass()), methods);
if (getTargetClass() != getTarget().getClass()) {
addAnnotatedMethods(acls, getTargetClass().getMethods(), methods);
addAnnotatedMethods(acls, ReflectionUtil.getDeclaredMethods(getTargetClass()), methods);
}
return methods;
}
private void addAnnotatedMethods(Class<? extends Annotation> acls, Method[] methods,
Collection<Method> annotatedMethods) {
for (Method method : methods) {
if (method.getAnnotation(acls) != null
&& !annotatedMethods.contains(method)) {
annotatedMethods.add(method);
}
}
}
/**
* making this protected to keep pmd happy
*/
protected Class<?> getResourceType(Resource res, Field field) {
assert res != null;
Class<?> type = res.type();
if (res.type() == null || Object.class == res.type()) {
type = field.getType();
}
return type;
}
private Class<?> getResourceType(Resource res, Method method) {
return res.type() != null && !Object.class.equals(res.type())
? res.type()
: method.getParameterTypes()[0];
}
private String getFieldNameForResource(Resource res, Field field) {
assert res != null;
if (res.name() == null || "".equals(res.name())) {
return field.getDeclaringClass().getCanonicalName() + "/" + field.getName();
}
return res.name();
}
private Object resolveResource(String resourceName, Class<?> type) {
if (resourceManager == null) {
return null;
}
return resourceManager.resolveResource(resourceName, type, resourceResolvers);
}
}