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 extends ClassLoader> 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;
+