blob: a16488e46bbb488b836063840602c1c39f5eb7a7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2005 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.wireadmin;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
import org.osgi.service.prefs.PreferencesService;
import org.osgi.service.wireadmin.Consumer;
import org.osgi.service.wireadmin.Producer;
import org.osgi.service.wireadmin.WireAdminEvent;
import org.osgi.service.wireadmin.WireConstants;
import org.osgi.util.tracker.ServiceTracker;
public class WireAdmin implements org.osgi.service.wireadmin.WireAdmin {
protected Vector wires;
protected BundleContext context;
protected LogTracker log;
protected WireAdminEventProducer eventProducer;
protected ServiceReference reference;
protected WireAdminStore wireAdminStore;
protected int lastPid;
private boolean alive = false;
protected ServiceTracker producerTracker;
protected ServiceTracker consumerTracker;
protected final String consumerFilter = "(" + WireConstants.WIREADMIN_CONSUMER_PID + "="; //$NON-NLS-1$ //$NON-NLS-2$
protected final String producerFilter = "(" + WireConstants.WIREADMIN_PRODUCER_PID + "="; //$NON-NLS-1$ //$NON-NLS-2$
protected PreferencesService preferencesService;
public WireAdmin(BundleContext context) {
this.context = context;
initWireAdmin();
}
/**
* Create a new <tt>Wire</tt> object that connects a <tt>Producer</tt>
* service to a <tt>Consumer</tt> service.
*
* The <tt>Producer</tt> service and <tt>Consumer</tt> service do not
* have to be registered when the the <tt>Wire</tt> object is created.
*
* <p>
* The <tt>Wire</tt> configuration data is persistently stored. All
* <tt>Wire</tt> connections are reestablished when the <tt>WireAdmin</tt>
* service is registered. A <tt>Wire</tt> connection can be 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 | BigInteger
* | BigDecimal | 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 #WIREADMIN_PID} set to the value of the wire's persistent
* identity (PID). This value is generated by the <tt>WireAdmin</tt>
* service when a <tt>Wire</tt> object is created. </li>
* <li> {@link #WIREADMIN_PRODUCER_PID} set to the value of
* <tt>Producer</tt> service's PID. </li>
* <li> {@link #WIREADMIN_CONSUMER_PID} set to the value of
* <tt>Consumer</tt> 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
* <tt>WireAdmin</tt> service.
*
* <p>
* The Wire Admin service must generate a {@link #WIREADMIN_CREATED} event
* after the new <tt>Wire</tt> object becomes available from
* {@link #getWires}.
*
* @param producerPID
* The &quot;service.pid&quot; of the <tt>Producer</tt> service
* to be connected to the <tt>Wire</tt>.
* @param consumerPID
* The &quot;service.pid&quot; of the <tt>Consumer</tt> service
* to be connected to the <tt>Wire</tt>.
* @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 synchronized org.osgi.service.wireadmin.Wire createWire(
String producerPID, String consumerPID, Dictionary properties) {
checkAlive();
// generate PID
lastPid++;
String pid = String.valueOf(lastPid);
if (consumerPID == null || producerPID == null) {
throw new IllegalArgumentException();
}
if (properties == null) {
properties = new Hashtable(10);
} else {
checkProperties(properties);
}
Wire wire = createWire(pid, producerPID, consumerPID, properties);
try {
wireAdminStore.addWire(wire, properties);
} catch (Exception ex) {
log.log(LogService.LOG_ERROR,
WireAdminMsg.BACKING_STORE_READ_EXCEPTION, ex);
}
eventProducer.generateEvent(WireAdminEvent.WIRE_CREATED, wire, null);
return (wire);
}
protected Wire createWire(String pid, String producerPID,
String consumerPID, Dictionary properties) {
Wire wire = new Wire(pid, producerPID, consumerPID, properties, this);
wires.addElement(wire);
wire.init();
return (wire);
}
/**
* Disconnect a <tt>Producer</tt> service from a <tt>Consumer</tt>
* service.
*
* <p>
* The connection between a <tt>Producer</tt> service and a
* <tt>Consumer</tt> service is removed. The persistently stored
* configuration data for the <tt>Wire</tt> object is destroyed. The
* <tt>Wire</tt> object's method {@link Wire#isValid} will return
* <tt>false</tt> after it is destroyed.
*
* <p>
* The Wire Admin service must generate a {@link #WIREADMIN_DELETED} event
* after the <tt>Wire</tt> object becomes invalid.
*
* @param wire
* The <tt>Wire</tt> object which is to be disconnected.
*/
public void deleteWire(org.osgi.service.wireadmin.Wire wire) {
checkAlive();
wires.removeElement(wire);
((org.eclipse.equinox.wireadmin.Wire) wire).destroy();
try {
wireAdminStore.removeWire((Wire) wire);
} catch (Exception ex) {
log.log(LogService.LOG_ERROR, WireAdminMsg.BACKING_STORE_READ_EXCEPTION,
ex);
}
eventProducer.generateEvent(WireAdminEvent.WIRE_DELETED, wire, null);
}
/**
* 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 <tt>Consumer</tt> and
* <tt>Producer</tt> services will be called with the respective
* {@link Consumer#producersConnected} and
* {@link Producer#consumersConnected} methods. methods..
*
* <p>
* The Wire Admin service must generate a {@link #WIREADMIN_UPDATED} event
* after the new <tt>properties</tt> become 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(org.osgi.service.wireadmin.Wire wire,
Dictionary properties) {
checkAlive();
checkProperties(properties);
if (!wire.isValid()) {
return;
}
((org.eclipse.equinox.wireadmin.Wire) wire).setProperties(properties);
try {
wireAdminStore.updateWire((Wire) wire, properties);
} catch (Exception ex) {
log.log(LogService.LOG_ERROR, WireAdminMsg.BACKING_STORE_READ_EXCEPTION,
ex);
}
try {
Consumer consumer = ((org.eclipse.equinox.wireadmin.Wire) wire).consumer;
Wire[] wireArray = new Wire[wires.size()];
wires.copyInto(wireArray);
if (consumer != null) {
consumer.producersConnected(wireArray);
}
Producer producer = ((org.eclipse.equinox.wireadmin.Wire) wire).producer;
if (producer != null) {
producer.consumersConnected(wireArray);
}
eventProducer
.generateEvent(WireAdminEvent.WIRE_UPDATED, wire, null);
} catch (Exception ex) {
// ???
log.log(LogService.LOG_ERROR, WireAdminMsg.BACKING_STORE_READ_EXCEPTION,
ex);
}
}
/**
* 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> are 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 Constants#WIREADMIN_PRODUCER_PID},
* {@link Constants#WIREADMIN_CONSUMER_PID} and
* {@link Constants#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 org.osgi.service.wireadmin.Wire[] getWires(String filterString)
throws InvalidSyntaxException {
checkAlive();
return getWires(filterString, false);
}
protected Wire[] getConnectedWires(String filterString)
throws InvalidSyntaxException {
return getWires(filterString, true);
}
protected Wire[] getWires(String filterString, boolean connected)
throws InvalidSyntaxException {
Vector returnedWires;
synchronized (this) {
if (filterString == null) {
returnedWires = wires;
}
else {
Filter filter = context.createFilter(filterString); // We do
// this
// first so
// an
// InvalidSyntaxException will be
// thrown even if there are no wires
// present.
returnedWires = new Vector();
for (int i = 0; i < wires.size(); i++) {
Wire wire = (Wire) wires.elementAt(i);
if (filter.match(wire.properties)) {
if (!connected || wire.isConnected()) {
returnedWires.addElement(wire);
}
}
}
}
int size = returnedWires.size();
if (size == 0) {
return (null);
}
Wire[] wireArray = new Wire[size];
returnedWires.copyInto(wireArray);
return (wireArray);
}
}
protected void setServiceReference(ServiceReference reference) {
if (this.reference == null) {
this.reference = reference;
eventProducer = new WireAdminEventProducer(reference, context, log,
this);
}
}
protected void destroy() {
alive = false;
wires = null;
eventProducer.close();
producerTracker.close();
consumerTracker.close();
}
protected void checkAlive() {
if (!alive) {
throw (new IllegalStateException(
WireAdminMsg.WIREADMIN_UNREGISTERED_EXCEPTION));
}
}
protected void notifyProducer(Producer producer, String pid) {
org.osgi.service.wireadmin.Wire[] wireArray = null;
try {
wireArray = getConnectedWires(producerFilter + pid + ")"); //$NON-NLS-1$
} catch (InvalidSyntaxException ex) {
ex.printStackTrace();
}
try {
producer.consumersConnected(wireArray);
} catch (Exception ex) {
eventProducer.generateEvent(WireAdminEvent.PRODUCER_EXCEPTION,
null, ex);
}
}
protected void notifyConsumer(Consumer consumer, String pid) {
org.osgi.service.wireadmin.Wire[] wireArray = null;
try {
wireArray = getConnectedWires(consumerFilter + pid + ")"); //$NON-NLS-1$
} catch (InvalidSyntaxException ex) {
ex.printStackTrace();
}
try {
consumer.producersConnected(wireArray);
} catch (Exception ex) {
eventProducer.generateEvent(WireAdminEvent.CONSUMER_EXCEPTION,
null, ex);
}
}
private void checkProperties(Dictionary properties) {
Enumeration e = properties.keys();
Vector keys = new Vector(properties.size());
while (e.hasMoreElements()) {
String key = null;
try {
key = (String) e.nextElement();
} catch (ClassCastException ex) {
throw new IllegalArgumentException(
WireAdminMsg.WIREADMIN_PROP_KEY_MUST_BE_STRING);
}
key = key.toUpperCase();
if (keys.contains(key)) {
throw new IllegalArgumentException(
WireAdminMsg.WIREADMIN_KEYS_CASE_INSENSITIVE_MATCH);
}
keys.add(key);
}
}
private void initWireAdmin() {
wires = new Vector(10);
log = new LogTracker(context, System.out);
wireAdminStore = new WireAdminStore(context, this, log,
preferencesService);
try {
wireAdminStore.init(preferencesService);
} catch (Exception ex) {
log.log(LogService.LOG_ERROR, WireAdminMsg.BACKING_STORE_READ_EXCEPTION,
ex);
}
consumerTracker = new ServiceTracker(context, Consumer.class.getName(),
new ConsumersCustomizer(context, this));
consumerTracker.open();
producerTracker = new ServiceTracker(context, Producer.class.getName(),
new ProducersCustomizer(context, this));
producerTracker.open();
alive = true;
}
}