/*******************************************************************************
 * Copyright (c) 1997, 2011 by ProSyst Software GmbH and others.
 * http://www.prosyst.com
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    ProSyst Software GmbH - initial API and implementation
 *    IBM Corporation - bug fix 347974
 *******************************************************************************/
package org.eclipse.equinox.internal.wireadmin;

import java.util.*;
import org.osgi.framework.*;
import org.osgi.service.wireadmin.*;

/**
 * @author Pavlin Dobrev
 * @version 1.0
 */

class WireImpl implements Wire, ServiceListener {

	private BundleContext bc;

	/** Holds all the properties associated with this <code>Wire</code> object */
	private WireProperties properties;

	/** Holds a service reference to the associated <code>Producer</code> */
	ServiceReference producerRef;

	/** Holds a service reference to the associated <code>Consumer</code> */
	ServiceReference consumerRef;

	private Producer producer;
	private Consumer consumer;

	private Class[] flavors;

	/** Holds the last value passed to this <code>Wire</code> by the <code>Producer</code>. */
	private Object lastValue;
	/** Holds the previous value passed through this <code>Wire</code> to the <code>Consumer</code>. */
	private Object previousValue;

	private Vector envelopes;

	/**
	 * <code>WireAdmin</code> object whit which this <code>Wire</code> was
	 * created.
	 */
	private WireAdminImpl parent;

	private Filter filter = null;

	/** Holds the time of previous <code>Consumer</code> update in milliseconds */
	private long previousUpdateTime = -1;

	/** Holds the available wire values (filter attributes) */
	private Hashtable wireValues;

	/* holds a list of scopes */
	private String[] scope;

	/** Indicates that this <code>Wire</code> object has been deleted */
	boolean isValid = true;

	private boolean interoperate = true;

	private boolean allAccepted = true;

