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     */
019    package org.apache.felix.framework.util;
020    
021    import java.security.AccessController;
022    import java.security.PrivilegedAction;
023    import java.util.ArrayList;
024    import java.util.Collection;
025    import java.util.Dictionary;
026    import java.util.EventListener;
027    import java.util.EventObject;
028    import java.util.Iterator;
029    import java.util.List;
030    import java.util.NoSuchElementException;
031    
032    import org.apache.felix.framework.InvokeHookCallback;
033    import org.apache.felix.framework.Logger;
034    import org.apache.felix.framework.ServiceRegistry;
035    import org.osgi.framework.AllServiceListener;
036    import org.osgi.framework.Bundle;
037    import org.osgi.framework.BundleContext;
038    import org.osgi.framework.BundleEvent;
039    import org.osgi.framework.BundleListener;
040    import org.osgi.framework.Constants;
041    import org.osgi.framework.Filter;
042    import org.osgi.framework.FrameworkEvent;
043    import org.osgi.framework.FrameworkListener;
044    import org.osgi.framework.ServiceEvent;
045    import org.osgi.framework.ServiceListener;
046    import org.osgi.framework.ServicePermission;
047    import org.osgi.framework.ServiceReference;
048    import org.osgi.framework.ServiceRegistration;
049    import org.osgi.framework.SynchronousBundleListener;
050    import org.osgi.framework.hooks.service.EventHook;
051    import org.osgi.framework.hooks.service.ListenerHook;
052    import org.osgi.framework.launch.Framework;
053    
054    public class EventDispatcher
055    {
056        static final int LISTENER_BUNDLE_OFFSET = 0;
057        static final int LISTENER_CLASS_OFFSET = 1;
058        static final int LISTENER_OBJECT_OFFSET = 2;
059        static final int LISTENER_FILTER_OFFSET = 3;
060        static final int LISTENER_SECURITY_OFFSET = 4;
061        static final int LISTENER_ARRAY_INCREMENT = 5;
062    
063        private Logger m_logger = null;
064        private volatile ServiceRegistry m_serviceRegistry = null;
065    
066        // Representation of an empty listener list.
067        private static final Object[] m_emptyList = new Object[0];
068    
069        private Object[] m_frameworkListeners = m_emptyList;
070        private Object[] m_bundleListeners = m_emptyList;
071        private Object[] m_syncBundleListeners = m_emptyList;
072        private Object[] m_serviceListeners = m_emptyList;
073    
074        // A single thread is used to deliver events for all dispatchers.
075        private static Thread m_thread = null;
076        private final static String m_threadLock = new String("thread lock");
077        private static int m_references = 0;
078        private static volatile boolean m_stopping = false;
079    
080        // List of requests.
081        private static final ArrayList m_requestList = new ArrayList();
082        // Pooled requests to avoid memory allocation.
083        private static final ArrayList m_requestPool = new ArrayList();
084    
085        private EventDispatcher(Logger logger)
086        {
087            m_logger = logger;
088        }
089    
090        public static EventDispatcher start(Logger logger)
091        {
092            EventDispatcher eventDispatcher = new EventDispatcher(logger);
093    
094            synchronized (m_threadLock)
095            {
096                // Start event dispatching thread if necessary.
097                if (m_thread == null || !m_thread.isAlive())
098                {
099                    m_stopping = false;
100    
101                    m_thread = new Thread(new Runnable() {
102                        public void run()
103                        {
104                            try
105                            {
106                                EventDispatcher.run();
107                            }
108                            finally
109                            {
110                                // Ensure we update state even if stopped by external cause
111                                // e.g. an Applet VM forceably killing threads
112                                synchronized (m_threadLock)
113                                {
114                                    m_thread = null;
115                                    m_stopping = false;
116                                    m_references = 0;
117                                    m_threadLock.notifyAll();
118                                }
119                            }
120                        }
121                    }, "FelixDispatchQueue");
122                    m_thread.start();
123                }
124    
125                // reference counting and flags
126                m_references++;
127            }
128    
129            return eventDispatcher;
130        }
131    
132        public void setServiceRegistry(ServiceRegistry sr)
133        {
134            m_serviceRegistry = sr;
135        }
136    
137        public static void shutdown()
138        {
139            synchronized (m_threadLock)
140            {
141                // Return if already dead or stopping.
142                if (m_thread == null || m_stopping)
143                {
144                    return;
145                }
146    
147                // decrement use counter, don't continue if there are users
148                m_references--;
149                if (m_references > 0)
150                {
151                    return;
152                }
153    
154                m_stopping = true;
155            }
156    
157            // Signal dispatch thread.
158            synchronized (m_requestList)
159            {
160                m_requestList.notify();
161            }
162    
163            // Use separate lock for shutdown to prevent any chance of nested lock deadlock
164            synchronized (m_threadLock)
165            {
166                while (m_thread != null)
167                {
168                    try
169                    {
170                        m_threadLock.wait();
171                    }
172                    catch (InterruptedException ex)
173                    {
174                    }
175                }
176            }
177        }
178    
179        public Filter addListener(Bundle bundle, Class clazz, EventListener l, Filter filter)
180        {
181            // Verify the listener.
182            if (l == null)
183            {
184                throw new IllegalArgumentException("Listener is null");
185            }
186            else if (!clazz.isInstance(l))
187            {
188                throw new IllegalArgumentException(
189                    "Listener not of type " + clazz.getName());
190            }
191    
192            // See if we can simply update the listener, if so then
193            // return immediately.
194            Filter oldFilter = updateListener(bundle, clazz, l, filter);
195            if (oldFilter != null)
196            {
197                return oldFilter;
198            }
199    
200            // Lock the object to add the listener.
201            synchronized (this)
202            {
203                Object[] listeners = null;
204                Object acc = null;
205    
206                if (clazz == FrameworkListener.class)
207                {
208                    listeners = m_frameworkListeners;
209                }
210                else if (clazz == BundleListener.class)
211                {
212                    if (SynchronousBundleListener.class.isInstance(l))
213                    {
214                        listeners = m_syncBundleListeners;
215                    }
216                    else
217                    {
218                        listeners = m_bundleListeners;
219                    }
220                }
221                else if (clazz == ServiceListener.class)
222                {
223                    // Remember security context for filtering service events.
224                    Object sm = System.getSecurityManager();
225                    if (sm != null)
226                    {
227                        acc = ((SecurityManager) sm).getSecurityContext();
228                    }
229                    // We need to create a Set for keeping track of matching service
230                    // registrations so we can fire ServiceEvent.MODIFIED_ENDMATCH
231                    // events. We need a Set even if filter is null, since the
232                    // listener can be updated and have a filter added later.
233                    listeners = m_serviceListeners;
234                }
235                else
236                {
237                    throw new IllegalArgumentException("Unknown listener: " + l.getClass());
238                }
239    
240                // If we have no listeners, then just add the new listener.
241                if (listeners == m_emptyList)
242                {
243                    listeners = new Object[LISTENER_ARRAY_INCREMENT];
244                    listeners[LISTENER_BUNDLE_OFFSET] = bundle;
245                    listeners[LISTENER_CLASS_OFFSET] = clazz;
246                    listeners[LISTENER_OBJECT_OFFSET] = l;
247                    listeners[LISTENER_FILTER_OFFSET] = filter;
248                    listeners[LISTENER_SECURITY_OFFSET] = acc;
249                }
250                // Otherwise, we need to do some array copying.
251                // Notice, the old array is always valid, so if
252                // the dispatch thread is in the middle of a dispatch,
253                // then it has a reference to the old listener array
254                // and is not affected by the new value.
255                else
256                {
257                    Object[] newList = new Object[listeners.length + LISTENER_ARRAY_INCREMENT];
258                    System.arraycopy(listeners, 0, newList, 0, listeners.length);
259                    newList[listeners.length + LISTENER_BUNDLE_OFFSET] = bundle;
260                    newList[listeners.length + LISTENER_CLASS_OFFSET] = clazz;
261                    newList[listeners.length + LISTENER_OBJECT_OFFSET] = l;
262                    newList[listeners.length + LISTENER_FILTER_OFFSET] = filter;
263                    newList[listeners.length + LISTENER_SECURITY_OFFSET] = acc;
264                    listeners = newList;
265                }
266    
267                if (clazz == FrameworkListener.class)
268                {
269                    m_frameworkListeners = listeners;
270                }
271                else if (clazz == BundleListener.class)
272                {
273                    if (SynchronousBundleListener.class.isInstance(l))
274                    {
275                        m_syncBundleListeners = listeners;
276                    }
277                    else
278                    {
279                        m_bundleListeners = listeners;
280                    }
281                }
282                else if (clazz == ServiceListener.class)
283                {
284                    m_serviceListeners = listeners;
285                }
286            }
287            return null;
288        }
289        
290        public ListenerHook.ListenerInfo removeListener(
291            Bundle bundle, Class clazz, EventListener l)
292        {
293            ListenerHook.ListenerInfo listenerInfo = null;
294    
295            // Verify listener.
296            if (l == null)
297            {
298                throw new IllegalArgumentException("Listener is null");
299            }
300            else if (!clazz.isInstance(l))
301            {
302                throw new IllegalArgumentException(
303                    "Listener not of type " + clazz.getName());
304            }
305    
306            // Lock the object to remove the listener.
307            synchronized (this)
308            {
309                Object[] listeners = null;
310    
311                if (clazz == FrameworkListener.class)
312                {
313                    listeners = m_frameworkListeners;
314                }
315                else if (clazz == BundleListener.class)
316                {
317                    if (SynchronousBundleListener.class.isInstance(l))
318                    {
319                        listeners = m_syncBundleListeners;
320                    }
321                    else
322                    {
323                        listeners = m_bundleListeners;
324                    }
325                }
326                else if (clazz == ServiceListener.class)
327                {
328                    listeners = m_serviceListeners;
329                }
330                else
331                {
332                    throw new IllegalArgumentException("Unknown listener: " + l.getClass());
333                }
334    
335                // Try to find the instance in our list.
336                int idx = -1;
337                for (int i = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT)
338                {
339                    if (listeners[i + LISTENER_BUNDLE_OFFSET].equals(bundle) &&
340                        (listeners[i + LISTENER_CLASS_OFFSET] == clazz) &&
341                        (listeners[i + LISTENER_OBJECT_OFFSET] == l))
342                    {
343                        // For service listeners, we must return some info about
344                        // the listener for the ListenerHook callback.
345                        if (ServiceListener.class == clazz)
346                        {
347                            listenerInfo = wrapListener(listeners, i, true);
348                        }
349                        idx = i;
350                        break;
351                    }
352                }
353    
354                // If we have the instance, then remove it.
355                if (idx >= 0)
356                {
357                    // If this is the last listener, then point to empty list.
358                    if ((listeners.length - LISTENER_ARRAY_INCREMENT) == 0)
359                    {
360                        listeners = m_emptyList;
361                    }
362                    // Otherwise, we need to do some array copying.
363                    // Notice, the old array is always valid, so if
364                    // the dispatch thread is in the middle of a dispatch,
365                    // then it has a reference to the old listener array
366                    // and is not affected by the new value.
367                    else
368                    {
369                        Object[] newList  = new Object[listeners.length - LISTENER_ARRAY_INCREMENT];
370                        System.arraycopy(listeners, 0, newList, 0, idx);
371                        if (idx < newList.length)
372                        {
373                            System.arraycopy(
374                                listeners, idx + LISTENER_ARRAY_INCREMENT,
375                                newList, idx, newList.length - idx);
376                        }
377                        listeners = newList;
378                    }
379                }
380    
381                if (clazz == FrameworkListener.class)
382                {
383                    m_frameworkListeners = listeners;
384                }
385                else if (clazz == BundleListener.class)
386                {
387                    if (SynchronousBundleListener.class.isInstance(l))
388                    {
389                        m_syncBundleListeners = listeners;
390                    }
391                    else
392                    {
393                        m_bundleListeners = listeners;
394                    }
395                }
396                else if (clazz == ServiceListener.class)
397                {
398                    m_serviceListeners = listeners;
399                }
400            }
401    
402            // Return information about the listener; this is null
403            // for everything but service listeners.
404            return listenerInfo;
405        }
406    
407        public void removeListeners(Bundle bundle)
408        {
409            if (bundle == null)
410            {
411                return;
412            }
413    
414            synchronized (this)
415            {
416                // Remove all framework listeners associated with the specified bundle.
417                Object[] listeners = m_frameworkListeners;
418                for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
419                    i >= 0;
420                    i -= LISTENER_ARRAY_INCREMENT)
421                {
422                    // Check if the bundle associated with the current listener
423                    // is the same as the specified bundle, if so remove the listener.
424                    Bundle registeredBundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
425                    if (bundle.equals(registeredBundle))
426                    {
427                        Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
428                        EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
429                        removeListener(bundle, clazz, l);
430                    }
431                }
432    
433                // Remove all bundle listeners associated with the specified bundle.
434                listeners = m_bundleListeners;
435                for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
436                    i >= 0;
437                    i -= LISTENER_ARRAY_INCREMENT)
438                {
439                    // Check if the bundle associated with the current listener
440                    // is the same as the specified bundle, if so remove the listener.
441                    Bundle registeredBundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
442                    if (bundle.equals(registeredBundle))
443                    {
444                        Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
445                        EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
446                        removeListener(bundle, clazz, l);
447                    }
448                }
449    
450                // Remove all synchronous bundle listeners associated with
451                // the specified bundle.
452                listeners = m_syncBundleListeners;
453                for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
454                    i >= 0;
455                    i -= LISTENER_ARRAY_INCREMENT)
456                {
457                    // Check if the bundle associated with the current listener
458                    // is the same as the specified bundle, if so remove the listener.
459                    Bundle registeredBundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
460                    if (bundle.equals(registeredBundle))
461                    {
462                        Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
463                        EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
464                        removeListener(bundle, clazz, l);
465                    }
466                }
467    
468                // Remove all service listeners associated with the specified bundle.
469                listeners = m_serviceListeners;
470                for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
471                    i >= 0;
472                    i -= LISTENER_ARRAY_INCREMENT)
473                {
474                    // Check if the bundle associated with the current listener
475                    // is the same as the specified bundle, if so remove the listener.
476                    Bundle registeredBundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
477                    if (bundle.equals(registeredBundle))
478                    {
479                        Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
480                        EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
481                        removeListener(bundle, clazz, l);
482                    }
483                }
484            }
485        }
486    
487        public Filter updateListener(Bundle bundle, Class clazz, EventListener l, Filter filter)
488        {
489            synchronized (this)
490            {
491                Object[] listeners = null;
492    
493                if (clazz == FrameworkListener.class)
494                {
495                    listeners = m_frameworkListeners;
496                }
497                else if (clazz == BundleListener.class)
498                {
499                    if (SynchronousBundleListener.class.isInstance(l))
500                    {
501                        listeners = m_syncBundleListeners;
502                    }
503                    else
504                    {
505                        listeners = m_bundleListeners;
506                    }
507                }
508                else if (clazz == ServiceListener.class)
509                {
510                    listeners = m_serviceListeners;
511                }
512    
513                // See if the listener is already registered, if so then
514                // handle it according to the spec.
515                for (int i = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT)
516                {
517                    if (listeners[i + LISTENER_BUNDLE_OFFSET].equals(bundle) &&
518                        (listeners[i + LISTENER_CLASS_OFFSET] == clazz) &&
519                        (listeners[i + LISTENER_OBJECT_OFFSET] == l))
520                    {
521                        Filter oldFilter = null;
522                        if (clazz == FrameworkListener.class)
523                        {
524                            // The spec says to ignore this case.
525                        }
526                        else if (clazz == BundleListener.class)
527                        {
528                            // The spec says to ignore this case.
529                        }
530                        else if (clazz == ServiceListener.class)
531                        {
532                            // The spec says to update the filter in this case.
533                            oldFilter = (Filter) listeners[i + LISTENER_FILTER_OFFSET];
534                            listeners[i + LISTENER_FILTER_OFFSET] = filter;
535                        }
536                        return oldFilter;
537                    }
538                }
539            }
540    
541            return null;
542        }
543    
544        /**
545         * Returns all existing service listener information into a collection of
546         * ListenerHook.ListenerInfo objects. This is used the first time a listener
547         * hook is registered to synchronize it with the existing set of listeners.
548         * @return Returns all existing service listener information into a collection of
549         *         ListenerHook.ListenerInfo objects
550        **/
551        public Collection /* <? extends ListenerHook.ListenerInfo> */ wrapAllServiceListeners(boolean removed)
552        {
553            Object[] listeners = null;
554            synchronized (this)
555            {
556                listeners = m_serviceListeners;
557            }
558    
559            List existingListeners = new ArrayList();
560            for (int i = 0, j = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT, j++)
561            {
562                existingListeners.add(wrapListener(listeners, i, removed));
563            }
564            return existingListeners;
565        }
566    
567        /**
568         * Wraps the information about a given listener in a ListenerHook.ListenerInfo
569         * object.
570         * @param listeners The array of listeners.
571         * @param offset The offset into the array of the listener to wrap.
572         * @return A ListenerHook.ListenerInfo object for the specified listener.
573         */
574        private static ListenerHook.ListenerInfo wrapListener(Object[] listeners, int offset, boolean removed)
575        {
576            Filter filter = ((Filter)listeners[offset + LISTENER_FILTER_OFFSET]);
577    
578            return new ListenerHookInfoImpl(
579                ((Bundle)listeners[offset + LISTENER_BUNDLE_OFFSET]).getBundleContext(),
580                (ServiceListener) listeners[offset + LISTENER_OBJECT_OFFSET], 
581                filter == null ? null : filter.toString(),
582                removed);
583        }
584    
585        public void fireFrameworkEvent(FrameworkEvent event)
586        {
587            // Take a snapshot of the listener array.
588            Object[] listeners = null;
589            synchronized (this)
590            {
591                listeners = m_frameworkListeners;
592            }
593    
594            // Fire all framework listeners on a separate thread.
595            fireEventAsynchronously(m_logger, Request.FRAMEWORK_EVENT, listeners, event);
596        }
597    
598        public void fireBundleEvent(BundleEvent event)
599        {
600            // Take a snapshot of the listener array.
601            Object[] listeners = null;
602            Object[] syncListeners = null;
603            synchronized (this)
604            {
605                listeners = m_bundleListeners;
606                syncListeners = m_syncBundleListeners;
607            }
608    
609            // Fire synchronous bundle listeners immediately on the calling thread.
610            fireEventImmediately(
611                m_logger, Request.BUNDLE_EVENT, syncListeners, event, null);
612    
613            // The spec says that asynchronous bundle listeners do not get events
614            // of types STARTING, STOPPING, or LAZY_ACTIVATION.
615            if ((event.getType() != BundleEvent.STARTING) &&
616                (event.getType() != BundleEvent.STOPPING) &&
617                (event.getType() != BundleEvent.LAZY_ACTIVATION))
618            {
619                // Fire asynchronous bundle listeners on a separate thread.
620                fireEventAsynchronously(m_logger, Request.BUNDLE_EVENT, listeners, event);
621            }
622        }
623    
624        public void fireServiceEvent(
625            final ServiceEvent event, final Dictionary oldProps, final Framework felix)
626        {
627            // Take a snapshot of the listener array.
628            Object[] listeners = null;
629            synchronized (this)
630            {
631                listeners = m_serviceListeners;
632            }
633    
634            if (m_serviceRegistry != null)
635            {
636                List eventHooks = m_serviceRegistry.getEventHooks();
637                if ((eventHooks != null) && (eventHooks.size() > 0))
638                {
639                    final ListenerBundleContextCollectionWrapper wrapper =
640                        new ListenerBundleContextCollectionWrapper(listeners);
641                    InvokeHookCallback callback = new InvokeHookCallback() 
642                    {
643                        public void invokeHook(Object hook) 
644                        {
645                            ((EventHook) hook).event(event, wrapper);                            
646                        }                        
647                    }; 
648                    for (int i = 0; i < eventHooks.size(); i++)
649                    {
650                        if (felix != null) 
651                        {
652                            m_serviceRegistry.invokeHook(
653                                (ServiceReference) eventHooks.get(i), felix, callback);
654                        }
655                    }
656    
657                    listeners = wrapper.getListeners();
658                }
659            }
660    
661            // Fire all service events immediately on the calling thread.
662            fireEventImmediately(
663                m_logger, Request.SERVICE_EVENT, listeners, event, oldProps);
664        }
665    
666        private void fireEventAsynchronously(
667            Logger logger, int type, Object[] listeners, EventObject event)
668        {
669            //TODO: should possibly check this within thread lock, seems to be ok though without
670            // If dispatch thread is stopped, then ignore dispatch request.
671            if (m_stopping || m_thread == null)
672            {
673                return;
674            }
675    
676            // First get a request from the pool or create one if necessary.
677            Request req = null;
678            synchronized (m_requestPool)
679            {
680                if (m_requestPool.size() > 0)
681                {
682                    req = (Request) m_requestPool.remove(0);
683                }
684                else
685                {
686                    req = new Request();
687                }
688            }
689    
690            // Initialize dispatch request.
691            req.m_logger = logger;
692            req.m_type = type;
693            req.m_listeners = listeners;
694            req.m_event = event;
695    
696            // Lock the request list.
697            synchronized (m_requestList)
698            {
699                // Add our request to the list.
700                m_requestList.add(req);
701                // Notify the dispatch thread that there is work to do.
702                m_requestList.notify();
703            }
704        }
705    
706        private static void fireEventImmediately(
707            Logger logger, int type, Object[] listeners, EventObject event, Dictionary oldProps)
708        {
709            if (listeners.length > 0)
710            {
711                // Notify appropriate listeners.
712                for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
713                    i >= 0;
714                    i -= LISTENER_ARRAY_INCREMENT)
715                {
716                    Bundle bundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
717                    EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
718                    Filter filter = (Filter) listeners[i + LISTENER_FILTER_OFFSET];
719                    Object acc = listeners[i + LISTENER_SECURITY_OFFSET];
720                    try
721                    {
722                        if (type == Request.FRAMEWORK_EVENT)
723                        {
724                            invokeFrameworkListenerCallback(bundle, l, event);
725                        }
726                        else if (type == Request.BUNDLE_EVENT)
727                        {
728                            invokeBundleListenerCallback(bundle, l, event);
729                        }
730                        else if (type == Request.SERVICE_EVENT)
731                        {
732                            invokeServiceListenerCallback(
733                                bundle, l, filter, acc, event, oldProps);
734                        }
735                    }
736                    catch (Throwable th)
737                    {
738                        logger.log(
739                            Logger.LOG_ERROR,
740                            "EventDispatcher: Error during dispatch.", th);
741                    }
742                }
743            }
744        }
745    
746        private static void invokeFrameworkListenerCallback(
747            Bundle bundle, final EventListener l, final EventObject event)
748        {
749            // The spec says only active bundles receive asynchronous events,
750            // but we will include starting bundles too otherwise
751            // it is impossible to see everything.
752            if ((bundle.getState() == Bundle.STARTING) ||
753                (bundle.getState() == Bundle.ACTIVE))
754            {
755                if (System.getSecurityManager() != null)
756                {
757                    AccessController.doPrivileged(new PrivilegedAction() {
758                        public Object run()
759                        {
760                            ((FrameworkListener) l).frameworkEvent((FrameworkEvent) event);
761                            return null;
762                        }
763                    });
764                }
765                else
766                {
767                    ((FrameworkListener) l).frameworkEvent((FrameworkEvent) event);
768                }
769            }
770        }
771    
772        private static void invokeBundleListenerCallback(
773            Bundle bundle, final EventListener l, final EventObject event)
774        {
775            // A bundle listener is either synchronous or asynchronous.
776            // If the bundle listener is synchronous, then deliver the
777            // event to bundles with a state of STARTING, STOPPING, or
778            // ACTIVE. If the listener is asynchronous, then deliver the
779            // event only to bundles that are STARTING or ACTIVE.
780            if (((SynchronousBundleListener.class.isAssignableFrom(l.getClass())) &&
781                ((bundle.getState() == Bundle.STARTING) ||
782                (bundle.getState() == Bundle.STOPPING) ||
783                (bundle.getState() == Bundle.ACTIVE)))
784                ||
785                ((bundle.getState() == Bundle.STARTING) ||
786                (bundle.getState() == Bundle.ACTIVE)))
787            {
788                if (System.getSecurityManager() != null)
789                {
790                    AccessController.doPrivileged(new PrivilegedAction() {
791                        public Object run()
792                        {
793                            ((BundleListener) l).bundleChanged((BundleEvent) event);
794                            return null;
795                        }
796                    });
797                }
798                else
799                {
800                    ((BundleListener) l).bundleChanged((BundleEvent) event);
801                }
802            }
803        }
804    
805        private static void invokeServiceListenerCallback(
806            Bundle bundle, final EventListener l, Filter filter, Object acc,
807            final EventObject event, final Dictionary oldProps)
808        {
809            // Service events should be delivered to STARTING,
810            // STOPPING, and ACTIVE bundles.
811            if ((bundle.getState() != Bundle.STARTING) &&
812                (bundle.getState() != Bundle.STOPPING) &&
813                (bundle.getState() != Bundle.ACTIVE))
814            {
815                return;
816            }
817    
818            // Check that the bundle has permission to get at least
819            // one of the service interfaces; the objectClass property
820            // of the service stores its service interfaces.
821            ServiceReference ref = ((ServiceEvent) event).getServiceReference();
822    
823            boolean hasPermission = true;
824            Object sm = System.getSecurityManager();
825            if ((acc != null) && (sm != null))
826            {
827                try
828                {
829                    ServicePermission perm =
830                        new ServicePermission(
831                            ref, ServicePermission.GET);
832                    ((SecurityManager) sm).checkPermission(perm, acc);
833                }
834                catch (Exception ex)
835                {
836                    hasPermission = false;
837                }
838            }
839    
840            if (hasPermission)
841            {
842                // Dispatch according to the filter.
843                boolean matched = (filter == null)
844                    || filter.match(((ServiceEvent) event).getServiceReference());
845    
846                if (matched)
847                {
848                    if ((l instanceof AllServiceListener) ||
849                        Util.isServiceAssignable(bundle, ((ServiceEvent) event).getServiceReference()))
850                    {
851                        if (System.getSecurityManager() != null)
852                        {
853                            AccessController.doPrivileged(new PrivilegedAction() 
854                            {
855                                public Object run()
856                                {
857                                    ((ServiceListener) l).serviceChanged((ServiceEvent) event);
858                                    return null;
859                                }
860                            });
861                        }
862                        else
863                        {
864                            ((ServiceListener) l).serviceChanged((ServiceEvent) event);
865                        }
866                    }
867                }
868                // We need to send an MODIFIED_ENDMATCH event if the listener
869                // matched previously.
870                else if (((ServiceEvent) event).getType() == ServiceEvent.MODIFIED)
871                {
872                    if (filter.match(oldProps))
873                    {
874                        final ServiceEvent se = new ServiceEvent(
875                            ServiceEvent.MODIFIED_ENDMATCH,
876                            ((ServiceEvent) event).getServiceReference());
877                        if (System.getSecurityManager() != null)
878                        {
879                            AccessController.doPrivileged(new PrivilegedAction() 
880                            {
881                                public Object run()
882                                {
883                                    ((ServiceListener) l).serviceChanged(se);
884                                    return null;
885                                }
886                            });
887                        }
888                        else
889                        {
890                            ((ServiceListener) l).serviceChanged(se);
891                        }
892                    }
893                }
894            }
895        }
896    
897        /**
898         * This is the dispatching thread's main loop.
899        **/
900        private static void run()
901        {
902            Request req = null;
903            while (true)
904            {
905                // Lock the request list so we can try to get a
906                // dispatch request from it.
907                synchronized (m_requestList)
908                {
909                    // Wait while there are no requests to dispatch. If the
910                    // dispatcher thread is supposed to stop, then let the
911                    // dispatcher thread exit the loop and stop.
912                    while ((m_requestList.size() == 0) && !m_stopping)
913                    {
914                        // Wait until some signals us for work.
915                        try
916                        {
917                            m_requestList.wait();
918                        }
919                        catch (InterruptedException ex)
920                        {
921                            // Not much we can do here except for keep waiting.
922                        }
923                    }
924    
925                    // If there are no events to dispatch and shutdown
926                    // has been called then exit, otherwise dispatch event.
927                    if ((m_requestList.size() == 0) && (m_stopping))
928                    {
929                        return;
930                    }
931    
932                    // Get the dispatch request.
933                    req = (Request) m_requestList.remove(0);
934                }
935    
936                // Deliver event outside of synchronized block
937                // so that we don't block other requests from being
938                // queued during event processing.
939                // NOTE: We don't catch any exceptions here, because
940                // the invoked method shields us from exceptions by
941                // catching Throwables when it invokes callbacks.
942                fireEventImmediately(req.m_logger, req.m_type, req.m_listeners, req.m_event, null);
943    
944                // Put dispatch request in cache.
945                synchronized (m_requestPool)
946                {
947                    req.m_logger = null;
948                    req.m_type = -1;
949                    req.m_listeners = null;
950                    req.m_event = null;
951                    m_requestPool.add(req);
952                }
953            }
954        }
955    
956        static class ListenerBundleContextCollectionWrapper implements Collection
957        {
958            private Object[] m_listeners;
959    
960            ListenerBundleContextCollectionWrapper(Object [] listeners)
961            {
962                m_listeners = listeners;
963            }
964    
965            Object [] getListeners()
966            {
967                return m_listeners;
968            }
969    
970            public boolean add(Object o)
971            {
972                throw new UnsupportedOperationException();
973            }
974    
975            public boolean addAll(Collection c)
976            {
977                throw new UnsupportedOperationException();
978            }
979    
980            public void clear()
981            {
982                m_listeners = new Object[0];
983            }
984    
985            public boolean contains(Object o)
986            {
987                return indexOf(o) >= 0;
988            }
989    
990            public boolean containsAll(Collection c)
991            {
992                for (Iterator it = c.iterator(); it.hasNext(); )
993                {
994                    if (!contains(it.next()))
995                    {
996                        return false;
997                    }
998                }
999                return true;
1000            }
1001    
1002            private int indexOf(Object o)
1003            {
1004                if (!(o instanceof BundleContext))
1005                {
1006                    return -1;
1007                }
1008    
1009                for (int i = m_listeners.length - LISTENER_ARRAY_INCREMENT;
1010                    i >= 0;
1011                    i -= LISTENER_ARRAY_INCREMENT)
1012                {
1013                    Bundle bundle = (Bundle) m_listeners[i + LISTENER_BUNDLE_OFFSET];
1014                    if (bundle != null)
1015                    {
1016                        if (bundle.getBundleContext().equals(o))
1017                        {
1018                            return i;
1019                        }
1020                    }
1021                }
1022                return -1;
1023            }
1024    
1025            public boolean isEmpty()
1026            {
1027                return m_listeners.length == 0;
1028            }
1029    
1030            public Iterator iterator()
1031            {
1032                return new WrapperIterator();
1033            }
1034    
1035            public boolean remove(Object o)
1036            {
1037                return removeIndex(indexOf(o));
1038            }
1039    
1040            private boolean removeIndex(int idx)
1041            {
1042                if (idx < 0)
1043                {
1044                    return false;
1045                }
1046    
1047                Object [] newListeners = new Object[m_listeners.length - LISTENER_ARRAY_INCREMENT];
1048                System.arraycopy(m_listeners, 0, newListeners, 0, idx);
1049                System.arraycopy(m_listeners, idx + LISTENER_ARRAY_INCREMENT,
1050                        newListeners, idx, newListeners.length - idx);
1051                m_listeners = newListeners;
1052    
1053                return true;
1054            }
1055    
1056            public boolean removeAll(Collection c)
1057            {
1058                boolean rv = false;
1059    
1060                for (Iterator it = c.iterator(); it.hasNext(); )
1061                {
1062                    if (remove(it.next()))
1063                    {
1064                        rv = true;
1065                    }
1066                }
1067    
1068                return rv;
1069            }
1070    
1071            public boolean retainAll(Collection c)
1072            {
1073                boolean rv = false;
1074    
1075                for (Iterator it = iterator(); it.hasNext(); )
1076                {
1077                    if (!(c.contains(it.next())))
1078                    {
1079                        it.remove();
1080                        rv = true;
1081                    }
1082                }
1083    
1084                return rv;
1085            }
1086    
1087            public int size()
1088            {
1089                return m_listeners.length / LISTENER_ARRAY_INCREMENT;
1090            }
1091    
1092            public Object[] toArray()
1093            {
1094                Object [] array = new Object[size()];
1095                int idx = 0;
1096                for (Iterator it = iterator(); it.hasNext(); )
1097                {
1098                    array[idx++] = it.next();
1099                }
1100                return array;
1101            }
1102    
1103            public Object[] toArray(Object[] a)
1104            {
1105                if (!(a.getClass().equals(Object[].class)))
1106                {
1107                    throw new ArrayStoreException();
1108                }
1109                return toArray();
1110            }
1111    
1112            private class WrapperIterator implements Iterator
1113            {
1114                int curIdx = 0;
1115                int lastIdx = -1;
1116    
1117                private WrapperIterator() {}
1118    
1119                public boolean hasNext()
1120                {
1121                    return curIdx < m_listeners.length;
1122                }
1123    
1124                public Object next()
1125                {
1126                    if (!hasNext())
1127                    {
1128                        throw new NoSuchElementException();
1129                    }
1130    
1131                    Bundle b = (Bundle) m_listeners[curIdx + LISTENER_BUNDLE_OFFSET];
1132                    lastIdx = curIdx;
1133                    curIdx += LISTENER_ARRAY_INCREMENT;
1134                    return b.getBundleContext();
1135                }
1136    
1137                public void remove()
1138                {
1139                    if (lastIdx < 0)
1140                    {
1141                        throw new IllegalStateException();
1142                    }
1143                    removeIndex(lastIdx);
1144    
1145                    curIdx = lastIdx;
1146                    lastIdx = -1;
1147                }
1148            }
1149        }
1150    
1151        private static class Request
1152        {
1153            public static final int FRAMEWORK_EVENT = 0;
1154            public static final int BUNDLE_EVENT = 1;
1155            public static final int SERVICE_EVENT = 2;
1156    
1157            public Logger m_logger = null;
1158            public int m_type = -1;
1159            public Object[] m_listeners = null;
1160            public EventObject m_event = null;
1161        }
1162    }