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
.FsUtils
;
20 import org
.argeo
.cms
.util
.OS
;
21 import org
.argeo
.internal
.cms
.jshell
.osgi
.OsgiExecutionControlProvider
;
22 import org
.osgi
.framework
.Bundle
;
24 /** A factory for JShell sessions. */
25 public class CmsJShell
{
26 private final static CmsLog log
= CmsLog
.getLog(CmsJShell
.class);
27 static ClassLoader loader
= CmsJShell
.class.getClassLoader();
29 public static UuidFactory uuidFactory
= null;
31 private CmsState cmsState
;
33 private Map
<Path
, LocalJShellSession
> localSessions
= new HashMap
<>();
34 private Map
<Path
, Path
> bundleDirs
= new HashMap
<>();
36 private Path stateRunDir
;
38 private Path jshLinkedDir
;
39 private Path jtermBase
;
40 private Path jtermLinkedDir
;
42 public void start() throws Exception
{
43 // TODO better define application id, make it configurable
45 if (Files
.exists(cmsState
.getStatePath("dev.properties"))) { // in Eclipse
46 applicationID
= cmsState
.getStatePath("").getFileName().toString();
48 applicationID
= cmsState
.getStatePath("").getParent().getFileName().toString();
51 // TODO centralise state run dir
52 stateRunDir
= OS
.getRunDir().resolve(applicationID
);
54 jshBase
= stateRunDir
.resolve(JShellClient
.JSH
);
55 Files
.createDirectories(jshBase
);
56 jshLinkedDir
= Files
.createSymbolicLink(cmsState
.getStatePath(JShellClient
.JSH
), jshBase
);
58 jtermBase
= stateRunDir
.resolve(JShellClient
.JTERM
);
59 Files
.createDirectories(jtermBase
);
60 jtermLinkedDir
= Files
.createSymbolicLink(cmsState
.getStatePath(JShellClient
.JTERM
), jtermBase
);
62 log
.info("Local JShell on " + jshBase
+ ", linked to " + jshLinkedDir
);
63 log
.info("Local JTerm on " + jtermBase
+ ", linked to " + jtermLinkedDir
);
67 WatchService watchService
= FileSystems
.getDefault().newWatchService();
69 jshBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
70 StandardWatchEventKinds
.ENTRY_DELETE
);
71 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(jshBase
)) {
72 for (Path bundleSnDir
: bundleSns
) {
73 addBundleSnDir(bundleSnDir
, watchService
);
76 jtermBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
77 StandardWatchEventKinds
.ENTRY_DELETE
);
78 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(jtermBase
)) {
79 for (Path bundleSnDir
: bundleSns
) {
80 addBundleSnDir(bundleSnDir
, watchService
);
85 while ((key
= watchService
.take()) != null) {
86 events
: for (WatchEvent
<?
> event
: key
.pollEvents()) {
87 // System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + ".");
88 Path parent
= (Path
) key
.watchable();
90 if (Files
.isSameFile(jshBase
, parent
)) {
91 Path bundleSnDir
= jshBase
.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())) {
96 } else if (Files
.isSameFile(jtermBase
, parent
)) {
97 Path bundleSnDir
= jtermBase
.resolve((Path
) event
.context());
98 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
99 addBundleSnDir(bundleSnDir
, watchService
);
100 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
103 Path path
= parent
.resolve((Path
) event
.context());
104 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
105 if (!Files
.isDirectory(path
)) {
106 log
.warn("Ignoring " + path
+ " as it is not a directory");
110 UUID
.fromString(path
.getFileName().toString());
111 } catch (IllegalArgumentException e
) {
112 log
.warn("Ignoring " + path
+ " as it is not named as UUID");
117 if (Files
.isSameFile(jshBase
, parent
.getParent())) {
119 } else if (Files
.isSameFile(jtermBase
, parent
.getParent())) {
122 log
.warn("Ignoring " + path
+ " as we don't know whether it is interactive or not");
125 Path bundleIdDir
= bundleDirs
.get(parent
);
126 LocalJShellSession localSession
= new LocalJShellSession(path
, bundleIdDir
,
128 localSessions
.put(path
, localSession
);
129 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
130 localSessions
.remove(path
);
136 } catch (IOException
| InterruptedException e
) {
139 }, "JShell local sessions watcher").start();
142 private void addBundleSnDir(Path bundleSnDir
, WatchService watchService
) throws IOException
{
143 String symbolicName
= bundleSnDir
.getFileName().toString();
144 Bundle fromBundle
= OsgiExecutionControlProvider
.getBundleFromSn(symbolicName
);
145 if (fromBundle
== null) {
146 log
.error("Removing directory for bundle " + symbolicName
+ " because it was not found in runtime...");
147 FsUtils
.delete(bundleSnDir
);
150 Long bundleId
= fromBundle
.getBundleId();
151 Path bundleIdDir
= stateRunDir
.resolve(bundleId
.toString());
152 Files
.createDirectories(bundleIdDir
);
153 bundleDirs
.put(bundleSnDir
, bundleIdDir
);
155 bundleSnDir
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
, StandardWatchEventKinds
.ENTRY_DELETE
);
160 Files
.delete(jshLinkedDir
);
161 } catch (IOException e
) {
162 log
.error("Cannot remove " + jshLinkedDir
);
165 Files
.delete(jtermLinkedDir
);
166 } catch (IOException e
) {
167 log
.error("Cannot remove " + jtermLinkedDir
);
171 public void setCmsState(CmsState cmsState
) {
172 this.cmsState
= cmsState
;