ratpack.background.internal.DefaultBackground Maven / Gradle / Ivy
/*
* Copyright 2013 the original author or authors.
*
* 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 ratpack.background.internal;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import ratpack.func.Action;
import ratpack.handling.Background;
import ratpack.handling.Context;
import ratpack.handling.ProcessingInterceptor;
import ratpack.handling.internal.ContextStorage;
import ratpack.handling.internal.FinishedOnThreadCallbackManager;
import ratpack.handling.internal.InterceptedOperation;
import ratpack.promise.SuccessOrErrorPromise;
import ratpack.promise.SuccessPromise;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import static ratpack.util.ExceptionUtils.toException;
public class DefaultBackground implements Background {
private final ExecutorService foregroundExecutor;
private final ListeningExecutorService backgroundExecutor;
private final ContextStorage contextStorage;
public DefaultBackground(ExecutorService foregroundExecutor, ListeningExecutorService backgroundExecutor, ContextStorage contextStorage) {
this.foregroundExecutor = foregroundExecutor;
this.backgroundExecutor = backgroundExecutor;
this.contextStorage = contextStorage;
}
@Override
public SuccessOrErrorPromise exec(Callable operation) {
return new DefaultSuccessOrErrorPromise<>(operation, contextStorage.get());
}
private class DefaultSuccessOrErrorPromise implements SuccessOrErrorPromise {
private final Callable backgroundAction;
protected final Context context;
DefaultSuccessOrErrorPromise(Callable backgroundAction, Context context) {
this.backgroundAction = backgroundAction;
this.context = context;
}
@Override
public SuccessPromise onError(final Action super Throwable> errorHandler) {
return new DefaultSuccessPromise<>(context, backgroundAction, new Action() {
@Override
public void execute(Throwable t) {
try {
errorHandler.execute(t);
} catch (Throwable errorHandlerError) {
new ForwardToContextErrorHandler().execute(errorHandlerError);
}
}
});
}
@Override
public void then(Action super T> then) {
new DefaultSuccessPromise<>(context, backgroundAction, new ForwardToContextErrorHandler()).then(then);
}
class ForwardToContextErrorHandler implements Action {
@Override
public void execute(Throwable t) {
context.error(toException(t));
}
}
}
private class DefaultSuccessPromise implements SuccessPromise {
private final Context context;
private final Callable backgroundAction;
private final Action errorHandler;
private FinishedOnThreadCallbackManager finishedOnThreadCallbackManager;
DefaultSuccessPromise(Context context, Callable backgroundAction, Action errorHandler) {
this.context = context;
this.backgroundAction = backgroundAction;
this.errorHandler = errorHandler;
this.finishedOnThreadCallbackManager = context.get(FinishedOnThreadCallbackManager.class);
}
@Override
public void then(final Action super T> then) {
finishedOnThreadCallbackManager.register(new Runnable() {
@Override
public void run() {
List interceptors = context.getAll(ProcessingInterceptor.class);
ListenableFuture future = backgroundExecutor.submit(new BackgroundOperation(interceptors));
Futures.addCallback(future, new ForegroundResume(interceptors, then), foregroundExecutor);
}
});
}
private class BackgroundOperation extends InterceptedOperation implements java.util.concurrent.Callable {
private Exception exception;
private T result;
public BackgroundOperation(List interceptors) {
super(ProcessingInterceptor.Type.BACKGROUND, interceptors, context);
}
@Override
public T call() throws Exception {
contextStorage.set(context);
try {
run();
if (exception != null) {
throw exception;
} else {
return result;
}
} finally {
contextStorage.remove();
}
}
@Override
protected void performOperation() {
try {
result = backgroundAction.call();
} catch (Exception e) {
exception = e;
}
}
}
private class ForegroundResume implements FutureCallback {
private final List interceptors;
private final Action super T> then;
private Exception exception;
public ForegroundResume(List interceptors, Action super T> then) {
this.interceptors = interceptors;
this.then = then;
}
@Override
public void onSuccess(final T result) {
contextStorage.set(context);
try {
new InterceptedOperation(ProcessingInterceptor.Type.FOREGROUND, interceptors, context) {
@Override
protected void performOperation() {
try {
then.execute(result);
finishedOnThreadCallbackManager.fire();
} catch (Exception e) {
exception = e;
}
}
}.run();
if (exception != null) {
throw exception;
}
} catch (Exception e) {
context.error(e);
} finally {
contextStorage.remove();
}
}
@SuppressWarnings("NullableProblems")
@Override
public void onFailure(final Throwable t) {
contextStorage.set(context);
try {
new InterceptedOperation(ProcessingInterceptor.Type.FOREGROUND, interceptors, context) {
@Override
protected void performOperation() {
try {
errorHandler.execute(t);
finishedOnThreadCallbackManager.fire();
} catch (Exception e) {
exception = e;
}
}
}.run();
if (exception != null) {
throw exception;
}
} catch (Exception e) {
context.error(e);
} finally {
contextStorage.remove();
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy