/* * JBoss, Home of Professional Open Source. * Copyright 2007, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.cluster.invokerha; import java.io.Serializable; import java.lang.management.ManagementFactory; import java.rmi.server.UID; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.transaction.Transaction; import org.jboss.ha.framework.interfaces.FamilyClusterInfo; import org.jboss.ha.framework.interfaces.FirstAvailable; import org.jboss.ha.framework.interfaces.FirstAvailableIdenticalAllProxies; import org.jboss.ha.framework.interfaces.HAPartition; import org.jboss.ha.framework.interfaces.HARMIClient; import org.jboss.ha.framework.interfaces.LoadBalancePolicy; import org.jboss.ha.framework.interfaces.RandomRobin; import org.jboss.ha.framework.interfaces.RoundRobin; import org.jboss.ha.framework.server.HATarget; import org.jboss.invocation.Invocation; import org.jboss.invocation.InvocationContext; import org.jboss.invocation.Invoker; import org.jboss.invocation.InvokerHA; import org.jboss.logging.Logger; import org.jboss.system.Registry; /** * Infrastructure class that encapsulates the simulated deployment of * servers or invoker endpoints and mbeans that receive the invocations. * * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a> */ public class InvokerHaInfrastructure { private static final Logger log = Logger.getLogger(InvokerHaInfrastructure.class); /** * MBeanServer instance. This is not gonna run in Java 1.4.x, so we can * safely assume that we can use Java's MBeanServer. */ private static final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); private InvokerHaFactory invokerHaFactory; private final ObjectName dateTimeTellerON; private final ObjectName systemTimeTellerON; private final ObjectName clientUserTransactionServiceON; private final Integer dateTimeTellerONHashCode; private final Integer systemTimeTellerONHashCode; private final Integer clientUserTransactionServiceONHashCode; private final int serverCount; private List<? extends InvokerHA> replicants; private List<ObjectName> invokerONs; /** * Create a new InvokerHaInfrastructure. * * @param serverCount number of invoker endpoints to create * @param invokerHaFactory invoker ha factory class implementation */ public InvokerHaInfrastructure(int serverCount, InvokerHaFactory invokerHaFactory) { this.serverCount = serverCount; this.invokerHaFactory = invokerHaFactory; try { /* initialise ObjectNames and hashcodes */ dateTimeTellerON = new ObjectName("com.acme.mbeans:type=DateTimeTeller"); systemTimeTellerON = new ObjectName("com.acme.mbeans:type=SystemTimeTeller"); clientUserTransactionServiceON = new ObjectName("com.acme.mbeans:type=ClientUserTransactionService"); dateTimeTellerONHashCode = new Integer(dateTimeTellerON.hashCode()); systemTimeTellerONHashCode = new Integer(systemTimeTellerON.hashCode()); clientUserTransactionServiceONHashCode = new Integer(clientUserTransactionServiceON.hashCode()); /* create a list of object names for the invoker endpoints */ invokerONs = new ArrayList<ObjectName>(this.serverCount); for (int i = 0; i < serverCount; i++) { invokerONs.add(new ObjectName("com.acme.invokers:type=" + this.invokerHaFactory.getInvokerTypeName() + "-Server" + (i + 1))); } } catch (MalformedObjectNameException mone) { throw new IllegalArgumentException("invalid object name", mone); } } /** * Binds ObjectName hash code with the ObjectName which is used by the * invoker. We just do it once cos both servers are in the same VM. */ public void registerManagedObjects() { Registry.bind(dateTimeTellerONHashCode, dateTimeTellerON); Registry.bind(systemTimeTellerONHashCode, systemTimeTellerON); Registry.bind(clientUserTransactionServiceONHashCode, clientUserTransactionServiceON); } /** * Create a list of invoker ha instances, called the replicants, and * register the list with the mbean server. */ public void deployServers() throws Exception { List<InvokerHA> replicantServers = new ArrayList<InvokerHA>(serverCount); /* create n invoker instances that emulate n AS servers */ for (int i =0; i < serverCount; i++) { InvokerHA server = invokerHaFactory.createInvokerHaServer("Server", i + 1); /* add invoker as replicant */ replicantServers.add(server); mbs.registerMBean(server, invokerONs.get(i)); } replicants = replicantServers; } /** * Create new instance of DateTimeTeller and register it with the * MBeanServer. Note that a single instance is created and both server * invokers point to this instance behind the scenes. This simplifies * testing. */ public DateTimeTeller createDateTimeTeller() throws Exception { DateTimeTeller dateTimeTellerMBean = new DateTimeTeller(); mbs.registerMBean(dateTimeTellerMBean, dateTimeTellerON); return dateTimeTellerMBean; } /** * Create new instance of SystemTimeTeller and register it with the * MBeanServer. Note that a single instance is created and both server * invokers point to this instance behind the scenes. This simplifies * testing. */ public SystemTimeTeller createSystemTimeTeller() throws Exception { SystemTimeTeller systemTimeTellerMBean = new SystemTimeTeller(); mbs.registerMBean(systemTimeTellerMBean, systemTimeTellerON); return systemTimeTellerMBean; } public ClientUserTransactionService createClientUserTransactionService() throws Exception { ClientUserTransactionService clientUserTransactionServiceMBean = new ClientUserTransactionService(); mbs.registerMBean(clientUserTransactionServiceMBean, clientUserTransactionServiceON); return clientUserTransactionServiceMBean; } /** * Deploy date time teller mbean in each server, or invoker endpoint, * creating a mock HATarget with the list of replicants and associating the * mbean's object name with the HATarget instance. */ public void deployDateTimeTeller() throws Exception { deploy(replicants, dateTimeTellerON, "DateTimeTellerReplicant"); } /** * Deploy system time teller mbean in each server, or invoker endpoint, * creating a mock HATarget with the list of replicants and associating the * mbean's object name with the HATarget instance. */ public void deploySystemTimeTeller() throws Exception { deploy(replicants, systemTimeTellerON, "SystemTimeTellerReplicant"); } public void deployClientUserTransactionService() throws Exception { deploy(replicants, clientUserTransactionServiceON, "ClientUserTransactionServiceReplicant"); } /** * Create a proxy to date time teller bean. * * @param serverIndex invoker endpoint from which to return the proxy * @param policyClass load balance policy to use in the proxy * @return * @throws Exception */ public Invoker createDateTimeTellerProxy(int serverIndex, Class<? extends LoadBalancePolicy> policyClass) throws Exception { InvokerHA server = replicants.get(serverIndex); log.debug("replicants: " + replicants); return server.createProxy(dateTimeTellerON, policyClass.newInstance(), "UnitTestPartition/DateTimeTellerMBean"); } /** * Create a proxy to system time teller bean. * * @param serverIndex invoker endpoint from which to return the proxy * @param policyClass load balance policy to use in the proxy * @return * @throws Exception */ public Invoker createSystemTimeTellerProxy(int serverIndex, Class<? extends LoadBalancePolicy> policyClass) throws Exception { InvokerHA server = replicants.get(serverIndex); return server.createProxy(systemTimeTellerON, policyClass.newInstance(), "UnitTestPartition/SystemTimeTellerMBean"); } public Invoker createClientUserTransactionProxy(int serverIndex, Class<? extends LoadBalancePolicy> policyClass) throws Exception { InvokerHA server = replicants.get(serverIndex); return server.createProxy(clientUserTransactionServiceON, policyClass.newInstance(), "UnitTestPartition/ClientUserTransactionServiceMBean"); } /** * Create a new invocation for date time teller mbean * * @param tx instance of Transaction. If tx is null, transaction is not * added to invocation, which is useful to replicate transactions starting * in non managed environments. * @param failureType type of failure to inject. If null, no failure is * injected. * @return */ public Invocation createDateTimeTellerInvocation(Transaction tx, InvokerHaFailureType failureType, Invoker invoker) { return createInvocation(tx, dateTimeTellerONHashCode, failureType, invoker); } /** * Create a new invocation for system time teller mbean * * @param tx instance of Transaction. If tx is null, transaction is not * added to invocation, which is useful to replicate transactions starting * in non managed environments. * @return */ public Invocation createSystemTimeTellerInvocation(Transaction tx, InvokerHaFailureType failureType, Invoker invoker) { return createInvocation(tx, systemTimeTellerONHashCode, failureType, invoker); } public Invocation createClientUserTransactionInvocation(Transaction tx, InvokerHaFailureType failureType, Invoker invoker) { return createInvocation(tx, clientUserTransactionServiceONHashCode, failureType, invoker); } /** * Unbind mbean object name hashcodes from JMX registry. */ public void unregisterManagedObjects() { /* Unregister from the JMX registry */ Registry.unbind(dateTimeTellerONHashCode); Registry.unbind(systemTimeTellerONHashCode); Registry.unbind(clientUserTransactionServiceONHashCode); } /** * Unregister date time teller mbean object name from each invoker endpoint * and from the mbean server. */ public void undeployDateTimeTeller() throws Exception { undeploy(replicants, dateTimeTellerON); } /** * Unregister system time teller mbean object name from each invoker endpoint * and from the mbean server. */ public void undeploySystemTimeTeller() throws Exception { undeploy(replicants, systemTimeTellerON); } public void undeployClientUserTransactionService() throws Exception { undeploy(replicants, clientUserTransactionServiceON); } /** * Unregister invoker endpoints from mbean server. */ public void undeployServers() throws Exception { for (int i = 0; i < serverCount; i++) { mbs.unregisterMBean(invokerONs.get(i)); } } public List<? extends InvokerHA> getReplicants() { return replicants; } /** * Simulate the deployment of an mbean in a list of invoker endpoints. * * @param replicants list of invoker endpoints * @param targetName object name of the mbean to deploy * @param replicantName replicant name * @throws Exception */ protected void deploy(List<? extends InvokerHA> replicants, ObjectName targetName, String replicantName) throws Exception { for(InvokerHA replicant : replicants) { /* create ha-target in the server with the list of replicants and register * it with the MBeanServer. */ HATarget target = new MockHATarget(null, replicantName, null, 2); target.replicantsChanged(null, replicants, 0, false); replicant.registerBean(targetName, target); } } /** * Simulate the undeployment of an mbean from a list of invoker endpoints. * * @param replicants list of invoker endpoints * @param targetName object name of the mbean to deploy * @throws Exception */ protected void undeploy(List<? extends InvokerHA> replicants, ObjectName targetName) throws Exception { for(InvokerHA replicant : replicants) { replicant.unregisterBean(targetName); } mbs.unregisterMBean(targetName); } /** * Create invocation with optional transaction instance for the target mbean * with the given hashcode. * * @param tx instance of transaction * @param hashCode hashcode of object name of the mbean * @return */ private Invocation createInvocation(Transaction tx, Integer hashCode, InvokerHaFailureType failureType, Invoker invoker) { Invocation inv = new Invocation(); inv.setObjectName(hashCode); if (tx != null) { inv.setTransaction(tx); } if (failureType != null) { inv.setValue("FAILURE_TYPE", failureType); } InvocationContext ctx = new InvocationContext(); ctx.setInvoker(invoker); inv.setInvocationContext(ctx); return inv; } /** Interfaces **/ /** * Factory interface to be implemented by different type of invokers * available, i.e. jrmp, unified, pooled...etc. */ public interface InvokerHaFactory { /** * Returns the invoker type name * * @return String representing the invoker type */ String getInvokerTypeName(); /** * Return invoker ha instructure instance associated with this invoker. * * @param serverCount number of invocation endpoints, or simulated cluster nodes * @return InvokerHaInfrastructure instance of infrastructure */ InvokerHaInfrastructure getInvokerHaInfrastructure(int serverCount); /** * Returns the invoker ha tx failover authorisations map. * * @return WeakHashMap containing the tx failover authorisations. */ Map getTxFailoverAuthorizationsMap(); /** * Creates an instance of invoker endpoint. * * @param serverName name of invoker endpoint * @param serverNumber invoker endpoint number * @return */ InvokerHA createInvokerHaServer(String serverName, int serverNumber); /** * Returns transient payload key to retrieve chosen target. * * @return String representation of the key */ String getChosenTargetKey(); } /** * Date time teller mbean interface */ public interface DateTimeTellerMBean { Object invoke(Invocation invocation) throws Exception; } /** * Systemr time teller mbean interface */ public interface SystemTimeTellerMBean { Object invoke(Invocation invocation) throws Exception; } public interface ClientUserTransactionServiceMBean { Object invoke(Invocation invocation) throws Exception; } /** Classes **/ public class DateTimeTeller implements DateTimeTellerMBean { public Object invoke(Invocation invocation) throws Exception { /* returns a Date representation of the current time */ return new Date(System.currentTimeMillis()); } } public class SystemTimeTeller implements SystemTimeTellerMBean { public Object invoke(Invocation invocation) throws Exception { /* returns a long (milliseconds) representation of the current time */ return System.currentTimeMillis(); } } public class ClientUserTransactionService implements ClientUserTransactionServiceMBean { public Object invoke(Invocation invocation) throws Exception { // Return new tpc -> only begin() calls really tested as they set the sticky target return new UID(); } } /** * MockHATarget class that avoids using DistributedReplicantManager in any * way. The unit test will force changes in the composition of replicants, * that avoids the need of replicant manager or listener. */ public class MockHATarget extends HATarget { public MockHATarget(HAPartition partition, String replicantName, Serializable target, int allowInvocations) throws Exception { super(partition, replicantName, target, allowInvocations); } /** * No-op to avoid DistributedReplicantManager being set up at within the * super's constructor. */ @Override public void updateHAPartition(HAPartition partition) throws Exception { } } /** * Load balance policy based on a delegate pattern that tracks down the * chosen target for the invocation and puts it in the transient payload. * This allows for UTs to inspect the content and potentially check whether * load balance policies are working correctly. * * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a> */ public static class TraceLoadBalancePolicy implements LoadBalancePolicy { /** The serialVersionUID */ private static final long serialVersionUID = 3089456214843995414L; /** Load balance policy to delegate to */ private LoadBalancePolicy delegateTo; public TraceLoadBalancePolicy(LoadBalancePolicy delegateTo) { this.delegateTo = delegateTo; } public Object chooseTarget(FamilyClusterInfo clusterFamily) { return delegateTo.chooseTarget(clusterFamily); } public Object chooseTarget(FamilyClusterInfo clusterFamily, Invocation routingDecision) { Object chosenTarget = delegateTo.chooseTarget(clusterFamily, routingDecision); /* put chosen target in the transient payload */ routingDecision.getTransientPayload().put("TEST_CHOSEN_TARGET", chosenTarget); log.debug("chosen target: " + chosenTarget); return chosenTarget; } public void init(HARMIClient father) { delegateTo.init(father); } } /** * Trace load balance policy specific for round robin. This allows for non * argument construction of such load balance policy, which makes it testing * cleaner. We just pass the load balance policy class and we use reflection * to create a new instance. * * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a> */ public static class TraceRoundRobin extends TraceLoadBalancePolicy { /** The serialVersionUID */ private static final long serialVersionUID = -8583420254744619692L; public TraceRoundRobin() { super(new RoundRobin()); } } /** * Trace load balance policy specific for first available. This allows for non * argument construction of such load balance policy, which makes it testing * cleaner. We just pass the load balance policy class and we use reflection * to create a new instance. * * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a> */ public static class TraceFirstAvailable extends TraceLoadBalancePolicy { /** The serialVersionUID */ private static final long serialVersionUID = -1626190092127048933L; public TraceFirstAvailable() { super(new FirstAvailable()); } } /** * Trace load balance policy specific for first available indentical all * proxies. This allows for non argument construction of such load balance * policy, which makes it testing cleaner. We just pass the load balance * policy class and we use reflection to create a new instance. * * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a> */ public static class TraceFirstAvailableIdenticalAllProxies extends TraceLoadBalancePolicy { /** The serialVersionUID */ private static final long serialVersionUID = -8656749681577922508L; public TraceFirstAvailableIdenticalAllProxies() { super(new FirstAvailableIdenticalAllProxies()); } } /** * Trace load balance policy specific for random robin. This allows for non * argument construction of such load balance policy, which makes it testing * cleaner. We just pass the load balance policy class and we use reflection * to create a new instance. * * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a> */ public static class TraceRandomRobin extends TraceLoadBalancePolicy { /** The serialVersionUID */ private static final long serialVersionUID = -1626190092127048933L; public TraceRandomRobin() { super(new RandomRobin()); } } }