/*
* Copyright 2012 Jason Miller
*
* 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 jj.script;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import jj.event.Publisher;
import jj.execution.TaskRunner;
/**
* <p>
* initializes a script environment, so that it is ready for execution. if possible :D
*
* @author jason
*
*/
@Singleton
public class ScriptEnvironmentInitializer implements DependsOnScriptEnvironmentInitialization {
private final TaskRunner taskRunner;
private final IsThread isScriptThread;
private final Publisher publisher;
private static final class TaskOrKey {
private final PendingKey pendingKey;
private final ScriptTask<? extends ScriptEnvironment<?>> task;
TaskOrKey(final ScriptTask<? extends ScriptEnvironment<?>> task, final PendingKey pendingKey) {
this.task = task;
this.pendingKey = pendingKey;
}
}
private final ThreadLocal<HashMap<ScriptEnvironment<?>, List<TaskOrKey>>> pendingInitialization =
new ThreadLocal<HashMap<ScriptEnvironment<?>, List<TaskOrKey>>>() {
@Override
protected HashMap<ScriptEnvironment<?>, List<TaskOrKey>> initialValue() {
return new HashMap<>();
}
};
@Inject
ScriptEnvironmentInitializer(
final TaskRunner taskRunner,
final IsThread isScriptThread,
final Publisher publisher
) {
this.taskRunner = taskRunner;
this.isScriptThread = isScriptThread;
this.publisher = publisher;
}
void initializeScript(AbstractScriptEnvironment<?> se) {
taskRunner.execute(new InitializerTask("initializing " + se, se));
}
void scriptEnvironmentInitialized(ScriptEnvironment<?> scriptEnvironment) {
List<TaskOrKey> tasksOrKeys = pendingInitialization.get().remove(scriptEnvironment);
if (tasksOrKeys != null) {
for (TaskOrKey taskOrKey : tasksOrKeys) {
if (taskOrKey.task != null) {
taskRunner.execute(taskOrKey.task);
} else if (taskOrKey.pendingKey != null) {
taskOrKey.pendingKey.resume(scriptEnvironment.exports());
} else {
throw new AssertionError("taskOrKey list was not maintained properly!");
}
}
}
}
private List<TaskOrKey> getTaskOrKeyList(ScriptEnvironment<?> scriptEnvironment) {
List<TaskOrKey> taskOrKeys = pendingInitialization.get().get(scriptEnvironment);
if (taskOrKeys == null) {
pendingInitialization.get().put(scriptEnvironment, new ArrayList<>());
taskOrKeys = pendingInitialization.get().get(scriptEnvironment);
}
return taskOrKeys;
}
@Override
public void executeOnInitialization(ScriptEnvironment<?> scriptEnvironment, ScriptTask<? extends ScriptEnvironment<?>> task) {
assert isScriptThread.forScriptEnvironment(scriptEnvironment) : "only wait on script environments from their own thread!";
assert !scriptEnvironment.initialized() : "do not wait on scriptEnvironments that are initialized!";
getTaskOrKeyList(scriptEnvironment).add(new TaskOrKey(task, null));
}
/**
* register here to have a pendingKey resumed when a scriptEnvironment has transitioned to initialized
*/
@Override
public void resumeOnInitialization(final ScriptEnvironment<?> scriptEnvironment, final PendingKey pendingKey) {
assert isScriptThread.forScriptEnvironment(scriptEnvironment) : "only wait on script environments from their own thread!";
assert !scriptEnvironment.initialized() : "do not wait on scriptEnvironments that are initialized!";
getTaskOrKeyList(scriptEnvironment).add(new TaskOrKey(null, pendingKey));
}
private class InitializerTask extends ScriptTask<AbstractScriptEnvironment<?>> {
protected InitializerTask(String name, AbstractScriptEnvironment<?> scriptEnvironment) {
super(name, scriptEnvironment);
}
protected void begin() throws Exception {
pendingKey = scriptEnvironment.beginInitializing();
}
protected void complete() throws Exception {
if (pendingKey == null) {
initialized();
}
}
private void initialized() {
scriptEnvironment.initialized(true);
// and tell the world about it!
publisher.publish(new ScriptEnvironmentInitialized(scriptEnvironment));
scriptEnvironmentInitialized(scriptEnvironment);
checkParentResumption(scriptEnvironment.exports());
}
@Override
protected boolean errored(Throwable cause) {
scriptEnvironment.initializationError(cause);
publisher.publish(new ScriptEnvironmentInitializationError(scriptEnvironment, cause));
scriptEnvironmentInitialized(scriptEnvironment);
checkParentResumption(false);
return true;
}
private void checkParentResumption(Object result) {
PendingKey pendingKey = scriptEnvironment.initializationContinuationPendingKey();
if (pendingKey != null) {
pendingKey.resume(result);
}
}
}
}