Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
hprose.client.HproseClient Maven / Gradle / Ivy
Go to download
Hprose is a High Performance Remote Object Service Engine.
It is a modern, lightweight, cross-language, cross-platform, object-oriented, high performance, remote dynamic communication middleware. It is not only easy to use, but powerful. You just need a little time to learn, then you can use it to easily construct cross language cross platform distributed application system.
Hprose supports many programming languages, for example:
* AAuto Quicker
* ActionScript
* ASP
* C++
* Dart
* Delphi/Free Pascal
* dotNET(C#, Visual Basic...)
* Golang
* Java
* JavaScript
* Node.js
* Objective-C
* Perl
* PHP
* Python
* Ruby
* ...
Through Hprose, You can conveniently and efficiently intercommunicate between those programming languages.
This project is the implementation of Hprose for Java.
/**********************************************************\
| |
| hprose |
| |
| Official WebSite: http://www.hprose.com/ |
| http://www.hprose.org/ |
| |
\**********************************************************/
/**********************************************************\
* *
* HproseClient.java *
* *
* hprose client class for Java. *
* *
* LastModified: Jun 2, 2016 *
* Author: Ma Bingyao *
* *
\**********************************************************/
package hprose.client;
import hprose.common.FilterHandler;
import hprose.common.FilterHandlerManager;
import hprose.common.HproseCallback;
import hprose.common.HproseCallback1;
import hprose.common.HproseContext;
import hprose.common.HproseErrorEvent;
import hprose.common.HproseException;
import hprose.common.HproseFilter;
import hprose.common.HproseInvocationHandler;
import hprose.common.HproseInvoker;
import hprose.common.HproseResultMode;
import hprose.common.InvokeHandler;
import hprose.common.InvokeSettings;
import hprose.common.NextFilterHandler;
import hprose.common.NextInvokeHandler;
import hprose.io.ByteBufferStream;
import hprose.io.HproseMode;
import static hprose.io.HproseTags.TagArgument;
import static hprose.io.HproseTags.TagCall;
import static hprose.io.HproseTags.TagEnd;
import static hprose.io.HproseTags.TagError;
import static hprose.io.HproseTags.TagResult;
import hprose.io.serialize.Writer;
import hprose.io.unserialize.Reader;
import hprose.net.ReceiveCallback;
import hprose.util.ClassUtil;
import hprose.util.StrUtil;
import hprose.util.concurrent.Action;
import hprose.util.concurrent.Func;
import hprose.util.concurrent.Promise;
import hprose.util.concurrent.Threads;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class HproseClient implements HproseInvoker {
protected final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private final static Object[] nullArgs = new Object[0];
private final ArrayList invokeHandlers = new ArrayList();
private final ArrayList beforeFilterHandlers = new ArrayList();
private final ArrayList afterFilterHandlers = new ArrayList();
private final ArrayList filters = new ArrayList();
private final ArrayList uris = new ArrayList();
private final AtomicInteger index = new AtomicInteger(-1);
private HproseMode mode;
private int timeout = 30000;
private int retry = 10;
private boolean idempontent = false;
private boolean failswitch = false;
private boolean byref = false;
private boolean simple = false;
private final NextInvokeHandler defaultInvokeHandler = new NextInvokeHandler() {
public Object handle(String name, Object[] args, HproseContext context) throws Throwable {
return invokeHandler(name, args, (ClientContext)context);
}
};
private final NextFilterHandler defaultBeforeFilterHandler = new NextFilterHandler() {
public Object handle(ByteBuffer request, HproseContext context) throws Throwable {
return beforeFilterHandler(request, (ClientContext)context);
}
};
private final NextFilterHandler defaultAfterFilterHandler = new NextFilterHandler() {
public Object handle(ByteBuffer request, HproseContext context) throws Throwable {
return afterFilterHandler(request, (ClientContext)context);
}
};
private NextInvokeHandler invokeHandler = defaultInvokeHandler;
private NextFilterHandler beforeFilterHandler = defaultBeforeFilterHandler;
private NextFilterHandler afterFilterHandler = defaultAfterFilterHandler;
protected String uri;
public HproseErrorEvent onError = null;
static {
Threads.registerShutdownHandler(new Runnable() {
public void run() {
if (!executor.isShutdown()) {
executor.shutdown();
}
}
});
}
protected HproseClient() {
this((String[])null, HproseMode.MemberMode);
}
protected HproseClient(HproseMode mode) {
this((String[])null, mode);
}
protected HproseClient(String uri) {
this(uri, HproseMode.MemberMode);
}
protected HproseClient(String uri, HproseMode mode) {
this(uri == null ? (String[])null : new String[] {uri}, mode);
}
protected HproseClient(String[] uris) {
this(uris, HproseMode.MemberMode);
}
protected HproseClient(String[] uris, HproseMode mode) {
this.mode = mode;
if (uris != null) {
useService(uris);
}
}
public void close() {}
private final static HashMap> clientFactories = new HashMap>();
public static void registerClientFactory(String scheme, Class extends HproseClient> clientClass) {
synchronized (clientFactories) {
clientFactories.put(scheme, clientClass);
}
}
static {
registerClientFactory("tcp", HproseTcpClient.class);
registerClientFactory("tcp4", HproseTcpClient.class);
registerClientFactory("tcp6", HproseTcpClient.class);
registerClientFactory("http", HproseHttpClient.class);
registerClientFactory("https", HproseHttpClient.class);
}
public static HproseClient create(String uri) throws IOException, URISyntaxException {
return create(new String[] { uri }, HproseMode.MemberMode);
}
public static HproseClient create(String uri, HproseMode mode) throws IOException, URISyntaxException {
return create(new String[] { uri }, mode);
}
public static HproseClient create(String[] uris, HproseMode mode) throws IOException, URISyntaxException {
String scheme = (new URI(uris[0])).getScheme().toLowerCase();
for (int i = 1, n = uris.length; i < n; ++i) {
if (!(new URI(uris[i])).getScheme().toLowerCase().equalsIgnoreCase(scheme)) {
throw new HproseException("Not support multiple protocol.");
}
}
Class extends HproseClient> clientClass = clientFactories.get(scheme);
if (clientClass != null) {
try {
HproseClient client = clientClass.newInstance();
client.mode = mode;
client.useService(uris);
return client;
}
catch (Exception ex) {
throw new HproseException("This client doesn't support " + scheme + " scheme.");
}
}
throw new HproseException("This client doesn't support " + scheme + " scheme.");
}
public final int getTimeout() {
return timeout;
}
public final void setTimeout(int timeout) {
if (timeout < 1) throw new IllegalArgumentException("timeout must be great than 0");
this.timeout = timeout;
}
public final int getRetry() {
return retry;
}
public final void setRetry(int retry) {
this.retry = retry;
}
public final boolean isIdempontent() {
return idempontent;
}
public final void setIdempontent(boolean idempontent) {
this.idempontent = idempontent;
}
public final boolean isFailswitch() {
return failswitch;
}
public final void setFailswitch(boolean failswitch) {
this.failswitch = failswitch;
}
public final boolean isByref() {
return byref;
}
public final void setByref(boolean byref) {
this.byref = byref;
}
public final boolean isSimple() {
return simple;
}
public final void setSimple(boolean simple) {
this.simple = simple;
}
public final HproseFilter getFilter() {
if (filters.isEmpty()) {
return null;
}
return filters.get(0);
}
public final void setFilter(HproseFilter filter) {
if (!filters.isEmpty()) {
filters.clear();
}
if (filter != null) {
filters.add(filter);
}
}
public final void addFilter(HproseFilter filter) {
if (filter != null) {
filters.add(filter);
}
}
public final boolean removeFilter(HproseFilter filter) {
return filters.remove(filter);
}
public final void useService(String uri) {
useService(new String[] { uri });
}
public final void useService(String[] uris) {
this.uris.clear();
int n = uris.length;
for (int i = 0; i < n; i++) {
this.uris.add(uris[i]);
}
if (n > 0) {
index.set((int)Math.floor(Math.random() * n));
this.uri = uris[index.get()];
}
}
public final T useService(Class type) {
return useService(type, null);
}
public final T useService(String uri, Class type) {
return useService(uri, type, null);
}
public final T useService(String[] uris, Class type) {
return useService(uris, type, null);
}
@SuppressWarnings("unchecked")
public final T useService(Class type, String ns) {
HproseInvocationHandler handler = new HproseInvocationHandler(this, ns);
if (type.isInterface()) {
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class>[]{type}, handler);
}
else {
return (T) Proxy.newProxyInstance(type.getClassLoader(), type.getInterfaces(), handler);
}
}
public final T useService(String uri, Class type, String ns) {
useService(uri);
return useService(type, ns);
}
public final T useService(String[] uris, Class type, String ns) {
useService(uris);
return useService(type, ns);
}
private ByteBuffer outputFilter(ByteBuffer request, ClientContext context) {
if (request.position() != 0) {
request.flip();
}
for (int i = 0, n = filters.size(); i < n; ++i) {
request = filters.get(i).outputFilter(request, context);
if (request.position() != 0) {
request.flip();
}
}
return request;
}
private ByteBuffer inputFilter(ByteBuffer response, ClientContext context) {
if (response.position() != 0) {
response.flip();
}
for (int i = filters.size() - 1; i >= 0; --i) {
response = filters.get(i).inputFilter(response, context);
if (response.position() != 0) {
response.flip();
}
}
return response;
}
@SuppressWarnings("unchecked")
private Object beforeFilterHandler(ByteBuffer request, final ClientContext context) throws Throwable {
request = outputFilter(request, context);
Object response = afterFilterHandler.handle(request, context);
if (response instanceof Promise) {
return ((Promise)response).then(new Func() {
public ByteBuffer call(ByteBuffer response) throws Throwable {
if (context.getSettings().isOneway()) return null;
response = inputFilter(response, context);
return response;
}
});
}
else if (response instanceof Buffer) {
if (context.getSettings().isOneway()) return null;
response = inputFilter((ByteBuffer) response, context);
return response;
}
else {
throw new HproseException("Wrong return type of afterFilterHander");
}
}
private Object afterFilterHandler(ByteBuffer request, ClientContext context) throws Throwable {
if (context.getSettings().isAsync()) {
final Promise response = new Promise();
sendAndReceive(request, new ReceiveCallback() {
public void handler(ByteBuffer stream, Throwable e) {
if (e != null) {
response.reject(e);
}
else {
response.resolve(stream);
}
}
}, context.getSettings().getTimeout());
return response;
}
return sendAndReceive(request, context.getSettings().getTimeout());
}
@SuppressWarnings("unchecked")
private Object sendAndReceive(final ByteBuffer request, final ClientContext context) throws Throwable {
try {
Object response = beforeFilterHandler.handle(request, context);
if (response instanceof Promise) {
return ((Promise) response).catchError(
new Func() {
public Object call(Throwable e) throws Throwable {
Object response = retry(request, context);
if (response != null) {
return response;
}
throw e;
}
}
);
}
else if (response instanceof Buffer) {
return response;
}
else {
throw new HproseException("Wrong return type of beforeFilterHander");
}
}
catch (IOException e) {
Object response = retry(request, context);
if (response != null) return response;
throw e;
}
}
private Object retry(final ByteBuffer request, final ClientContext context) throws Throwable {
InvokeSettings settings = context.getSettings();
if (settings.isFailswitch()) {
failswitch();
}
if (settings.isIdempotent()) {
int n = settings.getRetry();
if (n > 0) {
settings.setRetry(n - 1);
int interval = (n >= 10) ? 500 : (10 - n) * 500;
if (settings.isAsync()) {
return Promise.delayed(interval, new Callable() {
public Object call() throws Exception {
try {
return sendAndReceive(request, context);
}
catch (Throwable e) {
throw new HproseException(e);
}
}
});
}
else {
try {
Thread.sleep(interval);
}
catch (InterruptedException ex) {
return null;
}
return sendAndReceive(request, context);
}
}
}
return null;
}
private void failswitch() {
int i = index.get() + 1;
if (i >= uris.size()) {
index.set(i = 0);
}
index.set(i);
uri = uris.get(i);
}
private ClientContext getContext(InvokeSettings settings) {
ClientContext context = new ClientContext(this);
context.getSettings().copyFrom(settings);
return context;
}
private ByteBufferStream encode(String name, Object[] args, ClientContext context) throws IOException {
ByteBufferStream stream = new ByteBufferStream();
InvokeSettings settings = context.getSettings();
Writer writer = new Writer(stream.getOutputStream(), mode, settings.isSimple());
stream.write(TagCall);
writer.writeString(name);
if ((args != null) && (args.length > 0 || settings.isByref())) {
writer.reset();
writer.writeArray(args);
if (settings.isByref()) {
writer.writeBoolean(true);
}
}
stream.write(TagEnd);
return stream;
}
private Object getRaw(ByteBufferStream stream, Type returnType) throws HproseException {
stream.flip();
if (returnType == null ||
returnType == Object.class ||
returnType == ByteBuffer.class ||
returnType == Buffer.class) {
return stream.buffer;
}
else if (returnType == ByteBufferStream.class) {
return stream;
}
else if (returnType == byte[].class) {
byte[] bytes = stream.toArray();
stream.close();
return bytes;
}
throw new HproseException("Can't Convert ByteBuffer to Type: " + returnType.toString());
}
private Object decode(ByteBufferStream stream, Object[] args, ClientContext context) throws IOException, HproseException {
InvokeSettings settings = context.getSettings();
if (settings.isOneway()) {
return null;
}
if (stream.available() == 0) throw new HproseException("EOF");
int tag = stream.buffer.get(stream.buffer.limit() - 1);
if (tag != TagEnd) {
throw new HproseException("Wrong Response: \r\n" + StrUtil.toString(stream));
}
HproseResultMode resultMode = settings.getMode();
Type returnType = settings.getReturnType();
if (resultMode == HproseResultMode.RawWithEndTag) {
return getRaw(stream, returnType);
}
else if (resultMode == HproseResultMode.Raw) {
stream.buffer.limit(stream.buffer.limit() - 1);
return getRaw(stream, returnType);
}
Object result = null;
Reader reader = new Reader(stream.getInputStream(), mode);
tag = stream.read();
if (tag == TagResult) {
if (resultMode == HproseResultMode.Normal) {
result = reader.unserialize(returnType);
}
else if (resultMode == HproseResultMode.Serialized) {
result = getRaw(reader.readRaw(), returnType);
}
tag = stream.read();
if (tag == TagArgument) {
reader.reset();
Object[] arguments = reader.readObjectArray();
int length = args.length;
if (length > arguments.length) {
length = arguments.length;
}
System.arraycopy(arguments, 0, args, 0, length);
tag = stream.read();
}
}
else if (tag == TagError) {
throw new HproseException(reader.readString());
}
if (tag != TagEnd) {
stream.rewind();
throw new HproseException("Wrong Response: \r\n" + StrUtil.toString(stream));
}
return result;
}
@SuppressWarnings("unchecked")
private Object invokeHandler(String name, final Object[] args, final ClientContext context) throws Throwable {
final ByteBufferStream stream = encode(name, args, context);
Object buffer = sendAndReceive(stream.buffer, context);
if (buffer instanceof Promise) {
return ((Promise)buffer).then(new Func() {
public Object call(ByteBuffer value) throws Throwable {
stream.buffer = value;
try {
return decode(stream, args, context);
}
finally {
stream.close();
}
}
});
}
else {
stream.buffer = (ByteBuffer)buffer;
try {
return decode(stream, args, context);
}
finally {
stream.close();
}
}
}
private NextInvokeHandler getNextInvokeHandler(final NextInvokeHandler next, final InvokeHandler handler) {
return new NextInvokeHandler() {
public Object handle(String name, Object[] args, HproseContext context) throws Throwable {
return handler.handle(name, args, context, next);
}
};
}
private NextFilterHandler getNextFilterHandler(final NextFilterHandler next, final FilterHandler handler) {
return new NextFilterHandler() {
public Object handle(ByteBuffer request, HproseContext context) throws Throwable {
return handler.handle(request, context, next);
}
};
}
public final void addInvokeHandler(InvokeHandler handler) {
if (handler == null) return;
invokeHandlers.add(handler);
NextInvokeHandler next = defaultInvokeHandler;
for (int i = invokeHandlers.size() - 1; i >= 0; --i) {
next = getNextInvokeHandler(next, invokeHandlers.get(i));
}
invokeHandler = next;
}
public final void addBeforeFilterHandler(FilterHandler handler) {
if (handler == null) return;
beforeFilterHandlers.add(handler);
NextFilterHandler next = defaultBeforeFilterHandler;
for (int i = beforeFilterHandlers.size() - 1; i >= 0; --i) {
next = getNextFilterHandler(next, beforeFilterHandlers.get(i));
}
beforeFilterHandler = next;
}
public final void addAfterFilterHandler(FilterHandler handler) {
if (handler == null) return;
afterFilterHandlers.add(handler);
NextFilterHandler next = defaultAfterFilterHandler;
for (int i = afterFilterHandlers.size() - 1; i >= 0; --i) {
next = getNextFilterHandler(next, afterFilterHandlers.get(i));
}
afterFilterHandler = next;
}
public final HproseClient use(InvokeHandler handler) {
addInvokeHandler(handler);
return this;
}
public final FilterHandlerManager beforeFilter = new FilterHandlerManager() {
public final FilterHandlerManager use(FilterHandler handler) {
addBeforeFilterHandler(handler);
return this;
}
};
public final FilterHandlerManager afterFilter = new FilterHandlerManager() {
public final FilterHandlerManager use(FilterHandler handler) {
addAfterFilterHandler(handler);
return this;
}
};
protected abstract ByteBuffer sendAndReceive(ByteBuffer buffer, int timeout) throws Throwable;
protected abstract void sendAndReceive(ByteBuffer buffer, ReceiveCallback callback, int timeout);
public final void invoke(String name, HproseCallback1> callback) {
invoke(name, nullArgs, callback, null, null, null);
}
public final void invoke(String name, HproseCallback1> callback, HproseErrorEvent errorEvent) {
invoke(name, nullArgs, callback, errorEvent, null, null);
}
public final void invoke(String name, HproseCallback1> callback, InvokeSettings settings) {
invoke(name, nullArgs, callback, null, null, settings);
}
public final void invoke(String name, HproseCallback1> callback, HproseErrorEvent errorEvent, InvokeSettings settings) {
invoke(name, nullArgs, callback, errorEvent, null, settings);
}
public final void invoke(String name, Object[] args, HproseCallback1> callback) {
invoke(name, args, callback, null, null, null);
}
public final void invoke(String name, Object[] args, HproseCallback1> callback, HproseErrorEvent errorEvent) {
invoke(name, args, callback, errorEvent, null, null);
}
public final void invoke(String name, Object[] args, HproseCallback1> callback, InvokeSettings settings) {
invoke(name, args, callback, null, null, settings);
}
public final void invoke(String name, Object[] args, HproseCallback1> callback, HproseErrorEvent errorEvent, InvokeSettings settings) {
invoke(name, args, callback, errorEvent, null, settings);
}
public final void invoke(String name, HproseCallback1 callback, Class returnType) {
invoke(name, nullArgs, callback, null, returnType, null);
}
public final void invoke(String name, HproseCallback1 callback, HproseErrorEvent errorEvent, Class returnType) {
invoke(name, nullArgs, callback, errorEvent, returnType, null);
}
public final void invoke(String name, HproseCallback1 callback, Class returnType, InvokeSettings settings) {
invoke(name, nullArgs, callback, null, returnType, settings);
}
public final void invoke(String name, HproseCallback1 callback, HproseErrorEvent errorEvent, Class returnType, InvokeSettings settings) {
invoke(name, nullArgs, callback, errorEvent, returnType, settings);
}
public final void invoke(String name, Object[] args, HproseCallback1 callback, Class returnType) {
invoke(name, args, callback, null, returnType, null);
}
public final void invoke(String name, Object[] args, HproseCallback1 callback, HproseErrorEvent errorEvent, Class returnType) {
invoke(name, args, callback, errorEvent, returnType, null);
}
public final void invoke(String name, Object[] args, HproseCallback1 callback, Class returnType, InvokeSettings settings) {
invoke(name, args, callback, null, returnType, settings);
}
@SuppressWarnings("unchecked")
public final void invoke(final String name, Object[] args, final HproseCallback1 callback, final HproseErrorEvent errorEvent, Class returnType, InvokeSettings settings) {
if (settings == null) settings = new InvokeSettings();
if (returnType != null) settings.setReturnType(returnType);
final HproseErrorEvent errEvent = (errorEvent == null) ? onError : errorEvent;
settings.setAsync(true);
try {
((Promise) invokeHandler.handle(name, args, getContext(settings))).then(
new Action() {
public void call(T value) throws Throwable {
callback.handler(value);
}
},
new Action() {
public void call(Throwable e) throws Throwable {
if (errEvent != null) {
errEvent.handler(name, e);
}
}
}
);
}
catch (Throwable e) {
if (errEvent != null) {
errEvent.handler(name, e);
}
}
}
public final void invoke(String name, Object[] args, HproseCallback> callback) {
invoke(name, args, callback, null, null, null);
}
public final void invoke(String name, Object[] args, HproseCallback> callback, HproseErrorEvent errorEvent) {
invoke(name, args, callback, errorEvent, null, null);
}
public final void invoke(String name, Object[] args, HproseCallback> callback, InvokeSettings settings) {
invoke(name, args, callback, null, null, settings);
}
public final void invoke(String name, Object[] args, HproseCallback> callback, HproseErrorEvent errorEvent, InvokeSettings settings) {
invoke(name, args, callback, errorEvent, null, settings);
}
public final void invoke(String name, Object[] args, HproseCallback callback, Class returnType) {
invoke(name, args, callback, null, returnType, null);
}
public final void invoke(String name, Object[] args, HproseCallback callback, HproseErrorEvent errorEvent, Class returnType) {
invoke(name, args, callback, errorEvent, returnType, null);
}
public final void invoke(String name, Object[] args, HproseCallback callback, Class returnType, InvokeSettings settings) {
invoke(name, args, callback, null, returnType, settings);
}
@SuppressWarnings("unchecked")
public final void invoke(final String name, final Object[] args, final HproseCallback callback, final HproseErrorEvent errorEvent, Class returnType, InvokeSettings settings) {
if (settings == null) settings = new InvokeSettings();
if (returnType != null) settings.setReturnType(returnType);
final HproseErrorEvent errEvent = (errorEvent == null) ? onError : errorEvent;
settings.setAsync(true);
try {
((Promise) invokeHandler.handle(name, args, getContext(settings))).then(
new Action() {
public void call(T value) throws Throwable {
callback.handler(value, args);
}
},
new Action() {
public void call(Throwable e) throws Throwable {
if (errEvent != null) {
errEvent.handler(name, e);
}
}
}
);
}
catch (Throwable e) {
if (errEvent != null) {
errEvent.handler(name, e);
}
}
}
public final Object invoke(String name) throws Throwable {
return invoke(name, nullArgs, (Class>)null, null);
}
public final Object invoke(String name, InvokeSettings settings) throws Throwable {
return invoke(name, nullArgs, (Class>)null, settings);
}
public final Object invoke(String name, Object[] args) throws Throwable {
return invoke(name, args, (Class>)null, null);
}
public final Object invoke(String name, Object[] args, InvokeSettings settings) throws Throwable {
return invoke(name, args, (Class>)null, settings);
}
public final T invoke(String name, Class returnType) throws Throwable {
return invoke(name, nullArgs, returnType, null);
}
public final T invoke(String name, Class returnType, InvokeSettings settings) throws Throwable {
return invoke(name, nullArgs, returnType, settings);
}
public final T invoke(String name, Object[] args, Class returnType) throws Throwable {
return invoke(name, args, returnType, null);
}
@SuppressWarnings("unchecked")
public final T invoke(String name, Object[] args, Class returnType, InvokeSettings settings) throws Throwable {
if (settings == null) settings = new InvokeSettings();
if (returnType != null) settings.setReturnType(returnType);
Type type = settings.getReturnType();
Class> cls = ClassUtil.toClass(type);
if (Promise.class.equals(cls)) {
return (T)asyncInvoke(name, args, type, settings);
}
if (Future.class.equals(cls)) {
return (T)asyncInvoke(name, args, type, settings).toFuture();
}
if (settings.isAsync()) {
return (T)asyncInvoke(name, args, type, settings);
}
return (T)invokeHandler.handle(name, args, getContext(settings));
}
@SuppressWarnings("unchecked")
private Promise> asyncInvoke(String name, Object[] args, Type type, InvokeSettings settings) {
settings.setAsync(true);
if (type instanceof ParameterizedType) {
type = ((ParameterizedType)type).getActualTypeArguments()[0];
if (void.class.equals(type) || Void.class.equals(type)) {
type = null;
}
}
else {
Class> cls = ClassUtil.toClass(type);
if (Promise.class.equals(cls) || Future.class.equals(cls)) {
type = null;
}
}
settings.setReturnType(type);
try {
return (Promise>)invokeHandler.handle(name, args, getContext(settings));
}
catch (Throwable e) {
return Promise.error(e);
}
}
private static class Topic {
Action handler;
final ConcurrentLinkedQueue> callbacks = new ConcurrentLinkedQueue>();
}
private final ConcurrentHashMap>> allTopics = new ConcurrentHashMap>>();
private Topic> getTopic(String name, Integer id, boolean create) {
ConcurrentHashMap> topics = allTopics.get(name);
if (topics != null) {
return topics.get(id);
}
if (create) {
allTopics.put(name, new ConcurrentHashMap>());
}
return null;
}
private static final InvokeSettings autoIdSettings = new InvokeSettings();
private volatile Promise autoId = null;
private volatile Integer _autoId = null;
static {
autoIdSettings.setReturnType(Integer.class);
autoIdSettings.setSimple(true);
autoIdSettings.setIdempotent(true);
autoIdSettings.setFailswitch(true);
autoIdSettings.setAsync(true);
}
private synchronized Promise autoId() throws Throwable {
if (autoId == null) {
autoId = (Promise)this.invoke("#", autoIdSettings);
autoId.then(new Action() {
public void call(Integer value) throws Throwable {
_autoId = value;
}
}, new Action() {
public void call(Throwable e) throws Throwable {
if (onError != null) {
onError.handler("autoId", e);
}
}
});
}
return autoId;
}
public final void subscribe(String name, Action callback) throws Throwable {
subscribe(name, callback, Object.class, timeout);
}
public final void subscribe(String name, Action callback, int timeout) throws Throwable {
subscribe(name, callback, Object.class, timeout);
}
public final void subscribe(String name, Integer id, Action callback) {
subscribe(name, id, callback, Object.class, timeout);
}
public final void subscribe(String name, Integer id, Action callback, int timeout) {
subscribe(name, id, callback, Object.class, timeout);
}
public final void subscribe(String name, Action callback, Type type) throws Throwable {
subscribe(name, callback, type, timeout);
}
public final void subscribe(final String name, final Action callback, final Type type, final int timeout) throws Throwable {
autoId().then(new Action() {
public void call(Integer value) throws Throwable {
subscribe(name, value, callback, type, timeout);
}
});
}
public final void subscribe(String name, Integer id, Action callback, Type type) {
subscribe(name, id, callback, type, timeout);
}
@SuppressWarnings("unchecked")
public final void subscribe(final String name, final Integer id, Action callback, final Type type, final int timeout) {
Topic topic = (Topic)getTopic(name, id, true);
if (topic == null) {
final Action cb = new Action() {
public void call(Throwable e) throws Throwable {
Topic topic = (Topic)getTopic(name, id, false);
if (topic != null) {
InvokeSettings settings = new InvokeSettings();
settings.setIdempotent(true);
settings.setFailswitch(false);
settings.setReturnType(type);
settings.setTimeout(timeout);
settings.setAsync(true);
try {
Promise result = (Promise)invokeHandler.handle(name, new Object[] {id}, getContext(settings));
result.then(topic.handler, this);
}
catch (Throwable ex) {
call(ex);
}
}
}
};
topic = new Topic();
topic.handler = new Action() {
public void call(T result) throws Throwable {
Topic topic = getTopic(name, id, false);
if (topic != null) {
if (result != null) {
ConcurrentLinkedQueue> callbacks = topic.callbacks;
for (Action callback: callbacks) {
try {
callback.call(result);
}
catch (Throwable e) {}
}
}
cb.call(null);
}
}
};
topic.callbacks.offer(callback);
allTopics.get(name).put(id, topic);
try {
cb.call(null);
}
catch (Throwable e) {
if (onError != null) {
onError.handler(name, e);
}
}
}
else if (!topic.callbacks.contains(callback)) {
topic.callbacks.offer(callback);
}
}
@SuppressWarnings("unchecked")
private void delTopic(ConcurrentHashMap> topics, Integer id, Action callback) {
if (topics != null && topics.size() > 0) {
if (callback != null) {
Topic topic = (Topic)topics.get(id);
if (topic != null) {
ConcurrentLinkedQueue> callbacks = topic.callbacks;
callbacks.remove(callback);
if (callbacks.isEmpty()) {
topics.remove(id);
}
}
}
else {
topics.remove(id);
}
}
}
public void unsubscribe(String name) {
unsubscribe(name, null, null);
}
public void unsubscribe(String name, Action callback) {
unsubscribe(name, null, callback);
}
public void unsubscribe(String name, Integer id) {
unsubscribe(name, id, null);
}
public void unsubscribe(String name, Integer id, final Action callback) {
final ConcurrentHashMap> topics = (ConcurrentHashMap>)allTopics.get(name);
if (topics != null) {
if (id == null) {
if (autoId == null) {
for (Integer i: topics.keySet()) {
delTopic(topics, i, callback);
}
}
else {
autoId.then(new Action() {
public void call(Integer value) throws Throwable {
delTopic(topics, value, callback);
}
});
}
}
else {
delTopic(topics, id, callback);
}
}
}
public Integer getId() {
return _autoId;
}
}