org.apache.openejb.assembler.classic.MethodInfoUtil 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.openejb.assembler.classic;
import org.apache.openejb.BeanContext;
import org.apache.openejb.util.Classes;
import org.apache.openejb.util.Join;
import org.apache.openejb.util.SetAccessible;
import javax.ejb.EJBLocalObject;
import javax.ejb.EJBObject;
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import static java.util.Arrays.asList;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.lang.reflect.Method;
/**
* @version $Rev: 1397108 $ $Date: 2012-10-11 17:29:04 +0200 (Thu, 11 Oct 2012) $
*/
public class MethodInfoUtil {
/**
* Finds the nearest java.lang.reflect.Method with the given NamedMethodInfo
* Callbacks can be private so class.getMethod() cannot be used. Searching
* starts by looking in the specified class, if the method is not found searching continues with
* the immediate parent and continues recurssively until the method is found or java.lang.Object
* is reached. If the method is not found a IllegalStateException is thrown.
*
* @param clazz
* @param methodName
* @param parameterTypes
* @return
* @throws IllegalStateException if the method is not found in this class or any of its parent classes
*/
public static Method toMethod(Class clazz, NamedMethodInfo info) {
List parameterTypes = new ArrayList();
if (info.methodParams != null){
for (String paramType : info.methodParams) {
try {
parameterTypes.add(Classes.forName(paramType, clazz.getClassLoader()));
} catch (ClassNotFoundException cnfe) {
throw new IllegalStateException("Parameter class could not be loaded for type " + paramType, cnfe);
}
}
}
Class[] parameters = parameterTypes.toArray(new Class[parameterTypes.size()]);
IllegalStateException noSuchMethod = null;
while (clazz != null) {
try {
Method method = clazz.getDeclaredMethod(info.methodName, parameters);
return SetAccessible.on(method);
} catch (NoSuchMethodException e) {
if (noSuchMethod == null) {
noSuchMethod = new IllegalStateException("Callback method does not exist: " + clazz.getName() + "." + info.methodName, e);
}
clazz = clazz.getSuperclass();
}
}
throw noSuchMethod;
}
public static List matchingMethods(Method signature, Class clazz) {
List list = new ArrayList();
METHOD: for (Method method : clazz.getMethods()) {
if (!method.getName().equals(signature.getName())) continue;
Class>[] methodTypes = method.getParameterTypes();
Class>[] signatureTypes = signature.getParameterTypes();
if (methodTypes.length != signatureTypes.length) continue;
for (int i = 0; i < methodTypes.length; i++) {
if (!methodTypes[i].equals(signatureTypes[i])) continue METHOD;
}
list.add(method);
}
return list;
}
public static List matchingMethods(MethodInfo mi, Class clazz) {
Method[] methods = clazz.getMethods();
return matchingMethods(mi, methods);
}
public static List matchingMethods(MethodInfo mi, Method[] methods) {
List filtered = filterByLevel(mi, methods);
filtered = filterByView(mi, filtered);
return filtered;
}
private static List filterByView(MethodInfo mi, List filtered) {
View view = view(mi);
switch(view){
case CLASS:{
return filterByClass(mi, filtered);
}
}
return filtered;
}
private static List filterByClass(MethodInfo mi, List methods) {
ArrayList list = new ArrayList();
for (Method method : methods) {
String className = method.getDeclaringClass().getName();
if (mi.className.equals(className)){
list.add(method);
}
}
return list;
}
private static List filterByLevel(MethodInfo mi, Method[] methods) {
Level level = level(mi);
switch(level){
case BEAN:
case PACKAGE: {
return asList(methods);
}
case OVERLOADED_METHOD:{
return filterByName(methods, mi.methodName);
}
case EXACT_METHOD:{
return filterByNameAndParams(methods, mi);
}
}
return Collections.EMPTY_LIST;
}
public static Method getMethod(Class clazz, MethodInfo info) {
ClassLoader cl = clazz.getClassLoader();
List params = new ArrayList();
for (String methodParam : info.methodParams) {
try {
params.add(getClassForParam(methodParam, cl));
} catch (ClassNotFoundException cnfe) {
}
}
Method method = null;
try {
method = clazz.getMethod(info.methodName, params.toArray(new Class[params.size()]));
} catch (NoSuchMethodException e) {
return null;
}
if (!info.className.equals("*") && !method.getDeclaringClass().getName().equals(info.className)){
return null;
}
return method;
}
private static List filterByName(Method[] methods, String methodName) {
List list = new ArrayList();
for (Method method : methods) {
if (method.getName().equals(methodName)){
list.add(method);
}
}
return list;
}
private static List filterByNameAndParams(Method[] methods, MethodInfo mi) {
List list = new ArrayList();
for (Method method : methods) {
if (matches(method, mi)){
list.add(method);
}
}
return list;
}
/**
* This method splits the MethodPermissionInfo objects so that there is
* exactly one MethodInfo per MethodPermissionInfo. A single MethodPermissionInfo
* with three MethodInfos would be expanded into three MethodPermissionInfo with
* one MethodInfo each.
*
* The MethodPermissionInfo list is then sorted from least to most specific.
*
* @param infos
* @return a normalized list of new MethodPermissionInfo objects
*/
public static List normalizeMethodPermissionInfos(List infos){
List normalized = new ArrayList();
for (MethodPermissionInfo oldInfo : infos) {
for (MethodInfo methodInfo : oldInfo.methods) {
MethodPermissionInfo newInfo = new MethodPermissionInfo();
newInfo.description = oldInfo.description;
newInfo.methods.add(methodInfo);
newInfo.roleNames.addAll(oldInfo.roleNames);
newInfo.unchecked = oldInfo.unchecked;
newInfo.excluded = oldInfo.excluded;
normalized.add(newInfo);
}
}
Collections.sort(normalized, new MethodPermissionComparator());
return normalized;
}
private static Class getClassForParam(String className, ClassLoader cl) throws ClassNotFoundException {
if (className.equals("int")) {
return Integer.TYPE;
} else if (className.equals("double")) {
return Double.TYPE;
} else if (className.equals("long")) {
return Long.TYPE;
} else if (className.equals("boolean")) {
return Boolean.TYPE;
} else if (className.equals("float")) {
return Float.TYPE;
} else if (className.equals("char")) {
return Character.TYPE;
} else if (className.equals("short")) {
return Short.TYPE;
} else if (className.equals("byte")) {
return Byte.TYPE;
} else
return Class.forName(className, false, cl);
}
public static Map resolveAttributes(List extends MethodAttributeInfo> infos, BeanContext beanContext) {
Map attributes = new LinkedHashMap();
Method[] wildCardView = getWildCardView(beanContext).toArray(new Method[]{});
for (MethodAttributeInfo attributeInfo : infos) {
for (MethodInfo methodInfo : attributeInfo.methods) {
if (methodInfo.ejbName == null || methodInfo.ejbName.equals("*") || methodInfo.ejbName.equals(beanContext.getEjbName())) {
List methods = new ArrayList();
if (methodInfo.methodIntf == null) {
methods.addAll(matchingMethods(methodInfo, wildCardView));
} else if (methodInfo.methodIntf.equals("Home")) {
methods.addAll(matchingMethods(methodInfo, beanContext.getHomeInterface()));
} else if (methodInfo.methodIntf.equals("Remote")) {
if (beanContext.getRemoteInterface() != null) {
methods.addAll(matchingMethods(methodInfo, beanContext.getRemoteInterface()));
}
for (Class intf : beanContext.getBusinessRemoteInterfaces()) {
methods.addAll(matchingMethods(methodInfo, intf));
}
} else if (methodInfo.methodIntf.equals("LocalHome")) {
methods.addAll(matchingMethods(methodInfo, beanContext.getLocalHomeInterface()));
} else if (methodInfo.methodIntf.equals("Local")) {
if (beanContext.getLocalInterface() != null) {
methods.addAll(matchingMethods(methodInfo, beanContext.getLocalInterface()));
}
for (Class intf : beanContext.getBusinessRemoteInterfaces()) {
methods.addAll(matchingMethods(methodInfo, intf));
}
} else if (methodInfo.methodIntf.equals("ServiceEndpoint")) {
methods.addAll(matchingMethods(methodInfo, beanContext.getServiceEndpointInterface()));
}
for (Method method : methods) {
if (containerMethod(method)) {
continue;
}
attributes.put(method, attributeInfo);
}
}
}
}
return attributes;
}
public static Map resolveViewAttributes(List extends MethodAttributeInfo> infos, BeanContext beanContext) {
Map attributes = new LinkedHashMap();
Method[] wildCardView = getWildCardView(beanContext).toArray(new Method[]{});
for (MethodAttributeInfo attributeInfo : infos) {
for (MethodInfo methodInfo : attributeInfo.methods) {
if (methodInfo.ejbName == null || methodInfo.ejbName.equals("*") || methodInfo.ejbName.equals(beanContext.getEjbName())) {
List methods = new ArrayList();
if (methodInfo.methodIntf == null) {
methods.addAll(matchingMethods(methodInfo, wildCardView));
} else if (methodInfo.methodIntf.equals("Home")) {
methods.addAll(matchingMethods(methodInfo, beanContext.getHomeInterface()));
} else if (methodInfo.methodIntf.equals("Remote")) {
if (beanContext.getRemoteInterface() != null) {
methods.addAll(matchingMethods(methodInfo, beanContext.getRemoteInterface()));
}
for (Class intf : beanContext.getBusinessRemoteInterfaces()) {
methods.addAll(matchingMethods(methodInfo, intf));
}
} else if (methodInfo.methodIntf.equals("LocalHome")) {
methods.addAll(matchingMethods(methodInfo, beanContext.getLocalHomeInterface()));
} else if (methodInfo.methodIntf.equals("Local")) {
if (beanContext.getLocalInterface() != null) {
methods.addAll(matchingMethods(methodInfo, beanContext.getLocalInterface()));
}
for (Class intf : beanContext.getBusinessRemoteInterfaces()) {
methods.addAll(matchingMethods(methodInfo, intf));
}
} else if (methodInfo.methodIntf.equals("ServiceEndpoint")) {
methods.addAll(matchingMethods(methodInfo, beanContext.getServiceEndpointInterface()));
}
for (Method method : methods) {
if (containerMethod(method)) {
continue;
}
final ViewMethod viewMethod = new ViewMethod(methodInfo.methodIntf, method);
attributes.put(viewMethod, attributeInfo);
// List methodAttributeInfos = attributes.get(method);
// if (methodAttributeInfos == null) {
// methodAttributeInfos = new ArrayList();
// attributes.put(method, methodAttributeInfos);
// }
// methodAttributeInfos.add(attributeInfo);
}
}
}
}
return attributes;
}
public static class ViewMethod {
private final String view;
private final Method method;
public ViewMethod(String view, Method method) {
this.view = view;
this.method = method;
}
public String getView() {
return view;
}
public Method getMethod() {
return method;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ViewMethod that = (ViewMethod) o;
if (!method.equals(that.method)) return false;
if (view != null ? !view.equals(that.view) : that.view != null) return false;
return true;
}
@Override
public int hashCode() {
int result = view != null ? view.hashCode() : 0;
result = 31 * result + method.hashCode();
return result;
}
@Override
public String toString() {
return String.format("%s : %s(%s)", view, method.getName(), Join.join(", ", Classes.getSimpleNames(method.getParameterTypes())));
}
}
private static boolean containerMethod(Method method) {
return (method.getDeclaringClass() == EJBObject.class ||
method.getDeclaringClass() == EJBHome.class ||
method.getDeclaringClass() == EJBLocalObject.class ||
method.getDeclaringClass() == EJBLocalHome.class) &&
!method.getName().equals("remove");
}
private static List getWildCardView(BeanContext info) {
List methods = new ArrayList();
List beanMethods = asList(info.getBeanClass().getMethods());
methods.addAll(beanMethods);
if (info.getRemoteInterface() != null) {
methods.addAll(exclude(beanMethods, info.getRemoteInterface().getMethods()));
}
if (info.getHomeInterface() != null) {
methods.addAll(exclude(beanMethods, info.getHomeInterface().getMethods()));
}
if (info.getLocalInterface() != null) {
methods.addAll(exclude(beanMethods, info.getLocalInterface().getMethods()));
}
if (info.getLocalHomeInterface() != null) {
methods.addAll(exclude(beanMethods, info.getLocalHomeInterface().getMethods()));
}
if(info.getMdbInterface() != null) {
methods.addAll(exclude(beanMethods, info.getMdbInterface().getMethods()));
}
if(info.getServiceEndpointInterface() != null) {
methods.addAll(exclude(beanMethods, info.getServiceEndpointInterface().getMethods()));
}
for (Class intf : info.getBusinessRemoteInterfaces()) {
methods.addAll(exclude(beanMethods, intf.getMethods()));
}
for (Class intf : info.getBusinessLocalInterfaces()) {
methods.addAll(exclude(beanMethods, intf.getMethods()));
}
// Remove methods that cannot be controlled by the user
Iterator iterator = methods.iterator();
while (iterator.hasNext()) {
Method method = iterator.next();
if (containerMethod(method)) iterator.remove();
}
return methods;
}
private static List exclude(List excludes, Method[] methods) {
ArrayList list = new ArrayList();
for (Method method : methods) {
if (!matches(excludes, method)){
list.add(method);
}
}
return list;
}
private static boolean matches(List excludes, Method method) {
for (Method excluded : excludes) {
boolean match = match(method, excluded);
if (match){
return true;
}
}
return false;
}
public static boolean match(Method methodA, Method methodB) {
if (!methodA.getName().equals(methodB.getName())) return false;
if (methodA.getParameterTypes().length != methodB.getParameterTypes().length){
return false;
}
for (int i = 0; i < methodA.getParameterTypes().length; i++) {
Class> a = methodA.getParameterTypes()[i];
Class> b = methodB.getParameterTypes()[i];
if (!a.equals(b)) return false;
}
return true;
}
public static boolean matches(Method method, MethodInfo methodInfo) {
return matches(method, methodInfo.methodName, methodInfo.methodParams);
}
public static boolean matches(Method method, NamedMethodInfo methodInfo) {
return matches(method, methodInfo.methodName, methodInfo.methodParams);
}
public static boolean matches(Method method, String methodName, List methodParams) {
if (!methodName.equals(method.getName())) {
return false;
}
// do we have parameters?
if (methodParams == null) {
return true;
}
// do we have the same number of parameters?
if (methodParams.size() != method.getParameterTypes().length) {
return false;
}
// match parameters names
Class>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class> parameterType = parameterTypes[i];
String methodParam = methodParams.get(i);
if (!methodParam.equals(getName(parameterType)) && !methodParam.equals(parameterType.getName())) {
return false;
}
}
return true;
}
private static String getName(Class> type) {
if (type.isArray()) {
return getName(type.getComponentType()) + "[]"; // depend on JVM? type.getName() seems to work on Oracle one
} else {
return type.getName();
}
}
public static enum Level {
PACKAGE, BEAN, OVERLOADED_METHOD, EXACT_METHOD
}
public static enum View {
CLASS, ANY, INTERFACE;
}
public static View view(MethodInfo methodInfo) {
if (methodInfo.className != null && !methodInfo.className.equals("*")) return View.CLASS;
if (methodInfo.methodIntf != null && !methodInfo.methodIntf.equals("*")) return View.INTERFACE;
else return View.ANY;
}
public static Level level(MethodInfo methodInfo) {
if (methodInfo.ejbName != null && methodInfo.ejbName.equals("*")) return Level.PACKAGE;
if (methodInfo.methodName.equals("*")) return Level.BEAN;
if (methodInfo.methodParams == null) return Level.OVERLOADED_METHOD;
return Level.EXACT_METHOD;
}
public static class MethodPermissionComparator extends BaseComparator {
public int compare(MethodPermissionInfo a, MethodPermissionInfo b) {
return compare(a.methods.get(0), b.methods.get(0));
}
}
public static abstract class BaseComparator implements Comparator{
public int compare(MethodInfo am, MethodInfo bm) {
Level levelA = level(am);
Level levelB = level(bm);
// Primary sort
if (levelA != levelB) return levelA.ordinal() - levelB.ordinal();
// Secondary sort
return view(am).ordinal() - view(bm).ordinal();
}
}
public static String toString(MethodInfo i) {
String s = i.ejbName;
s += " : ";
s += (i.methodIntf == null) ? "*" : i.methodIntf;
s += " : ";
s += i.className;
s += " : ";
s += i.methodName;
s += "(";
if (i.methodParams != null) {
s += Join.join(", ", i.methodParams);
} else {
s += "*";
}
s += ")";
return s;
}
public static String toString(MethodPermissionInfo i) {
String s = toString(i.methods.get(0));
if (i.unchecked){
s += " Unchecked";
} else if (i.excluded){
s += " Excluded";
} else {
s += " " + Join.join(", ", i.roleNames);
}
return s;
}
public static String toString(MethodTransactionInfo i) {
String s = toString(i.methods.get(0));
s += " " + i.transAttribute;
return s;
}
public static String toString(MethodConcurrencyInfo i) {
String s = toString(i.methods.get(0));
s += " " + i.concurrencyAttribute;
return s;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy