use of com.cinchapi.concourse.server.plugin.io.MessageQueue in project concourse by cinchapi.
the class BackgroundThreadTest method testBackgroundExecutorHasCorrectInformation.
@Test
public void testBackgroundExecutorHasCorrectInformation() throws InterruptedException {
InterProcessCommunication outgoing = new MessageQueue();
ConcurrentMap<AccessToken, RemoteMethodResponse> responses = Maps.newConcurrentMap();
BackgroundExecutor executor = PluginExecutors.newCachedBackgroundExecutor(outgoing, responses);
CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean passed = new AtomicBoolean(true);
executor.execute(Random.getSimpleString(), () -> {
InterProcessCommunication myOutgoing = ((BackgroundThread) Thread.currentThread()).outgoing();
ConcurrentMap<AccessToken, RemoteMethodResponse> myResponses = ((BackgroundThread) Thread.currentThread()).responses();
try {
Assert.assertSame(outgoing, myOutgoing);
Assert.assertSame(responses, myResponses);
} catch (AssertionError e) {
passed.set(false);
e.printStackTrace();
}
latch.countDown();
});
latch.await();
Assert.assertTrue(passed.get());
}
use of com.cinchapi.concourse.server.plugin.io.MessageQueue in project concourse by cinchapi.
the class RealTimePlugin method run.
@Override
public final void run() {
Reflection.call(this, "setReadyState");
// For a RealTimePlugin, the first fromServer message contains the
// address for the stream channel
ByteBuffer data = fromServer.read();
RemoteMessage message = serializer.deserialize(data);
if (message.type() == RemoteMessage.Type.ATTRIBUTE) {
RemoteAttributeExchange attribute = (RemoteAttributeExchange) message;
if (attribute.key().equalsIgnoreCase(STREAM_ATTRIBUTE)) {
log.debug("Listening for streamed packets at {}", attribute.value());
@SuppressWarnings("resource") final InterProcessCommunication stream = new MessageQueue(attribute.value());
// Create a separate event loop to process Packets of writes
// that come from the server.
Thread loop = new Thread(() -> {
ByteBuffer bytes = null;
while ((bytes = stream.read()) != null) {
final Packet packet = serializer.deserialize(bytes);
// Each packet should be processed in a separate
// worker thread
workers.execute(() -> {
log.debug("Received packet from Concourse Server: {}", packet);
handlePacket(packet);
});
}
});
loop.setDaemon(true);
loop.start();
// Start normal plugin operations
super.run();
} else {
throw new IllegalStateException("Unsupported attribute " + attribute);
}
} else {
throw new IllegalStateException();
}
}
use of com.cinchapi.concourse.server.plugin.io.MessageQueue in project concourse by cinchapi.
the class PluginManager method startStreamToPlugin.
/**
* Create a {@link InterProcessCommunication} segment over which the
* PluginManager will stream real-time {@link Packet packets} that contain
* writes.
*
* @param id the plugin id
*/
private void startStreamToPlugin(String id) {
String tempDir = getPluginTempDirectory(id);
String streamFile = FileSystem.tempFile(tempDir, "RT-", ".shm");
Logger.debug("Creating real-time stream for {} at {}", id, streamFile);
InterProcessCommunication stream = new MessageQueue(streamFile);
Logger.debug("Shared memory for real-time stream of '{} is located at " + "'{}", id, streamFile);
RemoteAttributeExchange attribute = new RemoteAttributeExchange("stream", streamFile);
InterProcessCommunication fromServer = (InterProcessCommunication) registry.get(id, RegistryData.FROM_SERVER);
ByteBuffer buffer = serializer.serialize(attribute);
fromServer.write(buffer);
streams.add(stream);
}
use of com.cinchapi.concourse.server.plugin.io.MessageQueue in project concourse by cinchapi.
the class PluginManager method launch.
/**
* <p>
* This method is called from {@link #activate(String, ActivationType)} once
* any pre-launch checks have successfully completed.
* </p>
* <p>
* Launch the {@code plugin} from {@code dist} within a separate JVM
* configured with the specified {@code classpath} and the values from the
* {@code prefs} file.
* </p>
*
* @param bundle the bundle directory that contains the plugin libraries
* @param prefs the {@link Path} to the config file
* @param plugin the class to launch in a separate JVM
* @param classpath the classpath for the separate JVM
*/
private void launch(final String bundle, final Path prefs, final Class<?> plugin, final List<String> classpath) {
// Write an arbitrary main class that'll construct the Plugin and run it
String launchClass = plugin.getName();
String launchClassShort = plugin.getSimpleName();
String processName = "Concourse_" + launchClassShort;
String tempDir = getPluginTempDirectory(launchClass);
String fromServer = FileSystem.tempFile(tempDir, "FS-", ".shm");
String fromPlugin = FileSystem.tempFile(tempDir, "FP-", ".shm");
String source = pluginLaunchClassTemplate.replace("INSERT_PROCESS_NAME", processName).replace("INSERT_IMPORT_STATEMENT", launchClass).replace("INSERT_FROM_SERVER", fromServer).replace("INSERT_FROM_PLUGIN", fromPlugin).replace("INSERT_CLASS_NAME", launchClassShort);
// Create an external JavaApp in which the Plugin will run. Get the
// plugin config to size the JVM properly.
PluginConfiguration config = Reflection.newInstance(StandardPluginConfiguration.class, prefs);
Logger.info("Configuring plugin '{}' from bundle '{}' with " + "preferences located in {}", plugin, bundle, prefs);
long heapSize = config.getHeapSize() / BYTES_PER_MB;
for (String alias : config.getAliases()) {
if (!aliases.containsKey(alias) && !ambiguous.contains(alias)) {
aliases.put(alias, plugin.getName());
Logger.info("Registering '{}' as an alias for {}", alias, plugin);
} else {
aliases.remove(alias);
ambiguous.add(alias);
Logger.info("Alias '{}' can't be used because it is " + "associated with multiple plugins", alias);
}
}
String pluginHome = home + File.separator + bundle;
String serviceToken = BaseEncoding.base32Hex().encode(server.newServiceToken().bufferForData().array());
ArrayList<String> options = new ArrayList<String>();
if (config.getRemoteDebuggerEnabled()) {
options.add("-Xdebug");
options.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=" + config.getRemoteDebuggerPort());
}
options.add("-Xms" + heapSize + "M");
options.add("-Xmx" + heapSize + "M");
options.add("-D" + Plugin.PLUGIN_HOME_JVM_PROPERTY + "=" + pluginHome);
options.add("-D" + Plugin.PLUGIN_SERVICE_TOKEN_JVM_PROPERTY + "=" + serviceToken);
String cp = StringUtils.join(classpath, JavaApp.CLASSPATH_SEPARATOR);
JavaApp app = new JavaApp(cp, source, options);
app.run();
if (app.isRunning()) {
Logger.info("Starting plugin '{}' from bundle '{}'", launchClass, bundle);
}
app.onPrematureShutdown((out, err) -> {
try {
List<String> outLines = CharStreams.readLines(new InputStreamReader(out));
List<String> errLines = CharStreams.readLines(new InputStreamReader(err));
Logger.warn("Plugin '{}' unexpectedly crashed. ", plugin);
Logger.warn("Standard Output for {}: {}", plugin, StringUtils.join(outLines, System.lineSeparator()));
Logger.warn("Standard Error for {}: {}", plugin, StringUtils.join(errLines, System.lineSeparator()));
Logger.warn("Restarting {} now...", plugin);
Iterator<Entry<String, String>> it = aliases.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> entry = it.next();
if (entry.getValue().equals(plugin.getName())) {
it.remove();
}
}
// TODO: it would be nice to just restart the same JavaApp
// instance (e.g. app.restart();)
launch(bundle, prefs, plugin, classpath);
} catch (IOException e) {
throw CheckedExceptions.wrapAsRuntimeException(e);
}
});
// Ensure that the Plugin is ready to run before adding it to the
// registry to avoid premature invocations
Path readyCheck = com.cinchapi.common.io.Files.getHashedFilePath(serviceToken);
try {
while (!Files.deleteIfExists(readyCheck)) {
Thread.sleep(1000);
continue;
}
Logger.info("Plugin '{}' is ready", plugin);
} catch (IOException | InterruptedException e) {
}
// Store metadata about the Plugin
String id = launchClass;
registry.put(id, RegistryData.PLUGIN_BUNDLE, bundle);
registry.put(id, RegistryData.FROM_SERVER, new MessageQueue(fromServer));
registry.put(id, RegistryData.FROM_PLUGIN, new MessageQueue(fromPlugin));
registry.put(id, RegistryData.STATUS, PluginStatus.ACTIVE);
registry.put(id, RegistryData.APP_INSTANCE, app);
registry.put(id, RegistryData.FROM_PLUGIN_RESPONSES, Maps.<AccessToken, RemoteMethodResponse>newConcurrentMap());
Logger.debug("Shared memory for server-based communication to '{} is " + "located at '{}", id, fromServer);
Logger.debug("Shared memory for plugin-based communication from '{} is " + "located at '{}", id, fromPlugin);
}
use of com.cinchapi.concourse.server.plugin.io.MessageQueue in project concourse by cinchapi.
the class BackgroundThreadTest method testBackgroundExecutorSetsEnvironmentCorrectly.
@Test
public void testBackgroundExecutorSetsEnvironmentCorrectly() throws InterruptedException {
InterProcessCommunication outgoing = new MessageQueue();
ConcurrentMap<AccessToken, RemoteMethodResponse> responses = Maps.newConcurrentMap();
String environment1 = Random.getSimpleString();
String environment2 = Random.getSimpleString();
MockConcourseRuntime runtime = new MockConcourseRuntime();
BackgroundExecutor executor = PluginExecutors.newCachedBackgroundExecutor(outgoing, responses);
CountDownLatch latch = new CountDownLatch(2);
final AtomicBoolean passed = new AtomicBoolean(true);
executor.execute(environment1, () -> {
try {
Assert.assertEquals(environment1, runtime.environment());
latch.countDown();
} catch (AssertionError e) {
passed.set(false);
e.printStackTrace();
}
});
executor.execute(environment2, () -> {
try {
Assert.assertEquals(environment2, runtime.environment());
latch.countDown();
} catch (AssertionError e) {
passed.set(false);
e.printStackTrace();
}
});
latch.await();
Assert.assertTrue(passed.get());
}
Aggregations