blob: 0a5ab38d8fd45e9f90820809bc38274e589b7819 [file] [log] [blame]
/*******************************************************************************
* 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) {
final AbstractExecutor executor = new ThreadsExecutor();
return executor.execute(new IProgressRunnable() {
public Object run(IProgressMonitor monitor) throws Exception {
return callSync(call);
}
}, null);
}
/**
* 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();
}
IExecutor executor = new ThreadsExecutor();
IFuture future = executor.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;
}
});
}
}
}
}