| /******************************************************************************* |
| * Copyright (c) 2018 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.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.ecf.core.ContainerConnectException; |
| import org.eclipse.ecf.core.ContainerTypeDescription; |
| import org.eclipse.ecf.core.IContainer; |
| import org.eclipse.ecf.core.identity.ID; |
| import org.eclipse.ecf.core.identity.IDCreateException; |
| import org.eclipse.ecf.core.identity.Namespace; |
| import org.eclipse.ecf.core.provider.ContainerInstantiatorUtils; |
| import org.eclipse.ecf.core.provider.ContainerIntentException; |
| import org.eclipse.ecf.core.security.IConnectContext; |
| import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.DebugOptions; |
| import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.IDUtil; |
| import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.LogUtility; |
| import org.eclipse.ecf.remoteservice.IRemoteServiceContainer; |
| import org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter; |
| import org.eclipse.ecf.remoteservice.RemoteServiceContainer; |
| import org.osgi.framework.ServiceReference; |
| |
| /** |
| * Abstract superclass for host container selectors...i.e. implementers of |
| * {@link IHostContainerSelector}. |
| * |
| */ |
| public abstract class AbstractHostContainerSelector extends |
| AbstractContainerSelector { |
| |
| private static final String REQUIRE_SERVER_PROP = "org.eclipse.ecf.osgi.service.remoteserviceadmin.hostcontainerselector.requireserver"; //$NON-NLS-1$ |
| private static final String EXCLUDED_DESCRIPTIONS_PROP = "org.eclipse.ecf.osgi.service.remoteserviceadmin.hostcontainerselector.excludeddescriptions"; //$NON-NLS-1$ |
| private boolean defaultRequireServer = new Boolean(System.getProperty(REQUIRE_SERVER_PROP,"true")); //$NON-NLS-1$ |
| private List<String> excludedDescriptions; |
| |
| protected String[] defaultConfigTypes; |
| |
| public AbstractHostContainerSelector(String[] defaultConfigTypes) { |
| this.defaultConfigTypes = defaultConfigTypes; |
| String propValue = System.getProperty(EXCLUDED_DESCRIPTIONS_PROP); |
| String[] excludedVals = (propValue==null)?new String[0]:propValue.trim().split(","); //$NON-NLS-1$ |
| this.excludedDescriptions = Arrays.asList(excludedVals); |
| } |
| |
| /** |
| * @param serviceReference service reference |
| * @param overridingProperties overriding properties |
| * @param serviceExportedInterfaces service exported interfaces to select for |
| * @param serviceExportedConfigs service exported configs to select for |
| * @param serviceIntents service exported intents to select for |
| * @return Collection of existing host containers |
| * @since 2.0 |
| */ |
| protected Collection selectExistingHostContainers( |
| ServiceReference serviceReference, |
| Map<String, Object> overridingProperties, |
| String[] serviceExportedInterfaces, |
| String[] serviceExportedConfigs, String[] serviceIntents) { |
| List results = new ArrayList(); |
| // Get all existing containers |
| IContainer[] containers = getContainers(); |
| // If nothing there, then return empty array |
| if (containers == null || containers.length == 0) |
| return results; |
| |
| for (int i = 0; i < containers.length; i++) { |
| ID cID = containers[i].getID(); |
| trace("selectExistingHostContainers","Considering existing container="+cID); //$NON-NLS-1$ //$NON-NLS-2$ |
| // Check to make sure it's a rs container adapter. If it's not go |
| // onto next one |
| IRemoteServiceContainerAdapter adapter = hasRemoteServiceContainerAdapter(containers[i]); |
| if (adapter == null) { |
| trace("selectExistingHostContainers","Existing container="+cID+" does not implement IRemoteServiceContainerAdapter"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| continue; |
| } |
| // Get container type description and intents |
| ContainerTypeDescription description = getContainerTypeDescription(containers[i]); |
| // If it has no description go onto next |
| if (description == null) { |
| trace("selectExistingHostContainers","Existing container="+cID+" does not have container type description"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| continue; |
| } |
| // http://bugs.eclipse.org/331532 |
| if (!description.isServer()) { |
| trace("selectExistingHostContainers","Existing container="+cID+" is not server"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| continue; |
| } |
| |
| if (matchExistingHostContainer(serviceReference, |
| overridingProperties, containers[i], adapter, description, |
| serviceExportedConfigs, serviceIntents)) { |
| trace("selectExistingHostContainers", "INCLUDING containerID=" //$NON-NLS-1$ //$NON-NLS-2$ |
| + containers[i].getID() |
| + " configs=" //$NON-NLS-1$ |
| + ((serviceExportedConfigs == null) ? "null" : Arrays //$NON-NLS-1$ |
| .asList(serviceExportedConfigs).toString()) |
| + " intents=" //$NON-NLS-1$ |
| + ((serviceIntents == null) ? "null" : Arrays.asList( //$NON-NLS-1$ |
| serviceIntents).toString())); |
| results.add(new RemoteServiceContainer(containers[i], adapter)); |
| } else { |
| trace("selectExistingHostContainers", "EXCLUDING containerID=" //$NON-NLS-1$ //$NON-NLS-2$ |
| + containers[i].getID() |
| + " configs=" //$NON-NLS-1$ |
| + ((serviceExportedConfigs == null) ? "null" : Arrays //$NON-NLS-1$ |
| .asList(serviceExportedConfigs).toString()) |
| + " intents=" //$NON-NLS-1$ |
| + ((serviceIntents == null) ? "null" : Arrays.asList( //$NON-NLS-1$ |
| serviceIntents).toString())); |
| } |
| } |
| return results; |
| } |
| |
| /** |
| * @param serviceReference serviceReference |
| * @param properties properties |
| * @param container container to match |
| * @return boolean true if match false otherwise |
| * @since 2.0 |
| */ |
| protected boolean matchHostContainerToConnectTarget( |
| ServiceReference serviceReference, Map<String, Object> properties, |
| IContainer container) { |
| String target = (String) properties |
| .get(RemoteConstants.ENDPOINT_CONNECTTARGET_ID); |
| if (target == null) |
| return true; |
| // If a targetID is specified, make sure it either matches what the |
| // container |
| // is already connected to, or that we connect an unconnected container |
| ID connectedID = container.getConnectedID(); |
| // If the container is not already connected to anything |
| // then we connect it to the given target |
| if (connectedID == null) { |
| // connect to the target and we have a match |
| try { |
| connectHostContainer(serviceReference, properties, container, |
| target); |
| } catch (Exception e) { |
| logException("doConnectContainer containerID=" //$NON-NLS-1$ |
| + container.getID() + " target=" + target, e); //$NON-NLS-1$ |
| return false; |
| } |
| return true; |
| } else { |
| ID targetID = createTargetID(container, target); |
| // We check here if the currently connectedID equals the target. |
| // If it does we have a match |
| if (connectedID.equals(targetID)) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @param serviceReference service reference |
| * @param properties properties |
| * @param container container |
| * @param adapter remote service container adapter |
| * @param description container type description |
| * @param requiredConfigTypes required config types |
| * @param requiredServiceIntents required service intents |
| * @return boolean true if match, false otherwise |
| * @since 2.0 |
| */ |
| protected boolean matchExistingHostContainer( |
| ServiceReference serviceReference, Map<String, Object> properties, |
| IContainer container, IRemoteServiceContainerAdapter adapter, |
| ContainerTypeDescription description, String[] requiredConfigTypes, |
| String[] requiredServiceIntents) { |
| |
| return matchHostSupportedConfigTypes(requiredConfigTypes, description) |
| && matchHostSupportedIntents(requiredServiceIntents, |
| description, container) |
| && matchHostContainerID(serviceReference, properties, container) |
| && matchHostContainerToConnectTarget(serviceReference, |
| properties, container); |
| } |
| |
| /** |
| * @param serviceReference serviceReference |
| * @param properties properties |
| * @param container container |
| * @return boolean true if match, false otherwise |
| * @since 2.0 |
| */ |
| protected boolean matchHostContainerID(ServiceReference serviceReference, |
| Map<String, Object> properties, IContainer container) { |
| |
| ID containerID = container.getID(); |
| // No match if the container has no ID |
| if (containerID == null) |
| return false; |
| |
| // Then get containerid if specified directly by user in properties |
| ID requiredContainerID = (ID) properties |
| .get(RemoteConstants.SERVICE_EXPORTED_CONTAINER_ID); |
| // If the CONTAINER_I |
| if (requiredContainerID != null) { |
| return requiredContainerID.equals(containerID); |
| } |
| // Else get the container factory arguments, create an ID from the |
| // arguments |
| // and check if the ID matches that |
| Namespace ns = containerID.getNamespace(); |
| Object cid = properties |
| .get(RemoteConstants.SERVICE_EXPORTED_CONTAINER_FACTORY_ARGS); |
| // If no arguments are present, then any container ID should match |
| if (cid == null) |
| return true; |
| ID cID = null; |
| if (cid instanceof ID) { |
| cID = (ID) cid; |
| } else if (cid instanceof String) { |
| cID = IDUtil.createID(ns, (String) cid); |
| } else if (cid instanceof Object[]) { |
| Object cido = ((Object[]) cid)[0]; |
| cID = IDUtil.createID(ns, new Object[] { cido }); |
| } |
| if (cID == null) |
| return true; |
| return containerID.equals(cID); |
| } |
| |
| /** |
| * @param requiredConfigTypes request config types |
| * @param containerTypeDescription container type description |
| * @return boolean true if match, false otherwise |
| */ |
| protected boolean matchHostSupportedConfigTypes( |
| String[] requiredConfigTypes, |
| ContainerTypeDescription containerTypeDescription) { |
| // if no config type is set the spec requires to create a default |
| // endpoint (see section 122.5.1) |
| if (requiredConfigTypes == null) |
| return true; |
| trace("matchHostSupportedConfigTypes","description="+containerTypeDescription.getName()+" testing for requiredConfigTypes"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| // Get supported config types for this description |
| String[] supportedConfigTypes = getSupportedConfigTypes(containerTypeDescription); |
| // If it doesn't support anything, return false |
| if (supportedConfigTypes == null || supportedConfigTypes.length == 0) { |
| trace("matchHostSupportedConfigTypes","No supported configs found for description="+containerTypeDescription.getName()); //$NON-NLS-1$ //$NON-NLS-2$ |
| return false; |
| } |
| // Turn supported config types for this description into list |
| List supportedConfigTypesList = Arrays.asList(supportedConfigTypes); |
| List requiredConfigTypesList = Arrays.asList(requiredConfigTypes); |
| // We check all of the required config types and make sure |
| // that one or more of them are present in the supportedConfigTypes |
| boolean result = false; |
| for (Iterator i = requiredConfigTypesList.iterator(); i.hasNext();) |
| result |= supportedConfigTypesList.contains(i.next()); |
| if (!result) |
| trace("matchHostSupportedConfigTypes","description="+containerTypeDescription.getName()+" does not support all required config types"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return result; |
| } |
| |
| /** |
| * @param serviceReference service reference |
| * @param properties overriding properties |
| * @param serviceExportedInterfaces service exported interfaces to select for |
| * @param requiredConfigs service exported configs to select for |
| * @param requiredIntents intents to select for |
| * @return Collection of host containers |
| * @throws SelectContainerException if container cannot be created or configured |
| * @since 2.0 |
| */ |
| protected Collection createAndConfigureHostContainers( |
| ServiceReference serviceReference, Map<String, Object> properties, |
| String[] serviceExportedInterfaces, String[] requiredConfigs, |
| String[] serviceIntents) throws SelectContainerException { |
| |
| List results = new ArrayList(); |
| ContainerTypeDescription[] descriptions = getContainerTypeDescriptions(); |
| if (descriptions == null) |
| return Collections.EMPTY_LIST; |
| // Iterate through all descriptions and see if we have a match |
| for (int i = 0; i < descriptions.length; i++) { |
| trace("createAndConfigureHostContainers","Considering description="+descriptions[i]); //$NON-NLS-1$ //$NON-NLS-2$ |
| IRemoteServiceContainer matchingContainer = createMatchingContainer( |
| descriptions[i], serviceReference, properties, |
| serviceExportedInterfaces, requiredConfigs, |
| serviceIntents); |
| if (matchingContainer != null) |
| results.add(matchingContainer); |
| } |
| return results; |
| } |
| |
| protected ContainerTypeDescription[] getContainerTypeDescriptionsForDefaultConfigTypes( |
| ContainerTypeDescription[] descriptions) { |
| String[] defaultConfigTypes = getDefaultConfigTypes(); |
| if (defaultConfigTypes == null || defaultConfigTypes.length == 0) |
| return null; |
| List results = new ArrayList(); |
| for (int i = 0; i < descriptions.length; i++) { |
| // For each description, get supported config types |
| String[] supportedConfigTypes = descriptions[i] |
| .getSupportedConfigs(); |
| if (supportedConfigTypes != null |
| && matchDefaultConfigTypes(defaultConfigTypes, |
| supportedConfigTypes)) |
| results.add(descriptions[i]); |
| } |
| return (ContainerTypeDescription[]) results |
| .toArray(new ContainerTypeDescription[] {}); |
| } |
| |
| protected boolean matchDefaultConfigTypes(String[] defaultConfigTypes, |
| String[] supportedConfigTypes) { |
| List supportedConfigTypesList = Arrays.asList(supportedConfigTypes); |
| for (int i = 0; i < defaultConfigTypes.length; i++) { |
| if (supportedConfigTypesList.contains(defaultConfigTypes[i])) |
| return true; |
| } |
| return false; |
| } |
| |
| protected String[] getDefaultConfigTypes() { |
| return defaultConfigTypes; |
| } |
| |
| /** |
| * @since 4.6 |
| */ |
| protected boolean matchRequireServer(ContainerTypeDescription description) { |
| boolean result = this.defaultRequireServer && description.isServer(); |
| LogUtility.trace("matchRequireServer", DebugOptions.CONTAINER_SELECTOR, this.getClass(), "description="+description.getName()+((result)?" matched require server":" DID NOT match require server")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| return result; |
| } |
| |
| /** |
| * @since 4.6 |
| */ |
| protected boolean matchNotExcluded(ContainerTypeDescription description) { |
| boolean result = this.excludedDescriptions.contains(description.getName()); |
| LogUtility.trace("matchNotExcluded", DebugOptions.CONTAINER_SELECTOR, this.getClass(), "description="+description.getName()+((result)?" EXCLUDED via excludedDescriptions="+this.excludedDescriptions:" not excluded via excludedDescriptions="+this.excludedDescriptions)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| return !result; |
| } |
| /** |
| * @param containerTypeDescription containerTypeDescription |
| * @param serviceReference reference |
| * @param properties properties |
| * @param serviceExportedInterfaces exported interfaces |
| * @param requiredConfigs configs |
| * @param requiredIntents intents |
| * @return IRemoteServiceContainer matching container created |
| * @throws SelectContainerException container cannot be created or selected |
| * @since 2.0 |
| */ |
| protected IRemoteServiceContainer createMatchingContainer( |
| ContainerTypeDescription containerTypeDescription, |
| ServiceReference serviceReference, Map<String, Object> properties, |
| String[] serviceExportedInterfaces, String[] requiredConfigs, |
| String[] serviceIntents) throws SelectContainerException { |
| |
| if (matchRequireServer(containerTypeDescription) && matchNotExcluded(containerTypeDescription) |
| && matchHostSupportedConfigTypes(requiredConfigs, containerTypeDescription) |
| && matchHostSupportedIntents(serviceIntents, containerTypeDescription)) { |
| return createRSContainer(serviceReference, properties, containerTypeDescription, serviceIntents); |
| } |
| return null; |
| } |
| |
| /** |
| * @param serviceReference serviceReference |
| * @param properties properties |
| * @param containerTypeDescription container type description |
| * @return IRemoteServiceContainer created remote service container |
| * @throws SelectContainerException if could not be created |
| * @since 2.0 |
| */ |
| protected IRemoteServiceContainer createRSContainer( |
| ServiceReference serviceReference, Map<String, Object> properties, |
| ContainerTypeDescription containerTypeDescription) |
| throws SelectContainerException { |
| return createRSContainer(serviceReference, properties, containerTypeDescription, null); |
| } |
| |
| /** |
| * @param serviceReference serviceReference |
| * @param properties properties |
| * @param containerTypeDescription container type description |
| * @return IRemoteServiceContainer created remote service container |
| * @throws SelectContainerException if could not be created |
| * @since 4.6 |
| */ |
| protected IRemoteServiceContainer createRSContainer( |
| ServiceReference serviceReference, Map<String, Object> properties, |
| ContainerTypeDescription containerTypeDescription, String[] intents) |
| throws SelectContainerException { |
| trace("createRSContainer", //$NON-NLS-1$ |
| "Creating container instance for ref=" + serviceReference + ";properties=" + properties //$NON-NLS-1$ //$NON-NLS-2$ |
| + ";description=" + containerTypeDescription.getName() + ";intents=" //$NON-NLS-1$ //$NON-NLS-2$ |
| + ((intents == null) ? "" : Arrays.asList(intents).toString())); //$NON-NLS-1$ |
| IContainer container = createContainer(serviceReference, properties, |
| containerTypeDescription, intents); |
| if (container == null) |
| return null; |
| IRemoteServiceContainerAdapter adapter = (IRemoteServiceContainerAdapter) container |
| .getAdapter(IRemoteServiceContainerAdapter.class); |
| if (adapter == null) { |
| LogUtility.logError("createRSContainer", DebugOptions.CONTAINER_SELECTOR, this.getClass(), "Container="+container.getID()+" does not implement IRemoteServiceContainerAdapter"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return null; |
| } |
| return new RemoteServiceContainer(container, adapter); |
| } |
| |
| |
| /** |
| * @param serviceReference service reference |
| * @param properties properties |
| * @param container container |
| * @param target target |
| * @throws ContainerConnectException if container cannot be connected |
| * @throws IDCreateException thrown if ID cannot be created |
| * @since 2.0 |
| */ |
| protected void connectHostContainer(ServiceReference serviceReference, |
| Map<String, Object> properties, IContainer container, Object target) |
| throws ContainerConnectException, IDCreateException { |
| ID targetID = (target instanceof String) ? IDUtil.createID( |
| container.getConnectNamespace(), (String) target) : IDUtil |
| .createID(container.getConnectNamespace(), |
| new Object[] { target }); |
| Object context = properties |
| .get(RemoteConstants.SERVICE_EXPORTED_CONTAINER_CONNECT_CONTEXT); |
| IConnectContext connectContext = null; |
| if (context != null) { |
| connectContext = createConnectContext(serviceReference, properties, |
| container, context); |
| } |
| // connect the container |
| container.connect(targetID, connectContext); |
| } |
| |
| protected boolean matchHostSupportedIntents( |
| String[] serviceRequiredIntents, |
| ContainerTypeDescription containerTypeDescription) { |
| return matchHostSupportedIntents(serviceRequiredIntents, containerTypeDescription, null); |
| } |
| |
| /** |
| * @since 4.6 |
| */ |
| protected boolean matchHostSupportedIntents( |
| String[] serviceRequiredIntents, |
| ContainerTypeDescription containerTypeDescription, IContainer container) { |
| // If there are no required intents then we have a match |
| if (serviceRequiredIntents == null) |
| return true; |
| |
| trace("matchHostSupportedIntents","description="+containerTypeDescription.getName()+" testing for serviceRequiredIntents="+Arrays.asList(serviceRequiredIntents)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| |
| String[] supportedIntents = getSupportedIntents(containerTypeDescription); |
| |
| if (supportedIntents == null) { |
| trace("matchHostSupportedIntents","description="+containerTypeDescription.getName()+" does not have any supported intents"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return false; |
| } |
| // checks to see that containerTypeDescription supports requiredIntents |
| boolean result = true; |
| for (int i = 0; i < serviceRequiredIntents.length; i++) { |
| boolean found = false; |
| for(String supportedIntent: supportedIntents) |
| if (serviceRequiredIntents[i].equals(supportedIntent) || serviceRequiredIntents[i].startsWith(supportedIntent+".")) {//$NON-NLS-1$ |
| found = true; |
| break; |
| } |
| result &= found; |
| } |
| |
| if (!result) { |
| trace("matchHostSupportedIntents","description="+containerTypeDescription.getName()+" does not have all required intents"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return false; |
| } |
| |
| // If container is non-null, |
| // check to see that it's ID is private. If it's not private, return null |
| if (container != null) { |
| try { |
| if (ContainerInstantiatorUtils.containsPrivateIntent(serviceRequiredIntents)) |
| ContainerInstantiatorUtils.checkPrivate(container.getID()); |
| } catch (ContainerIntentException e) { |
| trace("matchHostSupportedIntents","container="+container.getID()+" does not have osgi private intent"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return false; |
| } |
| } |
| |
| if (!result) { |
| trace("matchHostSupportedIntents","container="+container.getID()+" does not have all required intents"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| return result; |
| } |
| |
| } |