All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rhq.bindings.util.InterfaceSimplifier Maven / Gradle / Ivy

Go to download

Abstraction of different facilities and default configurations for script bindings

The newest version!
/*
 * RHQ Management Platform
 * Copyright (C) 2005-2011 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.bindings.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.ParameterAnnotationsAttribute;
import javassist.bytecode.SignatureAttribute;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.AnnotationMemberValue;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.ByteMemberValue;
import javassist.bytecode.annotation.CharMemberValue;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.DoubleMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.FloatMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.LongMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.MemberValueVisitor;
import javassist.bytecode.annotation.ShortMemberValue;
import javassist.bytecode.annotation.StringMemberValue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.rhq.core.domain.auth.Subject;

/**
 * The scripts can use simplified interfaces that omit the first "Subject" argument
 * to most methods from RHQ's remote API. This helper class prepares such simplified interfaces.
 * 
 * @author Greg Hinkle
 * @author Lukas Krejci
 */
public class InterfaceSimplifier {
    private static final Log LOG = LogFactory.getLog(InterfaceSimplifier.class);

    private InterfaceSimplifier() {

    }

    /**
     * Returns the method on the original interface that the simplified interface with given method was generated from
     * using the {@link #simplify(Class)} method (i.e. this method is kind of reverse to the {@link #simplify(Class)} 
     * method).
     * 

* The returned method may or may not have different signature from the supplied method - that depends on whether * the {@link #simplify(Class)} simplified the method or not. * * @param method the potentially simplified method * @return null if the method doesn't come from a simplified class, otherwise a method on the original interface * that the supplied method was generated from. */ public static Method getOriginalMethod(Method method) { SimplifiedClass simplifiedClass = method.getDeclaringClass().getAnnotation(SimplifiedClass.class); if (simplifiedClass == null) { return null; } else { SimplifiedMethod simplifiedMethod = method.getAnnotation(SimplifiedMethod.class); Class origClass = simplifiedClass.originalClass(); if (simplifiedMethod == null) { try { return origClass.getMethod(method.getName(), method.getParameterTypes()); } catch (NoSuchMethodException e) { throw new IllegalStateException("Inconsisten interface simplification. The non-simplified method " + method + " should have had a counterpart with the exact signature on the interface " + origClass + " but it could not be found.", e); } } else { Class[] paramTypes = new Class[method.getParameterTypes().length + 1]; paramTypes[0] = Subject.class; System.arraycopy(method.getParameterTypes(), 0, paramTypes, 1, paramTypes.length - 1); try { return origClass.getMethod(method.getName(), paramTypes); } catch (NoSuchMethodException e) { throw new IllegalStateException("Inconsistent interface simplification. The simplified method " + method + " should have had a counterpart on the interface " + origClass + " but it couldn't be found.", e); } } } } /** * Determines whether given class is simplified or not. This method will return true for any class returned from * {@link #simplify(Class)}. * * @param cls the class * @return true if the class object was created by the {@link #simplify(Class)} method, false otherwise. */ public static boolean isSimplified(Class cls) { return cls.getAnnotation(SimplifiedClass.class) != null; } /** * Determines whether the method (declared on the simplified interface, i.e. * isSimplified(method.getDeclaringClass() returns true) has been "tampered with" by the simplifier or * has been left intact. *

* If you want to get the original method that the supplied method corresponds to, use * the {@link #getOriginalMethod(Method)} method. * * @param method the potentially simplified method present on a simplified class * @return true if the method's signature has been modified by the simplifier, false otherwise. */ public static boolean isSimplified(Method method) { return method.getAnnotation(SimplifiedMethod.class) != null; } public static Class simplify(Class intf) { try { ClassPool classPool = ClassPoolFactory.getClassPoolForCurrentContextClassLoader(); String simplifiedName = getSimplifiedName(intf); LOG.debug("Simplifying " + intf + " (simplified interface name: " + simplifiedName + ")..."); CtClass cached = null; try { cached = classPool.get(simplifiedName); return Class.forName(simplifiedName, false, classPool.getClassLoader()); } catch (NotFoundException e) { // ok... load it } catch (ClassNotFoundException e) { LOG.debug("Class [" + simplifiedName + "] not found - cause: " + e, e); if (cached != null) { // strange - we found the class definition in the class pool, which means we must have touched it // before but Class.forName failed to find the class in the class pool's class loader. return cached.toClass(); } } CtClass originalClass = classPool.get(intf.getName()); ClassFile originalClassFile = originalClass.getClassFile(); CtClass newClass = classPool.makeInterface(simplifiedName); newClass.defrost(); ClassFile newClassFile = newClass.getClassFile(); //we'll be adding new constants to the class file (for generics and annotations) ConstPool constPool = newClassFile.getConstPool(); //copy the annotations on the class AnnotationsAttribute annotations = (AnnotationsAttribute) originalClassFile .getAttribute(AnnotationsAttribute.visibleTag); AnnotationsAttribute newAnnotations = copyAnnotations(annotations, constPool); //add our @Simplified annotation to the new class newAnnotations = addSimplifiedClassAnnotation(originalClass.getName(), newAnnotations, constPool); newClassFile.addAttribute(newAnnotations); //copy the generic signature of the class SignatureAttribute signature = (SignatureAttribute) originalClassFile.getAttribute(SignatureAttribute.tag); if (signature != null) { newClassFile.addAttribute(new SignatureAttribute(constPool, signature.getSignature())); } //now copy over the methods CtMethod[] methods = originalClass.getMethods(); for (CtMethod originalMethod : methods) { //we are only simplifying interfaces here, but the CtClass.getMethods() also returns concrete methods //inherited from Object. Let's just skip those - we don't need to worry about them... if (!Modifier.isAbstract(originalMethod.getModifiers())) { continue; } CtClass[] params = originalMethod.getParameterTypes(); //capture all the runtime visible method annotations on the original method annotations = (AnnotationsAttribute) originalMethod.getMethodInfo().getAttribute( AnnotationsAttribute.visibleTag); //capture all the runtime visible parameter annotations on the original method ParameterAnnotationsAttribute parameterAnnotations = (ParameterAnnotationsAttribute) originalMethod .getMethodInfo().getAttribute(ParameterAnnotationsAttribute.visibleTag); //capture the generic signature of the original method. signature = (SignatureAttribute) originalMethod.getMethodInfo().getAttribute( SignatureAttribute.tag); boolean simplify = params.length > 0 && params[0].getName().equals(Subject.class.getName()); if (simplify) { //generate new params, leaving out the first parameter (the subject) CtClass[] simpleParams = new CtClass[params.length - 1]; System.arraycopy(params, 1, simpleParams, 0, params.length - 1); params = simpleParams; } //generate the new method with possibly modified parameters CtMethod newMethod = CtNewMethod.abstractMethod(originalMethod.getReturnType(), originalMethod.getName(), params, originalMethod.getExceptionTypes(), newClass); //copy over the method annotations annotations = copyAnnotations(annotations, constPool); if (simplify) { //add the @SimplifiedMethod to the method annotations annotations = addSimplifiedMethodAnnotation(annotations, constPool); if (signature != null) { //fun, we need to modify the signature, too, because we have left out the parameter MethodSignature sig = MethodSignature.parse(signature.getSignature()); sig.paramTypes.remove(0); signature = new SignatureAttribute(constPool, sig.toString()); } //next, we need to copy the parameter annotations parameterAnnotations = copyParameterAnnotations(parameterAnnotations, constPool, 1); } else { //just copy the sig and parameter annotations verbatim if (signature != null) { signature = new SignatureAttribute(constPool, signature.getSignature()); } parameterAnnotations = copyParameterAnnotations(parameterAnnotations, constPool, 0); } if (parameterAnnotations != null) { newMethod.getMethodInfo().addAttribute(parameterAnnotations); } if (signature != null) { newMethod.getMethodInfo().addAttribute(signature); } if (annotations != null) { newMethod.getMethodInfo().addAttribute(annotations); } //it is important to add the method directly to the classfile, not the class //because otherwise the generics info wouldn't survive newClassFile.addMethod(newMethod.getMethodInfo()); } return newClass.toClass(); } catch (Exception e) { String msg = "Failed to simplify " + intf + "."; LOG.error(msg, e); throw new IllegalStateException(msg, e); } } private static String getSimplifiedName(Class interfaceClass) { String fullName = interfaceClass.getName(); String simpleName = interfaceClass.getSimpleName(); Package pkg = interfaceClass.getPackage(); String packageName = (pkg != null) ? pkg.getName() : fullName.substring(0, fullName.length() - (simpleName.length() + 1)); return packageName + ".wrapped." + simpleName + "Simple"; } /** * Copies the provided annotation into the provided const pool. * * @param annotation * @param constPool * @return * @throws NotFoundException */ private static Annotation cloneAnnotation(Annotation annotation, final ConstPool constPool) throws NotFoundException { Annotation ret = new Annotation(annotation.getTypeName(), constPool); if (annotation.getMemberNames() != null) { for (Object m : annotation.getMemberNames()) { final String memberName = (String) m; MemberValue origValue = annotation.getMemberValue(memberName); final MemberValue[] newValue = new MemberValue[1]; origValue.accept(new ArrayIndexAssigningVisitor(newValue, 0, constPool)); ret.addMemberValue(memberName, newValue[0]); } } return ret; } private static AnnotationsAttribute copyAnnotations(AnnotationsAttribute annotations, ConstPool constPool) throws NotFoundException { if (annotations != null) { Annotation[] origAnnotations = annotations.getAnnotations(); Annotation[] newClassAnnotations = new Annotation[origAnnotations.length]; for (int i = 0; i < newClassAnnotations.length; ++i) { newClassAnnotations[i] = cloneAnnotation(origAnnotations[i], constPool); } AnnotationsAttribute newAnnotations = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); newAnnotations.setAnnotations(newClassAnnotations); return newAnnotations; } return null; } private static ParameterAnnotationsAttribute copyParameterAnnotations( ParameterAnnotationsAttribute parameterAnnotations, ConstPool constPool, int fromIndex) throws NotFoundException { if (parameterAnnotations != null) { Annotation[][] originalAnnotations = parameterAnnotations.getAnnotations(); //return early if there are no annotations to copy if (originalAnnotations.length - fromIndex <= 0) { return null; } Annotation[][] newParameterAnnotations = new Annotation[originalAnnotations.length - fromIndex][]; for (int i = fromIndex; i < originalAnnotations.length; i++) { newParameterAnnotations[i - fromIndex] = new Annotation[originalAnnotations[i].length]; for (int j = 0; j < originalAnnotations[i].length; ++j) { Annotation origAnnotation = originalAnnotations[i][j]; newParameterAnnotations[i - fromIndex][j] = cloneAnnotation(origAnnotation, constPool); } } ParameterAnnotationsAttribute newAnnotationsAttribute = new ParameterAnnotationsAttribute(constPool, ParameterAnnotationsAttribute.visibleTag); newAnnotationsAttribute.setAnnotations(newParameterAnnotations); return newAnnotationsAttribute; } return null; } private static class ArrayIndexAssigningVisitor implements MemberValueVisitor { private MemberValue[] array; private int index; private ConstPool constPool; public ArrayIndexAssigningVisitor(MemberValue[] array, int index, ConstPool constPool) { this.array = array; this.index = index; this.constPool = constPool; } @Override public void visitStringMemberValue(StringMemberValue node) { array[index] = new StringMemberValue(node.getValue(), constPool); } @Override public void visitShortMemberValue(ShortMemberValue node) { array[index] = new ShortMemberValue(node.getValue(), constPool); } @Override public void visitLongMemberValue(LongMemberValue node) { array[index] = new LongMemberValue(node.getValue(), constPool); } @Override public void visitIntegerMemberValue(IntegerMemberValue node) { array[index] = new IntegerMemberValue(constPool, node.getValue()); } @Override public void visitFloatMemberValue(FloatMemberValue node) { array[index] = new FloatMemberValue(node.getValue(), constPool); } @Override public void visitEnumMemberValue(EnumMemberValue node) { EnumMemberValue val = new EnumMemberValue(constPool); val.setType(node.getType()); val.setValue(node.getValue()); array[index] = val; } @Override public void visitDoubleMemberValue(DoubleMemberValue node) { array[index] = new DoubleMemberValue(node.getValue(), constPool); } @Override public void visitClassMemberValue(ClassMemberValue node) { array[index] = new ClassMemberValue(node.getValue(), constPool); } @Override public void visitCharMemberValue(CharMemberValue node) { array[index] = new CharMemberValue(node.getValue(), constPool); } @Override public void visitByteMemberValue(ByteMemberValue node) { array[index] = new ByteMemberValue(node.getValue(), constPool); } @Override public void visitBooleanMemberValue(BooleanMemberValue node) { array[index] = new BooleanMemberValue(node.getValue(), constPool); } @Override public void visitArrayMemberValue(ArrayMemberValue node) { ArrayMemberValue val = new ArrayMemberValue(node.getType(), constPool); MemberValue[] newVals = new MemberValue[node.getValue().length]; for (int i = 0; i < node.getValue().length; ++i) { node.getValue()[i].accept(new ArrayIndexAssigningVisitor(newVals, i, constPool)); } val.setValue(newVals); array[index] = val; } @Override public void visitAnnotationMemberValue(AnnotationMemberValue node) { array[index] = new AnnotationMemberValue(node.getValue(), constPool); } } //a quick and dirty method signature parser //see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4 private static class MethodSignature { public String returnType; public List paramTypes = new ArrayList(); public String typeParameters; public String exceptionTypes; public static MethodSignature parse(String signature) { int startParams = signature.indexOf('(') + 1; int endParams = signature.indexOf(')'); int startExceptions = signature.indexOf('^'); MethodSignature sig = new MethodSignature(); sig.typeParameters = signature.substring(0, startParams - 1); if (startExceptions == -1) { sig.returnType = signature.substring(endParams + 1); sig.exceptionTypes = ""; } else { sig.returnType = signature.substring(endParams + 1, startExceptions); sig.exceptionTypes = signature.substring(startExceptions); } int idx = startParams; while (idx < endParams) { int end = findEndOfTypeSignature(idx, signature); sig.paramTypes.add(signature.substring(idx, end)); idx = end; } return sig; } private static int findEndOfTypeSignature(int idx, String signature) { int c = signature.charAt(idx); switch (c) { case 'L': return findEndOfClassSignature(idx, signature); case '[': return findEndOfTypeSignature(idx + 1, signature); case 'T': return signature.indexOf(';', idx + 1) + 1; default: return idx + 1; } } private static int findEndOfClassSignature(int indexOfL, String signature) { int idx = indexOfL + 1; int genericDeclDepth = 0; while (idx < signature.length()) { boolean sigComplete = false; char c = signature.charAt(idx++); switch (c) { case '<': genericDeclDepth++; break; case '>': genericDeclDepth--; break; case ';': sigComplete = genericDeclDepth == 0; break; } if (sigComplete) { break; } } return idx; } @Override public String toString() { StringBuilder bld = new StringBuilder(typeParameters); bld.append("("); for (String p : paramTypes) { bld.append(p); } bld.append(")"); bld.append(returnType); bld.append(exceptionTypes); return bld.toString(); } } private static AnnotationsAttribute addSimplifiedClassAnnotation(String originalClassName, AnnotationsAttribute annotations, ConstPool constPool) { if (annotations == null) { annotations = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); } Annotation simplified = new Annotation(SimplifiedClass.class.getName(), constPool); simplified.addMemberValue("originalClass", new ClassMemberValue(originalClassName, constPool)); annotations.addAnnotation(simplified); return annotations; } private static AnnotationsAttribute addSimplifiedMethodAnnotation(AnnotationsAttribute annotations, ConstPool constPool) { if (annotations == null) { annotations = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); } annotations.addAnnotation(new Annotation(SimplifiedMethod.class.getName(), constPool)); return annotations; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy