/* * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Stephane Lacoin (Nuxeo EP Software Engineer) */ package org.nuxeo.runtime.management; import java.time.Instant; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.modelmbean.InvalidTargetObjectTypeException; import javax.management.modelmbean.RequiredModelMBean; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.management.inspector.ModelMBeanInfoFactory; import org.nuxeo.runtime.model.ComponentContext; import org.nuxeo.runtime.model.ComponentInstance; import org.nuxeo.runtime.model.ComponentName; import org.nuxeo.runtime.model.DefaultComponent; /** * @author Stephane Lacoin (Nuxeo EP Software Engineer) */ public class ResourcePublisherService extends DefaultComponent implements ResourcePublisher, ResourcePublisherMBean { public static final String SERVICES_EXT_KEY = "services"; public static final String FACTORIES_EXT_KEY = "factories"; public static final String SHORTCUTS_EXT_KEY = "shortcuts"; public static final ComponentName NAME = new ComponentName("org.nuxeo.runtime.management.ResourcePublisher"); private static final Log log = LogFactory.getLog(ResourcePublisherService.class); protected final ShortcutsRegistry shortcutsRegistry = new ShortcutsRegistry(); protected final FactoriesRegistry factoriesRegistry = new FactoriesRegistry(); protected final ResourcesRegistry resourcesRegistry = new ResourcesRegistry(); protected ServerLocatorService serverLocatorService; public ResourcePublisherService() { super(); // enables breaking } @Override public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { if (extensionPoint.equals(SERVICES_EXT_KEY)) { resourcesRegistry.doRegisterResource((ServiceDescriptor) contribution); } else if (extensionPoint.equals(FACTORIES_EXT_KEY)) { factoriesRegistry.doRegisterFactory((ResourceFactoryDescriptor) contribution); } else if (extensionPoint.equals(SHORTCUTS_EXT_KEY)) { shortcutsRegistry.doRegisterShortcut((ShortcutDescriptor) contribution); } } @Override public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { if (extensionPoint.equals(SERVICES_EXT_KEY)) { resourcesRegistry.doUnregisterResource((ServiceDescriptor) contribution); } else if (extensionPoint.equals(FACTORIES_EXT_KEY)) { factoriesRegistry.doUnregisterFactory((ResourceFactoryDescriptor) contribution); } else if (extensionPoint.equals(SHORTCUTS_EXT_KEY)) { shortcutsRegistry.doUnregisterShortcut((ShortcutDescriptor) contribution); } } protected class FactoriesRegistry { protected final Map<Class<? extends ResourceFactory>, ResourceFactory> registry = new HashMap<Class<? extends ResourceFactory>, ResourceFactory>(); protected void doRegisterFactory(ResourceFactoryDescriptor descriptor) { ResourceFactory factory; Class<? extends ResourceFactory> factoryClass = descriptor.getFactoryClass(); try { factory = factoryClass.newInstance(); } catch (ReflectiveOperationException e) { throw new ManagementRuntimeException("Cannot create factory " + factoryClass, e); } factory.configure(ResourcePublisherService.this, descriptor); registry.put(factoryClass, factory); } protected void doUnregisterFactory(ResourceFactoryDescriptor descriptor) { registry.remove(descriptor.getFactoryClass()); } protected void doRegisterResources() { for (ResourceFactory factory : registry.values()) { factory.registerResources(); } } } protected class ShortcutsRegistry { protected final Map<String, ObjectName> registry = new TreeMap<String, ObjectName>(); protected void doRegisterShortcut(ShortcutDescriptor descriptor) { doRegisterShortcut(descriptor.getShortName(), descriptor.getQualifiedName()); } protected void doRegisterShortcut(String shortName, String qualifiedName) { registry.put(shortName, ObjectNameFactory.getObjectName(qualifiedName)); } protected void doRegisterShortcut(String shortName, ObjectName qualifiedName) { registry.put(shortName, qualifiedName); } protected void doUnregisterShortcut(ShortcutDescriptor descriptor) { doUnregisterShortcut(descriptor.getShortName()); } protected void doUnregisterShortcut(String name) { registry.remove(name); } public void unregisterShortcut(String name) { doUnregisterShortcut(name); } } protected class ResourcesRegistry { protected final Map<ObjectName, Resource> registry = new HashMap<ObjectName, Resource>(); protected void doRegisterResource(String qualifiedName, Class<?> info, Object instance) { Resource resource = new Resource(ObjectNameFactory.getObjectName(qualifiedName), info, instance); doRegisterResource(resource); } protected void doRegisterResource(ServiceDescriptor descriptor) { Resource resource = doResolveServiceDescriptor(descriptor); doRegisterResource(resource); String shortName = descriptor.getName(); if (!StringUtils.isEmpty(shortName)) { shortcutsRegistry.doRegisterShortcut(shortName, resource.getManagementName()); } } protected final ModelMBeanInfoFactory mbeanInfoFactory = new ModelMBeanInfoFactory(); protected RequiredModelMBean doBind(MBeanServer server, ObjectName name, Object instance, Class<?> clazz) throws JMException, InvalidTargetObjectTypeException { RequiredModelMBean mbean = new RequiredModelMBean(); mbean.setManagedResource(instance, "ObjectReference"); mbean.setModelMBeanInfo(mbeanInfoFactory.getModelMBeanInfo(clazz)); server.registerMBean(mbean, name); return mbean; } protected void doBind(Resource resource) { if (!started) { return; } if (resource.mbean != null) { throw new IllegalStateException(resource + " is already bound"); } MBeanServer server = serverLocatorService.lookupServer(resource.managementName.getDomain()); try { resource.mbean = doBind(server, resource.managementName, resource.instance, resource.clazz); if (ResourcePublisherService.log.isDebugEnabled()) { ResourcePublisherService.log.debug("bound " + resource); } } catch (JMException | InvalidTargetObjectTypeException e) { ResourcePublisherService.log.error("Cannot bind " + resource, e); } } protected void doUnbind(Resource resource) { if (resource.mbean == null) { throw new IllegalStateException(resource.managementName + " is not bound"); } try { MBeanServer server = serverLocatorService.lookupServer(resource.managementName); server.unregisterMBean(resource.managementName); } catch (JMException e) { throw ManagementRuntimeException.wrap("Cannot unbind " + resource, e); } finally { resource.mbean = null; if (ResourcePublisherService.log.isDebugEnabled()) { ResourcePublisherService.log.debug("unbound " + resource); } } } protected void doRegisterResource(Resource resource) { final ObjectName name = resource.getManagementName(); if (registry.containsKey(name)) { return; } registry.put(name, resource); doBind(resource); if (log.isDebugEnabled()) { log.debug("registered " + name); } } protected ObjectName doResolveServiceName(ServiceDescriptor descriptor) { String qualifiedName = descriptor.getName(); if (qualifiedName == null) { qualifiedName = ObjectNameFactory.getQualifiedName(descriptor.getResourceClass().getCanonicalName()); } return ObjectNameFactory.getObjectName(qualifiedName); } protected Resource doResolveServiceDescriptor(ServiceDescriptor descriptor) { Class<?> resourceClass = descriptor.getResourceClass(); Object resourceInstance = doResolveService(resourceClass, descriptor); ObjectName managementName = doResolveServiceName(descriptor); Class<?> ifaceClass = descriptor.getIfaceClass(); Class<?> managementClass = ifaceClass != null ? ifaceClass : resourceClass; return new Resource(managementName, managementClass, resourceInstance); } protected <T> T doResolveService(Class<T> resourceClass, ServiceDescriptor descriptor) { T service = Framework.getService(resourceClass); if (service == null) { throw new ManagementRuntimeException("Cannot locate resource using " + resourceClass); } return service; } protected void doUnregisterResources() { Iterator<Entry<ObjectName, Resource>> iterator = registry.entrySet().iterator(); while (iterator.hasNext()) { Entry<ObjectName, Resource> entry = iterator.next(); iterator.remove(); Resource resource = entry.getValue(); if (resource.mbean != null) { doUnbind(entry.getValue()); } } } protected void doUnregisterResource(ServiceDescriptor descriptor) { ObjectName objectName = doResolveServiceName(descriptor); doUnregisterResource(objectName); String shortName = descriptor.getName(); if (!StringUtils.isEmpty(shortName)) { shortcutsRegistry.unregisterShortcut(shortName); } } protected void doUnregisterResource(String qualifiedName) { ObjectName objectName = ObjectNameFactory.getObjectName(qualifiedName); doUnregisterResource(objectName); } protected void doUnregisterResource(ObjectName objectName) { Resource resource = registry.remove(objectName); if (resource == null) { throw new IllegalArgumentException(objectName + " is not registered"); } if (resource.mbean != null) { doUnbind(resource); } } } @Override public void registerResource(String shortName, String qualifiedName, Class<?> managementClass, Object instance) { resourcesRegistry.doRegisterResource(qualifiedName, managementClass, instance); if (shortName != null) { shortcutsRegistry.doRegisterShortcut(shortName, qualifiedName); } } @Override public void unregisterResource(String shortName, String qualifiedName) { resourcesRegistry.doUnregisterResource(qualifiedName); if (shortName != null) { shortcutsRegistry.doUnregisterShortcut(shortName); } } public void registerShortcut(String shortName, String qualifiedName) { shortcutsRegistry.doRegisterShortcut(shortName, qualifiedName); } public void unregisterShortcut(String shortName) { shortcutsRegistry.doUnregisterShortcut(shortName); } @Override public Set<String> getShortcutsName() { return new HashSet<String>(shortcutsRegistry.registry.keySet()); } @Override public Set<ObjectName> getResourcesName() { return new HashSet<ObjectName>(resourcesRegistry.registry.keySet()); } @Override public ObjectName lookupName(String name) { if (!shortcutsRegistry.registry.containsKey(name)) { return ObjectNameFactory.getObjectName(name); } return shortcutsRegistry.registry.get(name); } protected void doBindResources() { for (Resource resource : resourcesRegistry.registry.values()) { if (resource.mbean == null) { resourcesRegistry.doBind(resource); } } } @Override public void bindResources() { doBindResources(); } protected void doUnbindResources() { for (Resource resource : resourcesRegistry.registry.values()) { if (resource.mbean != null) { resourcesRegistry.doUnbind(resource); } else {; } } } @Override public void unbindResources() { doUnbindResources(); } protected boolean started = false; @Override public void applicationStarted(ComponentContext context) { started = true; factoriesRegistry.doRegisterResources(); doBindResources(); } @Override public void applicationStopped(ComponentContext context, Instant deadline) { started = false; doUnbindResources(); } @Override public void activate(ComponentContext context) { serverLocatorService = (ServerLocatorService) Framework.getLocalService(ServerLocator.class); } @Override public void deactivate(ComponentContext context) { resourcesRegistry.doUnregisterResources(); } public void bindResource(ObjectName name) { Resource resource = resourcesRegistry.registry.get(name); if (resource == null) { throw new IllegalArgumentException(name + " is not registered"); } resourcesRegistry.doBind(resource); } public void unbindResource(ObjectName name) { Resource resource = resourcesRegistry.registry.get(name); if (resource == null) { throw new IllegalArgumentException(name + " is not registered"); } resourcesRegistry.doUnbind(resource); } protected void bindForTest(MBeanServer server, ObjectName name, Object instance, Class<?> clazz) throws JMException, InvalidTargetObjectTypeException { resourcesRegistry.doBind(server, name, instance, clazz); } }