+Main-Class: org.argeo.cms.jshell.JShellClient
+
Import-Package: \
org.osgi.framework.namespace, \
*
--- /dev/null
+package org.argeo.cms.jshell;
+
+import jdk.jshell.execution.DirectExecutionControl;
+import jdk.jshell.execution.LoaderDelegate;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionEnv;
+
+/** Custom {@link ExecutionControl}. */
+public class CmsExecutionControl extends DirectExecutionControl {
+ private final ExecutionEnv executionEnv;
+
+ public CmsExecutionControl(ExecutionEnv executionEnv, LoaderDelegate loaderDelegate) {
+ super(loaderDelegate);
+ this.executionEnv = executionEnv;
+
+ }
+
+ @Override
+ protected void clientCodeEnter() throws InternalException {
+ super.clientCodeEnter();
+ }
+
+ @Override
+ protected void clientCodeLeave() throws InternalException {
+ super.clientCodeLeave();
+ executionEnv.userOut().flush();
+ }
+
+}
import java.net.StandardSocketOptions;
import java.net.UnixDomainSocketAddress;
import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.Channels;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ReadableByteChannel;
import java.util.Map;
import java.util.UUID;
+/** A JShell client to a local CMS node. */
public class JShellClient {
private final static Logger logger = System.getLogger(JShellClient.class.getName());
ctl.setOutputStream(System.err);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- System.out.println("\nShutting down...");
+// System.out.println("\nShutting down...");
toOriginalTerminal();
std.shutdown();
ctl.shutdown();
if (benchmark)
System.err.println(ManagementFactory.getRuntimeMXBean().getUptime());
StringBuilder sb = new StringBuilder();
-// sb.append("/set feedback silent\n");
if (!scriptArgs.isEmpty()) {
// additional arguments as $1, $2, etc.
for (String arg : scriptArgs)
if (sb.length() > 0)
writeLine(sb);
- ByteBuffer buffer = ByteBuffer.allocate(1024);
try (BufferedReader reader = Files.newBufferedReader(script)) {
String line;
lines: while ((line = reader.readLine()) != null) {
if (line.startsWith("#"))
continue lines;
- buffer.put((line + "\n").getBytes(UTF_8));
- buffer.flip();
- channel.write(buffer);
- buffer.rewind();
+ writeLine(line);
}
}
-// ByteBuffer buffer = ByteBuffer.allocate(1024);
-// try (SeekableByteChannel scriptChannel = Files.newByteChannel(script, StandardOpenOption.READ)) {
-// while (channel.isConnected()) {
-// if (scriptChannel.read(buffer) < 0)
-// break;
-// buffer.flip();
-// channel.write(buffer);
-// buffer.rewind();
-// }
-// }
-
// exit
if (channel.isConnected())
writeLine("/exit");
}
}
+ /** Not optimal, but performance is not critical here. */
private void writeLine(Object obj) throws IOException {
channel.write(ByteBuffer.wrap((obj + "\n").getBytes(UTF_8)));
}
}
} catch (ClosedByInterruptException e) {
// silent
+ } catch (AsynchronousCloseException e) {
+ // silent
} catch (IOException e) {
e.printStackTrace();
}
try {
ByteBuffer buffer = ByteBuffer.allocate(inBufferSize);
while (channel.isConnected()) {
- if (inChannel.read(buffer) < 0)
+ if (inChannel.read(buffer) < 0) {
+ System.err.println("in EOF");
+ channel.shutdownOutput();
break;
+ }
// int b = (int) buffer.get(0);
// if (b == 0x1B) {
// System.out.println("Ctrl+C");
} catch (IOException e) {
e.printStackTrace();
}
- if (inChannel != null)
- forwardThread.interrupt();
- readThread.interrupt();
+// if (inChannel != null)
+// forwardThread.interrupt();
+// readThread.interrupt();
}
public void setInputStream(InputStream in) {
try {
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (!readInThread.isInterrupted() && channel.isConnected()) {
- if (channel.read(buffer) < 0)
+ if (channel.read(buffer) < 0) {
+ in.close();
break;
+ }
buffer.flip();
inPipe.sink().write(buffer);
buffer.rewind();
}
} catch (AsynchronousCloseException e) {
// ignore
- // TODO make it cleaner
} catch (IOException e) {
e.printStackTrace();
}
try {
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (!writeOutThread.isInterrupted() && channel.isConnected()) {
- if (outPipe.source().read(buffer) < 0)
+ if (outPipe.source().read(buffer) < 0) {
+ out.close();
break;
+ }
buffer.flip();
channel.write(buffer);
buffer.rewind();
import java.util.TreeSet;
import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.jshell.CmsExecutionControl;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
-import jdk.jshell.execution.DirectExecutionControl;
import jdk.jshell.spi.ExecutionControl;
import jdk.jshell.spi.ExecutionControlProvider;
import jdk.jshell.spi.ExecutionEnv;
@Override
public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) throws Throwable {
- // TODO find a better way to get a default bundle context
- // NOTE: the related default bundle has to be started
-
-// String symbolicName = parameters.get(BUNDLE_PARAMETER);
-// Bundle fromBundle = getBundleFromSn(symbolicName);
-
Long bundleId = Long.parseLong(parameters.get(BUNDLE_PARAMETER));
Bundle fromBundle = getBundleFromId(bundleId);
BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class);
ClassLoader fromBundleClassLoader = fromBundleWiring.getClassLoader();
- // use the bundle classloade as context classloader
- Thread.currentThread().setContextClassLoader(fromBundleClassLoader);
-
- ExecutionControl executionControl = new DirectExecutionControl(
+ ExecutionControl executionControl = new CmsExecutionControl(env,
new WrappingLoaderDelegate(env, fromBundleClassLoader));
- log.debug("JShell from " + fromBundle.getSymbolicName() + "_" + fromBundle.getVersion() + " ["
+ log.trace(() -> "JShell from " + fromBundle.getSymbolicName() + "_" + fromBundle.getVersion() + " ["
+ fromBundle.getBundleId() + "]");
return executionControl;
}
String std = """
import jdk.jshell.spi.ExecutionEnv;
- InputStream STDIN = new Supplier<InputStream>() {
+ /** Redirected standard IO. */
+ public class Std {
+ final static InputStream in = new Supplier<InputStream>() {
@Override
public InputStream get() {
}
}.get();
- PrintStream STDOUT = new Supplier<PrintStream>() {
+ final static PrintStream out = new Supplier<PrintStream>() {
@Override
public PrintStream get() {
}
}.get();
- PrintStream STDERR = new Supplier<PrintStream>() {
+ final static PrintStream err = new Supplier<PrintStream>() {
@Override
public PrintStream get() {
}
}.get();
- """;
+
+ }
+ """;
writer.write(std);
} catch (IOException e) {
throw new RuntimeException("Cannot writer bundle startup script to " + bundleStartupScript, e);