All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jsync.impl.DefaultAsync Maven / Gradle / Ivy

There is a newer version: 1.10.13
Show newest version
/*
 * 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