| /**************************************************************************** |
| * Copyright (c) 2009 Composent, Inc. and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * Contributors: |
| * Composent, Inc. - initial API and implementation |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *****************************************************************************/ |
| package org.eclipse.ecf.remoteservice.client; |
| |
| import java.io.NotSerializableException; |
| import java.util.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.ecf.core.AbstractContainer; |
| import org.eclipse.ecf.core.ContainerConnectException; |
| import org.eclipse.ecf.core.events.*; |
| import org.eclipse.ecf.core.identity.ID; |
| import org.eclipse.ecf.core.identity.Namespace; |
| import org.eclipse.ecf.core.jobs.JobsExecutor; |
| import org.eclipse.ecf.core.security.IConnectContext; |
| import org.eclipse.ecf.internal.remoteservice.Activator; |
| import org.eclipse.ecf.remoteservice.*; |
| import org.eclipse.ecf.remoteservice.client.AbstractClientService.UriRequest; |
| import org.eclipse.ecf.remoteservice.events.*; |
| import org.eclipse.ecf.remoteservice.util.RemoteFilterImpl; |
| import org.eclipse.equinox.concurrent.future.*; |
| import org.osgi.framework.InvalidSyntaxException; |
| |
| /** |
| * Remote service client abstract superclass. |
| * |
| * @since 4.0 |
| */ |
| @SuppressWarnings("unchecked") |
| public abstract class AbstractClientContainer extends AbstractContainer implements IRemoteServiceClientContainerAdapter { |
| |
| protected ID containerID; |
| // The ID we've been assigned to connect to |
| protected ID connectedID; |
| protected Object connectLock = new Object(); |
| |
| protected IConnectContext connectContext; |
| |
| protected Object remoteResponseDeserializerLock = new Object(); |
| protected IRemoteResponseDeserializer remoteResponseDeserializer = null; |
| |
| protected Object parameterSerializerLock = new Object(); |
| protected IRemoteCallParameterSerializer parameterSerializer = null; |
| |
| protected RemoteServiceClientRegistry registry; |
| protected List remoteServiceListeners = new ArrayList(); |
| |
| private List referencesInUse = new ArrayList(); |
| |
| /** |
| * @since 8.12 |
| */ |
| protected IRemoteServiceFactory remoteServiceFactory; |
| |
| /** |
| * @since 8.12 |
| */ |
| public void setRemoteServiceFactory(IRemoteServiceFactory factory) { |
| this.remoteServiceFactory = factory; |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| protected boolean alwaysSendDefaultParameters; |
| |
| public AbstractClientContainer(ID containerID) { |
| this.containerID = containerID; |
| Assert.isNotNull(this.containerID); |
| this.registry = new RemoteServiceClientRegistry(this); |
| } |
| |
| public void setConnectContextForAuthentication(IConnectContext connectContext) { |
| this.connectContext = connectContext; |
| } |
| |
| public IConnectContext getConnectContextForAuthentication() { |
| return connectContext; |
| } |
| |
| public void setResponseDeserializer(IRemoteResponseDeserializer resource) { |
| synchronized (remoteResponseDeserializerLock) { |
| this.remoteResponseDeserializer = resource; |
| } |
| } |
| |
| public IRemoteResponseDeserializer getResponseDeserializer() { |
| synchronized (remoteResponseDeserializerLock) { |
| return this.remoteResponseDeserializer; |
| } |
| } |
| |
| public void setParameterSerializer(IRemoteCallParameterSerializer serializer) { |
| synchronized (parameterSerializerLock) { |
| this.parameterSerializer = serializer; |
| } |
| } |
| |
| protected IRemoteCallParameterSerializer getParameterSerializer() { |
| synchronized (parameterSerializerLock) { |
| return this.parameterSerializer; |
| } |
| } |
| |
| protected IRemoteResponseDeserializer getResponseDeserializer(IRemoteCall call, IRemoteCallable callable, Map responseHeaders) { |
| synchronized (remoteResponseDeserializerLock) { |
| return remoteResponseDeserializer; |
| } |
| } |
| |
| protected IRemoteCallParameterSerializer getParameterSerializer(IRemoteCallParameter parameter, Object value) { |
| synchronized (parameterSerializerLock) { |
| return parameterSerializer; |
| } |
| } |
| |
| /** |
| * Set the flag to <code>true</code> to include default parameters (which are specified when the callables are created) with |
| * every request to the remote service. |
| * <p> |
| * Setting to <code>false</code> will only send those parameter specified when the call is invoked. |
| * <p> |
| * Parameters which are specifed with the call override the defaults. Default parameters with a value of <code>null</code> |
| * are not included. |
| * |
| * @param alwaysSendDefaultParameters whether to send default parameters with every remote call |
| * @since 4.1 |
| */ |
| public void setAlwaysSendDefaultParameters(boolean alwaysSendDefaultParameters) { |
| this.alwaysSendDefaultParameters = alwaysSendDefaultParameters; |
| } |
| |
| public void addRemoteServiceListener(IRemoteServiceListener listener) { |
| synchronized (remoteServiceListeners) { |
| remoteServiceListeners.add(listener); |
| } |
| } |
| |
| public IFuture asyncGetRemoteServiceReferences(final ID[] idFilter, final String clazz, final String filter) { |
| IExecutor executor = new JobsExecutor("asyncGetRemoteServiceReferences"); //$NON-NLS-1$ |
| return executor.execute(new IProgressRunnable() { |
| public Object run(IProgressMonitor monitor) throws Exception { |
| return getRemoteServiceReferences(idFilter, clazz, filter); |
| } |
| }, null); |
| } |
| |
| public IFuture asyncGetRemoteServiceReferences(final ID target, final String clazz, final String filter) { |
| IExecutor executor = new JobsExecutor("asyncGetRemoteServiceReferences"); //$NON-NLS-1$ |
| return executor.execute(new IProgressRunnable() { |
| public Object run(IProgressMonitor monitor) throws Exception { |
| return getRemoteServiceReferences(target, clazz, filter); |
| } |
| }, null); |
| } |
| |
| /** |
| * @since 5.0 |
| */ |
| public IFuture asyncGetRemoteServiceReferences(final ID target, final ID[] idFilter, final String clazz, final String filter) { |
| IExecutor executor = new JobsExecutor("asyncGetRemoteServiceReferences"); //$NON-NLS-1$ |
| return executor.execute(new IProgressRunnable() { |
| public Object run(IProgressMonitor monitor) throws Exception { |
| return getRemoteServiceReferences(target, idFilter, clazz, filter); |
| } |
| }, null); |
| } |
| |
| public IRemoteFilter createRemoteFilter(String filter) throws InvalidSyntaxException { |
| return new RemoteFilterImpl(filter); |
| } |
| |
| public IRemoteServiceReference[] getAllRemoteServiceReferences(String clazz, String filter) throws InvalidSyntaxException { |
| return registry.getAllRemoteServiceReferences(clazz, (filter == null) ? null : createRemoteFilter(filter)); |
| } |
| |
| public IRemoteService getRemoteService(IRemoteServiceReference reference) { |
| if (reference == null || !(reference instanceof RemoteServiceClientReference)) |
| return null; |
| RemoteServiceClientRegistration registration = registry.findServiceRegistration((RemoteServiceClientReference) reference); |
| if (registration == null) |
| return null; |
| IRemoteService result = (registration == null) ? null : createRemoteService(registration); |
| if (result != null) |
| referencesInUse.add(reference); |
| return result; |
| } |
| |
| public IRemoteServiceID getRemoteServiceID(ID containerID1, long containerRelativeID) { |
| return registry.getRemoteServiceID(containerID1, containerRelativeID); |
| } |
| |
| public Namespace getRemoteServiceNamespace() { |
| return getConnectNamespace(); |
| } |
| |
| public IRemoteServiceReference getRemoteServiceReference(IRemoteServiceID serviceID) { |
| return registry.findServiceReference(serviceID); |
| } |
| |
| public IRemoteServiceReference[] getRemoteServiceReferences(ID[] idFilter, String clazz, String filter) throws InvalidSyntaxException { |
| return registry.getRemoteServiceReferences(idFilter, clazz, (filter == null) ? null : createRemoteFilter(filter)); |
| } |
| |
| public IRemoteServiceReference[] getRemoteServiceReferences(ID target, String clazz, String filter) throws InvalidSyntaxException, ContainerConnectException { |
| return registry.getRemoteServiceReferences(target, clazz, (filter == null) ? null : createRemoteFilter(filter)); |
| } |
| |
| /** |
| * @since 5.0 |
| */ |
| public IRemoteServiceReference[] getRemoteServiceReferences(ID target, ID[] idFilter, String clazz, String filter) throws InvalidSyntaxException, ContainerConnectException { |
| return registry.getRemoteServiceReferences(target, idFilter, clazz, (filter == null) ? null : createRemoteFilter(filter)); |
| } |
| |
| public IRemoteServiceRegistration registerRemoteService(final String[] clazzes, Object service, Dictionary properties) { |
| throw new RuntimeException("registerRemoteService cannot be used with client container"); //$NON-NLS-1$ |
| } |
| |
| public void removeRemoteServiceListener(IRemoteServiceListener listener) { |
| synchronized (remoteServiceListeners) { |
| remoteServiceListeners.remove(listener); |
| } |
| } |
| |
| public boolean ungetRemoteService(final IRemoteServiceReference reference) { |
| boolean result = referencesInUse.contains(reference); |
| referencesInUse.remove(reference); |
| fireRemoteServiceEvent(new IRemoteServiceUnregisteredEvent() { |
| |
| public IRemoteServiceReference getReference() { |
| return reference; |
| } |
| |
| public ID getLocalContainerID() { |
| return getID(); |
| } |
| |
| public ID getContainerID() { |
| return getID(); |
| } |
| |
| public String[] getClazzes() { |
| return registry.getClazzes(reference); |
| } |
| }); |
| return result; |
| } |
| |
| // Implementation of IRestClientContainerAdapter |
| public IRemoteServiceRegistration registerCallables(IRemoteCallable[] callables, Dictionary properties) { |
| Assert.isNotNull(callables); |
| final RemoteServiceClientRegistration registration = createRestServiceRegistration(callables, properties); |
| this.registry.registerRegistration(registration); |
| // notify |
| fireRemoteServiceEvent(new IRemoteServiceRegisteredEvent() { |
| |
| public IRemoteServiceReference getReference() { |
| return registration.getReference(); |
| } |
| |
| public ID getLocalContainerID() { |
| return registration.getContainerID(); |
| } |
| |
| public ID getContainerID() { |
| return getID(); |
| } |
| |
| public String[] getClazzes() { |
| return registration.getClazzes(); |
| } |
| }); |
| return registration; |
| } |
| |
| /** |
| * @param serviceType serviceType |
| * @param callables callables |
| * @param properties properties |
| * @return IRemoteServiceRegistration registration created for registration |
| * @since 8.5 |
| */ |
| public IRemoteServiceRegistration registerCallables(Class<?> serviceType, IRemoteCallable[] callables, Dictionary properties) { |
| return registerCallables(new String[] {serviceType.getName()}, new IRemoteCallable[][] {callables}, properties); |
| } |
| |
| public IRemoteServiceRegistration registerCallables(String[] clazzes, IRemoteCallable[][] callables, Dictionary properties) { |
| final RemoteServiceClientRegistration registration = createRestServiceRegistration(clazzes, callables, properties); |
| this.registry.registerRegistration(registration); |
| // notify |
| fireRemoteServiceEvent(new IRemoteServiceRegisteredEvent() { |
| |
| public IRemoteServiceReference getReference() { |
| return registration.getReference(); |
| } |
| |
| public ID getLocalContainerID() { |
| return registration.getContainerID(); |
| } |
| |
| public ID getContainerID() { |
| return getID(); |
| } |
| |
| public String[] getClazzes() { |
| return registration.getClazzes(); |
| } |
| }); |
| return registration; |
| } |
| |
| // IContainer implementation methods |
| public void connect(ID targetID, IConnectContext connectContext1) throws ContainerConnectException { |
| if (targetID == null) |
| throw new ContainerConnectException("targetID cannot be null"); //$NON-NLS-1$ |
| Namespace targetNamespace = targetID.getNamespace(); |
| Namespace connectNamespace = getConnectNamespace(); |
| if (connectNamespace == null) |
| throw new ContainerConnectException("targetID namespace cannot be null"); //$NON-NLS-1$ |
| if (!(targetNamespace.getName().equals(connectNamespace.getName()))) |
| throw new ContainerConnectException("targetID of incorrect type"); //$NON-NLS-1$ |
| fireContainerEvent(new ContainerConnectingEvent(containerID, targetID)); |
| synchronized (connectLock) { |
| if (connectedID == null) { |
| connectedID = targetID; |
| this.connectContext = connectContext1; |
| } else if (!connectedID.equals(targetID)) |
| throw new ContainerConnectException("Already connected to " + connectedID.getName()); //$NON-NLS-1$ |
| } |
| fireContainerEvent(new ContainerConnectedEvent(containerID, targetID)); |
| } |
| |
| public void disconnect() { |
| ID oldId = connectedID; |
| fireContainerEvent(new ContainerDisconnectingEvent(containerID, oldId)); |
| synchronized (connectLock) { |
| connectedID = null; |
| connectContext = null; |
| } |
| fireContainerEvent(new ContainerDisconnectedEvent(containerID, oldId)); |
| } |
| |
| public ID getConnectedID() { |
| synchronized (connectLock) { |
| return connectedID; |
| } |
| } |
| |
| public ID getID() { |
| return containerID; |
| } |
| |
| public void dispose() { |
| disconnect(); |
| synchronized (remoteServiceListeners) { |
| remoteServiceListeners.clear(); |
| } |
| super.dispose(); |
| } |
| |
| void fireRemoteServiceEvent(IRemoteServiceEvent event) { |
| List toNotify = null; |
| // Copy array |
| synchronized (remoteServiceListeners) { |
| toNotify = new ArrayList(remoteServiceListeners); |
| } |
| for (Iterator i = toNotify.iterator(); i.hasNext();) { |
| ((IRemoteServiceListener) i.next()).handleServiceEvent(event); |
| } |
| } |
| |
| protected RemoteServiceClientRegistration createRestServiceRegistration(String[] clazzes, IRemoteCallable[][] callables, Dictionary properties) { |
| return new RemoteServiceClientRegistration(getRemoteServiceNamespace(), clazzes, callables, properties, registry); |
| } |
| |
| protected RemoteServiceClientRegistration createRestServiceRegistration(IRemoteCallable[] callables, Dictionary properties) { |
| return new RemoteServiceClientRegistration(getRemoteServiceNamespace(), callables, properties, registry); |
| } |
| |
| protected void logException(String string, Throwable e) { |
| Activator a = Activator.getDefault(); |
| if (a != null) |
| a.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, string, e)); |
| } |
| |
| protected ID getRemoteCallTargetID() { |
| // First synchronize on connect lock |
| synchronized (connectLock) { |
| ID cID = getConnectedID(); |
| return (cID == null) ? getID() : cID; |
| } |
| } |
| |
| /** |
| * @param uri uri |
| * @param call remote call |
| * @param callable callable |
| * @return IRemoteCallParameter[] remote call parameters prepared |
| * @throws NotSerializableException if cannot be serialized |
| * @since 8.5 |
| */ |
| protected IRemoteCallParameter[] prepareCallParameters(String uri, IRemoteCall call, IRemoteCallable callable) throws NotSerializableException { |
| List<IRemoteCallParameter> results = new ArrayList<IRemoteCallParameter>(); |
| Object[] callParameters = call.getParameters(); |
| IRemoteCallParameter[] defaultCallableParameters = callable.getDefaultParameters(); |
| |
| if (callParameters != null) { |
| for (int i = 0; i < callParameters.length; i++) { |
| Object p = callParameters[i]; |
| // If the parameter is already a remote call parameter just add |
| if (p instanceof IRemoteCallParameter) { |
| results.add((IRemoteCallParameter) p); |
| continue; |
| } |
| if (defaultCallableParameters != null && i < defaultCallableParameters.length) { |
| // If the call parameter (p) is null, then add the associated |
| // callableParameter |
| if (p == null) |
| results.add(defaultCallableParameters[i]); |
| // If not null, then we need to serialize |
| IRemoteCallParameter val = serializeParameter(uri, call, callable, defaultCallableParameters[i], p); |
| if (val != null) |
| results.add(val); |
| } |
| } |
| } |
| return results.toArray(new IRemoteCallParameter[results.size()]); |
| } |
| |
| /** |
| * @param uri uri |
| * @param call call |
| * @param callable callable |
| * @return IRemoteCallParameter[] extra parameters |
| * @throws NotSerializableException if not serializable |
| * @since 8.5 |
| */ |
| protected IRemoteCallParameter[] prepareExtraParameters(String uri, IRemoteCall call, IRemoteCallable callable) throws NotSerializableException { |
| List<IRemoteCallParameter> results = new ArrayList<IRemoteCallParameter>(); |
| Object[] callParameters = call.getParameters(); |
| IRemoteCallParameter[] defaultCallableParameters = callable.getDefaultParameters(); |
| // Check if we should send _additional_ default parameters and whether there are more to send. |
| // This depends on the previous for loop and how many (default) parameters have been used by it. |
| if (alwaysSendDefaultParameters && (defaultCallableParameters.length > callParameters.length)) { |
| // Start with the first parameter that wasn't specified |
| for (int i = callParameters.length; i < defaultCallableParameters.length; i++) { |
| IRemoteCallParameter param = defaultCallableParameters[i]; |
| Object value = param.getValue(); |
| // skip default parameters with null values |
| if (value == null) |
| continue; |
| // else serialize the parameter using the container's parameterSerializer |
| results.add(serializeParameter(uri, call, callable, param, value)); |
| } |
| } |
| return results.toArray(new IRemoteCallParameter[results.size()]); |
| } |
| |
| protected IRemoteCallParameter[] prepareParameters(String uri, IRemoteCall call, IRemoteCallable callable) throws NotSerializableException { |
| List<IRemoteCallParameter> results = new ArrayList<IRemoteCallParameter>(); |
| IRemoteCallParameter[] preparedCallParameters = prepareCallParameters(uri, call, callable); |
| for (int i = 0; i < preparedCallParameters.length; i++) |
| results.add(preparedCallParameters[i]); |
| |
| IRemoteCallParameter[] preparedExtraParameters = prepareExtraParameters(uri, call, callable); |
| for (int i = 0; i < preparedExtraParameters.length; i++) |
| results.add(preparedExtraParameters[i]); |
| |
| return results.toArray(new IRemoteCallParameter[results.size()]); |
| } |
| |
| /** |
| * Serialize the parameter using the container's parameterSerializer. If there is no serializer for this container, return null. |
| * |
| * @param uri uri |
| * @param call call |
| * @param callable callable |
| * @param defaultParameter default parameter |
| * @param parameterValue parameter value |
| * @return IRemoteCallParameter the serialized parameter or null if there is no parameterSerializer for this container |
| * @throws NotSerializableException thrown if parameters cannot be serialized |
| * @see IRemoteCallParameterSerializer#serializeParameter(String, IRemoteCall, IRemoteCallable, IRemoteCallParameter, Object) |
| * @since 4.1 |
| */ |
| protected IRemoteCallParameter serializeParameter(String uri, IRemoteCall call, IRemoteCallable callable, IRemoteCallParameter defaultParameter, Object parameterValue) throws NotSerializableException { |
| // Get parameter serializer...and |
| IRemoteCallParameterSerializer serializer = getParameterSerializer(); |
| IRemoteCallParameter val = (serializer == null) ? null : serializer.serializeParameter(uri, call, callable, defaultParameter, parameterValue); |
| return val; |
| } |
| |
| /** |
| * Serialize the parameter using the container's parameterSerializer. If there is no serializer for this container, return null. |
| * |
| * @param uri uri |
| * @param call call |
| * @param callable callable |
| * @param currentParameters current parameters |
| * @param parameterValue parameter value |
| * @return IRemoteCallParameter[] parameters for given |
| * @throws NotSerializableException thrown if parameters cannot be serialized |
| * @since 8.0 |
| */ |
| protected IRemoteCallParameter[] serializeParameter(String uri, IRemoteCall call, IRemoteCallable callable, List currentParameters, Object[] parameterValue) throws NotSerializableException { |
| // Get parameter serializer...and |
| IRemoteCallParameterSerializer serializer = getParameterSerializer(); |
| IRemoteCallParameter[] current = (IRemoteCallParameter[]) currentParameters.toArray(new IRemoteCallParameter[currentParameters.size()]); |
| IRemoteCallParameter[] val = (serializer == null) ? current : serializer.serializeParameter(uri, call, callable, current, parameterValue); |
| return val; |
| } |
| |
| /** |
| * @param uri uri |
| * @param call call |
| * @param callable callable |
| * @param responseHeaders http response headers |
| * @param responseBody response body as byte[] |
| * @return Object response deserialized via response deserializer |
| * @throws NotSerializableException if response cannot be deserialized for processing |
| * @since 8.0 |
| */ |
| protected Object processResponse(String uri, IRemoteCall call, IRemoteCallable callable, Map responseHeaders, byte[] responseBody) throws NotSerializableException { |
| IRemoteResponseDeserializer deserializer = getResponseDeserializer(); |
| return (deserializer == null) ? null : deserializer.deserializeResponse(uri, call, callable, responseHeaders, responseBody); |
| } |
| |
| /** |
| * Create a remote service for a given remote service registration. This method will be |
| * called as part of the RemoteServiceAdmin.importService. |
| * |
| * @param registration the remote service client registration associated with the service |
| * being imported. Will not be <code>null</code>. |
| */ |
| protected IRemoteService createRemoteService(RemoteServiceClientRegistration registration) { |
| IRemoteServiceFactory f = this.remoteServiceFactory; |
| if (f == null) |
| throw new NullPointerException("Cannot create remote service as remoteServiceFactory is null"); //$NON-NLS-1$ |
| return f.createRemoteService(registration); |
| } |
| |
| /** |
| * Prepare an endpoint address for the given call and callable. |
| * |
| * @param call to create an endpoint for. Will not be <code>null</code>. |
| * @param callable to create an endpoing for. Will not be <code>null</code>. |
| * @return String that represents the endpoing for the given call and callable. May only return <code>null</code> if the |
| * given call should not be completed (i.e. there is no endpoint associated with the given call). |
| */ |
| protected abstract String prepareEndpointAddress(IRemoteCall call, IRemoteCallable callable); |
| |
| /** |
| * @param endpoint endpoint |
| * @param call call |
| * @param callable callable |
| * @return UriRequest to use for request. May be <code>null</code> |
| * @since 8.5 |
| */ |
| public UriRequest createUriRequest(String endpoint, IRemoteCall call, IRemoteCallable callable) { |
| return null; |
| } |
| |
| } |