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