package org.hotswap.agent.plugin.weld; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.net.URL; import java.util.Collection; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.CDI; import org.hotswap.agent.plugin.hotswapper.HotSwapper; import org.hotswap.agent.plugin.weld.command.BeanDeploymentArchiveAgent; import org.hotswap.agent.plugin.weld.testBeans.ChangedHelloProducer; import org.hotswap.agent.plugin.weld.testBeans.DependentHello; import org.hotswap.agent.plugin.weld.testBeans.HelloProducer; import org.hotswap.agent.plugin.weld.testBeans.HelloService; import org.hotswap.agent.plugin.weld.testBeans.HelloServiceDependant; import org.hotswap.agent.plugin.weld.testBeans.HelloServiceImpl; import org.hotswap.agent.plugin.weld.testBeans.ProxyHello; import org.hotswap.agent.plugin.weld.testBeans.ProxyHosting; import org.hotswap.agent.plugin.weld.testBeansHotswap.DependentHello2; import org.hotswap.agent.plugin.weld.testBeansHotswap.HelloProducer2; import org.hotswap.agent.plugin.weld.testBeansHotswap.HelloServiceImpl2; import org.hotswap.agent.plugin.weld.testBeansHotswap.ProxyHello2; import org.hotswap.agent.util.ReflectionHelper; import org.hotswap.agent.util.test.WaitHelper; import org.junit.Test; import org.junit.runner.RunWith; /** * Hotswap class files of weld beans. * * See maven setup for javaagent and autohotswap settings. * * @author Jiri Bubnik / modified by Vladimir Dvorak */ @RunWith(WeldJUnit4Runner.class) public class WeldPluginTest { public <T> T getBean(Class<T> beanClass) { BeanManager beanManager = CDI.current().getBeanManager(); Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(beanClass)); T result = beanManager.getContext(bean.getScope()).get(bean, beanManager.createCreationalContext(bean)); return result; } /** * Check correct setup. */ @Test public void basicTest() { assertEquals("Service:Hello", getBean(HelloService.class).hello()); assertEquals("Dependent:Service:Hello", getBean(DependentHello.class).hello()); } /** * Switch method implementation (using bean definition or interface). */ @Test public void hotswapServiceTest() throws Exception { HelloServiceImpl bean = getBean(HelloServiceImpl.class); assertEquals("Service:Hello", bean.hello()); swapClasses(HelloServiceImpl.class, HelloServiceImpl2.class.getName()); assertEquals("null:ChangedHello", bean.hello()); HelloServiceImpl.class.getMethod("initName", new Class[0]).invoke(bean, new Object[0]); assertEquals("Service2:ChangedHello", getBean(HelloServiceImpl.class).hello()); // ensure that using interface is Ok as well assertEquals("Service2:ChangedHello", getBean(HelloService.class).hello()); // return configuration swapClasses(HelloServiceImpl.class, HelloServiceImpl.class.getName()); assertEquals("Service:Hello", bean.hello()); } /** * Add new method - invoke via reflection (not available at compilation * time). */ @Test public void hotswapSeviceAddMethodTest() throws Exception { swapClasses(HelloServiceImpl.class, HelloServiceImpl2.class.getName()); String helloNewMethodIfaceVal = (String) ReflectionHelper.invoke(getBean(HelloService.class), HelloServiceImpl.class, "helloNewMethod", new Class[]{}); assertEquals("Hello from helloNewMethod", helloNewMethodIfaceVal); String helloNewMethodImplVal = (String) ReflectionHelper.invoke(getBean(HelloServiceImpl.class), HelloServiceImpl.class, "helloNewMethod", new Class[]{}); assertEquals("Hello from helloNewMethod", helloNewMethodImplVal); // return configuration swapClasses(HelloServiceImpl.class, HelloServiceImpl.class.getName()); assertEquals("Service:Hello", getBean(HelloServiceImpl.class).hello()); } @Test public void hotswapRepositoryTest() throws Exception { HelloServiceDependant bean = getBean(HelloServiceDependant.class); assertEquals("Service:Hello", bean.hello()); swapClasses(HelloProducer.class, ChangedHelloProducer.class.getName()); assertEquals("Service:ChangedHello", bean.hello()); swapClasses(HelloProducer.class, HelloProducer2.class.getName()); try{ assertEquals("Service:ChangedHello2", bean.hello()); } catch (NullPointerException npe){ System.out.println("Error: all linked beans are not updated injecton points"); System.out.println("TODO: organize cache for dependant scope and reinitialize injection points"); System.out.println("TODO: reinitialize singleton after swap dependant ????"); } assertEquals("Service:ChangedHello2", getBean(HelloServiceDependant.class).hello()); // return configuration swapClasses(HelloProducer.class, HelloProducer.class.getName()); assertEquals("Service:Hello", bean.hello()); } @Test public void hotswapRepositoryNewMethodTest() throws Exception { assertEquals("Service:Hello", getBean(HelloServiceImpl.class).hello()); swapClasses(HelloProducer.class, HelloProducer2.class.getName()); String helloNewMethodImplVal = (String) ReflectionHelper.invoke(getBean(HelloProducer.class), HelloProducer.class, "helloNewMethod", new Class[]{}); assertEquals("Hello from helloNewMethod2", helloNewMethodImplVal); // return configuration swapClasses(HelloProducer.class, HelloProducer.class.getName()); assertEquals("Service:Hello", getBean(HelloServiceImpl.class).hello()); } @Test public void hotswapPrototypeTest() throws Exception { assertEquals("Dependent:Service:Hello", getBean(DependentHello.class).hello()); // swap service this prototype is dependent to swapClasses(HelloServiceImpl.class, HelloServiceImpl2.class.getName()); assertEquals("Dependent:null:ChangedHello", getBean(DependentHello.class).hello()); HelloServiceImpl.class.getMethod("initName", new Class[0]).invoke(getBean(HelloServiceImpl.class), new Object[0]); assertEquals("Dependent:Service2:ChangedHello", getBean(DependentHello.class).hello()); // swap Inject field swapClasses(DependentHello.class, DependentHello2.class.getName()); assertEquals("Dependant2:Hello", getBean(DependentHello.class).hello()); // return configuration swapClasses(HelloServiceImpl.class, HelloServiceImpl.class.getName()); swapClasses(DependentHello.class, DependentHello.class.getName()); assertEquals("Dependent:Service:Hello", getBean(DependentHello.class).hello()); } /** * Plugin is currently unable to reload prototype bean instance. */ @Test public void hotswapPrototypeTestNotFailWhenHoldingInstanceBecauseSingletonInjectionPointWasReinitialize() throws Exception { DependentHello dependentBeanInstance = getBean(DependentHello.class); assertEquals("Dependent:Service:Hello", dependentBeanInstance.hello()); // swap service this is dependent to swapClasses(HelloServiceImpl.class, HelloServiceImpl2.class.getName()); ReflectionHelper.invoke(getBean(HelloService.class), HelloServiceImpl.class, "initName", new Class[]{}); assertEquals("Dependent:Service2:ChangedHello", dependentBeanInstance.hello()); // return configuration swapClasses(HelloServiceImpl.class, HelloServiceImpl.class.getName()); assertEquals("Dependent:Service:Hello", getBean(DependentHello.class).hello()); } @Test public void newBeanClassIsManagedBeanReRunTestOnlyAfterMvnClean() throws Exception { try { WeldPlugin.isTestEnvironment = true; Collection<BeanDeploymentArchiveAgent> instances = BeanDeploymentArchiveAgent.getInstances(); for (BeanDeploymentArchiveAgent instance : instances) { //create new class and class file. rerun test only after clean Class newClass = HotSwapper.newClass("NewClass", instance.getBdaId(), getClass().getClassLoader()); URL resource = newClass.getClassLoader().getResource("NewClass.class"); Thread.sleep(1000); // wait redefine Object bean = getBean(newClass); assertNotNull(bean); break; } } finally { WeldPlugin.isTestEnvironment = false; } } @Test public void proxyTest() throws Exception { ProxyHosting proxyHosting = getBean(ProxyHosting.class); assertEquals("ProxyHello:hello", proxyHosting.hello()); swapClasses(ProxyHello.class, ProxyHello2.class.getName()); assertEquals("ProxyHello2:hello", proxyHosting.hello()); Object proxy = proxyHosting.proxy; String hello2 = (String) ReflectionHelper.invoke(proxy, ProxyHello.class, "hello2", new Class[]{}, null); assertEquals("ProxyHello2:hello2", hello2); // return configuration swapClasses(ProxyHello.class, ProxyHello.class.getName()); assertEquals("ProxyHello:hello", proxyHosting.hello()); } private void swapClasses(Class original, String swap) throws Exception { BeanDeploymentArchiveAgent.reloadFlag = true; HotSwapper.swapClasses(original, swap); assertTrue(WaitHelper.waitForCommand(new WaitHelper.Command() { @Override public boolean result() throws Exception { return !BeanDeploymentArchiveAgent.reloadFlag; } })); // TODO do not know why sleep is needed, maybe a separate thread in weld refresh? Thread.sleep(100); } }