/******************************************************************************* * Copyright (c) 2010-2011 Composent, Inc. and others. 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: * Composent, Inc. - initial API and implementation ******************************************************************************/ package org.eclipse.ecf.osgi.services.remoteserviceadmin; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.ecf.core.identity.Namespace; import org.eclipse.ecf.discovery.IDiscoveryAdvertiser; import org.eclipse.ecf.discovery.IDiscoveryLocator; import org.eclipse.ecf.discovery.IServiceEvent; import org.eclipse.ecf.discovery.IServiceInfo; import org.eclipse.ecf.discovery.IServiceListener; import org.eclipse.ecf.discovery.identity.IServiceID; import org.eclipse.ecf.discovery.identity.IServiceTypeID; import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.Activator; import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.DebugOptions; import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.LogUtility; import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.PropertiesUtil; import org.eclipse.equinox.concurrent.future.IExecutor; import org.eclipse.equinox.concurrent.future.IProgressRunnable; import org.eclipse.equinox.concurrent.future.ThreadsExecutor; import org.eclipse.osgi.framework.eventmgr.CopyOnWriteIdentityMap; import org.eclipse.osgi.framework.eventmgr.EventDispatcher; import org.eclipse.osgi.framework.eventmgr.EventManager; import org.eclipse.osgi.framework.eventmgr.ListenerQueue; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.service.remoteserviceadmin.EndpointDescription; import org.osgi.service.remoteserviceadmin.EndpointEvent; import org.osgi.service.remoteserviceadmin.EndpointEventListener; import org.osgi.service.remoteserviceadmin.EndpointListener; import org.osgi.util.tracker.BundleTracker; import org.osgi.util.tracker.BundleTrackerCustomizer; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; /** * Implementation of EndpointDescription discovery mechanism, using any/all ECF * discovery providers (implementers if {@link IDiscoveryLocator}. * */ public class EndpointDescriptionLocator implements IEndpointDescriptionLocator { private BundleContext context; private IExecutor executor; // service info factory default private ServiceInfoFactory serviceInfoFactory; private ServiceRegistration defaultServiceInfoFactoryRegistration; // service info factory service tracker private Object serviceInfoFactoryTrackerLock = new Object(); private ServiceTracker serviceInfoFactoryTracker; // endpoint description factory default private DiscoveredEndpointDescriptionFactory defaultEndpointDescriptionFactory; private ServiceRegistration defaultEndpointDescriptionFactoryRegistration; // endpoint description factory tracker private Object endpointDescriptionFactoryTrackerLock = new Object(); private ServiceTracker endpointDescriptionFactoryTracker; // endpointDescriptionReader default private ServiceRegistration defaultEndpointDescriptionReaderRegistration; // For processing synchronous notifications asynchronously private EventManager eventManager; private ListenerQueue eventQueue; // ECF IDiscoveryLocator tracker private ServiceTracker locatorServiceTracker; // Locator listeners private Map<IDiscoveryLocator, LocatorServiceListener> locatorListeners; private ServiceTracker endpointListenerTracker; private ServiceTracker endpointEventListenerTracker; private ServiceTracker advertiserTracker; private Object advertiserTrackerLock = new Object(); private BundleTracker bundleTracker; private EndpointDescriptionBundleTrackerCustomizer bundleTrackerCustomizer; private String frameworkUUID; private ServiceRegistration<IEndpointDescriptionLocator> endpointLocatorReg; private String getFrameworkUUID() { return frameworkUUID; } public EndpointDescriptionLocator(BundleContext context) { this.context = context; this.executor = new ThreadsExecutor(); this.frameworkUUID = Activator.getDefault().getFrameworkUUID(); } public void start() { // For service info and endpoint description factories // set the service ranking to Integer.MIN_VALUE // so that any other registered factories will be preferred final Properties properties = new Properties(); properties.put(Constants.SERVICE_RANKING, new Integer(Integer.MIN_VALUE)); serviceInfoFactory = new ServiceInfoFactory(); defaultServiceInfoFactoryRegistration = context.registerService( IServiceInfoFactory.class.getName(), serviceInfoFactory, (Dictionary) properties); defaultEndpointDescriptionFactory = new DiscoveredEndpointDescriptionFactory(); defaultEndpointDescriptionFactoryRegistration = context .registerService( IDiscoveredEndpointDescriptionFactory.class.getName(), defaultEndpointDescriptionFactory, (Dictionary) properties); // setup/register default endpointDescriptionReader defaultEndpointDescriptionReaderRegistration = context.registerService( IEndpointDescriptionReader.class.getName(), new EndpointDescriptionReader(), (Dictionary) properties); // Create thread group, event manager, and eventQueue, and setup to // dispatch EndpointListenerEvents ThreadGroup eventGroup = new ThreadGroup( "RSA EndpointDescriptionLocator ThreadGroup"); //$NON-NLS-1$ eventGroup.setDaemon(true); eventManager = new EventManager( "RSA EndpointDescriptionLocator Dispatcher", eventGroup); //$NON-NLS-1$ eventQueue = new ListenerQueue(eventManager); CopyOnWriteIdentityMap listeners = new CopyOnWriteIdentityMap(); listeners.put(this, this); eventQueue.queueListeners(listeners.entrySet(), new EventDispatcher() { public void dispatchEvent(Object eventListener, Object listenerObject, int eventAction, Object eventObject) { final String logMethodName = "dispatchEvent"; //$NON-NLS-1$ // We now dispatch both EndpointListenerEvents if (eventObject instanceof EndpointListenerEvent) { final EndpointListenerEvent event = (EndpointListenerEvent) eventObject; final EndpointListener endpointListener = event .getEndpointListener(); final EndpointDescription endpointDescription = event .getEndointDescription(); final String matchingFilter = event.getMatchingFilter(); try { boolean discovered = event.isDiscovered(); trace("endpointListener.discovered=" + discovered + " ", "fwk=" + getFrameworkUUID() + ", endpointListener=" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + endpointListener + ", endpointDescription=" //$NON-NLS-1$ + endpointDescription + ", matchingFilter=" //$NON-NLS-1$ + matchingFilter); if (discovered) endpointListener.endpointAdded(endpointDescription, matchingFilter); else endpointListener.endpointRemoved( endpointDescription, matchingFilter); } catch (Exception e) { String message = "Exception in EndpointListener listener=" //$NON-NLS-1$ + endpointListener + " description=" //$NON-NLS-1$ + endpointDescription + " matchingFilter=" //$NON-NLS-1$ + matchingFilter; logError(logMethodName, message, e); } catch (LinkageError e) { String message = "LinkageError in EndpointListener listener=" //$NON-NLS-1$ + endpointListener + " description=" //$NON-NLS-1$ + endpointDescription + " matchingFilter=" //$NON-NLS-1$ + matchingFilter; logError(logMethodName, message, e); } catch (AssertionError e) { String message = "AssertionError in EndpointListener listener=" //$NON-NLS-1$ + endpointListener + " description=" //$NON-NLS-1$ + endpointDescription + " matchingFilter=" //$NON-NLS-1$ + matchingFilter; logError(logMethodName, message, e); } // and EndpointEventListenerEvents } else if (eventObject instanceof EndpointEventListenerEvent) { final EndpointEventListenerEvent event = (EndpointEventListenerEvent) eventObject; final EndpointEventListener endpointEventListener = event .getEndpointEventListener(); final EndpointEvent endpointEvent = event .getEndpointEvent(); final String matchingFilter = event.getMatchingFilter(); try { trace("endpointEventListener.discovered=" + getEndpointEventTypeAsString(endpointEvent.getType()) + " ", "fwk=" + getFrameworkUUID() + ", endpointEventListener=" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + endpointEventListener + ", endpointEvent=" //$NON-NLS-1$ + endpointEvent + ", matchingFilter=" //$NON-NLS-1$ + matchingFilter); endpointEventListener.endpointChanged(endpointEvent, matchingFilter); } catch (Exception e) { String message = "Exception in EndpointEventListener listener=" //$NON-NLS-1$ + endpointEventListener + " event=" //$NON-NLS-1$ + endpointEvent + " matchingFilter=" //$NON-NLS-1$ + matchingFilter; logError(logMethodName, message, e); } catch (LinkageError e) { String message = "LinkageError in EndpointEventListener listener=" //$NON-NLS-1$ + endpointEventListener + " event=" //$NON-NLS-1$ + endpointEvent + " matchingFilter=" //$NON-NLS-1$ + matchingFilter; logError(logMethodName, message, e); } catch (AssertionError e) { String message = "AssertionError in EndpointEventListener listener=" //$NON-NLS-1$ + endpointEventListener + " event=" //$NON-NLS-1$ + endpointEvent + " matchingFilter=" //$NON-NLS-1$ + matchingFilter; logError(logMethodName, message, e); } } } }); // Register the endpoint listener tracker, so that endpoint listeners // that are subsequently added // will then be notified of discovered endpoints endpointListenerTracker = new ServiceTracker(context, EndpointListener.class.getName(), new ServiceTrackerCustomizer() { public Object addingService(ServiceReference reference) { if (context == null) return null; EndpointListener listener = (EndpointListener) context .getService(reference); if (listener == null) return null; Collection<org.osgi.service.remoteserviceadmin.EndpointDescription> allDiscoveredEndpointDescriptions = getEDs(); for (org.osgi.service.remoteserviceadmin.EndpointDescription ed : allDiscoveredEndpointDescriptions) { EndpointDescriptionLocator.EndpointListenerHolder[] endpointListenerHolders = getMatchingEndpointListenerHolders( new ServiceReference[] { reference }, ed); if (endpointListenerHolders != null) { for (int i = 0; i < endpointListenerHolders.length; i++) { queueEndpointDescription( endpointListenerHolders[i] .getListener(), endpointListenerHolders[i] .getDescription(), endpointListenerHolders[i] .getMatchingFilter(), true); } } } return listener; } public void modifiedService(ServiceReference reference, Object service) { } public void removedService(ServiceReference reference, Object service) { } }); endpointListenerTracker.open(); // Register the endpoint event listener tracker, so that endpoint event // listeners // that are subsequently added // will then be notified of discovered endpoints endpointEventListenerTracker = new ServiceTracker(context, EndpointEventListener.class.getName(), new ServiceTrackerCustomizer() { public Object addingService(ServiceReference reference) { if (context == null) return null; EndpointEventListener listener = (EndpointEventListener) context .getService(reference); if (listener == null) return null; Collection<org.osgi.service.remoteserviceadmin.EndpointDescription> allDiscoveredEndpointDescriptions = getEDs(); for (org.osgi.service.remoteserviceadmin.EndpointDescription ed : allDiscoveredEndpointDescriptions) { EndpointDescriptionLocator.EndpointEventListenerHolder[] endpointEventListenerHolders = getMatchingEndpointEventListenerHolders( new ServiceReference[] { reference }, ed, EndpointEvent.ADDED); if (endpointEventListenerHolders != null) { for (int i = 0; i < endpointEventListenerHolders.length; i++) { queueEndpointDescription( endpointEventListenerHolders[i] .getListener(), endpointEventListenerHolders[i] .getDescription(), endpointEventListenerHolders[i] .getMatchingFilter(), endpointEventListenerHolders[i] .getType()); } } } return listener; } public void modifiedService(ServiceReference reference, Object service) { } public void removedService(ServiceReference reference, Object service) { } }); endpointEventListenerTracker.open(); locatorListeners = new HashMap(); // Create locator service tracker, so new IDiscoveryLocators can // be used to discover endpoint descriptions locatorServiceTracker = new ServiceTracker(context, IDiscoveryLocator.class.getName(), new LocatorTrackerCustomizer()); locatorServiceTracker.open(); // Create bundle tracker for reading local/xml-file endpoint // descriptions bundleTrackerCustomizer = new EndpointDescriptionBundleTrackerCustomizer(); bundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STARTING, bundleTrackerCustomizer); // This may trigger local endpoint description discovery bundleTracker.open(); this.endpointLocatorReg = this.context.registerService( IEndpointDescriptionLocator.class, this, null); } private void logError(String methodName, String message, Throwable e) { LogUtility.logError(methodName, DebugOptions.ENDPOINT_DESCRIPTION_LOCATOR, this.getClass(), message, e); } private void trace(String methodName, String message) { LogUtility.trace(methodName, DebugOptions.ENDPOINT_DESCRIPTION_LOCATOR, this.getClass(), message); } public void close() { if (this.endpointLocatorReg != null) { this.endpointLocatorReg.unregister(); this.endpointLocatorReg = null; } if (bundleTracker != null) { bundleTracker.close(); bundleTracker = null; } if (bundleTrackerCustomizer != null) { bundleTrackerCustomizer.close(); bundleTrackerCustomizer = null; } // shutdown locatorListeners synchronized (locatorListeners) { for (IDiscoveryLocator l : locatorListeners.keySet()) { LocatorServiceListener locatorListener = locatorListeners .get(l); if (locatorListener != null) { l.removeServiceListener(locatorListener); locatorListener.close(); } } locatorListeners.clear(); } Object[] locators = locatorServiceTracker.getServices(); if (locators != null) { for (int i = 0; i < locators.length; i++) { // Add service listener to locator shutdownLocator((IDiscoveryLocator) locators[i]); } } if (endpointListenerTracker != null) { endpointListenerTracker.close(); endpointListenerTracker = null; } if (endpointEventListenerTracker != null) { endpointEventListenerTracker.close(); endpointEventListenerTracker = null; } // Shutdown asynchronous event manager if (eventManager != null) { eventManager.close(); eventManager = null; } synchronized (endpointDescriptionFactoryTrackerLock) { if (endpointDescriptionFactoryTracker != null) { endpointDescriptionFactoryTracker.close(); endpointDescriptionFactoryTracker = null; } } if (defaultEndpointDescriptionFactoryRegistration != null) { defaultEndpointDescriptionFactoryRegistration.unregister(); defaultEndpointDescriptionFactoryRegistration = null; } if (defaultEndpointDescriptionFactory != null) { defaultEndpointDescriptionFactory.close(); defaultEndpointDescriptionFactory = null; } synchronized (serviceInfoFactoryTrackerLock) { if (serviceInfoFactoryTracker != null) { serviceInfoFactoryTracker.close(); serviceInfoFactoryTracker = null; } } if (defaultServiceInfoFactoryRegistration != null) { defaultServiceInfoFactoryRegistration.unregister(); defaultServiceInfoFactoryRegistration = null; } if (serviceInfoFactory != null) { serviceInfoFactory.close(); serviceInfoFactory = null; } if (defaultEndpointDescriptionReaderRegistration != null) { defaultEndpointDescriptionReaderRegistration.unregister(); defaultEndpointDescriptionReaderRegistration = null; } if (locatorServiceTracker != null) { locatorServiceTracker.close(); locatorServiceTracker = null; } synchronized (advertiserTrackerLock) { if (advertiserTracker != null) { advertiserTracker.close(); advertiserTracker = null; } } synchronized (edToServiceIDMap) { edToServiceIDMap.clear(); } this.executor = null; this.context = null; } public IDiscoveryAdvertiser[] getDiscoveryAdvertisers() { return AccessController .doPrivileged(new PrivilegedAction<IDiscoveryAdvertiser[]>() { public IDiscoveryAdvertiser[] run() { synchronized (advertiserTrackerLock) { if (advertiserTracker == null) { advertiserTracker = new ServiceTracker(context, IDiscoveryAdvertiser.class.getName(), null); advertiserTracker.open(); } } ServiceReference[] advertiserRefs = advertiserTracker .getServiceReferences(); if (advertiserRefs == null) return null; List<IDiscoveryAdvertiser> results = new ArrayList<IDiscoveryAdvertiser>(); for (int i = 0; i < advertiserRefs.length; i++) { results.add((IDiscoveryAdvertiser) context .getService(advertiserRefs[i])); } return results.toArray(new IDiscoveryAdvertiser[results .size()]); } }); } private void openLocator(IDiscoveryLocator locator) { if (context == null) return; synchronized (locatorListeners) { LocatorServiceListener locatorListener = new LocatorServiceListener( locator); locatorListeners.put(locator, locatorListener); processInitialLocatorServices(locator, locatorListener); } } private void shutdownLocator(IDiscoveryLocator locator) { if (locator == null || context == null) return; synchronized (locatorListeners) { LocatorServiceListener locatorListener = locatorListeners .remove(locator); if (locatorListener != null) locatorListener.close(); } } void queueEndpointDescription( EndpointEventListener listener, org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription, String matchingFilter, int eventType) { if (eventQueue == null) return; synchronized (eventQueue) { eventQueue.dispatchEventAsynchronous(0, new EndpointEventListenerEvent(listener, new EndpointEvent( eventType, endpointDescription), matchingFilter)); } } void queueEndpointDescription( EndpointListener listener, org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription, String matchingFilters, boolean discovered) { if (eventQueue == null) return; synchronized (eventQueue) { eventQueue .dispatchEventAsynchronous(0, new EndpointListenerEvent( listener, endpointDescription, matchingFilters, discovered)); } } void queueEndpointEvent( org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription, int type) { EndpointEventListenerHolder[] endpointEventListenerHolders = getMatchingEndpointEventListenerHolders( endpointDescription, type); if (endpointEventListenerHolders != null) { for (int i = 0; i < endpointEventListenerHolders.length; i++) { queueEndpointDescription( endpointEventListenerHolders[i].getListener(), endpointEventListenerHolders[i].getDescription(), endpointEventListenerHolders[i].getMatchingFilter(), endpointEventListenerHolders[i].getType()); } } else { LogUtility.logWarning( "queueEndpointDescription", //$NON-NLS-1$ DebugOptions.ENDPOINT_DESCRIPTION_LOCATOR, this.getClass(), "No matching EndpointEventListeners found for event type" //$NON-NLS-1$ + getEndpointEventTypeAsString(type) + " endpointDescription=" + endpointDescription); //$NON-NLS-1$ } } String getEndpointEventTypeAsString(int eventType) { if (eventType == EndpointEvent.ADDED) return "added"; //$NON-NLS-1$ if (eventType == EndpointEvent.MODIFIED) return "modified"; //$NON-NLS-1$ if (eventType == EndpointEvent.MODIFIED_ENDMATCH) return "modified endmatch"; //$NON-NLS-1$ if (eventType == EndpointEvent.REMOVED) return "removed"; //$NON-NLS-1$ return "unknown"; //$NON-NLS-1$ } void queueEndpointDescription( org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription, boolean discovered) { EndpointListenerHolder[] endpointListenerHolders = getMatchingEndpointListenerHolders(endpointDescription); if (endpointListenerHolders != null) { for (int i = 0; i < endpointListenerHolders.length; i++) { queueEndpointDescription( endpointListenerHolders[i].getListener(), endpointListenerHolders[i].getDescription(), endpointListenerHolders[i].getMatchingFilter(), discovered); } } else { // For old-style notification, we ignore this since it's probably using EndpointEvents } } private void processInitialLocatorServices(final IDiscoveryLocator locator, final LocatorServiceListener locatorListener) { IProgressRunnable runnable = new IProgressRunnable() { public Object run(IProgressMonitor arg0) throws Exception { IServiceInfo[] serviceInfos = null; try { serviceInfos = locator.getServices(); } catch (Exception e) { logError( "processInitialLocatorServices", "Exception in locator.getServices()", e); //$NON-NLS-1$ //$NON-NLS-2$ } if (serviceInfos != null) for (int i = 0; i < serviceInfos.length; i++) { locatorListener.handleService(serviceInfos[i], true); } return null; } }; executor.execute(runnable, null); } void shutdownLocators() { Object[] locators = locatorServiceTracker.getServices(); if (locators != null) { for (int i = 0; i < locators.length; i++) { // Add service listener to locator shutdownLocator((IDiscoveryLocator) locators[i]); } } } private class EndpointEventListenerEvent { private EndpointEventListener endpointEventListener; private EndpointEvent event; private String matchingFilter; public EndpointEventListenerEvent( EndpointEventListener endpointEventListener, EndpointEvent event, String matchingFilter) { this.endpointEventListener = endpointEventListener; this.event = event; this.matchingFilter = matchingFilter; } public EndpointEventListener getEndpointEventListener() { return endpointEventListener; } public EndpointEvent getEndpointEvent() { return event; } public String getMatchingFilter() { return matchingFilter; } } private class EndpointListenerEvent { private EndpointListener endpointListener; private org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription; private String matchingFilter; private boolean discovered; public EndpointListenerEvent( EndpointListener endpointListener, org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription, String matchingFilter, boolean discovered) { this.endpointListener = endpointListener; this.endpointDescription = endpointDescription; this.matchingFilter = matchingFilter; this.discovered = discovered; } public EndpointListener getEndpointListener() { return endpointListener; } public org.osgi.service.remoteserviceadmin.EndpointDescription getEndointDescription() { return endpointDescription; } public String getMatchingFilter() { return matchingFilter; } public boolean isDiscovered() { return discovered; } } private class LocatorTrackerCustomizer implements ServiceTrackerCustomizer { /* * (non-Javadoc) * * @see * org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org. * osgi.framework.ServiceReference) */ public Object addingService(ServiceReference reference) { IDiscoveryLocator locator = (IDiscoveryLocator) context .getService(reference); if (locator != null) openLocator(locator); return locator; } public void modifiedService(ServiceReference reference, Object service) { } public void removedService(ServiceReference reference, Object service) { shutdownLocator((IDiscoveryLocator) service); } } public IServiceInfoFactory getServiceInfoFactory() { return AccessController .doPrivileged(new PrivilegedAction<IServiceInfoFactory>() { public IServiceInfoFactory run() { synchronized (serviceInfoFactoryTrackerLock) { if (serviceInfoFactoryTracker == null) { serviceInfoFactoryTracker = new ServiceTracker( context, IServiceInfoFactory.class .getName(), null); serviceInfoFactoryTracker.open(); } } return (IServiceInfoFactory) serviceInfoFactoryTracker .getService(); } }); } public IDiscoveredEndpointDescriptionFactory getDiscoveredEndpointDescriptionFactory() { synchronized (endpointDescriptionFactoryTrackerLock) { if (context == null) return null; if (endpointDescriptionFactoryTracker == null) { endpointDescriptionFactoryTracker = new ServiceTracker(context, IDiscoveredEndpointDescriptionFactory.class.getName(), null); endpointDescriptionFactoryTracker.open(); } return (IDiscoveredEndpointDescriptionFactory) endpointDescriptionFactoryTracker .getService(); } } private Object endpointListenerServiceTrackerLock = new Object(); private Object endpointEventListenerServiceTrackerLock = new Object(); protected EndpointListenerHolder[] getMatchingEndpointListenerHolders( final EndpointDescription description) { return AccessController .doPrivileged(new PrivilegedAction<EndpointListenerHolder[]>() { public EndpointListenerHolder[] run() { synchronized (endpointListenerServiceTrackerLock) { return getMatchingEndpointListenerHolders( endpointListenerTracker .getServiceReferences(), description); } } }); } /** * @param description description * @param type type * @return EndpointEventListenerHolder[] matching endpoint event listener holders * @since 4.1 */ protected EndpointEventListenerHolder[] getMatchingEndpointEventListenerHolders( final EndpointDescription description, final int type) { return AccessController .doPrivileged(new PrivilegedAction<EndpointEventListenerHolder[]>() { public EndpointEventListenerHolder[] run() { synchronized (endpointEventListenerServiceTrackerLock) { return getMatchingEndpointEventListenerHolders( endpointEventListenerTracker .getServiceReferences(), description, type); } } }); } /** * @since 4.1 */ public class EndpointEventListenerHolder { private EndpointEventListener listener; private EndpointDescription description; private String matchingFilter; private int type; public EndpointEventListenerHolder(EndpointEventListener l, EndpointDescription d, String f, int t) { this.listener = l; this.description = d; this.matchingFilter = f; this.type = t; } public EndpointEventListener getListener() { return listener; } public EndpointDescription getDescription() { return description; } public String getMatchingFilter() { return matchingFilter; } public int getType() { return type; } } public class EndpointListenerHolder { private EndpointListener listener; private EndpointDescription description; private String matchingFilter; public EndpointListenerHolder(EndpointListener l, EndpointDescription d, String f) { this.listener = l; this.description = d; this.matchingFilter = f; } public EndpointListener getListener() { return listener; } public EndpointDescription getDescription() { return description; } public String getMatchingFilter() { return matchingFilter; } } /** * @param refs service references * @param description description * @param type type * @return EndpointEventListenerHolder[] matching endpoint event listener holders * @since 4.1 */ public EndpointEventListenerHolder[] getMatchingEndpointEventListenerHolders( ServiceReference[] refs, EndpointDescription description, int type) { if (refs == null) return null; List results = new ArrayList(); for (int i = 0; i < refs.length; i++) { EndpointEventListener listener = (EndpointEventListener) context .getService(refs[i]); if (listener == null) continue; List<String> filters = PropertiesUtil.getStringPlusProperty( getMapFromProperties(refs[i]), EndpointEventListener.ENDPOINT_LISTENER_SCOPE); // Only proceed if there is a filter present if (filters.size() > 0) { String matchingFilter = isMatch(description, filters); if (matchingFilter != null) results.add(new EndpointEventListenerHolder(listener, description, matchingFilter, type)); } } return (EndpointEventListenerHolder[]) results .toArray(new EndpointEventListenerHolder[results.size()]); } public EndpointListenerHolder[] getMatchingEndpointListenerHolders( ServiceReference[] refs, EndpointDescription description) { if (refs == null) return null; List results = new ArrayList(); for (int i = 0; i < refs.length; i++) { EndpointListener listener = (EndpointListener) context .getService(refs[i]); if (listener == null) continue; List<String> filters = PropertiesUtil.getStringPlusProperty( getMapFromProperties(refs[i]), EndpointListener.ENDPOINT_LISTENER_SCOPE); if (filters.size() > 0) { String matchingFilter = isMatch(description, filters); if (matchingFilter != null) results.add(new EndpointListenerHolder(listener, description, matchingFilter)); } } return (EndpointListenerHolder[]) results .toArray(new EndpointListenerHolder[results.size()]); } private String isMatch(EndpointDescription description, List<String> filters) { for (String filter : filters) { if (filter == null || "".equals(filter))continue; //$NON-NLS-1$ try { if (description.matches(filter)) return filter; } catch (IllegalArgumentException e) { logError("isMatch", "invalid endpoint listener filter=" //$NON-NLS-1$ //$NON-NLS-2$ + filters, e); } } return null; } private Map getMapFromProperties(ServiceReference ref) { Map<String, Object> results = new TreeMap<String, Object>( String.CASE_INSENSITIVE_ORDER); String[] keys = ref.getPropertyKeys(); if (keys != null) { for (int i = 0; i < keys.length; i++) { results.put(keys[i], ref.getProperty(keys[i])); } } return results; } class EndpointDescriptionBundleTrackerCustomizer implements BundleTrackerCustomizer { private static final String REMOTESERVICE_MANIFESTHEADER = "Remote-Service"; //$NON-NLS-1$ private static final String XML_FILE_PATTERN = "*.xml"; //$NON-NLS-1$ private Map<Long, Collection<org.osgi.service.remoteserviceadmin.EndpointDescription>> bundleDescriptionMap = Collections .synchronizedMap(new HashMap<Long, Collection<org.osgi.service.remoteserviceadmin.EndpointDescription>>()); private Object endpointDescriptionReaderTrackerLock = new Object(); private ServiceTracker endpointDescriptionReaderTracker; private IEndpointDescriptionReader getEndpointDescriptionReader() { synchronized (endpointDescriptionReaderTrackerLock) { if (endpointDescriptionReaderTracker == null) { endpointDescriptionReaderTracker = new ServiceTracker( context, IEndpointDescriptionReader.class.getName(), null); endpointDescriptionReaderTracker.open(); } } return (IEndpointDescriptionReader) endpointDescriptionReaderTracker .getService(); } public Object addingBundle(Bundle bundle, BundleEvent event) { handleAddingBundle(bundle); return bundle; } private void handleAddingBundle(Bundle bundle) { if (context == null) return; String remoteServicesHeaderValue = (String) bundle.getHeaders() .get(REMOTESERVICE_MANIFESTHEADER); if (remoteServicesHeaderValue != null) { // First parse into comma-separated values String[] paths = remoteServicesHeaderValue.split(","); //$NON-NLS-1$ if (paths != null) for (int i = 0; i < paths.length; i++) handleEndpointDescriptionPath(bundle, paths[i].trim()); } } private void handleEndpointDescriptionPath(Bundle bundle, String remoteServicesHeaderValue) { // if it's empty, ignore if ("".equals(remoteServicesHeaderValue)) //$NON-NLS-1$ return; Enumeration<URL> e = null; // if it endswith a '/', then scan for *.xml files if (remoteServicesHeaderValue.endsWith("/")) { //$NON-NLS-1$ e = bundle.findEntries(remoteServicesHeaderValue, XML_FILE_PATTERN, false); } else { // Break into path and filename/pattern int lastSlashIndex = remoteServicesHeaderValue.lastIndexOf('/'); if (lastSlashIndex == -1) { // no slash...might be a file name or pattern, assumed to be // at root of bundle e = bundle.findEntries( "/", remoteServicesHeaderValue, false); //$NON-NLS-1$ } else { String path = remoteServicesHeaderValue.substring(0, lastSlashIndex); if ("".equals(path)) { //$NON-NLS-1$ // path is empty so assume it's root path = "/"; //$NON-NLS-1$ } String filePattern = remoteServicesHeaderValue .substring(lastSlashIndex + 1); e = bundle.findEntries(path, filePattern, false); } } // Now process any found Collection<org.osgi.service.remoteserviceadmin.EndpointDescription> endpointDescriptions = new ArrayList<org.osgi.service.remoteserviceadmin.EndpointDescription>(); if (e != null) { while (e.hasMoreElements()) { org.osgi.service.remoteserviceadmin.EndpointDescription[] eps = handleEndpointDescriptionFile( bundle, e.nextElement()); if (eps != null) for (int i = 0; i < eps.length; i++) endpointDescriptions.add(eps[i]); } } else { // logError logError( "handleEndpointDescriptionPath", "EDEF file(s) not found. The EDEF files given by Remote-Service header value='" + remoteServicesHeaderValue + "' in bundle='" + bundle.getSymbolicName() + "' cannot be found for remote services discovery", new FileNotFoundException("name=" + remoteServicesHeaderValue)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } // finally, handle them if (endpointDescriptions.size() > 0) { bundleDescriptionMap.put(new Long(bundle.getBundleId()), endpointDescriptions); for (org.osgi.service.remoteserviceadmin.EndpointDescription ed : endpointDescriptions) { addED(ed, null); handleEndpointDescription(ed, true); } } } private org.osgi.service.remoteserviceadmin.EndpointDescription[] handleEndpointDescriptionFile( Bundle bundle, URL fileURL) { InputStream ins = null; try { IEndpointDescriptionReader endpointDescriptionReader = getEndpointDescriptionReader(); if (endpointDescriptionReader == null) throw new NullPointerException( "No endpointDescriptionReader available for handleEndpointDescriptionFile fileURL=" //$NON-NLS-1$ + fileURL); ins = fileURL.openStream(); return endpointDescriptionReader.readEndpointDescriptions(ins); } catch (Throwable e) { logError("handleEndpointDescriptionFile", //$NON-NLS-1$ "Exception creating endpoint descriptions from fileURL=" //$NON-NLS-1$ + fileURL, e); return null; } finally { if (ins != null) try { ins.close(); } catch (IOException e) { logError("handleEndpointDescriptionFile", //$NON-NLS-1$ "Exception closing endpointDescription input fileURL=" //$NON-NLS-1$ + fileURL, e); } } } private void logError(String method, String message, Throwable t) { LogUtility.logError(method, DebugOptions.ENDPOINT_DESCRIPTION_LOCATOR, this.getClass(), new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, message, t)); } public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) { } public void removedBundle(Bundle bundle, BundleEvent event, Object object) { handleRemovedBundle(bundle); } private void handleRemovedBundle(Bundle bundle) { Collection<org.osgi.service.remoteserviceadmin.EndpointDescription> endpointDescriptions = bundleDescriptionMap .remove(new Long(bundle.getBundleId())); if (endpointDescriptions != null) for (org.osgi.service.remoteserviceadmin.EndpointDescription ed : endpointDescriptions) { removeED(ed); handleEndpointDescription(ed, false); } } public void close() { synchronized (endpointDescriptionReaderTrackerLock) { if (endpointDescriptionReaderTracker != null) { endpointDescriptionReaderTracker.close(); endpointDescriptionReaderTracker = null; } } bundleDescriptionMap.clear(); } } private Map<EndpointDescription, IServiceID> edToServiceIDMap = new HashMap<EndpointDescription, IServiceID>(); Set<EndpointDescription> getEDs() { synchronized (edToServiceIDMap) { return edToServiceIDMap.keySet(); } } EndpointDescription findED(IServiceID serviceID) { synchronized (edToServiceIDMap) { for (EndpointDescription ed : getEDs()) { IServiceID sid = edToServiceIDMap.get(ed); if (sid != null && sid.getLocation().equals(serviceID.getLocation())) return ed; } } return null; } void updateED(EndpointDescription existing, EndpointDescription update, IServiceID updateServiceID) { synchronized (edToServiceIDMap) { edToServiceIDMap.remove(existing); edToServiceIDMap.put(update, updateServiceID); } } void addED(org.osgi.service.remoteserviceadmin.EndpointDescription ed, IServiceID serviceID) { synchronized (edToServiceIDMap) { edToServiceIDMap.put(ed, serviceID); } } void removeED(org.osgi.service.remoteserviceadmin.EndpointDescription ed) { synchronized (edToServiceIDMap) { edToServiceIDMap.remove(ed); } } boolean containsED(EndpointDescription ed) { synchronized (edToServiceIDMap) { return getEDs().contains(ed); } } Set<EndpointDescription> getEDsForNamespace(Namespace namespace) { Set<EndpointDescription> results = new HashSet<EndpointDescription>(); synchronized (edToServiceIDMap) { for (EndpointDescription ed : edToServiceIDMap.keySet()) { IServiceID svcID = edToServiceIDMap.get(ed); if (svcID != null && svcID.getNamespace().getName() .equals(namespace.getName())) results.add(ed); } } return results; } /** * @since 4.3 */ public IServiceID getNetworkDiscoveredServiceID( org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription endpointDescription) { synchronized (edToServiceIDMap) { return edToServiceIDMap.get(endpointDescription); } } void handleEndpointDescription(EndpointDescription endpointDescription, boolean discovered) { if (discovered) { queueEndpointEvent(endpointDescription, EndpointEvent.ADDED); queueEndpointDescription(endpointDescription, discovered); } else { queueEndpointEvent(endpointDescription, EndpointEvent.REMOVED); queueEndpointDescription(endpointDescription, discovered); } } class LocatorServiceListener implements IServiceListener { private IDiscoveryLocator locator; public LocatorServiceListener(IDiscoveryLocator locator) { this.locator = locator; if (locator != null) this.locator.addServiceListener(this); } Collection<EndpointDescription> getEndpointDescriptions() { return (this.locator == null) ? Collections.EMPTY_SET : getEDsForNamespace(this.locator.getServicesNamespace()); } public void serviceDiscovered(IServiceEvent anEvent) { handleService(anEvent.getServiceInfo(), true); } public void serviceUndiscovered(IServiceEvent anEvent) { handleService(anEvent.getServiceInfo(), false); } void handleService(IServiceInfo serviceInfo, boolean discovered) { if (locator == null) return; IServiceID serviceID = serviceInfo.getServiceID(); // Make sure this is an OSGi Remote Service if (Arrays.asList(serviceID.getServiceTypeID().getServices()) .contains(RemoteConstants.DISCOVERY_SERVICE_TYPE)) { trace("handleService", "fwk=" + getFrameworkUUID() + " serviceInfo=" + serviceInfo //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + ", discovered=" + discovered + ", locator=" + locator); //$NON-NLS-1$ //$NON-NLS-2$ synchronized (edToServiceIDMap) { // Try to find ED from ServiceID, whether discovered or // undiscovered org.osgi.service.remoteserviceadmin.EndpointDescription ed = findED(serviceID); if (discovered) { // The IServiceInfo was discovered/added if (ed == null) { // Deserialize EndpointDescription from service // properties DiscoveredEndpointDescription discoveredEndpointDescription = getDiscoveredEndpointDescription( serviceID, serviceInfo, true); // Make sure that the discoveredEndpointDescription // is non-null if (discoveredEndpointDescription != null) { ed = discoveredEndpointDescription .getEndpointDescription(); if (ed != null) { EndpointDescription prevEd = isEndpointDescriptionUpdate( ed, serviceID); if (prevEd == null) { if (!containsED(ed)) { addED(ed, serviceID); handleEndpointDescription(ed, true); } else trace("handleEndpointDescription", "endpointDescription previously discovered...ignoring"); //$NON-NLS-1$ //$NON-NLS-2$ } else { // It was a modify/update trace("handleEndpointDescription", "endpointDescription updated. prev=" + prevEd + ", update=" + ed); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ queueEndpointEvent(ed, EndpointEvent.MODIFIED); } } else trace("handleService", "EndpointDescription is null for serviceID=" + serviceID); //$NON-NLS-1$ //$NON-NLS-2$ } else trace("handleService", "DiscoveredEndpointDescription is null for serviceID=" + serviceID); //$NON-NLS-1$ //$NON-NLS-2$ } else trace("handleService", "Found previous EndpointDescription with same serviceID=" + serviceID + ". Ignoring"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } else { // It was undiscovered if (ed != null) { removeED(ed); handleEndpointDescription(ed, false); } else trace("handleService", "Did not find serviceInfo with serviceID=" + serviceID + ". Ignoring"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } } } EndpointDescription isEndpointDescriptionUpdate( EndpointDescription endpointDescription, IServiceID updateServiceID) { if (endpointDescription instanceof org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription) { org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription ed = (org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription) endpointDescription; Long receivedTS = ed.getTimestamp(); if (receivedTS != null) { String receivedId = ed.getId(); boolean update = false; org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription ped = null; for (EndpointDescription previousEndpoint : getEndpointDescriptions()) { if (previousEndpoint instanceof org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription) { ped = (org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription) previousEndpoint; // Test pedId against receivedId...we only care // about // matches if (!ped.getId().equals(receivedId)) continue; Long pedTS = ped.getTimestamp(); // Now, it's only an update if the received // timestamp is // after the // previous timestamp if (pedTS != null && pedTS.longValue() < receivedTS .longValue()) update = true; } } if (update) { updateED(ped, ed, updateServiceID); return ed; } } } else { Map<String, Object> edProperties = endpointDescription .getProperties(); Long receivedTS = PropertiesUtil .getOSGiEndpointModifiedValue(edProperties); if (receivedTS != null) { String receivedId = endpointDescription.getId(); boolean update = false; EndpointDescription ped = null; for (EndpointDescription previousEndpoint : getEndpointDescriptions()) { ped = previousEndpoint; // If the previously discovered endpoint id does not // equal // the receivedId, then we haven't found it if (!previousEndpoint.getId().equals(receivedId)) continue; // If we have found it, get the property value if // present Long pedTS = (Long) previousEndpoint.getProperties() .get(RemoteConstants.OSGI_ENDPOINT_MODIFIED); // If it wasn't there before then this is definitely an // update if (pedTS == null) update = true; else if (pedTS.longValue() == receivedTS.longValue()) return null; else if (pedTS == null || pedTS.longValue() < receivedTS.longValue()) update = true; } if (update) { updateED(ped, endpointDescription, updateServiceID); return endpointDescription; } } } return null; } private DiscoveredEndpointDescription getDiscoveredEndpointDescription( IServiceID serviceId, IServiceInfo serviceInfo, boolean discovered) { // Get IEndpointDescriptionFactory final String methodName = "getDiscoveredEndpointDescription"; //$NON-NLS-1$ IDiscoveredEndpointDescriptionFactory factory = getDiscoveredEndpointDescriptionFactory(); try { // Else get endpoint description factory to create // EndpointDescription // for given serviceID and serviceInfo return (discovered) ? factory .createDiscoveredEndpointDescription(locator, serviceInfo) : factory.removeDiscoveredEndpointDescription(locator, serviceId); } catch (Exception e) { logError( methodName, "Exception calling IEndpointDescriptionFactory." //$NON-NLS-1$ + ((discovered) ? "createDiscoveredEndpointDescription" //$NON-NLS-1$ : "getUndiscoveredEndpointDescription"), e); //$NON-NLS-1$ return null; } catch (NoClassDefFoundError e) { logError( methodName, "NoClassDefFoundError calling IEndpointDescriptionFactory." //$NON-NLS-1$ + ((discovered) ? "createDiscoveredEndpointDescription" //$NON-NLS-1$ : "getUndiscoveredEndpointDescription"), e); //$NON-NLS-1$ return null; } } public synchronized void close() { if (locator != null) { locator.removeServiceListener(this); locator = null; } } public boolean triggerDiscovery() { return false; } } /** * @since 4.3 */ public void discoverEndpoint( org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription endpointDescription) { addED(endpointDescription, null); queueEndpointEvent(endpointDescription, EndpointEvent.ADDED); } /** * @since 4.3 */ public void updateEndpoint( org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription endpointDescription) { updateED(endpointDescription, endpointDescription, null); queueEndpointEvent(endpointDescription, EndpointEvent.MODIFIED); } /** * @since 4.3 */ public void undiscoverEndpoint( org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription endpointDescription) { removeED(endpointDescription); queueEndpointEvent(endpointDescription, EndpointEvent.REMOVED); } /** * @since 4.3 */ public org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription[] getDiscoveredEndpoints() { List<org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription> results = new ArrayList<org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription>(); for (EndpointDescription ed : getEDs()) { if (ed instanceof org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription) results.add((org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription) ed); } return results .toArray(new org.eclipse.ecf.osgi.services.remoteserviceadmin.EndpointDescription[results .size()]); } }