content: /*
   * 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;
          }
      }
  
  }
  