| /* |
| * 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.felix.wireadmin; |
| |
| import org.osgi.framework.ServiceListener; |
| import org.osgi.framework.ServiceEvent; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.BundleContext; |
| |
| import java.util.Map; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| |
| import org.osgi.service.wireadmin.WireAdminEvent; |
| import org.osgi.service.wireadmin.WireAdminListener; |
| import org.osgi.service.wireadmin.WireConstants; |
| |
| /** |
| * Class that tracks listeners and manages event dispatching |
| * |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| class EventManager implements ServiceListener |
| { |
| // <ServiceReference,WireAdminListener> |
| private Map m_wireAdminListeners = new HashMap(); |
| |
| // The bundle context |
| private BundleContext m_bundleContext; |
| |
| // Asynchronous dispatcher |
| private AsyncDispatcher m_eventDispatcher = new AsyncDispatcher(); |
| |
| // Service reference from the WireAdmin service |
| private ServiceReference m_ref; |
| |
| /** |
| * The constructor receives the bundle context and a service reference |
| * corresponding to the wire admin service |
| * |
| * @param ctxt |
| * @param ref |
| */ |
| EventManager(BundleContext ctxt) |
| { |
| m_bundleContext = ctxt; |
| ServiceReference [] serviceRefs = null; |
| |
| try |
| { |
| m_bundleContext.addServiceListener(this,"(objectClass=org.osgi.service.wireadmin.WireAdminListener)"); |
| serviceRefs = m_bundleContext.getServiceReferences(WireAdminListener.class.getName(),null); |
| } |
| catch(Exception ex) |
| { |
| // Exception never thrown since filter is correct |
| } |
| |
| if(serviceRefs != null) |
| { |
| // lock the producers Map to avoid concurrent modifications due |
| // to service events |
| synchronized(m_wireAdminListeners) |
| { |
| for(int i=0;i<serviceRefs.length;i++) |
| { |
| ServiceReference currentRef=(ServiceReference)serviceRefs[i]; |
| |
| m_wireAdminListeners.put(currentRef,m_bundleContext.getService(currentRef)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * When the service reference is set the event dispatching thread is |
| * started. This is necessary since events require m_ref to be set |
| * |
| * @param ref <tt>ServiceReference</tt> to the wire admin service. |
| */ |
| void setServiceReference(ServiceReference ref) |
| { |
| m_ref = ref; |
| |
| // Activate thread that does asynchronous calls to |
| // the producersConnected and consummersConnected methods |
| new Thread(m_eventDispatcher).start(); |
| } |
| |
| /** |
| * Stop the event manager |
| * |
| */ |
| void stop() |
| { |
| m_eventDispatcher.stop(); |
| } |
| |
| /** |
| * Tracks Listener changes |
| */ |
| public void serviceChanged(ServiceEvent e) |
| { |
| ServiceReference serviceRef = e.getServiceReference(); |
| switch (e.getType()) |
| { |
| case ServiceEvent.REGISTERED: |
| synchronized(m_wireAdminListeners) |
| { |
| m_wireAdminListeners.put(serviceRef,m_bundleContext.getService(serviceRef)); |
| } |
| break; |
| case ServiceEvent.UNREGISTERING: |
| synchronized(m_wireAdminListeners) |
| { |
| m_wireAdminListeners.remove(serviceRef); |
| } |
| break; |
| case ServiceEvent.MODIFIED: |
| // Not necessary |
| break; |
| } |
| } |
| |
| /** |
| * Fire an event |
| * |
| * @param eventType (see WireAdminEvent) |
| * @param wire the wire |
| */ |
| void fireEvent(int eventType,WireImpl wire) |
| { |
| fireEvent(eventType, wire, null); |
| } |
| |
| /** |
| * Fire an event that contains an exception |
| * |
| * @param eventType |
| * @param wire |
| * @param exception |
| */ |
| void fireEvent(int eventType,WireImpl wire, Throwable exception) |
| { |
| WireAdminEvent evt = new WireAdminEvent(m_ref,eventType,wire,exception); |
| m_eventDispatcher.queueEvent(evt); |
| } |
| |
| /** |
| * p. 345 "A WireAdminEvent object can be sent asynchronously but must be |
| * ordered for each WireAdminListener service" |
| * But the API doc says "WireAdminEvent objects are delivered asynchronously |
| * to all registered WireAdminListener service objects which specify an |
| * interest in the WireAdminEvent type." |
| * We choose asynchronous delivery to be safe |
| * |
| **/ |
| class AsyncDispatcher implements Runnable |
| { |
| private boolean m_stop = false; |
| private boolean m_empty = true; |
| |
| private List m_eventStack = new ArrayList(); |
| |
| public void run() |
| { |
| while (m_stop == false || (m_stop == true && m_empty == false)) |
| { |
| WireAdminEvent nextEvent = null; |
| |
| synchronized (m_eventStack) |
| { |
| while (m_eventStack.size() == 0) |
| { |
| try |
| { |
| m_eventStack.wait(); |
| } |
| catch (InterruptedException ex) |
| { |
| // Ignore. |
| } |
| } |
| nextEvent = (WireAdminEvent) m_eventStack.remove(0); |
| |
| if(m_eventStack.size()==0) |
| { |
| // This allows the queue to be flushed upon termination |
| m_empty = true; |
| } |
| } |
| |
| synchronized (m_wireAdminListeners) |
| { |
| Iterator listenerIt = m_wireAdminListeners.keySet().iterator(); |
| while(listenerIt.hasNext()) |
| { |
| ServiceReference listenerRef = (ServiceReference)listenerIt.next(); |
| WireAdminListener listener = (WireAdminListener)m_wireAdminListeners.get(listenerRef); |
| |
| try |
| { |
| Integer evtsInteger = (Integer) listenerRef.getProperty(WireConstants.WIREADMIN_EVENTS); |
| if(evtsInteger != null) |
| { |
| int events = evtsInteger.intValue(); |
| if((nextEvent.getType()&events)!=0) |
| { |
| listener.wireAdminEvent(nextEvent); |
| } |
| } |
| else |
| { |
| WireAdminImpl.trace(new Exception("Listener with no WIREADMIN_EVENTS"+listenerRef)); |
| } |
| } |
| catch(ClassCastException ex) |
| { |
| WireAdminImpl.trace("Listener returned WIREADMIN_EVENTS of wrong type:"+ex); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Queue an event on the stack |
| * |
| * @param evt the event |
| */ |
| public void queueEvent(WireAdminEvent evt) |
| { |
| synchronized (m_eventStack) |
| { |
| m_eventStack.add(evt); |
| m_empty = false; |
| m_eventStack.notify(); |
| } |
| } |
| |
| /** |
| * stop the dispatcher |
| * |
| */ |
| void stop() |
| { |
| m_stop = true; |
| } |
| } |
| } |