1 package org
.argeo
.cms
.jshell
;
3 import java
.io
.IOException
;
4 import java
.nio
.file
.ClosedWatchServiceException
;
5 import java
.nio
.file
.DirectoryStream
;
6 import java
.nio
.file
.FileSystems
;
7 import java
.nio
.file
.Files
;
8 import java
.nio
.file
.Path
;
9 import java
.nio
.file
.StandardWatchEventKinds
;
10 import java
.nio
.file
.WatchEvent
;
11 import java
.nio
.file
.WatchKey
;
12 import java
.nio
.file
.WatchService
;
13 import java
.util
.HashMap
;
15 import java
.util
.UUID
;
17 import org
.argeo
.api
.cms
.CmsLog
;
18 import org
.argeo
.api
.cms
.CmsState
;
19 import org
.argeo
.api
.uuid
.UuidFactory
;
20 import org
.argeo
.cms
.util
.FsUtils
;
21 import org
.argeo
.cms
.util
.OS
;
22 import org
.argeo
.internal
.cms
.jshell
.osgi
.OsgiExecutionControlProvider
;
23 import org
.osgi
.framework
.Bundle
;
25 /** A factory for JShell sessions. */
26 public class CmsJShell
{
27 private final static CmsLog log
= CmsLog
.getLog(CmsJShell
.class);
28 static ClassLoader loader
= CmsJShell
.class.getClassLoader();
30 public static UuidFactory uuidFactory
= null;
32 private CmsState cmsState
;
34 private Map
<Path
, LocalJShellSession
> localSessions
= new HashMap
<>();
35 private Map
<Path
, Path
> bundleDirs
= new HashMap
<>();
37 private Path stateRunDir
;
39 private Path jshLinkedDir
;
40 private Path jtermBase
;
41 private Path jtermLinkedDir
;
43 private WatchService watchService
;
45 public void start() throws Exception
{
46 // TODO better define application id, make it configurable
48 if (Files
.exists(cmsState
.getStatePath("dev.properties"))) { // in Eclipse
49 applicationID
= cmsState
.getStatePath("").getFileName().toString();
51 applicationID
= cmsState
.getStatePath("").getParent().getFileName().toString();
54 // TODO centralise state run dir
55 stateRunDir
= OS
.getRunDir().resolve(applicationID
);
57 // TODO factorise create/delete pattern
58 jshBase
= stateRunDir
.resolve(JShellClient
.JSH
);
59 if (Files
.exists(jshBase
)) {
60 log
.warn(jshBase
+ " already exists, deleting it...");
61 FsUtils
.delete(jshBase
);
63 Files
.createDirectories(jshBase
);
64 jshLinkedDir
= cmsState
.getStatePath(JShellClient
.JSH
);
65 if (Files
.exists(jshLinkedDir
)) {
66 log
.warn(jshLinkedDir
+ " already exists, deleting it...");
67 FsUtils
.delete(jshLinkedDir
);
69 Files
.createSymbolicLink(jshLinkedDir
, jshBase
);
71 jtermBase
= stateRunDir
.resolve(JShellClient
.JTERM
);
72 if (Files
.exists(jtermBase
)) {
73 log
.warn(jtermBase
+ " already exists, deleting it...");
74 FsUtils
.delete(jtermBase
);
76 Files
.createDirectories(jtermBase
);
77 jtermLinkedDir
= cmsState
.getStatePath(JShellClient
.JTERM
);
78 if (Files
.exists(jtermLinkedDir
)) {
79 log
.warn(jtermLinkedDir
+ " already exists, deleting it...");
80 FsUtils
.delete(jtermLinkedDir
);
82 Files
.createSymbolicLink(jtermLinkedDir
, jtermBase
);
84 log
.info("Local JShell on " + jshBase
+ ", linked to " + jshLinkedDir
);
85 log
.info("Local JTerm on " + jtermBase
+ ", linked to " + jtermLinkedDir
);
89 watchService
= FileSystems
.getDefault().newWatchService();
91 jshBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
92 StandardWatchEventKinds
.ENTRY_DELETE
);
93 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(jshBase
)) {
94 for (Path bundleSnDir
: bundleSns
) {
95 addBundleSnDir(bundleSnDir
, watchService
);
98 jtermBase
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
,
99 StandardWatchEventKinds
.ENTRY_DELETE
);
100 try (DirectoryStream
<Path
> bundleSns
= Files
.newDirectoryStream(jtermBase
)) {
101 for (Path bundleSnDir
: bundleSns
) {
102 addBundleSnDir(bundleSnDir
, watchService
);
107 while ((key
= watchService
.take()) != null) {
108 events
: for (WatchEvent
<?
> event
: key
.pollEvents()) {
109 // System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + ".");
110 Path parent
= (Path
) key
.watchable();
112 if (Files
.isSameFile(jshBase
, parent
)) {
113 Path bundleSnDir
= jshBase
.resolve((Path
) event
.context());
114 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
115 addBundleSnDir(bundleSnDir
, watchService
);
116 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
118 } else if (Files
.isSameFile(jtermBase
, parent
)) {
119 Path bundleSnDir
= jtermBase
.resolve((Path
) event
.context());
120 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
121 addBundleSnDir(bundleSnDir
, watchService
);
122 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
125 Path path
= parent
.resolve((Path
) event
.context());
126 if (StandardWatchEventKinds
.ENTRY_CREATE
.equals(event
.kind())) {
127 if (!Files
.isDirectory(path
)) {
128 log
.warn("Ignoring " + path
+ " as it is not a directory");
132 UUID
.fromString(path
.getFileName().toString());
133 } catch (IllegalArgumentException e
) {
134 log
.warn("Ignoring " + path
+ " as it is not named as UUID");
139 if (Files
.isSameFile(jshBase
, parent
.getParent())) {
141 } else if (Files
.isSameFile(jtermBase
, parent
.getParent())) {
144 log
.warn("Ignoring " + path
+ " as we don't know whether it is interactive or not");
147 Path bundleIdDir
= bundleDirs
.get(parent
);
148 LocalJShellSession localSession
= new LocalJShellSession(path
, bundleIdDir
,
150 localSessions
.put(path
, localSession
);
151 } else if (StandardWatchEventKinds
.ENTRY_DELETE
.equals(event
.kind())) {
152 localSessions
.remove(path
);
158 } catch (ClosedWatchServiceException e
) {
159 if (log
.isTraceEnabled())
160 log
.trace("JShell file watch service was closed");
161 } catch (IOException
| InterruptedException e
) {
162 log
.error("Unexpected exception in JShell file watch service", e
);
164 }, "JShell local sessions watcher").start();
167 private void addBundleSnDir(Path bundleSnDir
, WatchService watchService
) throws IOException
{
168 String symbolicName
= bundleSnDir
.getFileName().toString();
169 Bundle fromBundle
= OsgiExecutionControlProvider
.getBundleFromSn(symbolicName
);
170 if (fromBundle
== null) {
171 log
.error("Removing directory for bundle " + symbolicName
+ " because it was not found in runtime...");
172 FsUtils
.delete(bundleSnDir
);
175 Long bundleId
= fromBundle
.getBundleId();
176 Path bundleIdDir
= stateRunDir
.resolve(bundleId
.toString());
177 Files
.createDirectories(bundleIdDir
);
178 bundleDirs
.put(bundleSnDir
, bundleIdDir
);
180 bundleSnDir
.register(watchService
, StandardWatchEventKinds
.ENTRY_CREATE
, StandardWatchEventKinds
.ENTRY_DELETE
);
184 if (watchService
!= null)
186 watchService
.close();
187 } catch (IOException e
) {
188 log
.error("Cannot close JShell watch service", e
);
191 Files
.delete(jshLinkedDir
);
192 } catch (IOException e
) {
193 log
.error("Cannot remove " + jshLinkedDir
, e
);
196 FsUtils
.delete(jshBase
);
197 } catch (IOException e
) {
198 log
.error("Cannot remove " + jshBase
, e
);
201 Files
.delete(jtermLinkedDir
);
202 } catch (IOException e
) {
203 log
.error("Cannot remove " + jtermLinkedDir
, e
);
206 FsUtils
.delete(jtermBase
);
207 } catch (IOException e
) {
208 log
.error("Cannot remove " + jtermBase
, e
);
212 public void setCmsState(CmsState cmsState
) {
213 this.cmsState
= cmsState
;