/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 WARRANTIESOR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.aries.jmx.core.whiteboard; import java.util.Hashtable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; import org.apache.aries.jmx.Logger; import org.apache.aries.jmx.framework.BundleState; import org.apache.aries.jmx.framework.Framework; import org.apache.aries.jmx.framework.PackageState; import org.apache.aries.jmx.framework.ServiceState; import org.apache.aries.jmx.framework.StateConfig; import org.apache.aries.jmx.util.ObjectNameUtils; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceFactory; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.jmx.framework.BundleStateMBean; import org.osgi.jmx.framework.FrameworkMBean; import org.osgi.jmx.framework.PackageStateMBean; import org.osgi.jmx.framework.ServiceStateMBean; import org.osgi.jmx.service.cm.ConfigurationAdminMBean; import org.osgi.jmx.service.permissionadmin.PermissionAdminMBean; import org.osgi.jmx.service.provisioning.ProvisioningServiceMBean; import org.osgi.jmx.service.useradmin.UserAdminMBean; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.packageadmin.PackageAdmin; import org.osgi.service.permissionadmin.PermissionAdmin; import org.osgi.service.provisioning.ProvisioningService; import org.osgi.service.startlevel.StartLevel; import org.osgi.service.useradmin.UserAdmin; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; public class Activator implements BundleActivator, ServiceTrackerCustomizer { private ServiceTracker tracker; private BundleContext ctx; private ConcurrentMap<Long, ServiceRegistration> _provisioningMBeans = new ConcurrentHashMap<Long, ServiceRegistration>(); private ConcurrentMap<Long, ServiceRegistration> _userAdminMBeans = new ConcurrentHashMap<Long, ServiceRegistration>(); private ConcurrentMap<Long, ServiceRegistration> _configAdminMBeans = new ConcurrentHashMap<Long, ServiceRegistration>(); private AtomicReference<ServiceRegistration> _serviceStateMbean = new AtomicReference<ServiceRegistration>(); private AtomicReference<ServiceRegistration> _permissionAdminMbean = new AtomicReference<ServiceRegistration>(); private AtomicReference<ServiceRegistration> _packageStateMbean = new AtomicReference<ServiceRegistration>(); private AtomicReference<ServiceRegistration> _bundleState = new AtomicReference<ServiceRegistration>(); private AtomicReference<ServiceRegistration> _framework = new AtomicReference<ServiceRegistration>(); private AtomicReference<ServiceReference> _startLevel = new AtomicReference<ServiceReference>(); private AtomicReference<ServiceReference> _packageAdmin = new AtomicReference<ServiceReference>(); private static final String PACKAGE_ADMIN = "org.osgi.service.packageadmin.PackageAdmin"; private static final String START_LEVEL = "org.osgi.service.startlevel.StartLevel"; private static final String PERMISSION_ADMIN = "org.osgi.service.permissionadmin.PermissionAdmin"; private static final String CONFIG_ADMIN = "org.osgi.service.cm.ConfigurationAdmin"; private static final String USER_ADMIN = "org.osgi.service.useradmin.UserAdmin"; private static final String PROVISIONING_SERVICE = "org.osgi.service.provisioning.ProvisioningService"; private Logger logger; private StateConfig stateConfig; private class MBeanServiceProxy<T> implements ServiceFactory { private Factory<T> objectFactory; private AtomicReference<T> result = new AtomicReference<T>(); private MBeanServiceProxy(Factory<T> factory) { objectFactory = factory; } public Object getService(Bundle bundle, ServiceRegistration registration) { if (result.get() == null) { result.compareAndSet(null, objectFactory.create()); } return result.get(); } public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { } } private interface Factory<T> { public abstract T create(); } private abstract class BaseFactory<T> implements Factory<T> { public abstract T create(PackageAdmin pa, StartLevel sl); public final T create() { StartLevel sl = null; PackageAdmin pa = null; ServiceReference slRef = _startLevel.get(); if (slRef != null) { sl = (StartLevel) ctx.getService(slRef); } ServiceReference paRef = _packageAdmin.get(); if (paRef != null) { pa = (PackageAdmin) ctx.getService(paRef); } if (pa == null) { ctx.ungetService(slRef); } if (sl != null && pa != null) { return create(pa, sl); } else { return null; } } } public void start(BundleContext context) throws Exception { ctx = context; logger = new Logger(ctx); Filter filter = getFilter(context, PACKAGE_ADMIN, START_LEVEL, PERMISSION_ADMIN, CONFIG_ADMIN, USER_ADMIN, PROVISIONING_SERVICE); tracker = new ServiceTracker(context, filter, this); tracker.open(); stateConfig = StateConfig.register(context); registerMBean(ServiceStateMBean.class.getName(), new Factory<ServiceStateMBean>() { public ServiceStateMBean create() { return new ServiceState(ctx, stateConfig, logger); } }, ServiceStateMBean.OBJECTNAME, _serviceStateMbean ); } private Filter getFilter(BundleContext ctx, String ... services) throws InvalidSyntaxException { StringBuilder builder = new StringBuilder("(|"); for (String type : services) { builder.append('('); builder.append(Constants.OBJECTCLASS); builder.append('='); builder.append(type); builder.append(')'); } builder.append(')'); return ctx.createFilter(builder.toString()); } public void stop(BundleContext context) throws Exception { stateConfig = null; tracker.close(); } public Object addingService(ServiceReference reference) { Object tracked = null; String[] types = (String[]) reference.getProperty(Constants.OBJECTCLASS); for (String t : types) { if (PACKAGE_ADMIN.equals(t)) { foundPackageAdmin(reference); tracked = reference; } else if (START_LEVEL.equals(t)) { foundStartLevel(reference); tracked = reference; } else if (PERMISSION_ADMIN.equals(t)) { foundPermissionAdmin(reference); tracked = reference; } else if (CONFIG_ADMIN.equals(t)) { foundConfigAdmin(reference); tracked = reference; } else if (USER_ADMIN.equals(t)) { foundUserAdmin(reference); tracked = reference; } else if (PROVISIONING_SERVICE.equals(t)) { foundProvisioningService(reference); tracked = reference; } } return tracked; } private <T> void registerMBean(String type, Factory<T> factory, String objectName, AtomicReference<ServiceRegistration> result) { synchronized (result) { ServiceRegistration reg = registerAnMbean(type, factory, objectName); if (!!!result.compareAndSet(null, reg)) { reg.unregister(); } } } private <T> void registerMBean(String type, Factory<T> factory, String objectName, ConcurrentMap<Long, ServiceRegistration> mbeans, ServiceReference referencedServices, String underlyingType) { try { Class.forName(underlyingType); if (referencedServices.isAssignableTo(ctx.getBundle(), underlyingType)) { ServiceRegistration reg = registerAnMbean(type, factory, objectName); Long id = (Long) reg.getReference().getProperty(Constants.SERVICE_ID); mbeans.put(id, reg); } } catch (ClassNotFoundException e) { } } private <T> ServiceRegistration registerAnMbean(String type, Factory<T> factory, String objectName) { Hashtable<String, Object> properties = new Hashtable<String, Object>(); properties.put("jmx.objectname", ObjectNameUtils.createFullObjectName(ctx, objectName)); Object service = new MBeanServiceProxy<T>(factory); ServiceRegistration reg = ctx.registerService(type, service, properties); return reg; } private void foundPermissionAdmin(final ServiceReference reference) { registerMBean(PermissionAdminMBean.class.getName(), new Factory<PermissionAdminMBean>() { public PermissionAdminMBean create() { PermissionAdmin service = (PermissionAdmin) ctx.getService(reference); if (service == null) return null; else return new org.apache.aries.jmx.permissionadmin.PermissionAdmin(service); } }, PermissionAdminMBean.OBJECTNAME, _permissionAdminMbean); } private void foundProvisioningService(final ServiceReference reference) { registerMBean(ProvisioningServiceMBean.class.getName(), new Factory<ProvisioningServiceMBean>() { public ProvisioningServiceMBean create() { ProvisioningService service = (ProvisioningService) ctx.getService(reference); if (service == null) return null; else return new org.apache.aries.jmx.provisioning.ProvisioningService(service); } }, ProvisioningServiceMBean.OBJECTNAME, _provisioningMBeans, reference, PROVISIONING_SERVICE); } private void foundUserAdmin(final ServiceReference reference) { try { Class.forName(USER_ADMIN); if (reference.isAssignableTo(ctx.getBundle(), USER_ADMIN)) { registerMBean(UserAdminMBean.class.getName(), new Factory<UserAdminMBean>() { public UserAdminMBean create() { UserAdmin service = (UserAdmin) ctx.getService(reference); if (service == null) return null; else return new org.apache.aries.jmx.useradmin.UserAdmin(service); } }, UserAdminMBean.OBJECTNAME, _userAdminMBeans, reference, USER_ADMIN); } } catch (ClassNotFoundException e) { } } private void foundConfigAdmin(final ServiceReference reference) { registerMBean(ConfigurationAdminMBean.class.getName(), new Factory<ConfigurationAdminMBean>() { public ConfigurationAdminMBean create() { ConfigurationAdmin service = (ConfigurationAdmin) ctx.getService(reference); if (service == null) return null; else return new org.apache.aries.jmx.cm.ConfigurationAdmin(service); } }, ConfigurationAdminMBean.OBJECTNAME, _configAdminMBeans, reference, CONFIG_ADMIN); } private void foundStartLevel(final ServiceReference reference) { if (_startLevel.compareAndSet(null, reference)) { registerBundleStateAndFrameworkIfPossible(); } } private void foundPackageAdmin(final ServiceReference reference) { registerMBean(PackageStateMBean.class.getName(), new Factory<PackageStateMBean>() { public PackageStateMBean create() { PackageAdmin service = (PackageAdmin) ctx.getService(reference); if (service == null) return null; else return new PackageState(ctx, service); } }, PackageStateMBean.OBJECTNAME, _packageStateMbean); if (_packageAdmin.compareAndSet(null, reference)) { registerBundleStateAndFrameworkIfPossible(); } } // This method is synchronized to ensure that notification of StartLevel and PackageAdmin // on different threads at the same time doesn't cause problems. It only affects these services // so it shouldn't be too expensive. private synchronized void registerBundleStateAndFrameworkIfPossible() { if (_bundleState.get() == null && _startLevel.get() != null && _packageAdmin.get() != null) { registerMBean(BundleStateMBean.class.getName(), new BaseFactory<BundleStateMBean>() { @Override public BundleStateMBean create(PackageAdmin pa, StartLevel sl) { return new BundleState(ctx, pa, sl, stateConfig, logger); } }, BundleStateMBean.OBJECTNAME, _bundleState); } if (_framework.get() == null && _startLevel.get() != null && _packageAdmin.get() != null) { registerMBean(FrameworkMBean.class.getName(), new BaseFactory<FrameworkMBean>() { @Override public FrameworkMBean create(PackageAdmin pa, StartLevel sl) { return new Framework(ctx, sl, pa); } }, FrameworkMBean.OBJECTNAME, _framework); } } public void modifiedService(ServiceReference reference, Object service) { } public void removedService(ServiceReference reference, Object service) { String[] types = (String[]) reference.getProperty(Constants.OBJECTCLASS); for (String t : types) { if (PACKAGE_ADMIN.equals(t)) { lostPackageAdmin(reference); } else if (START_LEVEL.equals(t)) { lostStartLevel(reference); } else if (PERMISSION_ADMIN.equals(t)) { lostPermissionAdmin(reference); } else if (CONFIG_ADMIN.equals(t)) { lostConfigAdmin(reference); } else if (USER_ADMIN.equals(t)) { lostUserAdmin(reference); } else if (PROVISIONING_SERVICE.equals(t)) { lostProvisioningService(reference); } } } private void lostProvisioningService(ServiceReference reference) { unregister(reference, _provisioningMBeans); } private void lostUserAdmin(ServiceReference reference) { unregister(reference, _userAdminMBeans); } private void lostConfigAdmin(ServiceReference reference) { unregister(reference, _configAdminMBeans); } private void unregister(ServiceReference reference, ConcurrentMap<Long, ServiceRegistration> mbeans) { Long id = (Long) reference.getProperty(Constants.SERVICE_ID); ServiceRegistration reg = mbeans.remove(id); if (reg != null) reg.unregister(); } private void lostPermissionAdmin(ServiceReference reference) { safeUnregister(_permissionAdminMbean); } private void lostStartLevel(ServiceReference reference) { if (_startLevel.compareAndSet(reference, null)) { safeUnregister(_bundleState); safeUnregister(_framework); } } private void lostPackageAdmin(ServiceReference reference) { if (_packageAdmin.compareAndSet(reference, null)) { safeUnregister(_bundleState); safeUnregister(_framework); safeUnregister(_packageStateMbean); } } private void safeUnregister(AtomicReference<ServiceRegistration> atomicRegistration) { ServiceRegistration reg = atomicRegistration.getAndSet(null); if (reg != null) reg.unregister(); } }