001/* 002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Stephane Lacoin (Nuxeo EP Software Engineer) 016 */ 017package org.nuxeo.runtime.management; 018 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.Iterator; 022import java.util.Map; 023import java.util.Map.Entry; 024import java.util.Set; 025import java.util.TreeMap; 026 027import javax.management.JMException; 028import javax.management.MBeanServer; 029import javax.management.ObjectName; 030import javax.management.modelmbean.InvalidTargetObjectTypeException; 031import javax.management.modelmbean.RequiredModelMBean; 032 033import org.apache.commons.lang.StringUtils; 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036import org.nuxeo.runtime.RuntimeServiceEvent; 037import org.nuxeo.runtime.RuntimeServiceListener; 038import org.nuxeo.runtime.api.Framework; 039import org.nuxeo.runtime.management.inspector.ModelMBeanInfoFactory; 040import org.nuxeo.runtime.model.ComponentContext; 041import org.nuxeo.runtime.model.ComponentInstance; 042import org.nuxeo.runtime.model.ComponentName; 043import org.nuxeo.runtime.model.DefaultComponent; 044 045/** 046 * @author Stephane Lacoin (Nuxeo EP Software Engineer) 047 */ 048public class ResourcePublisherService extends DefaultComponent implements ResourcePublisher, ResourcePublisherMBean { 049 050 public static final String SERVICES_EXT_KEY = "services"; 051 052 public static final String FACTORIES_EXT_KEY = "factories"; 053 054 public static final String SHORTCUTS_EXT_KEY = "shortcuts"; 055 056 public static final ComponentName NAME = new ComponentName("org.nuxeo.runtime.management.ResourcePublisher"); 057 058 private static final Log log = LogFactory.getLog(ResourcePublisherService.class); 059 060 protected final ShortcutsRegistry shortcutsRegistry = new ShortcutsRegistry(); 061 062 protected final FactoriesRegistry factoriesRegistry = new FactoriesRegistry(); 063 064 protected final ResourcesRegistry resourcesRegistry = new ResourcesRegistry(); 065 066 protected ServerLocatorService serverLocatorService; 067 068 public ResourcePublisherService() { 069 super(); // enables breaking 070 } 071 072 @Override 073 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 074 if (extensionPoint.equals(SERVICES_EXT_KEY)) { 075 resourcesRegistry.doRegisterResource((ServiceDescriptor) contribution); 076 } else if (extensionPoint.equals(FACTORIES_EXT_KEY)) { 077 factoriesRegistry.doRegisterFactory((ResourceFactoryDescriptor) contribution); 078 } else if (extensionPoint.equals(SHORTCUTS_EXT_KEY)) { 079 shortcutsRegistry.doRegisterShortcut((ShortcutDescriptor) contribution); 080 } 081 } 082 083 @Override 084 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 085 if (extensionPoint.equals(SERVICES_EXT_KEY)) { 086 resourcesRegistry.doUnregisterResource((ServiceDescriptor) contribution); 087 } else if (extensionPoint.equals(FACTORIES_EXT_KEY)) { 088 factoriesRegistry.doUnregisterFactory((ResourceFactoryDescriptor) contribution); 089 } else if (extensionPoint.equals(SHORTCUTS_EXT_KEY)) { 090 shortcutsRegistry.doUnregisterShortcut((ShortcutDescriptor) contribution); 091 } 092 } 093 094 protected class FactoriesRegistry { 095 096 protected final Map<Class<? extends ResourceFactory>, ResourceFactory> registry = new HashMap<Class<? extends ResourceFactory>, ResourceFactory>(); 097 098 protected void doRegisterFactory(ResourceFactoryDescriptor descriptor) { 099 ResourceFactory factory; 100 Class<? extends ResourceFactory> factoryClass = descriptor.getFactoryClass(); 101 try { 102 factory = factoryClass.newInstance(); 103 } catch (ReflectiveOperationException e) { 104 throw new ManagementRuntimeException("Cannot create factory " + factoryClass, e); 105 } 106 factory.configure(ResourcePublisherService.this, descriptor); 107 registry.put(factoryClass, factory); 108 } 109 110 protected void doUnregisterFactory(ResourceFactoryDescriptor descriptor) { 111 registry.remove(descriptor.getFactoryClass()); 112 } 113 114 protected void doRegisterResources() { 115 for (ResourceFactory factory : registry.values()) { 116 factory.registerResources(); 117 } 118 } 119 } 120 121 protected class ShortcutsRegistry { 122 protected final Map<String, ObjectName> registry = new TreeMap<String, ObjectName>(); 123 124 protected void doRegisterShortcut(ShortcutDescriptor descriptor) { 125 doRegisterShortcut(descriptor.getShortName(), descriptor.getQualifiedName()); 126 } 127 128 protected void doRegisterShortcut(String shortName, String qualifiedName) { 129 registry.put(shortName, ObjectNameFactory.getObjectName(qualifiedName)); 130 } 131 132 protected void doRegisterShortcut(String shortName, ObjectName qualifiedName) { 133 registry.put(shortName, qualifiedName); 134 } 135 136 protected void doUnregisterShortcut(ShortcutDescriptor descriptor) { 137 doUnregisterShortcut(descriptor.getShortName()); 138 } 139 140 protected void doUnregisterShortcut(String name) { 141 registry.remove(name); 142 } 143 144 public void unregisterShortcut(String name) { 145 doUnregisterShortcut(name); 146 } 147 } 148 149 protected class ResourcesRegistry { 150 151 protected final Map<ObjectName, Resource> registry = new HashMap<ObjectName, Resource>(); 152 153 protected void doRegisterResource(String qualifiedName, Class<?> info, Object instance) { 154 Resource resource = new Resource(ObjectNameFactory.getObjectName(qualifiedName), info, instance); 155 doRegisterResource(resource); 156 } 157 158 protected void doRegisterResource(ServiceDescriptor descriptor) { 159 Resource resource = doResolveServiceDescriptor(descriptor); 160 doRegisterResource(resource); 161 String shortName = descriptor.getName(); 162 if (!StringUtils.isEmpty(shortName)) { 163 shortcutsRegistry.doRegisterShortcut(shortName, resource.getManagementName()); 164 } 165 } 166 167 protected final ModelMBeanInfoFactory mbeanInfoFactory = new ModelMBeanInfoFactory(); 168 169 protected RequiredModelMBean doBind(MBeanServer server, ObjectName name, Object instance, Class<?> clazz) 170 throws JMException, InvalidTargetObjectTypeException { 171 RequiredModelMBean mbean = new RequiredModelMBean(); 172 mbean.setManagedResource(instance, "ObjectReference"); 173 mbean.setModelMBeanInfo(mbeanInfoFactory.getModelMBeanInfo(clazz)); 174 server.registerMBean(mbean, name); 175 return mbean; 176 } 177 178 protected void doBind(Resource resource) { 179 if (!started) { 180 return; 181 } 182 if (resource.mbean != null) { 183 throw new IllegalStateException(resource + " is already bound"); 184 } 185 MBeanServer server = serverLocatorService.lookupServer(resource.managementName.getDomain()); 186 try { 187 resource.mbean = doBind(server, resource.managementName, resource.instance, resource.clazz); 188 if (ResourcePublisherService.log.isDebugEnabled()) { 189 ResourcePublisherService.log.debug("bound " + resource); 190 } 191 } catch (JMException | InvalidTargetObjectTypeException e) { 192 ResourcePublisherService.log.error("Cannot bind " + resource, e); 193 } 194 } 195 196 protected void doUnbind(Resource resource) { 197 if (resource.mbean == null) { 198 throw new IllegalStateException(resource.managementName + " is not bound"); 199 } 200 try { 201 MBeanServer server = serverLocatorService.lookupServer(resource.managementName); 202 server.unregisterMBean(resource.managementName); 203 } catch (JMException e) { 204 throw ManagementRuntimeException.wrap("Cannot unbind " + resource, e); 205 } finally { 206 resource.mbean = null; 207 if (ResourcePublisherService.log.isDebugEnabled()) { 208 ResourcePublisherService.log.debug("unbound " + resource); 209 } 210 } 211 } 212 213 protected void doRegisterResource(Resource resource) { 214 final ObjectName name = resource.getManagementName(); 215 if (registry.containsKey(name)) { 216 log.warn("Already registered " + name + ", skipping", new Throwable("Stack trace")); 217 return; 218 } 219 registry.put(name, resource); 220 doBind(resource); 221 if (log.isDebugEnabled()) { 222 log.debug("registered " + name); 223 } 224 } 225 226 protected ObjectName doResolveServiceName(ServiceDescriptor descriptor) { 227 String qualifiedName = descriptor.getName(); 228 if (qualifiedName == null) { 229 qualifiedName = ObjectNameFactory.getQualifiedName(descriptor.getResourceClass().getCanonicalName()); 230 } 231 return ObjectNameFactory.getObjectName(qualifiedName); 232 } 233 234 protected Resource doResolveServiceDescriptor(ServiceDescriptor descriptor) { 235 Class<?> resourceClass = descriptor.getResourceClass(); 236 Object resourceInstance = doResolveService(resourceClass, descriptor); 237 ObjectName managementName = doResolveServiceName(descriptor); 238 Class<?> ifaceClass = descriptor.getIfaceClass(); 239 Class<?> managementClass = ifaceClass != null ? ifaceClass : resourceClass; 240 return new Resource(managementName, managementClass, resourceInstance); 241 } 242 243 protected <T> T doResolveService(Class<T> resourceClass, ServiceDescriptor descriptor) { 244 T service = Framework.getService(resourceClass); 245 if (service == null) { 246 throw new ManagementRuntimeException("Cannot locate resource using " + resourceClass); 247 } 248 return service; 249 } 250 251 protected void doUnregisterResources() { 252 Iterator<Entry<ObjectName, Resource>> iterator = registry.entrySet().iterator(); 253 while (iterator.hasNext()) { 254 Entry<ObjectName, Resource> entry = iterator.next(); 255 iterator.remove(); 256 Resource resource = entry.getValue(); 257 if (resource.mbean != null) { 258 doUnbind(entry.getValue()); 259 } 260 } 261 } 262 263 protected void doUnregisterResource(ServiceDescriptor descriptor) { 264 ObjectName objectName = doResolveServiceName(descriptor); 265 doUnregisterResource(objectName); 266 String shortName = descriptor.getName(); 267 if (!StringUtils.isEmpty(shortName)) { 268 shortcutsRegistry.unregisterShortcut(shortName); 269 } 270 } 271 272 protected void doUnregisterResource(String qualifiedName) { 273 ObjectName objectName = ObjectNameFactory.getObjectName(qualifiedName); 274 doUnregisterResource(objectName); 275 } 276 277 protected void doUnregisterResource(ObjectName objectName) { 278 Resource resource = registry.remove(objectName); 279 if (resource == null) { 280 throw new IllegalArgumentException(objectName + " is not registered"); 281 } 282 if (resource.mbean != null) { 283 doUnbind(resource); 284 } 285 } 286 287 } 288 289 @Override 290 public void registerResource(String shortName, String qualifiedName, Class<?> managementClass, Object instance) { 291 resourcesRegistry.doRegisterResource(qualifiedName, managementClass, instance); 292 if (shortName != null) { 293 shortcutsRegistry.doRegisterShortcut(shortName, qualifiedName); 294 } 295 } 296 297 @Override 298 public void unregisterResource(String shortName, String qualifiedName) { 299 resourcesRegistry.doUnregisterResource(qualifiedName); 300 if (shortName != null) { 301 shortcutsRegistry.doUnregisterShortcut(shortName); 302 } 303 } 304 305 public void registerShortcut(String shortName, String qualifiedName) { 306 shortcutsRegistry.doRegisterShortcut(shortName, qualifiedName); 307 } 308 309 public void unregisterShortcut(String shortName) { 310 shortcutsRegistry.doUnregisterShortcut(shortName); 311 } 312 313 @Override 314 public Set<String> getShortcutsName() { 315 return new HashSet<String>(shortcutsRegistry.registry.keySet()); 316 } 317 318 @Override 319 public Set<ObjectName> getResourcesName() { 320 return new HashSet<ObjectName>(resourcesRegistry.registry.keySet()); 321 } 322 323 @Override 324 public ObjectName lookupName(String name) { 325 if (!shortcutsRegistry.registry.containsKey(name)) { 326 return ObjectNameFactory.getObjectName(name); 327 } 328 return shortcutsRegistry.registry.get(name); 329 } 330 331 protected void doBindResources() { 332 for (Resource resource : resourcesRegistry.registry.values()) { 333 if (resource.mbean == null) { 334 resourcesRegistry.doBind(resource); 335 } 336 } 337 } 338 339 @Override 340 public void bindResources() { 341 doBindResources(); 342 } 343 344 protected void doUnbindResources() { 345 for (Resource resource : resourcesRegistry.registry.values()) { 346 if (resource.mbean != null) { 347 resourcesRegistry.doUnbind(resource); 348 } else {; 349 } 350 } 351 } 352 353 @Override 354 public void unbindResources() { 355 doUnbindResources(); 356 } 357 358 protected boolean started = false; 359 360 @Override 361 public void applicationStarted(ComponentContext context) { 362 started = true; 363 factoriesRegistry.doRegisterResources(); 364 doBindResources(); 365 Framework.addListener(new RuntimeServiceListener() { 366 367 @Override 368 public void handleEvent(RuntimeServiceEvent event) { 369 if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP) { 370 return; 371 } 372 Framework.removeListener(this); 373 doUnbindResources(); 374 } 375 }); 376 } 377 378 @Override 379 public void activate(ComponentContext context) { 380 serverLocatorService = (ServerLocatorService) Framework.getLocalService(ServerLocator.class); 381 } 382 383 @Override 384 public void deactivate(ComponentContext context) { 385 resourcesRegistry.doUnregisterResources(); 386 } 387 388 public void bindResource(ObjectName name) { 389 Resource resource = resourcesRegistry.registry.get(name); 390 if (resource == null) { 391 throw new IllegalArgumentException(name + " is not registered"); 392 } 393 resourcesRegistry.doBind(resource); 394 } 395 396 public void unbindResource(ObjectName name) { 397 Resource resource = resourcesRegistry.registry.get(name); 398 if (resource == null) { 399 throw new IllegalArgumentException(name + " is not registered"); 400 } 401 resourcesRegistry.doUnbind(resource); 402 } 403 404 protected void bindForTest(MBeanServer server, ObjectName name, Object instance, Class<?> clazz) 405 throws JMException, InvalidTargetObjectTypeException { 406 resourcesRegistry.doBind(server, name, instance, clazz); 407 } 408 409}