/**
*
* 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.core.stateless;
import junit.framework.TestCase;
import org.apache.openejb.OpenEJB;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.assembler.classic.ProxyFactoryInfo;
import org.apache.openejb.assembler.classic.SecurityServiceInfo;
import org.apache.openejb.assembler.classic.StatelessSessionContainerInfo;
import org.apache.openejb.assembler.classic.TransactionServiceInfo;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.core.LocalInitialContextFactory;
import org.apache.openejb.core.ivm.EjbObjectInputStream;
import org.apache.openejb.core.ivm.IntraVmCopyMonitor;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.StatelessBean;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
import javax.ejb.EJBObject;
import javax.ejb.SessionContext;
import javax.naming.InitialContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
/**
* @version $Revision$ $Date$
*/
public class CrossClassLoaderProxyTest extends TestCase {
public void testBusinessLocalInterface() throws Exception {
final InitialContext ctx = new InitialContext();
final Widget widget = (Widget) ctx.lookup("WidgetBeanLocal");
// Do a business method...
final Stack<Lifecycle> lifecycle = widget.getLifecycle();
assertNotNull("lifecycle", lifecycle);
// Check the lifecycle of the bean
final List expected = Arrays.asList(Lifecycle.values());
assertEquals(join("\n", expected), join("\n", lifecycle));
}
public void testBusinessRemoteInterface() throws Exception {
final InitialContext ctx = new InitialContext();
final RemoteWidget widget = (RemoteWidget) ctx.lookup("WidgetBeanRemote");
// Do a business method...
final Stack<Lifecycle> lifecycle = widget.getLifecycle();
assertNotNull("lifecycle", lifecycle);
assertNotSame("is copy", lifecycle, WidgetBean.lifecycle);
// Check the lifecycle of the bean
final List expected = Arrays.asList(Lifecycle.values());
assertEquals(join("\n", expected), join("\n", lifecycle));
}
public void testRemoteInterface() throws Exception {
final InitialContext ctx = new InitialContext();
final EJBHome home = (EJBHome) ctx.lookup("WidgetBeanRemoteHome");
assertNotNull("home", home);
assertTrue("home should be an instance of WidgetHome", home instanceof WidgetHome);
CrossClassLoaderProxyTestObject.widgetHome = (WidgetHome) home;
final CrossClassLoaderProxyTestObject proxyTestObject = new CrossClassLoaderProxyTestObject();
proxyTestObject.testRemoteInterface();
}
public void testCrossClassLoaderRemoteInterface() throws Exception {
final HackClassLoader loader = new HackClassLoader(getClass().getClassLoader());
final ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(loader);
try {
final Class testObjectClass = loader.loadClass(CrossClassLoaderProxyTestObject.class.getName());
assertFalse(CrossClassLoaderProxyTestObject.class.equals(testObjectClass));
final Class widgetClass = (Class) testObjectClass.getField("widgetClass").get(null);
assertEquals(Widget.class, widgetClass);
final Class widgetHomeClass = (Class) testObjectClass.getField("widgetHomeClass").get(null);
assertFalse(WidgetHome.class.equals(widgetHomeClass));
final Class widgetRemoteClass = (Class) testObjectClass.getField("widgetRemoteClass").get(null);
assertFalse(WidgetRemote.class.equals(widgetRemoteClass));
final Object testObject = testObjectClass.newInstance();
final InitialContext ctx = new InitialContext();
final EJBHome rawHome = (EJBHome) ctx.lookup("WidgetBeanRemoteHome");
final EJBHome home = (EJBHome) copy(rawHome);
assertNotNull("home", home);
assertEquals(widgetHomeClass.getClassLoader(), home.getClass().getClassLoader());
assertTrue(widgetHomeClass.isAssignableFrom(home.getClass()));
testObjectClass.getField("widgetHome").set(testObject, home);
testObjectClass.getMethod("testRemoteInterface").invoke(testObject);
} finally {
Thread.currentThread().setContextClassLoader(oldClassLoader);
}
}
private static Object copy(final Object source) throws Exception {
IntraVmCopyMonitor.preCrossClassLoaderOperation();
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
final ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(source);
out.close();
final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
final ObjectInputStream in = new EjbObjectInputStream(bais);
final Object copy = in.readObject();
return copy;
} finally {
IntraVmCopyMonitor.postCrossClassLoaderOperation();
}
}
public static class HackClassLoader extends ClassLoader {
protected HackClassLoader(final ClassLoader parent) {
super(parent);
}
public Class loadClass(final String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected synchronized Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
// see if we've already loaded it
final Class c = findLoadedClass(name);
if (c != null) {
return c;
}
if (!name.equals("org.apache.openejb.core.stateless.CrossClassLoaderProxyTest$WidgetHome") &&
!name.equals("org.apache.openejb.core.stateless.CrossClassLoaderProxyTest$WidgetRemote") &&
!name.equals("org.apache.openejb.core.stateless.CrossClassLoaderProxyTestObject")) {
return super.loadClass(name, resolve);
}
final String resourceName = name.replace('.', '/') + ".class";
final InputStream in = getResourceAsStream(resourceName);
if (in == null) {
throw new ClassNotFoundException(name);
}
// 80% of class files are smaller then 6k
final ByteArrayOutputStream bout = new ByteArrayOutputStream(8 * 1024);
// copy the input stream into a byte array
final byte[] bytes;
try {
final byte[] buf = new byte[4 * 1024];
for (int count; (count = in.read(buf)) >= 0; ) {
bout.write(buf, 0, count);
}
bytes = bout.toByteArray();
} catch (final IOException e) {
throw new ClassNotFoundException(name, e);
}
// define the package
final int packageEndIndex = name.lastIndexOf('.');
if (packageEndIndex != -1) {
final String packageName = name.substring(0, packageEndIndex);
if (getPackage(packageName) == null) {
definePackage(packageName, null, null, null, null, null, null, null);
}
}
// define the class
try {
return defineClass(name, bytes, 0, bytes.length);
} catch (final SecurityException e) {
// possible prohibited package: defer to the parent
return super.loadClass(name, resolve);
}
}
public String toString() {
return "HackClassLoader";
}
}
protected void setUp() throws Exception {
super.setUp();
System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, LocalInitialContextFactory.class.getName());
final ConfigurationFactory config = new ConfigurationFactory();
final Assembler assembler = new Assembler();
assembler.createProxyFactory(config.configureService(ProxyFactoryInfo.class));
assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
assembler.createSecurityService(config.configureService(SecurityServiceInfo.class));
// containers
final StatelessSessionContainerInfo statelessContainerInfo = config.configureService(StatelessSessionContainerInfo.class);
statelessContainerInfo.properties.setProperty("TimeOut", "10");
statelessContainerInfo.properties.setProperty("MaxSize", "0");
statelessContainerInfo.properties.setProperty("StrictPooling", "false");
assembler.createContainer(statelessContainerInfo);
// Setup the descriptor information
final StatelessBean bean = new StatelessBean(WidgetBean.class);
bean.addBusinessLocal(Widget.class.getName());
bean.addBusinessRemote(RemoteWidget.class.getName());
bean.setHomeAndRemote(WidgetHome.class, WidgetRemote.class);
bean.addPostConstruct("init");
bean.addPreDestroy("destroy");
final EjbJar ejbJar = new EjbJar();
ejbJar.addEnterpriseBean(bean);
assembler.createApplication(config.configureApplication(ejbJar));
WidgetBean.lifecycle.clear();
}
@Override
protected void tearDown() throws Exception {
OpenEJB.destroy();
}
private static String join(final String delimeter, final List items) {
final StringBuilder sb = new StringBuilder();
for (final Object item : items) {
sb.append(item.toString()).append(delimeter);
}
return sb.toString();
}
public static interface Widget {
Stack<Lifecycle> getLifecycle();
}
public static interface RemoteWidget extends Widget {
}
public static interface WidgetRemote extends EJBObject {
Stack<Lifecycle> getLifecycle();
}
public static interface WidgetHome extends EJBHome {
WidgetRemote create() throws CreateException, RemoteException;
}
public static enum Lifecycle {
CONSTRUCTOR, POST_CONSTRUCT, BUSINESS_METHOD, PRE_DESTROY
}
public static class WidgetBean implements Widget, RemoteWidget {
public static Stack<Lifecycle> lifecycle = new Stack<Lifecycle>();
public WidgetBean() {
WidgetBean.lifecycle.push(Lifecycle.CONSTRUCTOR);
}
public void setSessionContext(final SessionContext sessionContext) {
//lifecycle.push(Lifecycle.INJECTION);
}
public Stack<Lifecycle> getLifecycle() {
WidgetBean.lifecycle.push(Lifecycle.BUSINESS_METHOD);
return WidgetBean.lifecycle;
}
public void init() {
WidgetBean.lifecycle.push(Lifecycle.POST_CONSTRUCT);
}
public void destroy() {
WidgetBean.lifecycle.push(Lifecycle.PRE_DESTROY);
}
}
}