| /** |
| * 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); |
| } |
| |
| } |