embulk-executor-remoteserver 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +42 -0
- data/.gitignore +14 -0
- data/Dockerfile +14 -0
- data/LICENSE +21 -0
- data/README.md +30 -0
- data/build.gradle +70 -0
- data/docker-compose.yml +24 -0
- data/docker/run_embulk_server.sh +3 -0
- data/gradle.properties +1 -0
- data/gradle/dependency-locks/compileClasspath.lockfile +40 -0
- data/gradle/dependency-locks/testCompileClasspath.lockfile +49 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +5 -0
- data/gradlew +172 -0
- data/gradlew.bat +84 -0
- data/lib/embulk/executor/remoteserver.rb +3 -0
- data/settings.gradle +1 -0
- data/src/main/java/org/embulk/executor/remoteserver/EmbulkClient.java +111 -0
- data/src/main/java/org/embulk/executor/remoteserver/EmbulkServer.java +32 -0
- data/src/main/java/org/embulk/executor/remoteserver/Host.java +32 -0
- data/src/main/java/org/embulk/executor/remoteserver/InitializeSessionCommand.java +94 -0
- data/src/main/java/org/embulk/executor/remoteserver/Launcher.java +25 -0
- data/src/main/java/org/embulk/executor/remoteserver/NotifyTaskStateCommand.java +23 -0
- data/src/main/java/org/embulk/executor/remoteserver/PluginArchive.java +170 -0
- data/src/main/java/org/embulk/executor/remoteserver/RemoteServerExecutor.java +131 -0
- data/src/main/java/org/embulk/executor/remoteserver/RemoveSessionCommand.java +24 -0
- data/src/main/java/org/embulk/executor/remoteserver/Session.java +177 -0
- data/src/main/java/org/embulk/executor/remoteserver/SessionManager.java +37 -0
- data/src/main/java/org/embulk/executor/remoteserver/SessionState.java +143 -0
- data/src/main/java/org/embulk/executor/remoteserver/StartTaskCommand.java +51 -0
- data/src/main/java/org/embulk/executor/remoteserver/TaskExecutionException.java +11 -0
- data/src/main/java/org/embulk/executor/remoteserver/TaskState.java +5 -0
- data/src/main/java/org/embulk/executor/remoteserver/UpdateTaskStateData.java +55 -0
- data/src/main/resources/logback.xml +11 -0
- data/src/test/java/org/embulk/executor/remoteserver/TestRemoteServerExecutor.java +80 -0
- data/src/test/resources/json/test1.json +1 -0
- data/src/test/resources/json/test2.json +1 -0
- data/test/Gemfile +4 -0
- data/test/Gemfile.lock +20 -0
- data/test/setup.sh +8 -0
- metadata +119 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
package org.embulk.executor.remoteserver;
|
2
|
+
|
3
|
+
import com.google.inject.Inject;
|
4
|
+
import org.embulk.config.Config;
|
5
|
+
import org.embulk.config.ConfigDefault;
|
6
|
+
import org.embulk.config.ConfigInject;
|
7
|
+
import org.embulk.config.ConfigSource;
|
8
|
+
import org.embulk.config.ModelManager;
|
9
|
+
import org.embulk.config.Task;
|
10
|
+
import org.embulk.exec.ForSystemConfig;
|
11
|
+
import org.embulk.spi.Exec;
|
12
|
+
import org.embulk.spi.ExecutorPlugin;
|
13
|
+
import org.embulk.spi.ProcessState;
|
14
|
+
import org.embulk.spi.ProcessTask;
|
15
|
+
import org.embulk.spi.Schema;
|
16
|
+
import org.jruby.embed.ScriptingContainer;
|
17
|
+
import org.slf4j.Logger;
|
18
|
+
import org.slf4j.LoggerFactory;
|
19
|
+
|
20
|
+
import java.io.File;
|
21
|
+
import java.io.FileOutputStream;
|
22
|
+
import java.io.IOException;
|
23
|
+
import java.io.UncheckedIOException;
|
24
|
+
import java.nio.file.Files;
|
25
|
+
import java.util.Collections;
|
26
|
+
import java.util.List;
|
27
|
+
import java.util.concurrent.TimeoutException;
|
28
|
+
|
29
|
+
public class RemoteServerExecutor implements ExecutorPlugin {
|
30
|
+
private static final Logger log = LoggerFactory.getLogger(RemoteServerExecutor.class);
|
31
|
+
private static final Host DEFAULT_HOST = new Host("localhost", 30000);
|
32
|
+
private final ConfigSource systemConfig;
|
33
|
+
private final ScriptingContainer jruby;
|
34
|
+
|
35
|
+
interface PluginTask extends Task {
|
36
|
+
@Config("hosts")
|
37
|
+
@ConfigDefault("[]")
|
38
|
+
List<Host> getHosts();
|
39
|
+
|
40
|
+
@Config("timeout_seconds")
|
41
|
+
@ConfigDefault("3600")
|
42
|
+
int getTimeoutSeconds();
|
43
|
+
|
44
|
+
@ConfigInject
|
45
|
+
ModelManager getModelManager();
|
46
|
+
}
|
47
|
+
|
48
|
+
@Inject
|
49
|
+
public RemoteServerExecutor(@ForSystemConfig ConfigSource systemConfig, ScriptingContainer jruby) {
|
50
|
+
this.systemConfig = systemConfig;
|
51
|
+
this.jruby = jruby;
|
52
|
+
}
|
53
|
+
|
54
|
+
@Override
|
55
|
+
public void transaction(ConfigSource config, Schema outputSchema, int inputTaskCount, Control control) {
|
56
|
+
PluginTask task = config.loadConfig(PluginTask.class);
|
57
|
+
if (task.getHosts().isEmpty()) {
|
58
|
+
log.info("Hosts is empty. Run with a local server.");
|
59
|
+
try (EmbulkServer _autoclosed = EmbulkServer.start(DEFAULT_HOST.getName(), DEFAULT_HOST.getPort(), 1)) {
|
60
|
+
control.transaction(outputSchema, inputTaskCount, new ExecutorImpl(inputTaskCount, task, Collections.singletonList(DEFAULT_HOST)));
|
61
|
+
} catch (IOException e) {
|
62
|
+
throw new UncheckedIOException(e);
|
63
|
+
}
|
64
|
+
} else {
|
65
|
+
control.transaction(outputSchema, inputTaskCount, new ExecutorImpl(inputTaskCount, task, task.getHosts()));
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
private class ExecutorImpl implements ExecutorPlugin.Executor {
|
70
|
+
private final PluginTask pluginTask;
|
71
|
+
private final int inputTaskCount;
|
72
|
+
private final List<Host> hosts;
|
73
|
+
|
74
|
+
ExecutorImpl(int inputTaskCount, PluginTask pluginTask, List<Host> hosts) {
|
75
|
+
this.inputTaskCount = inputTaskCount;
|
76
|
+
this.pluginTask = pluginTask;
|
77
|
+
this.hosts = hosts;
|
78
|
+
}
|
79
|
+
|
80
|
+
@Override
|
81
|
+
public void execute(ProcessTask processTask, ProcessState state) {
|
82
|
+
byte[] pluginArchiveBytes;
|
83
|
+
List<PluginArchive.GemSpec> gemSpecs;
|
84
|
+
try {
|
85
|
+
File tempFile = Exec.getTempFileSpace().createTempFile("gems", ".zip");
|
86
|
+
gemSpecs = archivePlugins(tempFile);
|
87
|
+
pluginArchiveBytes = Files.readAllBytes(tempFile.toPath());
|
88
|
+
} catch (IOException e) {
|
89
|
+
throw new UncheckedIOException(e);
|
90
|
+
}
|
91
|
+
// Remove 'jruby_global_bundler_plugin_source_directory' (--bundle option)
|
92
|
+
// because all gems will be loaded via PluginArchive on server
|
93
|
+
ConfigSource systemConfigToSend = systemConfig.deepCopy().remove("jruby_global_bundler_plugin_source_directory");
|
94
|
+
|
95
|
+
ModelManager modelManager = pluginTask.getModelManager();
|
96
|
+
String systemConfigJson = modelManager.writeObject(systemConfigToSend);
|
97
|
+
String pluginTaskJson = modelManager.writeObject(pluginTask);
|
98
|
+
String processTaskJson = modelManager.writeObject(processTask);
|
99
|
+
|
100
|
+
SessionState sessionState = new SessionState(
|
101
|
+
systemConfigJson, pluginTaskJson, processTaskJson, gemSpecs, pluginArchiveBytes, state, inputTaskCount, modelManager);
|
102
|
+
try (EmbulkClient client = EmbulkClient.open(sessionState, hosts)) {
|
103
|
+
client.createSession();
|
104
|
+
|
105
|
+
state.initialize(inputTaskCount, inputTaskCount);
|
106
|
+
for (int i = 0; i < inputTaskCount; i++) {
|
107
|
+
if (state.getOutputTaskState(i).isCommitted()) {
|
108
|
+
log.warn("Skipped resumed task {}", i);
|
109
|
+
continue;
|
110
|
+
}
|
111
|
+
client.startTask(i);
|
112
|
+
}
|
113
|
+
sessionState.waitUntilCompleted(pluginTask.getTimeoutSeconds() + 1); // Add 1 sec to consider network latency
|
114
|
+
} catch (InterruptedException | TimeoutException e) {
|
115
|
+
throw new IllegalStateException(e);
|
116
|
+
} catch (IOException e) {
|
117
|
+
throw new UncheckedIOException(e);
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
private List<PluginArchive.GemSpec> archivePlugins(File tempFile) throws IOException {
|
122
|
+
// archive plugins
|
123
|
+
PluginArchive archive = new PluginArchive.Builder()
|
124
|
+
.addLoadedRubyGems(jruby)
|
125
|
+
.build();
|
126
|
+
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
|
127
|
+
return archive.dump(fos);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
package org.embulk.executor.remoteserver;
|
2
|
+
|
3
|
+
import com.github.kamatama41.nsocket.Connection;
|
4
|
+
import com.github.kamatama41.nsocket.SyncCommand;
|
5
|
+
|
6
|
+
public class RemoveSessionCommand implements SyncCommand<String, Void> {
|
7
|
+
static final String ID = "remove_session";
|
8
|
+
private final SessionManager sessionManager;
|
9
|
+
|
10
|
+
RemoveSessionCommand(SessionManager sessionManager) {
|
11
|
+
this.sessionManager = sessionManager;
|
12
|
+
}
|
13
|
+
|
14
|
+
@Override
|
15
|
+
public Void apply(String sessionId, Connection connection) {
|
16
|
+
sessionManager.removeSession(sessionId);
|
17
|
+
return null;
|
18
|
+
}
|
19
|
+
|
20
|
+
@Override
|
21
|
+
public String getId() {
|
22
|
+
return ID;
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,177 @@
|
|
1
|
+
package org.embulk.executor.remoteserver;
|
2
|
+
|
3
|
+
import com.github.kamatama41.nsocket.Connection;
|
4
|
+
import org.embulk.EmbulkEmbed;
|
5
|
+
import org.embulk.config.ConfigSource;
|
6
|
+
import org.embulk.config.ModelManager;
|
7
|
+
import org.embulk.config.TaskReport;
|
8
|
+
import org.embulk.spi.Exec;
|
9
|
+
import org.embulk.spi.ExecSession;
|
10
|
+
import org.embulk.spi.ProcessTask;
|
11
|
+
import org.embulk.spi.util.Executors;
|
12
|
+
import org.jruby.embed.ScriptingContainer;
|
13
|
+
import org.slf4j.Logger;
|
14
|
+
import org.slf4j.LoggerFactory;
|
15
|
+
|
16
|
+
import java.io.ByteArrayInputStream;
|
17
|
+
import java.io.IOException;
|
18
|
+
import java.io.InputStream;
|
19
|
+
import java.io.UncheckedIOException;
|
20
|
+
import java.nio.file.Files;
|
21
|
+
import java.nio.file.Path;
|
22
|
+
import java.util.LinkedList;
|
23
|
+
import java.util.List;
|
24
|
+
import java.util.Queue;
|
25
|
+
import java.util.concurrent.ConcurrentHashMap;
|
26
|
+
import java.util.concurrent.ConcurrentMap;
|
27
|
+
import java.util.concurrent.ExecutorService;
|
28
|
+
import java.util.concurrent.TimeUnit;
|
29
|
+
|
30
|
+
import static java.nio.charset.StandardCharsets.UTF_8;
|
31
|
+
|
32
|
+
class Session implements AutoCloseable {
|
33
|
+
private static final Logger log = LoggerFactory.getLogger(Session.class);
|
34
|
+
private final String id;
|
35
|
+
private final EmbulkEmbed embed;
|
36
|
+
private final ScriptingContainer jruby;
|
37
|
+
private final RemoteServerExecutor.PluginTask pluginTask;
|
38
|
+
private final ProcessTask processTask;
|
39
|
+
private final List<PluginArchive.GemSpec> gemSpecs;
|
40
|
+
private final byte[] pluginArchive;
|
41
|
+
private final ExecSession session;
|
42
|
+
private final ModelManager modelManager;
|
43
|
+
private final ConcurrentMap<Integer, Queue<UpdateTaskStateData>> bufferMap;
|
44
|
+
private final ExecutorService sessionRunner;
|
45
|
+
private volatile Connection connection;
|
46
|
+
|
47
|
+
Session(
|
48
|
+
String id,
|
49
|
+
String systemConfig,
|
50
|
+
String pluginTaskConfig,
|
51
|
+
String processTaskConfig,
|
52
|
+
List<PluginArchive.GemSpec> gemSpecs,
|
53
|
+
byte[] pluginArchive
|
54
|
+
) {
|
55
|
+
this.id = id;
|
56
|
+
this.embed = newEmbulkBootstrap(systemConfig).initialize();
|
57
|
+
this.jruby = embed.getInjector().getInstance(ScriptingContainer.class);
|
58
|
+
this.modelManager = embed.getModelManager();
|
59
|
+
this.pluginTask = modelManager.readObject(RemoteServerExecutor.PluginTask.class, pluginTaskConfig);
|
60
|
+
this.processTask = modelManager.readObject(ProcessTask.class, processTaskConfig);
|
61
|
+
this.gemSpecs = gemSpecs;
|
62
|
+
this.pluginArchive = pluginArchive;
|
63
|
+
this.session = ExecSession.builder(embed.getInjector()).build();
|
64
|
+
loadPluginArchive();
|
65
|
+
this.bufferMap = new ConcurrentHashMap<>();
|
66
|
+
this.sessionRunner = java.util.concurrent.Executors.newCachedThreadPool(r -> {
|
67
|
+
Thread t = new Thread(r);
|
68
|
+
t.setName("session-runner-" + id);
|
69
|
+
t.setDaemon(true);
|
70
|
+
return t;
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
74
|
+
void runTaskAsynchronously(int taskIndex) {
|
75
|
+
sessionRunner.submit(() -> Exec.doWith(session, () -> {
|
76
|
+
runTask(taskIndex);
|
77
|
+
return null;
|
78
|
+
}));
|
79
|
+
}
|
80
|
+
|
81
|
+
private void runTask(int taskIndex) throws InterruptedException {
|
82
|
+
bufferMap.putIfAbsent(taskIndex, new LinkedList<>());
|
83
|
+
try {
|
84
|
+
Executors.process(session, processTask, taskIndex, new Executors.ProcessStateCallback() {
|
85
|
+
@Override
|
86
|
+
public void started() {
|
87
|
+
sendCommand(taskIndex, new UpdateTaskStateData(id, taskIndex, TaskState.STARTED));
|
88
|
+
}
|
89
|
+
|
90
|
+
@Override
|
91
|
+
public void inputCommitted(TaskReport report) {
|
92
|
+
UpdateTaskStateData data = new UpdateTaskStateData(id, taskIndex, TaskState.INPUT_COMMITTED);
|
93
|
+
data.setTaskReport(modelManager.writeObject(report));
|
94
|
+
sendCommand(taskIndex, data);
|
95
|
+
}
|
96
|
+
|
97
|
+
@Override
|
98
|
+
public void outputCommitted(TaskReport report) {
|
99
|
+
UpdateTaskStateData data = new UpdateTaskStateData(id, taskIndex, TaskState.OUTPUT_COMMITTED);
|
100
|
+
data.setTaskReport(modelManager.writeObject(report));
|
101
|
+
sendCommand(taskIndex, data);
|
102
|
+
}
|
103
|
+
});
|
104
|
+
sendCommand(taskIndex, new UpdateTaskStateData(id, taskIndex, TaskState.FINISHED));
|
105
|
+
} catch (Exception e) {
|
106
|
+
log.warn(String.format("Failed to run task[%d]", taskIndex), e);
|
107
|
+
UpdateTaskStateData data = new UpdateTaskStateData(id, taskIndex, TaskState.FAILED);
|
108
|
+
data.setErrorMessage(e.getMessage());
|
109
|
+
sendCommand(taskIndex, data);
|
110
|
+
}
|
111
|
+
|
112
|
+
Queue<UpdateTaskStateData> buffer = bufferMap.get(taskIndex);
|
113
|
+
if (buffer.isEmpty()) {
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
|
117
|
+
// Flush buffer if remaining
|
118
|
+
int waitSeconds = 10;
|
119
|
+
while (!buffer.isEmpty()) {
|
120
|
+
if (connection.isOpen()) {
|
121
|
+
flushBuffer(taskIndex, connection);
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
log.warn("Connection is closed, wait {} seconds until reconnected.", waitSeconds);
|
125
|
+
TimeUnit.SECONDS.sleep(waitSeconds);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
void updateConnection(Connection connection) {
|
130
|
+
this.connection = connection;
|
131
|
+
}
|
132
|
+
|
133
|
+
private void sendCommand(int taskIndex, UpdateTaskStateData data) {
|
134
|
+
bufferMap.get(taskIndex).offer(data);
|
135
|
+
if (!connection.isOpen()) {
|
136
|
+
log.warn("Connection is closed, add data to buffer.");
|
137
|
+
return;
|
138
|
+
}
|
139
|
+
flushBuffer(taskIndex, connection);
|
140
|
+
}
|
141
|
+
|
142
|
+
private void flushBuffer(int taskIndex, Connection connection) {
|
143
|
+
UpdateTaskStateData data;
|
144
|
+
Queue<UpdateTaskStateData> buffer = bufferMap.get(taskIndex);
|
145
|
+
while ((data = buffer.poll()) != null) {
|
146
|
+
connection.sendCommand(NotifyTaskStateCommand.ID, data);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
private static EmbulkEmbed.Bootstrap newEmbulkBootstrap(String configJson) {
|
151
|
+
ConfigSource systemConfig = getSystemConfig(configJson);
|
152
|
+
return new EmbulkEmbed.Bootstrap().setSystemConfig(systemConfig);
|
153
|
+
}
|
154
|
+
|
155
|
+
private static ConfigSource getSystemConfig(String configJson) {
|
156
|
+
try (InputStream in = new ByteArrayInputStream(configJson.getBytes(UTF_8))) {
|
157
|
+
return EmbulkEmbed.newSystemConfigLoader().fromJson(in);
|
158
|
+
} catch (IOException e) {
|
159
|
+
throw new UncheckedIOException(e);
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
private void loadPluginArchive() {
|
164
|
+
try (ByteArrayInputStream bis = new ByteArrayInputStream(pluginArchive)) {
|
165
|
+
Path gemsDir = Files.createTempDirectory("embulk_gems");
|
166
|
+
PluginArchive.load(gemsDir.toFile(), gemSpecs, bis).restoreLoadPathsTo(jruby);
|
167
|
+
} catch (IOException e) {
|
168
|
+
throw new UncheckedIOException(e);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
@Override
|
173
|
+
public void close() {
|
174
|
+
log.debug("Closing the session {}", id);
|
175
|
+
sessionRunner.shutdownNow();
|
176
|
+
}
|
177
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
package org.embulk.executor.remoteserver;
|
2
|
+
|
3
|
+
import com.github.kamatama41.nsocket.Connection;
|
4
|
+
|
5
|
+
import java.util.List;
|
6
|
+
import java.util.concurrent.ConcurrentHashMap;
|
7
|
+
import java.util.concurrent.ConcurrentMap;
|
8
|
+
|
9
|
+
class SessionManager {
|
10
|
+
private final ConcurrentMap<String, Session> sessionMap;
|
11
|
+
|
12
|
+
SessionManager() {
|
13
|
+
this.sessionMap = new ConcurrentHashMap<>();
|
14
|
+
}
|
15
|
+
|
16
|
+
void registerNewSession(String sessionId,
|
17
|
+
String systemConfig,
|
18
|
+
String pluginTaskConfig,
|
19
|
+
String processTaskConfig,
|
20
|
+
List<PluginArchive.GemSpec> gemSpecs,
|
21
|
+
byte[] pluginArchive,
|
22
|
+
Connection connection) {
|
23
|
+
Session session = sessionMap.computeIfAbsent(
|
24
|
+
sessionId, (k) -> new Session(
|
25
|
+
sessionId, systemConfig, pluginTaskConfig, processTaskConfig, gemSpecs, pluginArchive));
|
26
|
+
session.updateConnection(connection);
|
27
|
+
}
|
28
|
+
|
29
|
+
Session getSession(String sessionId) {
|
30
|
+
return sessionMap.get(sessionId);
|
31
|
+
}
|
32
|
+
|
33
|
+
void removeSession(String sessionId) {
|
34
|
+
Session removed = sessionMap.remove(sessionId);
|
35
|
+
removed.close();
|
36
|
+
}
|
37
|
+
}
|
@@ -0,0 +1,143 @@
|
|
1
|
+
package org.embulk.executor.remoteserver;
|
2
|
+
|
3
|
+
import org.embulk.config.ModelManager;
|
4
|
+
import org.embulk.config.TaskReport;
|
5
|
+
import org.embulk.spi.ProcessState;
|
6
|
+
import org.slf4j.Logger;
|
7
|
+
import org.slf4j.LoggerFactory;
|
8
|
+
|
9
|
+
import java.util.List;
|
10
|
+
import java.util.Map;
|
11
|
+
import java.util.UUID;
|
12
|
+
import java.util.concurrent.ConcurrentHashMap;
|
13
|
+
import java.util.concurrent.CountDownLatch;
|
14
|
+
import java.util.concurrent.TimeUnit;
|
15
|
+
import java.util.concurrent.TimeoutException;
|
16
|
+
import java.util.stream.Collectors;
|
17
|
+
|
18
|
+
class SessionState {
|
19
|
+
private static final Logger log = LoggerFactory.getLogger(SessionState.class);
|
20
|
+
|
21
|
+
private String sessionId;
|
22
|
+
private final String systemConfigJson;
|
23
|
+
private final String pluginTaskJson;
|
24
|
+
private final String processTaskJson;
|
25
|
+
private final List<PluginArchive.GemSpec> gemSpecs;
|
26
|
+
private final byte[] pluginArchiveBytes;
|
27
|
+
|
28
|
+
private final ProcessState state;
|
29
|
+
private final CountDownLatch timer;
|
30
|
+
private final int inputTaskCount;
|
31
|
+
private final ModelManager modelManager;
|
32
|
+
private volatile boolean isFinished;
|
33
|
+
private final Map<Integer, String> errorMessages;
|
34
|
+
|
35
|
+
SessionState(
|
36
|
+
String systemConfigJson, String pluginTaskJson, String processTaskJson,
|
37
|
+
List<PluginArchive.GemSpec> gemSpecs, byte[] pluginArchiveBytes,
|
38
|
+
ProcessState state, int inputTaskCount, ModelManager modelManager) {
|
39
|
+
this.sessionId = UUID.randomUUID().toString();
|
40
|
+
this.systemConfigJson = systemConfigJson;
|
41
|
+
this.pluginTaskJson = pluginTaskJson;
|
42
|
+
this.processTaskJson = processTaskJson;
|
43
|
+
this.gemSpecs = gemSpecs;
|
44
|
+
this.pluginArchiveBytes = pluginArchiveBytes;
|
45
|
+
this.state = state;
|
46
|
+
this.timer = new CountDownLatch(inputTaskCount);
|
47
|
+
this.inputTaskCount = inputTaskCount;
|
48
|
+
this.modelManager = modelManager;
|
49
|
+
this.isFinished = false;
|
50
|
+
this.errorMessages = new ConcurrentHashMap<>();
|
51
|
+
}
|
52
|
+
|
53
|
+
String getSessionId() {
|
54
|
+
return sessionId;
|
55
|
+
}
|
56
|
+
|
57
|
+
String getSystemConfigJson() {
|
58
|
+
return systemConfigJson;
|
59
|
+
}
|
60
|
+
|
61
|
+
String getPluginTaskJson() {
|
62
|
+
return pluginTaskJson;
|
63
|
+
}
|
64
|
+
|
65
|
+
String getProcessTaskJson() {
|
66
|
+
return processTaskJson;
|
67
|
+
}
|
68
|
+
|
69
|
+
List<PluginArchive.GemSpec> getGemSpecs() {
|
70
|
+
return gemSpecs;
|
71
|
+
}
|
72
|
+
|
73
|
+
byte[] getPluginArchiveBytes() {
|
74
|
+
return pluginArchiveBytes;
|
75
|
+
}
|
76
|
+
|
77
|
+
ProcessState getState() {
|
78
|
+
return state;
|
79
|
+
}
|
80
|
+
|
81
|
+
boolean isFinished() {
|
82
|
+
return isFinished;
|
83
|
+
}
|
84
|
+
|
85
|
+
synchronized void update(UpdateTaskStateData data) {
|
86
|
+
switch (data.getTaskState()) {
|
87
|
+
case STARTED:
|
88
|
+
state.getInputTaskState(data.getTaskIndex()).start();
|
89
|
+
state.getOutputTaskState(data.getTaskIndex()).start();
|
90
|
+
break;
|
91
|
+
case INPUT_COMMITTED:
|
92
|
+
state.getInputTaskState(data.getTaskIndex()).setTaskReport(getTaskReport(data.getTaskReport()));
|
93
|
+
break;
|
94
|
+
case OUTPUT_COMMITTED:
|
95
|
+
state.getOutputTaskState(data.getTaskIndex()).setTaskReport(getTaskReport(data.getTaskReport()));
|
96
|
+
break;
|
97
|
+
case FAILED:
|
98
|
+
errorMessages.put(data.getTaskIndex(), data.getErrorMessage());
|
99
|
+
timer.countDown();
|
100
|
+
break;
|
101
|
+
case FINISHED:
|
102
|
+
state.getInputTaskState(data.getTaskIndex()).finish();
|
103
|
+
state.getOutputTaskState(data.getTaskIndex()).finish();
|
104
|
+
timer.countDown();
|
105
|
+
showProgress(state, inputTaskCount);
|
106
|
+
break;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
void waitUntilCompleted(int timeoutSeconds) throws InterruptedException, TimeoutException {
|
111
|
+
try {
|
112
|
+
if (!timer.await(timeoutSeconds, TimeUnit.SECONDS)) {
|
113
|
+
throw new TimeoutException(String.format("The session (%s) was time-out.", sessionId));
|
114
|
+
}
|
115
|
+
if (!errorMessages.isEmpty()) {
|
116
|
+
String message = errorMessages.entrySet().stream()
|
117
|
+
.map(e -> String.format("%d: %s", e.getKey(), e.getValue()))
|
118
|
+
.collect(Collectors.joining(System.lineSeparator()));
|
119
|
+
throw new TaskExecutionException(message);
|
120
|
+
}
|
121
|
+
} finally {
|
122
|
+
isFinished = true;
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
private TaskReport getTaskReport(String json) {
|
127
|
+
return modelManager.readObject(TaskReport.class, json);
|
128
|
+
}
|
129
|
+
|
130
|
+
private static void showProgress(ProcessState state, int taskCount) {
|
131
|
+
int started = 0;
|
132
|
+
int finished = 0;
|
133
|
+
for (int i = 0; i < taskCount; i++) {
|
134
|
+
if (state.getOutputTaskState(i).isStarted()) {
|
135
|
+
started++;
|
136
|
+
}
|
137
|
+
if (state.getOutputTaskState(i).isFinished()) {
|
138
|
+
finished++;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
log.info(String.format("{done:%3d / %d, running: %d}", finished, taskCount, started - finished));
|
142
|
+
}
|
143
|
+
}
|