	/**
	 * Creates a <code>Wire</code> object, representing a connection between a
	 * <code>Producer</code> and <code>Consumer</code>.
	 * 
	 * @param bc
	 *            is the Wiring <code>BundleContext</code>.
	 * @param parent
	 *            is the <code>WireAdmin</code> which is the creator of this
	 *            <code>Wire</code>.
	 * @param wirePID
	 *            is a <code>String</code> holding a unique presistent
	 *            identifier of this <code>Wire</code>, generated by the
	 *            parent <code>WireAdmin</code>.
	 * @param properties
	 *            is a collection of the initial wire properties.
	 */
	WireImpl(BundleContext bc, WireAdminImpl parent, Dictionary properties) {
		this.bc = bc;
		this.parent = parent;
		this.properties = new WireProperties();

		for (Enumeration en = properties.keys(); en.hasMoreElements();) {
			Object key = en.nextElement();
			this.properties.put0(key, properties.get(key));
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#isValid()
	 */
	public boolean isValid() {
		return isValid;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#getFlavors()
	 */
	public Class[] getFlavors() {
		return isConnected() ? flavors : null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#getProperties()
	 */
	public Dictionary getProperties() {
		return properties;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#getLastValue()
	 */
	public synchronized Object getLastValue() {
		return lastValue;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#update(java.lang.Object)
	 */
	public synchronized void update(Object value) {
		if (!isConnected() || !interoperate || !isAcceptable(value)) {
			return;
		}

		if (value instanceof Envelope) {
			Envelope e = (Envelope) value;
			if (!hasScope(e.getScope())) {
				if (Activator.LOG_DEBUG) {
					Activator.log.debug(Activator.PREFIX + "Try to pass a value in an Envelop without permission, silent return.", null);
				}
				return;
			}
		}

		try {
			if (filter != null) {
				wireValues.put(WireConstants.WIREVALUE_CURRENT, value);

				// #3329
				if (previousValue != null) {
					wireValues.put(WireConstants.WIREVALUE_PREVIOUS, previousValue);
					wireValues.put(WireConstants.WIREVALUE_ELAPSED, new Long(System.currentTimeMillis() - previousUpdateTime));
				} else {
					previousValue = value; // this is to "prime the pump"
				}

				if (Number.class.isInstance(value) && Number.class.isInstance(previousValue)) {
					double val = ((Number) value).doubleValue();
					double prevVal = ((Number) previousValue).doubleValue();

					wireValues.put(WireConstants.WIREVALUE_DELTA_ABSOLUTE, new Double(Math.abs(val - prevVal)));
					// #3328
					wireValues.put(WireConstants.WIREVALUE_DELTA_RELATIVE, new Double(Math.abs(1 - prevVal / val)));
				} else {
					wireValues.remove(WireConstants.WIREVALUE_DELTA_ABSOLUTE);
					wireValues.remove(WireConstants.WIREVALUE_DELTA_RELATIVE);
				}

				if (!filter.match(wireValues)) {
					if (Activator.LOG_DEBUG) {
						Activator.log.debug(0, 10012, filter + " / " + value, null, false);
					}
					return;
				}
			}

			if (consumer != null) {
				try {
					consumer.updated(this, value);
				} catch (Throwable t) {
					parent.notifyListeners(this, WireAdminEvent.CONSUMER_EXCEPTION, t);
				} finally {
					previousValue = value;
					previousUpdateTime = System.currentTimeMillis();
					parent.notifyListeners(this, WireAdminEvent.WIRE_TRACE, null);
				}
			}
		} finally {
			lastValue = value;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#poll()
	 */
	public synchronized Object poll() {
		Object value = null;
		if (isConnected() && interoperate) {
			try {
				value = producer.polled(this);
			} catch (Throwable t) {
				// no exception in the Producer must prevent correct Wire
				// functioning
				parent.notifyListeners(this, WireAdminEvent.PRODUCER_EXCEPTION, t);
				return null;
			}
			parent.notifyListeners(this, WireAdminEvent.WIRE_TRACE, null);
			if (!isAcceptable(value) && (!(value instanceof Envelope[]))) {
				value = null;
			}
		}

		if (value != null) {
			lastValue = value;
			if (value instanceof Envelope[]) {

				if (allAccepted) {
					return value;
				}
				Envelope[] envs = (Envelope[]) value;
				if (scope == null) {
					return value;
				}

				if (envelopes == null) {
					envelopes = new Vector(envs.length);
				}
				boolean changed = false;
				for (int i = 0; i < envs.length; i++) {
					if (hasScope(envs[i].getScope())) {
						envelopes.addElement(envs[i]);
					} else {
						changed = true;
					}
				}

				if (changed) {
					value = new Envelope[envelopes.size()];
					envelopes.copyInto((Envelope[]) value);
					envelopes.removeAllElements();
				}
			}
		}
		return value;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#isConnected()
	 */
	public boolean isConnected() {
		return isValid && (consumerRef != null) && (producerRef != null);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#getScope()
	 */
	public synchronized String[] getScope() {
		return scope;
	}

	private void setScope() {
		if ((producerRef == null) || (consumerRef == null)) {
			return;
		}

		Vector prodScope = checkPermission((String[]) producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_SCOPE), WirePermission.PRODUCE, producerRef.getBundle());

		Vector consScope = checkPermission((String[]) consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_SCOPE), WirePermission.CONSUME, consumerRef.getBundle());

		if ((prodScope == null) || (consScope == null)) {
			return;
		}

		if ((consScope.size() == 1) && consScope.elementAt(0).equals("*")) {
			scope = new String[prodScope.size()];
			prodScope.copyInto(scope);
			return;
		}

		if ((prodScope.size() != ((String[]) producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_SCOPE)).length) || (consScope.size() != ((String[]) consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_SCOPE)).length)) {
			this.allAccepted = false;
		}

		Vector cloning = (Vector) prodScope.clone();

		for (Enumeration en = cloning.elements(); en.hasMoreElements();) {
			Object next = en.nextElement();
			if (!consScope.contains(next)) {
				prodScope.removeElement(next);
				this.allAccepted = false;
			}
		}

		scope = new String[prodScope.size()];
		prodScope.copyInto(scope);
	}

	private static Vector checkPermission(String[] scope, String action, Bundle b) {
		if (scope == null) {
			return null;
		}

		Vector v = new Vector();
		for (int i = 0; i < scope.length; i++) {
			WirePermission wp = new WirePermission(scope[i], action);
			if (b.hasPermission(wp)) {
				v.addElement(scope[i]);
			}
		}
		return v;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.service.wireadmin.Wire#hasScope(java.lang.String)
	 */
	public boolean hasScope(String name) {
		if ((scope == null) || ((scope.length == 1) && scope[0].equals("*"))) {
			return true;
		}

		for (int i = 0; i < scope.length; i++) {
			if (name.equals(scope[i]) || scope[i].equals(WireConstants.WIREADMIN_SCOPE_ALL[0])) {
				return true;
			}
		}
		return false;
	}

	// Utility methods
	/**
	 * Creates a string representation for this <code>Wire</code>
	 * 
	 * @return a string representation of this object.
	 */
	public String toString() {
		StringBuffer sb = new StringBuffer(100);
		sb.append("Wire[PID=");
		sb.append(properties.get(WireConstants.WIREADMIN_PID));
		sb.append(";prodPID=");
		sb.append(properties.get(WireConstants.WIREADMIN_PRODUCER_PID));
		sb.append(";consPID=");
		sb.append(properties.get(WireConstants.WIREADMIN_CONSUMER_PID));
		sb.append(";connected=");
		sb.append(isConnected());
		// sb.append(";valid=");
		// sb.append(isValid());
		// sb.append(";Scope={");
		// if (scope == null) {
		// sb.append("null");
		// } else {
		// for (int i = 0; i < scope.length; i++) {
		// sb.append(scope[i]);
		// if (i != scope.length - 2) {
		// sb.append(", ");
		// }
		// }
		// }
		sb.append("}]");

		return sb.toString();
	}

	/**
	 * This method starts tracking of
	 * <code>Producer</code> <code>Consumer</code>services associated with
	 * this <code>Wire</code> object. First we check if there are already such
	 * services started on the framework, after that two
	 * <code>ServiceTracker</code> objects are created to handle
	 * <code>Producer</code> and <code>Consumer</code> service tracking.
	 */
	synchronized void start() {
		String producerPID = (String) properties.get(WireConstants.WIREADMIN_PRODUCER_PID);
		String consumerPID = (String) properties.get(WireConstants.WIREADMIN_CONSUMER_PID);
		updateListenerFilter();
		// check if there are such services already started
		ServiceReference ref = getSingleRef(Producer.class.getName(), producerPID);

		if (ref != null) {
			serviceRegistered(ref);
		}

		ref = getSingleRef(Consumer.class.getName(), consumerPID);

		if (ref != null) {
			serviceRegistered(ref);
		}

		if (Activator.LOG_DEBUG) {
			Activator.log.debug(0, 10013, properties.get(WireConstants.WIREADMIN_PID).toString(), null, false);
		}
	}

	void updateListenerFilter() {
		String producerPID = escapeSpecialCharacters((String) properties.get(WireConstants.WIREADMIN_PRODUCER_PID));
		String consumerPID = escapeSpecialCharacters((String) properties.get(WireConstants.WIREADMIN_CONSUMER_PID));

		try {
			// Create filter for tracking services regstered as Consumer's with
			// the current
			// WIRE_CONSUMER_PID and as Producer's with current
			// WIRE_PRODUCER_PID
			// escaping *, ) and ( in the pid.
			StringBuffer sb = new StringBuffer();// "(");
			sb.append('(');
			sb.append('|');

			sb.append('(').append('&');
			sb.append('(').append(Constants.SERVICE_PID).append('=').append(consumerPID).append(')');
			sb.append('(').append(Constants.OBJECTCLASS).append('=').append(Consumer.class.getName()).append(')');
			sb.append(')');

			sb.append('(').append('&');
			sb.append('(').append(Constants.SERVICE_PID).append('=').append(producerPID).append(')');
			sb.append('(').append(Constants.OBJECTCLASS).append('=').append(Producer.class.getName()).append(')');
			sb.append(')');

			if (consumerRef != null) {// in case service.pid changes
				Long id = (Long) consumerRef.getProperty(Constants.SERVICE_ID);
				sb.append('(').append(Constants.SERVICE_ID).append('=').append(id).append(')');
			}

			if (producerRef != null) {// in case service.pid changes
				Long id = (Long) producerRef.getProperty(Constants.SERVICE_ID);
				sb.append('(').append(Constants.SERVICE_ID).append('=').append(id).append(')');
			}

			sb.append(')');

			// System.out.println("Filter is: " + sb.toString());

			bc.addServiceListener(this, sb.toString());
		} catch (InvalidSyntaxException ise) {
			/* Syntax is valid */
		}

	}

	private ServiceReference getSingleRef(String clazz, String pid) {
		ServiceReference[] ref = null;

		try {
			ref = bc.getServiceReferences(clazz, "(" + Constants.SERVICE_PID + "=" + escapeSpecialCharacters(pid) + ")");
		} catch (InvalidSyntaxException e) {
		}

		if (ref != null) {
			if (ref.length > 1) {
				if (Activator.LOG_DEBUG) {
					Activator.log.debug(Activator.PREFIX + "Found more than one " + clazz + " services registered with the same pid: " + pid + "Wire was not created, please unregister all services which duplicate the pid.", null);
				}

				parent.deleteWire(this);
			} else if (ref.length == 1) {
				return ref[0];
			}
		}
		return null;
	}

	/**
	 * Stops service tracking, removes this <code>Wire</code> from the wire
	 * lists of the associated Consumer and Producer services and informs them
	 * for disconnecting.
	 */
	synchronized void stop() {
		if (!isValid)
			return; // already stopped

		if (Activator.LOG_DEBUG) {
			Activator.log.debug(0, 10014, this.toString(), null, false);
		}
		bc.removeServiceListener(this);
		if ((producerRef != null) && (consumerRef != null)) {
			// if this wire was connected
			isValid = false;
			informServices();
		}
		isValid = false;

		if (producerRef != null) {
			bc.ungetService(producerRef);
		}
		if (consumerRef != null) {
			bc.ungetService(consumerRef);
		}

		// let gc do his work
		producerRef = null;
		consumerRef = null;

		producer = null;
		consumer = null;

		lastValue = null;
		previousValue = null;
		parent = null;
		filter = null;
		wireValues = null;
		scope = null;
		bc = null;
	}

	/**
	 * This method is invoked when the Wiring tracker detects a service
	 * registration or when the Wire is created The service type is determined
	 * (Producer or Consumer) and added to this <code>Wire</code>. Both
	 * Consumer and Producer (if available) are informed for connecting.
	 * 
	 * @param sRef
	 *            the service reference of the wire's producer or consumer
	 * @param notifyService
	 *            specifies whether notification of the Producer/Consumer is
	 *            necessary if it is the only one available part.
	 */
	private void serviceRegistered(ServiceReference sRef) {
		String pid = (String) sRef.getProperty(Constants.SERVICE_PID);

		if (pid.equals(properties.get(WireConstants.WIREADMIN_PRODUCER_PID))) {
			if (producerRef == null) {
				this.producerRef = sRef;
				this.producer = (Producer) bc.getService(producerRef);
				if (Activator.LOG_DEBUG) {
					Activator.log.debug(Activator.PREFIX + "Wire " + properties.get(WireConstants.WIREADMIN_PID) + " detected producer " + pid, null);
				}
			} else
				return;
		} else {
			if (consumerRef == null) {
				if (Activator.LOG_DEBUG) {
					Activator.log.debug(Activator.PREFIX + "Wire " + properties.get(WireConstants.WIREADMIN_PID) + " detected consumer " + pid, null);
				}
				this.consumerRef = sRef;
				this.consumer = (Consumer) bc.getService(consumerRef);
				try {
					this.flavors = (Class[]) consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_FLAVORS);
				} catch (ClassCastException cce) {
					/* won't be initialized */
				}
			} else
				return;
		}

		if (isConnected()) {
			// inform the Producer/Consumer for connecting
			setScope();
			informServices();
			parent.notifyListeners(this, WireAdminEvent.WIRE_CONNECTED, null);
			checkInteroperability();
		} else if (!parent.hasAConnectedWire(pid.equals(properties.get(WireConstants.WIREADMIN_PRODUCER_PID)), pid)) {
			// this service has no connected wire objects attached to it
			if (Activator.LOG_DEBUG) {
				Activator.log.debug(0, 10015, pid, null, false);
			}
			// if (notifyService) { // The service is notified if it is only
			// just registered
			// informServices();
			// }
		}

		updateListenerFilter();
		checkWireFilter();
	}

	private void checkWireFilter() {
		boolean performFiltering = ((producerRef != null) && producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_FILTERS) == null) && (properties.get(WireConstants.WIREADMIN_FILTER) != null);

		if (performFiltering) {
			if ((wireValues == null)) {
				wireValues = new Hashtable(6, 1.0f);
			}

			try {
				filter = bc.createFilter((String) properties.get(WireConstants.WIREADMIN_FILTER));
			} catch (InvalidSyntaxException ise) {
				if (Activator.LOG_DEBUG) {
					Activator.log.debug(Activator.PREFIX + "Filter syntax is invalid, filtering won't be made", null);
				}
			}
		} else {
			filter = null;
		}
	}

	private void serviceModified(ServiceReference sRef) { // fix #1073
		String pid = (String) sRef.getProperty(Constants.SERVICE_PID);

		if (sRef.equals(producerRef)) {
			String currentPID = (String) properties.get(WireConstants.WIREADMIN_PRODUCER_PID);

			if (!currentPID.equals(pid)) {
				// System.out.println("The pid of the PRODUCER is changed");
				serviceUnregistered(sRef);
				return;
			}
		}

		if (sRef.equals(consumerRef)) {
			String currentPID = (String) properties.get(WireConstants.WIREADMIN_CONSUMER_PID);

			if (!currentPID.equals(pid)) {
				serviceUnregistered(sRef);
				// System.out.println("The pid of the CONSUMER is changed");
				return;
			}
			try {
				this.flavors = (Class[]) consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_FLAVORS);
			} catch (ClassCastException cce) {
				/* won't be initialized */
			}
		}

		if (producerRef == null || consumerRef == null) {
			// System.out.println("Check if PID is BACK");
			serviceRegistered(sRef);
			return;
		}

		if (!isConnected()) {
			return;
		}

		setScope();
		checkInteroperability();
		checkWireFilter();
	}

	private void checkInteroperability() {
		String[] p = (String[]) producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_COMPOSITE);
		String[] c = (String[]) consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_COMPOSITE);

		if ((p != null) && (c != null)) {
			for (int i = 0; i < p.length; i++) {
				for (int j = 0; j < c.length; j++) {
					if (p[i].equals(c[j])) {
						// found at least one match
						interoperate = true;
						return;
					}
				}
			}
			if (Activator.LOG_DEBUG) {
				Activator.log.debug(0, 10016, this.toString(), null, false);
			}
			interoperate = false;
		} else {
			interoperate = true;
		}
	}

	private void serviceUnregistered(ServiceReference sRef) {
		boolean lastStatus = isConnected();

		if (sRef.equals(producerRef)) {
			this.producerRef = null;
			this.producer = null;
		} else if (sRef.equals(consumerRef)) {
			this.consumerRef = null;
			this.consumer = null;
			this.flavors = null;
		} else {
			if (Activator.LOG_DEBUG) {
				Activator.log.debug(Activator.PREFIX + "Unregistering another consumer with the same pid, ignoring it ...", null);
			}
			return;
		}

		updateListenerFilter();

		if (lastStatus) {
			// last wire state was connected - now it is disconnected
			informServices();
			parent.notifyListeners(this, WireAdminEvent.WIRE_DISCONNECTED, null);
		}
		if (bc != null) {
			bc.ungetService(sRef);
		}

	}

	/**
	 * This method checks if the given String contains one of the characters *, (,
	 * and ) which have a special meaning for the LDAP search filters. If there
	 * are such characters they are escaped with the backslash character ('\').
	 */
	static String escapeSpecialCharacters(String s) {
		char[] content = s.toCharArray();
		StringBuffer result = new StringBuffer(s);
		int offset = 0;

		for (int i = 0; i < content.length; i++) {
			if ((content[i] == 40) || (content[i] == 41) || (content[i] == 42)) {
				result.insert(i + offset, "\\"); // fix #1078
				offset++;
			}
		}
		return result.toString();
	}

	/**
	 * Change the properties of this wire. If the changes concern one of the
	 * properties WIRE_PRODUCER_PID and WIRE_CONSUMER_PID this wire must be
	 * stopped and started again to begin tracking of the new Services.
	 */
	void setProperties(Dictionary newProps) {
		if (newProps != null) {
			String newConsPID = (String) newProps.get(WireConstants.WIREADMIN_CONSUMER_PID);
			String newProdPID = (String) newProps.get(WireConstants.WIREADMIN_PRODUCER_PID);

			String oldConsPID = (String) properties.get(WireConstants.WIREADMIN_CONSUMER_PID);
			String oldProdPID = (String) properties.get(WireConstants.WIREADMIN_PRODUCER_PID);

			boolean restart = false;

			if (newConsPID == null) {
				newProps.put(WireConstants.WIREADMIN_CONSUMER_PID, oldConsPID);
			} else if (!newConsPID.equals(oldConsPID)) {
				restart = true;
			}

			if (newProdPID == null) {
				newProps.put(WireConstants.WIREADMIN_PRODUCER_PID, oldProdPID);
			} else if (!newProdPID.equals(oldProdPID)) {
				restart = true;
			}
			// fix #1074
			if (newProps.get(WireConstants.WIREADMIN_PID) == null) {
				newProps.put(WireConstants.WIREADMIN_PID, properties.get(WireConstants.WIREADMIN_PID));
			}

			// WireProperties wprops = new WireProperties();
			properties.clear();

			for (Enumeration en = newProps.keys(); en.hasMoreElements();) {
				Object key = en.nextElement();
				properties.put0(key, newProps.get(key));
			}

			checkWireFilter();

			if (restart) {
				// One of the Consumer or Producer has been changed so restart
				// the wire
				stop();
				start();
				return;
			}
		}

		if (isConnected()) {
			informServices();
		}

		parent.notifyListeners(this, WireAdminEvent.WIRE_UPDATED, null);
	}

	private boolean isAcceptable(Object value) {
		Class[] flavors = getFlavors();

		if (flavors == null) {
			return true;
		}

		for (int i = 0; i < flavors.length; i++) {
			if (flavors[i].isInstance(value)) {
				return true;
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
	 */
	public void serviceChanged(ServiceEvent evt) {
		if (bc == null) {
			return;
		}
		switch (evt.getType()) {
			case ServiceEvent.REGISTERED :
				// System.out.println("E V E N T : registered");
				serviceRegistered(evt.getServiceReference());
				break;

			case ServiceEvent.UNREGISTERING :
				// System.out.println("E V E N T : unregistering");
				serviceUnregistered(evt.getServiceReference());
				break;

			case ServiceEvent.MODIFIED :
				// System.out.println("E V E N T : modified");
				serviceModified(evt.getServiceReference());
		}
	}

	String getWirePID() {
		return (String) properties.get(WireConstants.WIREADMIN_PID);
	}

	/**
	 * This method simply informs both Producer and Consumer with the methods
	 * consumersConnected, producersConnected if a wire bacames connected and
	 * the remaining service if the wire becames disconnected.
	 */
	private void informServices() {
		if (producerRef != null) {
			String producerPID = (String) properties.get(WireConstants.WIREADMIN_PRODUCER_PID);
			NotificationEvent ne = new NotificationEvent(producer, null, this, parent.getConnected(WireConstants.WIREADMIN_PRODUCER_PID, producerPID));
			parent.notifyConsumerProducer(ne);

			// try {
			// producer.consumersConnected(parent.getConnected(WireConstants.WIREADMIN_PRODUCER_PID,
			// producerPID));
			// } catch (Exception ex) {
			// parent.notifyListeners(this, WireAdminEvent.PRODUCER_EXCEPTION ,
			// ex);
			// }
		}

		if (consumerRef != null) {
			String consumerPID = (String) properties.get(WireConstants.WIREADMIN_CONSUMER_PID);
			NotificationEvent ne = new NotificationEvent(null, consumer, this, parent.getConnected(WireConstants.WIREADMIN_CONSUMER_PID, consumerPID));
			parent.notifyConsumerProducer(ne);

			// try {
			// consumer.producersConnected(parent.getConnected(WireConstants.WIREADMIN_CONSUMER_PID,
			// consumerPID));
			// } catch (Exception ex) {
			// parent.notifyListeners(this, WireAdminEvent.CONSUMER_EXCEPTION ,
			// ex);
			// }
		}
	}

}
