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