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 a715d26280..9098ecc6fa 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 @@ -20,7 +20,6 @@ import java.lang.instrument.ClassFileTransformer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.instrument.InstrumentationSavingAgent; @@ -31,6 +30,7 @@ import org.springframework.instrument.classloading.glassfish.GlassFishLoadTimeWe import org.springframework.instrument.classloading.jboss.JBossLoadTimeWeaver; import org.springframework.instrument.classloading.oc4j.OC4JLoadTimeWeaver; import org.springframework.instrument.classloading.weblogic.WebLogicLoadTimeWeaver; +import org.springframework.instrument.classloading.websphere.WebSphereLoadTimeWeaver; /** * Default {@link LoadTimeWeaver} bean for use in an application context, @@ -50,6 +50,7 @@ import org.springframework.instrument.classloading.weblogic.WebLogicLoadTimeWeav * * @author Juergen Hoeller * @author Ramnivas Laddad + * @author Costin Leau * @since 2.5 * @see org.springframework.context.ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME */ @@ -110,6 +111,9 @@ public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLo else if (name.startsWith("org.jboss")) { return new JBossLoadTimeWeaver(classLoader); } + else if (name.startsWith("com.ibm")) { + return new WebSphereLoadTimeWeaver(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/websphere/WebSphereClassLoaderAdapter.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java new file mode 100644 index 0000000000..234b4c77b1 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java @@ -0,0 +1,105 @@ +/* + * Copyright 2011 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.websphere; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.List; + +import org.springframework.util.Assert; + +/** + * + * Reflective wrapper around a WebSphere 7 class loader. Used to + * encapsulate the classloader-specific methods (discovered and + * called through reflection) from the load-time weaver. + * + * @author Costin Leau + */ +class WebSphereClassLoaderAdapter { + + private static final String COMPOUND_CLASS_LOADER_NAME = "com.ibm.ws.classloader.CompoundClassLoader"; + private static final String CLASS_PRE_PROCESSOR_NAME = "com.ibm.websphere.classloader.ClassLoaderInstancePreDefinePlugin"; + private static final String PLUGINS_FIELD = "preDefinePlugins"; + + private ClassLoader classLoader; + private Class wsPreProcessorClass; + private Method addPreDefinePlugin; + private Constructor cloneConstructor; + private Field transformerList; + + public WebSphereClassLoaderAdapter(ClassLoader classLoader) { + Class wsCompoundClassLoaderClass = null; + try { + wsCompoundClassLoaderClass = classLoader.loadClass(COMPOUND_CLASS_LOADER_NAME); + cloneConstructor = classLoader.getClass().getDeclaredConstructor(wsCompoundClassLoaderClass); + cloneConstructor.setAccessible(true); + + wsPreProcessorClass = classLoader.loadClass(CLASS_PRE_PROCESSOR_NAME); + addPreDefinePlugin = classLoader.getClass().getMethod("addPreDefinePlugin", wsPreProcessorClass); + transformerList = wsCompoundClassLoaderClass.getDeclaredField(PLUGINS_FIELD); + transformerList.setAccessible(true); + + } catch (Exception ex) { + throw new IllegalStateException( + "Could not initialize WebSphere LoadTimeWeaver because WebSphere 7 API classes are not available", + ex); + } + Assert.isInstanceOf(wsCompoundClassLoaderClass, classLoader, "ClassLoader must be instance of [" + + COMPOUND_CLASS_LOADER_NAME + "]"); + this.classLoader = classLoader; + } + + public ClassLoader getClassLoader() { + return this.classLoader; + } + + public void addTransformer(ClassFileTransformer transformer) { + Assert.notNull(transformer, "ClassFileTransformer must not be null"); + try { + InvocationHandler adapter = new WebSphereClassPreDefinePlugin(transformer); + Object adapterInstance = Proxy.newProxyInstance(this.wsPreProcessorClass.getClassLoader(), + new Class[] { this.wsPreProcessorClass }, adapter); + this.addPreDefinePlugin.invoke(this.classLoader, adapterInstance); + + } catch (InvocationTargetException ex) { + throw new IllegalStateException("WebSphere addPreDefinePlugin method threw exception", ex.getCause()); + } catch (Exception ex) { + throw new IllegalStateException("Could not invoke WebSphere addPreDefinePlugin method", ex); + } + } + + @SuppressWarnings("unchecked") + public ClassLoader getThrowawayClassLoader() { + try { + ClassLoader loader = (ClassLoader) cloneConstructor.newInstance(getClassLoader()); + // clear out the transformers (copied as well) + List list = (List) transformerList.get(loader); + list.clear(); + return loader; + } catch (InvocationTargetException ex) { + throw new IllegalStateException("WebSphere CompoundClassLoader constructor failed", ex.getCause()); + } catch (Exception ex) { + throw new IllegalStateException("Could not construct WebSphere CompoundClassLoader", ex); + } + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassPreDefinePlugin.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassPreDefinePlugin.java new file mode 100644 index 0000000000..5cf576d3c6 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassPreDefinePlugin.java @@ -0,0 +1,93 @@ +/* + * Copyright 2011 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.websphere; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.security.CodeSource; + +import org.springframework.util.FileCopyUtils; + +/** + * Adapter that implements WebSphere 7.0 ClassPreProcessPlugin interface, delegating to a + * standard JDK {@link ClassFileTransformer} underneath. + * + *

To avoid compile time checks again the vendor API, a dynamic proxy is + * being used. + * + * @author Costin Leau + */ +class WebSphereClassPreDefinePlugin implements InvocationHandler { + + private final ClassFileTransformer transformer; + + private class Dummy { + } + + /** + * Creates a new {@link WebSphereClassPreDefinePlugin}. + * + * @param transformer the {@link ClassFileTransformer} to be adapted (must + * not be null) + */ + public WebSphereClassPreDefinePlugin(ClassFileTransformer transformer) { + this.transformer = transformer; + ClassLoader classLoader = transformer.getClass().getClassLoader(); + + // first force the full class loading of the weaver by invoking transformation on a dummy class + try { + String dummyClass = Dummy.class.getName().replace('.', '/'); + byte[] bytes = FileCopyUtils.copyToByteArray(classLoader.getResourceAsStream(dummyClass + ".class")); + transformer.transform(classLoader, dummyClass, null, null, bytes); + } catch (Throwable ex) { + throw new IllegalArgumentException("Cannot load transformer", ex); + } + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + + if ("equals".equals(name)) { + return (Boolean.valueOf(proxy == args[0])); + } else if ("hashCode".equals(name)) { + return hashCode(); + } else if ("toString".equals(name)) { + return toString(); + } else if ("transformClass".equals(name)) { + return transform((String) args[0], (byte[]) args[1], (CodeSource) args[2], (ClassLoader) args[3]); + } else { + throw new IllegalArgumentException("Unknown method: " + method); + } + } + + public byte[] transform(String className, byte[] classfileBuffer, CodeSource codeSource, ClassLoader classLoader) + throws Exception { + // NB: WebSphere passes className as "." without class while the + // transformer expects a VM, "/" format + byte[] result = transformer.transform(classLoader, className.replace('.', '/'), null, null, classfileBuffer); + + return (result != null ? result : classfileBuffer); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getName()); + builder.append(" for transformer: "); + builder.append(this.transformer); + return builder.toString(); + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereLoadTimeWeaver.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereLoadTimeWeaver.java new file mode 100644 index 0000000000..9d51d4adb5 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereLoadTimeWeaver.java @@ -0,0 +1,68 @@ +/* + * Copyright 2011 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.websphere; + +import java.lang.instrument.ClassFileTransformer; + +import org.springframework.instrument.classloading.LoadTimeWeaver; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * {@link LoadTimeWeaver} implementation for WebSphere instrumentable classloader. + * + *

NOTE: Requires WebSphere Application Server version 7.0.0 or higher. + * + * @author Costin Leau + * @since 3.1 + */ +public class WebSphereLoadTimeWeaver implements LoadTimeWeaver { + + private final WebSphereClassLoaderAdapter classLoader; + + /** + * Create a new instance of the {@link WebSphereLoadTimeWeaver} class using + * the default {@link ClassLoader class loader}. + * @see org.springframework.util.ClassUtils#getDefaultClassLoader() + */ + public WebSphereLoadTimeWeaver() { + this(ClassUtils.getDefaultClassLoader()); + } + + /** + * Create a new instance of the {@link WebSphereLoadTimeWeaver} class using + * the supplied {@link ClassLoader}. + * @param classLoader the ClassLoader to delegate to for + * weaving (must not be null) + */ + public WebSphereLoadTimeWeaver(ClassLoader classLoader) { + Assert.notNull(classLoader, "ClassLoader must not be null"); + this.classLoader = new WebSphereClassLoaderAdapter(classLoader); + } + + public void addTransformer(ClassFileTransformer transformer) { + this.classLoader.addTransformer(transformer); + } + + public ClassLoader getInstrumentableClassLoader() { + return this.classLoader.getClassLoader(); + } + + public ClassLoader getThrowawayClassLoader() { + return this.classLoader.getThrowawayClassLoader(); + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/package-info.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/package-info.java new file mode 100644 index 0000000000..2f65915eb2 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/websphere/package-info.java @@ -0,0 +1,8 @@ + +/** + * + * Support for class instrumentation on IBM WebSphere Application Server 7. + * + */ +package org.springframework.instrument.classloading.websphere; +