From: Mathieu Baudier Date: Tue, 9 May 2023 09:45:23 +0000 (+0200) Subject: JShell improvements X-Git-Tag: v2.3.16~7 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=53682708bfc0daecb0359d7f2af8415d986b72e4;p=lgpl%2Fargeo-commons.git JShell improvements --- diff --git a/org.argeo.cms.jshell/bnd.bnd b/org.argeo.cms.jshell/bnd.bnd index e575175a8..5f4b47a62 100644 --- a/org.argeo.cms.jshell/bnd.bnd +++ b/org.argeo.cms.jshell/bnd.bnd @@ -1,3 +1,5 @@ +Main-Class: org.argeo.cms.jshell.JShellClient + Import-Package: \ org.osgi.framework.namespace, \ * diff --git a/org.argeo.cms.jshell/src/org/argeo/cms/jshell/CmsExecutionControl.java b/org.argeo.cms.jshell/src/org/argeo/cms/jshell/CmsExecutionControl.java new file mode 100644 index 000000000..5cd575c32 --- /dev/null +++ b/org.argeo.cms.jshell/src/org/argeo/cms/jshell/CmsExecutionControl.java @@ -0,0 +1,29 @@ +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(); + } + +} diff --git a/org.argeo.cms.jshell/src/org/argeo/cms/jshell/JShellClient.java b/org.argeo.cms.jshell/src/org/argeo/cms/jshell/JShellClient.java index f6846f196..87e88b77f 100644 --- a/org.argeo.cms.jshell/src/org/argeo/cms/jshell/JShellClient.java +++ b/org.argeo.cms.jshell/src/org/argeo/cms/jshell/JShellClient.java @@ -16,6 +16,7 @@ import java.lang.management.ManagementFactory; 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; @@ -30,6 +31,7 @@ import java.util.List; 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()); @@ -97,7 +99,7 @@ public class JShellClient { ctl.setOutputStream(System.err); Runtime.getRuntime().addShutdownHook(new Thread(() -> { - System.out.println("\nShutting down..."); +// System.out.println("\nShutting down..."); toOriginalTerminal(); std.shutdown(); ctl.shutdown(); @@ -258,7 +260,6 @@ public class JShellClient { 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) @@ -267,30 +268,15 @@ public class JShellClient { 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"); @@ -299,6 +285,7 @@ public class JShellClient { } } + /** Not optimal, but performance is not critical here. */ private void writeLine(Object obj) throws IOException { channel.write(ByteBuffer.wrap((obj + "\n").getBytes(UTF_8))); } @@ -346,6 +333,8 @@ class SocketPipeSource { } } catch (ClosedByInterruptException e) { // silent + } catch (AsynchronousCloseException e) { + // silent } catch (IOException e) { e.printStackTrace(); } @@ -366,8 +355,11 @@ class SocketPipeSource { 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"); @@ -423,9 +415,9 @@ class SocketPipeSource { } catch (IOException e) { e.printStackTrace(); } - if (inChannel != null) - forwardThread.interrupt(); - readThread.interrupt(); +// if (inChannel != null) +// forwardThread.interrupt(); +// readThread.interrupt(); } public void setInputStream(InputStream in) { diff --git a/org.argeo.cms.jshell/src/org/argeo/cms/jshell/SocketPipeMirror.java b/org.argeo.cms.jshell/src/org/argeo/cms/jshell/SocketPipeMirror.java index 7a2332bed..1e6c9deab 100644 --- a/org.argeo.cms.jshell/src/org/argeo/cms/jshell/SocketPipeMirror.java +++ b/org.argeo.cms.jshell/src/org/argeo/cms/jshell/SocketPipeMirror.java @@ -36,15 +36,16 @@ class SocketPipeMirror implements Closeable { 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(); } @@ -56,8 +57,10 @@ class SocketPipeMirror implements Closeable { 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(); diff --git a/org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java b/org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java index 80acd5551..a98326c30 100644 --- a/org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java +++ b/org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java @@ -19,6 +19,7 @@ import java.util.TreeMap; 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; @@ -28,7 +29,6 @@ import org.osgi.framework.wiring.BundleRevision; 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; @@ -53,24 +53,15 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider { @Override public ExecutionControl generate(ExecutionEnv env, Map 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; } @@ -123,7 +114,9 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider { String std = """ import jdk.jshell.spi.ExecutionEnv; - InputStream STDIN = new Supplier() { + /** Redirected standard IO. */ + public class Std { + final static InputStream in = new Supplier() { @Override public InputStream get() { @@ -131,7 +124,7 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider { } }.get(); - PrintStream STDOUT = new Supplier() { + final static PrintStream out = new Supplier() { @Override public PrintStream get() { @@ -139,7 +132,7 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider { } }.get(); - PrintStream STDERR = new Supplier() { + final static PrintStream err = new Supplier() { @Override public PrintStream get() { @@ -147,7 +140,9 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider { } }.get(); - """; + + } + """; writer.write(std); } catch (IOException e) { throw new RuntimeException("Cannot writer bundle startup script to " + bundleStartupScript, e);