/******************************************************************************* * Copyright (c) 2007 Versant Corp. * 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: * Markus Kuppe (mkuppe <at> versant <dot> com) - initial API and implementation ******************************************************************************/ package org.eclipse.ecf.provider.discovery; import java.net.URI; import java.util.*; import org.eclipse.core.runtime.Assert; import org.eclipse.ecf.core.ContainerConnectException; import org.eclipse.ecf.core.IContainer; import org.eclipse.ecf.core.events.*; import org.eclipse.ecf.core.identity.*; import org.eclipse.ecf.core.security.IConnectContext; import org.eclipse.ecf.core.util.ECFRuntimeException; import org.eclipse.ecf.core.util.Trace; import org.eclipse.ecf.discovery.*; import org.eclipse.ecf.discovery.identity.*; import org.eclipse.ecf.discovery.service.IDiscoveryService; import org.eclipse.ecf.internal.provider.discovery.Activator; import org.eclipse.ecf.internal.provider.discovery.CompositeNamespace; public class CompositeDiscoveryContainer extends AbstractDiscoveryContainerAdapter implements IDiscoveryService { public static final String NAME = "ecf.discovery.composite"; //$NON-NLS-1$ protected class CompositeContainerServiceListener implements IServiceListener { /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IServiceListener#serviceDiscovered(org.eclipse.ecf.discovery.IServiceEvent) */ public void serviceDiscovered(final IServiceEvent event) { final Collection col = getListeners(event.getServiceInfo().getServiceID().getServiceTypeID()); if (!col.isEmpty()) { for (final Iterator itr = col.iterator(); itr.hasNext();) { final IServiceListener isl = (IServiceListener) itr.next(); // we want to pretend the discovery event comes from us, thus we change the connectedId isl.serviceDiscovered(new CompositeServiceContainerEvent(event, getConnectedID())); Trace.trace(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "serviceDiscovered", //$NON-NLS-1$ "serviceResolved fired for listener " //$NON-NLS-1$ + isl.toString() + " with event: " + event.toString()); //$NON-NLS-1$ } } else { Trace.trace(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "serviceDiscovered", //$NON-NLS-1$ "serviceResolved fired without any listeners present"); //$NON-NLS-1$ } } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IServiceListener#serviceUndiscovered(org.eclipse.ecf.discovery.IServiceEvent) */ public void serviceUndiscovered(final IServiceEvent event) { final Collection col = getListeners(event.getServiceInfo().getServiceID().getServiceTypeID()); if (!col.isEmpty()) { for (final Iterator itr = col.iterator(); itr.hasNext();) { final IServiceListener isl = (IServiceListener) itr.next(); // we want to pretend the discovery event comes from us, thus we change the connectedId isl.serviceUndiscovered(new CompositeServiceContainerEvent(event, getConnectedID())); Trace.trace(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "serviceUndiscovered", //$NON-NLS-1$ "serviceRemoved fired for listener " //$NON-NLS-1$ + isl.toString() + " with event: " + event.toString()); //$NON-NLS-1$ } } else { Trace.trace(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "serviceUndiscovered", //$NON-NLS-1$ "serviceRemoved fired without any listeners present"); //$NON-NLS-1$ } } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IServiceListener#triggerDiscovery() */ public boolean triggerDiscovery() { return false; } } protected class CompositeContainerServiceTypeListener implements IServiceTypeListener { /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IServiceTypeListener#serviceTypeDiscovered(org.eclipse.ecf.discovery.IServiceEvent) */ public synchronized void serviceTypeDiscovered(final IServiceTypeEvent event) { // notify our listeners first so they get a chance to register for // the type before the underlying provider fires service added synchronized (serviceTypeListeners) { for (final Iterator itr = serviceTypeListeners.iterator(); itr.hasNext();) { final IServiceTypeListener listener = (IServiceTypeListener) itr.next(); // we want to pretend the discovery event comes from us, thus we change the connectedId listener.serviceTypeDiscovered(new CompositeServiceTypeContainerEvent(event, getConnectedID())); Trace.trace(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "serviceTypeDiscovered", //$NON-NLS-1$ "serviceTypeDiscovered fired for listener " //$NON-NLS-1$ + listener.toString() + " with event: " + event.toString()); //$NON-NLS-1$ } } // add ourself as a listener to the underlying providers. This might // trigger a serviceAdded alread final IServiceTypeID istid = event.getServiceTypeID(); synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { // TODO ccstl doesn't have to be a listener for a non // matching (namespace) container, but it doesn't hurt // either final IDiscoveryLocator idca = (IDiscoveryLocator) itr.next(); idca.addServiceListener(istid, ccsl); } } } } protected static final String METHODS_CATCHING = Activator.PLUGIN_ID + "/debug/methods/catching"; //$NON-NLS-1$ protected static final String METHODS_TRACING = Activator.PLUGIN_ID + "/debug/methods/tracing"; //$NON-NLS-1$ protected final CompositeContainerServiceListener ccsl = new CompositeContainerServiceListener(); protected final CompositeContainerServiceTypeListener ccstl = new CompositeContainerServiceTypeListener(); /** * History of services registered with this IDCA * * Used on newly added IDCAs */ protected Set registeredServices; protected final Collection containers; private ID targetID; /** * @param containers */ public CompositeDiscoveryContainer(final Collection containers) { super(CompositeNamespace.NAME, new DiscoveryContainerConfig(IDFactory.getDefault().createStringID(CompositeDiscoveryContainer.class.getName()))); this.containers = containers; this.registeredServices = new HashSet(); } /* (non-Javadoc) * @see org.eclipse.ecf.core.IContainer#connect(org.eclipse.ecf.core.identity.ID, org.eclipse.ecf.core.security.IConnectContext) */ public void connect(final ID aTargetID, final IConnectContext connectContext) throws ContainerConnectException { if (targetID != null || getConfig() == null) { throw new ContainerConnectException("Already connected"); //$NON-NLS-1$ } targetID = (aTargetID == null) ? getConfig().getID() : aTargetID; fireContainerEvent(new ContainerConnectingEvent(this.getID(), targetID, connectContext)); synchronized (containers) { final Collection containersFailedToConnect = new HashSet(); for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IContainer container = (IContainer) itr.next(); if (container.getConnectedID() == null) { try { container.connect(targetID, connectContext); } catch (ContainerConnectException cce) { Trace.catching(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "connect", //$NON-NLS-1$ cce); containersFailedToConnect.add(container); continue; } } final IDiscoveryLocator idca = (IDiscoveryLocator) container; idca.addServiceListener(ccsl); idca.addServiceTypeListener(ccstl); } // remove all containers that failed to connect and thus are unusable subsequently containers.removeAll(containersFailedToConnect); } fireContainerEvent(new ContainerConnectedEvent(this.getID(), targetID)); } /* (non-Javadoc) * @see org.eclipse.ecf.core.IContainer#disconnect() */ public void disconnect() { fireContainerEvent(new ContainerDisconnectingEvent(this.getID(), getConnectedID())); targetID = null; synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IContainer container = (IContainer) itr.next(); container.disconnect(); } containers.clear(); } synchronized (registeredServices) { registeredServices.clear(); } synchronized (allServiceListeners) { allServiceListeners.clear(); } synchronized (serviceListeners) { serviceListeners.clear(); } synchronized (serviceTypeListeners) { serviceTypeListeners.clear(); } fireContainerEvent(new ContainerDisconnectedEvent(this.getID(), getConnectedID())); } public static class CompositeServiceInfoWrapper implements IServiceInfo { private final IServiceInfo anInfo; private final ID anId; public CompositeServiceInfoWrapper(IServiceInfo anInfo, ID anId) { this.anInfo = anInfo; this.anId = anId; } public ID getId() { return anId; } public URI getLocation() { return anInfo.getLocation(); } public IServiceID getServiceID() { return anInfo.getServiceID(); } public int getPriority() { return anInfo.getPriority(); } public Object getAdapter(Class adapter) { return anInfo.getAdapter(adapter); } public int getWeight() { return anInfo.getWeight(); } public long getTTL() { return anInfo.getTTL(); } public IServiceProperties getServiceProperties() { return anInfo.getServiceProperties(); } public String getServiceName() { return anInfo.getServiceName(); } } protected IServiceEvent getServiceEvent(IServiceInfo iServiceInfo, ID id) { final CompositeServiceInfoWrapper csi = (CompositeServiceInfoWrapper) iServiceInfo; return new CompositeServiceContainerEvent(iServiceInfo, id, csi.getId()); } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.AbstractDiscoveryContainerAdapter#dispose() */ public void dispose() { disconnect(); synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IContainer container = (IContainer) itr.next(); container.dispose(); } containers.clear(); } targetID = null; super.dispose(); } /* (non-Javadoc) * @see org.eclipse.ecf.core.IContainer#getConnectedID() */ public ID getConnectedID() { return targetID; } private IServiceID getServiceIDForDiscoveryContainer(final IServiceID service, final IDiscoveryLocator dca) { final Namespace connectNamespace = dca.getServicesNamespace(); if (!connectNamespace.equals(service.getNamespace())) { return (IServiceID) connectNamespace.createInstance(new Object[] {service.getServiceTypeID().getName(), service.getLocation()}); } return service; } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IDiscoveryContainerAdapter#getServiceInfo(org.eclipse.ecf.discovery.identity.IServiceID) */ public IServiceInfo getServiceInfo(final IServiceID aService) { Assert.isNotNull(aService); synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IDiscoveryLocator idca = (IDiscoveryLocator) itr.next(); final IServiceID isi = getServiceIDForDiscoveryContainer(aService, idca); final IServiceInfo service = idca.getServiceInfo(isi); if (service != null) { return service; } } } return null; } private IServiceInfo getServiceInfoForDiscoveryContainer(final IServiceInfo aSi, final IDiscoveryLocator idca) { final IServiceID serviceId = aSi.getServiceID(); final IServiceID sid = getServiceIDForDiscoveryContainer(serviceId, idca); final IServiceTypeID serviceTypeID = sid.getServiceTypeID(); return new ServiceInfo(serviceId.getLocation(), aSi.getServiceName(), serviceTypeID, aSi.getPriority(), aSi.getWeight(), aSi.getServiceProperties()); } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IDiscoveryContainerAdapter#getServices() */ public IServiceInfo[] getServices() { final Set set = new HashSet(); synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IDiscoveryLocator idca = (IDiscoveryLocator) itr.next(); final ID containerId = ((IContainer) idca).getID(); final IServiceInfo[] services = idca.getServices(); for (int i = 0; i < services.length; i++) { IServiceInfo iServiceInfo = services[i]; services[i] = new CompositeServiceInfoWrapper(iServiceInfo, containerId); } set.addAll(Arrays.asList(services)); } } return (IServiceInfo[]) set.toArray(new IServiceInfo[set.size()]); } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IDiscoveryContainerAdapter#getServices(org.eclipse.ecf.discovery.identity.IServiceTypeID) */ public IServiceInfo[] getServices(final IServiceTypeID type) { Assert.isNotNull(type); final Set set = new HashSet(); synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IDiscoveryLocator idca = (IDiscoveryLocator) itr.next(); final IServiceTypeID isti = getServiceTypeIDForDiscoveryContainer(type, idca); final IServiceInfo[] services = idca.getServices(isti); set.addAll(Arrays.asList(services)); } } return (IServiceInfo[]) set.toArray(new IServiceInfo[set.size()]); } private IServiceTypeID getServiceTypeIDForDiscoveryContainer(final IServiceTypeID type, final IDiscoveryLocator dca) { final Namespace connectNamespace = dca.getServicesNamespace(); if (!connectNamespace.equals(type.getNamespace())) { return ServiceIDFactory.getDefault().createServiceTypeID(connectNamespace, type); } return type; } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IDiscoveryContainerAdapter#getServiceTypes() */ public IServiceTypeID[] getServiceTypes() { final Set set = new HashSet(); synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IDiscoveryLocator idca = (IDiscoveryLocator) itr.next(); final IServiceTypeID[] services = idca.getServiceTypes(); set.addAll(Arrays.asList(services)); } } return (IServiceTypeID[]) set.toArray(new IServiceTypeID[set.size()]); } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IDiscoveryContainerAdapter#registerService(org.eclipse.ecf.discovery.IServiceInfo) */ public void registerService(final IServiceInfo serviceInfo) { Assert.isNotNull(serviceInfo); synchronized (registeredServices) { Assert.isTrue(registeredServices.add(serviceInfo)); } synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IDiscoveryAdvertiser dca = (IDiscoveryAdvertiser) itr.next(); final IServiceInfo isi = getServiceInfoForDiscoveryContainer(serviceInfo, (IDiscoveryLocator) dca); dca.registerService(isi); Trace.trace(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "registerService", "registeredService " //$NON-NLS-1$ //$NON-NLS-2$ + serviceInfo.toString()); } } } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.IDiscoveryContainerAdapter#unregisterService(org.eclipse.ecf.discovery.IServiceInfo) */ public void unregisterService(final IServiceInfo serviceInfo) { Assert.isNotNull(serviceInfo); synchronized (registeredServices) { // no assert as unregisterService might be called with an non-existing ISI registeredServices.remove(serviceInfo); } synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IDiscoveryAdvertiser idca = (IDiscoveryAdvertiser) itr.next(); final IServiceInfo isi = getServiceInfoForDiscoveryContainer(serviceInfo, (IDiscoveryLocator) idca); idca.unregisterService(isi); } } } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.AbstractDiscoveryContainerAdapter#unregisterAllServices() */ public void unregisterAllServices() { synchronized (registeredServices) { synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IDiscoveryAdvertiser idca = (IDiscoveryAdvertiser) itr.next(); for (Iterator itr2 = registeredServices.iterator(); itr2.hasNext();) { final IServiceInfo serviceInfo = (IServiceInfo) itr2.next(); final IServiceInfo isi = getServiceInfoForDiscoveryContainer(serviceInfo, (IDiscoveryLocator) idca); idca.unregisterService(isi); } } } } } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.AbstractDiscoveryContainerAdapter#purgeCache() */ public IServiceInfo[] purgeCache() { final Set set = new HashSet(); synchronized (containers) { for (final Iterator itr = containers.iterator(); itr.hasNext();) { final IDiscoveryLocator idca = (IDiscoveryLocator) itr.next(); final IServiceInfo[] services = idca.purgeCache(); set.addAll(Arrays.asList(services)); } } return (IServiceInfo[]) set.toArray(new IServiceInfo[set.size()]); } /** * @param object * @return true on success * @see java.util.List#add(java.lang.Object) */ public boolean addContainer(final Object object) { // connect the new container if necessary and register ourself as listeners IContainer iContainer = (IContainer) object; if (iContainer.getConnectedID() == null) { try { iContainer.connect(targetID, null); } catch (ContainerConnectException e) { // we eat the exception here Trace.catching(Activator.PLUGIN_ID, METHODS_CATCHING, this.getClass(), "addContainer(Object)", e); //$NON-NLS-1$ return false; } } final IDiscoveryLocator idca = (IDiscoveryLocator) object; idca.addServiceListener(ccsl); idca.addServiceTypeListener(ccstl); // register previously registered with the new IDS synchronized (registeredServices) { final IDiscoveryAdvertiser ida = (IDiscoveryAdvertiser) object; for (final Iterator itr = registeredServices.iterator(); itr.hasNext();) { final IServiceInfo serviceInfo = (IServiceInfo) itr.next(); try { ida.registerService(serviceInfo); } catch (final ECFRuntimeException e) { // we eat the exception here since the original registerService call is long done Trace.catching(Activator.PLUGIN_ID, METHODS_CATCHING, this.getClass(), "addContainer(Object)", e); //$NON-NLS-1$ } } } synchronized (containers) { Trace.trace(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "addContainer(Object)", "addContainer " //$NON-NLS-1$ //$NON-NLS-2$ + object.toString()); return containers.add(object); } } /** * @param object * @return true on success * @see java.util.List#remove(java.lang.Object) */ public boolean removeContainer(final Object object) { final IDiscoveryLocator idca = (IDiscoveryLocator) object; idca.removeServiceListener(ccsl); idca.removeServiceTypeListener(ccstl); synchronized (containers) { Trace.trace(Activator.PLUGIN_ID, METHODS_TRACING, this.getClass(), "removeContainer(Object)", "removeContainer " //$NON-NLS-1$ //$NON-NLS-2$ + object.toString()); return containers.remove(object); } } /** * @return The List of currently registered containers. * @since 2.1 */ public Collection getDiscoveryContainers() { return Collections.unmodifiableCollection(containers); } /* (non-Javadoc) * @see org.eclipse.ecf.discovery.AbstractDiscoveryContainerAdapter#getContainerName() */ public String getContainerName() { return NAME; } }