/*
* jPOS Project [http://jpos.org]
* Copyright (C) 2000-2014 Alejandro P. Revilla
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jpos.q2;
import org.jdom.Element;
import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.core.XmlConfigurable;
import org.jpos.q2.qbean.QConfig;
import org.jpos.util.LogSource;
import org.jpos.util.Logger;
import org.jpos.util.NameRegistrar;
import javax.management.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
/**
* @author <a href="mailto:taherkordy@dpi2.dpi.net.ir">Alireza Taherkordi</a>
* @author <a href="mailto:apr@cs.com.uy">Alejandro P. Revilla</a>
* @version $Revision$ $Date$
*/
@SuppressWarnings("unchecked")
public class QFactory {
ObjectName loaderName;
Q2 q2;
ResourceBundle classMapping;
ConfigurationFactory defaultConfigurationFactory = new SimpleConfigurationFactory();
public QFactory (ObjectName loaderName, Q2 q2) {
super ();
this.loaderName = loaderName;
this.q2 = q2;
classMapping = ResourceBundle.getBundle(this.getClass().getName());
}
@SuppressWarnings("PMD.EmptyCatchBlock")
public Object instantiate (Q2 server, Element e)
throws ReflectionException,
MBeanException,
InstanceNotFoundException
{
String clazz = e.getAttributeValue ("class");
if (clazz == null) {
try {
clazz = classMapping.getString (e.getName());
} catch (MissingResourceException ex) {
// no class attribute, no mapping
// let MBeanServer do the yelling
}
}
MBeanServer mserver = server.getMBeanServer();
getExtraPath (server.getLoader (), e);
return mserver.instantiate (clazz, loaderName);
}
public ObjectInstance createQBean (Q2 server, Element e, Object obj)
throws ClassNotFoundException,
InstantiationException,
IllegalAccessException,
MalformedObjectNameException,
MalformedURLException,
InstanceAlreadyExistsException,
InstanceNotFoundException,
MBeanException,
NotCompliantMBeanException,
InvalidAttributeValueException,
ReflectionException,
ConfigurationException
{
String name = e.getAttributeValue ("name");
if (name == null)
name = e.getName ();
ObjectName objectName = new ObjectName (Q2.QBEAN_NAME + name);
MBeanServer mserver = server.getMBeanServer();
if(mserver.isRegistered(objectName)) {
throw new InstanceAlreadyExistsException (name+" has already been deployed in another file.");
}
ObjectInstance instance = mserver.registerMBean (
obj, objectName
);
try {
setAttribute (mserver, objectName, "Name", name);
String logger = e.getAttributeValue ("logger");
if (logger != null)
setAttribute (mserver, objectName, "Logger", logger);
String realm = e.getAttributeValue ("realm");
if (realm != null)
setAttribute (mserver, objectName, "Realm", realm);
setAttribute (mserver, objectName, "Server", server);
setAttribute (mserver, objectName, "Persist", e);
configureQBean(mserver,objectName,e);
setConfiguration (obj, e); // handle legacy (QSP v1) Configurables
if (obj instanceof QBean)
mserver.invoke (objectName, "init", null, null);
}
catch (ConfigurationException ce) {
mserver.unregisterMBean(objectName);
ce.fillInStackTrace();
throw ce;
}
return instance;
}
public Q2 getQ2() {
return q2;
}
public void getExtraPath (QClassLoader loader, Element e) {
Element classpathElement = e.getChild ("classpath");
if (classpathElement != null) {
try {
loader = loader.scan (true); // force a new classloader
} catch (Throwable t) {
getQ2().getLog().error(t);
}
for (Object o : classpathElement.getChildren("url")) {
Element u = (Element) o;
try {
loader.addURL(u.getTextTrim());
} catch (MalformedURLException ex) {
q2.getLog().warn(u.getTextTrim(), ex);
}
}
}
}
@SuppressWarnings("PMD.EmptyCatchBlock")
public void setAttribute
(MBeanServer server, ObjectName objectName,
String attribute, Object value)
throws InstanceNotFoundException,
MBeanException,
InvalidAttributeValueException,
ReflectionException
{
try {
server.setAttribute (
objectName, new Attribute (attribute, value)
);
} catch (AttributeNotFoundException ex) {
// okay to fail
} catch (InvalidAttributeValueException ex) {
// okay to fail (produced by some application servers instead of AttributeNotFoundException)
}
}
public void startQBean (Q2 server, ObjectName objectName)
throws ClassNotFoundException,
InstantiationException,
IllegalAccessException,
MalformedObjectNameException,
MalformedURLException,
InstanceAlreadyExistsException,
InstanceNotFoundException,
MBeanException,
NotCompliantMBeanException,
InvalidAttributeValueException,
ReflectionException
{
MBeanServer mserver = server.getMBeanServer();
mserver.invoke (objectName, "start", null, null);
}
public void destroyQBean (Q2 server, ObjectName objectName, Object obj)
throws ClassNotFoundException,
InstantiationException,
IllegalAccessException,
MalformedObjectNameException,
MalformedURLException,
InstanceAlreadyExistsException,
InstanceNotFoundException,
MBeanException,
NotCompliantMBeanException,
InvalidAttributeValueException,
ReflectionException
{
MBeanServer mserver = server.getMBeanServer();
if (obj instanceof QBean) {
mserver.invoke (objectName, "stop", null, null);
mserver.invoke (objectName, "destroy", null, null);
}
if (objectName != null)
mserver.unregisterMBean (objectName);
}
public void configureQBean(MBeanServer server, ObjectName objectName, Element e)
throws ConfigurationException
{
try {
AttributeList attributeList = getAttributeList(e);
for (Object anAttributeList : attributeList)
server.setAttribute(objectName, (Attribute) anAttributeList);
} catch (Exception e1) {
throw new ConfigurationException(e1);
}
}
public AttributeList getAttributeList(Element e)
throws ConfigurationException
{
AttributeList attributeList = new AttributeList();
List childs = e.getChildren("attr");
for (Object child : childs) {
Element childElement = (Element) child;
String name = childElement.getAttributeValue("name");
name = getAttributeName(name);
Attribute attr = new Attribute(name, getObject(childElement));
attributeList.add(attr);
}
return attributeList;
}
/**
* Creates an object from a definition element.
* The element may have an attribute called type indicating the type of the object
* to create, if this attribute is not present java.lang.String is assumed.
* int, long and boolean are converted to their wrappers.
* @return The created object.
* @param childElement Dom Element with the definition of the object.
* @throws ConfigurationException If an exception is found trying to create the object.
*/
@SuppressWarnings("unchecked")
protected Object getObject(Element childElement)
throws ConfigurationException
{
String type = childElement.getAttributeValue("type","java.lang.String");
if ("int".equals (type))
type = "java.lang.Integer";
else if ("long".equals (type))
type = "java.lang.Long";
else if ("boolean".equals (type))
type = "java.lang.Boolean";
String value = childElement.getText();
try {
Class attributeType = Class.forName(type);
if(Collection.class.isAssignableFrom(attributeType))
return getCollection(attributeType, childElement);
else{
Class[] parameterTypes = {"".getClass()};
Object[] parameterValues = {value};
return attributeType.getConstructor(parameterTypes).newInstance(parameterValues);
}
} catch (Exception e1) {
throw new ConfigurationException(e1);
}
}
/** Creats a collection from a definition element with the format.
* <PRE>
* <{attr|item} type="...">
* <item [type="..."]>...</item>
* ...
* </attr>
* </PRE>
* @param type class type
* @param e the Element
* @throws ConfigurationException
* @return the object collection
*/
@SuppressWarnings("unchecked")
protected Collection getCollection(Class type, Element e)
throws ConfigurationException
{
try{
Collection<Object> col = (Collection<Object>) type.newInstance();
for (Object o : e.getChildren("item")) {
col.add(getObject((Element) o));
}
return col;
}catch(Exception e1){
throw new ConfigurationException(e1);
}
}
/**
* sets the first character of the string to the upper case
* @param name attribute name
* @return attribute name
*/
public String getAttributeName(String name)
{
StringBuilder tmp = new StringBuilder(name);
if (tmp.length() > 0)
tmp.setCharAt(0,name.toUpperCase().charAt(0)) ;
return tmp.toString();
}
public Object newInstance (String clazz)
throws ConfigurationException
{
try {
MBeanServer mserver = q2.getMBeanServer();
return mserver.instantiate (clazz, loaderName);
} catch (Exception e) {
throw new ConfigurationException (clazz, e);
}
}
public Configuration getConfiguration (Element e)
throws ConfigurationException
{
String configurationFactoryClazz = e.getAttributeValue("configuration-factory");
ConfigurationFactory cf = configurationFactoryClazz != null ?
(ConfigurationFactory) newInstance(configurationFactoryClazz) : defaultConfigurationFactory;
Configuration cfg = cf.getConfiguration(e);
String merge = e.getAttributeValue("merge-configuration");
if (merge != null) {
StringTokenizer st = new StringTokenizer(merge, ", ");
while (st.hasMoreElements()) {
try {
Configuration c = QConfig.getConfiguration(st.nextToken());
for (String k : c.keySet()) {
if (cfg.get(k, null) == null) {
String[] v = c.getAll(k);
switch (v.length) {
case 0:
break;
case 1:
cfg.put(k, v[0]);
break;
default:
cfg.put(k, v);
}
}
}
} catch (NameRegistrar.NotFoundException ex) {
throw new ConfigurationException (ex.getMessage());
}
}
}
return cfg;
}
public void setLogger (Object obj, Element e) {
if (obj instanceof LogSource) {
String loggerName = e.getAttributeValue ("logger");
if (loggerName != null) {
String realm = e.getAttributeValue ("realm");
if (realm == null)
realm = e.getName();
Logger logger = Logger.getLogger (loggerName);
((LogSource)obj).setLogger (logger, realm);
}
}
}
public void setConfiguration (Object obj, Element e)
throws ConfigurationException
{
try {
if (obj instanceof Configurable)
((Configurable)obj).setConfiguration (getConfiguration (e));
if (obj instanceof XmlConfigurable)
((XmlConfigurable)obj).setConfiguration(e);
} catch (ConfigurationException ex) {
throw new ConfigurationException (ex);
}
}
/**
* Try to invoke a method (usually a setter) on the given object
* silently ignoring if method does not exist
* @param obj the object
* @param m method to invoke
* @param p parameter
* @throws ConfigurationException if method happens to throw an exception
*/
public static void invoke (Object obj, String m, Object p)
throws ConfigurationException
{
invoke (obj, m, p, p != null ? p.getClass() : null);
}
/**
* Try to invoke a method (usually a setter) on the given object
* silently ignoring if method does not exist
* @param obj the object
* @param m method to invoke
* @param p parameter
* @param pc parameter class
* @throws ConfigurationException if method happens to throw an exception
*/
@SuppressWarnings("PMD.EmptyCatchBlock")
public static void invoke (Object obj, String m, Object p, Class pc)
throws ConfigurationException
{
try {
if (p!=null) {
Class[] paramTemplate = { pc };
Method method = obj.getClass().getMethod(m, paramTemplate);
Object[] param = new Object[1];
param[0] = p;
method.invoke (obj, param);
} else {
Method method = obj.getClass().getMethod(m);
method.invoke (obj);
}
} catch (NoSuchMethodException ignored) {
} catch (NullPointerException ignored) {
} catch (IllegalAccessException ignored) {
} catch (InvocationTargetException e) {
throw new ConfigurationException (
obj.getClass().getName() + "." + m + "(" + p +")" ,
e.getTargetException()
);
}
}
}