larryi 2002/09/29 19:17:43 Added: proposals/JmxSupport build.xml proposals/JmxSupport/WEB-INF interceptors.xml proposals/JmxSupport/WEB-INF/classes/org/apache/tomcat/modules/config DynamicMBeanProxy.java MxInterceptor.java Log: Files for JmxSupport add-on. Derived from original MxInterceptor.java and DynamicMBeanProxy.java files. Revision Changes Path 1.1 jakarta-tomcat/proposals/JmxSupport/build.xml Index: build.xml =================================================================== <?xml version="1.0"?> <project name="PasswordPrompter" default="main" basedir="."> <!-- Compilation properties --> <property name="optimize" value="false"/> <property name="debug" value="on"/> <!-- Directories --> <property name="ws" value="../../.."/> <property name="work.dir" value="${ws}/jakarta-tomcat"/> <property name="tomcat.build" value="${ws}/jakarta-tomcat/build/tomcat"/> <property name="tomcat.dist" value="${ws}/jakarta-tomcat/dist/tomcat"/> <property name="tomcat.build.modules" value="${tomcat.build}/modules"/> <property name="tomcat.dist.modules" value="${tomcat.dist}/modules"/> <property name="commons-logging.jar" location="${tomcat.build}/lib/common/commons-logging.jar" /> <property name="jmx.jar" location="${jakarta-tomcat-connectors}/lib/mx4j.jar"/> <property name="jmxtools.jar" location="${jakarta-tomcat-connectors}/lib/mx4j-tools.jar"/> <property name="tomcat.classes.dir" location="${tomcat.build}/classes"/> <property name="tomcat.classes" value="${tomcat.classes.dir}"/> <!-- ==================== Initialization - check if build and dist present ========== --> <target name="init"> <available property="build.modules.available" file="${tomcat.build.modules}"/> <available property="dist.modules.available" file="${tomcat.dist.modules}"/> <available property="jmx-present" file="${jmx.jar}" /> <available property="jmxtools-present" file="${jmxtools.jar}" /> </target> <!-- ==================== Preparation ==================== --> <target name="prepare" depends="init"> </target> <target name="build.modules.check" depends="prepare" unless="build.modules.available"> <echo message="You must build Tomcat first!"/> </target> <target name="dist.modules.check" depends="prepare" unless="dist.modules.available"> <echo message="You must build the Tomcat distribution first!"/> </target> <!-- ==================== Build the Password Prompter sample add-on ================== --> <target name="jmx.support" depends="prepare" if="build.modules.available"> <mkdir dir="${tomcat.build.modules}/JmxSupport"/> <copy todir="${tomcat.build.modules}/JmxSupport"> <fileset dir="${work.dir}/proposals/JmxSupport" excludes="build.xml"/> </copy> <javac srcdir="${tomcat.build.modules}/JmxSupport/WEB-INF/classes" optimize="${optimize}" debug="${debug}" destdir="${tomcat.build.modules}/JmxSupport/WEB-INF/classes"> <classpath path="${tomcat.classes}"/> <classpath location="${jmx.jar}"/> <classpath location="${commons-logging.jar}"/> </javac> <mkdir dir="${tomcat.build.modules}/JmxSupport/WEB-INF/lib"/> <copy todir="${tomcat.build.modules}/JmxSupport/WEB-INF/lib" file="${jmx.jar}"/> <copy todir="${tomcat.build.modules}/JmxSupport/WEB-INF/lib" file="${jmxtools.jar}"/> <copy todir="${tomcat.build.modules}/JmxSupport/WEB-INF/lib" file="${commons-logging.jar}"/> <jar jarFile="${tomcat.build.modules}/JmxSupport/WEB-INF/lib/JmxSupport.jar" basedir="${tomcat.build.modules}/JmxSupport/WEB-INF/classes"/> <delete dir="${tomcat.build.modules}/JmxSupport/WEB-INF/classes"/> </target> <!-- ==================== Build the Password Prompter sample add-on WAR ================== --> <target name="jmx.support.war" depends="build.modules.check,dist.modules.check,jmx.support" if="dist.modules.available"> <jar jarFile="${tomcat.dist.modules}/JmxSupport.war" baseDir="${tomcat.build.modules}/JmxSupport"/> </target> <!-- ==================== Admin & agreagate ==================== --> <target name="clean" depends="init"> <delete dir="${tomcat.build.modules}/JmxSupport"/> <delete dir="${tomcat.dist.modules}/JmxSupport"/> <delete file="${tomcat.dist.modules}/JmxSupport.war"/> </target> <target name="main" depends="build.modules.check,jmx.support"/> </project> 1.1 jakarta-tomcat/proposals/JmxSupport/WEB-INF/interceptors.xml Index: interceptors.xml =================================================================== <?xml version="1.0" encoding="UTF-8"?> <Server> <module name="MxInterceptor" javaClass="org.apache.tomcat.modules.config.MxInterceptor" /> <ContextManager> <!-- MxInterceptor to enable JMX monitoring. options are : port : http adaptor will listen to this port If you're using the MX4J HTTP Adaptor, you could also set host and authentication : host : the MX4J http adaptor will listen to this IP/host authentication: the MX4J http adaptor will use HTTP authentication method which could be (none, basic, digest) user: set the user to be entered when asking for user/password in HTTP authentication. password : define the password for HTTP authentication Note, if you're using a JMX console, you don't have to use the http adaptor --> <MxInterceptor port="8999" authentication="basic" user="admin" password="changeit" /> </ContextManager> </Server> 1.1 jakarta-tomcat/proposals/JmxSupport/WEB-INF/classes/org/apache/tomcat/modules/config/DynamicMBeanProxy.java Index: DynamicMBeanProxy.java =================================================================== /* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.tomcat.modules.config; import java.io.*; import java.net.*; import java.lang.reflect.*; import java.util.*; import javax.management.*; /** * DynamicMBean implementation using introspection to manage any * component that follows the bean/ant/Interceptor/Valve/Jk2 patterns. * * The class will wrap any component conforming to those patterns. * * @author Costin Manolache */ public class DynamicMBeanProxy implements DynamicMBean { Object real; String name; Method methods[]=null; Hashtable attMap=new Hashtable(); // key: attribute val: getter method Hashtable getAttMap=new Hashtable(); // key: attribute val: setter method Hashtable setAttMap=new Hashtable(); // key: operation val: invoke method Hashtable invokeAttMap=new Hashtable(); static MBeanServer mserver=null; static Hashtable instances=new Hashtable(); /** Create a Dynamic proxy, using introspection to manage a * real tomcat component. */ public DynamicMBeanProxy() { } public void setName(String name ) { this.name=name; } public String getName() { if( name!=null ) return name; if( real==null ) return null; name=generateName(real.getClass()); return name; } /** If a name was not provided, generate a name based on the * class name and a sequence number. */ public static String generateName(Class realClass) { String name=realClass.getName(); name=name.substring( name.lastIndexOf( ".") + 1 ); Integer iInt=(Integer)instances.get(name ); int seq=0; if( iInt!= null ) { seq=iInt.intValue(); seq++; instances.put( name, new Integer( seq )); } else { instances.put( name, new Integer( 0 )); } return "name=" + name + ",seq=" + seq; } public static void createMBean( Object proxy, String domain, String name ) { try { DynamicMBeanProxy mbean=new DynamicMBeanProxy(); mbean.setReal( proxy ); if( name!=null ) { mbean.setName( name ); } else { mbean.setName( generateName( proxy.getClass() )); } mbean.registerMBean( domain ); } catch( Throwable t ) { log.error( "Error creating mbean ", t ); } } public void registerMBean( String domain ) { try { // XXX use aliases, suffix only, proxy.getName(), etc ObjectName oname=new ObjectName( domain + ": " + getName()); if( getMBeanServer().isRegistered( oname )) { log.info("Re-registering " + oname ); getMBeanServer().unregisterMBean( oname ); } getMBeanServer().registerMBean( this, oname ); } catch( Throwable t ) { log.error( "Error creating mbean ", t ); } } public static MBeanServer getMBeanServer() { if( mserver==null ) { if( MBeanServerFactory.findMBeanServer(null).size() > 0 ) { mserver=(MBeanServer)MBeanServerFactory.findMBeanServer(null).get(0); } else { mserver=MBeanServerFactory.createMBeanServer(); } } return mserver; } private boolean supportedType( Class ret ) { return ret == String.class || ret == Integer.class || ret == Integer.TYPE || ret == Long.class || ret == Long.TYPE || ret == java.io.File.class || ret == Boolean.class || ret == Boolean.TYPE ; } /** Set the managed object. * * @todo Read an XML ( or .properties ) file containing descriptions, * generated from source comments * @todo Also filter methods based on config ( hide methods/attributes ) * @todo Adapters for notifications ( Interceptor hooks, etc ). */ public void setReal( Object realBean ) { real=realBean; } private void init() { if( methods!=null ) return; methods = real.getClass().getMethods(); for (int j = 0; j < methods.length; ++j) { String name=methods[j].getName(); if( name.startsWith( "get" ) ) { if( methods[j].getParameterTypes().length != 0 ) { continue; } if( ! Modifier.isPublic( methods[j].getModifiers() ) ) { //log.debug("not public " + methods[j] ); continue; } Class ret=methods[j].getReturnType(); if( ! supportedType( ret ) ) { if( log.isDebugEnabled() ) log.debug("Unsupported " + ret ); continue; } name=unCapitalize( name.substring(3)); getAttMap.put( name, methods[j] ); // just a marker, we don't use the value attMap.put( name, methods[j] ); } else if( name.startsWith( "is" ) ) { // not used in our code. Add later } else if( name.startsWith( "set" ) ) { Class params[]=methods[j].getParameterTypes(); if( params.length != 1 ) { continue; } if( ! Modifier.isPublic( methods[j].getModifiers() ) ) continue; Class ret=params[0]; if( ! supportedType( ret ) ) { continue; } name=unCapitalize( name.substring(3)); setAttMap.put( name, methods[j] ); attMap.put( name, methods[j] ); } else { if( methods[j].getParameterTypes().length != 0 ) { continue; } if( methods[j].getDeclaringClass() == Object.class ) continue; if( ! Modifier.isPublic( methods[j].getModifiers() ) ) continue; invokeAttMap.put( name, methods[j]); } } } /** * @todo Find if the 'className' is the name of the MBean or * the real class ( I suppose first ) * @todo Read (optional) descriptions from a .properties, generated * from source * @todo Deal with constructors * */ public MBeanInfo getMBeanInfo() { if( methods==null ) { init(); } try { MBeanAttributeInfo attributes[]=new MBeanAttributeInfo[attMap.size()]; Enumeration en=attMap.keys(); int i=0; while( en.hasMoreElements() ) { String name=(String)en.nextElement(); attributes[i++]=new MBeanAttributeInfo(name, "Attribute " + name , (Method)getAttMap.get(name), (Method)setAttMap.get(name)); } MBeanOperationInfo operations[]=new MBeanOperationInfo[invokeAttMap.size()]; en=invokeAttMap.keys(); i=0; while( en.hasMoreElements() ) { String name=(String)en.nextElement(); Method m=(Method)invokeAttMap.get(name); if( m!=null && name != null ) { operations[i++]=new MBeanOperationInfo(name, m); } else { System.out.println("Null arg " + name + " " + m ); } } if( log.isDebugEnabled() ) log.debug(real.getClass().getName() + " getMBeanInfo()"); return new MBeanInfo( real.getClass().getName(), /* ??? */ "MBean for " + getName(), attributes, new MBeanConstructorInfo[0], operations, new MBeanNotificationInfo[0]); } catch( Exception ex ) { ex.printStackTrace(); return null; } } static final Object[] NO_ARGS_PARAM=new Object[0]; public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { if( methods==null ) init(); Method m=(Method)getAttMap.get( attribute ); if( m==null ) throw new AttributeNotFoundException(attribute); try { if( log.isDebugEnabled() ) log.debug(real.getClass().getName() + " getAttribute " + attribute); return m.invoke(real, NO_ARGS_PARAM ); } catch( IllegalAccessException ex ) { ex.printStackTrace(); throw new MBeanException( ex ); } catch( InvocationTargetException ex1 ) { ex1.printStackTrace(); throw new MBeanException( ex1 ); } } public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { if( methods==null ) init(); // XXX Send notification !!! Method m=(Method)setAttMap.get( attribute.getName() ); if( m==null ) throw new AttributeNotFoundException(attribute.getName()); try { log.info(real.getClass().getName() + "setAttribute " + attribute.getName()); m.invoke(real, new Object[] { attribute.getValue() } ); } catch( IllegalAccessException ex ) { ex.printStackTrace(); throw new MBeanException( ex ); } catch( InvocationTargetException ex1 ) { ex1.printStackTrace(); throw new MBeanException( ex1 ); } } /** * Invoke a method. Only no param methods are supported at the moment * ( init, start, execute, etc ) ( that's the most common pattern we have * in tomcat/ant/etc ) * * @todo Implement invoke for methods with more arguments. */ public Object invoke(String method, Object[] arguments, String[] params) throws MBeanException, ReflectionException { if( methods==null ) init(); Method m=(Method)invokeAttMap.get( method ); if( m==null ) return null; try { log.info(real.getClass().getName() + "invoke " + m.getName()); return m.invoke(real, NO_ARGS_PARAM ); } catch( IllegalAccessException ex ) { throw new MBeanException( ex ); } catch( InvocationTargetException ex1 ) { throw new MBeanException( ex1 ); } } // -------------------- Auxiliary methods -------------------- public AttributeList setAttributes(AttributeList attributes) { Iterator attE=attributes.iterator(); while( attE.hasNext() ) { Attribute att=(Attribute)attE.next(); try { setAttribute( att ); } catch( Exception ex ) { ex.printStackTrace(); } } return attributes; } public AttributeList getAttributes(String[] attributes) { AttributeList al=new AttributeList(); if( attributes==null ) return null; for( int i=0; i<attributes.length; i++ ) { try { Attribute att=new Attribute( attributes[i], getAttribute( attributes[i] )); al.add( att ); } catch( Exception ex ) { ex.printStackTrace(); } } return al; } // -------------------- Utils -------------------- public static String unCapitalize(String name) { if (name == null || name.length() == 0) { return name; } char chars[] = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); } private static org.apache.commons.logging.Log log= org.apache.commons.logging.LogFactory.getLog( DynamicMBeanProxy.class ); } 1.1 jakarta-tomcat/proposals/JmxSupport/WEB-INF/classes/org/apache/tomcat/modules/config/MxInterceptor.java Index: MxInterceptor.java =================================================================== /* $Id: MxInterceptor.java,v 1.1 2002/09/30 02:17:43 larryi Exp $ * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.tomcat.modules.config; import org.apache.tomcat.core.*; import org.apache.tomcat.util.io.FileUtil; import java.io.*; import java.util.*; import javax.management.*; /** * * @author Costin Manolache */ public class MxInterceptor extends BaseInterceptor { MBeanServer mserver; private int port=-1; private String host; private String auth; private String user; private String password; // -------------------- Tomcat callbacks -------------------- private void createMBean( String domain, Object proxy, String name ) { try { DynamicMBeanProxy mbean=new DynamicMBeanProxy(); mbean.setReal( proxy ); if( name!=null ) { mbean.setName( "name=" + name ); } mbean.registerMBean( domain ); // Set mserver once if (mserver == null) mserver = mbean.getMBeanServer(); } catch( Throwable t ) { log( "Error creating mbean ", t ); } } /* -------------------- Public methods -------------------- */ /** Enable the MX4J internal adapter */ public void setPort( int i ) { port=i; } public int getPort() { return port; } public void setHost(String host ) { this.host=host; } public String getHost() { return host; } public void setAuthentication( String auth ) { if ("none".equals(auth) || "basic".equals(auth) || "digest".equals(auth)) this.auth=auth; } public String getAuthentication() { return auth; } public void setUser(String user) { this.user = user; } public void setPassword(String password) { this.password = password; } public boolean isPasswordSet() { return password != null && password.length() > 0; } /* ==================== Start/stop ==================== */ ObjectName serverName=null; /** Initialize the worker. After this call the worker will be * ready to accept new requests. */ public void loadAdapter() throws IOException { try { serverName = new ObjectName("Http:name=HttpAdaptor"); mserver.createMBean("mx4j.adaptor.http.HttpAdaptor", serverName, null); if( host!=null ) mserver.setAttribute(serverName, new Attribute("Host", host)); mserver.setAttribute(serverName, new Attribute("Port", new Integer(port))); // use authentication if user/password set if( auth!=null && user!=null && password!=null) mserver.setAttribute(serverName, new Attribute("AuthenticationMethod", auth)); // add user names mserver.invoke(serverName, "addAuthorization", new Object[] {user, password}, new String[] {"java.lang.String", "java.lang.String"}); ObjectName processorName = new ObjectName("Http:name=XSLTProcessor"); mserver.createMBean("mx4j.adaptor.http.XSLTProcessor", processorName, null); mserver.setAttribute(serverName, new Attribute("ProcessorName", processorName)); mserver.invoke(serverName, "start", null, null); log( "Started mx4j http adaptor" + ((host != null) ? " for host " + host : "") + " at port " + port); return; } catch( Throwable t ) { log( "Can't load the MX4J http adapter " + t.toString() ); } try { Class c=Class.forName( "com.sun.jdmk.comm.HtmlAdaptorServer" ); Object o=c.newInstance(); serverName=new ObjectName("Adaptor:name=html,port=" + port); log("Registering the JMX_RI html adapter " + serverName); mserver.registerMBean(o, serverName); mserver.setAttribute(serverName, new Attribute("Port", new Integer(port))); mserver.invoke(serverName, "start", null, null); log( "Start JMX_RI http adaptor at port " + port); } catch( Throwable t ) { log( "Can't load the JMX_RI http adapter " + t.toString() ); } } public void destroy() { try { log("Stoping JMX "); if( serverName!=null ) { mserver.invoke(serverName, "stop", null, null); } } catch( Throwable t ) { log( "Destroy error", t ); } } public void addContext( ContextManager cm, Context ctx ) throws TomcatException { String host=ctx.getHost(); if( host==null ) host="DEFAULT"; createMBean( "webapps", ctx, host + ctx.getPath() ); } public void addInterceptor( ContextManager cm, Context ctx, BaseInterceptor bi ) throws TomcatException { // If executing config generation, skip startup if ( cm.getProperty("jkconf") != null ) { return; } // if time to start up if ( cm.getState() == ContextManager.STATE_CONFIG ) { if( bi==this ) { // Adding myself and on-time things createMBean( "tomcat3", cm, "Tomcat3Container" ); if( port > 0 ) { try { loadAdapter(); } catch (IOException ioe) { log("can't load adaptor"); } } // add previously added interceptors, including this interceptor Object[] modules = cm.getContainer().getInterceptors(); for (int i=0; i < modules.length; i++) { createMBean( "tomcat3", modules[i], null); } } else { createMBean( "tomcat3", bi, null); } } } public void initRequest( ContextManager cm, Request req, Response resp ) { createMBean( "tomcat3.requests", req, null); } }
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>