public boolean checkEvents(BundleEvent[] expevents) {
   boolean res = true;
   for (int i = 0; i < 20; i++) {
     try {
       Thread.sleep(100);
     } catch (InterruptedException ignore) {
     }
     if (events.size() == expevents.length) {
       break;
     }
   }
   if (events.size() == expevents.length) {
     for (int i = 0; i < events.size(); i++) {
       BundleEvent be = (BundleEvent) events.elementAt(i);
       if (!(be.getBundle().equals(expevents[i].getBundle())
           && be.getType() == expevents[i].getType())) {
         res = false;
       }
     }
   } else {
     res = false;
   }
   if (!res) {
     out.println("Real events");
     for (int i = 0; i < events.size(); i++) {
       BundleEvent be = (BundleEvent) events.elementAt(i);
       out.println("Event " + be.getBundle() + ", Type " + be.getType());
     }
     out.println("Expected events");
     for (int i = 0; i < expevents.length; i++) {
       out.println("Event " + expevents[i].getBundle() + ", Type " + expevents[i].getType());
     }
   }
   return res;
 }
  public long[] getBundleEvents() {
    synchronized (bundleEvents) {
      long[] r = new long[bundleEvents.size() * 2];
      int i = 0;

      for (Iterator it = bundleEvents.iterator(); it.hasNext(); ) {
        BundleEvent ev = (BundleEvent) it.next();
        r[i * 2] = ev.getBundle().getBundleId();
        r[i * 2 + 1] = ev.getType();
        i++;
      }
      bundleEvents.clear();
      return r;
    }
  }
 public void bundleChanged(BundleEvent evt) {
   if (evt.getType() == BundleEvent.STARTED || evt.getType() == BundleEvent.STOPPED) {
     events.addElement(evt);
   }
 }
  /**
   * Bottom level event dispatcher for the BundleContext.
   *
   * @param originalListener listener object registered under.
   * @param l listener to call (may be filtered).
   * @param action Event class type
   * @param object Event object
   */
  public void dispatchEvent(Object originalListener, Object l, int action, Object object) {
    // save the bundle ref to a local variable
    // to avoid interference from another thread closing this context
    AbstractBundle tmpBundle = bundle;
    Object previousTCCL = setContextFinder();
    try {
      if (isValid()) /* if context still valid */ {
        switch (action) {
          case Framework.BUNDLEEVENT:
          case Framework.BUNDLEEVENTSYNC:
            {
              BundleListener listener = (BundleListener) l;

              if (Debug.DEBUG_EVENTS) {
                String listenerName =
                    listener.getClass().getName()
                        + "@"
                        + Integer.toHexString(System.identityHashCode(listener)); // $NON-NLS-1$
                Debug.println(
                    "dispatchBundleEvent["
                        + tmpBundle
                        + "]("
                        + listenerName
                        + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
              }

              BundleEvent event = (BundleEvent) object;
              switch (event.getType()) {
                case Framework.BATCHEVENT_BEGIN:
                  {
                    if (listener instanceof BatchBundleListener)
                      ((BatchBundleListener) listener).batchBegin();
                    break;
                  }
                case Framework.BATCHEVENT_END:
                  {
                    if (listener instanceof BatchBundleListener)
                      ((BatchBundleListener) listener).batchEnd();
                    break;
                  }
                default:
                  {
                    listener.bundleChanged((BundleEvent) object);
                  }
              }
              break;
            }

          case ServiceRegistry.SERVICEEVENT:
            {
              ServiceEvent event = (ServiceEvent) object;

              ServiceListener listener = (ServiceListener) l;
              if (Debug.DEBUG_EVENTS) {
                String listenerName =
                    listener.getClass().getName()
                        + "@"
                        + Integer.toHexString(System.identityHashCode(listener)); // $NON-NLS-1$
                Debug.println(
                    "dispatchServiceEvent["
                        + tmpBundle
                        + "]("
                        + listenerName
                        + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
              }
              listener.serviceChanged(event);

              break;
            }

          case Framework.FRAMEWORKEVENT:
            {
              FrameworkListener listener = (FrameworkListener) l;

              if (Debug.DEBUG_EVENTS) {
                String listenerName =
                    listener.getClass().getName()
                        + "@"
                        + Integer.toHexString(System.identityHashCode(listener)); // $NON-NLS-1$
                Debug.println(
                    "dispatchFrameworkEvent["
                        + tmpBundle
                        + "]("
                        + listenerName
                        + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
              }

              listener.frameworkEvent((FrameworkEvent) object);
              break;
            }
          default:
            {
              throw new InternalError();
            }
        }
      }
    } catch (Throwable t) {
      if (Debug.DEBUG_GENERAL) {
        Debug.println(
            "Exception in bottom level event dispatcher: " + t.getMessage()); // $NON-NLS-1$
        Debug.printStackTrace(t);
      }
      // allow the adaptor to handle this unexpected error
      framework.adaptor.handleRuntimeError(t);
      publisherror:
      {
        if (action == Framework.FRAMEWORKEVENT) {
          FrameworkEvent event = (FrameworkEvent) object;
          if (event.getType() == FrameworkEvent.ERROR) {
            break publisherror; // avoid infinite loop
          }
        }

        framework.publishFrameworkEvent(FrameworkEvent.ERROR, tmpBundle, t);
      }
    } finally {
      if (previousTCCL != Boolean.FALSE)
        Thread.currentThread().setContextClassLoader((ClassLoader) previousTCCL);
    }
  }