X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms.jshell%2Fsrc%2Forg%2Fargeo%2Fcms%2Fjshell%2FLocalJShellSession.java;h=e77f653b7ce63a394a55546957461b498baa8119;hb=9a975983b2f3509a287dfb5751799544ec97ce70;hp=786ee272df87d23bebcb29056c40741be0974ac7;hpb=4108226e6c1bd93cf03e6b181d868caa4bf3b8d8;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms.jshell/src/org/argeo/cms/jshell/LocalJShellSession.java b/org.argeo.cms.jshell/src/org/argeo/cms/jshell/LocalJShellSession.java index 786ee272d..e77f653b7 100644 --- a/org.argeo.cms.jshell/src/org/argeo/cms/jshell/LocalJShellSession.java +++ b/org.argeo.cms.jshell/src/org/argeo/cms/jshell/LocalJShellSession.java @@ -1,17 +1,15 @@ package org.argeo.cms.jshell; -import java.io.File; +import static java.net.StandardProtocolFamily.UNIX; + import java.io.IOException; +import java.io.OutputStream; import java.io.PrintStream; -import java.net.StandardProtocolFamily; -import java.net.URI; import java.net.UnixDomainSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.StringJoiner; import java.util.UUID; import java.util.concurrent.Executors; @@ -21,41 +19,59 @@ import javax.security.auth.login.LoginException; import org.argeo.api.cms.CmsAuth; import org.argeo.api.cms.CmsLog; import org.argeo.cms.util.CurrentSubject; +import org.argeo.cms.util.FsUtils; import org.argeo.internal.cms.jshell.osgi.OsgiExecutionControlProvider; import jdk.jshell.tool.JavaShellToolBuilder; +/** A JShell session based on local UNIX sockets. */ class LocalJShellSession implements Runnable { private final static CmsLog log = CmsLog.getLog(LocalJShellSession.class); private UUID uuid; private Path sessionDir; + private Path socketsDir; - private String fromBundle = "eu.netiket.on.apaf.project.togo2023"; - - private Path stdioPath; - private Path stderrPath; - private Path cmdioPath; + private Path stdPath; + private Path ctlPath; private Thread replThread; private LoginContext loginContext; - LocalJShellSession(Path sessionDir) { - this.sessionDir = sessionDir; - this.uuid = UUID.fromString(sessionDir.getFileName().toString()); + private Long bundleId; - stdioPath = sessionDir.resolve(JShellClient.STDIO); + private final boolean interactive; - // TODO proper login + LocalJShellSession(Path sessionDir, Path bundleIdDir, boolean interactive) { + this.interactive = interactive; try { - loginContext = new LoginContext(CmsAuth.DATA_ADMIN.getLoginContextName()); - loginContext.login(); - } catch (LoginException e1) { - throw new RuntimeException("Could not login as data admin", e1); - } finally { - } + this.sessionDir = sessionDir; + this.uuid = UUID.fromString(sessionDir.getFileName().toString()); + bundleId = Long.parseLong(bundleIdDir.getFileName().toString()); + socketsDir = bundleIdDir.resolve(uuid.toString()); + Files.createDirectories(socketsDir); + + stdPath = socketsDir.resolve(JShellClient.STD); + Files.createSymbolicLink(sessionDir.resolve(JShellClient.STD), stdPath); + + ctlPath = socketsDir.resolve(JShellClient.CTL); + Files.createSymbolicLink(sessionDir.resolve(JShellClient.CTL), ctlPath); + + // TODO proper login + try { + loginContext = new LoginContext(CmsAuth.DATA_ADMIN.getLoginContextName()); + loginContext.login(); + } catch (LoginException e1) { + throw new RuntimeException("Could not login as data admin", e1); + } finally { + } + } catch (IOException e) { + log.error("Cannot initiate local session " + uuid, e); + cleanUp(); + return; + } replThread = new Thread(() -> CurrentSubject.callAs(loginContext.getSubject(), Executors.callable(this)), "JShell " + sessionDir); replThread.start(); @@ -64,42 +80,58 @@ class LocalJShellSession implements Runnable { public void run() { log.debug(() -> "Started JShell session " + sessionDir); - try (SocketPipeMirror std = new SocketPipeMirror()) { + try (SocketPipeMirror std = new SocketPipeMirror(JShellClient.STD + " " + uuid); // + SocketPipeMirror ctl = new SocketPipeMirror(JShellClient.CTL + " " + uuid);) { // prepare jshell tool builder + String feedbackMode; JavaShellToolBuilder builder = JavaShellToolBuilder.builder(); - builder.in(std.getInputStream(), null); - builder.interactiveTerminal(true); - builder.out(new PrintStream(std.getOutputStream())); + if (interactive) { + builder.in(std.getInputStream(), null); + builder.out(new PrintStream(std.getOutputStream())); + builder.err(new PrintStream(ctl.getOutputStream())); + builder.interactiveTerminal(true); + feedbackMode = "concise"; + } else { + builder.in(ctl.getInputStream(), std.getInputStream()); + PrintStream cmdOut = new PrintStream(ctl.getOutputStream()); + PrintStream discard = new PrintStream(OutputStream.nullOutputStream()); + builder.out(cmdOut, discard, new PrintStream(std.getOutputStream())); + builder.err(cmdOut); + builder.promptCapture(true); + feedbackMode = "silent"; + } - // UnixDomainSocketAddress ioSocketAddress = JSchellClient.ioSocketAddress(); - // Files.deleteIfExists(ioSocketAddress.getPath()); - UnixDomainSocketAddress stdSocketAddress = UnixDomainSocketAddress.of(stdioPath); + UnixDomainSocketAddress stdSocketAddress = UnixDomainSocketAddress.of(stdPath); + UnixDomainSocketAddress ctlSocketAddress = UnixDomainSocketAddress.of(ctlPath); - try (ServerSocketChannel stdServerChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) { + try (ServerSocketChannel stdServerChannel = ServerSocketChannel.open(UNIX); + ServerSocketChannel ctlServerChannel = ServerSocketChannel.open(UNIX);) { stdServerChannel.bind(stdSocketAddress); - try (SocketChannel channel = stdServerChannel.accept()) { - std.open(channel); - - String frameworkLocation = System.getProperty("osgi.framework"); - StringJoiner classpath = new StringJoiner(File.pathSeparator); - classpath.add(Paths.get(URI.create(frameworkLocation)).toAbsolutePath().toString()); + ctlServerChannel.bind(ctlSocketAddress); + try (SocketChannel stdChannel = stdServerChannel.accept(); + SocketChannel ctlChannel = ctlServerChannel.accept();) { + std.open(stdChannel); + ctl.open(ctlChannel); ClassLoader cmsJShellBundleCL = OsgiExecutionControlProvider.class.getClassLoader(); ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); try { + String classpath = OsgiExecutionControlProvider.getBundleClasspath(bundleId); + Path bundleStartupScript = OsgiExecutionControlProvider.getBundleStartupScript(bundleId); // we need our own class loader so that Java service loader // finds our ExecutionControlProvider implementation Thread.currentThread().setContextClassLoader(cmsJShellBundleCL); // // START JSHELL // - int exitCode = builder.start("--execution", "osgi:bundle(" + fromBundle + ")", "--class-path", - classpath.toString()); + int exitCode = builder.start("--execution", "osgi:bundle(" + bundleId + ")", "--class-path", + classpath, "--startup", bundleStartupScript.toString(), "--feedback", feedbackMode); // log.debug("JShell " + sessionDir + " completed with exit code " + exitCode); } finally { Thread.currentThread().setContextClassLoader(currentContextClassLoader); } + } finally { } } } catch (Exception e) { @@ -109,43 +141,21 @@ class LocalJShellSession implements Runnable { } } - void cleanUp() { + private void cleanUp() { try { - if (Files.exists(stdioPath)) - Files.delete(stdioPath); + if (Files.exists(socketsDir)) + FsUtils.delete(socketsDir); if (Files.exists(sessionDir)) - Files.delete(sessionDir); + FsUtils.delete(sessionDir); } catch (IOException e) { log.error("Cannot clean up JShell " + sessionDir, e); } - try { - loginContext.logout(); - } catch (LoginException e) { - log.error("Cannot log out JShell " + sessionDir, e); - } + if (loginContext != null) + try { + loginContext.logout(); + } catch (LoginException e) { + log.error("Cannot log out JShell " + sessionDir, e); + } } - -// void addChild(Path p) throws IOException { -// if (replThread != null) -// throw new IllegalStateException("JShell " + sessionDir + " is already started"); -// -// if (STDIO.equals(p.getFileName().toString())) { -// stdioPath = p; -// } else if (STDERR.equals(p.getFileName().toString())) { -// stderrPath = p; -// } else if (CMDIO.equals(p.getFileName().toString())) { -// cmdioPath = p; -// } else { -// log.warn("Unkown file name " + p.getFileName() + " in " + sessionDir); -// } -// -// // check that all paths are available -// // if (stdioPath != null && stderrPath != null && cmdioPath != null) { -// if (stdioPath != null) { -// replThread = new Thread(this, "JShell " + sessionDir); -// replThread.start(); -// } -// } - }