/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.jmx; import com.caucho.util.Alarm; import com.caucho.util.CharBuffer; import com.caucho.util.L10N; import javax.management.*; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.*; /** * Static convenience methods. */ public class Jmx { private static final L10N L = new L10N(Jmx.class); private static final Logger log = Logger.getLogger(Jmx.class.getName()); private static EnvironmentMBeanServer _mbeanServer; private static MBeanServer _globalMBeanServer; /** * Sets the server. */ static void setMBeanServer(EnvironmentMBeanServer server) { _mbeanServer = server; } /** * Returns the context mbean server. */ public static MBeanServer getContextMBeanServer() { if (_mbeanServer == null) _mbeanServer = (EnvironmentMBeanServer) EnvironmentMBeanServerBuilder.getGlobal("resin"); return _mbeanServer; } /** * Returns the global mbean server. */ public static MBeanServer getGlobalMBeanServer() { if (_globalMBeanServer == null) { getContextMBeanServer(); ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); _globalMBeanServer = new GlobalMBeanServer(systemLoader); } return _globalMBeanServer; } /** * Gets the static mbean server. */ public static AbstractMBeanServer getMBeanServer() { return _mbeanServer; } /** * Returns a copy of the context properties. */ public static LinkedHashMap<String,String> copyContextProperties() { AbstractMBeanServer mbeanServer = getMBeanServer(); if (mbeanServer != null) return mbeanServer.createContext().copyProperties(); else return new LinkedHashMap<String,String>(); } /** * Returns a copy of the context properties. */ public static LinkedHashMap<String,String> copyContextProperties(ClassLoader loader) { AbstractMBeanServer mbeanServer = getMBeanServer(); if (mbeanServer != null) return mbeanServer.createContext(loader).copyProperties(); else return new LinkedHashMap<String,String>(); } /** * Sets the context properties. */ public static void setContextProperties(Map<String,String> properties) { AbstractMBeanServer mbeanServer = getMBeanServer(); if (mbeanServer != null) mbeanServer.createContext().setProperties(properties); } /** * Sets the context properties. */ public static void setContextProperties(Map<String,String> properties, ClassLoader loader) { AbstractMBeanServer mbeanServer = getMBeanServer(); if (mbeanServer != null) mbeanServer.createContext(loader).setProperties(properties); } /** * Conditionally registers an MBean with the server. * * @param object the object to be registered as an MBean * @param name the name of the mbean. * * @return the instantiated object. */ public static ObjectInstance register(Object object, String name) throws InstanceAlreadyExistsException, MBeanRegistrationException, MalformedObjectNameException, NotCompliantMBeanException { if (name.indexOf(':') < 0) { Map<String,String> props = parseProperties(name); if (props.get("type") == null) { String type = object.getClass().getName(); int p = type.lastIndexOf('.'); if (p > 0) type = type.substring(p + 1); props.put("type", type); } ObjectName objectName = getObjectName("resin", props); return register(object, objectName); } else return register(object, new ObjectName(name)); } /** * Conditionally registers an MBean with the server. * * @param object the object to be registered as an MBean * @param name the name of the mbean. * * @return the instantiated object. */ public static ObjectInstance registerContext(Object object, String name) throws InstanceAlreadyExistsException, MBeanRegistrationException, MalformedObjectNameException, NotCompliantMBeanException { if (name.indexOf(':') < 0) { Map<String,String> nameProps = parseProperties(name); Map<String,String> props = copyContextProperties(); props.putAll(nameProps); if (props.get("type") == null) { String type = object.getClass().getName(); int p = type.lastIndexOf('.'); if (p > 0) type = type.substring(p + 1); props.put("type", type); } ObjectName objectName = getObjectName("resin", props); return register(object, objectName); } else return register(object, new ObjectName(name)); } /** * Conditionally registers an MBean with the server. * * @param object the object to be registered as an MBean * @param name the name of the mbean. * * @return the instantiated object. */ public static ObjectInstance register(Object object, Map<String,String> properties) throws InstanceAlreadyExistsException, MBeanRegistrationException, MalformedObjectNameException, NotCompliantMBeanException { Map<String,String> props = copyContextProperties(); props.putAll(properties); return register(object, getObjectName("resin", props)); } /** * Registers an MBean with the server. * * @param object the object to be registered as an MBean * @param name the name of the mbean. * * @return the instantiated object or null if the object * doesn't have an MBean interface. */ public static ObjectInstance register(Object object, ObjectName name) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { DynamicMBean dynMBean = createMBean(object, name); if (dynMBean != null) return getMBeanServer().registerMBean(dynMBean, name); else return null; } /** * Registers an MBean with the server. * * @param object the object to be registered as an MBean * @param name the name of the mbean. * * @return the instantiated object. */ public static ObjectInstance register(Object object, ObjectName name, ClassLoader loader) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { thread.setContextClassLoader(loader); AbstractMBeanServer mbeanServer = getMBeanServer(); if (mbeanServer != null) { DynamicMBean dynMBean = createMBean(object, name); if (dynMBean != null) return mbeanServer.registerMBean(dynMBean, name); } return null; } finally { thread.setContextClassLoader(oldLoader); } } /** * Creates the dynamic mbean. */ private static DynamicMBean createMBean(Object obj, ObjectName name) throws NotCompliantMBeanException { if (obj == null) return null; else if (obj instanceof DynamicMBean) return (DynamicMBean) obj; Class<?> ifc = getMBeanInterface(obj.getClass()); if (ifc == null) return null; return new IntrospectionMBean(obj, ifc); } /** * Returns the mbean interface. */ private static Class<?> getMBeanInterface(Class<?> cl) { for (; cl != null; cl = cl.getSuperclass()) { Class<?> []interfaces = cl.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { Class<?> ifc = interfaces[i]; if (ifc.isAnnotationPresent(MXBean.class)) return ifc; if (ifc.getName().endsWith("MBean") || ifc.getName().endsWith("MXBean")) return ifc; } } return null; } /** * Unregisters an MBean with the server. * * @param name the name of the mbean. */ public static void unregister(ObjectName name) throws MBeanRegistrationException, InstanceNotFoundException { getMBeanServer().unregisterMBean(name); } /** * Unregisters an MBean with the server. * * @param name the name of the mbean. */ public static void unregister(ObjectName name, ClassLoader loader) throws MBeanRegistrationException, InstanceNotFoundException { Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { thread.setContextClassLoader(loader); getMBeanServer().unregisterMBean(name); } finally { thread.setContextClassLoader(oldLoader); } } /** * Registers an MBean with the server. * * @param object the object to be registered as an MBean * @param name the name of the mbean. * @param api the api for the server * * @return the instantiated object. */ public static ObjectInstance register(Object object, String name, Class api) throws InstanceAlreadyExistsException, MBeanRegistrationException, MalformedObjectNameException, NotCompliantMBeanException { return register(object, new ObjectName(name), api); } /** * Registers an MBean with the server. * * @param object the object to be registered as an MBean * @param name the name of the mbean. * @param api the api for the server * * @return the instantiated object. */ public static ObjectInstance register(Object object, ObjectName name, Class api) throws InstanceAlreadyExistsException, MBeanRegistrationException, MalformedObjectNameException, NotCompliantMBeanException { IntrospectionMBean mbean = new IntrospectionMBean(object, api); return getMBeanServer().registerMBean(mbean, name); } /** * Conditionally registers an MBean with the server. * * @param object the object to be registered as an MBean * @param name the name of the mbean. * * @return the instantiated object. */ public static void unregister(String name) throws InstanceNotFoundException, MalformedObjectNameException, MBeanRegistrationException { ObjectName objectName = getObjectName(name); getMBeanServer().unregisterMBean(objectName); // return register(object, objectName); } /** * Returns an ObjectName based on a short name. */ public static ObjectName getObjectName(String name) throws MalformedObjectNameException { return getMBeanServer().createContext().getObjectName(name); } /** * Parses a name. */ public static LinkedHashMap<String,String> parseProperties(String name) { LinkedHashMap<String,String> map = new LinkedHashMap<String,String>(); parseProperties(map, name); return map; } /** * Parses a name. */ public static void parseProperties(Map<String,String> properties, String name) { parseProperties(properties, name, 0); } /** * Parses a name. */ private static void parseProperties(Map<String,String> properties, String name, int i) { CharBuffer cb = CharBuffer.allocate(); int len = name.length(); while (i < len) { for (; i < len && Character.isWhitespace(name.charAt(i)); i++) { } cb.clear(); int ch; for (; i < len && (ch = name.charAt(i)) != '=' && ch != ',' && ! Character.isWhitespace((char) ch); i++) { cb.append((char) ch); } String key = cb.toString(); if (key.length() == 0) { throw new IllegalArgumentException(L.l("`{0}' is an illegal name syntax.", name)); } for (; i < len && Character.isWhitespace(name.charAt(i)); i++) { } if (len <= i || (ch = name.charAt(i)) == ',') { properties.put(key, ""); } else if (ch == '=') { for (i++; i < len && Character.isWhitespace(name.charAt(i)); i++) { } if (len <= i || (ch = name.charAt(i)) == ',') { properties.put(key, ""); } else if (ch == '"' || ch == '\'') { int end = ch; cb.clear(); for (i++; i < len && (ch = name.charAt(i)) != end; i++) { if (ch == '\\') { ch = name.charAt(++i); cb.append((char) ch); } else cb.append((char) ch); } if (ch != end) throw new IllegalArgumentException(L.l("`{0}' is an illegal name syntax.", name)); i++; String value = cb.toString(); properties.put(key, value); } else { cb.clear(); for (; i < len && (ch = name.charAt(i)) != ','; i++) cb.append((char) ch); properties.put(key, cb.toString()); } } else { throw new IllegalArgumentException(L.l("`{0}' is an illegal name syntax.", name)); } for (; i < len && Character.isWhitespace(name.charAt(i)); i++) { } if (i < len && name.charAt(i) != ',') throw new IllegalArgumentException(L.l("`{0}' is an illegal name syntax.", name)); i++; } } /** * Creates the clean name */ public static ObjectName getObjectName(String domain, Map<String,String> properties) throws MalformedObjectNameException { StringBuilder cb = new StringBuilder(); cb.append(domain); cb.append(':'); boolean isFirst = true; Pattern escapePattern = Pattern.compile("[,=:\"*?]"); // sort type first String type = properties.get("type"); if (type != null) { cb.append("type="); if (escapePattern.matcher(type).find()) type = ObjectName.quote(type); cb.append(type); isFirst = false; } for (String key : properties.keySet()) { if (key.equals("type")) continue; if (! isFirst) cb.append(','); isFirst = false; cb.append(key); cb.append('='); String value = properties.get(key); if (value.length() == 0 || (escapePattern.matcher(value).find() && ! (value.startsWith("\"") && value.endsWith("\"")))) { value = ObjectName.quote(value); } cb.append(value); } return new ObjectName(cb.toString()); } /** * Returns the local view. */ /* public static MBeanView getLocalView() { MBeanContext context = MBeanContext.getLocal(); return context.getView(); } */ /** * Returns the local view. */ /* public static MBeanView getLocalView(ClassLoader loader) { MBeanContext context = MBeanContext.getLocal(loader); return context.getView(); } */ /** * Returns the local manged object. */ public static Object find(String localName) throws MalformedObjectNameException { return find(getMBeanServer().createContext().getObjectName(localName)); } /** * Returns the local manged object. */ public static Object find(ObjectName name) { return find(name, Thread.currentThread().getContextClassLoader()); } /** * Returns the local manged object. */ public static Object findGlobal(String localName) throws MalformedObjectNameException { return findGlobal(getMBeanServer().createContext().getObjectName(localName)); } /** * Returns the local manged object. */ public static Object findGlobal(ObjectName name) { return find(name, ClassLoader.getSystemClassLoader(), getGlobalMBeanServer()); } /** * Returns the local manged object. */ public static Object find(ObjectName name, ClassLoader loader) { return find(name, loader, getMBeanServer()); } /** * Returns the local manged object. */ public static Object find(ObjectName name, ClassLoader loader, MBeanServer mbeanServer) { try { ObjectInstance obj = mbeanServer.getObjectInstance(name); if (obj == null) return null; String className = obj.getClassName(); Class<?> cl = Class.forName(className, false, loader); Class<?> ifc; if (cl.isInterface()) ifc = cl; else ifc = getMBeanInterface(cl); if (ifc == null) return null; boolean isBroadcast = true; Object proxy; proxy = JmxInvocationHandler.newProxyInstance(mbeanServer, loader, name, ifc, true); return proxy; } catch (InstanceNotFoundException e) { log.log(Level.FINE, e.toString(), e); return null; } catch (ClassNotFoundException e) { log.log(Level.FINE, e.toString(), e); return null; } } /** * Returns the local manged object. */ public static ArrayList<Object> query(ObjectName namePattern) { Set<ObjectName> names = getMBeanServer().queryNames(namePattern, null); ArrayList<Object> proxy = new ArrayList<Object>(); Iterator<ObjectName> iter = names.iterator(); while (iter.hasNext()) { ObjectName name = iter.next(); proxy.add(find(name)); } return proxy; } /** * Queues a task. */ public static void queueAbsolute(TimerTask job, long time) { JobThread.queue(job, time); } /** * Queues a task. */ public static void queueRelative(TimerTask job, long delta) { queueAbsolute(job, Alarm.getCurrentTime() + delta); } /** * Dequeues a task. */ public static void dequeue(TimerTask job) { JobThread.dequeue(job); } // static public Jmx() {} }