/* * 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.deltaspike.core.api.config.view.metadata; import org.apache.deltaspike.core.api.provider.BeanProvider; import javax.inject.Named; import java.beans.Introspector; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Basic descriptor for a given class and callback type. It finds and caches the method(s) of the given class which are * annotated with the given callback-type. */ public abstract class CallbackDescriptor { protected List<CallbackEntry> callbacks = new ArrayList<CallbackEntry>(); protected Class<? extends Annotation> callbackType; protected CallbackDescriptor(Class<?> beanClass, Class<? extends Annotation> callbackMarker) { init(new Class[]{beanClass}, callbackMarker); } protected CallbackDescriptor(Class<?>[] beanClasses, Class<? extends Annotation> callbackMarker) { init(beanClasses, callbackMarker); } protected void init(Class<?>[] targetBeanClasses, Class<? extends Annotation> callbackMarker) { if (callbackMarker == null) { callbackMarker = DefaultCallback.class; } this.callbackType = callbackMarker; //TODO discuss how deep we should scan for (Class<?> targetBeanClass : targetBeanClasses) { CallbackEntry callbackEntry = new CallbackEntry(targetBeanClass, callbackMarker); if (!callbackEntry.callbackMethods.isEmpty()) { this.callbacks.add(callbackEntry); } } } public Map<Class<?>, List<Method>> getCallbackMethods() { Map<Class<?>, List<Method>> result = new HashMap<Class<?>, List<Method>>(this.callbacks.size()); for (CallbackEntry callbackEntry : this.callbacks) { result.put(callbackEntry.targetBeanClass, new ArrayList<Method>(callbackEntry.callbackMethods)); } return result; } protected <T> T getTargetObject(Class<T> targetType) { return BeanProvider.getContextualReference(targetType, true); } protected Object getTargetObjectByName(String beanName) { return BeanProvider.getContextualReference(beanName, true); } public boolean isBoundTo(Class<? extends Annotation> callbackType) { return this.callbackType.equals(callbackType); } protected static class CallbackEntry { private List<Method> callbackMethods = new ArrayList<Method>(); private final Class<?> targetBeanClass; private final String beanName; private CallbackEntry(Class<?> beanClass, Class<? extends Annotation> callbackMarker) { this.targetBeanClass = beanClass; Named named = this.targetBeanClass.getAnnotation(Named.class); if (named != null && !"".equals(named.value())) { this.beanName = named.value(); } else { //fallback to the default (which might exist) -> TODO check meta-data of Bean<T> this.beanName = Introspector.decapitalize(targetBeanClass.getSimpleName()); } List<String> processedMethodNames = new ArrayList<String>(); findMethodWithCallbackMarker(callbackMarker, beanClass, processedMethodNames); } private void findMethodWithCallbackMarker(Class<? extends Annotation> callbackMarker, Class<?> classToAnalyze, List<String> processedMethodNames) { Class<?> currentClass = classToAnalyze; while (currentClass != null && !Object.class.getName().equals(currentClass.getName())) { for (Method currentMethod : currentClass.getDeclaredMethods()) { //don't process overridden methods //ds now allows callbacks with parameters -> TODO refactor this approach if (processedMethodNames.contains(currentMethod.getName())) { continue; } if (currentMethod.isAnnotationPresent(callbackMarker)) { processedMethodNames.add(currentMethod.getName()); if (Modifier.isPrivate(currentMethod.getModifiers())) { throw new IllegalStateException( "Private methods aren't supported to avoid side-effects with normal-scoped CDI beans." + " Please use e.g. protected or public instead. "); } currentMethod.setAccessible(true); this.callbackMethods.add(currentMethod); } } //scan interfaces for (Class<?> interfaceClass : currentClass.getInterfaces()) { findMethodWithCallbackMarker(callbackMarker, interfaceClass, processedMethodNames); } currentClass = currentClass.getSuperclass(); } } public List<Method> getCallbackMethods() { return callbackMethods; } public Class<?> getTargetBeanClass() { return targetBeanClass; } public String getBeanName() { return beanName; } } }