]> git.argeo.org Git - lgpl/argeo-commons.git/blob - LocalJShellSession.java
786ee272df87d23bebcb29056c40741be0974ac7
[lgpl/argeo-commons.git] / LocalJShellSession.java
1 package org.argeo.cms.jshell;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.PrintStream;
6 import java.net.StandardProtocolFamily;
7 import java.net.URI;
8 import java.net.UnixDomainSocketAddress;
9 import java.nio.channels.ServerSocketChannel;
10 import java.nio.channels.SocketChannel;
11 import java.nio.file.Files;
12 import java.nio.file.Path;
13 import java.nio.file.Paths;
14 import java.util.StringJoiner;
15 import java.util.UUID;
16 import java.util.concurrent.Executors;
17
18 import javax.security.auth.login.LoginContext;
19 import javax.security.auth.login.LoginException;
20
21 import org.argeo.api.cms.CmsAuth;
22 import org.argeo.api.cms.CmsLog;
23 import org.argeo.cms.util.CurrentSubject;
24 import org.argeo.internal.cms.jshell.osgi.OsgiExecutionControlProvider;
25
26 import jdk.jshell.tool.JavaShellToolBuilder;
27
28 class LocalJShellSession implements Runnable {
29 private final static CmsLog log = CmsLog.getLog(LocalJShellSession.class);
30
31 private UUID uuid;
32 private Path sessionDir;
33
34 private String fromBundle = "eu.netiket.on.apaf.project.togo2023";
35
36 private Path stdioPath;
37 private Path stderrPath;
38 private Path cmdioPath;
39
40 private Thread replThread;
41
42 private LoginContext loginContext;
43
44 LocalJShellSession(Path sessionDir) {
45 this.sessionDir = sessionDir;
46 this.uuid = UUID.fromString(sessionDir.getFileName().toString());
47
48 stdioPath = sessionDir.resolve(JShellClient.STDIO);
49
50 // TODO proper login
51 try {
52 loginContext = new LoginContext(CmsAuth.DATA_ADMIN.getLoginContextName());
53 loginContext.login();
54 } catch (LoginException e1) {
55 throw new RuntimeException("Could not login as data admin", e1);
56 } finally {
57 }
58
59 replThread = new Thread(() -> CurrentSubject.callAs(loginContext.getSubject(), Executors.callable(this)),
60 "JShell " + sessionDir);
61 replThread.start();
62 }
63
64 public void run() {
65
66 log.debug(() -> "Started JShell session " + sessionDir);
67 try (SocketPipeMirror std = new SocketPipeMirror()) {
68 // prepare jshell tool builder
69 JavaShellToolBuilder builder = JavaShellToolBuilder.builder();
70 builder.in(std.getInputStream(), null);
71 builder.interactiveTerminal(true);
72 builder.out(new PrintStream(std.getOutputStream()));
73
74 // UnixDomainSocketAddress ioSocketAddress = JSchellClient.ioSocketAddress();
75 // Files.deleteIfExists(ioSocketAddress.getPath());
76 UnixDomainSocketAddress stdSocketAddress = UnixDomainSocketAddress.of(stdioPath);
77
78 try (ServerSocketChannel stdServerChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
79 stdServerChannel.bind(stdSocketAddress);
80 try (SocketChannel channel = stdServerChannel.accept()) {
81 std.open(channel);
82
83 String frameworkLocation = System.getProperty("osgi.framework");
84 StringJoiner classpath = new StringJoiner(File.pathSeparator);
85 classpath.add(Paths.get(URI.create(frameworkLocation)).toAbsolutePath().toString());
86
87 ClassLoader cmsJShellBundleCL = OsgiExecutionControlProvider.class.getClassLoader();
88 ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
89 try {
90 // we need our own class loader so that Java service loader
91 // finds our ExecutionControlProvider implementation
92 Thread.currentThread().setContextClassLoader(cmsJShellBundleCL);
93 //
94 // START JSHELL
95 //
96 int exitCode = builder.start("--execution", "osgi:bundle(" + fromBundle + ")", "--class-path",
97 classpath.toString());
98 //
99 log.debug("JShell " + sessionDir + " completed with exit code " + exitCode);
100 } finally {
101 Thread.currentThread().setContextClassLoader(currentContextClassLoader);
102 }
103 }
104 }
105 } catch (Exception e) {
106 throw new RuntimeException("JShell " + sessionDir + " failed", e);
107 } finally {
108 cleanUp();
109 }
110 }
111
112 void cleanUp() {
113 try {
114 if (Files.exists(stdioPath))
115 Files.delete(stdioPath);
116 if (Files.exists(sessionDir))
117 Files.delete(sessionDir);
118 } catch (IOException e) {
119 log.error("Cannot clean up JShell " + sessionDir, e);
120 }
121
122 try {
123 loginContext.logout();
124 } catch (LoginException e) {
125 log.error("Cannot log out JShell " + sessionDir, e);
126 }
127 }
128
129 // void addChild(Path p) throws IOException {
130 // if (replThread != null)
131 // throw new IllegalStateException("JShell " + sessionDir + " is already started");
132 //
133 // if (STDIO.equals(p.getFileName().toString())) {
134 // stdioPath = p;
135 // } else if (STDERR.equals(p.getFileName().toString())) {
136 // stderrPath = p;
137 // } else if (CMDIO.equals(p.getFileName().toString())) {
138 // cmdioPath = p;
139 // } else {
140 // log.warn("Unkown file name " + p.getFileName() + " in " + sessionDir);
141 // }
142 //
143 // // check that all paths are available
144 // // if (stdioPath != null && stderrPath != null && cmdioPath != null) {
145 // if (stdioPath != null) {
146 // replThread = new Thread(this, "JShell " + sessionDir);
147 // replThread.start();
148 // }
149 // }
150
151 }