/*
* 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.test.system.controller.legacy;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Constructor;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URL;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.jboss.deployment.DeploymentException;
import org.jboss.logging.Logger;
import org.jboss.mx.service.ServiceConstants;
import org.jboss.mx.util.JMXExceptionDecoder;
import org.jboss.system.ConfigurationException;
import org.jboss.util.Classes;
import org.jboss.util.StringPropertyReplacer;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* A helper class for the controller.
*
* @see org.jboss.system.Service
*
* @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
* @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
* @version $Revision: 85945 $
*/
public class OldServiceCreator
{
// Static --------------------------------------------------------
/** The default XMBean class, when one is not specified */
private static final String XMBEAN_CODE = "org.jboss.mx.modelmbean.XMBean";
/** Instance logger. */
private static final Logger log = Logger.getLogger(OldServiceCreator.class);
// Attributes ----------------------------------------------------
/** The server */
private MBeanServer server;
// Constructors --------------------------------------------------
/**
* CTOR
*
* @param server the MBeanServer
*/
public OldServiceCreator(final MBeanServer server)
{
this.server = server;
}
// Public --------------------------------------------------------
/**
* Clean shutdown
*/
public void shutdown()
{
this.server = null;
}
/**
* Parses the given configuration document and creates MBean
* instances in the current MBean server.
*
* @param mbeanName the object name
* @param loaderName the classloader
* @param mbeanElement the config
* @return the created object instance
* @throws Exception for any error
*/
public ObjectInstance install(ObjectName mbeanName, ObjectName loaderName,
Element mbeanElement) throws Exception
{
if (server.isRegistered(mbeanName))
{
throw new DeploymentException("Trying to install an already registered mbean: " + mbeanName);
}
// If class is given, instantiate it
String code = mbeanElement.getAttribute("code");
if ( code == null || "".equals(code))
{
throw new ConfigurationException("missing 'code' attribute");
}
// get the constructor params/sig to use
ConstructorInfo constructor = ConstructorInfo.create(mbeanElement);
// Check for xmbean specific attributes
String xmbeandd = null;
Attr xmbeanddAttr = mbeanElement.getAttributeNode("xmbean-dd");
if( xmbeanddAttr != null )
xmbeandd = xmbeanddAttr.getValue();
String xmbeanCode = mbeanElement.getAttribute("xmbean-code");
if( xmbeanCode.length() == 0 )
xmbeanCode = XMBEAN_CODE;
// Create the mbean instance
ObjectInstance instance = null;
try
{
if ( xmbeandd == null )
{
// Check for the explicit management interface in case of a standard MBean
Attr itfAttr = mbeanElement.getAttributeNode("interface");
if (itfAttr != null)
{
// Get the good class loader
ClassLoader classLoader = server.getClassLoader(loaderName);
// Load interface class
String itf = itfAttr.getValue();
Class itfClass = classLoader.loadClass(itf);
log.debug("About to create bean resource: " + mbeanName + " with code: " + code);
Object resource = server.instantiate(code,
loaderName,
constructor.params,
constructor.signature);
//
log.debug("About to register StandardMBean : " + mbeanName);
instance = server.createMBean("javax.management.StandardMBean",
mbeanName,
loaderName,
new Object[]{resource,itfClass},
new String[]{Object.class.getName(),Class.class.getName()});
}
else
{
// This is a standard or dynamic mbean
log.debug("About to create bean: " + mbeanName + " with code: " + code);
instance = server.createMBean(code,
mbeanName,
loaderName,
constructor.params,
constructor.signature);
}
} // end of if ()
else if( xmbeandd.length() == 0 )
{
// This is an xmbean with an embedded mbean descriptor
log.debug("About to create xmbean object: " + mbeanName
+ " with code: " + code + " with embedded descriptor");
//xmbean: construct object first.
Object resource = server.instantiate(code, loaderName,
constructor.params, constructor.signature);
NodeList mbeans = mbeanElement.getElementsByTagName("xmbean");
if( mbeans.getLength() == 0 )
throw new ConfigurationException("No nested mbean element given for xmbean");
Element mbeanDescriptor = (Element) mbeans.item(0);
Object[] args = {resource, mbeanDescriptor,
ServiceConstants.PUBLIC_JBOSSMX_XMBEAN_DTD_1_0};
String[] sig = {Object.class.getName(), Element.class.getName(),
String.class.getName()};
instance = server.createMBean(xmbeanCode,
mbeanName,
loaderName,
args,
sig);
}
else
{
// This is an xmbean with an external descriptor
log.debug("About to create xmbean object: " + mbeanName
+ " with code: " + code + " with descriptor: "+xmbeandd);
//xmbean: construct object first.
Object resource = server.instantiate(code, loaderName,
constructor.params, constructor.signature);
// Try to find the dd first as a resource then as a URL
URL xmbeanddUrl = null;
try
{
xmbeanddUrl = resource.getClass().getClassLoader().getResource(xmbeandd);
}
catch (Exception e)
{
} // end of try-catch
if (xmbeanddUrl == null)
{
xmbeanddUrl = new URL(xmbeandd);
} // end of if ()
//now create the mbean
Object[] args = {resource, xmbeanddUrl};
String[] sig = {Object.class.getName(), URL.class.getName()};
instance = server.createMBean(xmbeanCode,
mbeanName,
loaderName,
args,
sig);
} // end of else
}
catch (Throwable e)
{
Throwable newE = JMXExceptionDecoder.decode(e);
// didn't work, unregister in case the jmx agent is screwed.
try
{
server.unregisterMBean(mbeanName);
}
catch (Throwable ignore)
{
}
if (newE instanceof Exception)
{
throw (Exception)newE;
} // end of if ()
throw new UndeclaredThrowableException(newE);
}
log.debug("Created bean: "+mbeanName);
return instance;
}
public void remove(ObjectName name) throws Exception
{
// add defaut domain if there isn't one in this name
String domain = name.getDomain();
if (domain == null || "".equals(domain))
{
name = new ObjectName(server.getDefaultDomain() + name);
}
// Remove the MBean from the MBeanServer
server.unregisterMBean(name);
}
// Inner Class --------------------------------------------------
/**
* Provides a wrapper around the information about which constructor
* that MBeanServer should use to construct a MBean.
* Please note that only basic datatypes (type is then the same as
* you use to declare it "short", "int", "float" etc.) and any class
* having a constructor taking a single "String" as only parameter.
*
* <p>XML syntax for contructor:
* <pre>
* <constructor>
* <arg type="xxx" value="yyy"/>
* ...
* <arg type="xxx" value="yyy"/>
* </constructor>
* </pre>
*/
private static class ConstructorInfo
{
/** An empty parameters list. */
public static final Object EMPTY_PARAMS[] = {};
/** An signature list. */
public static final String EMPTY_SIGNATURE[] = {};
/** The constructor signature. */
public String[] signature = EMPTY_SIGNATURE;
/** The constructor parameters. */
public Object[] params = EMPTY_PARAMS;
/**
* Create a ConstructorInfo object for the given configuration.
*
* @param element The element to build info for.
* @return A constructor information object.
*
* @throws ConfigurationException Failed to create info object.
*/
public static ConstructorInfo create(Element element)
throws ConfigurationException
{
ConstructorInfo info = new ConstructorInfo();
NodeList list = element.getElementsByTagName("constructor");
if (list.getLength() > 1 && list.item(0).getParentNode() == element)
{
throw new ConfigurationException
("only one <constructor> element may be defined");
}
else if (list.getLength() == 1)
{
element = (Element)list.item(0);
// get all of the "arg" elements
list = element.getElementsByTagName("arg");
int length = list.getLength();
info.params = new Object[length];
info.signature = new String[length];
ClassLoader loader = Thread.currentThread().getContextClassLoader();
// decode the values into params & signature
for (int j=0; j<length; j++)
{
Element arg = (Element)list.item(j);
String signature = arg.getAttribute("type");
String value = arg.getAttribute("value");
// Allow for system property reference replacement
value = StringPropertyReplacer.replaceProperties(arg.getAttribute("value"));
Object realValue = value;
if( signature != null )
{
// See if it is a primitive type first
Class typeClass = Classes.getPrimitiveTypeForName(signature);
if (typeClass == null)
{
// Try to load the class
try
{
typeClass = loader.loadClass(signature);
}
catch (ClassNotFoundException e)
{
throw new ConfigurationException
("Class not found for type: " + signature, e);
}
}
// Convert the string to the real value
PropertyEditor editor = PropertyEditorManager.findEditor(typeClass);
if (editor == null)
{
try
{
// See if there is a ctor(String) for the type
Class[] sig = {String.class};
Constructor ctor = typeClass.getConstructor(sig);
Object[] args = {value};
realValue = ctor.newInstance(args);
}
catch (Exception e)
{
throw new ConfigurationException("No property editor for type: " + typeClass);
}
}
else
{
editor.setAsText(value);
realValue = editor.getValue();
}
}
info.signature[j] = signature;
info.params[j] = realValue;
}
}
return info;
}
}
}