blob: 5d85de4069e91151d9fada36480cdffb4bb80f39 [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.InvocationTargetException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ecf.core.util.ECFException;
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 implements IRemoteService {
// the ECF remote refImpl
RemoteServiceReferenceImpl refImpl;
// the service object.
private 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;
}
/**
* 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 {
final Class[] formalParams = new Class[call.getParameters().length];
for (int i = 0; i < formalParams.length; i++) {
formalParams[i] = call.getParameters()[i].getClass();
}
try {
return service.getClass().getMethod(call.getMethod(), formalParams).invoke(service, call.getParameters());
} catch (Throwable t) {
throw new ECFException(t);
}
}
/**
* 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 service;
}
/**
* 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;
}
/**
* clear the effects of the last remote call.
*
* @see org.eclipse.ecf.core.util.IAsyncResult#clear()
*/
public synchronized void clear() {
result = null;
exception = null;
}
/**
* get the result, block until available.
*
* @return the result.
*
* @see org.eclipse.ecf.core.util.IAsyncResult#get()
*/
public Object get() throws InterruptedException, InvocationTargetException {
return get(0);
}
/**
* get the result. If not yet available, wait at most <code>msecs</code>
* milliseconds.
*
* @param msecs
* the maximum amount of milliseconds to wait.
* @return the result.
* @see org.eclipse.ecf.core.util.IAsyncResult#get(long)
*/
public synchronized Object get(final long msecs) throws InterruptedException, InvocationTargetException {
if (exception != null) {
throw getException();
}
if (result != null) {
return result;
}
wait(msecs);
return result;
}
/**
* get the exception of the last call, if any happened.
*
* @return the exception, or <code>null</code>.
* @see org.eclipse.ecf.core.util.IAsyncResult#getException()
*/
public synchronized InvocationTargetException getException() {
return exception == null ? null : new InvocationTargetException(exception);
}
/**
* tests, if the result is ready and available.
*
* @return <code>true</code>, if the result is available.
* @see org.eclipse.ecf.core.util.IAsyncResult#isReady()
*/
public synchronized boolean isReady() {
return result != null || exception != null;
}
/**
* peek, if the result is already available.
*
* @return the result, or <code>null</code>, if not yet available.
* @see org.eclipse.ecf.core.util.IAsyncResult#peek()
*/
public synchronized Object peek() {
return result;
}
// 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;
}
});
}
}
}
}