/*
* Copyright 2016 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 org.gradle.internal.actor.internal;
import org.gradle.internal.actor.Actor;
import org.gradle.internal.actor.ActorFactory;
import org.gradle.internal.concurrent.*;
import org.gradle.internal.dispatch.*;
import org.slf4j.LoggerFactory;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* A basic {@link ActorFactory} implementation. Currently cannot support creating both a blocking and non-blocking actor for the same target object.
*/
public class DefaultActorFactory implements ActorFactory, Stoppable {
private final Map<Object, NonBlockingActor> nonBlockingActors = new IdentityHashMap<Object, NonBlockingActor>();
private final Map<Object, BlockingActor> blockingActors = new IdentityHashMap<Object, BlockingActor>();
private final Object lock = new Object();
private final ExecutorFactory executorFactory;
public DefaultActorFactory(ExecutorFactory executorFactory) {
this.executorFactory = executorFactory;
}
/**
* Stops all actors.
*/
public void stop() {
synchronized (lock) {
try {
CompositeStoppable.stoppable(nonBlockingActors.values()).add(blockingActors.values()).stop();
} finally {
nonBlockingActors.clear();
}
}
}
public Actor createActor(Object target) {
if (target instanceof NonBlockingActor) {
return (NonBlockingActor) target;
}
synchronized (lock) {
if (blockingActors.containsKey(target)) {
throw new UnsupportedOperationException("Cannot create a non-blocking and blocking actor for the same object. This is not implemented yet.");
}
NonBlockingActor actor = nonBlockingActors.get(target);
if (actor == null) {
actor = new NonBlockingActor(target);
nonBlockingActors.put(target, actor);
}
return actor;
}
}
public Actor createBlockingActor(Object target) {
synchronized (lock) {
if (nonBlockingActors.containsKey(target)) {
throw new UnsupportedOperationException("Cannot create a non-blocking and blocking actor for the same object. This is not implemented yet.");
}
BlockingActor actor = blockingActors.get(target);
if (actor == null) {
actor = new BlockingActor(target);
blockingActors.put(target, actor);
}
return actor;
}
}
private void stopped(NonBlockingActor actor) {
synchronized (lock) {
nonBlockingActors.values().remove(actor);
}
}
private void stopped(BlockingActor actor) {
synchronized (lock) {
blockingActors.values().remove(actor);
}
}
private class BlockingActor implements Actor {
private final Dispatch<MethodInvocation> dispatch;
private final Object lock = new Object();
private boolean stopped;
public BlockingActor(Object target) {
dispatch = new ReflectionDispatch(target);
}
public <T> T getProxy(Class<T> type) {
return new ProxyDispatchAdapter<T>(this, type, ThreadSafe.class).getSource();
}
public void stop() throws DispatchException {
synchronized (lock) {
stopped = true;
}
stopped(this);
}
public void dispatch(MethodInvocation message) {
synchronized (lock) {
if (stopped) {
throw new IllegalStateException("This actor has been stopped.");
}
dispatch.dispatch(message);
}
}
}
private class NonBlockingActor implements Actor {
private final Dispatch<MethodInvocation> dispatch;
private final StoppableExecutor executor;
private final ExceptionTrackingFailureHandler failureHandler;
public NonBlockingActor(Object targetObject) {
executor = executorFactory.create("Dispatch " + targetObject);
failureHandler = new ExceptionTrackingFailureHandler(LoggerFactory.getLogger(NonBlockingActor.class));
dispatch = new AsyncDispatch<MethodInvocation>(executor,
new FailureHandlingDispatch<MethodInvocation>(
new ReflectionDispatch(targetObject),
failureHandler));
}
public <T> T getProxy(Class<T> type) {
return new ProxyDispatchAdapter<T>(this, type, ThreadSafe.class).getSource();
}
public void stop() {
try {
CompositeStoppable.stoppable(dispatch, executor, failureHandler).stop();
} finally {
stopped(this);
}
}
public void dispatch(MethodInvocation message) {
dispatch.dispatch(message);
}
}
}