Moved from sandbox to trunk

git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@391355 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.wireadmin/pom.xml b/org.apache.felix.wireadmin/pom.xml
new file mode 100644
index 0000000..4080b91
--- /dev/null
+++ b/org.apache.felix.wireadmin/pom.xml
@@ -0,0 +1,51 @@
+<project>
+  <parent>
+    <groupId>org.apache.felix</groupId>
+    <artifactId>felix</artifactId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <packaging>osgi-bundle</packaging>
+  <name>Apache Felix WireAdmin</name>
+  <artifactId>org.apache.felix.wireadmin</artifactId>
+  <dependencies>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <version>${pom.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <version>${pom.version}</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix.plugins</groupId>
+        <artifactId>maven-osgi-plugin</artifactId>
+        <version>${pom.version}</version>
+        <extensions>true</extensions>
+        <configuration>
+          <osgiManifest>
+            <bundleName>WireAdmin</bundleName>
+            <bundleVendor>Apache Software Foundation</bundleVendor>
+            <bundleDescription>
+              Implementation of the WireAdmin Service.
+            </bundleDescription>
+            <bundleActivator>
+              org.apache.felix.wireadmin.Activator
+            </bundleActivator>
+            <bundleSymbolicName>org.apache.felix.wireadmin</bundleSymbolicName>
+            <importPackage>
+              org.osgi.service.wireadmin; specification-version=1.0.0
+            </importPackage>
+          </osgiManifest>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/Activator.java b/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/Activator.java
new file mode 100644
index 0000000..d0e17cb
--- /dev/null
+++ b/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/Activator.java
@@ -0,0 +1,125 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.util.Dictionary;
+import java.util.Properties;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.wireadmin.WireAdmin;
+import org.osgi.service.wireadmin.WireConstants;
+import org.osgi.service.wireadmin.WireAdminListener;
+import org.osgi.service.wireadmin.WireAdminEvent;
+/**
+ * The activator
+ *
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class Activator implements BundleActivator {
+
+	private final static String WIREADMIN_PID="org.apache.felix.wireadmin";
+	private ServiceRegistration m_reg=null;
+	private WireAdminImpl m_wai=null;
+	
+	/**
+	 * Called upon starting of the bundle.
+	 *
+	 * @param   context  The bundle context passed by the framework
+	 * @exception   Exception
+	 */
+	public void start(BundleContext bundleContext) throws BundleException {
+		
+        m_wai= new WireAdminImpl(bundleContext);
+
+        // Register the service
+        Dictionary properties=new Properties();
+		properties.put(WireConstants.WIREADMIN_PID,WIREADMIN_PID);
+		m_reg = bundleContext.registerService(WireAdmin.class.getName(),m_wai,properties);
+
+        // Event dispatching does not start until the reference is set        
+        m_wai.setServiceReference(m_reg.getReference());
+
+        if(bundleContext.getProperty("fr.imag.adele.wireadmin.traceEvt") != null)
+        {
+            String value = bundleContext.getProperty("fr.imag.adele.wireadmin.traceEvt");
+            if(value.equals("true"))
+            {
+                Dictionary props=new Properties();
+                props.put(WireConstants.WIREADMIN_EVENTS,new Integer(0x80|0x40|0x20|0x10|0x08|0x04|0x02|0x01));
+                properties.put(WireConstants.WIREADMIN_PID,WIREADMIN_PID);
+                bundleContext.registerService(WireAdminListener.class.getName(),new eventTracer(),props);
+            }
+        }
+	}
+
+    /**
+     * Called upon stopping the bundle.
+     *
+     * @param   context  The bundle context passed by the framework
+     * @exception   Exception
+     */
+	public void stop(BundleContext bundleContext) throws BundleException 
+    {   
+        m_wai.releaseAll();
+        m_wai = null;
+	}
+
+    class eventTracer implements WireAdminListener
+    {
+        public void wireAdminEvent(WireAdminEvent evt)
+        {
+            int type = evt.getType();
+            if((type & WireAdminEvent.WIRE_CREATED)!=0)
+            {
+                WireAdminImpl.traceln("Received event WIRE_CREATED");
+            }
+            if((type & WireAdminEvent.WIRE_CONNECTED)!=0)
+            {
+                WireAdminImpl.traceln("Received event WIRE_CONNECTED");
+            }
+            if((type & WireAdminEvent.WIRE_UPDATED)!=0)
+            {
+                WireAdminImpl.traceln("Received event WIRE_UPDATED");
+            }
+            if((type & WireAdminEvent.WIRE_TRACE)!=0)
+            {
+                WireAdminImpl.traceln("Received event WIRE_TRACE");
+            }
+            if((type & WireAdminEvent.WIRE_DISCONNECTED)!=0)
+            {
+                WireAdminImpl.traceln("Received event WIRE_DISCONNECTED");
+            }
+            if((type & WireAdminEvent.WIRE_DELETED)!=0)
+            {
+                WireAdminImpl.traceln("Received event WIRE_DELETED");
+            }
+            if((type & WireAdminEvent.PRODUCER_EXCEPTION)!=0)
+            {
+                WireAdminImpl.traceln("Received event PRODUCER_EXCEPTION");
+                evt.getThrowable().printStackTrace();
+            }
+            if((type & WireAdminEvent.CONSUMER_EXCEPTION)!=0)
+            {
+                WireAdminImpl.traceln("Received event CONSUMER_EXCEPTION");
+                evt.getThrowable().printStackTrace();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/EventManager.java b/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/EventManager.java
new file mode 100644
index 0000000..2fe530e
--- /dev/null
+++ b/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/EventManager.java
@@ -0,0 +1,266 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.wireadmin;
+
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.BundleContext;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.osgi.service.wireadmin.WireAdminEvent;
+import org.osgi.service.wireadmin.WireAdminListener;
+import org.osgi.service.wireadmin.WireConstants;
+
+/**
+ * Class that tracks listeners and manages event dispatching 
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+class EventManager implements ServiceListener
+{
+    // <ServiceReference,WireAdminListener>
+    private Map m_wireAdminListeners = new HashMap();
+
+    // The bundle context
+    private BundleContext m_bundleContext;
+    
+    // Asynchronous dispatcher
+    private AsyncDispatcher m_eventDispatcher = new AsyncDispatcher();
+       
+    // Service reference from the WireAdmin service
+    private ServiceReference m_ref;
+    
+    /**
+     * The constructor receives the bundle context and a service reference
+     * corresponding to the wire admin service
+     * 
+     * @param ctxt
+     * @param ref
+     */
+    EventManager(BundleContext ctxt)
+    {
+        m_bundleContext = ctxt;
+        ServiceReference [] serviceRefs = null;
+        
+        try
+        {
+            m_bundleContext.addServiceListener(this,"(objectClass=org.osgi.service.wireadmin.WireAdminListener)");
+            serviceRefs = m_bundleContext.getServiceReferences(WireAdminListener.class.getName(),null);
+        }
+        catch(Exception ex)
+        {
+            // Exception never thrown since filter is correct
+        }        
+
+        if(serviceRefs != null)
+        {
+            // lock the producers Map to avoid concurrent modifications due
+            // to service events
+            synchronized(m_wireAdminListeners)
+            {
+                for(int i=0;i<serviceRefs.length;i++)
+                {
+                    ServiceReference currentRef=(ServiceReference)serviceRefs[i];
+                    
+                    m_wireAdminListeners.put(currentRef,m_bundleContext.getService(currentRef));
+                }
+            }
+        }        
+    }
+    
+    /**
+     * When the service reference is set the event dispatching thread is
+     * started. This is necessary since events require m_ref to be set
+     * 
+     * @param ref <tt>ServiceReference</tt> to the wire admin service.
+     */
+    void setServiceReference(ServiceReference ref)
+    {
+        m_ref = ref;
+
+        // Activate thread that does asynchronous calls to
+        // the producersConnected and consummersConnected methods
+        new Thread(m_eventDispatcher).start();
+    }
+    
+    /**
+     * Stop the event manager
+     *
+     */
+    void stop()
+    {
+        m_eventDispatcher.stop();
+    }
+    
+    /**
+     * Tracks Listener changes
+     */
+    public void serviceChanged(ServiceEvent e) 
+    {
+        ServiceReference serviceRef = e.getServiceReference();
+        switch (e.getType()) 
+        {
+            case ServiceEvent.REGISTERED:
+                synchronized(m_wireAdminListeners)
+                {
+                    m_wireAdminListeners.put(serviceRef,m_bundleContext.getService(serviceRef));
+                }
+                break;
+            case ServiceEvent.UNREGISTERING:
+            synchronized(m_wireAdminListeners)
+                {
+                    m_wireAdminListeners.remove(serviceRef);
+                }
+                break;
+            case ServiceEvent.MODIFIED:
+                // Not necessary
+                break;
+        }
+    }
+    
+    /**
+     * Fire an event
+     * 
+     * @param eventType (see WireAdminEvent)
+     * @param wire the wire
+     */
+    void fireEvent(int eventType,WireImpl wire) 
+    {
+        fireEvent(eventType, wire, null);
+    }
+
+    /**
+     * Fire an event that contains an exception
+     * 
+     * @param eventType
+     * @param wire
+     * @param exception
+     */
+    void fireEvent(int eventType,WireImpl wire, Throwable exception) 
+    {
+        WireAdminEvent evt = new WireAdminEvent(m_ref,eventType,wire,exception);
+        m_eventDispatcher.queueEvent(evt);
+    }
+
+    /**
+     * p. 345 "A WireAdminEvent object can be sent asynchronously but must be
+     * ordered for each WireAdminListener service"
+     * But the API doc says "WireAdminEvent objects are delivered asynchronously 
+     * to all registered WireAdminListener service objects which specify an 
+     * interest in the WireAdminEvent type."
+     * We choose asynchronous delivery to be safe
+     *
+    **/
+    class AsyncDispatcher implements Runnable
+    {
+        private boolean m_stop = false;
+        private boolean m_empty = true;
+
+        private List m_eventStack = new ArrayList();
+
+        public void run()
+        {
+            while (m_stop == false || (m_stop == true && m_empty == false))
+            {
+                WireAdminEvent nextEvent = null;
+
+                synchronized (m_eventStack)
+                {
+                    while (m_eventStack.size() == 0)
+                    {
+                        try
+                        {
+                            m_eventStack.wait();
+                        } 
+                        catch (InterruptedException ex)
+                        {
+                            // Ignore.
+                        }
+                    }
+                    nextEvent = (WireAdminEvent) m_eventStack.remove(0);
+                    
+                    if(m_eventStack.size()==0)
+                    {
+                        // This allows the queue to be flushed upon termination
+                        m_empty = true;
+                    }
+                }
+                
+                synchronized (m_wireAdminListeners)
+                {
+                    Iterator listenerIt = m_wireAdminListeners.keySet().iterator();
+                    while(listenerIt.hasNext())
+                    {
+                        ServiceReference listenerRef = (ServiceReference)listenerIt.next();
+                        WireAdminListener listener = (WireAdminListener)m_wireAdminListeners.get(listenerRef);
+                        
+                        try
+                        {
+                            Integer evtsInteger = (Integer) listenerRef.getProperty(WireConstants.WIREADMIN_EVENTS);
+                            if(evtsInteger != null)
+                            {
+                                int events = evtsInteger.intValue();
+                                if((nextEvent.getType()&events)!=0)
+                                {
+                                    listener.wireAdminEvent(nextEvent);
+                                }                            
+                            }
+                            else
+                            {
+                                WireAdminImpl.trace(new Exception("Listener with no WIREADMIN_EVENTS"+listenerRef));
+                            }
+                        }
+                        catch(ClassCastException ex)
+                        {
+                            WireAdminImpl.trace("Listener returned WIREADMIN_EVENTS of wrong type:"+ex);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Queue an event on the stack
+         * 
+         * @param evt the event
+         */
+        public void queueEvent(WireAdminEvent evt)
+        {
+            synchronized (m_eventStack)
+            {
+                m_eventStack.add(evt);
+                m_empty = false;
+                m_eventStack.notify();
+            }
+        }
+
+        /**
+         * stop the dispatcher
+         *
+         */
+        void stop()
+        {
+            m_stop = true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/WireAdminImpl.java b/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/WireAdminImpl.java
new file mode 100644
index 0000000..eaf95fd
--- /dev/null
+++ b/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/WireAdminImpl.java
@@ -0,0 +1,1034 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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:felix-dev@incubator.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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/WireImpl.java b/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/WireImpl.java
new file mode 100644
index 0000000..e587a68
--- /dev/null
+++ b/org.apache.felix.wireadmin/src/main/java/org/apache/felix/wireadmin/WireImpl.java
@@ -0,0 +1,952 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.Date;
+
+import org.osgi.framework.Filter;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+import org.osgi.service.wireadmin.Consumer;
+import org.osgi.service.wireadmin.Producer;
+import org.osgi.service.wireadmin.Wire;
+import org.osgi.service.wireadmin.WireConstants;
+import org.osgi.service.wireadmin.WireAdminEvent;
+
+/**
+ * A connection between a Producer service and a Consumer service.
+ *
+ * <p>A <tt>Wire</tt> object connects a Producer service
+ * to a Consumer service.
+ * Both the Producer and Consumer services are identified
+ * by their unique <tt>service.pid</tt> values.
+ * The Producer and Consumer services may communicate with
+ * each other via <tt>Wire</tt> objects that connect them.
+ * The Producer service may send updated values to the
+ * Consumer service by calling the {@link #update} method.
+ * The Consumer service may request an updated value from the
+ * Producer service by calling the {@link #poll} method.
+ *
+ * <p>A Producer service and a Consumer service may be
+ * connected through multiple <tt>Wire</tt> objects.
+ *
+ * <p>Security Considerations. <tt>Wire</tt> objects are available to
+ * Producer and Consumer services connected to a given
+ * <tt>Wire</tt> object and to bundles which can access the <tt>WireAdmin</tt> service.
+ * A bundle must have <tt>ServicePermission[GET,WireAdmin]</tt> to get the <tt>WireAdmin</tt> service to
+ * access all <tt>Wire</tt> objects.
+ * A bundle registering a Producer service or a Consumer service
+ * must have the appropriate <tt>ServicePermission[REGISTER,Consumer|Producer]</tt> to register the service and
+ * will be passed <tt>Wire</tt> objects when the service object's
+ * <tt>consumersConnected</tt> or <tt>producersConnected</tt> method is called.
+ *
+ * <p>Scope. Each Wire object can have a scope set with the <tt>setScope</tt> method. This
+ * method should be called by a Consumer service when it assumes a Producer service that is
+ * composite (supports multiple information items). The names in the scope must be
+ * verified by the <tt>Wire</tt> object before it is used in communication. The semantics of the
+ * names depend on the Producer service and must not be interpreted by the Wire Admin service.
+ *
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a> * 
+ */
+public class WireImpl implements Wire, java.io.Serializable 
+{
+	static final long serialVersionUID = -3637966367104019136L;
+	
+    // Persistent attributes
+    
+    // p. 327 "The Wire admin object contains a Persistend Identity (PID) for
+    // a consumer service and a PID for a producer service" These properties
+    // are also contained in the dictionary, but they are stored to optimize
+    // access. 
+    private String m_producerPID;
+    private String m_consumerPID;
+
+    private Dictionary m_properties;
+    
+    // Transient attributes
+
+	transient private boolean m_isValid = true;
+    transient private boolean m_isConnected = false;
+    transient private Filter m_filter = null;
+    
+    transient private ServiceReference m_producerServiceRef;
+    transient private Producer m_producer;
+    transient private boolean m_producerIsComposite = false;
+    transient private String [] m_producerScope = null;
+    
+    transient private ServiceReference m_consumerServiceRef;	
+	transient private Consumer m_consumer;
+    transient private boolean m_consumerIsComposite = false;
+    transient private String [] m_consumerScope = null;
+    
+    transient private BundleContext m_bundleContext;
+    transient private EventManager m_eventManager;
+	transient private Object m_lastValue;
+	transient private String[] m_scope;
+    
+    transient private long m_lastUpdate;    
+    transient private boolean m_isFirstUpdate;
+    transient FilterDictionary m_dictionary;
+    
+    /**
+     * Constructor with package visibility
+     * 
+     * @param producerPID
+     * @param consumerPID
+     * @param properties
+     */
+	WireImpl(String producerPID, String consumerPID, Dictionary properties)
+    {
+		m_producerPID = producerPID;
+		m_consumerPID = consumerPID;
+		m_properties = properties;
+		
+		// set the scope which is the intersection of strings in the WIREADMIN_PRODUCER_SCOPE properties and the strings in the WIREADMIN_CONSUMER_SCOPE
+		m_scope = null;
+	}
+    
+    /**
+     * Method called after construction and after deserialization
+     * 
+     * @param ctxt
+     * @param eventManager
+     */
+    void initialize(BundleContext ctxt, EventManager eventManager)
+    {
+        m_isValid = true;
+        m_isConnected = false;
+
+        m_bundleContext = ctxt;
+        m_eventManager = eventManager;
+        
+        m_lastValue = null;
+        
+        m_lastUpdate = 0;
+        
+        m_isFirstUpdate = true;
+        
+        /*
+        if(m_date == null)
+        {
+            m_date = new Date();
+        }
+        */
+        m_dictionary = new FilterDictionary();
+    }
+
+	/**
+	 * Return the state of this <tt>Wire</tt> object.
+	 *
+	 * <p>A connected <tt>Wire</tt> must always be disconnected before
+	 * becoming invalid.
+	 *
+	 * @return <tt>false</tt> if this <tt>Wire</tt> object is invalid because it
+	 * has been deleted via {@link WireAdmin#deleteWire};
+	 * <tt>true</tt> otherwise.
+	 */
+	public boolean isValid() 
+    {
+		return m_isValid;
+	}
+
+	/**
+	 * Return the connection state of this <tt>Wire</tt> object.
+	 *
+	 * <p>A <tt>Wire</tt> is connected after the Wire Admin service receives
+	 * notification that the Producer service and
+	 * the Consumer service for this <tt>Wire</tt> object are both registered.
+	 * This method will return <tt>true</tt> prior to notifying the Producer
+	 * and Consumer services via calls
+	 * to their respective <tt>consumersConnected</tt> and <tt>producersConnected</tt>
+	 * methods.
+	 * <p>A <tt>WireAdminEvent</tt> of type {@link WireAdminEvent#WIRE_CONNECTED}
+	 * must be broadcast by the Wire Admin service when
+	 * the <tt>Wire</tt> becomes connected.
+	 *
+	 * <p>A <tt>Wire</tt> object
+	 * is disconnected when either the Consumer or Producer
+	 * service is unregistered or the <tt>Wire</tt> object is deleted.
+	 * <p>A <tt>WireAdminEvent</tt> of type {@link WireAdminEvent#WIRE_DISCONNECTED}
+	 * must be broadcast by the Wire Admin service when
+	 * the <tt>Wire</tt> becomes disconnected.
+	 *
+	 * @return <tt>true</tt> if both the Producer and Consumer
+	 * for this <tt>Wire</tt> object are connected to the <tt>Wire</tt> object;
+	 * <tt>false</tt> otherwise.
+	 */
+	public boolean isConnected() 
+    {
+		return m_isConnected;
+	}
+
+	/**
+	 * Return the list of data types understood by the
+	 * Consumer service connected to this <tt>Wire</tt> object. Note that
+	 * subclasses of the classes in this list are acceptable data types as well.
+	 *
+	 * <p>The list is the value of the {@link WireConstants#WIREADMIN_CONSUMER_FLAVORS}
+	 * service property of the
+	 * Consumer service object connected to this object. If no such
+	 * property was registered or the type of the property value is not
+	 * <tt>Class[]</tt>, this method must return <tt>null</tt>.
+	 *
+	 * @return An array containing the list of classes understood by the
+	 * Consumer service or <tt>null</tt> if
+	 * the <tt>Wire</tt> is not connected,
+	 * or the consumer did not register a {@link WireConstants#WIREADMIN_CONSUMER_FLAVORS} property
+	 * or the value of the property is not of type <tt>Class[]</tt>.
+	 */
+
+	public Class[] getFlavors() 
+    {
+        if(isConnected())
+        {
+            try
+            {
+                return (Class [])m_consumerServiceRef.getProperty(WireConstants.WIREADMIN_CONSUMER_FLAVORS);
+            }
+            catch(ClassCastException ex)
+            {
+                return null;
+            }
+        }
+        else
+        {
+            return null;
+
+        }
+	}
+
+	/**
+	 * Update the value.
+	 *
+	 * <p>This methods is called by the Producer service to
+	 * notify the Consumer service connected to this <tt>Wire</tt> object
+	 * of an updated value.
+	 * <p>If the properties of this <tt>Wire</tt> object contain a
+	 * {@link WireConstants#WIREADMIN_FILTER} property,
+	 * then filtering is performed.
+	 * If the Producer service connected to this <tt>Wire</tt>
+	 * object was registered with the service
+	 * property {@link WireConstants#WIREADMIN_PRODUCER_FILTERS}, the
+	 * Producer service will perform the filtering according to the rules specified
+	 * for the filter. Otherwise, this <tt>Wire</tt> object
+	 * will perform the filtering of the value.
+	 * <p>If no filtering is done, or the filter indicates the updated value should
+	 * be delivered to the Consumer service, then
+	 * this <tt>Wire</tt> object must call
+	 * the {@link Consumer#updated} method with the updated value.
+	 * If this <tt>Wire</tt> object is not connected, then the Consumer
+	 * service must not be called and the value is ignored.<p>
+	 * If the value is an <tt>Envelope</tt> object, and the scope name is not permitted, then the
+	 * <tt>Wire</tt> object must ignore this call and not transfer the object to the Consumer
+	 * service.
+	 *
+	 * <p>A <tt>WireAdminEvent</tt> of type {@link WireAdminEvent#WIRE_TRACE}
+	 * must be broadcast by the Wire Admin service after
+	 * the Consumer service has been successfully called.
+	 *
+	 * @param value The updated value. The value should be an instance of
+	 * one of the types returned by {@link #getFlavors}.
+	 * @see WireConstants#WIREADMIN_FILTER
+	 */
+	public void update(Object value) 
+    {
+        // TODO Implement Access Control (p. 338)
+		if (isConnected())
+        {       
+            if(m_producerIsComposite == true)
+            {
+                // TODO Implement update for composite producers
+                WireAdminImpl.traceln("WireImpl.update: update for composite producers not yet implemented");
+                return;
+            }
+            else // not a composite (Note: p. 341 "Filtering for composite producer services is not supported")
+            {
+                //long time = m_date.getTime();
+                long time = new Date().getTime();
+                
+                // We ignore filtering the first time...
+                if(m_isFirstUpdate == false && m_filter != null)
+                {
+                    // If the Producer service was registered with the WIREADMIN_PRODUCER_FILTERS  
+                    // service property indicating that the Producer service will perform the data 
+                    // filtering then the Wire object will not perform data filtering. Otherwise, 
+                    // the Wire object must perform basic filtering.
+                    try
+                    {
+                        m_dictionary.reset(value,time);
+                        if(!m_filter.match(m_dictionary))
+                        {
+                            WireAdminImpl.traceln("### Update rejected ("+m_properties.get(WireConstants.WIREADMIN_PID)+") filter evaluated to false:"+m_filter);
+                            WireAdminImpl.traceln("  WIREVALUE_CURRENT.class"+m_dictionary.get(WireConstants.WIREVALUE_CURRENT).getClass().getName());
+                            WireAdminImpl.traceln("  WIREVALUE_CURRENT="+m_dictionary.get(WireConstants.WIREVALUE_CURRENT));
+                            WireAdminImpl.traceln("  WIREVALUE_PREVIOUS="+m_dictionary.get(WireConstants.WIREVALUE_PREVIOUS));
+                            WireAdminImpl.traceln("  WIREVALUE_DELTA_ABSOLUTE="+m_dictionary.get(WireConstants.WIREVALUE_DELTA_ABSOLUTE));
+                            WireAdminImpl.traceln("  WIREVALUE_DELTA_RELATIVE="+m_dictionary.get(WireConstants.WIREVALUE_DELTA_RELATIVE));
+                            WireAdminImpl.traceln("  WIREVALUE_ELAPSED="+m_dictionary.get(WireConstants.WIREVALUE_ELAPSED));
+                            return;
+                        }
+                    }
+                    catch(Exception ex)
+                    {
+                        // Could happen...
+                        WireAdminImpl.trace(ex);
+                    }
+    
+                }
+                try
+                {
+        			m_consumer.updated(this, value);
+                    if(m_isFirstUpdate == true)
+                    {
+                        m_isFirstUpdate = false;
+                    }
+                    m_lastUpdate = time;
+                    m_lastValue = value;
+                    // Fire event
+                    m_eventManager.fireEvent(WireAdminEvent.WIRE_TRACE,this);
+                }
+                catch(Exception ex)
+                {
+                    m_eventManager.fireEvent(WireAdminEvent.CONSUMER_EXCEPTION,this,ex);
+                }
+            }
+        }
+	}
+
+	/**
+	 * Poll for an updated value.
+	 *
+	 * <p>This methods is normally called by the Consumer service to
+	 * request an updated value from the Producer service
+	 * connected to this <tt>Wire</tt> object.
+	 * This <tt>Wire</tt> object will call
+	 * the {@link Producer#polled} method to obtain an updated value.
+	 * If this <tt>Wire</tt> object is not connected, then the Producer
+	 * service must not be called.<p>
+	 *
+	 * If this <tt>Wire</tt> object has a scope, then this method
+	 * must return an array of <tt>Envelope</tt> objects. The objects returned must
+	 * match the scope of this object. The <tt>Wire</tt> object must remove
+	 * all <tt>Envelope</tt> objects with a scope name that is not in the <tt>Wire</tt> object's scope.
+	 * Thus, the list of objects returned
+	 * must only contain <tt>Envelope</tt> objects with a permitted scope name. If the
+	 * array becomes empty, <tt>null</tt> must be returned.
+	 *
+	 * <p>A <tt>WireAdminEvent</tt> of type {@link WireAdminEvent#WIRE_TRACE}
+	 * must be broadcast by the Wire Admin service after
+	 * the Producer service has been successfully called.
+	 *
+	 * @return A value whose type should be one of the types
+	 * returned by {@link #getFlavors}, <tt>Envelope[]</tt>, or <tt>null</tt> if
+	 * the <tt>Wire</tt> object is not connected,
+	 * the Producer service threw an exception, or
+	 * the Producer service returned a value which is not an instance of
+	 * one of the types returned by {@link #getFlavors}.
+	 */
+	public Object poll() {
+        // p.330 "Update filtering must not apply to polling"
+		if (isConnected()) 
+        {
+            try
+            {
+    			Object value = m_producer.polled(this);
+                Class []flavors = getFlavors();
+                
+                boolean valueOk = false;
+                
+                // Test if the value is ok with respect to the flavors understood by
+                // the consumer
+                for(int i=0; i<flavors.length; i++)
+                {
+                    Class currentClass = flavors[i];
+                    if(currentClass.isInstance(value))
+                    {
+                        valueOk = true;
+                    }                    
+                }
+                if(valueOk)
+                {
+                    m_eventManager.fireEvent(WireAdminEvent.WIRE_TRACE,this);
+                    m_lastValue = value;
+        			return m_lastValue;
+                }
+                else
+                {
+                    WireAdminImpl.traceln("WireImpl.poll: value returned by producer is not undestood by consumer");
+                    return null;
+                }
+            }
+            catch(Exception ex)
+            {
+                m_eventManager.fireEvent(WireAdminEvent.PRODUCER_EXCEPTION,this,ex);
+                return null;
+            }
+		} 
+        else
+        {
+            // p. 333 "If the poll() method on the wire object is called and the
+            // producer is unregistered it must return a null value"
+			return null;
+        }
+	}
+
+	/**
+	 * Return the last value sent through this <tt>Wire</tt> object.
+	 *
+	 * <p>The returned value is the most recent, valid value passed to the
+	 * {@link #update} method or returned by the {@link #poll} method
+	 * of this object. If filtering is performed by this <tt>Wire</tt> object,
+	 * this methods returns the last value provided by the Producer service. This
+	 * value may be an <tt>Envelope[]</tt> when the Producer service
+	 * uses scoping. If the return value is an Envelope object (or array), it
+	 * must be verified that the Consumer service has the proper WirePermission to see it.
+	 *
+	 * @return The last value passed though this <tt>Wire</tt> object
+	 * or <tt>null</tt> if no valid values have been passed or the Consumer service has no permission.
+	 */
+	public Object getLastValue() 
+    {
+		return m_lastValue;
+	}
+
+	/**
+	 * Return the wire properties for this <tt>Wire</tt> object.
+	 *
+	 * @return The properties for this <tt>Wire</tt> object.
+	 * The returned <tt>Dictionary</tt> must be read only.
+	 */
+	public Dictionary getProperties() 
+    {
+		return m_properties;
+	}
+
+	/**
+	 * Return the calculated scope of this <tt>Wire</tt> object.
+	 *
+	 * The purpose of the <tt>Wire</tt> object's scope is to allow a Producer
+	 * and/or Consumer service to produce/consume different types
+	 * over a single <tt>Wire</tt> object (this was deemed necessary for efficiency
+	 * reasons). Both the Consumer service and the
+	 * Producer service must set an array of scope names (their scope) with
+	 * the service registration property <tt>WIREADMIN_PRODUCER_SCOPE</tt>, or <tt>WIREADMIN_CONSUMER_SCOPE</tt> when they can
+	 * produce multiple types. If a Producer service can produce different types, it should set this property
+	 * to the array of scope names it can produce, the Consumer service
+	 * must set the array of scope names it can consume. The scope of a <tt>Wire</tt>
+	 * object is defined as the intersection of permitted scope names of the
+	 * Producer service and Consumer service.
+	 * <p>If neither the Consumer, or the Producer service registers scope names with its
+	 * service registration, then the <tt>Wire</tt> object's scope must be <tt>null</tt>.
+	 * <p>The <tt>Wire</tt> object's scope must not change when a Producer or Consumer services
+	 * modifies its scope.
+	 * <p>A scope name is permitted for a Producer service when the registering bundle has
+	 * <tt>WirePermission[PRODUCE]</tt>, and for a Consumer service when
+	 * the registering bundle has <tt>WirePermission[CONSUME]</tt>.<p>
+	 * If either Consumer service or Producer service has not set a <tt>WIREADMIN_*_SCOPE</tt> property, then
+	 * the returned value must be <tt>null</tt>.<p>
+	 * If the scope is set, the <tt>Wire</tt> object must enforce the scope names when <tt>Envelope</tt> objects are
+	 * used as a parameter to update or returned from the <tt>poll</tt> method. The <tt>Wire</tt> object must then
+	 * remove all <tt>Envelope</tt> objects with a scope name that is not permitted.
+	 *
+	 * @return A list of permitted scope names or null if the Produce or Consumer service has set no scope names.
+	 */
+	public String[] getScope() 
+    {
+		return m_scope;
+	}
+
+	/**
+	 * Return true if the given name is in this <tt>Wire</tt> object's scope.
+	 *
+	 * @param name The scope name
+	 * @return true if the name is listed in the permitted scope names
+	 */
+	public boolean hasScope(String name) 
+    {
+		if (m_scope != null) 
+        {
+			for (int i = 0; i < m_scope.length; i++) 
+            {
+				if (name.equals(m_scope[i])) 
+                {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() 
+    {
+		return super.toString()
+			+ ":["
+			+ getProperties().get(WireConstants.WIREADMIN_PID)
+			+ ","
+			+ getProperties().get(WireConstants.WIREADMIN_PRODUCER_PID)
+			+ ","
+			+ getProperties().get(WireConstants.WIREADMIN_CONSUMER_PID)
+			+ "]";
+	}
+
+    /**
+     * Bind the consumer.
+     * 
+     * @param consumer A <tt>ServiceReference</tt> corresponding to the consumer service
+     */
+    void bindConsumer(ServiceReference consumerRef)
+    {
+        if(m_consumerServiceRef != null)
+        {
+            WireAdminImpl.traceln("WireImpl: consumer already bound!");
+            return;
+        }
+        
+        // Pid must be present
+        if(consumerRef.getProperty(Constants.SERVICE_PID) == null)
+        {
+            WireAdminImpl.traceln("WireImpl: Consumer service has no PID "+consumerRef);
+            return;
+        }
+        
+        // Flavors must be present
+        if(consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_FLAVORS) == null)
+        {
+            WireAdminImpl.traceln("WireImpl: Consumer service has no WIREADMIN_CONSUMER_FLAVORS "+consumerRef);
+            return;
+        }
+
+        // Is this a composite?
+        if(consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_COMPOSITE) != null)
+        {
+            m_consumerIsComposite = true;
+            m_consumerScope = (String []) consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_SCOPE);
+
+        }
+
+        m_consumerServiceRef = consumerRef;
+        m_consumer = (Consumer) m_bundleContext.getService(consumerRef);
+        
+        if(m_producer != null)
+        {
+            if(m_producerIsComposite)
+            {
+                if(matchComposites() == false)
+                {
+                    WireAdminImpl.traceln("WireImpl.bindConsumer : warning composite identities do not match");
+                }
+            }
+            m_isConnected = true;
+            m_eventManager.fireEvent(WireAdminEvent.WIRE_CONNECTED,this);
+        }
+    }
+    
+    /**
+     * Unbind the consumer
+     *
+     */
+    void unbindConsumer()
+    {
+    	// This test is made because knopflerfish doesn't support ungetService(null);
+    	if(m_consumerServiceRef != null)
+    	{
+	        m_bundleContext.ungetService(m_consumerServiceRef);
+	        if(m_isConnected)
+	        {
+	            m_isConnected = false;
+	            m_eventManager.fireEvent(WireAdminEvent.WIRE_DISCONNECTED,this);
+	        }
+	        m_consumer = null;
+	        m_consumerServiceRef = null;
+    	}
+    }
+    
+    /**
+     * Bind the producer
+     * 
+     * @param producer A <tt>ServiceReference</tt> corresponding to the producer service
+     */
+    void bindProducer(ServiceReference producerRef)
+    {
+        if(m_producerServiceRef != null)
+        {
+            WireAdminImpl.traceln("WireImpl: producer already bound!");
+            return;
+        }
+        
+        // Pid must be present
+        if(producerRef.getProperty(Constants.SERVICE_PID) == null)
+        {
+            WireAdminImpl.traceln("WireImpl.bindProducer: Producer service has no PID "+producerRef);
+            return;
+        }
+
+        // Flavors must be present
+        if(producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_FLAVORS) == null)
+        {
+            WireAdminImpl.traceln("WireImpl: Consumer service has no WIREADMIN_PRODUCER_FLAVORS "+producerRef);
+            return;
+        }
+
+        // Is this a composite?
+        if(producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_COMPOSITE) != null)
+        {
+            m_producerIsComposite = true;
+            m_producerScope = (String []) producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_SCOPE);
+        }
+
+        m_producerServiceRef = producerRef;
+        m_producer = (Producer) m_bundleContext.getService(producerRef);
+        
+        // p. 329 " If this property (wireadmin.producer.filters) is not set,
+        // the Wire object must filter according to the description in CompositeObjects"
+        if(producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_FILTERS) == null)
+        {
+            String filter = (String) m_properties.get(WireConstants.WIREADMIN_FILTER);
+            if(filter != null)
+            {
+                try
+                {
+                    m_filter = m_bundleContext.createFilter(filter);
+                }
+                catch(InvalidSyntaxException ex)
+                {
+                    WireAdminImpl.traceln("WireImpl.bindProducer: Ignoring filter with invalid syntax "+filter);                    
+                }
+            }
+        }
+        
+        if(m_consumer != null)
+        {
+            if(m_consumerIsComposite)
+            {
+                if(matchComposites() == false)
+                {
+                    WireAdminImpl.traceln("WireImpl.bindProducer : warning composite identities do not match");
+                }
+            }
+            m_isConnected = true;
+            // fire the corresponding event
+            m_eventManager.fireEvent(WireAdminEvent.WIRE_CONNECTED,this);
+        }
+    }
+    
+    /**
+     * Unbind the producer
+     *
+     */
+    void unbindProducer()
+    {
+    	if(m_producerServiceRef != null)
+    	{
+	        m_bundleContext.ungetService(m_producerServiceRef);
+	        if(m_isConnected)
+	        {
+	            m_isConnected = false;
+	            // fire the corresponding event
+	            m_eventManager.fireEvent(WireAdminEvent.WIRE_DISCONNECTED,this);
+	        }
+	        m_producer = null;
+	        m_producerServiceRef = null;
+    	}
+    }
+
+    /**
+     * Check if composites match. Match occurs when "at least one equal composite identity is
+     * listed on both the Producer and Consmer composite identity service property" (p. 336)
+     * 
+     * @return <tt>true</tt> if they match, <tt>false</tt>otherwise
+     */    
+    private boolean matchComposites()
+    {
+        String [] producerIdentities = (String []) m_producerServiceRef.getProperty(WireConstants.WIREADMIN_PRODUCER_COMPOSITE);
+        String [] consumerIdentities = (String []) m_producerServiceRef.getProperty(WireConstants.WIREADMIN_PRODUCER_COMPOSITE);
+        
+        boolean match = false;
+        
+        // confirm matching
+        
+        for(int i = 0; i< producerIdentities.length; i++)
+        {
+            String currentProducerIdentity = producerIdentities [i]; 
+            for(int j = 0; j < consumerIdentities.length; j++)
+            {
+                String currentConsumerIdentity = consumerIdentities [j];
+                if (currentProducerIdentity.equals(currentConsumerIdentity))
+                {
+                    match = true;
+                    break;
+                }
+            }
+        }
+        
+        // setup wire scope
+        
+        if (m_consumerScope != null && m_producerScope != null)
+        {
+            // p. 337 "It is allowed to register with a wildcard, indicating that all scopenames
+            // are supported. In that case, the WIREADMIN_SCOPE_ALL ({"*"}) should be registered as the
+            // scope of the serivce. The WireObject's scope is then fully defined by the
+            // other service connected to the wire object
+            if(m_consumerScope.length == 1 && m_consumerScope[0].equals("*"))
+            {
+                m_scope = m_producerScope;
+            }
+            else if(m_producerScope.length == 1 && m_producerScope[0].equals("*"))
+            {
+                m_scope = m_consumerScope;
+            }
+            else
+            {
+                /*
+                // TODO Implement wire scope creation based on producer, consumer and wire permissions (p. 338)
+
+                ServiceReference ref = m_bundleContext.getServiceReference(PermissionAdmin.class.getName());
+                if(ref != null)
+                {
+                    PermissionAdmin permAdmin = (PermissionAdmin) m_bundleContext.getService(ref);
+                    PermissionInfo [] producerPermissions = permAdmin.getPermissions(m_producerServiceRef.getBundle().getLocation());
+                    PermissionInfo [] consumerPermissions = permAdmin.getPermissions(m_consumerServiceRef.getBundle().getLocation()); 
+                }
+                */
+            }
+        }
+        else
+        {
+            // p. 337 "Not registering this property (WIREADMIN_CONSUMER_SCOPE or WIREADMIN_PRODUCER_SCOPE)
+            // by the Consumer or the Producer service indicates to the WireAdmin service that any
+            // Wire object connected to that service must return null for the Wire.getScope() method"
+            m_scope = null;
+        }
+        
+        return match;
+    }
+
+    /**
+     * Called to invalidate the wire
+     *
+     */
+    void invalidate()
+    {
+    	if(m_isValid)
+    	{
+	        unbindProducer();
+	        unbindConsumer();
+	        m_isValid=false;
+    	}
+    }
+    
+    /**
+     * Update the properties
+     * 
+     * @param properties new properties
+     */
+    void updateProperties(Dictionary properties)
+    {
+        m_properties = properties;
+    }
+
+    /**
+     * Return the service reference corresponding to the producer
+     * 
+     * @return a <tt>ServiceReference</tt> corresponding to the producer
+     */
+    ServiceReference getProducerServiceRef()
+    {
+        return m_producerServiceRef;
+    }
+
+    /**
+     * Return the producer service object
+     * 
+     * @return An <tt>Object</tt> corresponding to the producer
+     */
+    Producer getProducer()
+    {
+        return m_producer;
+    }
+
+    /**
+     * return the producer PID
+     * 
+     * @return
+     */
+    String getProducerPID()
+    {
+        return m_producerPID;
+    }
+
+    /**
+     * Return the service reference corresponding to the consumer
+     * 
+     * @return a <tt>ServiceReference</tt> corresponding to the consumer
+     */
+    ServiceReference getConsumerServiceRef()
+    {
+        return m_consumerServiceRef;
+    }
+
+    /**
+     * Returns the consumer service object
+     * 
+     * @return An <tt>Object</tt> corresponding to the consumer
+     */
+    Consumer getConsumer()
+    {
+        return m_consumer;
+    }
+
+    /**
+     * return the consumer PID
+     * 
+     * @return
+     */
+    String getConsumerPID()
+    {
+        return m_consumerPID;
+    }
+    
+    /**
+     * This inner class implements a dictionary that is used to filter out values
+     * during calls to update. This design choice was favored to avoid constructing
+     * a new dictionary for every call to update and to avoid doing unnecessary
+     * calculations.
+     */
+    class FilterDictionary extends Dictionary
+    {
+        private Object m_value;
+        private long m_time;
+        
+        /**
+         * Must be called prior to evaluating the filter
+         * 
+         * @param value
+         * @param time
+         */
+        void reset(Object value, long time)
+        {
+            m_value = value;
+            m_time = time;
+        }
+        
+        /**
+         * 
+         */
+        public Object get(Object key)
+        {
+            if(key.equals(WireConstants.WIREVALUE_CURRENT))
+            {
+                return m_value;
+            }
+            else if(key.equals(WireConstants.WIREVALUE_PREVIOUS))
+            {
+                return m_lastValue;
+            }
+            else if(m_value instanceof Number && key.equals(WireConstants.WIREVALUE_DELTA_ABSOLUTE))
+            {
+                return null;
+            }
+            else if(m_value instanceof Number && key.equals(WireConstants.WIREVALUE_DELTA_RELATIVE))
+            {
+                return null;
+            }
+            else if(key.equals(WireConstants.WIREVALUE_ELAPSED))
+            {
+                if(m_lastUpdate == 0)
+                {
+                    return new Long(0);    
+                }
+                else
+                {
+                    long delay = m_time - m_lastUpdate;
+                    //System.out.println("### delay = "+(delay));
+                    return new Long(delay);
+                }
+            }
+            else
+            {
+            	WireAdminImpl.traceln("### key not found:"+key);
+                return null;
+            }
+        }
+
+        /**
+         * Never empty
+         */
+        public boolean isEmpty()
+        {
+            return false;       
+        }
+        
+        /**
+         * Remove not supported
+         */
+        public Object remove(Object obj)
+        {
+            return null;
+        }
+        
+        /**
+         * Size is static
+         */
+        public int size()
+        {
+            return 5;
+        }
+        
+        /**
+         * Put not supported
+         */
+        public Object put(Object key, Object value)
+        {
+            return null;
+        }
+        
+        /**
+         * 
+         */
+        public Enumeration keys()
+        {
+        	Vector keys=new Vector();
+        	
+        	keys.addElement(WireConstants.WIREVALUE_ELAPSED);
+        	keys.addElement(WireConstants.WIREVALUE_CURRENT);
+        	keys.addElement(WireConstants.WIREVALUE_PREVIOUS);
+        	keys.addElement(WireConstants.WIREVALUE_DELTA_ABSOLUTE);
+        	keys.addElement(WireConstants.WIREVALUE_DELTA_RELATIVE);
+        	
+        	return keys.elements();
+        }
+        
+        /**
+         * Not supported
+         */
+        public Enumeration elements()
+        {
+            return null;
+        }
+    }
+}
\ No newline at end of file