001/* 002 * (C) Copyright 2006-2017 Nuxeo (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.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<>(); 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 return; 217 } 218 registry.put(name, resource); 219 doBind(resource); 220 if (log.isDebugEnabled()) { 221 log.debug("registered " + name); 222 } 223 } 224 225 protected ObjectName doResolveServiceName(ServiceDescriptor descriptor) { 226 String qualifiedName = descriptor.getName(); 227 if (qualifiedName == null) { 228 qualifiedName = ObjectNameFactory.getQualifiedName(descriptor.getResourceClass().getCanonicalName()); 229 } 230 return ObjectNameFactory.getObjectName(qualifiedName); 231 } 232 233 protected Resource doResolveServiceDescriptor(ServiceDescriptor descriptor) { 234 Class<?> resourceClass = descriptor.getResourceClass(); 235 Object resourceInstance = doResolveService(resourceClass, descriptor); 236 ObjectName managementName = doResolveServiceName(descriptor); 237 Class<?> ifaceClass = descriptor.getIfaceClass(); 238 Class<?> managementClass = ifaceClass != null ? ifaceClass : resourceClass; 239 return new Resource(managementName, managementClass, resourceInstance); 240 } 241 242 protected <T> T doResolveService(Class<T> resourceClass, ServiceDescriptor descriptor) { 243 T service = Framework.getService(resourceClass); 244 if (service == null) { 245 throw new ManagementRuntimeException("Cannot locate resource using " + resourceClass); 246 } 247 return service; 248 } 249 250 protected void doUnregisterResources() { 251 Iterator<Entry<ObjectName, Resource>> iterator = registry.entrySet().iterator(); 252 while (iterator.hasNext()) { 253 Entry<ObjectName, Resource> entry = iterator.next(); 254 iterator.remove(); 255 Resource resource = entry.getValue(); 256 if (resource.mbean != null) { 257 doUnbind(entry.getValue()); 258 } 259 } 260 } 261 262 protected void doUnregisterResource(ServiceDescriptor descriptor) { 263 ObjectName objectName = doResolveServiceName(descriptor); 264 doUnregisterResource(objectName); 265 String shortName = descriptor.getName(); 266 if (!StringUtils.isEmpty(shortName)) { 267 shortcutsRegistry.unregisterShortcut(shortName); 268 } 269 } 270 271 protected void doUnregisterResource(String qualifiedName) { 272 ObjectName objectName = ObjectNameFactory.getObjectName(qualifiedName); 273 doUnregisterResource(objectName); 274 } 275 276 protected void doUnregisterResource(ObjectName objectName) { 277 Resource resource = registry.remove(objectName); 278 if (resource == null) { 279 throw new IllegalArgumentException(objectName + " is not registered"); 280 } 281 if (resource.mbean != null) { 282 doUnbind(resource); 283 } 284 } 285 286 } 287 288 @Override 289 public void registerResource(String shortName, String qualifiedName, Class<?> managementClass, Object instance) { 290 resourcesRegistry.doRegisterResource(qualifiedName, managementClass, instance); 291 if (shortName != null) { 292 shortcutsRegistry.doRegisterShortcut(shortName, qualifiedName); 293 } 294 } 295 296 @Override 297 public void unregisterResource(String shortName, String qualifiedName) { 298 resourcesRegistry.doUnregisterResource(qualifiedName); 299 if (shortName != null) { 300 shortcutsRegistry.doUnregisterShortcut(shortName); 301 } 302 } 303 304 public void registerShortcut(String shortName, String qualifiedName) { 305 shortcutsRegistry.doRegisterShortcut(shortName, qualifiedName); 306 } 307 308 public void unregisterShortcut(String shortName) { 309 shortcutsRegistry.doUnregisterShortcut(shortName); 310 } 311 312 @Override 313 public Set<String> getShortcutsName() { 314 return new HashSet<>(shortcutsRegistry.registry.keySet()); 315 } 316 317 @Override 318 public Set<ObjectName> getResourcesName() { 319 return new HashSet<>(resourcesRegistry.registry.keySet()); 320 } 321 322 @Override 323 public ObjectName lookupName(String name) { 324 if (!shortcutsRegistry.registry.containsKey(name)) { 325 return ObjectNameFactory.getObjectName(name); 326 } 327 return shortcutsRegistry.registry.get(name); 328 } 329 330 protected void doBindResources() { 331 for (Resource resource : resourcesRegistry.registry.values()) { 332 if (resource.mbean == null) { 333 resourcesRegistry.doBind(resource); 334 } 335 } 336 } 337 338 @Override 339 public void bindResources() { 340 doBindResources(); 341 } 342 343 protected void doUnbindResources() { 344 for (Resource resource : resourcesRegistry.registry.values()) { 345 if (resource.mbean != null) { 346 resourcesRegistry.doUnbind(resource); 347 } 348 } 349 } 350 351 @Override 352 public void unbindResources() { 353 doUnbindResources(); 354 } 355 356 protected boolean started = false; 357 358 @Override 359 public void start(ComponentContext context) { 360 started = true; 361 factoriesRegistry.doRegisterResources(); 362 doBindResources(); 363 } 364 365 @Override 366 public void stop(ComponentContext context) { 367 started = false; 368 doUnbindResources(); 369 } 370 371 @Override 372 public void activate(ComponentContext context) { 373 serverLocatorService = (ServerLocatorService) Framework.getLocalService(ServerLocator.class); 374 } 375 376 @Override 377 public void deactivate(ComponentContext context) { 378 resourcesRegistry.doUnregisterResources(); 379 } 380 381 public void bindResource(ObjectName name) { 382 Resource resource = resourcesRegistry.registry.get(name); 383 if (resource == null) { 384 throw new IllegalArgumentException(name + " is not registered"); 385 } 386 resourcesRegistry.doBind(resource); 387 } 388 389 public void unbindResource(ObjectName name) { 390 Resource resource = resourcesRegistry.registry.get(name); 391 if (resource == null) { 392 throw new IllegalArgumentException(name + " is not registered"); 393 } 394 resourcesRegistry.doUnbind(resource); 395 } 396 397 protected void bindForTest(MBeanServer server, ObjectName name, Object instance, Class<?> clazz) 398 throws JMException, InvalidTargetObjectTypeException { 399 resourcesRegistry.doBind(server, name, instance, clazz); 400 } 401 402}