/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.felix.wireadmin; import java.io.PrintStream; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; import org.osgi.service.wireadmin.Consumer; import org.osgi.service.wireadmin.Producer; import org.osgi.service.wireadmin.Wire; import org.osgi.service.wireadmin.WireAdmin; import org.osgi.service.wireadmin.WireConstants; import org.osgi.service.wireadmin.WireAdminEvent; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; /** * Wire Administration service. * * <p>This service can be used to create <tt>Wire</tt> objects connecting * a Producer service and a Consumer service. * <tt>Wire</tt> objects also have wire properties that may be specified * when a <tt>Wire</tt> object is created. The Producer and Consumer * services may use the <tt>Wire</tt> object's properties to manage or control their * interaction. * The use of <tt>Wire</tt> object's properties by a Producer or Consumer * services is optional. * * <p>Security Considerations. * A bundle must have <tt>ServicePermission[GET,WireAdmin]</tt> to get the Wire Admin service to * create, modify, find, and delete <tt>Wire</tt> objects. * * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class WireAdminImpl implements WireAdmin, ServiceListener { private BundleContext m_bundleContext; // A Map containing a service reference associated to a producer and a List // of wire objects private Map m_consumers = new HashMap(); /* ServiceReferences, List */ private Map m_producers = new HashMap(); /* ServiceReferences, List */ private List m_wires; // List containing the wires //private BindingController wireAdminListenersBindingController; // Filter corresponding to a consumer service private Filter m_consumerFilter; // Filter corresponding to a producer service private Filter m_producerFilter; // EventManager private EventManager m_eventManager; private static int m_wireCount = 0; private AsyncMethodCaller m_asyncMethodCaller = new AsyncMethodCaller(); //m_eventDispatcher.stop(); private static PrintStream m_traceout = null; private static PrintStream m_errorout = System.err; /** * Constructor with package visibility * * @param bundleContext the bundle context */ WireAdminImpl(BundleContext bundleContext) { m_bundleContext = bundleContext; if(bundleContext.getProperty("fr.imag.adele.wireadmin.trace") != null) { String value = bundleContext.getProperty("fr.imag.adele.wireadmin.trace"); if(value.equals("true")) { m_traceout = System.out; } } // Create the event manager (the event manager will start its own thread) m_eventManager = new EventManager(m_bundleContext); try { m_producerFilter = m_bundleContext.createFilter( "(objectClass=org.osgi.service.wireadmin.Producer)"); m_consumerFilter = m_bundleContext.createFilter( "(objectClass=org.osgi.service.wireadmin.Consumer)"); } catch (InvalidSyntaxException e) { // never thrown since LDAP expressions are correct } // Recover persistent wires getPersistentWires(); // Activate thread that does asynchronous calls to // the producersConnected and consummersConnected methods new Thread(m_asyncMethodCaller).start(); // Gets all producers and consumers that are present at the // moment the wire admin is created try { // Registration for events must be done first, as some service // can be registered during initialization m_bundleContext.addServiceListener(this,"(|"+m_producerFilter.toString()+m_consumerFilter.toString()+")"); // Replacement for the two following lines which work under OSCAR, // but not work under IBM's SMF //m_bundleContext.addServiceListener(this,m_consumerFilter.toString()); //m_bundleContext.addServiceListener(this,m_producerFilter.toString()); // Get all producers ServiceReference[] producerRefs = m_bundleContext.getServiceReferences(Producer.class.getName(),null); if(producerRefs!=null) { // lock the producers Map to avoid concurrent modifications due // to service events synchronized(m_producers) { for(int i=0;i<producerRefs.length;i++) { ServiceReference currentRef=(ServiceReference)producerRefs[i]; Iterator wireIt = m_wires.iterator(); while(wireIt.hasNext()) { WireImpl currentWire = (WireImpl) wireIt.next(); if(currentWire.getProducerPID().equals(currentRef.getProperty(Constants.SERVICE_PID))) { currentWire.bindProducer(currentRef); } } m_producers.put(currentRef,new ArrayList()); } } } // Get all the consumers ServiceReference[] consumerRefs = m_bundleContext.getServiceReferences(Consumer.class.getName(),null); if(consumerRefs!=null) { for(int i=0;i<consumerRefs.length;i++) { // lock the consumers to avoid concurrent modifications due // to service events synchronized(m_consumers) { ServiceReference currentRef=(ServiceReference)consumerRefs[i]; Iterator wireIt = m_wires.iterator(); while(wireIt.hasNext()) { WireImpl currentWire = (WireImpl) wireIt.next(); if(currentWire.getConsumerPID().equals(currentRef.getProperty(Constants.SERVICE_PID))) { currentWire.bindConsumer(currentRef); } } m_consumers.put(currentRef,new ArrayList()); } } } } catch (InvalidSyntaxException e) { trace(e); } // Iterate over all the wires, when a wire is connected // add it to the list of wires associated to a particular // producer or consumer synchronized(m_wires) { Iterator wireIterator = m_wires.iterator(); while(wireIterator.hasNext()) { WireImpl currentWire = (WireImpl) wireIterator.next(); if(currentWire.isConnected()) { // p. 327 "If both Producer and consumer services are registered // with the framework, they are connected by the WireAdmin service" List wires = (List) m_producers.get(currentWire.getProducerServiceRef()); wires.add(currentWire); m_asyncMethodCaller.consumersConnected(currentWire.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()])); wires = (List) m_consumers.get(currentWire.getConsumerServiceRef()); wires.add(currentWire); m_asyncMethodCaller.producersConnected(currentWire.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()])); } } } } /** * Pass the service reference to the event dispatcher * * @param ref the service reference */ void setServiceReference(ServiceReference ref) { m_eventManager.setServiceReference(ref); } /** * Create a new <tt>Wire</tt> object that connects a Producer * service to a Consumer service. * * The Producer service and Consumer service do not * have to be registered when the <tt>Wire</tt> object is created. * * <p>The <tt>Wire</tt> configuration data must be persistently stored. * All <tt>Wire</tt> connections are reestablished when the * <tt>WireAdmin</tt> service is registered. * A <tt>Wire</tt> can be permanently removed by using the * {@link #deleteWire} method. * * <p>The <tt>Wire</tt> object's properties must have case * insensitive <tt>String</tt> objects as keys (like the Framework). * However, the case of the key must be preserved. * The type of the value of the property must be one of the following: * * <pre> * type = basetype * | vector | arrays * * basetype = String | Integer | Long * | Float | Double | Byte * | Short | Character * | Boolean * * primitive = long | int | short * | char | byte | double | float * * arrays = primitive '[]' | basetype '[]' * * vector = Vector of basetype * </pre> * * <p>The <tt>WireAdmin</tt> service must automatically add the * following <tt>Wire</tt> properties: * <ul> * <li> * {@link WireConstants#WIREADMIN_PID} set to the value of the <tt>Wire</tt> object's * persistent identity (PID). This value is generated by the * Wire Admin service when a <tt>Wire</tt> object is created. * </li> * <li> * {@link WireConstants#WIREADMIN_PRODUCER_PID} set to the value of * Producer service's PID. * </li> * <li> * {@link WireConstants#WIREADMIN_CONSUMER_PID} set to the value of * Consumer service's PID. * </li> * </ul> * If the <tt>properties</tt> argument * already contains any of these keys, then the supplied values * are replaced with the values assigned by the Wire Admin service. * * <p>The Wire Admin service must broadcast a <tt>WireAdminEvent</tt> of type * {@link WireAdminEvent#WIRE_CREATED} * after the new <tt>Wire</tt> object becomes available from {@link #getWires}. * * @param producerPID The <tt>service.pid</tt> of the Producer service * to be connected to the <tt>Wire</tt> object. * @param consumerPID The <tt>service.pid</tt> of the Consumer service * to be connected to the <tt>Wire</tt> object. * @param properties The <tt>Wire</tt> object's properties. This argument may be <tt>null</tt> * if the caller does not wish to define any <tt>Wire</tt> object's properties. * @return The <tt>Wire</tt> object for this connection. * @throws java.lang.IllegalArgumentException If * <tt>properties</tt> contains case variants of the same key name. */ public Wire createWire(String producerPID, String consumerPID, Dictionary props) { ServiceReference producerServiceRef = null; ServiceReference consumerServiceRef = null; Dictionary properties; if(props == null) { properties = new Hashtable(); } else { //Clone the dictionary properties = cloneProperties(props); } // Addition of mandatory properties properties.put(WireConstants.WIREADMIN_CONSUMER_PID, consumerPID); properties.put(WireConstants.WIREADMIN_PRODUCER_PID, producerPID); properties.put(WireConstants.WIREADMIN_PID, generateWirePID()); // p.327 "Wire objects can be created when the producer or consumer // service is not registered WireImpl wire = new WireImpl(producerPID, consumerPID, properties); // Initialize the wire wire.initialize(m_bundleContext,m_eventManager); // Add the wire to the list synchronized(m_wires) { m_wires.add(wire); } // p. 357 "The Wire Admin service must broadcast a WireAdminEvent of // type WireAdminEvent.WIRE_CREATED after the new Wire object becomes // available from getWires(java.lang.String)." m_eventManager.fireEvent(WireAdminEvent.WIRE_CREATED,wire); synchronized (m_producers) { Iterator producerIterator = m_producers.keySet().iterator(); while(producerIterator.hasNext()) { producerServiceRef = (ServiceReference) producerIterator.next(); if (producerServiceRef.getProperty(Constants.SERVICE_PID).equals(producerPID)) { wire.bindProducer(producerServiceRef); break; } } } synchronized (m_consumers) { Iterator consumerIterator = m_consumers.keySet().iterator(); while(consumerIterator.hasNext()) { consumerServiceRef = (ServiceReference) consumerIterator.next(); if (consumerServiceRef.getProperty(Constants.SERVICE_PID).equals(consumerPID)) { wire.bindConsumer(consumerServiceRef); break; } } } // p.327 If both Producer and Consumer services are registered, they are // connected by the wire admin service. if(wire.isConnected()) { List wires = (List) m_producers.get(producerServiceRef); wires.add(wire); m_asyncMethodCaller.consumersConnected(wire.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()])); wires = (List) m_consumers.get(consumerServiceRef); wires.add(wire); m_asyncMethodCaller.producersConnected(wire.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()])); } // Newly created wires are immediately persisted to avoid information // loss in case of crashes. (spec not clear about this) persistWires(); return wire; } /** * Delete a <tt>Wire</tt> object. * * <p>The <tt>Wire</tt> object representing a connection between * a Producer service and a Consumer service must be * removed. * The persistently stored configuration data for the <tt>Wire</tt> object * must destroyed. The <tt>Wire</tt> object's method {@link Wire#isValid} will return <tt>false</tt> * after it is deleted. * * <p>The Wire Admin service must broadcast a <tt>WireAdminEvent</tt> of type * {@link WireAdminEvent#WIRE_DELETED} * after the <tt>Wire</tt> object becomes invalid. * * @param wire The <tt>Wire</tt> object which is to be deleted. */ public void deleteWire(Wire wire) { if(m_wires.contains(wire)) { WireImpl wireImpl = (WireImpl) wire; m_wires.remove(wire); if(wireImpl.isConnected()) { List wires = (List) m_producers.get(wireImpl.getProducerServiceRef()); wires.remove(wireImpl); m_asyncMethodCaller.consumersConnected(wireImpl.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()])); wires = (List) m_consumers.get(wireImpl.getConsumerServiceRef()); wires.remove(wireImpl); m_asyncMethodCaller.producersConnected(wireImpl.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()])); } wireImpl.invalidate(); // fire an event m_eventManager.fireEvent(WireAdminEvent.WIRE_DELETED,wireImpl); // Persist state to avoid losses in case of crashes (spec not clear about this). persistWires(); } else { traceln("WireAdminImpl: Cannot delete a wire that is not managed by this service"); } } /** * Update the properties of a <tt>Wire</tt> object. * * The persistently stored configuration data for the <tt>Wire</tt> object * is updated with the new properties and then the Consumer and Producer * services will be called at the respective {@link Consumer#producersConnected} * and {@link Producer#consumersConnected} methods. * * <p>The Wire Admin service must broadcast a <tt>WireAdminEvent</tt> of type * {@link WireAdminEvent#WIRE_UPDATED} * after the updated properties are available from the <tt>Wire</tt> object. * * @param wire The <tt>Wire</tt> object which is to be updated. * @param properties The new <tt>Wire</tt> object's properties or <tt>null</tt> if no properties are required. */ public void updateWire(Wire wire, Dictionary props) { if(m_wires.contains(wire) == false) { traceln("WireAdminImpl: Cannot update a wire that is not managed by this service"); return; } // Clone the dictionary Dictionary properties = cloneProperties(props); // Put again the mandatory properties, in case they are not set properties.put(WireConstants.WIREADMIN_CONSUMER_PID,wire.getProperties().get(WireConstants.WIREADMIN_CONSUMER_PID)); properties.put(WireConstants.WIREADMIN_PRODUCER_PID,wire.getProperties().get(WireConstants.WIREADMIN_PRODUCER_PID)); properties.put(WireConstants.WIREADMIN_PID,wire.getProperties().get(WireConstants.WIREADMIN_PID)); WireImpl wireImpl = (WireImpl) wire; wireImpl.updateProperties(properties); // Call methods on Consumer and Producer if(wireImpl.isConnected()) { List wires = (List) m_producers.get(wireImpl.getProducerServiceRef()); m_asyncMethodCaller.consumersConnected(wireImpl.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()])); wires = (List) m_consumers.get(wireImpl.getConsumerServiceRef()); m_asyncMethodCaller.producersConnected(wireImpl.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()])); } // fire an event m_eventManager.fireEvent(WireAdminEvent.WIRE_UPDATED,wireImpl); } /** * Return the <tt>Wire</tt> objects that match the given <tt>filter</tt>. * * <p>The list of available <tt>Wire</tt> objects is matched against the * specified <tt>filter</tt>. <tt>Wire</tt> objects which match the * <tt>filter</tt> must be returned. These <tt>Wire</tt> objects are not necessarily * connected. The Wire Admin service should not return * invalid <tt>Wire</tt> objects, but it is possible that a <tt>Wire</tt> * object is deleted after it was placed in the list. * * <p>The filter matches against the <tt>Wire</tt> object's properties including * {@link WireConstants#WIREADMIN_PRODUCER_PID}, {@link WireConstants#WIREADMIN_CONSUMER_PID} * and {@link WireConstants#WIREADMIN_PID}. * * @param filter Filter string to select <tt>Wire</tt> objects * or <tt>null</tt> to select all <tt>Wire</tt> objects. * @return An array of <tt>Wire</tt> objects which match the <tt>filter</tt> * or <tt>null</tt> if no <tt>Wire</tt> objects match the <tt>filter</tt>. * @throws org.osgi.framework.InvalidSyntaxException If the specified <tt>filter</tt> * has an invalid syntax. * @see org.osgi.framework.Filter */ public Wire[] getWires(String filter) throws InvalidSyntaxException { List res = null; if (filter == null) { return (Wire [])m_wires.toArray(new Wire[m_wires.size()]); } else { Filter tempFilter = m_bundleContext.createFilter(filter); Iterator iter = m_wires.iterator(); while (iter.hasNext()) { WireImpl currentWire = (WireImpl) iter.next(); if (tempFilter.match(currentWire.getProperties())) { if (res == null) { res = new ArrayList(); } res.add(currentWire); } } } if (res == null) { return null; } else { return (Wire [])res.toArray(new Wire[res.size()]); } } /** * listens Producer and Consumer services changes * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent) */ public void serviceChanged(ServiceEvent e) { ServiceReference serviceRef = e.getServiceReference(); // A consumer service changed if (m_consumerFilter.match(serviceRef)) { switch (e.getType()) { case ServiceEvent.REGISTERED : traceln("consumer registered"); List wires = new ArrayList(); synchronized(m_consumers) { m_consumers.put(serviceRef,wires); } synchronized(m_wires) { Iterator wireIt = m_wires.iterator(); boolean called = false; // Iterate over all existing wires while(wireIt.hasNext()) { WireImpl currentWire = (WireImpl) wireIt.next(); if(currentWire.getConsumerPID().equals(serviceRef.getProperty(Constants.SERVICE_PID))) { // This wire is associated to the newly arrived consumer currentWire.bindConsumer(serviceRef); if(currentWire.isConnected()) { // The wire has been connected, both producer and consumer // must be updated wires.add(currentWire); called = true; m_asyncMethodCaller.producersConnected(currentWire.getConsumer(),(Wire[])wires.toArray(new Wire[wires.size()])); List producerWires = (List) m_producers.get(currentWire.getProducerServiceRef()); producerWires.add(currentWire); m_asyncMethodCaller.consumersConnected(currentWire.getProducer(),(Wire[])producerWires.toArray(new Wire[producerWires.size()])); } } } if(!called) { // P. 329 "If the Consumer service has no Wire objects attached when it // is registered, the WireAdmin service must always call producersConnected(null) m_asyncMethodCaller.producersConnected((Consumer) m_bundleContext.getService(serviceRef),null); } } break; case ServiceEvent.UNREGISTERING : traceln("consumer unregistering"); synchronized(m_consumers) { m_consumers.remove(serviceRef); } synchronized(m_wires) { Iterator wireIt = m_wires.iterator(); while(wireIt.hasNext()) { WireImpl currentWire = (WireImpl) wireIt.next(); if(currentWire.getConsumerPID().equals(serviceRef.getProperty(Constants.SERVICE_PID))) { // p. 328 "When a Consumer or Producer service is unregistered // from the OSGi framework, the other object in the association // is informed that the Wire object is no longer valid" if(currentWire.isConnected()) { currentWire.unbindConsumer(); List producerWires = (List) m_producers.get(currentWire.getProducerServiceRef()); producerWires.remove(currentWire); m_asyncMethodCaller.consumersConnected(currentWire.getProducer(),(Wire[])producerWires.toArray(new Wire[producerWires.size()])); } else { currentWire.unbindConsumer(); } } } } break; case ServiceEvent.MODIFIED : // TODO Respond to consumer service modification traceln("consumer service modified"); break; } } // Removed else to manage services which are both producers AND consumers if (m_producerFilter.match(serviceRef)) { switch (e.getType()) { case ServiceEvent.REGISTERED : traceln("producer registered"); List wires = new ArrayList(); synchronized(m_producers) { m_producers.put(serviceRef,wires); } synchronized(m_wires) { Iterator wireIt = m_wires.iterator(); boolean called = false; // Iterate over all existing wires while(wireIt.hasNext()) { WireImpl currentWire = (WireImpl) wireIt.next(); if(currentWire.getProducerPID().equals(serviceRef.getProperty(Constants.SERVICE_PID))) { // This wire is associated to the newly arrived producer currentWire.bindProducer(serviceRef); if(currentWire.isConnected()) { // The wire has been connected, both producer and consumer // must be updated wires.add(currentWire); called = true; m_asyncMethodCaller.consumersConnected(currentWire.getProducer(),(Wire[])wires.toArray(new Wire[wires.size()])); List consumerWires = (List) m_consumers.get(currentWire.getConsumerServiceRef()); consumerWires.add(currentWire); m_asyncMethodCaller.producersConnected(currentWire.getConsumer(),(Wire[])consumerWires.toArray(new Wire[consumerWires.size()])); } } } if(!called) { // P. 329 "If the Producer service has no Wire objects attached when it // is registered, the WireAdmin service must always call consumersConnected(null) m_asyncMethodCaller.consumersConnected((Producer) m_bundleContext.getService(serviceRef),null); } } break; case ServiceEvent.UNREGISTERING : traceln("Producer unregistering"); synchronized(m_producers) { m_producers.remove(serviceRef); } synchronized(m_wires) { Iterator wireIt = m_wires.iterator(); while(wireIt.hasNext()) { WireImpl currentWire = (WireImpl) wireIt.next(); if(currentWire.getProducerPID().equals(serviceRef.getProperty(Constants.SERVICE_PID))) { // p. 328 "When a Consumer or Producer service is unregistered // from the OSGi framework, the other object in the association // is informed that the Wire object is no longer valid" if(currentWire.isConnected()) { currentWire.unbindProducer(); List consumerWires = (List) m_consumers.get(currentWire.getConsumerServiceRef()); consumerWires.remove(currentWire); m_asyncMethodCaller.producersConnected(currentWire.getConsumer(),(Wire[])consumerWires.toArray(new Wire[consumerWires.size()])); } else { currentWire.unbindProducer(); } } } } break; case ServiceEvent.MODIFIED : // TODO Respond to producer service modification traceln("producer service modified"); break; } } } /** * release all references before stop */ synchronized void releaseAll() { Iterator wireIt = m_wires.iterator(); while(wireIt.hasNext()) { WireImpl currentWire = (WireImpl) wireIt.next(); currentWire.invalidate(); } Iterator producerIt = m_producers.keySet().iterator(); while (producerIt.hasNext()) { ServiceReference producerRef = (ServiceReference) producerIt.next(); ((Producer)m_bundleContext.getService(producerRef)).consumersConnected(null); } Iterator consumerIt = m_consumers.keySet().iterator(); while (consumerIt.hasNext()) { ServiceReference consumerRef = (ServiceReference) consumerIt.next(); ((Consumer)m_bundleContext.getService(consumerRef)).producersConnected(null); } // Stop the thread m_asyncMethodCaller.stop(); // Notify the event manager so that it stops its thread m_eventManager.stop(); persistWires(); } /** * This method generates a PID. The pid is generated from the bundle id, * a hash code from the current time and a counter. * * @return a wire PID */ private String generateWirePID() { Date d = new Date(); String PID="wire."+m_bundleContext.getBundle().getBundleId()+d.hashCode()+m_wireCount; m_wireCount ++; // Maybe the counter should go above 9? if(m_wireCount>9) { m_wireCount = 0; } return PID; } /** * Recover persistent wires * */ private void getPersistentWires() { try { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(m_bundleContext.getDataFile("wires.ser"))); m_wires = (ArrayList) ois.readObject(); ois.close(); if(m_wires!=null) { traceln("Deserialized "+m_wires.size()+" wires"); Iterator wireIt = m_wires.iterator(); while(wireIt.hasNext()) { WireImpl currentWire = (WireImpl) wireIt.next(); currentWire.initialize(m_bundleContext,m_eventManager); } } else { traceln("Couldn't Deserialize wires"); m_wires = new ArrayList(); } } catch(FileNotFoundException ex) { // do not show anything as this exception is thrown every // time the wire admin service is launched for the first // time m_wires = new ArrayList(); } catch(Exception ex) { trace(ex); m_wires = new ArrayList(); } } /** * Persist existing wires * */ private void persistWires() { try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(m_bundleContext.getDataFile("wires.ser"))); oos.writeObject(m_wires); oos.close(); traceln("Serialized "+m_wires.size()+" wires"); } catch(Exception ex) { trace(ex); } } /** * print an error * @param message message to error */ static void error(String message) { if (m_errorout != null) { m_errorout.println(message); } } /** * print a trace * @param message message to trace */ static void traceln(String message) { if (m_traceout != null) { trace(message); trace("\n"); } } /** * print a trace * @param message message to trace */ static void trace(String message) { if (m_traceout != null) { m_traceout.print(message); } } /** * print a trace * @param e exception to trace */ static void trace(Exception e) { if (m_traceout != null) { e.printStackTrace(m_traceout); } } /** * Clone a dictionary * * @param dictionary The dictionary to clone * @return a copy of the dicionary */ private Dictionary cloneProperties(Dictionary dictionary){ Dictionary properties=new Hashtable(); if (dictionary == null) { properties = new Hashtable(); } else { Enumeration enumeration=dictionary.keys(); while(enumeration.hasMoreElements()){ Object key=enumeration.nextElement(); Object value=dictionary.get(key); properties.put(key,value); } } return properties; } /** * This class enables calls to Producer.consumersConnected and Consumer.producersConnected * to be done asynchronously * * p.333 "The WireAdmin service can call the consumersConnected or producersConnected * methods during the registration of the consumer of producer service" * **/ class AsyncMethodCaller implements Runnable { private boolean m_stop = false; private List m_methodCallStack = new ArrayList(); public void run() { while (!m_stop) { Object nextTarget[] = null; synchronized (m_methodCallStack) { while (m_methodCallStack.size() == 0) { try { m_methodCallStack.wait(); } catch (InterruptedException ex) { // Ignore. } } nextTarget = (Object[]) m_methodCallStack.remove(0); } if(nextTarget[0] instanceof Producer) { try { ((Producer)nextTarget[0]).consumersConnected((Wire[])nextTarget[1]); } catch(Exception ex) { trace(ex); } } // Removed else because nextTarget can be both producer and consumer if(nextTarget[0] instanceof Consumer) { try { ((Consumer)nextTarget[0]).producersConnected((Wire[])nextTarget[1]); } catch(Exception ex) { trace(ex); } } } } /** * Place a call to Consumer.producersConnected on the stack * * @param c the consumer * @param wires the wires */ public void producersConnected(Consumer c,Wire []wires) { synchronized (m_methodCallStack) { m_methodCallStack.add(new Object[]{c,wires}); m_methodCallStack.notify(); } } /** * Place a call to Producer.consumersConnected on the stack * * @param p the producer * @param wires the wires */ public void consumersConnected(Producer p,Wire []wires) { synchronized (m_methodCallStack) { m_methodCallStack.add(new Object[]{p,wires}); m_methodCallStack.notify(); } } /** * stop the dispatcher * */ void stop() { m_stop = true; } } }