Can specify JShell bundle
[lgpl/argeo-commons.git] / org.argeo.cms.jshell / src / org / argeo / cms / jshell / JShellClient.java
index c86049881e4cebe9e1c1264fa5548aeb9a5f79a9..f458c5c29d4e8559a3824a57596fbedf6c9861ef 100644 (file)
@@ -5,24 +5,23 @@ import java.io.Console;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.System.Logger;
 import java.net.StandardProtocolFamily;
 import java.net.UnixDomainSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.Channels;
+import java.nio.channels.ClosedByInterruptException;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.SocketChannel;
 import java.nio.channels.WritableByteChannel;
-import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.nio.file.StandardWatchEventKinds;
-import java.nio.file.WatchEvent;
-import java.nio.file.WatchKey;
-import java.nio.file.WatchService;
 import java.util.UUID;
 
 public class JShellClient {
+       private final static Logger logger = System.getLogger(JShellClient.class.getName());
+
        public final static String STDIO = "stdio";
        public final static String STDERR = "stderr";
        public final static String CMDIO = "cmdio";
@@ -32,6 +31,7 @@ public class JShellClient {
        public static void main(String[] args) throws IOException, InterruptedException {
                try {
                        Path targetStateDirectory = Paths.get(args[0]);
+                       String symbolicName = args[1];
                        Path localBase = targetStateDirectory.resolve("jsh");
                        if (Files.isSymbolicLink(localBase)) {
                                localBase = localBase.toRealPath();
@@ -42,44 +42,60 @@ public class JShellClient {
                                toRawTerminal();
                        }
 
-                       SocketPipeSource std = new SocketPipeSource();
-                       std.setInputStream(System.in);
-                       std.setOutputStream(System.out);
+                       SocketPipeSource stdio = new SocketPipeSource();
+                       stdio.setInputStream(System.in);
+                       stdio.setOutputStream(System.out);
+
+                       Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+                               // logger.log(Logger.Level.INFO, "Shutting down...");
+                               System.out.println("\nShutting down...");
+                               stdio.shutdown();
+                       }, "Shut down JShell client"));
 
+                       Path bundleSnDir = localBase.resolve(symbolicName);
+                       if (!Files.exists(bundleSnDir))
+                               Files.createDirectory(bundleSnDir);
                        UUID uuid = UUID.randomUUID();
-                       Path sessionDir = localBase.resolve(uuid.toString());
+                       Path sessionDir = bundleSnDir.resolve(uuid.toString());
                        Files.createDirectory(sessionDir);
-                       Path stdPath = sessionDir.resolve(JShellClient.STDIO);
-
-                       // wait for sockets to be available
-                       WatchService watchService = FileSystems.getDefault().newWatchService();
-                       sessionDir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
-                       WatchKey key;
-                       watch: while ((key = watchService.take()) != null) {
-                               for (WatchEvent<?> event : key.pollEvents()) {
-                                       Path path = sessionDir.resolve((Path) event.context());
-                                       if (Files.isSameFile(stdPath, path)) {
-                                               break watch;
-                                       }
-                               }
+                       Path stdioPath = sessionDir.resolve(JShellClient.STDIO);
+
+                       while (!(Files.exists(stdioPath))) {
+                               // TODO timeout
+                               Thread.sleep(50);
+
+//                             // wait for sockets to be available
+//                             WatchService watchService = FileSystems.getDefault().newWatchService();
+//                             sessionDir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
+//                             WatchKey key;
+//                             watch: while ((key = watchService.take()) != null) {
+//                                     for (WatchEvent<?> event : key.pollEvents()) {
+//                                             Path path = sessionDir.resolve((Path) event.context());
+//                                             if (Files.isSameFile(stdioPath, path)) {
+//                                                     break watch;
+//                                             }
+//                                     }
+//                             }
+//                             watchService.close();
                        }
-                       watchService.close();
 
-                       UnixDomainSocketAddress stdSocketAddress = UnixDomainSocketAddress.of(stdPath);
+                       UnixDomainSocketAddress stdioSocketAddress = UnixDomainSocketAddress.of(stdioPath.toRealPath());
 
-                       SocketChannel channel = SocketChannel.open(StandardProtocolFamily.UNIX);
-                       channel.connect(stdSocketAddress);
+                       try (SocketChannel stdioChannel = SocketChannel.open(StandardProtocolFamily.UNIX)) {
+                               stdioChannel.connect(stdioSocketAddress);
+                               stdio.forward(stdioChannel);
+                       }
 
-                       std.forward(channel);
                } catch (IOException | InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } finally {
-                       try {
-                               stty(ttyConfig.trim());
-                       } catch (Exception e) {
-                               System.err.println("Exception restoring tty config");
-                       }
+                       if (ttyConfig != null)
+                               try {
+                                       stty(ttyConfig.trim());
+                               } catch (Exception e) {
+                                       System.err.println("Exception restoring tty config");
+                               }
                }
 
        }
@@ -309,8 +325,10 @@ class SocketPipeSource {
        private WritableByteChannel outChannel;
 
        private Thread readOutThread;
+       private Thread forwardThread;
 
        public void forward(SocketChannel channel) throws IOException {
+               forwardThread = Thread.currentThread();
                readOutThread = new Thread(() -> {
 
                        try {
@@ -323,6 +341,8 @@ class SocketPipeSource {
                                        buffer.rewind();
                                }
                                System.exit(0);
+                       } catch (ClosedByInterruptException e) {
+                               // silent
                        } catch (IOException e) {
                                e.printStackTrace();
                        }
@@ -341,6 +361,11 @@ class SocketPipeSource {
                while (channel.isConnected()) {
                        if (inChannel.read(buffer) < 0)
                                break;
+//                     int b = (int) buffer.get(0);
+//                     if (b == 0x1B) {
+//                             System.out.println("Ctrl+C");
+//                     }
+
                        buffer.flip();
                        channel.write(buffer);
                        buffer.rewind();
@@ -356,6 +381,21 @@ class SocketPipeSource {
                }
        }
 
+       public void shutdown() {
+               try {
+                       inChannel.close();
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+               try {
+                       outChannel.close();
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+               forwardThread.interrupt();
+               readOutThread.interrupt();
+       }
+
        public void setInputStream(InputStream in) {
                inChannel = Channels.newChannel(in);
        }