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 /** A factory for JShell sessions. */
24 public class CmsJShell
{
25 private final static CmsLog log
= CmsLog
.getLog(CmsJShell
.class);
26 static ClassLoader loader
= CmsJShell
.class.getClassLoader();
28 public static UuidFactory uuidFactory
= null;
30 private CmsState cmsState
;
32 private Map
<Path
, LocalJShellSession
> localSessions
= new HashMap
<>();
33 private Map
<Path
, Path
> bundleDirs
= new HashMap
<>();
35 private Path stateRunDir
;
37 private Path jshLinkedDir
;
38 private Path jtermBase
;
39 private Path jtermLinkedDir
;
41 public void start() throws Exception
{
42 // TODO better define application id, make it configurable
43 String applicationID
= cmsState
.getStatePath("").getFileName().toString();
45 // TODO centralise state run dir
46 stateRunDir
= OS
.getRunDir().resolve(applicationID
);
48 jshBase
= stateRunDir
.resolve(JShellClient
.JSH
);
49 Files
.createDirectories(jshBase
);
50 jshLinkedDir
= Files
.createSymbolicLink(cmsState
.getStatePath(JShellClient
.JSH
), jshBase
);
52 jtermBase
= stateRunDir
.resolve(JShellClient
.JTERM
);
53 Files
.createDirectories(jtermBase
);
54 jtermLinkedDir
= Files
.createSymbolicLink(cmsState
.getStatePath(JShellClient
.JTERM
), jtermBase
);
56 log
.info("Local JShell on " + jshBase
+ ", linked to " + jshLinkedDir
);
57 log
.info("Local JTerm on " + jtermBase
+ ", linked to " + jtermLinkedDir
);
61 WatchService watchService
= FileSystems
.getDefault().newWatchService();
63 jshBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
64 StandardWatchEventKinds
.ENTRY_DELETE
);
65 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(jshBase
)) {
66 for (Path bundleSnDir
: bundleSns
) {
67 addBundleSnDir(bundleSnDir
, watchService
);
70 jtermBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
71 StandardWatchEventKinds
.ENTRY_DELETE
);
72 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(jtermBase
)) {
73 for (Path bundleSnDir
: bundleSns
) {
74 addBundleSnDir(bundleSnDir
, watchService
);
79 while ((key
= watchService
.take()) != null) {
80 events
: for (WatchEvent
<?
> event
: key
.pollEvents()) {
81 // System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + ".");
82 Path parent
= (Path
) key
.watchable();
84 if (Files
.isSameFile(jshBase
, parent
)) {
85 Path bundleSnDir
= jshBase
.resolve((Path
) event
.context());
86 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
87 addBundleSnDir(bundleSnDir
, watchService
);
88 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
90 } else if (Files
.isSameFile(jtermBase
, parent
)) {
91 Path bundleSnDir
= jtermBase
.resolve((Path
) event
.context());
92 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
93 addBundleSnDir(bundleSnDir
, watchService
);
94 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
97 Path path
= parent
.resolve((Path
) event
.context());
98 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
99 if (!Files
.isDirectory(path
)) {
100 log
.warn("Ignoring " + path
+ " as it is not a directory");
104 UUID
.fromString(path
.getFileName().toString());
105 } catch (IllegalArgumentException e
) {
106 log
.warn("Ignoring " + path
+ " as it is not named as UUID");
111 if (Files
.isSameFile(jshBase
, parent
.getParent())) {
113 } else if (Files
.isSameFile(jtermBase
, parent
.getParent())) {
116 log
.warn("Ignoring " + path
+ " as we don't know whether it is interactive or not");
119 Path bundleIdDir
= bundleDirs
.get(parent
);
120 LocalJShellSession localSession
= new LocalJShellSession(path
, bundleIdDir
,
122 localSessions
.put(path
, localSession
);
123 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
124 localSessions
.remove(path
);
130 } catch (IOException
| InterruptedException e
) {
133 }, "JShell local sessions watcher").start();
136 private void addBundleSnDir(Path bundleSnDir
, WatchService watchService
) throws IOException
{
137 String symbolicName
= bundleSnDir
.getFileName().toString();
138 Bundle fromBundle
= OsgiExecutionControlProvider
.getBundleFromSn(symbolicName
);
139 if (fromBundle
== null) {
140 log
.error("Ignoring bundle " + symbolicName
+ " because it was not found");
143 Long bundleId
= fromBundle
.getBundleId();
144 Path bundleIdDir
= stateRunDir
.resolve(bundleId
.toString());
145 Files
.createDirectories(bundleIdDir
);
146 bundleDirs
.put(bundleSnDir
, bundleIdDir
);
148 bundleSnDir
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
, StandardWatchEventKinds
.ENTRY_DELETE
);
153 Files
.delete(jshLinkedDir
);
154 } catch (IOException e
) {
155 log
.error("Cannot remove " + jshLinkedDir
);
159 public void setCmsState(CmsState cmsState
) {
160 this.cmsState
= cmsState
;