From 16540eaf5c1fc65d78aed333971f5f19a8d56998 Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Fri, 30 Oct 2009 17:11:26 +0000 Subject: [PATCH] added classes from contributed patch for load-time weaving in JBoss 5 (SPR-5764) git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@2232 50f2f4bb-b051-0410-bef5-90022cba6387 --- org.springframework.context/ivy.xml | 2 + org.springframework.context/pom.xml | 12 ++ .../weaving/DefaultContextLoadTimeWeaver.java | 4 + .../ClassFileTransformer2Translator.java | 50 ++++++++ .../jboss/JBoss50ClassLoader.java | 78 ++++++++++++ .../jboss/JBoss51ClassLoader.java | 37 ++++++ .../classloading/jboss/JBoss5ClassLoader.java | 120 ++++++++++++++++++ .../jboss/JBoss5LoadTimeWeaver.java | 120 ++++++++++++++++++ .../classloading/jboss/ReflectionHelper.java | 59 +++++++++ org.springframework.context/template.mf | 2 + 10 files changed, 484 insertions(+) create mode 100644 org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/ClassFileTransformer2Translator.java create mode 100644 org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss50ClassLoader.java create mode 100644 org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss51ClassLoader.java create mode 100644 org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss5ClassLoader.java create mode 100644 org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss5LoadTimeWeaver.java create mode 100644 org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/ReflectionHelper.java diff --git a/org.springframework.context/ivy.xml b/org.springframework.context/ivy.xml index b531ca58618..e6e58d9690c 100644 --- a/org.springframework.context/ivy.xml +++ b/org.springframework.context/ivy.xml @@ -46,6 +46,8 @@ + + diff --git a/org.springframework.context/pom.xml b/org.springframework.context/pom.xml index 333233bcef7..aa0e30b9f49 100644 --- a/org.springframework.context/pom.xml +++ b/org.springframework.context/pom.xml @@ -129,6 +129,18 @@ 1.6 true + + org.jboss.cl + com.springsource.org.jboss.classloader + 2.0.5.GA + provided + + + org.jboss.util + com.springsource.org.jboss.util + 2.2.13.GA + provided + org.springframework spring-asm diff --git a/org.springframework.context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java b/org.springframework.context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java index e67116c484b..78047eebafe 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java +++ b/org.springframework.context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java @@ -27,6 +27,7 @@ import org.springframework.instrument.InstrumentationSavingAgent; import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver; import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver; +import org.springframework.instrument.classloading.jboss.JBoss5LoadTimeWeaver; import org.springframework.instrument.classloading.glassfish.GlassFishLoadTimeWeaver; import org.springframework.instrument.classloading.oc4j.OC4JLoadTimeWeaver; import org.springframework.instrument.classloading.weblogic.WebLogicLoadTimeWeaver; @@ -105,6 +106,9 @@ public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLo else if (classLoader.getClass().getName().startsWith("com.sun.enterprise")) { return new GlassFishLoadTimeWeaver(classLoader); } + else if (classLoader.getClass().getName().startsWith("org.jboss")) { + return new JBoss5LoadTimeWeaver(classLoader); + } } catch (IllegalStateException ex) { logger.info("Could not obtain server-specific LoadTimeWeaver: " + ex.getMessage()); diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/ClassFileTransformer2Translator.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/ClassFileTransformer2Translator.java new file mode 100644 index 00000000000..b7a139cef58 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/ClassFileTransformer2Translator.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.instrument.classloading.jboss; + +import java.security.ProtectionDomain; +import java.lang.instrument.ClassFileTransformer; + +import org.jboss.util.loading.Translator; + +/** + * ClassFileTransfomer to Translator bridge. + * + * @author Ales Justin + */ +public class ClassFileTransformer2Translator implements Translator { + + private ClassFileTransformer transformer; + + public ClassFileTransformer2Translator(ClassFileTransformer transformer) { + if (transformer == null) { + throw new IllegalArgumentException("Null transformer"); + } + + this.transformer = transformer; + } + + public byte[] transform(ClassLoader loader, + String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) throws Exception { + return transformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); + } + + public void unregisterClassLoader(ClassLoader loader) { + } +} diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss50ClassLoader.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss50ClassLoader.java new file mode 100644 index 00000000000..e520154c6bc --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss50ClassLoader.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.instrument.classloading.jboss; + +import java.lang.reflect.Method; + +import org.jboss.classloader.spi.ClassLoaderSystem; +import org.jboss.classloader.spi.base.BaseClassLoader; +import org.jboss.classloader.spi.base.BaseClassLoaderDomain; +import org.jboss.classloader.spi.base.BaseClassLoaderPolicy; +import org.jboss.classloader.spi.base.BaseClassLoaderSystem; +import org.jboss.util.loading.Translator; + +/** + * Reflective wrapper around a JBoss_5.0.x class loader. Used to encapsulate the classloader-specific methods + * (discovered and called through reflection) from the load-time weaver. + * + * @author Ales Justin + */ +public class JBoss50ClassLoader extends JBoss5ClassLoader { + + private Method addTranslator; + + private ClassLoaderSystem system; + + public JBoss50ClassLoader(BaseClassLoader classLoader) { + super(classLoader); + } + + protected void fallbackStrategy() throws Exception { + try { + // let's check if we have a patched policy, with translator per policy + addTranslator = getMethod(BaseClassLoaderPolicy.class, "addTranslator"); + } + catch (Exception ignored) { + //log.info("Policy doesn't have addTranslator, falling back to ClassLoaderSystem."); + + Method getClassLoaderDomain = getMethod(BaseClassLoaderPolicy.class, "getClassLoaderDomain"); + BaseClassLoaderDomain domain = invokeMethod(getClassLoaderDomain, getPolicy(), BaseClassLoaderDomain.class); + Method getClassLoaderSystem = getMethod(BaseClassLoaderDomain.class, "getClassLoaderSystem"); + BaseClassLoaderSystem system = invokeMethod(getClassLoaderSystem, domain, BaseClassLoaderSystem.class); + if (system instanceof ClassLoaderSystem) { + this.system = ClassLoaderSystem.class.cast(system); + } + else { + throw new IllegalArgumentException( + "ClassLoaderSystem must be instance of [" + ClassLoaderSystem.class.getName() + "]"); + } + } + } + + protected void addTranslator(Translator translator) { + if (addTranslator != null) { + try { + addTranslator.invoke(translator); + } + catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + else { + system.setTranslator(translator); + } + } +} diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss51ClassLoader.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss51ClassLoader.java new file mode 100644 index 00000000000..a792c427248 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss51ClassLoader.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.instrument.classloading.jboss; + +import org.jboss.classloader.spi.base.BaseClassLoader; +import org.jboss.util.loading.Translator; + +/** + * Reflective wrapper around a JBoss_5.1.x class loader. Used to encapsulate the classloader-specific methods + * (discovered and called through reflection) from the load-time weaver. + * + * @author Ales Justin + */ +public class JBoss51ClassLoader extends JBoss5ClassLoader { + + public JBoss51ClassLoader(BaseClassLoader classLoader) { + super(classLoader); + } + + protected void addTranslator(Translator translator) { + + getPolicy().addTranslator(translator); + } +} diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss5ClassLoader.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss5ClassLoader.java new file mode 100644 index 00000000000..3bea1c9fae9 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss5ClassLoader.java @@ -0,0 +1,120 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.instrument.classloading.jboss; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; + +import org.jboss.classloader.spi.ClassLoaderPolicy; +import org.jboss.classloader.spi.base.BaseClassLoader; +import org.jboss.util.loading.Translator; + +import org.springframework.util.Assert; +import org.springframework.instrument.classloading.SimpleThrowawayClassLoader; + +/** + * Reflective wrapper around a JBoss5 class loader. Used to encapsulate the classloader-specific methods (discovered and + * called through reflection) from the load-time weaver. + * + * @author Ales Justin + * @author Marius Bogoevici + */ +public abstract class JBoss5ClassLoader extends ReflectionHelper { + + private final BaseClassLoader classLoader; + + private ClassLoaderPolicy policy; + + @SuppressWarnings("unchecked") + protected JBoss5ClassLoader(BaseClassLoader classLoader) { + Assert.notNull(classLoader, "ClassLoader must not be null"); + this.classLoader = classLoader; + + try { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + AccessController.doPrivileged(new InstantiationAction()); + } + else { + doInstantiate(); + } + } + catch (Exception e) { + throw new IllegalStateException( + "Could not initialize JBoss ClassLoader because JBoss5 API classes are not available", e); + } + } + + /** + * Get the policy. + * + * @return the policy + */ + protected ClassLoaderPolicy getPolicy() { + return policy; + } + + /** + * Do instantiate method, variables. + * + * @throws Exception for any error + */ + private void doInstantiate() throws Exception { + Method getPolicy = getMethod(BaseClassLoader.class, "getPolicy"); + policy = invokeMethod(getPolicy, classLoader, ClassLoaderPolicy.class); + fallbackStrategy(); + } + + /** + * The fallback strategy. + * + * @throws Exception for any error + */ + protected void fallbackStrategy() throws Exception { + } + + public void addTransformer(ClassFileTransformer transformer) { + Assert.notNull(transformer, "ClassFileTransformer must not be null"); + Translator translator = new ClassFileTransformer2Translator(transformer); + addTranslator(translator); + } + + /** + * Add the translator. + * + * @param translator the translator + */ + protected abstract void addTranslator(Translator translator); + + public ClassLoader getInternalClassLoader() { + return classLoader; + } + + public ClassLoader getThrowawayClassLoader() { + return new SimpleThrowawayClassLoader(classLoader); + } + + /** Instantiation action. */ + private class InstantiationAction implements PrivilegedExceptionAction { + + public Object run() throws Exception { + doInstantiate(); + return null; + } + } +} diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss5LoadTimeWeaver.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss5LoadTimeWeaver.java new file mode 100644 index 00000000000..6be70ce4633 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBoss5LoadTimeWeaver.java @@ -0,0 +1,120 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.instrument.classloading.jboss; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.reflect.Method; + +import org.jboss.classloader.spi.base.BaseClassLoader; + +import org.springframework.instrument.classloading.LoadTimeWeaver; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * {@link LoadTimeWeaver} implementation for JBoss5's instrumentable ClassLoader. + * + * @author Ales Justin + */ +public class JBoss5LoadTimeWeaver extends ReflectionHelper implements LoadTimeWeaver { + + private JBoss5ClassLoader classLoader; + + public JBoss5LoadTimeWeaver() { + this(ClassUtils.getDefaultClassLoader()); + } + + public JBoss5LoadTimeWeaver(ClassLoader classLoader) { + Assert.notNull(classLoader, "ClassLoader must not be null"); + BaseClassLoader bcl = determineClassLoader(classLoader); + if (bcl == null) { + throw new IllegalArgumentException( + classLoader + " and its parents are not suitable ClassLoaders: " + "An [" + + BaseClassLoader.class.getName() + "] implementation is required."); + } + this.classLoader = createClassLoaderWrapper(bcl); + } + + /** + * Create a JBoss5 classloader wrapper based on the underlying JBossAS version. + * + * @param bcl the base classloader + * @return new JBoss5 classloader wrapper + */ + protected JBoss5ClassLoader createClassLoaderWrapper(BaseClassLoader bcl) { + int versionNumber = 0; + String tag; + + try { + // BCL should see Version class + Class versionClass = bcl.loadClass("org.jboss.Version"); + Method getInstance = getMethod(versionClass, "getInstance"); + Object version = getInstance.invoke(null); // static method + + Method getMajor = getMethod(versionClass, "getMajor"); + versionNumber += 100 * invokeMethod(getMajor, version, Integer.class); + Method getMinor = getMethod(versionClass, "getMinor"); + versionNumber += 10 * invokeMethod(getMinor, version, Integer.class); + Method getRevision = getMethod(versionClass, "getRevision"); + versionNumber += invokeMethod(getRevision, version, Integer.class); + Method getTag = getMethod(versionClass, "getTag"); + tag = invokeMethod(getTag, version, String.class); + } + catch (Exception e) { + //log.warn("Exception creating JBoss5 CL wrapper: " + e + ", falling back to JBoss50ClassLoader wrapper."); + return new JBoss50ClassLoader(bcl); + } + + if (versionNumber < 500) // this only works on new MC code + { + throw new IllegalArgumentException( + "JBoss5LoadTimeWeaver can only be used on new JBoss Microcontainer ClassLoader."); + } + else if (versionNumber <= 501 || (versionNumber == 510 && "Beta1".equals(tag))) { + return new JBoss50ClassLoader(bcl); + } + else { + return new JBoss51ClassLoader(bcl); + } + } + + /** + * Find first BaseClassLoader implementation. + * + * @param classLoader the classloader + * @return BaseClassLoader instance or null if not found + */ + private BaseClassLoader determineClassLoader(ClassLoader classLoader) { + for (ClassLoader cl = classLoader; cl != null; cl = cl.getParent()) { + if (cl instanceof BaseClassLoader) { + return (BaseClassLoader) cl; + } + } + return null; + } + + public void addTransformer(ClassFileTransformer transformer) { + classLoader.addTransformer(transformer); + } + + public ClassLoader getInstrumentableClassLoader() { + return classLoader.getInternalClassLoader(); + } + + public ClassLoader getThrowawayClassLoader() { + return classLoader.getThrowawayClassLoader(); + } +} diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/ReflectionHelper.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/ReflectionHelper.java new file mode 100644 index 00000000000..a882bbdc2df --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/ReflectionHelper.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.instrument.classloading.jboss; + +import java.lang.reflect.Method; + +/** + * Reflection helper. + * + * @author Ales Justin + */ +public abstract class ReflectionHelper { + + /** + * Get method from class. + * + * @param clazz the owner class + * @param name the method name + * @return declared method + * @throws Exception for any error + */ + protected static Method getMethod(Class clazz, String name) throws Exception { + Method method = clazz.getDeclaredMethod(name); + method.setAccessible(true); + return method; + } + + /** + * Invoke method and check the result. + * + * @param method the method + * @param target the target + * @param expectedType the expected type + * @param the exact type + * @return invocation's result + * @throws Exception for any error + */ + protected static T invokeMethod(Method method, Object target, Class expectedType) throws Exception { + Object result = method.invoke(target); + if (expectedType.isInstance(result) == false) { + throw new IllegalArgumentException("Returned result must be instance of [" + expectedType.getName() + "]"); + } + + return expectedType.cast(result); + } +} diff --git a/org.springframework.context/template.mf b/org.springframework.context/template.mf index 24eaeaf47d4..99980952a81 100644 --- a/org.springframework.context/template.mf +++ b/org.springframework.context/template.mf @@ -30,6 +30,8 @@ Import-Template: org.aspectj.weaver.*;version="[1.5.4, 2.0.0)";resolution:=optional, org.codehaus.groovy.*;version="[1.5.0, 2.0.0)";resolution:=optional, org.joda.*;version="[1.6.0, 2.0.0)";resolution:=optional, + org.jboss.classloader.*;version="[2.0.5, 3.0.0)";resolution:=optional, + org.jboss.util.*;version="[2.2.13, 3.0.0)";resolution:=optional, org.jruby.*;version="[1.1.0, 2.0.0)";resolution:=optional, org.omg.CORBA.*;version="0";resolution:=optional, org.springframework.aop.*;version="[3.0.0, 3.0.1)";resolution:=optional,