/******************************************************************************* * Copyright (c) 2008 Jan S. Rellermeyer, 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: * Jan S. Rellermeyer - initial API and implementation ******************************************************************************/ package org.eclipse.ecf.internal.provider.r_osgi; import ch.ethz.iks.r_osgi.RemoteOSGiException; import java.lang.reflect.Method; import org.eclipse.core.runtime.*; import org.eclipse.ecf.core.util.ECFException; import org.eclipse.ecf.core.util.reflection.ClassUtil; import org.eclipse.ecf.remoteservice.*; import org.eclipse.ecf.remoteservice.events.IRemoteCallCompleteEvent; import org.eclipse.ecf.remoteservice.events.IRemoteCallStartEvent; import org.eclipse.equinox.concurrent.future.*; /** * The R-OSGi adapter implementation of the IRemoteService interface. * * @author Jan S. Rellermeyer, ETH Zurich */ final class RemoteServiceImpl extends AbstractRemoteService { // the ECF remote refImpl RemoteServiceReferenceImpl refImpl; // the service object. Object service; // the next free service ID. private long nextID; /** * constructor. * * @param service * the service (proxy) object. */ public RemoteServiceImpl(final RemoteServiceReferenceImpl refImpl, final Object service) { this.refImpl = refImpl; this.service = service; } protected IRemoteServiceID getRemoteServiceID() { return refImpl.getID(); } protected IRemoteServiceReference getRemoteServiceReference() { return refImpl; } protected String[] getInterfaceClassNames() { return refImpl.getR_OSGiServiceReference().getServiceInterfaces(); } /** * call the service asynchronously. * * @param call * the call object. * @param listener * the callback listener. * @see org.eclipse.ecf.remoteservice.IRemoteService#callAsync(org.eclipse.ecf.remoteservice.IRemoteCall, * org.eclipse.ecf.remoteservice.IRemoteCallListener) */ public void callAsync(final IRemoteCall call, final IRemoteCallListener listener) { new AsyncResult(call, listener).start(); } /** * call the service asynchronously. * * @param call * the call object. * @return the result proxy. * @see org.eclipse.ecf.remoteservice.IRemoteService#callAsync(org.eclipse.ecf.remoteservice.IRemoteCall) */ public IFuture callAsync(final IRemoteCall call) { return getAsyncExecutor().execute(new IProgressRunnable() { public Object run(IProgressMonitor monitor) throws Exception { return callSync(call); } }, null); } private IExecutor asyncExecutor; private IExecutor syncExecutor; private final Object executorLock = new Object(); private IExecutor getAsyncExecutor() { synchronized (executorLock) { if (asyncExecutor == null) { IExecutor executor = Activator.getDefault().getExecutor(false); asyncExecutor = (executor == null) ? new ThreadsExecutor() : executor; } return asyncExecutor; } } private IExecutor getSyncExecutor() { synchronized (executorLock) { if (syncExecutor == null) { IExecutor executor = Activator.getDefault().getExecutor(true); syncExecutor = (executor == null) ? new ImmediateExecutor() : executor; } return syncExecutor; } } /** * call the service synchronously. * * @param call * the call object. * @return the result or <code>null</code> * @see org.eclipse.ecf.remoteservice.IRemoteService#callSync(org.eclipse.ecf.remoteservice.IRemoteCall) */ public Object callSync(final IRemoteCall call) throws ECFException { Object[] ps = call.getParameters(); final Object[] parameters = (ps == null) ? EMPTY_ARGS : ps; final Class[] formalParams = new Class[parameters.length]; for (int i = 0; i < formalParams.length; i++) { formalParams[i] = call.getParameters()[i].getClass(); } IFuture future = getSyncExecutor().execute(new IProgressRunnable() { public Object run(IProgressMonitor monitor) throws Exception { final Method method = ClassUtil.getMethod(service.getClass(), call.getMethod(), formalParams); return method.invoke(service, parameters); } }, null); Object result = null; try { result = future.get(call.getTimeout()); } catch (OperationCanceledException e) { throw new ECFException("callSync cancelled", e); //$NON-NLS-1$ } catch (InterruptedException e) { // If thread interrupted, then just return null return null; } catch (TimeoutException e) { throw new ECFException("callSync timed out after " + Long.toString(call.getTimeout()) + "ms", new TimeoutException(call.getTimeout())); //$NON-NLS-1$ //$NON-NLS-2$ } IStatus status = future.getStatus(); if (!status.isOK()) throw new ECFException("Exception during callSync", status.getException()); //$NON-NLS-1$ return result; } /** * fire an asynchronous call without getting the result returned. * * @param call * the call object. * @see org.eclipse.ecf.remoteservice.IRemoteService#fireAsync(org.eclipse.ecf.remoteservice.IRemoteCall) */ public void fireAsync(final IRemoteCall call) throws ECFException { try { callAsync(call); } catch (RemoteOSGiException r) { throw new ECFException(r); } catch (Throwable t) { // do not rethrow } } /** * get the service proxy object. * * @return the service proxy object. * @see org.eclipse.ecf.remoteservice.IRemoteService#getProxy() */ public Object getProxy() throws ECFException { if (!refImpl.getR_OSGiServiceReference().isActive()) { throw new ECFException("Container currently not connected"); //$NON-NLS-1$ } return super.getProxy(); } /** * get the next call id. * * @return the next call id. */ synchronized long getNextID() { return nextID++; } /** * inner class implementing the asynchronous result object. This * implementation also provides the calling infrastructure. */ private class AsyncResult extends Thread { // the result of the call. Object result; // the exception, if any happened during the call. Throwable exception; // the remote call object. IRemoteCall call; // the callback listener, if provided. private IRemoteCallListener listener; // constructor AsyncResult(final IRemoteCall call, final IRemoteCallListener listener) { this.call = call; this.listener = listener; } // the call happens here. public void run() { Object r = null; Throwable e = null; final long reqID = getNextID(); if (listener != null) { listener.handleEvent(new IRemoteCallStartEvent() { public IRemoteCall getCall() { return call; } public IRemoteServiceReference getReference() { return refImpl; } public long getRequestId() { return reqID; } }); } try { r = callSync(call); } catch (Throwable t) { e = t; } synchronized (AsyncResult.this) { result = r; exception = e; AsyncResult.this.notify(); } if (listener != null) { listener.handleEvent(new IRemoteCallCompleteEvent() { public Throwable getException() { return exception; } public Object getResponse() { return result; } public boolean hadException() { return exception != null; } public long getRequestId() { return reqID; } }); } } } }