
io.jsync.impl.DefaultAsync Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsync.io Show documentation
Show all versions of jsync.io Show documentation
jsync.io is a non-blocking, event-driven networking framework for Java
/*
* Copyright (c) 2011-2013 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.jsync.impl;
import io.jsync.*;
import io.jsync.datagram.DatagramSocket;
import io.jsync.datagram.InternetProtocolFamily;
import io.jsync.datagram.impl.DefaultDatagramSocket;
import io.jsync.dns.DnsClient;
import io.jsync.dns.impl.DefaultDnsClient;
import io.jsync.eventbus.EventBus;
import io.jsync.eventbus.impl.DefaultEventBus;
import io.jsync.file.FileSystem;
import io.jsync.file.impl.DefaultFileSystem;
import io.jsync.file.impl.WindowsFileSystem;
import io.jsync.http.HttpClient;
import io.jsync.http.HttpServer;
import io.jsync.http.impl.DefaultHttpClient;
import io.jsync.http.impl.DefaultHttpServer;
import io.jsync.logging.Logger;
import io.jsync.logging.impl.LoggerFactory;
import io.jsync.net.NetClient;
import io.jsync.net.NetServer;
import io.jsync.net.impl.DefaultNetClient;
import io.jsync.net.impl.DefaultNetServer;
import io.jsync.net.impl.ServerID;
import io.jsync.shareddata.SharedData;
import io.jsync.sockjs.SockJSServer;
import io.jsync.sockjs.impl.DefaultSockJSServer;
import io.jsync.spi.Action;
import io.jsync.spi.cluster.ClusterManager;
import io.jsync.spi.cluster.ClusterManagerFactory;
import io.jsync.spi.cluster.impl.HazelcastClusterManagerFactory;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.util.ResourceLeakDetector;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Tim Fox
*/
public class DefaultAsync implements AsyncInternal {
private static final Logger log = LoggerFactory.getLogger(DefaultAsync.class);
static {
// Netty resource leak detection has a performance overhead and we do not need it in jsync.io
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
// Use the JDK deflater/inflater by default
System.setProperty("io.netty.noJdkZlibDecoder", "false");
}
private final FileSystem fileSystem = getFileSystem();
private final EventBus eventBus;
private final SharedData sharedData = new SharedData();
private final ConcurrentMap timeouts = new ConcurrentHashMap<>();
private final AtomicLong timeoutCounter = new AtomicLong(0);
private final ClusterManager clusterManager;
private ExecutorService backgroundPool = AsyncExecutorFactory.workerPool("async-worker-thread-");
private final OrderedExecutorFactory orderedFact = new OrderedExecutorFactory(backgroundPool);
private EventLoopGroup eventLoopGroup = AsyncExecutorFactory.eventLoopGroup("async-eventloop-thread-");
private Map sharedHttpServers = new HashMap<>();
private Map sharedNetServers = new HashMap<>();
public DefaultAsync() {
this.eventBus = new DefaultEventBus(this);
this.clusterManager = null;
}
public DefaultAsync(String hostname) {
this(0, hostname, null);
}
public DefaultAsync(int port, String hostname, final Handler> resultHandler) {
ClusterManagerFactory factory = new HazelcastClusterManagerFactory();
ClusterManager manager = factory.createClusterManager(this);
try {
manager.join();
} catch (Exception e) {
log.error("Failed to started clustered async");
resultHandler.handle(new DefaultFutureResult<>(e));
manager = null;
}
if (manager != null) {
final Async inst = this;
this.clusterManager = manager;
this.eventBus = new DefaultEventBus(this, port, hostname, clusterManager, new AsyncResultHandler() {
@Override
public void handle(AsyncResult res) {
if (resultHandler != null) {
if (res.succeeded()) {
resultHandler.handle(new DefaultFutureResult<>(inst));
} else {
resultHandler.handle(new DefaultFutureResult<>(res.cause()));
}
} else if (res.failed()) {
log.error("Failed to start event bus", res.cause());
}
}
});
} else {
this.eventBus = new DefaultEventBus(this);
this.clusterManager = null;
}
}
@Override
public boolean isClustered() {
return clusterManager != null;
}
@Override
public ClusterManager clusterManager() {
return clusterManager;
}
/**
* @return The FileSystem implementation for the OS
*/
protected FileSystem getFileSystem() {
return Windows.isWindows() ? new WindowsFileSystem(this) : new DefaultFileSystem(this);
}
@Override
public DatagramSocket createDatagramSocket(InternetProtocolFamily family) {
return new DefaultDatagramSocket(this, family);
}
public NetServer createNetServer() {
return new DefaultNetServer(this);
}
public NetClient createNetClient() {
return new DefaultNetClient(this);
}
public FileSystem fileSystem() {
return fileSystem;
}
public SharedData sharedData() {
return sharedData;
}
public HttpServer createHttpServer() {
return new DefaultHttpServer(this);
}
public HttpClient createHttpClient() {
return new DefaultHttpClient(this);
}
public SockJSServer createSockJSServer(HttpServer httpServer) {
return new DefaultSockJSServer(this, httpServer);
}
public EventBus eventBus() {
return eventBus;
}
public DefaultContext startOnEventLoop(final Runnable runnable) {
DefaultContext context = createEventLoopContext();
context.execute(runnable);
return context;
}
public DefaultContext startInBackground(final Runnable runnable, final boolean multiThreaded) {
DefaultContext context = createWorkerContext(multiThreaded);
context.execute(runnable);
return context;
}
public boolean isEventLoop() {
DefaultContext context = getContext();
if (context != null) {
return context instanceof EventLoopContext;
}
return false;
}
public boolean isWorker() {
DefaultContext context = getContext();
if (context != null) {
return context instanceof WorkerContext;
}
return false;
}
public long setPeriodic(long delay, final Handler handler) {
return scheduleTimeout(getOrCreateContext(), handler, delay, true);
}
public long setTimer(long delay, final Handler handler) {
return scheduleTimeout(getOrCreateContext(), handler, delay, false);
}
public void runOnContext(final Handler task) {
DefaultContext context = getOrCreateContext();
context.runOnContext(task);
}
public Context currentContext() {
return getContext();
}
// The background pool is used for making blocking calls to legacy synchronous APIs
public ExecutorService getBackgroundPool() {
return backgroundPool;
}
public EventLoopGroup getEventLoopGroup() {
return eventLoopGroup;
}
public DefaultContext getOrCreateContext() {
DefaultContext ctx = getContext();
if (ctx == null) {
// Create a context
ctx = createEventLoopContext();
}
return ctx;
}
public void reportException(Throwable t) {
DefaultContext ctx = getContext();
if (ctx != null) {
ctx.reportException(t);
} else {
log.error("default async Unhandled exception ", t);
}
}
public Map sharedHttpServers() {
return sharedHttpServers;
}
public Map sharedNetServers() {
return sharedNetServers;
}
public boolean cancelTimer(long id) {
InternalTimerHandler handler = timeouts.remove(id);
if (handler != null) {
handler.context.removeCloseHook(handler);
return handler.cancel();
} else {
return false;
}
}
public EventLoopContext createEventLoopContext() {
return new EventLoopContext(this, orderedFact.getExecutor());
}
@Override
public DnsClient createDnsClient(InetSocketAddress... dnsServers) {
return new DefaultDnsClient(this, dnsServers);
}
private long scheduleTimeout(final DefaultContext context, final Handler handler, long delay, boolean periodic) {
if (delay < 1) {
throw new IllegalArgumentException("Cannot schedule a timer with delay < 1 ms");
}
long timerId = timeoutCounter.getAndIncrement();
final InternalTimerHandler task = new InternalTimerHandler(timerId, handler, periodic, context);
final Runnable wrapped = context.wrapTask(task);
final Runnable toRun;
final EventLoop el = context.getEventLoop();
if (context instanceof EventLoopContext) {
toRun = wrapped;
} else {
// On worker context
toRun = new Runnable() {
public void run() {
// Make sure the timer gets executed on the worker context
context.execute(wrapped);
}
};
}
Future> future;
if (periodic) {
future = el.scheduleAtFixedRate(toRun, delay, delay, TimeUnit.MILLISECONDS);
} else {
future = el.schedule(toRun, delay, TimeUnit.MILLISECONDS);
}
task.future = future;
timeouts.put(timerId, task);
context.addCloseHook(task);
return timerId;
}
private DefaultContext createWorkerContext(boolean multiThreaded) {
if (multiThreaded) {
return new MultiThreadedWorkerContext(this, orderedFact.getExecutor(), backgroundPool);
} else {
return new WorkerContext(this, orderedFact.getExecutor());
}
}
public DefaultContext getContext() {
Thread current = Thread.currentThread();
if (current instanceof AsyncThread) {
return ((AsyncThread) current).getContext();
}
return null;
}
public void setContext(DefaultContext context) {
Thread current = Thread.currentThread();
if (current instanceof AsyncThread) {
((AsyncThread) current).setContext(context);
}
if (context != null) {
context.setTCCL();
} else {
Thread.currentThread().setContextClassLoader(null);
}
}
@Override
public void stop() {
if (sharedHttpServers != null) {
// Copy set to prevent ConcurrentModificationException
for (HttpServer server : new HashSet<>(sharedHttpServers.values())) {
server.close();
}
sharedHttpServers.clear();
}
if (sharedNetServers != null) {
// Copy set to prevent ConcurrentModificationException
for (NetServer server : new HashSet<>(sharedNetServers.values())) {
server.close();
}
sharedNetServers.clear();
}
if (backgroundPool != null) {
backgroundPool.shutdown();
}
try {
if (backgroundPool != null) {
backgroundPool.awaitTermination(20, TimeUnit.SECONDS);
backgroundPool = null;
}
} catch (InterruptedException ex) {
// ignore
}
if (eventLoopGroup != null) {
eventLoopGroup.shutdownGracefully();
}
eventBus.close(null);
setContext(null);
}
@Override
public void executeBlocking(final Action action, final Handler> resultHandler) {
final DefaultContext context = getOrCreateContext();
Runnable runner = new Runnable() {
public void run() {
final DefaultFutureResult res = new DefaultFutureResult<>();
try {
T result = action.perform();
res.setResult(result);
} catch (Exception e) {
res.setFailure(e);
}
if (resultHandler != null) {
context.execute(new Runnable() {
public void run() {
res.setHandler(resultHandler);
}
});
}
}
};
context.executeOnOrderedWorkerExec(runner);
}
private class InternalTimerHandler implements Runnable, Closeable {
final Handler handler;
final boolean periodic;
final long timerID;
final DefaultContext context;
volatile Future> future;
boolean cancelled;
InternalTimerHandler(long timerID, Handler runnable, boolean periodic, DefaultContext context) {
this.context = context;
this.timerID = timerID;
this.handler = runnable;
this.periodic = periodic;
}
boolean cancel() {
cancelled = true;
return future.cancel(false);
}
public void run() {
if (!cancelled) {
try {
handler.handle(timerID);
} finally {
if (!periodic) {
// Clean up after it's fired
cleanupNonPeriodic();
}
}
}
}
private void cleanupNonPeriodic() {
DefaultAsync.this.timeouts.remove(timerID);
DefaultContext context = getContext();
context.removeCloseHook(this);
}
// Called via Context close hook when Verticle is undeployed
public void close(Handler> doneHandler) {
DefaultAsync.this.timeouts.remove(timerID);
cancel();
doneHandler.handle(new DefaultFutureResult<>((Void) null));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy