package org.openanzo.jmx.internal; import java.io.IOException; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryManagerMXBean; import java.lang.management.MemoryNotificationInfo; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryType; import java.net.MalformedURLException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import java.security.AccessController; import java.security.Principal; import java.security.PrivilegedAction; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.Properties; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.JMException; import javax.management.MBeanException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.Notification; import javax.management.NotificationEmitter; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.openmbean.CompositeData; import javax.management.remote.JMXAuthenticator; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import javax.security.auth.Subject; import org.apache.activemq.management.StatisticImpl; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.AnzoRuntimeException; import org.openanzo.exceptions.ExceptionConstants; import org.openanzo.exceptions.LogUtils; import org.openanzo.jmx.DynamicStatsMBean; import org.openanzo.jmx.IJMXServiceEndpoint; import org.openanzo.jmx.JMXDictionary; import org.openanzo.osgi.IServiceTrackerListener; import org.openanzo.osgi.OsgiServiceTracker; import org.openanzo.services.AnzoPrincipal; import org.openanzo.services.IAuthenticationService; import org.openanzo.services.IOperationContext; import org.openanzo.services.IStatisticsProvider; import org.openanzo.services.impl.BaseOperationContext; import org.osgi.framework.BundleContext; import org.osgi.service.event.Event; import org.osgi.service.event.EventAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.management.HotSpotDiagnosticMXBean; /** * @author Matthew Roy ( <a href="mailto:mroy@us.ibm.com">mroy@us.ibm.com </a>) * */ class JMXService { /** JMX_DOMAIN */ public static final String JMX_DOMAIN = "openanzo.org"; private int port = 5000; private static final Logger log = LoggerFactory.getLogger(JMXService.class); protected Registry registry = null; protected MBeanServer mbServer = null; private JMXConnectorServer connectorServer = null; private final BundleContext context; private OsgiServiceTracker<IJMXServiceEndpoint> tracker = null; private OsgiServiceTracker<IStatisticsProvider> statsTracker = null; protected final IAuthenticationService authService; protected final EventAdmin eventAdmin; protected final HashMap<String, MemoryPoolMXBean> pools = new HashMap<String, MemoryPoolMXBean>(); /** * */ protected JMXService(BundleContext context, Dictionary<? extends Object, ? extends Object> configProperties, EventAdmin eventAdmin, IAuthenticationService authService) { this.context = context; this.eventAdmin = eventAdmin; this.port = JMXDictionary.getPort(configProperties, 5000); this.authService = authService; } public StatisticImpl getStatistics() { return null; } private void registerBean(MBeanServer mbServer, Object bean, ObjectName name) throws MBeanRegistrationException, NotCompliantMBeanException, InstanceAlreadyExistsException { if (!mbServer.isRegistered(name)) { if (bean != null && name != null) { mbServer.registerMBean(bean, name); } } } private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; protected void start() { log.info(LogUtils.LIFECYCLE_MARKER, "Initializing the JMXService component."); try { MBeanServer platformServer = ManagementFactory.getPlatformMBeanServer(); mbServer = MBeanServerFactory.createMBeanServer(); try { ObjectName name = new ObjectName(HOTSPOT_BEAN_NAME); if (!mbServer.isRegistered(name)) { registerBean(mbServer, ManagementFactory.newPlatformMXBeanProxy(platformServer, HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class), name); } } catch (Throwable t) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering platform mbean", t); } registerBean(mbServer, ManagementFactory.getClassLoadingMXBean(), new ObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME)); registerBean(mbServer, ManagementFactory.getCompilationMXBean(), new ObjectName(ManagementFactory.COMPILATION_MXBEAN_NAME)); for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { ObjectName name = new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",\"name\"=" + bean.getName()); if (!mbServer.isRegistered(name)) { try { registerBean(mbServer, bean, name); } catch (javax.management.InstanceAlreadyExistsException iaee) { if (log.isDebugEnabled()) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering mbean:" + name, iaee); } } } } for (MemoryManagerMXBean bean : ManagementFactory.getMemoryManagerMXBeans()) { ObjectName name = new ObjectName(ManagementFactory.MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE + ",\"name\"=" + bean.getName()); if (!mbServer.isRegistered(name)) { try { registerBean(mbServer, bean, name); } catch (javax.management.InstanceAlreadyExistsException iaee) { if (log.isDebugEnabled()) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering mbean:" + name, iaee); } } } } for (MemoryPoolMXBean bean : ManagementFactory.getMemoryPoolMXBeans()) { pools.put(bean.getName(), bean); ObjectName name = new ObjectName(ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE + ",\"name\"=" + bean.getName()); if (!mbServer.isRegistered(name)) { try { registerBean(mbServer, bean, name); if (bean.getType() == MemoryType.HEAP && bean.isUsageThresholdSupported()) { long maxMemory = bean.getUsage().getMax(); long warningThreshold = (long) (maxMemory * 0.75); bean.setUsageThreshold(warningThreshold); } } catch (javax.management.InstanceAlreadyExistsException iaee) { if (log.isDebugEnabled()) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering mbean:" + name, iaee); } } } } MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); NotificationEmitter emitter = (NotificationEmitter) memoryBean; emitter.addNotificationListener(new NotificationListener() { public void handleNotification(Notification notification, Object handback) { String notifType = notification.getType(); if (notifType.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED) || notifType.equals(MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) { CompositeData cd = (CompositeData) notification.getUserData(); MemoryNotificationInfo info = MemoryNotificationInfo.from(cd); Dictionary<Object, Object> props = new Properties(); props.put("poolName", info.getPoolName()); props.put("committed", info.getUsage().getCommitted()); props.put("init", info.getUsage().getInit()); props.put("max", info.getUsage().getMax()); props.put("used", info.getUsage().getUsed()); props.put("count", info.getCount()); eventAdmin.sendEvent(new Event("system/memory", props)); log.info(LogUtils.LIFECYCLE_MARKER, "Memory Usage greater than 75% of total:" + info.getUsage().getUsed()); } } }, null, null); registerBean(mbServer, memoryBean, new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME)); registerBean(mbServer, ManagementFactory.getOperatingSystemMXBean(), new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME)); registerBean(mbServer, ManagementFactory.getRuntimeMXBean(), new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME)); registerBean(mbServer, ManagementFactory.getThreadMXBean(), new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME)); int port2 = port + 1; ClassLoader currentLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); try { registry = LocateRegistry.createRegistry(port2); } catch (RemoteException re) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error creating registry", re); } if (registry == null) { registry = LocateRegistry.getRegistry(port2); } HashMap<String, Object> env = new HashMap<String, Object>(); env.put(JMXConnectorServer.AUTHENTICATOR, new JMXAuth()); env.put("jmx.remote.protocol.provider.class.loader", this.getClass().getClassLoader()); env.put("jmx.remote.default.class.loader", this.getClass().getClassLoader()); env.put("java.rmi.server.hostname", "localhost"); JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost:" + port + "/jndi/rmi://localhost:" + port2 + "/server"); connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbServer); connectorServer.start(); try { final ObjectName objectName = new ObjectName(JMX_DOMAIN + ":AnzoType=AnzoServer"); tracker = new OsgiServiceTracker<IJMXServiceEndpoint>(new IServiceTrackerListener<IJMXServiceEndpoint>() { public void unregisterService(IJMXServiceEndpoint service) { } public void registerService(IJMXServiceEndpoint service) { try { service.registerWithJMX(mbServer, objectName); } catch (AnzoException ae) { log.warn(LogUtils.LIFECYCLE_MARKER, "Error registering mbean", ae); } } public Class<IJMXServiceEndpoint> getComponentType() { return IJMXServiceEndpoint.class; } }, context); tracker.open(); statsTracker = new OsgiServiceTracker<IStatisticsProvider>(new IServiceTrackerListener<IStatisticsProvider>() { public void unregisterService(IStatisticsProvider service) { try { if (service.getStatistics() != null) { String nameString = objectName.getKeyPropertyListString(); ObjectName name = new ObjectName(objectName.getDomain() + ":" + nameString + ",AnzoService=StatisticsProviders," + service.getName()); String nameStringService = name.getKeyPropertyListString(); ObjectName nameServiceStats = new ObjectName(name.getDomain() + ":" + nameStringService + ",ChildType=Stats"); mbServer.unregisterMBean(nameServiceStats); } } catch (MalformedObjectNameException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error unregistering mbean", e); } catch (NullPointerException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error unregistering mbean", e); } catch (InstanceNotFoundException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error unregistering mbean", e); } catch (MBeanRegistrationException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error unregistering mbean", e); } } public void registerService(IStatisticsProvider service) { try { if (service.getStatistics() != null) { String nameString = objectName.getKeyPropertyListString(); ObjectName name = new ObjectName(objectName.getDomain() + ":" + nameString + ",AnzoService=StatisticsProviders," + service.getName()); String nameStringService = name.getKeyPropertyListString(); ObjectName nameServiceStats = new ObjectName(name.getDomain() + ":" + nameStringService + ",ChildType=Stats"); DynamicStatsMBean mbean = new DynamicStatsMBean(service.getStatistics()); registerBean(mbServer, mbean, nameServiceStats); } } catch (MalformedObjectNameException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering mbean", e); } catch (NullPointerException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering mbean", e); } catch (InstanceAlreadyExistsException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering mbean", e); } catch (MBeanRegistrationException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering mbean", e); } catch (NotCompliantMBeanException e) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error registering mbean", e); } } public Class<IStatisticsProvider> getComponentType() { return IStatisticsProvider.class; } }, context); statsTracker.open(); } catch (Exception e) { throw new AnzoException(ExceptionConstants.OSGI.INTERNAL_COMPONENT_ERROR, e); } Thread.currentThread().setContextClassLoader(currentLoader); } catch (MBeanException e) { throw new AnzoRuntimeException(ExceptionConstants.OSGI.INTERNAL_COMPONENT_ERROR, e); } catch (JMException e) { throw new AnzoRuntimeException(ExceptionConstants.OSGI.INTERNAL_COMPONENT_ERROR, e); } catch (RemoteException e) { throw new AnzoRuntimeException(ExceptionConstants.OSGI.INTERNAL_COMPONENT_ERROR, e); } catch (MalformedURLException e) { throw new AnzoRuntimeException(ExceptionConstants.OSGI.INTERNAL_COMPONENT_ERROR, e); } catch (IOException e) { throw new AnzoRuntimeException(ExceptionConstants.OSGI.INTERNAL_COMPONENT_ERROR, e); } catch (AnzoException ae) { throw new AnzoRuntimeException(ae); } } protected void close() { if (connectorServer != null) { try { connectorServer.stop(); tracker.close(); UnicastRemoteObject.unexportObject(registry, true); } catch (IOException e) { if (log.isDebugEnabled()) { log.debug(LogUtils.LIFECYCLE_MARKER, "Error closing jmx service", e); } } } } class JMXAuth implements JMXAuthenticator { public Subject authenticate(Object credentials) { if (!(credentials instanceof String[])) { if (credentials == null) throw new SecurityException("Credentials required"); final String message = "Credentials should be String[] instead of " + credentials.getClass().getName(); throw new SecurityException(message); } final String[] aCredentials = (String[]) credentials; if (aCredentials.length != 2) { final String message = "Credentials should have 2 elements not " + aCredentials.length; throw new SecurityException(message); } String username = aCredentials[0]; String password = aCredentials[1]; if (username == null || password == null) { final String message = "Username or password is null"; throw new SecurityException(message); } try { IOperationContext context = new BaseOperationContext("JMX Authenticate", BaseOperationContext.generateOperationId(), null); context.setMDC(); AnzoPrincipal principal = authService.authenticateUser(context, username, password); if (principal != null && principal.isSysadmin()) { final Subject subject = new Subject(false, Collections.<Principal> singleton(principal), Collections.EMPTY_SET, Collections.EMPTY_SET); AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { subject.setReadOnly(); return null; } }); return subject; } else { throw new SecurityException("User not found, or user is not in the sysadmin role"); } } catch (AnzoException le) { throw new SecurityException(le); } } } }