| /******************************************************************************* |
| * Copyright (c) 1997, 2009 by ProSyst Software GmbH |
| * http://www.prosyst.com |
| * 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: |
| * ProSyst Software GmbH - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.wireadmin; |
| |
| import java.io.IOException; |
| import java.util.*; |
| import org.osgi.framework.*; |
| import org.osgi.service.cm.*; |
| import org.osgi.service.wireadmin.*; |
| |
| /** |
| * Wire Admin service implementation |
| * |
| * @author Pavlin Dobrev |
| * @author Stoyan Boshev |
| * @version 1.0 |
| */ |
| class WireAdminImpl implements WireAdmin, ManagedServiceFactory, ServiceListener { |
| |
| private EventDispatcher evtDisp; |
| |
| private static final String PID_PREFIX = "WA_GENERATED_PID_"; |
| private static final String FACTORY_PID = "equinox.wireadmin.fpid"; |
| |
| private BundleContext bc; |
| |
| ConfigurationAdmin cm; |
| |
| private Hashtable wires; // maps wire pid to the WireImpl object |
| private ServiceRegistration regWireAdmin; |
| private ServiceRegistration regManagedFactory; |
| |
| private Vector waitForUpdate = new Vector(); |
| |
| long counter = System.currentTimeMillis(); |
| |
| /** |
| * Constructs an <code>WireAdminImpl</code> object, which provides |
| * Framework with methods for manipulating a <code>Wire</code> objects. |
| * Initialazed are hashtables for storing created wires, wires per |
| * <code>Producer</code>, and wires per <code>Consumer</code>. |
| * |
| * @param bc |
| * is the BundleContext object used for interaction with |
| * Framework. |
| */ |
| |
| WireAdminImpl(BundleContext bc, ConfigurationAdmin cm) { |
| this.bc = bc; |
| this.cm = cm; |
| wires = new Hashtable(); |
| evtDisp = new EventDispatcher(bc, this); |
| |
| Hashtable props = new Hashtable(2, 1.0f); |
| props.put(Constants.SERVICE_PID, FACTORY_PID); |
| props.put("service.factoryPid", FACTORY_PID); |
| |
| if (cm != null) { |
| try { |
| Configuration[] all = cm.listConfigurations("(service.factoryPid=" + FACTORY_PID + ")"); |
| if (all != null) { |
| WireImpl wire; |
| |
| for (int i = 0; i < all.length; i++) { |
| Dictionary properties = all[i].getProperties(); |
| String pid = (String) properties.get(WireConstants.WIREADMIN_PID); |
| |
| if (pid == null) { |
| pid = all[i].getPid(); |
| properties.put(WireConstants.WIREADMIN_PID, pid); |
| } |
| |
| wire = new WireImpl(bc, this, properties); |
| wires.put(pid, wire); |
| wire.start(); |
| } |
| } |
| } catch (IOException ioe) { |
| /* blocking won't be made */ |
| } catch (InvalidSyntaxException ise) { |
| /* syntax is valid */ |
| } |
| } |
| |
| // register as ManagedServiceFactory after loading the current config |
| // values and creating the wires !!!!!!!! |
| // This will not lead to bugs because of updating wire props while wire |
| // is not preloaded |
| regManagedFactory = bc.registerService(ManagedServiceFactory.class.getName(), this, props); |
| regWireAdmin = bc.registerService(WireAdmin.class.getName(), this, props); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.wireadmin.WireAdmin#createWire(java.lang.String, |
| * java.lang.String, java.util.Dictionary) |
| */ |
| public Wire createWire(String producerPID, String consumerPID, Dictionary props) { |
| return createWire(producerPID, consumerPID, props, null); |
| } |
| |
| private Wire createWire(String producerPID, String consumerPID, Dictionary props, String pid) { |
| if (Activator.LOG_DEBUG) { |
| Activator.log.debug(0, 10004, producerPID + " / " + consumerPID + " / =" + props, null, false); |
| } |
| |
| if ((pid == null) && (producerPID == null || consumerPID == null)) { |
| throw new IllegalArgumentException("PIDs can not be null"); |
| } |
| |
| if (props == null) { |
| props = new Hashtable(7, 1.0f); |
| } else if (caseVariants(props)) { |
| throw new IllegalArgumentException("Illegal wire properties. Two or more keys with the same value, or incorrect key type!"); |
| } |
| |
| if (pid == null) { |
| // put the keys |
| props.put(WireConstants.WIREADMIN_PRODUCER_PID, producerPID); |
| props.put(WireConstants.WIREADMIN_CONSUMER_PID, consumerPID); |
| } |
| WireImpl wire = null; |
| |
| if (pid != null) { |
| wire = new WireImpl(bc, this, props); |
| wires.put(pid, wire); |
| wire.start(); |
| } else if (cm != null) { |
| try { |
| Configuration config = cm.createFactoryConfiguration(FACTORY_PID); |
| props.put(WireConstants.WIREADMIN_PID, config.getPid()); |
| wire = new WireImpl(bc, this, props); |
| /* Object oldWire = */wires.put(config.getPid(), wire); |
| // if (oldWire != null) { |
| // System.out.println("\n\n@@@@@@@@@@@@@@@@@@ Old wire lost!!!! |
| // Wire is "+oldWire); |
| // System.out.println("@@@@@@@@@@@@@@@@@@ New Wire is "+wire); |
| // } |
| wire.start(); |
| waitForUpdate.addElement(wire); |
| config.update(props); |
| } catch (IOException ioe) { |
| if (Activator.LOG_DEBUG) { |
| Activator.log.debug(0, 10005, null, ioe, false); |
| } |
| } |
| } else { |
| String wirePID = getNextPID(); |
| props.put(WireConstants.WIREADMIN_PID, wirePID); |
| props.put("service.factoryPid", "equinox.wireadmin.fpid"); |
| wire = new WireImpl(bc, this, props); |
| wires.put(wirePID, wire); |
| wire.start(); |
| |
| Activator.log.info(Activator.PREFIX + "CM not available! The created wire from Producer=" + producerPID + " and Consumer=" + consumerPID + " won't be presistently stored!"); |
| |
| } |
| |
| notifyListeners(wire, WireAdminEvent.WIRE_CREATED, null); |
| return wire; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.wireadmin.WireAdmin#updateWire(org.osgi.service.wireadmin.Wire, |
| * java.util.Dictionary) |
| */ |
| public void updateWire(Wire wire, Dictionary properties) { |
| WireImpl wireImpl = (WireImpl) wire; |
| |
| if (wireImpl == null || !wireImpl.isValid) {// fix #1064 |
| return; |
| } |
| |
| if (properties == null) { |
| properties = new Hashtable(7, 1.0f); |
| } |
| |
| for (Enumeration en = properties.keys(); en.hasMoreElements();) { |
| if (!(en.nextElement() instanceof String)) { |
| throw new IllegalArgumentException("Illegal keys, must be String type"); |
| } |
| } |
| |
| if (caseVariants(properties)) { |
| if (Activator.LOG_DEBUG) { |
| Activator.log.debug(0, 10006, null, null, false); |
| } |
| throw new IllegalArgumentException("Found case variants in properties' keys"); |
| } |
| |
| String wirePID = (String) wire.getProperties().get(WireConstants.WIREADMIN_PID); |
| |
| if ((cm != null) && (wirePID.charAt(0) != 'W')) { |
| // CM is available and this wire was not created |
| // when the CM was not available |
| |
| wireImpl.setProperties(properties); |
| wires.put(wirePID, wire); |
| waitForUpdate.addElement(wire); |
| |
| try { |
| Configuration conf = cm.getConfiguration(wirePID); |
| conf.update(properties); |
| } catch (IOException ioe) { |
| Activator.log.error(Activator.PREFIX + "I/O error updating configuration!", ioe); |
| } |
| |
| } else { |
| // either CM is not available, or |
| // this wire was created when CM was not available |
| wires.remove(wirePID); |
| wireImpl.setProperties(properties); |
| wires.put(wirePID, wire); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.wireadmin.WireAdmin#deleteWire(org.osgi.service.wireadmin.Wire) |
| */ |
| public void deleteWire(Wire wire) { |
| WireImpl wireImpl = (WireImpl) wire; |
| try { |
| if (cm != null) { |
| Configuration current = cm.getConfiguration(wireImpl.getWirePID()); |
| if (current != null) { |
| current.delete(); |
| } |
| } else { |
| disconnectWire(wireImpl); |
| } |
| } catch (IOException ioe) { |
| Activator.log.error(Activator.PREFIX + "I/O error getting a configuration!", ioe); |
| } |
| } |
| |
| private Wire[] getWires(String pid, boolean isProducer) throws InvalidSyntaxException { |
| if (isProducer) { |
| return getWires('(' + WireConstants.WIREADMIN_PRODUCER_PID + '=' + WireImpl.escapeSpecialCharacters(pid) + ')'); |
| } |
| |
| return getWires('(' + WireConstants.WIREADMIN_CONSUMER_PID + '=' + WireImpl.escapeSpecialCharacters(pid) + ')'); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.wireadmin.WireAdmin#getWires(java.lang.String) |
| */ |
| public Wire[] getWires(String filterString) throws InvalidSyntaxException { |
| if (filterString == null) { |
| return getAllWires(); |
| } |
| |
| Filter filter = bc.createFilter(filterString); |
| |
| if (filter == null) { |
| return getAllWires(); |
| } |
| |
| Vector tmp = new Vector(); |
| |
| synchronized (wires) { |
| for (Enumeration en = wires.elements(); en.hasMoreElements();) { |
| Wire wire = (Wire) en.nextElement(); |
| Dictionary wireProps = wire.getProperties(); |
| |
| if (wire.isValid() && filter.match(wireProps)) { |
| tmp.addElement(wire); |
| } |
| } |
| } |
| |
| if (tmp.size() == 0) { |
| return null; |
| } |
| |
| Wire[] allWires = new Wire[tmp.size()]; |
| tmp.copyInto(allWires); |
| return allWires; |
| } |
| |
| private Wire[] getAllWires() { |
| if (wires.isEmpty()) { |
| return null; |
| } |
| |
| Vector tmp = new Vector(); |
| |
| synchronized (wires) { |
| for (Enumeration en = wires.elements(); en.hasMoreElements();) { |
| Wire wire = (Wire) en.nextElement(); |
| if (wire.isValid()) { |
| tmp.addElement(wire); |
| } |
| } |
| } |
| |
| if (tmp.size() == 0) { |
| return null; |
| } |
| |
| Wire[] allWires = new Wire[tmp.size()]; |
| tmp.copyInto(allWires); |
| return allWires; |
| } |
| |
| private void disconnectWire(WireImpl wire) { |
| if (wires == null) { |
| return; |
| } |
| Object result = wires.remove(wire.getWirePID());// Property(WireConstants.WIREADMIN_PID)); |
| if (result != null) { // the wire existed |
| boolean wasConnected = wire.isConnected(); |
| wire.stop(); |
| if (wasConnected) { |
| notifyListeners(wire, WireAdminEvent.WIRE_DISCONNECTED, null); |
| } |
| notifyListeners(wire, WireAdminEvent.WIRE_DELETED, null); |
| } |
| } |
| |
| Wire[] getConnected(String key, String value) { |
| if (key == null || value == null || wires.isEmpty()) { |
| return null; |
| } |
| |
| Vector connected = new Vector(); |
| |
| synchronized (wires) { |
| for (Enumeration en = wires.elements(); en.hasMoreElements();) { |
| WireImpl w = (WireImpl) en.nextElement(); |
| if (w.isValid() && w.isConnected() && value.equals(w.getProperties().get(key))) { |
| connected.addElement(w); |
| } |
| } |
| } |
| |
| if (connected.isEmpty()) { |
| return null; |
| } |
| |
| Wire[] cw = new Wire[connected.size()]; |
| connected.copyInto(cw); |
| return cw; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.cm.ManagedServiceFactory#deleted(java.lang.String) |
| */ |
| public void deleted(String pid) { |
| if (Activator.LOG_DEBUG) { |
| Activator.log.debug(0, 10007, pid, null, false); |
| } |
| |
| if (wires == null) { |
| return; |
| } |
| WireImpl wire = (WireImpl) wires.get(pid); |
| disconnectWire(wire); |
| if (Activator.LOG_DEBUG) { |
| Activator.log.debug(0, 10008, null, null, false); |
| } |
| } |
| |
| public String getName() { |
| return "WireAdmin Configuration Factory"; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.cm.ManagedServiceFactory#updated(java.lang.String, |
| * java.util.Dictionary) |
| */ |
| public void updated(String pid, Dictionary properties) throws ConfigurationException { |
| if (Activator.LOG_DEBUG) { |
| Activator.log.debug(0, 10009, pid, null, false); |
| Activator.log.debug(Activator.PREFIX + " " + properties, null); |
| } |
| |
| String consumerPID = (String) properties.get(WireConstants.WIREADMIN_CONSUMER_PID); |
| |
| if (consumerPID == null) { |
| throw new ConfigurationException(WireConstants.WIREADMIN_CONSUMER_PID, "is not provided"); |
| } |
| |
| String producerPID = (String) properties.get(WireConstants.WIREADMIN_PRODUCER_PID); |
| |
| if (producerPID == null) { |
| throw new ConfigurationException(WireConstants.WIREADMIN_PRODUCER_PID, "is not provided"); |
| } |
| |
| properties.put(WireConstants.WIREADMIN_PID, pid); |
| |
| if (wires == null) { |
| return; |
| } |
| |
| WireImpl wire = (WireImpl) wires.get(pid); |
| |
| if (wire != null) { |
| if (waitForUpdate.contains(wire)) { |
| // skip the first update after the wire was created |
| // or after the wire properties were updated |
| waitForUpdate.removeElement(wire); |
| } else { |
| // updating properties |
| if (Activator.LOG_DEBUG) { |
| Activator.log.debug(0, 10011, wire.toString(), null, false); |
| } |
| wire.setProperties(properties); |
| } |
| } else { |
| createWire(producerPID, consumerPID, properties, pid); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent) |
| */ |
| public void serviceChanged(ServiceEvent event) { |
| int type = event.getType(); |
| ServiceReference ref = event.getServiceReference(); |
| |
| if (type == ServiceEvent.UNREGISTERING) { |
| // presume that the unregistered service is a WireAdminListener |
| // service |
| evtDisp.removeListener(ref); |
| return; |
| } |
| |
| Object service = bc.getService(ref); |
| |
| if (type == ServiceEvent.REGISTERED) { |
| if (service instanceof WireAdminListener) { |
| evtDisp.addListener(ref, bc.getService(ref)); |
| } |
| |
| if (service instanceof Producer) { |
| Wire[] wires = null; |
| try { |
| wires = getWires((String) ref.getProperty(Constants.SERVICE_PID), true); |
| } catch (InvalidSyntaxException ise) { |
| /* syntax is valid */ |
| } |
| boolean doNotify = true; |
| if (wires != null) { |
| // there are wires, which may become connected |
| for (int i = 0; i < wires.length; i++) { |
| if (((WireImpl) wires[i]).consumerRef != null) { |
| // there is wire which will become (or is) connected |
| doNotify = false; |
| } |
| } |
| } |
| if (doNotify) { |
| notifyConsumerProducer(new NotificationEvent((Producer) service, null, null, null)); |
| } |
| } |
| |
| if (service instanceof Consumer) { |
| Wire[] wires = null; |
| try { |
| wires = getWires((String) ref.getProperty(Constants.SERVICE_PID), false); |
| } catch (InvalidSyntaxException ise) { |
| /* syntax is valid */ |
| } |
| |
| boolean doNotify = true; |
| if (wires != null) { |
| // there are wires, which may become connected |
| for (int i = 0; i < wires.length; i++) { |
| if (((WireImpl) wires[i]).producerRef != null) { |
| // there is wire which will become (or is) connected |
| doNotify = false; |
| } |
| } |
| } |
| if (doNotify) { |
| notifyConsumerProducer(new NotificationEvent(null, (Consumer) service, null, null)); |
| } |
| } |
| } |
| } |
| |
| // Utility methods |
| /** |
| * Sends a <code>WireAdminEvent</code> to all services registered as |
| * <code>WireAdminListener</code> |
| * |
| * @param type |
| * is the type of the <code>WireAdminEvent</code> that must be |
| * sent. |
| */ |
| void notifyListeners(Wire src, int type, Throwable t) { |
| if (regWireAdmin == null) { |
| return; |
| } |
| evtDisp.addEvent(new WireAdminEvent(regWireAdmin.getReference(), type, src, t)); |
| } |
| |
| void notifyConsumerProducer(NotificationEvent ne) { |
| evtDisp.addNotificationEvent(ne); |
| } |
| |
| void unregister() { |
| |
| if (regWireAdmin != null) { |
| regWireAdmin.unregister(); |
| regWireAdmin = null; |
| } |
| if (regManagedFactory != null) { |
| regManagedFactory.unregister(); |
| regManagedFactory = null; |
| } |
| |
| // disconnect all running wires |
| synchronized (wires) { |
| for (Enumeration en = wires.elements(); en.hasMoreElements();) { |
| disconnectWire((WireImpl) en.nextElement()); |
| } |
| } |
| |
| // stop event dispatcher |
| evtDisp.terminate(); |
| |
| cm = null; |
| wires.clear(); |
| wires = null; |
| bc = null; |
| } |
| |
| private static boolean caseVariants(Dictionary props) { |
| int k = 0; |
| int size = props.size(); |
| String[] keys = new String[size]; |
| |
| try { |
| for (Enumeration en = props.keys(); en.hasMoreElements();) { |
| keys[k] = (String) en.nextElement(); |
| k++; |
| } |
| } catch (ClassCastException cce) { |
| return true; |
| } |
| int j; |
| |
| for (int i = 0; i < size; i++) { |
| for (j = i + 1; j < size; j++) { |
| if (keys[i].equalsIgnoreCase(keys[j])) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private final String getNextPID() { |
| String nextPID = PID_PREFIX + counter++; |
| while (wires.get(nextPID) != null) { |
| nextPID = PID_PREFIX + counter++; |
| } |
| return nextPID; |
| } |
| |
| boolean hasAConnectedWire(boolean isProducer, String pid) { |
| String cPid; |
| synchronized (wires) { |
| for (Enumeration en = wires.elements(); en.hasMoreElements();) { |
| WireImpl wire = (WireImpl) en.nextElement(); |
| |
| if (wire.isValid() && wire.isConnected()) { |
| cPid = (String) wire.getProperties().get(isProducer ? WireConstants.WIREADMIN_PRODUCER_PID : WireConstants.WIREADMIN_CONSUMER_PID); |
| if (cPid.equals(pid)) { |
| return true; |
| } |
| } |
| } |
| } |
| // none of the wires in wich this service takes part |
| // will become connected after it's registering |
| return false; |
| } |
| } |