/* * (c) Rob Gordon 2005. */ package org.oddjob.jmx; import java.io.File; import java.util.HashMap; import java.util.Map; import javax.management.MBeanServerConnection; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import junit.framework.TestCase; import org.apache.commons.beanutils.DynaBean; import org.apache.log4j.Logger; import org.oddjob.Oddjob; import org.oddjob.OddjobConsole; import org.oddjob.OddjobLookup; import org.oddjob.Stateful; import org.oddjob.Structural; import org.oddjob.arooa.ArooaSession; import org.oddjob.arooa.registry.BeanRegistry; import org.oddjob.arooa.registry.MockBeanRegistry; import org.oddjob.arooa.registry.SimpleBeanRegistry; import org.oddjob.arooa.standard.StandardArooaSession; import org.oddjob.arooa.xml.XMLConfiguration; import org.oddjob.jobs.WaitJob; import org.oddjob.state.IsNot; import org.oddjob.state.ParentState; import org.oddjob.state.ServiceState; import org.oddjob.state.State; import org.oddjob.state.StateConditions; import org.oddjob.structural.ChildHelper; import org.oddjob.structural.StructuralListener; import org.oddjob.tools.OddjobTestHelper; import org.oddjob.tools.StateSteps; import org.oddjob.tools.WaitForChildren; /** * */ public class JMXServerJobTest extends TestCase { private static final Logger logger = Logger.getLogger(JMXServerJobTest.class); int unique; OddjobConsole.Close close; Map<Object, String> ids = new HashMap<Object, String>(); protected void setUp() { logger.debug("================= " + getName() + " =================="); close = OddjobConsole.initialise(); } @Override protected void tearDown() throws Exception { super.tearDown(); close.close(); } private class OurEmptyRegistrySession extends StandardArooaSession { @Override public BeanRegistry getBeanRegistry() { return new MockBeanRegistry() { @Override public String getIdFor(Object component) { assertNotNull(component); String id = ids.get(component); if (id == null) { id = "x" + unique++; ids.put(component, id); } return id; } }; } } public void testServerMBeans() throws Exception { Object root = new Object() { public String toString() { return "test"; } }; JMXServerJob server = new JMXServerJob(); server.setRoot(root); server.setArooaSession(new StandardArooaSession()); server.setUrl("service:jmx:rmi://"); server.start(); JMXServiceURL address = new JMXServiceURL(server.getAddress()); JMXConnector cntor = JMXConnectorFactory.connect(address); MBeanServerConnection mBeanServer = cntor.getMBeanServerConnection(); assertEquals(new Integer(3), mBeanServer.getMBeanCount()); cntor.close(); server.stop(); } /** Test the Server job starts runs OK. */ public void testRun() throws Exception { Object root = new Object() { public String toString() { return "test"; } }; JMXServerJob server = new JMXServerJob(); server.setRoot(root); server.setArooaSession(new StandardArooaSession()); server.setUrl("service:jmx:rmi://"); server.start(); JMXClientJob client = new JMXClientJob(); client.setConnection(server.getAddress()); client.setArooaSession(new StandardArooaSession()); client.run(); Object[] children = OddjobTestHelper.getChildren(client); assertEquals(1, children.length); assertEquals("test", children[0].toString()); client.stop(); server.stop(); } public static class Component { public String getFruit() { return "apples"; } } private class OurSession extends StandardArooaSession { SimpleBeanRegistry registry = new SimpleBeanRegistry(); @Override public BeanRegistry getBeanRegistry() { return registry; } } /** Test a nested client. */ public void testLinkedServers() throws Exception { // server2 ArooaSession server2Session = new OurSession(); Component comp1 = new Component(); server2Session.getBeanRegistry().register("fred", comp1); JMXServerJob server2 = new JMXServerJob(); server2.setRoot(comp1); server2.setArooaSession(server2Session); server2.setUrl("service:jmx:rmi://"); server2.start(); // server 1 and client context OurSession server1Session = new OurSession(); // client JMXClientJob client = new JMXClientJob(); server1Session.registry.register("client", client); client.setArooaSession(server1Session); client.setConnection(server2.getAddress()); client.run(); // server1 JMXServerJob server1 = new JMXServerJob(); server1.setRoot(client); server1.setUrl("service:jmx:rmi://"); server1.setArooaSession(new OurEmptyRegistrySession()); server1.start(); Object o = server1Session.registry.lookup("client/fred"); assertNotNull(o); DynaBean db = (DynaBean) o; assertEquals("apples", db.get("fruit")); client.stop(); server1.stop(); server2.stop(); } /** Test a nested oddjob. * The sever exports and oddjob which has a node. If the oddjob has id * 'oj' and the node has id 'test' the client should be able to look * up oj/test * */ public void testNestedOddjob() throws Exception { String EOL = System.getProperty("line.separator"); final String xml = "<oddjob xmlns:jmx='http://rgordon.co.uk/oddjob/jmx'>" + EOL + " <job>" + EOL + " <sequential>" + EOL + " <jobs>" + EOL + " <jmx:server id='server' root='${oj}' url='service:jmx:rmi://'/>" + EOL + " <oddjob id='oj'>" + EOL + " <configuration>" + EOL + " <xml>" + EOL + " <oddjob>" + EOL + " <job>" + EOL + " <echo name='Test' id='echo'>Hello</echo>" + EOL + " </job>" + EOL + " </oddjob>" + EOL + " </xml>" + EOL + " </configuration>" + EOL + " </oddjob>" + EOL + " </jobs>" + EOL + " </sequential>" + EOL + " </job>" + EOL + "</oddjob>" + EOL; final Oddjob oj = new Oddjob(); oj.setConfiguration(new XMLConfiguration("XML", xml)); oj.run(); // OddjobExplorer explorer = new OddjobExplorer(); // explorer.setOddjob(oj); // explorer.run(); // sanity check what we'll find via the client. assertNotNull(new OddjobLookup(oj).lookup("oj/echo")); // client JMXClientJob client = new JMXClientJob(); client.setArooaSession(new StandardArooaSession()); client.setConnection((String) new OddjobLookup(oj).lookup("server.address")); client.run(); oj.setConfiguration(new XMLConfiguration("TEST", xml)); oj.run(); Object o = new OddjobLookup(client).lookup("oj"); assertNotNull(o); // will takes time to process child added notifications. while (new OddjobLookup(client).lookup("oj/echo") == null) { try { Thread.sleep(10000); } catch (Exception e) {} Thread.yield(); } // check we found the right thing. assertNotNull("Test", new OddjobLookup(client).lookup("oj/echo").toString()); client.stop(); oj.stop(); } private class MyFolder implements Structural { final int level; final int number; final BeanRegistry registry; MyFolder(int number, int level, BeanRegistry registry) { this.number = number; this.level = level; this.registry = registry; registry.register("x" + unique++, this); } ChildHelper<MyFolder> childHelper = new ChildHelper<MyFolder>(this); public void addStructuralListener(StructuralListener listener) { childHelper.addStructuralListener(listener); } public void removeStructuralListener(StructuralListener listener) { childHelper.removeStructuralListener(listener); } private void addChildren(final int number, final int levels, final int level) { if (levels == 0) { return; } for (int i = 0; i < number; ++i) { final MyFolder child = new MyFolder(i, level, registry); childHelper.insertChild(i, child); child.addChildren(number, levels - 1, level + 1); } } void addChildren(int number, int levels) { addChildren(number, levels, level); } void removeChildren() { for (MyFolder child : childHelper.getChildren(new MyFolder[0])) { child.removeChildren(); } childHelper.removeAllChildren(); } public String toString() { return ("[" + number + ", " + level + "]"); } } /** Big Structural Test. */ public void testLotsOfStructural() throws Exception { OurSession session = new OurSession(); MyFolder folder = new MyFolder(0, 0, session.registry); JMXServerJob server = new JMXServerJob(); server.setRoot(folder); server.setUrl("service:jmx:rmi://"); server.setArooaSession(session); server.start(); // client JMXClientJob client = new JMXClientJob(); client.setArooaSession(new StandardArooaSession()); client.setConnection(server.getAddress()); client.run(); Object proxy = ChildHelper.getChildren(client)[0]; folder.addChildren(5, 3); WaitForChildren w = new WaitForChildren(proxy); w.waitFor(5); folder.removeChildren(); w.waitFor(0); client.stop(); server.stop(); } /** Test destroying server sets client to incomplete. */ public void testDestroyServer() throws Exception { OurSession session = new OurSession(); MyFolder folder = new MyFolder(0, 0, session.registry); folder.addChildren(3, 2); logger.info("**** Starting Server ****"); JMXServerJob server = new JMXServerJob(); server.setRoot(folder); server.setUrl("service:jmx:rmi://"); server.setArooaSession(session); server.start(); logger.info("**** Starting Client ****"); // client JMXClientJob client = new JMXClientJob(); client.setArooaSession(new StandardArooaSession()); client.setConnection(server.getAddress()); client.run(); Object proxy = ChildHelper.getChildren(client)[0]; WaitForChildren w = new WaitForChildren(proxy); w.waitFor(3); logger.info("**** Stopping Server ****"); server.stop(); logger.info("**** Waiting For Client to Stop ****"); WaitJob wj = new WaitJob(); wj.setState(new IsNot(StateConditions.RUNNING)); wj.setFor(client); wj.run(); State last = client.lastStateEvent().getState(); logger.info("Client State: " + last); if (last.isException()) { Throwable e = client.lastStateEvent().getException(); if ("Server Stopped.".equals(e.getMessage())) { logger.info(e.getMessage()); } else { // Heart beat failure could happen first. Just log this. // would be nice to guarantee that it didn't but I can't // work that out. logger.info("Client Exception is:", e); } } else { fail("Unexpected state: " + client.lastStateEvent().getState()); } } // test for a bug where the server didn't clear down it's registry so // bouncing a nested Oddjob caused an 'component with that id already exists' // exception. public void testBounceOddjob() throws Exception { String EOL = System.getProperty("line.separator"); final String xml = "<oddjob xmlns:jmx='http://rgordon.co.uk/oddjob/jmx'>" + EOL + " <job>" + EOL + " <sequential>" + EOL + " <jobs>" + EOL + " <jmx:server id='server' root='${oj}'" + " url='service:jmx:rmi://'/>" + EOL + " <oddjob id='oj'>" + EOL + " <configuration>" + EOL + " <xml>" + EOL + " <oddjob>" + EOL + " <job>" + EOL + " <echo name='Test' id='echo'>Hello</echo>" + EOL + " </job>" + EOL + " </oddjob>" + EOL + " </xml>" + EOL + " </configuration>" + EOL + " </oddjob>" + EOL + " </jobs>" + EOL + " </sequential>" + EOL + " </job>" + EOL + "</oddjob>" + EOL; Oddjob oj = new Oddjob(); oj.setConfiguration(new XMLConfiguration("XML", xml)); oj.run(); Oddjob innerOddjob = (Oddjob) new OddjobLookup(oj).lookup("oj"); innerOddjob.hardReset(); innerOddjob.run(); oj.stop(); assertEquals(ParentState.COMPLETE, oj.lastStateEvent().getState()); } /** * Tracking down a problem where the server doesn't stop when oddjob * is destroyed. */ public void testServerCopesWhenItsOddjobIsDestroyed() throws Exception { File file = new File(getClass().getResource( "JMXServerJobDestroyTest.xml").getFile()); Oddjob oddjob = new Oddjob(); oddjob.setFile(file); oddjob.run(); assertEquals(ParentState.STARTED, oddjob.lastStateEvent().getState()); Stateful server = new OddjobLookup(oddjob).lookup("server", Stateful.class); StateSteps serverStates = new StateSteps(server); serverStates.startCheck(ServiceState.STARTED, ServiceState.STOPPED, ServiceState.DESTROYED); oddjob.destroy(); assertEquals(ParentState.DESTROYED, oddjob.lastStateEvent().getState()); serverStates.checkNow(); } }