1 package org
.argeo
.cms
.jshell
;
3 import java
.io
.IOException
;
4 import java
.nio
.file
.DirectoryStream
;
5 import java
.nio
.file
.FileSystems
;
6 import java
.nio
.file
.Files
;
7 import java
.nio
.file
.Path
;
8 import java
.nio
.file
.StandardWatchEventKinds
;
9 import java
.nio
.file
.WatchEvent
;
10 import java
.nio
.file
.WatchKey
;
11 import java
.nio
.file
.WatchService
;
12 import java
.util
.HashMap
;
14 import java
.util
.UUID
;
16 import org
.argeo
.api
.cms
.CmsLog
;
17 import org
.argeo
.api
.cms
.CmsState
;
18 import org
.argeo
.api
.uuid
.UuidFactory
;
19 import org
.argeo
.cms
.util
.OS
;
20 import org
.argeo
.internal
.cms
.jshell
.osgi
.OsgiExecutionControlProvider
;
21 import org
.osgi
.framework
.Bundle
;
23 public class CmsJShell
{
24 private final static CmsLog log
= CmsLog
.getLog(CmsJShell
.class);
25 static ClassLoader loader
= CmsJShell
.class.getClassLoader();
27 public static UuidFactory uuidFactory
= null;
29 private CmsState cmsState
;
31 private Map
<Path
, LocalJShellSession
> localSessions
= new HashMap
<>();
32 private Map
<Path
, Path
> bundleDirs
= new HashMap
<>();
34 private Path stateRunDir
;
35 private Path localBase
;
36 private Path linkedDir
;
38 // private String defaultBundle = "org.argeo.cms.cli";
40 public void start() throws Exception
{
42 // Path localBase = cmsState.getStatePath("org.argeo.cms.jshell/local");
43 // UUID stateUuid = cmsState.getUuid();
45 // TODO better define application id, make it configurable
46 String applicationID
= cmsState
.getStatePath("").getFileName().toString();
48 // TODO centralise state run dir
49 stateRunDir
= OS
.getRunDir().resolve(applicationID
);
50 localBase
= stateRunDir
.resolve("jsh");
51 Files
.createDirectories(localBase
);
53 linkedDir
= Files
.createSymbolicLink(cmsState
.getStatePath("jsh"), localBase
);
55 log
.info("Local JShell on " + localBase
+ ", linked to " + linkedDir
);
59 WatchService watchService
= FileSystems
.getDefault().newWatchService();
61 localBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
62 StandardWatchEventKinds
.ENTRY_DELETE
);
63 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(localBase
)) {
64 for (Path bundleSnDir
: bundleSns
) {
65 addBundleSnDir(bundleSnDir
);
66 if (bundleDirs
.containsKey(bundleSnDir
)) {
67 bundleSnDir
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
68 StandardWatchEventKinds
.ENTRY_DELETE
);
74 while ((key
= watchService
.take()) != null) {
75 events
: for (WatchEvent
<?
> event
: key
.pollEvents()) {
76 // System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + ".");
77 Path parent
= (Path
) key
.watchable();
79 if (Files
.isSameFile(localBase
, parent
)) {
80 Path bundleSnDir
= localBase
.resolve((Path
) event
.context());
81 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
82 addBundleSnDir(bundleSnDir
);
83 if (bundleDirs
.containsKey(bundleSnDir
)) {
84 bundleSnDir
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
85 StandardWatchEventKinds
.ENTRY_DELETE
);
87 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
90 Path path
= parent
.resolve((Path
) event
.context());
91 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
92 if (!Files
.isDirectory(path
)) {
93 log
.warn("Ignoring " + path
+ " as it is not a directory");
97 UUID
.fromString(path
.getFileName().toString());
98 } catch (IllegalArgumentException e
) {
99 log
.warn("Ignoring " + path
+ " as it is not named as UUID");
103 Path bundleIdDir
= bundleDirs
.get(parent
);
104 LocalJShellSession localSession
= new LocalJShellSession(path
, bundleIdDir
);
105 localSessions
.put(path
, localSession
);
106 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
107 // TODO clean up session
108 LocalJShellSession localSession
= localSessions
.remove(path
);
109 localSession
.cleanUp();
115 } catch (IOException
| InterruptedException e
) {
118 }, "JShell local sessions watcher").start();
120 // thread context class loader should be where the service is defined
121 // Thread.currentThread().setContextClassLoader(loader);
122 // JavaShellToolBuilder builder = JavaShellToolBuilder.builder();
124 // builder.start("--execution", "osgi:bundle(org.argeo.cms.jshell)");
128 private void addBundleSnDir(Path bundleSnDir
) throws IOException
{
129 String symbolicName
= bundleSnDir
.getFileName().toString();
130 Bundle fromBundle
= OsgiExecutionControlProvider
.getBundleFromSn(symbolicName
);
131 if (fromBundle
== null) {
132 log
.error("Ignoring bundle " + symbolicName
+ " because it was not found");
135 Long bundleId
= fromBundle
.getBundleId();
136 Path bundleIdDir
= stateRunDir
.resolve(bundleId
.toString());
137 Files
.createDirectories(bundleIdDir
);
138 bundleDirs
.put(bundleSnDir
, bundleIdDir
);
141 // public void startX(BundleContext bc) {
142 // uuidFactory = new NoOpUuidFactory();
144 // List<String> locations = new ArrayList<>();
145 // for (Bundle bundle : bc.getBundles()) {
146 // locations.add(bundle.getLocation());
147 //// System.out.println(bundle.getLocation());
150 // CmsState cmsState = (CmsState) bc.getService(bc.getServiceReference("org.argeo.api.cms.CmsState"));
151 // System.out.println(cmsState.getDeployProperties(CmsDeployProperty.HTTP_PORT.getProperty()));
152 // System.out.println(cmsState.getUuid());
154 // ExecutionControlProvider executionControlProvider = new ExecutionControlProvider() {
156 // public String name() {
161 // public ExecutionControl generate(ExecutionEnv ee, Map<String, String> map) throws Throwable {
162 // return new LocalExecutionControl(new WrappingLoaderDelegate(loader));
163 //// Thread.currentThread().setContextClassLoader(loader);
164 //// return new DirectExecutionControl();
168 //// Thread.currentThread().setContextClassLoader(loader);
170 // try (JShell js = JShell.builder().executionEngine(executionControlProvider, null).build()) {
171 // js.addToClasspath("/home/mbaudier/dev/git/unstable/output/a2/org.argeo.cms/org.argeo.api.cms.2.3.jar");
172 // js.addToClasspath("/home/mbaudier/dev/git/unstable/output/a2/org.argeo.cms/org.argeo.cms.2.3.jar");
173 // js.addToClasspath(
174 // "/home/mbaudier/dev/git/unstable/output/a2/osgi/equinox/org.argeo.tp.osgi/org.eclipse.osgi.3.18.jar");
176 // System.out.print("Enter some Java code: ");
177 // // String input = console.readLine();
178 // String imports = """
179 // import org.argeo.api.cms.*;
180 // import org.argeo.cms.*;
181 // import org.argeo.slc.jshell.*;
184 // String input = """
185 // var bc = org.osgi.framework.FrameworkUtil.getBundle(org.argeo.cms.CmsDeployProperty.class).getBundleContext();
186 // var cmsState =(org.argeo.api.cms.CmsState) bc.getService(bc.getServiceReference("org.argeo.api.cms.CmsState"));
187 // System.out.println(cmsState.getDeployProperties(org.argeo.cms.CmsDeployProperty.HTTP_PORT.getProperty()));
188 // cmsState.getUuid();
190 //// if (input == null) {
194 // input.lines().forEach((l) -> {
196 // List<SnippetEvent> events = js.eval(l);
197 // for (SnippetEvent e : events) {
198 // StringBuilder sb = new StringBuilder();
199 // if (e.causeSnippet() == null) {
200 // // We have a snippet creation event
201 // switch (e.status()) {
203 // sb.append("Successful ");
205 // case RECOVERABLE_DEFINED:
206 // sb.append("With unresolved references ");
208 // case RECOVERABLE_NOT_DEFINED:
209 // sb.append("Possibly reparable, failed ");
212 // sb.append("Failed ");
215 // if (e.previousStatus() == Status.NONEXISTENT) {
216 // sb.append("addition");
218 // sb.append("modification");
220 // sb.append(" of ");
221 // sb.append(e.snippet().source());
222 // System.out.println(sb);
223 // if (e.value() != null) {
224 // System.out.printf("Value is: %s\n", e.value());
226 // System.out.flush();
236 Files
.delete(linkedDir
);
237 } catch (IOException e
) {
238 log
.error("Cannot remove " + linkedDir
);
242 public void setCmsState(CmsState cmsState
) {
243 this.cmsState
= cmsState
;
246 // public static void main(String[] args) throws Exception {
247 // Pipe inPipe = Pipe.open();
248 // Pipe outPipe = Pipe.open();
250 // InputStream in = Channels.newInputStream(inPipe.source());
251 // OutputStream out = Channels.newOutputStream(outPipe.sink());
252 // JavaShellToolBuilder builder = JavaShellToolBuilder.builder();
253 // builder.in(in, null);
254 // builder.interactiveTerminal(true);
255 // builder.out(new PrintStream(out));
257 // UnixDomainSocketAddress ioSocketAddress = JShellClient.ioSocketAddress();
258 // Files.deleteIfExists(ioSocketAddress.getPath());
260 // try (ServerSocketChannel serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
261 // serverChannel.bind(ioSocketAddress);
263 // try (SocketChannel channel = serverChannel.accept()) {
264 // new Thread(() -> {
267 // ByteBuffer buffer = ByteBuffer.allocate(1024);
269 // if (channel.read(buffer) < 0)
272 // inPipe.sink().write(buffer);
275 // } catch (IOException e) {
276 // e.printStackTrace();
278 // }, "Read in").start();
280 // new Thread(() -> {
283 // ByteBuffer buffer = ByteBuffer.allocate(1024);
285 // if (outPipe.source().read(buffer) < 0)
288 // channel.write(buffer);
291 // } catch (IOException e) {
292 // e.printStackTrace();
294 // }, "Write out").start();
299 // System.out.println("Completed");