]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.jshell/src/org/argeo/cms/jshell/CmsJShell.java
JShell improvements
[lgpl/argeo-commons.git] / org.argeo.cms.jshell / src / org / argeo / cms / jshell / CmsJShell.java
1 package org.argeo.cms.jshell;
2
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;
13 import java.util.Map;
14 import java.util.UUID;
15
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;
22
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();
27
28 public static UuidFactory uuidFactory = null;
29
30 private CmsState cmsState;
31
32 private Map<Path, LocalJShellSession> localSessions = new HashMap<>();
33 private Map<Path, Path> bundleDirs = new HashMap<>();
34
35 private Path stateRunDir;
36 private Path jshBase;
37 private Path jshLinkedDir;
38 private Path jtermBase;
39 private Path jtermLinkedDir;
40
41 public void start() throws Exception {
42 // TODO better define application id, make it configurable
43 String applicationID = cmsState.getStatePath("").getFileName().toString();
44
45 // TODO centralise state run dir
46 stateRunDir = OS.getRunDir().resolve(applicationID);
47
48 jshBase = stateRunDir.resolve(JShellClient.JSH);
49 Files.createDirectories(jshBase);
50 jshLinkedDir = Files.createSymbolicLink(cmsState.getStatePath(JShellClient.JSH), jshBase);
51
52 jtermBase = stateRunDir.resolve(JShellClient.JTERM);
53 Files.createDirectories(jtermBase);
54 jtermLinkedDir = Files.createSymbolicLink(cmsState.getStatePath(JShellClient.JTERM), jtermBase);
55
56 log.info("Local JShell on " + jshBase + ", linked to " + jshLinkedDir);
57 log.info("Local JTerml on " + jtermBase + ", linked to " + jtermLinkedDir);
58
59 new Thread(() -> {
60 try {
61 WatchService watchService = FileSystems.getDefault().newWatchService();
62
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);
68 }
69 }
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);
75 }
76 }
77
78 WatchKey key;
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();
83 // sessions
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())) {
89 }
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())) {
95 }
96 } else {
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");
101 continue events;
102 }
103 try {
104 UUID.fromString(path.getFileName().toString());
105 } catch (IllegalArgumentException e) {
106 log.warn("Ignoring " + path + " as it is not named as UUID");
107 continue events;
108 }
109
110 boolean interactive;
111 if (Files.isSameFile(jshBase, parent.getParent())) {
112 interactive = false;
113 } else if (Files.isSameFile(jtermBase, parent.getParent())) {
114 interactive = true;
115 } else {
116 log.warn("Ignoring " + path + " as we don't know whether it is interactive or not");
117 continue events;
118 }
119 Path bundleIdDir = bundleDirs.get(parent);
120 LocalJShellSession localSession = new LocalJShellSession(path, bundleIdDir,
121 interactive);
122 localSessions.put(path, localSession);
123 } else if (StandardWatchEventKinds.ENTRY_DELETE.equals(event.kind())) {
124 localSessions.remove(path);
125 }
126 }
127 }
128 key.reset();
129 }
130 } catch (IOException | InterruptedException e) {
131 e.printStackTrace();
132 }
133 }, "JShell local sessions watcher").start();
134 }
135
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");
141 return;
142 }
143 Long bundleId = fromBundle.getBundleId();
144 Path bundleIdDir = stateRunDir.resolve(bundleId.toString());
145 Files.createDirectories(bundleIdDir);
146 bundleDirs.put(bundleSnDir, bundleIdDir);
147
148 bundleSnDir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE);
149 }
150
151 public void stop() {
152 try {
153 Files.delete(jshLinkedDir);
154 } catch (IOException e) {
155 log.error("Cannot remove " + jshLinkedDir);
156 }
157 }
158
159 public void setCmsState(CmsState cmsState) {
160 this.cmsState = cmsState;
161 }
162 }