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