goja.Invoker Maven / Gradle / Ivy
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013-2014 sagyf Yang. The Four Group.
*/
package goja;
import goja.core.app.ApplicationMode;
import goja.core.app.GojaConfig;
import goja.core.exceptions.GojaException;
import goja.core.exceptions.UnexpectedException;
import goja.core.libs.Action;
import goja.core.libs.PThreadFactory;
import goja.core.libs.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* .
*
* @author sagyf yang
* @version 1.0 2014-06-24 22:05
* @since JDK 1.6
*/
@SuppressWarnings("UnusedDeclaration")
public final class Invoker {
private static final Logger logger = LoggerFactory.getLogger(Invoker.class);
/**
* Main executor for requests invocations.
*/
public static ScheduledThreadPoolExecutor executor = null;
/**
* Init executor at load time.
*/
static {
int core = GojaConfig.getPropertyToInt("goja.pool", GojaConfig.getApplicationMode().isDev() ? 1
: (Runtime.getRuntime().availableProcessors() + 1));
executor = new ScheduledThreadPoolExecutor(core, new PThreadFactory("goja"),
new ThreadPoolExecutor.AbortPolicy());
}
private Invoker() {
}
/**
* Run the code in a new thread took from a thread pool.
*
* @param invocation The code to run
* @return The future object, to know when the task is completed
*/
public static Future> invoke(final Invocation invocation) {
return executor.submit(invocation);
}
/**
* Run the code in a new thread after a delay
*
* @param invocation The code to run
* @param millis The time to wait before, in milliseconds
* @return The future object, to know when the task is completed
*/
public static Future> invoke(final Invocation invocation, long millis) {
return executor.schedule(invocation, millis, TimeUnit.MILLISECONDS);
}
/**
* Run the code in the same thread than caller.
*
* @param invocation The code to run
*/
public static void invokeInThread(DirectInvocation invocation) {
boolean retry = true;
while (retry) {
invocation.run();
if (invocation.retry == null) {
retry = false;
} else {
try {
if (invocation.retry.task != null) {
invocation.retry.task.get();
} else {
Thread.sleep(invocation.retry.timeout);
}
} catch (Exception e) {
throw new UnexpectedException(e);
}
retry = true;
}
}
}
/**
* The class/method that will be invoked by the current operation
*/
public static class InvocationContext {
public static ThreadLocal current = new ThreadLocal();
private final List annotations;
private final String invocationType;
public InvocationContext(String invocationType) {
this.invocationType = invocationType;
this.annotations = new ArrayList();
}
public InvocationContext(String invocationType, List annotations) {
this.invocationType = invocationType;
this.annotations = annotations;
}
public InvocationContext(String invocationType, Annotation[] annotations) {
this.invocationType = invocationType;
this.annotations = Arrays.asList(annotations);
}
public InvocationContext(String invocationType, Annotation[]... annotations) {
this.invocationType = invocationType;
this.annotations = new ArrayList();
for (Annotation[] some : annotations) {
this.annotations.addAll(Arrays.asList(some));
}
}
public static InvocationContext current() {
return current.get();
}
public List getAnnotations() {
return annotations;
}
@SuppressWarnings("unchecked")
public T getAnnotation(Class clazz) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAssignableFrom(clazz)) {
return (T) annotation;
}
}
return null;
}
public boolean isAnnotationPresent(Class clazz) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAssignableFrom(clazz)) {
return true;
}
}
return false;
}
/**
* Returns the InvocationType for this invocation - Ie: A plugin can use this to find out if
* it runs in the context of a background Job
*/
public String getInvocationType() {
return invocationType;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("InvocationType: ");
builder.append(invocationType);
builder.append(". annotations: ");
for (Annotation annotation : annotations) {
builder.append(annotation.toString()).append(",");
}
return builder.toString();
}
}
/**
* An Invocation in something to run in a Play! context
*/
public static abstract class Invocation implements Runnable {
/**
* Override this method
*/
public abstract void execute() throws Exception;
/**
* Needs this method to do stuff *before* init() is executed. The different
* Invocation-implementations does a lot of stuff in init() and they might do it before
* calling super.init()
*/
protected void preInit() {
// clear language for this request - we're resolving it later when it is needed
// I18n.clear();
}
/**
* Init the call (especially usefull in DEV mode to detect changes)
*/
public boolean init() {
Thread.currentThread().setContextClassLoader(Goja.class.getClassLoader());
if (!Goja.started) {
if (GojaConfig.getApplicationMode() == ApplicationMode.PROD) {
throw new UnexpectedException("Application is not started");
}
}
InvocationContext.current.set(getInvocationContext());
return true;
}
public abstract InvocationContext getInvocationContext();
/**
* Things to do before an Invocation
*/
public void before() {
Thread.currentThread().setContextClassLoader(Goja.class.getClassLoader());
}
/**
* Things to do after an Invocation. (if the Invocation code has not thrown any exception)
*/
public void after() {
}
/**
* Things to do when the whole invocation has succeeded (before + execute + after)
*/
public void onSuccess() throws Exception {
}
/**
* Things to do if the Invocation code thrown an exception
*/
public void onException(Throwable e) {
if (e instanceof GojaException) {
throw (GojaException) e;
}
throw new UnexpectedException(e);
}
/**
* The request is suspended
*/
public void suspend(Suspend suspendRequest) {
if (suspendRequest.task != null) {
WaitForTasksCompletion.waitFor(suspendRequest.task, this);
} else {
Invoker.invoke(this, suspendRequest.timeout);
}
}
/**
* Things to do in all cases after the invocation.
*/
public void _finally() {
InvocationContext.current.remove();
}
/**
* It's time to execute.
*/
public void run() {
try {
preInit();
if (init()) {
before();
execute();
after();
onSuccess();
}
} catch (Suspend e) {
suspend(e);
after();
} catch (Throwable e) {
onException(e);
} finally {
_finally();
}
}
}
/**
* A direct invocation (in the same thread than caller)
*/
public static abstract class DirectInvocation extends Invocation {
public static final String invocationType = "DirectInvocation";
Suspend retry = null;
@Override
public boolean init() {
retry = null;
return super.init();
}
@Override
public void suspend(Suspend suspendRequest) {
retry = suspendRequest;
}
@Override
public InvocationContext getInvocationContext() {
return new InvocationContext(invocationType);
}
}
/**
* Throwable to indicate that the request must be suspended
*/
@SuppressWarnings("UnusedDeclaration")
public static class Suspend extends GojaException {
private static final long serialVersionUID = 4876949569944002423L;
/**
* Suspend for a timeout (in milliseconds).
*/
long timeout;
/**
* Wait for task execution.
*/
Future> task;
public Suspend(long timeout) {
this.timeout = timeout;
}
public Suspend(Future> task) {
this.task = task;
}
@Override
public String getErrorTitle() {
return "Request is suspended";
}
@Override
public String getErrorDescription() {
if (task != null) {
return "Wait for " + task;
}
return "Retry in " + timeout + " ms.";
}
}
/**
* Utility that track tasks completion in order to resume suspended requests.
*/
static class WaitForTasksCompletion extends Thread {
static WaitForTasksCompletion instance;
Map, Invocation> queue;
public WaitForTasksCompletion() {
queue = new ConcurrentHashMap, Invocation>();
setName("WaitForTasksCompletion");
setDaemon(true);
}
public static void waitFor(Future task, final Invocation invocation) {
if (task instanceof Promise) {
Promise smartFuture = (Promise) task;
smartFuture.onRedeem(new Action>() {
public void invoke(Promise result) {
executor.submit(invocation);
}
});
} else {
synchronized (WaitForTasksCompletion.class) {
if (instance == null) {
instance = new WaitForTasksCompletion();
logger.warn("Start WaitForTasksCompletion");
instance.start();
}
instance.queue.put(task, invocation);
}
}
}
@Override
public void run() {
while (true) {
try {
if (!queue.isEmpty()) {
for (Future> task : new HashSet>(queue.keySet())) {
if (task.isDone()) {
executor.submit(queue.get(task));
queue.remove(task);
}
}
}
Thread.sleep(50);
} catch (InterruptedException ex) {
logger.warn("While waiting for task completions", ex);
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy