/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, 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.management.j2ee.cluster;
import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import org.jboss.aop.microcontainer.aspects.jmx.JMX;
import org.jboss.ha.framework.interfaces.HAPartition;
import org.jboss.management.j2ee.J2EEDomain;
import org.jboss.mx.util.ObjectNameFactory;
import org.jboss.system.ServiceMBeanSupport;
/**
* This class enables a client to get a management view (JSR-77) on a
* JBoss Cluster. It contains the entire logic to map all the {@link
* org.jboss.management.j2ee.J2EEManagedObject Managed Object (MO) of
* all the nodes in the cluster to one MO. The same applies to State
* Management and Performance Statistics.
*
* @author Andreas Schaefer
* @version $Revision: 97783 $ <p>
* @jmx:mbean extends="org.jboss.system.ServiceMBean"
*/
@JMX(name = "jboss:service=HAManagement", exposedInterface = HAManagementServiceMBean.class, registerDirectly = true)
public class HAManagementService
extends ServiceMBeanSupport
implements HAManagementServiceMBean
{
// Constants -----------------------------------------------------
private final static String SERVICE_NAME = "HAManagementService";
/**
* The default object name.
*/
ObjectName OBJECT_NAME = ObjectNameFactory.create("jboss:service=HAManagement");
// Attributes ----------------------------------------------------
private HAPartition mPartition;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
/**
* Creates a undefined clustering management service. Before this
* MBean can be started the HAPartition must be up and running.
* <p/>
* ATTENTION: The current implemenation is a brute force one because
* it is retrieving the data on every hit. Later on this has to be
* resolved with a caching to avoid unecessary round-trips.
*/
public HAManagementService()
{
}
// Properties ----------------------------------------------------
public void setHAPartition(HAPartition partition)
{
this.mPartition = partition;
}
// Public --------------------------------------------------------
/**
**/
public Object _getAttribute(ObjectName pName, String pAttribute)
{
Object lReturn = null;
try
{
lReturn = server.getAttribute(pName, pAttribute);
}
catch (Exception e)
{
// Ignore them
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public Object getAttribute(ObjectName pName, String pAttribute)
throws
MBeanException,
AttributeNotFoundException,
InstanceNotFoundException,
ReflectionException,
RemoteException
{
// First we try to get the attribute from the local node
// and if not found the look for an instance out in the
// node. This does not apply for JVMs.
//AS ToDo: JVMs are not supported yet
Object lReturn = null;
try
{
lReturn = server.getAttribute(pName, pAttribute);
}
catch (InstanceNotFoundException infe)
{
// Ignore and search on the cluster
Object[] lArguments = new Object[]{
pName,
pAttribute
};
List lValues = null;
try
{
lValues = mPartition.callMethodOnCluster(SERVICE_NAME,
"_getAttribute",
lArguments,
new Class[]{ObjectName.class, String.class},
false
);
}
catch (Exception e)
{
throw new RemoteException("Could not get management attributes on the cluster", e);
}
Iterator i = lValues.iterator();
while (i.hasNext())
{
Object lValue = i.next();
if (lValue != null)
{
lReturn = lValue;
break;
}
}
if (lReturn == null)
{
// Throw the instance not found exception because there is none
throw infe;
}
}
return lReturn;
}
/**
**/
public AttributeList _getAttributes(ObjectName pName, String[] pAttributes)
{
AttributeList lReturn = null;
try
{
lReturn = server.getAttributes(pName, pAttributes);
}
catch (Exception e)
{
// Ignore them
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public AttributeList getAttributes(ObjectName pName, String[] pAttributes)
throws
InstanceNotFoundException,
ReflectionException,
RemoteException
{
// First we try to get the attribute from the local node
// and if not found the look for an instance out in the
// node. This does not apply for JVMs.
//AS ToDo: JVMs are not supported yet
//AS ToDo: Now only the first list is taken, shall we add a merge capabilities ?
AttributeList lReturn = null;
try
{
lReturn = server.getAttributes(pName, pAttributes);
}
catch (InstanceNotFoundException infe)
{
// Ignore and search on the cluster
Object[] lArguments = new Object[]{
pName,
pAttributes
};
List lValues = null;
try
{
lValues = mPartition.callMethodOnCluster(SERVICE_NAME,
"_getAttributes",
lArguments,
new Class[]{ObjectName.class, String[].class},
false
);
}
catch (Exception e)
{
throw new RemoteException("Could not get management attributes on the cluster", e);
}
Iterator i = lValues.iterator();
while (i.hasNext())
{
Object lValue = i.next();
if (lValue != null)
{
lReturn = (AttributeList) lValue;
break;
}
}
if (lReturn == null)
{
// Throw the instance not found exception because there is none
throw infe;
}
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public String getDefaultDomain()
throws RemoteException
{
return J2EEDomain.getDomainName();
}
/**
* @jmx:managed-attribute
*/
public Integer getMBeanCount()
throws RemoteException
{
try
{
return new Integer(queryNames(new ObjectName("*:*"),
null).size());
}
catch (Exception e)
{
}
return new Integer(0);
}
/**
* @jmx:managed-attribute
*/
public MBeanInfo getMBeanInfo(ObjectName pName)
throws
IntrospectionException,
InstanceNotFoundException,
ReflectionException,
RemoteException
{
return server.getMBeanInfo(pName);
}
/**
* @jmx:managed-attribute
*/
public javax.management.j2ee.ListenerRegistration getListenerRegistry()
throws RemoteException
{
return null;
/*
return new ListenerRegistration(
(ManagementHome) mContext.getEJBObject().getEJBHome(),
new String[] {}
);
*/
}
/**
**/
public Object _invoke(ObjectName pName, String pOperationName, Object[] pParams, String[] pSignature)
{
Object lReturn = null;
try
{
log.info("_invoke(), name: " + pName + ", operation: " + pOperationName +
", params: " + pParams + ", signature: " + pSignature);
lReturn = server.invoke(pName,
pOperationName,
pParams,
pSignature);
}
catch (Exception e)
{
// Return the exception instead
lReturn = e;
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public Object invoke(ObjectName pName, String pOperationName, Object[] pParams, String[] pSignature)
throws
InstanceNotFoundException,
MBeanException,
ReflectionException,
RemoteException
{
Object lReturn = null;
InstanceNotFoundException lException = null;
log.info("invoke(), name: " + pName + ", operation: " + pOperationName +
", params: " + pParams + ", signature: " + pSignature);
try
{
lReturn = server.invoke(pName, pOperationName, pParams, pSignature);
}
catch (InstanceNotFoundException infe)
{
lException = infe;
}
Object[] lArguments = new Object[]{
pName,
pOperationName,
pParams,
pSignature
};
List lValues = null;
try
{
log.info("call _invoke()");
lValues = mPartition.callMethodOnCluster(SERVICE_NAME,
"_invoke",
lArguments,
new Class[]{ObjectName.class, String.class, Object[].class, String[].class},
true
);
}
catch (Exception e)
{
throw new RemoteException("Could not get management attributes on the cluster", e);
}
Iterator i = lValues.iterator();
while (i.hasNext())
{
Object lValue = i.next();
if (lValue instanceof Throwable)
{
log.debug("invoke a method on the cluster caused an exception: " + lValue);
if (lValue instanceof InstanceNotFoundException)
{
// Go ahead when INFE is found
continue;
}
}
// A non-INFE is found therefore ignore all the other INFE
lException = null;
if (lValue != null)
{
lReturn = lValue;
break;
}
}
if (lException != null)
{
// Only if all calls throws an INFE it will throw this exception
throw lException;
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public boolean isRegistered(ObjectName pName)
throws RemoteException
{
return server.isRegistered(pName);
}
/**
* @jmx:managed-attribute
*/
public Set queryNames(ObjectName pName, QueryExp pQuery)
throws RemoteException
{
return server.queryNames(pName, pQuery);
}
/**
**/
public Object _setAttribute(ObjectName pName, Attribute pAttribute)
{
Object lReturn = null;
try
{
log.info("_setAttribute(), name: " + pName + ", attribute: " + pAttribute);
server.setAttribute(pName, pAttribute);
}
catch (Exception e)
{
// Return the exception instead
lReturn = e;
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public void setAttribute(ObjectName pName, Attribute pAttribute)
throws
AttributeNotFoundException,
InstanceNotFoundException,
InvalidAttributeValueException,
MBeanException,
ReflectionException,
RemoteException
{
InstanceNotFoundException lException = null;
try
{
server.setAttribute(pName, pAttribute);
}
catch (InstanceNotFoundException infe)
{
lException = infe; // Remember that instance was not found
}
Object[] lArguments = new Object[]{
pName,
pAttribute
};
List lValues = null;
try
{
log.info("call _setAttribute()");
lValues = mPartition.callMethodOnCluster(SERVICE_NAME,
"_setAttribute",
lArguments,
new Class[]{ObjectName.class, Attribute.class},
true
);
}
catch (Exception e)
{
throw new RemoteException("Could not set management attributes on the cluster", e);
}
Iterator i = lValues.iterator();
while (i.hasNext())
{
Object lValue = i.next();
// If one value is null (because the method does not return a value) then
// everything is fine
if (lValue instanceof Throwable)
{
log.debug("invoke a method on the cluster caused an exception: " + lValue);
if (lValue instanceof InstanceNotFoundException)
{
if (lException == null)
{
lException = (InstanceNotFoundException) lValue; // Remember this exception
}
}
else
{
// Only Throwables are returned
if (lValue instanceof AttributeNotFoundException)
{
throw (AttributeNotFoundException) lValue;
}
if (lValue instanceof InvalidAttributeValueException)
{
throw (InvalidAttributeValueException) lValue;
}
if (lValue instanceof MBeanException)
{
throw (MBeanException) lValue;
}
if (lValue instanceof ReflectionException)
{
throw (ReflectionException) lValue;
}
throw new RemoteException(lValue.toString());
}
}
}
if (lException != null)
{
throw lException; // Now this exception can be thrown because it has low priority
}
}
/**
**/
public Object _setAttributes(ObjectName pName, AttributeList pAttributes)
{
Object lReturn = null;
try
{
log.info("_setAttributes(), name: " + pName + ", attribute: " + pAttributes);
server.setAttributes(pName, pAttributes);
}
catch (Exception e)
{
// Return the exception instead
lReturn = e;
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public AttributeList setAttributes(ObjectName pName, AttributeList pAttributes)
throws
InstanceNotFoundException,
ReflectionException,
RemoteException
{
Object lReturn = null;
InstanceNotFoundException lException = null;
try
{
lReturn = server.setAttributes(pName, pAttributes);
}
catch (InstanceNotFoundException infe)
{
lException = infe;
}
Object[] lArguments = new Object[]{
pName,
pAttributes
};
List lValues = null;
try
{
log.info("call _setAttributes()");
lValues = mPartition.callMethodOnCluster(SERVICE_NAME,
"_setAttributes",
lArguments,
new Class[]{ObjectName.class, AttributeList.class},
true
);
}
catch (Exception e)
{
throw new RemoteException("Could not set management attributes on the cluster", e);
}
Iterator i = lValues.iterator();
while (i.hasNext())
{
Object lValue = i.next();
// If one value is null (because the method does not return a value) then
// everything is fine
if (lValue instanceof Throwable)
{
log.debug("set Attributes on the cluster caused an exception: " + lValue);
if (lValue instanceof InstanceNotFoundException)
{
if (lException == null)
{
lException = (InstanceNotFoundException) lValue; // Remember this exception
}
}
else
{
if (lValue instanceof ReflectionException)
{
throw (ReflectionException) lValue;
}
throw new RemoteException(lValue.toString());
}
}
}
if (lException != null)
{
// If no other exception is thrown then the INFE will be thrown here
throw lException;
}
return (AttributeList) lReturn;
}
/**
**/
public Object _createMBean(String pClass,
ObjectName pName,
Object[] pParameters,
String[] pSignature)
{
Object lReturn = null;
try
{
log.info("_createMBean(), name: " + pName);
lReturn = server.createMBean(pClass, pName, pParameters, pSignature);
}
catch (Exception e)
{
// Return the exception instead
lReturn = e;
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public ObjectInstance createMBean(String pClass,
ObjectName pName,
Object[] pParameters,
String[] pSignature)
throws
InstanceAlreadyExistsException,
MBeanException,
MBeanRegistrationException,
NotCompliantMBeanException,
ReflectionException,
RemoteException
{
List lValues = null;
Object[] lArguments = new Object[]{
pClass,
pName,
pParameters,
pSignature
};
try
{
log.info("call _createMBean()");
lValues = mPartition.callMethodOnCluster(SERVICE_NAME,
"_createMBean",
lArguments,
new Class[]{String.class, ObjectName.class, Object[].class, String[].class},
false
);
}
catch (Exception e)
{
//AS ToDo: must be checked later how to go ahead here
throw new RemoteException("Could not create a MBean on the cluster", e);
}
Iterator i = lValues.iterator();
ObjectInstance lInstance = null;
Throwable lException = null;
while (i.hasNext())
{
Object lValue = i.next();
if (lValue instanceof ObjectInstance)
{
if (lInstance == null)
{
lInstance = (ObjectInstance) lValue;
}
}
else if (lValue instanceof Throwable)
{
if (lException == null)
{
lException = (Throwable) lValue;
}
}
}
if (lException != null)
{
if (lInstance != null)
{
// Remove all existing MBeans
try
{
unregisterMBean(lInstance.getObjectName());
}
catch (Exception e)
{
// Ignore any exception
}
}
if (lException instanceof InstanceAlreadyExistsException)
{
throw (InstanceAlreadyExistsException) lException;
}
if (lException instanceof MBeanException)
{
throw (MBeanException) lException;
}
if (lException instanceof MBeanRegistrationException)
{
throw (MBeanRegistrationException) lException;
}
if (lException instanceof NotCompliantMBeanException)
{
throw (NotCompliantMBeanException) lException;
}
if (lException instanceof ReflectionException)
{
throw (ReflectionException) lException;
}
throw new RemoteException(lException.toString());
}
return lInstance;
}
/**
**/
public Object _unregisterMBean(ObjectName pName)
{
Object lReturn = null;
try
{
log.info("_unregisterMBean(), name: " + pName);
server.unregisterMBean(pName);
}
catch (Exception e)
{
// Return the exception instead
lReturn = e;
}
return lReturn;
}
/**
* @jmx:managed-attribute
*/
public void unregisterMBean(ObjectName pName)
throws
InstanceNotFoundException,
MBeanRegistrationException,
RemoteException
{
List lValues = null;
Object[] lArguments = new Object[]{
pName
};
try
{
log.info("call _unregisterMBean()");
lValues = mPartition.callMethodOnCluster(SERVICE_NAME,
"_unregisterMBean",
lArguments,
new Class[]{ObjectName.class},
false
);
}
catch (Exception e)
{
//AS ToDo: must be checked later how to go ahead here
throw new RemoteException("Could not unregister a MBean on the cluster", e);
}
Iterator i = lValues.iterator();
Throwable lException = null;
while (i.hasNext())
{
Object lValue = i.next();
if (lValue instanceof Throwable)
{
lException = (Throwable) lValue;
break;
}
}
if (lException != null)
{
if (lException instanceof InstanceNotFoundException)
{
throw (InstanceNotFoundException) lException;
}
if (lException instanceof MBeanRegistrationException)
{
throw (MBeanRegistrationException) lException;
}
throw new RemoteException(lException.toString());
}
}
/**
* @jmx:managed-attribute
*/
public void addNotificationListener(ObjectName pBroadcaster,
ObjectName pListener,
NotificationFilter pFilter,
Object pHandback)
throws
InstanceNotFoundException,
RemoteException
{
server.addNotificationListener(pBroadcaster, pListener, pFilter, pHandback);
}
/**
* @jmx:managed-attribute
*/
public void removeNotificationListener(ObjectName pBroadcaster,
ObjectName pListener)
throws
InstanceNotFoundException,
ListenerNotFoundException,
RemoteException
{
server.removeNotificationListener(pBroadcaster, pListener);
}
// MBeanRegistration implementation ----------------------------------------
/**
* Saves the MBeanServer reference
*/
public ObjectName preRegister(MBeanServer pServer, ObjectName pName)
throws Exception
{
super.preRegister(pServer, pName);
log.info("HA Management Service MBean online");
// mHAManagementName = OBJECT_NAME;
//
// return mHAManagementName;
return OBJECT_NAME;
}
/**
* Removes the Notification Listener
*/
public void preDeregister()
throws Exception
{
super.preDeregister();
}
// Service implementation ----------------------------------------
public String getName()
{
return "HA Management Service";
}
/**
* Looks up the Server Config instance to figure out the
* temp-directory and the farm-deploy-directory
*/
protected void createService() throws Exception
{
}
/**
* Register itself as RPC-Handler to the HA-Partition
* and add the farm deployment directory to the scanner
*/
protected void startService()
throws Exception
{
if (mPartition == null)
{
throw new IllegalStateException(HAPartition.class.getSimpleName() + " is null; must inject a " + HAPartition.class.getSimpleName());
}
mPartition.registerRPCHandler(SERVICE_NAME, this);
log.debug("registered RPCHandler");
}
/**
* Remove the farm deployment directory from the scanner
*/
protected void stopService()
{
}
// Protected -----------------------------------------------------
/**
* Go through the myriad of nested JMX exception to pull out the true
* exception if possible and log it.
*
* @param e The exception to be logged.
*/
private void logException(Throwable e)
{
Throwable t = org.jboss.mx.util.JMXExceptionDecoder.decode(e);
log.error("operation failed", t);
}
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
}