/* * 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.varia.deployment; import java.io.InputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.net.URL; import javax.management.Attribute; import javax.management.AttributeNotFoundException; import javax.management.IntrospectionException; import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.ObjectName; import javax.management.ReflectionException; import org.jboss.deployment.DeploymentException; import org.jboss.deployment.DeploymentInfo; import org.jboss.system.ServiceDynamicMBeanSupport; import org.jboss.util.Classes; import bsh.EvalError; import bsh.Interpreter; /** * A wrapper service that exposes a BeanShell script as a JBoss service * MBean. * * @see BeanShellSubDeployer * * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>. * @version $Revision: 81038 $ */ public class BeanShellScript extends ServiceDynamicMBeanSupport { protected DeploymentInfo deploymentInfo = null; protected String name = null; protected ScriptService scriptService = null; protected ObjectName preferedObjectName = null; protected ObjectName[] dependsServices = null; protected HashMap supportedInterfaces = new HashMap (); protected MBeanInfo mbeanInfo = null; // Static -------------------------------------------------------- // Constructors -------------------------------------------------- @Deprecated public BeanShellScript(final DeploymentInfo di) throws DeploymentException { this.deploymentInfo = di; init(deploymentInfo.url); } public BeanShellScript(final URL url) throws DeploymentException { init(url); } BeanShellScript(String name, InputStream stream) throws DeploymentException { try { this.name = name; loadScript (stream); } catch (Exception e) { throw new DeploymentException (e); } } protected void init(URL url) throws DeploymentException { try { String name = url.toString(); if (name.endsWith("/")) { name = name.substring(0, name.length() - 1); } this.name = name; loadScript (url); } catch (Exception e) { throw new DeploymentException (e); } } // Public -------------------------------------------------------- // Z implementation ---------------------------------------------- // ServiceDynamicMBeanSupport overrides -------------------------- protected Object getInternalAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { try { String action = "get" + attribute.substring(0, 1).toUpperCase() + attribute.substring(1); InvocationCouple invoc = retrieveCompatibleInvocation (action, new Class[0]); if (invoc == null) throw new AttributeNotFoundException (attribute + " getter not implemented on target script"); return invoc.method.invoke(invoc.proxy, null); } catch (ClassNotFoundException cnfe) { throw new javax.management.ReflectionException (cnfe, "A signature class couldn't be loaded"); } catch (IllegalAccessException iae) { throw new javax.management.ReflectionException (iae, "Problem while invoking gettter for field " + attribute); } catch (InvocationTargetException ite) { throw new MBeanException (ite, "Problem while invoking gettter for field " + attribute); } } protected void setInternalAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { String field = attribute.getName(); try { String action = "set" + field.substring(0, 1).toUpperCase() + field.substring(1); Object value = attribute.getValue(); Class clazz = value.getClass(); Class tmp = Classes.getPrimitive(clazz); if (tmp != null) clazz = tmp; InvocationCouple invoc = retrieveCompatibleInvocation (action, new Class[] {clazz}); if (invoc == null) throw new AttributeNotFoundException (field + " setter not implemented on target script"); invoc.method.invoke(invoc.proxy, new Object[] {value}); } catch (ClassNotFoundException cnfe) { throw new javax.management.ReflectionException (cnfe, "A signature class couldn't be loaded"); } catch (IllegalAccessException iae) { throw new javax.management.ReflectionException (iae, "Problem while invoking setter for field " + field); } catch (InvocationTargetException ite) { throw new MBeanException (ite, "Problem while invoking setter for field " + field); } } protected Object internalInvoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { try { InvocationCouple invoc = retrieveCompatibleInvocation (actionName, signature); if (invoc == null) throw new javax.management.ReflectionException (new Exception(), actionName + " not implemented on target script"); Object value = invoc.method.invoke(invoc.proxy, params); return value; } catch (ClassNotFoundException cnfe) { throw new javax.management.ReflectionException (cnfe, "A signature class couldn't be loaded"); } catch (IllegalAccessException iae) { throw new javax.management.ReflectionException (iae, "Problem while invoking " + actionName); } catch (InvocationTargetException ite) { throw new MBeanException (ite, "Problem while invoking " + actionName); } } public MBeanInfo getMBeanInfo() { return this.mbeanInfo; } // ServiceMBeanSupport overrides --------------------------------------------------- protected void createService() throws Exception { try { this.scriptService.setCtx(this); } catch (Exception e) { log.trace("setCtx", e); } try { this.scriptService.create(); } catch (EvalError e) { log.trace("start", e); } } protected void startService() throws Exception { try { this.scriptService.start(); } catch (EvalError e) { log.trace("start", e); } } protected void stopService() throws Exception { try { this.scriptService.stop(); } catch (Exception e) { log.trace("stop", e); } } protected void destroyService() throws Exception { try { this.scriptService.destroy(); } catch (Exception e) { log.trace("destroy", e); } } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- protected InvocationCouple retrieveCompatibleInvocation (String name, String[] signature) throws ClassNotFoundException { ClassLoader ucl = Thread.currentThread().getContextClassLoader(); // first transform signature // Class[] realSignature = null; if (signature != null) { realSignature = new Class[signature.length]; for (int i=0; i<signature.length;i++) realSignature[i] = ucl.loadClass(signature[i]); } return retrieveCompatibleInvocation (name, realSignature); } protected InvocationCouple retrieveCompatibleInvocation (String name, Class[] signature) throws ClassNotFoundException { Iterator keys = supportedInterfaces.keySet().iterator(); while (keys.hasNext()) { Class key = (Class)keys.next(); try { Method method = key.getMethod(name, signature); Object targetProxy = supportedInterfaces.get(key); return new InvocationCouple (targetProxy, method); } catch (NoSuchMethodException ok) {} } // if we arrive here it means that this operation does not exist! // return null; } protected void loadScript (java.net.URL url) throws Exception { InputStream stream = url.openStream(); try { loadScript(stream); } finally { try { stream.close(); } catch (IOException e) { log.info(e); } } } /** * Load script. * Stream should/must be closed/handled * by the client invoking this method. * * @param stream the stream * @throws Exception for any error */ protected void loadScript (InputStream stream) throws Exception { Interpreter interpreter = new Interpreter (); interpreter.setClassLoader(Thread.currentThread().getContextClassLoader()); interpreter.eval (new java.io.InputStreamReader (stream)); scriptService = (ScriptService)interpreter.getInterface(ScriptService.class); // We now load the script preferences // String[] depends = null; try { depends = scriptService.dependsOn(); } catch (Exception e) { log.trace("dependsOn", e); } if (depends != null) { dependsServices = new ObjectName[depends.length]; for (int i=0; i<depends.length; i++) dependsServices[i] = new ObjectName (depends[i]); } String myName = null; try { myName = scriptService.objectName (); } catch (Exception e) { log.trace("objectName", e); } if (myName != null) this.preferedObjectName = new ObjectName (myName); Class[] intfs = null; try { intfs = scriptService.getInterfaces (); if (intfs != null) log.debug("getInterfaces=" + Arrays.asList(intfs)); } catch (Exception e) { log.trace("getInterfaces", e); } if (intfs != null) { for (int i=0; i<intfs.length; i++) { Object iface = interpreter.getInterface(intfs[i]); supportedInterfaces.put (intfs[i], iface); } } this.mbeanInfo = generateMBeanInfo (intfs); log.debug("mbeanInfo=" + mbeanInfo); } protected MBeanInfo generateMBeanInfo (Class[] intfs) throws IntrospectionException { MBeanInfo result = super.getMBeanInfo(); if (intfs != null && intfs.length > 0) { ArrayList attrs = new ArrayList (Arrays.asList(result.getAttributes())); ArrayList ops = new ArrayList (Arrays.asList(result.getOperations())); HashMap readAttr = new HashMap (); HashMap writeAttr = new HashMap (); // we now populate the MBeanInfo with information from our script // for (int i=0; i<intfs.length; i++) { Class clazz = intfs[i]; Method[] methods = clazz.getMethods(); for (int m=0; m<methods.length; m++) { Method meth = methods[m]; String name = meth.getName(); Class[] params = meth.getParameterTypes(); if (name.startsWith("get") && params.length == 0) { readAttr.put (name, meth); } else if (name.startsWith("set") && params.length == 1) { writeAttr.put (name, meth); } else { ops.add(new MBeanOperationInfo ( "Method " + name + " from class/interface " + clazz.getName(), meth ) ); } } } // we now combine the getters and setters in single RW attributes // Iterator readKeys = readAttr.keySet().iterator(); while (readKeys.hasNext()) { String getter = (String)readKeys.next(); Method getterMethod = (Method)readAttr.get( getter ); String attribute = getter.substring(3); String setter = "set" + attribute; Method setterMethod = (Method)writeAttr.remove(setter); attrs.add (new MBeanAttributeInfo (attribute, "", getterMethod, setterMethod)); } // we add the remaining WO attributes // Iterator writeKeys = writeAttr.keySet().iterator(); while (writeKeys.hasNext()) { String setter = (String)writeKeys.next(); Method setterMethod = (Method)writeAttr.get( setter ); String attribute = setter.substring(3); attrs.add (new MBeanAttributeInfo (attribute, "", null, setterMethod)); } result = new MBeanInfo(this.name, "Dynamic MBean Service around BSH script " + this.name, (MBeanAttributeInfo[])attrs.toArray(new MBeanAttributeInfo[attrs.size()]), result.getConstructors(), (MBeanOperationInfo[])ops.toArray(new MBeanOperationInfo[ops.size()]), result.getNotifications()); } return result; } public ObjectName getPreferedObjectName () { return this.preferedObjectName; } public ObjectName[] getDependsServices () { return this.dependsServices; } // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- public class InvocationCouple { public Object proxy = null; public Method method = null; public InvocationCouple (Object proxy, Method m) { this.proxy = proxy; this.method = m; } } }