| /******************************************************************************* |
| * 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 "service.pid" of the <tt>Producer</tt> service |
| * to be connected to the <tt>Wire</tt>. |
| * @param consumerPID |
| * The "service.pid" 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; |
| } |
| |
| } |