001/* 
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.felix.ipojo;
020
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027
028import org.osgi.framework.BundleContext;
029import org.osgi.framework.Constants;
030import org.osgi.framework.ServiceEvent;
031import org.osgi.framework.ServiceListener;
032
033/**
034 * iPOJO Internal event dispatcher.
035 * This class provides an internal service event dispatcher in order to tackle the
036 * event storm that can happen when starting large-scale applications.
037 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
038 * @see org.osgi.framework.ServiceListener
039 */
040public final class EventDispatcher implements ServiceListener {
041    
042    /**
043     * The internal event dispatcher.
044     * This dispatcher is a singleton.
045     */
046    private static EventDispatcher DISPATCHER;
047    
048    /**
049     * The list of listeners.
050     * Service interface -> List of {@link ServiceListener}
051     */
052    private Map m_listeners;
053    /**
054     * The global bundle context.
055     * This is the bundle context from iPOJO.
056     */
057    private BundleContext m_context;
058    
059    /**
060     * Creates the EventDispatcher.
061     * @param bc the bundle context used to register and unregister
062     * {@link ServiceListener}.
063     */
064    private EventDispatcher(BundleContext bc) {
065        m_context = bc;
066        m_listeners = new HashMap();
067        // Only one thread can call the start method.
068        m_context.addServiceListener(this);
069    }
070    
071    /**
072     * Creates the internal event
073     * dispatcher.
074     * @param bc the iPOJO bundle context to send to the 
075     * internal event dispatcher.
076     */
077    public static void create(BundleContext bc) {
078        DISPATCHER = new EventDispatcher(bc);
079    }
080    
081    /**
082     * Stops and delete the internal event dispatcher.
083     * This method must be call only
084     * if iPOJO is stopping.
085     */
086    public static void dispose() {
087        DISPATCHER.stop();
088        DISPATCHER = null;
089    }
090    
091    
092    /**
093     * Gets the iPOJO event dispatcher.
094     * @return the event dispatcher or
095     * <code>null</code> if not created.
096     */
097    public static EventDispatcher getDispatcher() {
098        return DISPATCHER;
099    }
100    
101    
102    /**
103     * Stops the event dispatcher.
104     * This method unregisters the {@link ServiceListener}.
105     * This methods must be called only when the iPOJO bundle
106     * stops.
107     */
108    private void stop() {
109        synchronized (this) {
110            m_context.removeServiceListener(this);
111            m_listeners.clear();
112        }
113    }
114
115    /**
116     * Method called when a {@link ServiceEvent} is
117     * fired by the OSGi framework. 
118     * According to the event, this method dispatches
119     * to interested registered listers from
120     * the {@link EventDispatcher#m_listeners} map.
121     * @param event the service event
122     * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
123     */
124    public void serviceChanged(ServiceEvent event) {
125        String[] itfs = (String[]) event.getServiceReference().getProperty(Constants.OBJECTCLASS);
126        for (int s = 0; s < itfs.length; s++) {
127            List list;
128            synchronized (this) {
129                List stored = (List) m_listeners.get(itfs[s]);
130                if (stored == null) { 
131                    return; // Nothing to do
132                }
133                // Creates a new list (stack confinement)
134                list = new ArrayList(stored);
135            }
136            for (int i = 0; i < list.size(); i++) {
137                ((ServiceListener) list.get(i)).serviceChanged(event);
138            }
139        }
140    }
141    
142    /**
143     * Adds a new service listener to the {@link EventDispatcher#m_listeners}
144     * map. This method specifies the listen service interface
145     * @param itf the service interface
146     * @param listener the service listener
147     */
148    public void addListener(String itf, ServiceListener listener) {
149        synchronized (this) {
150            List list = (List) m_listeners.get(itf);
151            if (list == null) {
152                list = new ArrayList(1);
153                list.add(listener);
154                m_listeners.put(itf, list);
155            } else {
156                list.add(listener);
157            }
158        }
159    }
160    
161    /**
162     * Removes a service listener.
163     * @param listener the service listener to remove
164     * @return <code>true</code> if the listener is 
165     * successfully removed.
166     */
167    public boolean removeListener(ServiceListener listener) {
168        boolean removed = false;
169        synchronized (this) {
170            Set keys = m_listeners.keySet();
171            Iterator it = keys.iterator();
172            while (it.hasNext()) {
173                String itf = (String) it.next();
174                List list = (List) m_listeners.get(itf);
175                removed = removed || list.remove(listener);
176            }
177        }
178        return removed;
179    }
180
181}