/* * 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 WARRANTIES OR 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.blueprint.impl; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import javax.management.MBeanNotificationInfo; import javax.management.MBeanRegistration; import javax.management.MBeanServer; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import javax.management.ObjectName; import javax.management.openmbean.CompositeData; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; import org.apache.aries.jmx.blueprint.BlueprintStateMBean; import org.apache.aries.jmx.blueprint.codec.OSGiBlueprintEvent; import org.apache.aries.util.AriesFrameworkUtil; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.service.blueprint.container.BlueprintEvent; import org.osgi.service.blueprint.container.BlueprintListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BlueprintState extends NotificationBroadcasterSupport implements BlueprintStateMBean, MBeanRegistration { // notification type description public static String BLUEPRINT_EVENT = "org.osgi.blueprint.event"; private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintState.class); private BundleContext context; private ServiceRegistration listenerReg; private Map<Long, CompositeData> dataMap = new HashMap<Long, CompositeData>(); private ExecutorService eventDispatcher; private AtomicInteger notificationSequenceNumber = new AtomicInteger(1); private AtomicInteger registrations = new AtomicInteger(0); public BlueprintState(BundleContext context) { this.context = context; } public synchronized long[] getBlueprintBundleIds() throws IOException { Long[] bundleIdKeys = dataMap.keySet().toArray(new Long[dataMap.size()]); long[] bundleIds = new long[bundleIdKeys.length]; for (int i = 0; i < bundleIdKeys.length; i++) { bundleIds[i] = bundleIdKeys[i]; } return bundleIds; } public synchronized CompositeData getLastEvent(long bundleId) throws IOException { return dataMap.get(bundleId); } public synchronized TabularData getLastEvents() throws IOException { TabularDataSupport table = new TabularDataSupport(BlueprintStateMBean.OSGI_BLUEPRINT_EVENTS_TYPE); table.putAll(dataMap); return table; } public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { // no op return name; } public void postRegister(Boolean registrationDone) { // reg listener if (registrationDone && registrations.incrementAndGet() == 1) { BlueprintListener listener = new BlueprintStateListener(); eventDispatcher = Executors.newSingleThreadExecutor(new JMXThreadFactory("JMX OSGi Blueprint State Event Dispatcher")); listenerReg = context.registerService(BlueprintListener.class.getName(), listener, null); } } public void preDeregister() throws Exception { // no op } public void postDeregister() { if (registrations.decrementAndGet() < 1) { AriesFrameworkUtil.safeUnregisterService(listenerReg); if (eventDispatcher != null) { eventDispatcher.shutdown(); } } } protected synchronized void onEvent(BlueprintEvent event) { CompositeData data = new OSGiBlueprintEvent(event).asCompositeData(); dataMap.put(event.getBundle().getBundleId(), data); if (!event.isReplay()) { final Notification notification = new Notification(EVENT_TYPE, OBJECTNAME, notificationSequenceNumber.getAndIncrement()); try { notification.setUserData(data); eventDispatcher.submit(new Runnable() { public void run() { sendNotification(notification); } }); } catch (RejectedExecutionException re) { LOGGER.warn("Task rejected for JMX Notification dispatch of event [" + event + "] - Dispatcher may have been shutdown"); } catch (Exception e) { LOGGER.warn("Exception occured on JMX Notification dispatch for event [" + event + "]", e); } } } /** * @see javax.management.NotificationBroadcasterSupport#getNotificationInfo() */ @Override public MBeanNotificationInfo[] getNotificationInfo() { String[] types = new String[] { BLUEPRINT_EVENT }; String name = Notification.class.getName(); String description = "A BlueprintEvent issued from the Blueprint Extender describing a blueprint bundle lifecycle change"; MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description); return new MBeanNotificationInfo[] { info }; } private class BlueprintStateListener implements BlueprintListener { public void blueprintEvent(BlueprintEvent event) { onEvent(event); } } public static class JMXThreadFactory implements ThreadFactory { private final ThreadFactory factory = Executors.defaultThreadFactory(); private final String name; public JMXThreadFactory(String name) { this.name = name; } public Thread newThread(Runnable r) { final Thread t = factory.newThread(r); t.setName(name); t.setDaemon(true); return t; } } }