Package org.apache.felix.wireadmin

Source Code of org.apache.felix.wireadmin.WireAdminImpl$AsyncMethodCaller

/*
* 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 java.io.PrintStream;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;

import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.wireadmin.Consumer;
import org.osgi.service.wireadmin.Producer;
import org.osgi.service.wireadmin.Wire;
import org.osgi.service.wireadmin.WireAdmin;
import org.osgi.service.wireadmin.WireConstants;
import org.osgi.service.wireadmin.WireAdminEvent;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;

/**
* Wire Administration service.
*
* <p>This service can be used to create <tt>Wire</tt> objects connecting
* a Producer service and a Consumer service.
* <tt>Wire</tt> objects also have wire properties that may be specified
* when a <tt>Wire</tt> object is created. The Producer and Consumer
* services may use the <tt>Wire</tt> object's properties to manage or control their
* interaction.
* The use of <tt>Wire</tt> object's properties by a Producer or Consumer
* services is optional.
*
* <p>Security Considerations.
* A bundle must have <tt>ServicePermission[GET,WireAdmin]</tt> to get the Wire Admin service to
* create, modify, find, and delete <tt>Wire</tt> objects.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class WireAdminImpl implements WireAdmin, ServiceListener {

  private BundleContext m_bundleContext;

    // A Map containing a service reference associated to a producer and a List
    // of wire objects 
    private Map m_consumers = new HashMap(); /* ServiceReferences, List */
 
    private Map m_producers = new HashMap(); /* ServiceReferences, List */

  private List m_wires; // List containing the wires

  //private BindingController wireAdminListenersBindingController;

    // Filter corresponding to a consumer service
  private Filter m_consumerFilter;
   
    //  Filter corresponding to a producer service
  private Filter m_producerFilter;

    // EventManager
    private EventManager m_eventManager;

  private static int m_wireCount = 0;
   
    private AsyncMethodCaller m_asyncMethodCaller = new AsyncMethodCaller();        //m_eventDispatcher.stop();
   
    private static PrintStream m_traceout = null;
   
    private static PrintStream m_errorout = System.err;

  /**
     * Constructor with package visibility
     *
   * @param bundleContext the bundle context
   */
  WireAdminImpl(BundleContext bundleContext)
    {
    m_bundleContext = bundleContext;
       
        if(bundleContext.getProperty("fr.imag.adele.wireadmin.trace") != null)
        {
            String value = bundleContext.getProperty("fr.imag.adele.wireadmin.trace");
            if(value.equals("true"))
            {
                m_traceout = System.out;
            }
        }
        // Create the event manager (the event manager will start its own thread)      
        m_eventManager = new EventManager(m_bundleContext);
       
    try
        {
      m_producerFilter = m_bundleContext.createFilter(
          "(objectClass=org.osgi.service.wireadmin.Producer)");
      m_consumerFilter = m_bundleContext.createFilter(
          "(objectClass=org.osgi.service.wireadmin.Consumer)");
    }
        catch (InvalidSyntaxException e)
        {
      // never thrown since LDAP expressions are correct
    }

        // Recover persistent wires
        getPersistentWires();

        // Activate thread that does asynchronous calls to
        // the producersConnected and consummersConnected methods
        new Thread(m_asyncMethodCaller).start();

        // Gets all producers and consumers that are present at the
        // moment the wire admin is created
        try
        {
            // Registration for events must be done first, as some service
            // can be registered during initialization
           
            m_bundleContext.addServiceListener(this,"(|"+m_producerFilter.toString()+m_consumerFilter.toString()+")");

            // Replacement for the two following lines which work under OSCAR,
            // but not work under IBM's SMF
            //m_bundleContext.addServiceListener(this,m_consumerFilter.toString());
            //m_bundleContext.addServiceListener(this,m_producerFilter.toString());
           
            // Get all producers
            ServiceReference[] producerRefs = m_bundleContext.getServiceReferences(Producer.class.getName(),null);
           
            if(producerRefs!=null)
            {
                // lock the producers Map to avoid concurrent modifications due
                // to service events
                synchronized(m_producers)
                {
                    for(int i=0;i<producerRefs.length;i++)
                    {
                        ServiceReference currentRef=(ServiceReference)producerRefs[i];
                       
                        Iterator wireIt = m_wires.iterator();
                        while(wireIt.hasNext())
                        {
                            WireImpl currentWire = (WireImpl) wireIt.next();
                            if(currentWire.getProducerPID().equals(currentRef.getProperty(Constants.SERVICE_PID)))
                            {
                                currentWire.bindProducer(currentRef);
                            }
                        }
                        m_producers.put(currentRef,new ArrayList());
                    }
                }
            }

            // Get all the consumers
            ServiceReference[] consumerRefs = m_bundleContext.getServiceReferences(Consumer.class.getName(),null);
           
            if(consumerRefs!=null)
            {
                for(int i=0;i<consumerRefs.length;i++)
                {
                    // lock the consumers to avoid concurrent modifications due
                    // to service events
                    synchronized(m_consumers)
                    {
                        ServiceReference currentRef=(ServiceReference)consumerRefs[i];

                        Iterator wireIt = m_wires.iterator();
                        while(wireIt.hasNext())
                        {
                            WireImpl currentWire = (WireImpl) wireIt.next();
                            if(currentWire.getConsumerPID().equals(currentRef.getProperty(Constants.SERVICE_PID)))
                            {
                                currentWire.bindConsumer(currentRef);
                            }
                        }
                        m_consumers.put(currentRef,new ArrayList());
                    }
                }
            }
        }
        catch (InvalidSyntaxException e)
        {
            trace(e);
        }

        // Iterate over all the wires, when a wire is connected
        // add it to the list of wires associated to a particular
        // producer or consumer
        synchronized(m_wires)
        {
            Iterator wireIterator = m_wires.iterator();
            while(wireIterator.hasNext())
            {
                WireImpl currentWire = (WireImpl) wireIterator.next();
                if(currentWire.isConnected())
                {               
                    // p. 327 "If both Producer and consumer services are registered
                    // with the framework, they are connected by the WireAdmin service"
                    List wires = (List) m_producers.get(currentWire.getProducerServiceRef());
                    wires.add(currentWire);
                    m_asyncMethodCaller.consumersConnected(currentWire.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()]));
                   
                    wires = (List) m_consumers.get(currentWire.getConsumerServiceRef());
                    wires.add(currentWire);
                    m_asyncMethodCaller.producersConnected(currentWire.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()]));
                }           
            }
        }      
  }
   
    /**
     * Pass the service reference to the event dispatcher
     *
     * @param ref the service reference
     */
    void setServiceReference(ServiceReference ref)
    {
        m_eventManager.setServiceReference(ref);
    }

  /**
   * Create a new <tt>Wire</tt> object that connects a Producer
   * service to a Consumer service.
   *
   * The Producer service and Consumer service do not
   * have to be registered when the <tt>Wire</tt> object is created.
   *
   * <p>The <tt>Wire</tt> configuration data must be persistently stored.
   * All <tt>Wire</tt> connections are reestablished when the
   * <tt>WireAdmin</tt> service is registered.
   * A <tt>Wire</tt> can be permanently removed by using the
   * {@link #deleteWire} method.
   *
   * <p>The <tt>Wire</tt> object's properties must have case
   * insensitive <tt>String</tt> objects as keys (like the Framework).
   * However, the case of the key must be preserved.
   * The type of the value of the property must be one of the following:
   *
   * <pre>
   * type        = basetype
   *  | vector | arrays
   *
   * basetype = String | Integer | Long
   *  | Float | Double | Byte
   *  | Short | Character
   *  | Boolean
   *
   * primitive   = long | int | short
   *  | char | byte | double | float
   *
   * arrays   = primitive '[]' | basetype '[]'
   *
   * vector   = Vector of basetype
   * </pre>
   *
   * <p>The <tt>WireAdmin</tt> service must automatically add the
   * following <tt>Wire</tt> properties:
   * <ul>
   * <li>
   * {@link WireConstants#WIREADMIN_PID} set to the value of the <tt>Wire</tt> object's
   * persistent identity (PID). This value is generated by the
   * Wire Admin service when a <tt>Wire</tt> object is created.
   * </li>
   * <li>
   * {@link WireConstants#WIREADMIN_PRODUCER_PID} set to the value of
   * Producer service's PID.
   * </li>
   * <li>
   * {@link WireConstants#WIREADMIN_CONSUMER_PID} set to the value of
   * Consumer service's PID.
   * </li>
   * </ul>
   * If the <tt>properties</tt> argument
   * already contains any of these keys, then the supplied values
   * are replaced with the values assigned by the Wire Admin service.
   *
   * <p>The Wire Admin service must broadcast a <tt>WireAdminEvent</tt> of type
   * {@link WireAdminEvent#WIRE_CREATED}
   * after the new <tt>Wire</tt> object becomes available from {@link #getWires}.
   *
   * @param producerPID The <tt>service.pid</tt> of the Producer service
   * to be connected to the <tt>Wire</tt> object.
   * @param consumerPID The <tt>service.pid</tt> of the Consumer service
   * to be connected to the <tt>Wire</tt> object.
   * @param properties The <tt>Wire</tt> object's properties. This argument may be <tt>null</tt>
   * if the caller does not wish to define any <tt>Wire</tt> object's properties.
   * @return The <tt>Wire</tt> object for this connection.
   * @throws java.lang.IllegalArgumentException If
   * <tt>properties</tt> contains case variants of the same key name.
   */
  public Wire createWire(String producerPID, String consumerPID, Dictionary props)
    {
        ServiceReference producerServiceRef = null;
        ServiceReference consumerServiceRef = null;

        Dictionary properties;
       
        if(props == null)
        {
          properties = new Hashtable();
        }
        else
        {
          //Clone the dictionary
          properties = cloneProperties(props);
        }

        // Addition of mandatory properties
        properties.put(WireConstants.WIREADMIN_CONSUMER_PID, consumerPID);
        properties.put(WireConstants.WIREADMIN_PRODUCER_PID, producerPID);
        properties.put(WireConstants.WIREADMIN_PID, generateWirePID());

        // p.327 "Wire objects can be created when the producer or consumer
        // service is not registered
        WireImpl wire = new WireImpl(producerPID, consumerPID, properties);

        // Initialize the wire
        wire.initialize(m_bundleContext,m_eventManager);

        // Add the wire to the list
        synchronized(m_wires)
        {
            m_wires.add(wire);           
        }

        // p. 357 "The Wire Admin service must broadcast a WireAdminEvent of
        // type WireAdminEvent.WIRE_CREATED  after the new Wire object becomes
        // available from getWires(java.lang.String)."
        m_eventManager.fireEvent(WireAdminEvent.WIRE_CREATED,wire);

        synchronized (m_producers)
        {
            Iterator producerIterator = m_producers.keySet().iterator();
            while(producerIterator.hasNext())
            {
                producerServiceRef = (ServiceReference) producerIterator.next();
                if (producerServiceRef.getProperty(Constants.SERVICE_PID).equals(producerPID))
                {
                    wire.bindProducer(producerServiceRef);                   
                    break;
                }
            }
        }       
       
        synchronized (m_consumers)
        {
            Iterator consumerIterator = m_consumers.keySet().iterator();
            while(consumerIterator.hasNext())
            {
                consumerServiceRef = (ServiceReference) consumerIterator.next();
                if (consumerServiceRef.getProperty(Constants.SERVICE_PID).equals(consumerPID))
                {
                    wire.bindConsumer(consumerServiceRef);
                    break;
                }
               
            }
        }
       
       
        // p.327 If both Producer and Consumer services are registered, they are
        // connected by the wire admin service.
        if(wire.isConnected())
        {
            List wires = (List) m_producers.get(producerServiceRef);
            wires.add(wire);
            m_asyncMethodCaller.consumersConnected(wire.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()]));

            wires = (List) m_consumers.get(consumerServiceRef);
            wires.add(wire);
            m_asyncMethodCaller.producersConnected(wire.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()]));
        }

        // Newly created wires are immediately persisted to avoid information
        // loss in case of crashes.  (spec not clear about this)       
        persistWires();

        return wire;
    }

  /**
   * Delete a <tt>Wire</tt> object.
   *
   * <p>The <tt>Wire</tt> object representing a connection between
   * a Producer service and a Consumer service must be
   * removed.
   * The persistently stored configuration data for the <tt>Wire</tt> object
   * must destroyed. The <tt>Wire</tt> object's method {@link Wire#isValid} will return <tt>false</tt>
   * after it is deleted.
   *
   * <p>The Wire Admin service must broadcast a <tt>WireAdminEvent</tt> of type
   * {@link WireAdminEvent#WIRE_DELETED}
   * after the <tt>Wire</tt> object becomes invalid.
   *
   * @param wire The <tt>Wire</tt> object which is to be deleted.
   */
  public void deleteWire(Wire wire)
    {
        if(m_wires.contains(wire))
        {
            WireImpl wireImpl = (WireImpl) wire;
            m_wires.remove(wire);
            if(wireImpl.isConnected())
            {
                List wires = (List) m_producers.get(wireImpl.getProducerServiceRef());
                wires.remove(wireImpl);
                m_asyncMethodCaller.consumersConnected(wireImpl.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()]));

                wires = (List) m_consumers.get(wireImpl.getConsumerServiceRef());
                wires.remove(wireImpl);
                m_asyncMethodCaller.producersConnected(wireImpl.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()]));
            }
           
            wireImpl.invalidate();

            // fire an event
            m_eventManager.fireEvent(WireAdminEvent.WIRE_DELETED,wireImpl);
           
            // Persist state to avoid losses in case of crashes (spec not clear about this).       
            persistWires();
        }
        else
        {
            traceln("WireAdminImpl: Cannot delete a wire that is not managed by this service");
        }

  }

  /**
   * Update the properties of a <tt>Wire</tt> object.
   *
   * The persistently stored configuration data for the <tt>Wire</tt> object
   * is updated with the new properties and then the Consumer and Producer
   * services will be called at the respective {@link Consumer#producersConnected}
   * and {@link Producer#consumersConnected} methods.
   *
   * <p>The Wire Admin service must broadcast a <tt>WireAdminEvent</tt> of type
   * {@link WireAdminEvent#WIRE_UPDATED}
   * after the updated properties are available from the <tt>Wire</tt> object.
   *
   * @param wire The <tt>Wire</tt> object which is to be updated.
   * @param properties The new <tt>Wire</tt> object's properties or <tt>null</tt> if no properties are required.
   */
  public void updateWire(Wire wire, Dictionary props)
    {
        if(m_wires.contains(wire) == false)
        {
            traceln("WireAdminImpl: Cannot update a wire that is not managed by this service");
            return;
        }
       
        // Clone the dictionary
        Dictionary properties = cloneProperties(props);

        // Put again the mandatory properties, in case they are not set
        properties.put(WireConstants.WIREADMIN_CONSUMER_PID,wire.getProperties().get(WireConstants.WIREADMIN_CONSUMER_PID));
        properties.put(WireConstants.WIREADMIN_PRODUCER_PID,wire.getProperties().get(WireConstants.WIREADMIN_PRODUCER_PID));
        properties.put(WireConstants.WIREADMIN_PID,wire.getProperties().get(WireConstants.WIREADMIN_PID));
       
        WireImpl wireImpl = (WireImpl) wire;
       
        wireImpl.updateProperties(properties);
       
        // Call methods on Consumer and Producer
        if(wireImpl.isConnected())
        {
            List wires = (List) m_producers.get(wireImpl.getProducerServiceRef());
            m_asyncMethodCaller.consumersConnected(wireImpl.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()]));

            wires = (List) m_consumers.get(wireImpl.getConsumerServiceRef());
            m_asyncMethodCaller.producersConnected(wireImpl.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()]));
        }

        // fire an event
        m_eventManager.fireEvent(WireAdminEvent.WIRE_UPDATED,wireImpl);
  }

  /**
   * Return the <tt>Wire</tt> objects that match the given <tt>filter</tt>.
   *
   * <p>The list of available <tt>Wire</tt> objects is matched against the
   * specified <tt>filter</tt>. <tt>Wire</tt> objects which match the
   * <tt>filter</tt> must be returned. These <tt>Wire</tt> objects are not necessarily
   * connected. The Wire Admin service should not return
   * invalid <tt>Wire</tt> objects, but it is possible that a <tt>Wire</tt>
   * object is deleted after it was placed in the list.
   *
   * <p>The filter matches against the <tt>Wire</tt> object's properties including
   * {@link WireConstants#WIREADMIN_PRODUCER_PID}, {@link WireConstants#WIREADMIN_CONSUMER_PID}
   * and {@link WireConstants#WIREADMIN_PID}.
   *
   * @param filter Filter string to select <tt>Wire</tt> objects
   * or <tt>null</tt> to select all <tt>Wire</tt> objects.
   * @return An array of <tt>Wire</tt> objects which match the <tt>filter</tt>
   * or <tt>null</tt> if no <tt>Wire</tt> objects match the <tt>filter</tt>.
   * @throws org.osgi.framework.InvalidSyntaxException If the specified <tt>filter</tt>
   * has an invalid syntax.
   * @see org.osgi.framework.Filter
   */
  public Wire[] getWires(String filter) throws InvalidSyntaxException
    {
    List res = null;
    if (filter == null)
        {
            return (Wire [])m_wires.toArray(new Wire[m_wires.size()]);
    }
        else
        {
      Filter tempFilter = m_bundleContext.createFilter(filter);
      Iterator iter = m_wires.iterator();
      while (iter.hasNext())
            {
        WireImpl currentWire = (WireImpl) iter.next();
        if (tempFilter.match(currentWire.getProperties()))
                {
          if (res == null)
                    {
            res = new ArrayList();
                    }
          res.add(currentWire);
        }
      }
    }
    if (res == null)
        {
      return null;
    }
        else
        {
            return (Wire [])res.toArray(new Wire[res.size()]);
    }
  }

    /**
     * listens Producer and Consumer services changes
     * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
     */
    public void serviceChanged(ServiceEvent e)
    {
    ServiceReference serviceRef = e.getServiceReference();
    // A consumer service changed
    if (m_consumerFilter.match(serviceRef))
        {
      switch (e.getType())
            {
                case ServiceEvent.REGISTERED :
                    traceln("consumer registered");
                   
                    List wires = new ArrayList();

                    synchronized(m_consumers)
                    {
                        m_consumers.put(serviceRef,wires);
                    }
                    synchronized(m_wires)
                    {
                        Iterator wireIt = m_wires.iterator();
                        boolean called = false;
                        // Iterate over all existing wires
                        while(wireIt.hasNext())
                        {
                            WireImpl currentWire = (WireImpl) wireIt.next();

                            if(currentWire.getConsumerPID().equals(serviceRef.getProperty(Constants.SERVICE_PID)))
                            {
                                // This wire is associated to the newly arrived consumer
                                currentWire.bindConsumer(serviceRef);
                                if(currentWire.isConnected())
                                {
                                    // The wire has been connected, both producer and consumer
                                    // must be updated
                                    wires.add(currentWire);
                                    called = true;
                                    m_asyncMethodCaller.producersConnected(currentWire.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()]));
                                    List producerWires = (List) m_producers.get(currentWire.getProducerServiceRef());
                                    producerWires.add(currentWire);
                                    m_asyncMethodCaller.consumersConnected(currentWire.getProducer(),(Wire[])producerWires.toArray(new Wire[producerWires.size()]));                                   
                                }
                            }
                        }
                        if(!called)
                        {
                            // P. 329 "If the Consumer service has no Wire objects attached when it
                            // is registered, the WireAdmin service must always call producersConnected(null)
                            m_asyncMethodCaller.producersConnected((Consumer) m_bundleContext.getService(serviceRef),null);
                        }
                    }
                    break;
                case ServiceEvent.UNREGISTERING :
                    traceln("consumer unregistering");
                   
                    synchronized(m_consumers)
                    {
                        m_consumers.remove(serviceRef);
                    }
                    synchronized(m_wires)
                    {
                        Iterator wireIt = m_wires.iterator();
                        while(wireIt.hasNext())
                        {
                            WireImpl currentWire = (WireImpl) wireIt.next();
                            if(currentWire.getConsumerPID().equals(serviceRef.getProperty(Constants.SERVICE_PID)))
                            {
                                // p. 328 "When a Consumer or Producer service is unregistered
                                // from the OSGi framework, the other object in the association
                                // is informed that the Wire object is no longer valid"

                                if(currentWire.isConnected())
                                {
                                    currentWire.unbindConsumer();                              
                                    List producerWires = (List) m_producers.get(currentWire.getProducerServiceRef());
                                    producerWires.remove(currentWire);
                                    m_asyncMethodCaller.consumersConnected(currentWire.getProducer(),(Wire[])producerWires.toArray(new Wire[producerWires.size()]));
                                }
                                else
                                {
                                    currentWire.unbindConsumer();   
                                }
                            }
                        }
                    }
                    break;
                case ServiceEvent.MODIFIED :
                    // TODO Respond to consumer service modification
                    traceln("consumer service modified");
                    break;

            }
        }
        // Removed else to manage services which are both producers AND consumers
    if (m_producerFilter.match(serviceRef))
        {
            switch (e.getType())
            {
                case ServiceEvent.REGISTERED :
                    traceln("producer registered");
                   
                    List wires = new ArrayList();

                    synchronized(m_producers)
                    {
                        m_producers.put(serviceRef,wires);
                    }
                    synchronized(m_wires)
                    {
                        Iterator wireIt = m_wires.iterator();
                        boolean called = false;
                        // Iterate over all existing wires
                        while(wireIt.hasNext())
                        {
                            WireImpl currentWire = (WireImpl) wireIt.next();
                            if(currentWire.getProducerPID().equals(serviceRef.getProperty(Constants.SERVICE_PID)))
                            {
                                // This wire is associated to the newly arrived producer
                                currentWire.bindProducer(serviceRef);
                                if(currentWire.isConnected())
                                {
                                    // The wire has been connected, both producer and consumer
                                    // must be updated
                                    wires.add(currentWire);
                                    called = true;
                                    m_asyncMethodCaller.consumersConnected(currentWire.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()]));
                                    List consumerWires = (List) m_consumers.get(currentWire.getConsumerServiceRef());
                                    consumerWires.add(currentWire);
                                    m_asyncMethodCaller.producersConnected(currentWire.getConsumer(),(Wire[])consumerWires.toArray(new Wire[consumerWires.size()]));                                   
                                }
                            }
                        }
                        if(!called)
                        {
                            // P. 329 "If the Producer service has no Wire objects attached when it
                            // is registered, the WireAdmin service must always call consumersConnected(null)
                            m_asyncMethodCaller.consumersConnected((Producer) m_bundleContext.getService(serviceRef),null);
                        }
                    }
                    break;
                case ServiceEvent.UNREGISTERING :
                    traceln("Producer unregistering");
                   
                    synchronized(m_producers)
                    {
                        m_producers.remove(serviceRef);
                    }
                    synchronized(m_wires)
                    {
                        Iterator wireIt = m_wires.iterator();
                        while(wireIt.hasNext())
                        {
                            WireImpl currentWire = (WireImpl) wireIt.next();
                            if(currentWire.getProducerPID().equals(serviceRef.getProperty(Constants.SERVICE_PID)))
                            {
                                // p. 328 "When a Consumer or Producer service is unregistered
                                // from the OSGi framework, the other object in the association
                                // is informed that the Wire object is no longer valid"

                                if(currentWire.isConnected())
                                {
                                    currentWire.unbindProducer();                              
                                    List consumerWires = (List) m_consumers.get(currentWire.getConsumerServiceRef());
                                    consumerWires.remove(currentWire);
                                    m_asyncMethodCaller.producersConnected(currentWire.getConsumer(),(Wire[])consumerWires.toArray(new Wire[consumerWires.size()]));
                                }
                                else
                                {
                                    currentWire.unbindProducer();   
                                }
                            }
                        }
                    }
                    break;
                case ServiceEvent.MODIFIED :
                    // TODO Respond to producer service modification
                    traceln("producer service modified");
                    break;
            }
        }       
    }


  /**
   * release all references before stop
   */
  synchronized void releaseAll()
    {
        Iterator wireIt = m_wires.iterator();
        while(wireIt.hasNext())
        {
            WireImpl currentWire = (WireImpl) wireIt.next();
            currentWire.invalidate();
        }
       
        Iterator producerIt = m_producers.keySet().iterator();       
        while (producerIt.hasNext())
        {
            ServiceReference producerRef = (ServiceReference) producerIt.next();
            ((Producer)m_bundleContext.getService(producerRef)).consumersConnected(null);           
        }
       
        Iterator consumerIt = m_consumers.keySet().iterator();       
        while (consumerIt.hasNext())
        {
            ServiceReference consumerRef = (ServiceReference) consumerIt.next();
            ((Consumer)m_bundleContext.getService(consumerRef)).producersConnected(null);           
        }

        // Stop the thread
        m_asyncMethodCaller.stop();
       
        // Notify the event manager so that it stops its thread
        m_eventManager.stop();
       
        persistWires();

  }

    /**
     * This method generates a PID. The pid is generated from the bundle id,
     * a hash code from the current time and a counter.
     *
     * @return a wire PID
     */
    private String generateWirePID()
    {
        Date d = new Date();
        String PID="wire."+m_bundleContext.getBundle().getBundleId()+d.hashCode()+m_wireCount;
        m_wireCount ++;
       
        // Maybe the counter should go above 9?
        if(m_wireCount>9)
        {
            m_wireCount = 0;
        }
        return PID;
    }
   
    /**
     * Recover persistent wires
     *
     */
    private void getPersistentWires()
    {
       
        try
        {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(m_bundleContext.getDataFile("wires.ser")));
            m_wires = (ArrayList) ois.readObject();
            ois.close();
            if(m_wires!=null)
            {
                traceln("Deserialized "+m_wires.size()+" wires");
                Iterator wireIt = m_wires.iterator();
                while(wireIt.hasNext())
                {
                    WireImpl currentWire = (WireImpl) wireIt.next();
                    currentWire.initialize(m_bundleContext,m_eventManager);    
                }
            }
            else
            {
                traceln("Couldn't Deserialize wires");
                m_wires = new ArrayList();
            }
        }
        catch(FileNotFoundException ex)
        {
            // do not show anything as this exception is thrown every
            // time the wire admin service is launched for the first
            // time
            m_wires = new ArrayList();
        }
        catch(Exception ex)
        {
            trace(ex);
            m_wires = new ArrayList();
        }
    }
   
    /**
     * Persist existing wires
     *
     */
    private void persistWires()
    {
        try
        {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(m_bundleContext.getDataFile("wires.ser")));
            oos.writeObject(m_wires);
            oos.close();
            traceln("Serialized "+m_wires.size()+" wires");
        }
        catch(Exception ex)
        {
            trace(ex);
        }
    }

    /**
     * print an error
     * @param message message to error
     */
    static void error(String message)
    {
        if (m_errorout != null)
        {
            m_errorout.println(message);
        }
    }

  /**
   * print a trace
   * @param message message to trace
   */
  static void traceln(String message)
    {
    if (m_traceout != null)
        {
      trace(message);
      trace("\n");
    }
  }

  /**
   * print a trace
   * @param message message to trace
   */
  static void trace(String message)
    {
    if (m_traceout != null)
        {
      m_traceout.print(message);
    }
  }
  /**
   * print a trace
   * @param e exception to trace
   */
  static void trace(Exception e)
    {
    if (m_traceout != null)
        {
      e.printStackTrace(m_traceout);
    }
  }

  /**
   * Clone a dictionary
   *
   * @param dictionary The dictionary to clone
   * @return a copy of the dicionary
   */
    private Dictionary cloneProperties(Dictionary dictionary){
        Dictionary properties=new Hashtable();
       
        if (dictionary == null) {
            properties = new Hashtable();
        } else {
            Enumeration enumeration=dictionary.keys();
            while(enumeration.hasMoreElements()){
                Object key=enumeration.nextElement();
                Object value=dictionary.get(key);
                properties.put(key,value);
            }
        }
       
        return properties;
    }
 
    /**
     * This class enables calls to Producer.consumersConnected and Consumer.producersConnected
     * to be done asynchronously
     *
     * p.333 "The WireAdmin service can call the consumersConnected or producersConnected
     * methods during the registration of the consumer of producer service"
     *
    **/
    class AsyncMethodCaller implements Runnable
    {
        private boolean m_stop = false;

        private List m_methodCallStack = new ArrayList();

        public void run()
        {
            while (!m_stop)
            {
                Object nextTarget[] = null;

                synchronized (m_methodCallStack)
                {
                    while (m_methodCallStack.size() == 0)
                    {
                        try
                        {
                            m_methodCallStack.wait();
                        }
                        catch (InterruptedException ex)
                        {
                            // Ignore.
                        }
                    }
                    nextTarget = (Object[]) m_methodCallStack.remove(0);
                }
               
                if(nextTarget[0] instanceof Producer)
                {
                    try
                    {
                        ((Producer)nextTarget[0]).consumersConnected((Wire[])nextTarget[1]);
                    }
                    catch(Exception ex)
                    {
                        trace(ex);
                    }
                }
                // Removed else because nextTarget can be both producer and consumer               
                if(nextTarget[0] instanceof Consumer)
                {
                    try
                    {
                        ((Consumer)nextTarget[0]).producersConnected((Wire[])nextTarget[1]);
                    }
                    catch(Exception ex)
                    {
                        trace(ex);
                    }
                }
            }
        }

        /**
         * Place a call to Consumer.producersConnected on the stack
         *
         * @param c the consumer
         * @param wires the wires
         */
        public void producersConnected(Consumer c,Wire []wires)
        {
            synchronized (m_methodCallStack)
            {
                m_methodCallStack.add(new Object[]{c,wires});
                m_methodCallStack.notify();
            }
        }

        /**
         * Place a call to Producer.consumersConnected on the stack
         *
         * @param p the producer
         * @param wires the wires
         */
        public void consumersConnected(Producer p,Wire []wires)
        {
            synchronized (m_methodCallStack)
            {
                m_methodCallStack.add(new Object[]{p,wires});
                m_methodCallStack.notify();
            }
        }

        /**
         * stop the dispatcher
         *
         */
        void stop()
        {
            m_stop = true;
        }
    }
}
TOP

Related Classes of org.apache.felix.wireadmin.WireAdminImpl$AsyncMethodCaller

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.