/* * RemoteServiceStub.java * * Copyright (C) 2010 Leo Osvald <leo.osvald@gmail.com> * * This file is part of SGLJ. * * SGLJ is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SGLJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see <http://www.gnu.org/licenses/>. */ package org.sglj.service.rmi.client; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.sglj.service.rmi.BasicRemoteServiceErrors; import org.sglj.service.rmi.RemoteCallException; import org.sglj.service.rmi.RemoteCallResult; import org.sglj.service.rmi.RemoteService; /** * Base class for implementing stub services on the calling side (usually * client). Provides wrapper type-safe methods that return the method's * return type. If there is an exception on the executing side (usually server), * every method in the service will throw the {@link RemoteCallException}.<br> * Note that this abstract implementation, unlike * {@link SafeRemoteServiceStub}, does not check whether * the method can actually be called, so it is in a certain way "unsafe".<br> * Here is the usage example: * <pre> // common interface for the calling and executing side public interface ExampleService extends RemoteService { int calcSum(int a, int b) throws RemoteCallException; String sayHello(String a) throws RemoteCallException; } ... public class ExampleServiceStub extends RemoteServiceStub<ExampleService> implements ExampleService { public ExampleServiceStub(RemoteCallRequestSender requestSender) { super(requestSender); } @RemoteMethod(RemoteMethodSide.CALLER) @Override public int calcSum(int a, int b) throws RemoteCallException { return callSynchronously("calcSum", a, b); } @RemoteMethod(RemoteMethodSide.CALLER) @Override public String sayHello(String a) throws RemoteCallException { return callSynchronously("sayHello", a); } @Override public byte getId() { return ExampleService.ID; } } </pre> * * @author Leo Osvald * * @param <T> type of the service stub that will be implemented */ public abstract class RemoteServiceStub<T extends RemoteService> implements RemoteService { private final RemoteServiceCaller<T> caller; /** * Creates a stub which delegate calls to the corresponding * request sender in an unsafe way (without checking whether the * methods can actually be called on the server or whether they exist * at all). * * @param requestSender the request sender to which method calls * should be delegated */ public RemoteServiceStub(RemoteCallRequestSender requestSender) { caller = new Caller(requestSender); } protected RemoteServiceStub(RemoteServiceCaller<T> caller) { this.caller = caller; } protected <R> R callSynchronously(String methodName, Object... args) throws RemoteCallException { //we could call the callAsynchronously method here, but this is faster Future<R> returnValueFuture = new ReturnValueFuture<R>(caller .callServiceMethod(methodName, args)); try { return returnValueFuture.get(); } catch (Exception e) { e.printStackTrace(); if (e instanceof RemoteCallException) { throw (RemoteCallException) e; } throw new RemoteCallException(e, BasicRemoteServiceErrors.UNKNOWN_ERROR); } } protected <R> Future<R> callAsynchronously(String methodName, Object... args) { return new ReturnValueFuture<R>(caller .callServiceMethod(methodName, args)); } protected RemoteServiceCaller<T> getCaller() { return caller; } private final class Caller extends AbstractRemoteServiceCaller<T> { public Caller(RemoteCallRequestSender requestSender) { super(requestSender); } @SuppressWarnings("unchecked") @Override public T getService() { return (T) RemoteServiceStub.this; } } private static final class ReturnValueFuture<R> implements Future<R> { private final Future<RemoteCallResult> resultFuture; ReturnValueFuture(Future<RemoteCallResult> resultFuture) { this.resultFuture = resultFuture; } @Override public boolean cancel(boolean mayInterruptIfRunning) { return resultFuture.cancel(mayInterruptIfRunning); } @SuppressWarnings("unchecked") @Override public R get() throws InterruptedException, ExecutionException { try { RemoteCallResult res = resultFuture.get(); if (res.isError()) throw new RemoteCallException(res.getError().byteValue()); return (R) res.getReturnValue(); } catch (ExecutionException e) { e.printStackTrace(); throw new RemoteCallException(e, BasicRemoteServiceErrors.UNKNOWN_ERROR); } } @SuppressWarnings("unchecked") @Override public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { RemoteCallResult res = resultFuture.get(timeout, unit); if (res.isError()) throw new RemoteCallException(res.getError().byteValue()); return (R) res.getReturnValue(); } @Override public boolean isCancelled() { return resultFuture.isCancelled(); } @Override public boolean isDone() { return resultFuture.isDone(); } } }