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
44 if (Files
.exists(cmsState
.getStatePath("dev.properties"))) { // in Eclipse
45 applicationID
= cmsState
.getStatePath("").getFileName().toString();
47 applicationID
= cmsState
.getStatePath("").getParent().getFileName().toString();
50 // TODO centralise state run dir
51 stateRunDir
= OS
.getRunDir().resolve(applicationID
);
53 jshBase
= stateRunDir
.resolve(JShellClient
.JSH
);
54 Files
.createDirectories(jshBase
);
55 jshLinkedDir
= Files
.createSymbolicLink(cmsState
.getStatePath(JShellClient
.JSH
), jshBase
);
57 jtermBase
= stateRunDir
.resolve(JShellClient
.JTERM
);
58 Files
.createDirectories(jtermBase
);
59 jtermLinkedDir
= Files
.createSymbolicLink(cmsState
.getStatePath(JShellClient
.JTERM
), jtermBase
);
61 log
.info("Local JShell on " + jshBase
+ ", linked to " + jshLinkedDir
);
62 log
.info("Local JTerm on " + jtermBase
+ ", linked to " + jtermLinkedDir
);
66 WatchService watchService
= FileSystems
.getDefault().newWatchService();
68 jshBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
69 StandardWatchEventKinds
.ENTRY_DELETE
);
70 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(jshBase
)) {
71 for (Path bundleSnDir
: bundleSns
) {
72 addBundleSnDir(bundleSnDir
, watchService
);
75 jtermBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
76 StandardWatchEventKinds
.ENTRY_DELETE
);
77 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(jtermBase
)) {
78 for (Path bundleSnDir
: bundleSns
) {
79 addBundleSnDir(bundleSnDir
, watchService
);
84 while ((key
= watchService
.take()) != null) {
85 events
: for (WatchEvent
<?
> event
: key
.pollEvents()) {
86 // System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + ".");
87 Path parent
= (Path
) key
.watchable();
89 if (Files
.isSameFile(jshBase
, parent
)) {
90 Path bundleSnDir
= jshBase
.resolve((Path
) event
.context());
91 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
92 addBundleSnDir(bundleSnDir
, watchService
);
93 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
95 } else if (Files
.isSameFile(jtermBase
, parent
)) {
96 Path bundleSnDir
= jtermBase
.resolve((Path
) event
.context());
97 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
98 addBundleSnDir(bundleSnDir
, watchService
);
99 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
102 Path path
= parent
.resolve((Path
) event
.context());
103 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
104 if (!Files
.isDirectory(path
)) {
105 log
.warn("Ignoring " + path
+ " as it is not a directory");
109 UUID
.fromString(path
.getFileName().toString());
110 } catch (IllegalArgumentException e
) {
111 log
.warn("Ignoring " + path
+ " as it is not named as UUID");
116 if (Files
.isSameFile(jshBase
, parent
.getParent())) {
118 } else if (Files
.isSameFile(jtermBase
, parent
.getParent())) {
121 log
.warn("Ignoring " + path
+ " as we don't know whether it is interactive or not");
124 Path bundleIdDir
= bundleDirs
.get(parent
);
125 LocalJShellSession localSession
= new LocalJShellSession(path
, bundleIdDir
,
127 localSessions
.put(path
, localSession
);
128 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
129 localSessions
.remove(path
);
135 } catch (IOException
| InterruptedException e
) {
138 }, "JShell local sessions watcher").start();
141 private void addBundleSnDir(Path bundleSnDir
, WatchService watchService
) throws IOException
{
142 String symbolicName
= bundleSnDir
.getFileName().toString();
143 Bundle fromBundle
= OsgiExecutionControlProvider
.getBundleFromSn(symbolicName
);
144 if (fromBundle
== null) {
145 log
.error("Ignoring bundle " + symbolicName
+ " because it was not found");
148 Long bundleId
= fromBundle
.getBundleId();
149 Path bundleIdDir
= stateRunDir
.resolve(bundleId
.toString());
150 Files
.createDirectories(bundleIdDir
);
151 bundleDirs
.put(bundleSnDir
, bundleIdDir
);
153 bundleSnDir
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
, StandardWatchEventKinds
.ENTRY_DELETE
);
158 Files
.delete(jshLinkedDir
);
159 } catch (IOException e
) {
160 log
.error("Cannot remove " + jshLinkedDir
);
163 Files
.delete(jtermLinkedDir
);
164 } catch (IOException e
) {
165 log
.error("Cannot remove " + jtermLinkedDir
);
169 public void setCmsState(CmsState cmsState
) {
170 this.cmsState
= cmsState
;