/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.datakernel.service;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import io.datakernel.async.CompletionCallback;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.eventloop.EventloopServer;
import io.datakernel.eventloop.EventloopService;
import io.datakernel.net.BlockingSocketServer;
import javax.sql.DataSource;
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.util.*;
import java.util.concurrent.*;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
/**
* Static utility methods pertaining to ConcurrentService. Creates
* ConcurrentService from some other type of instances.
*/
public final class ServiceAdapters {
private ServiceAdapters() {
}
private static CompletionCallback toCompletionCallback(final SettableFuture<?> future) {
return new CompletionCallback() {
@Override
protected void onComplete() {
future.set(null);
}
@Override
protected void onException(Exception exception) {
future.setException(exception);
}
};
}
public static abstract class SimpleServiceAdapter<S> implements ServiceAdapter<S> {
private final boolean startConcurrently;
private final boolean stopConcurrently;
protected SimpleServiceAdapter(boolean startConcurrently, boolean stopConcurrently) {
this.startConcurrently = startConcurrently;
this.stopConcurrently = stopConcurrently;
}
protected SimpleServiceAdapter() {
this(true, true);
}
protected abstract void start(S instance) throws Exception;
protected abstract void stop(S instance) throws Exception;
@Override
public final ListenableFuture<?> start(final S instance, Executor executor) {
final SettableFuture<Void> future = SettableFuture.create();
(startConcurrently ? executor : directExecutor()).execute(new Runnable() {
@Override
public void run() {
try {
start(instance);
future.set(null);
} catch (Exception e) {
future.setException(e);
}
}
});
return future;
}
@Override
public final ListenableFuture<?> stop(final S instance, Executor executor) {
final SettableFuture<Void> future = SettableFuture.create();
(stopConcurrently ? executor : directExecutor()).execute(new Runnable() {
@Override
public void run() {
try {
stop(instance);
future.set(null);
} catch (Exception e) {
future.setException(e);
}
}
});
return future;
}
}
public static ServiceAdapter<Service> forService() {
return new ServiceAdapter<Service>() {
@Override
public ListenableFuture<?> start(Service instance, Executor executor) {
return instance.start();
}
@Override
public ListenableFuture<?> stop(Service instance, Executor executor) {
return instance.stop();
}
};
}
public static ServiceAdapter<EventloopService> forEventloopService() {
return new ServiceAdapter<EventloopService>() {
@Override
public ListenableFuture<?> start(final EventloopService instance, Executor executor) {
final SettableFuture<?> future = SettableFuture.create();
instance.getEventloop().execute(new Runnable() {
@Override
public void run() {
instance.start(toCompletionCallback(future));
}
});
return future;
}
@Override
public ListenableFuture<?> stop(final EventloopService instance, Executor executor) {
final SettableFuture<?> future = SettableFuture.create();
instance.getEventloop().execute(new Runnable() {
@Override
public void run() {
instance.stop(toCompletionCallback(future));
}
});
return future;
}
};
}
public static ServiceAdapter<EventloopServer> forEventloopServer() {
return new ServiceAdapter<EventloopServer>() {
@Override
public ListenableFuture<?> start(final EventloopServer instance, Executor executor) {
final SettableFuture<?> future = SettableFuture.create();
instance.getEventloop().execute(new Runnable() {
@Override
public void run() {
try {
instance.listen();
future.set(null);
} catch (IOException e) {
future.setException(e);
}
}
});
return future;
}
@Override
public ListenableFuture<?> stop(final EventloopServer instance, Executor executor) {
final SettableFuture<?> future = SettableFuture.create();
instance.getEventloop().execute(new Runnable() {
@Override
public void run() {
instance.close(new CompletionCallback() {
@Override
protected void onComplete() {
future.set(null);
}
@Override
protected void onException(Exception e) {
future.setException(e);
}
});
}
});
return future;
}
};
}
public static ServiceAdapter<Eventloop> forEventloop(final ThreadFactory threadFactory) {
return new ServiceAdapter<Eventloop>() {
@Override
public ListenableFuture<?> start(final Eventloop eventloop, Executor executor) {
final SettableFuture<?> future = SettableFuture.create();
threadFactory.newThread(new Runnable() {
@Override
public void run() {
eventloop.keepAlive(true);
future.set(null);
eventloop.run();
}
}).start();
return future;
}
@Override
public ListenableFuture<?> stop(final Eventloop eventloop, Executor executor) {
final SettableFuture<?> future = SettableFuture.create();
final Thread eventloopThread = eventloop.getEventloopThread();
eventloop.execute(new Runnable() {
@Override
public void run() {
eventloop.keepAlive(false);
}
});
executor.execute(new Runnable() {
@Override
public void run() {
try {
eventloopThread.join();
future.set(null);
} catch (InterruptedException e) {
future.setException(e);
}
}
});
return future;
}
};
}
public static ServiceAdapter<Eventloop> forEventloop() {
return forEventloop(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = Executors.defaultThreadFactory().newThread(r);
thread.setName("eventloop: " + thread.getName());
return thread;
}
});
}
/**
* Returns factory which transforms blocking Service to asynchronous non-blocking ConcurrentService. It runs blocking operations from other thread from executor.
*/
public static ServiceAdapter<BlockingService> forBlockingService() {
return new SimpleServiceAdapter<BlockingService>() {
@Override
protected void start(BlockingService instance) throws Exception {
instance.start();
}
@Override
protected void stop(BlockingService instance) throws Exception {
instance.stop();
}
};
}
public static ServiceAdapter<BlockingSocketServer> forBlockingSocketServer() {
return new SimpleServiceAdapter<BlockingSocketServer>() {
@Override
protected void start(BlockingSocketServer instance) throws Exception {
instance.start();
}
@Override
protected void stop(BlockingSocketServer instance) throws Exception {
instance.stop();
}
};
}
/**
* Returns factory which transforms Timer to ConcurrentService. On starting it doing nothing, on stop it cancel timer.
*/
public static ServiceAdapter<Timer> forTimer() {
return new SimpleServiceAdapter<Timer>(false, false) {
@Override
protected void start(Timer instance) throws Exception {
}
@Override
protected void stop(Timer instance) throws Exception {
instance.cancel();
}
};
}
/**
* Returns factory which transforms ExecutorService to ConcurrentService. On starting it doing nothing, on stopping it shuts down ExecutorService.
*/
public static ServiceAdapter<ExecutorService> forExecutorService() {
return new SimpleServiceAdapter<ExecutorService>(false, true) {
@Override
protected void start(ExecutorService instance) throws Exception {
}
@Override
protected void stop(ExecutorService instance) throws Exception {
instance.shutdown();
}
};
}
/**
* Returns factory which transforms Closeable object to ConcurrentService. On starting it doing nothing, on stopping it close Closeable.
*/
public static ServiceAdapter<Closeable> forCloseable() {
return new SimpleServiceAdapter<Closeable>(false, true) {
@Override
protected void start(Closeable instance) throws Exception {
}
@Override
protected void stop(Closeable instance) throws Exception {
instance.close();
}
};
}
/**
* Returns factory which transforms DataSource object to ConcurrentService. On starting it checks connecting , on stopping it close DataSource.
*/
public static ServiceAdapter<DataSource> forDataSource() {
return new SimpleServiceAdapter<DataSource>(true, false) {
@Override
protected void start(DataSource instance) throws Exception {
Connection connection = instance.getConnection();
connection.close();
}
@Override
protected void stop(DataSource instance) throws Exception {
}
};
}
public static <T> ServiceAdapter<T> immediateServiceAdapter() {
return new SimpleServiceAdapter<T>(false, false) {
@Override
protected void start(T instance) throws Exception {
}
@Override
protected void stop(T instance) throws Exception {
}
};
}
@SafeVarargs
public static <T> ServiceAdapter<T> combinedAdapter(ServiceAdapter<? super T>... startOrder) {
return combinedAdapter(Arrays.asList(startOrder));
}
public static <T> ServiceAdapter<T> combinedAdapter(final List<? extends ServiceAdapter<? super T>> startOrder) {
List<? extends ServiceAdapter<? super T>> stopOrder = new ArrayList<>(startOrder);
Collections.reverse(stopOrder);
return combinedAdapter(startOrder, stopOrder);
}
private interface Action<T> {
ListenableFuture<?> doAction(ServiceAdapter<? super T> serviceAdapter, T instance, Executor executor);
}
public static <T> ServiceAdapter<T> combinedAdapter(final List<? extends ServiceAdapter<? super T>> startOrder,
final List<? extends ServiceAdapter<? super T>> stopOrder) {
return new ServiceAdapter<T>() {
private void doAction(final T instance, final Executor executor,
final Iterator<? extends ServiceAdapter<? super T>> iterator, final SettableFuture<?> future,
final Action<T> action) {
if (iterator.hasNext()) {
ServiceAdapter<? super T> next = iterator.next();
final ListenableFuture<?> nextFuture = action.doAction(next, instance, executor);
nextFuture.addListener(new Runnable() {
@Override
public void run() {
try {
nextFuture.get();
doAction(instance, executor, iterator, future, action);
} catch (InterruptedException e) {
future.setException(e);
} catch (ExecutionException e) {
future.setException(e.getCause());
}
}
}, directExecutor());
} else {
future.set(null);
}
}
@Override
public ListenableFuture<?> start(T instance, Executor executor) {
final SettableFuture<?> future = SettableFuture.create();
doAction(instance, executor, startOrder.iterator(), future, new Action<T>() {
@Override
public ListenableFuture<?> doAction(ServiceAdapter<? super T> serviceAdapter, T instance, Executor executor) {
return serviceAdapter.start(instance, executor);
}
});
return future;
}
@Override
public ListenableFuture<?> stop(T instance, Executor executor) {
final SettableFuture<?> future = SettableFuture.create();
doAction(instance, executor, stopOrder.iterator(), future, new Action<T>() {
@Override
public ListenableFuture<?> doAction(ServiceAdapter<? super T> serviceAdapter, T instance, Executor executor) {
return serviceAdapter.stop(instance, executor);
}
});
return future;
}
};
}
}