]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.jshell/src/org/argeo/cms/jshell/CmsJShell.java
Merge tag 'v2.3.23' into testing
[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.FsUtils;
20 import org.argeo.cms.util.OS;
21 import org.argeo.internal.cms.jshell.osgi.OsgiExecutionControlProvider;
22 import org.osgi.framework.Bundle;
23
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();
28
29 public static UuidFactory uuidFactory = null;
30
31 private CmsState cmsState;
32
33 private Map<Path, LocalJShellSession> localSessions = new HashMap<>();
34 private Map<Path, Path> bundleDirs = new HashMap<>();
35
36 private Path stateRunDir;
37 private Path jshBase;
38 private Path jshLinkedDir;
39 private Path jtermBase;
40 private Path jtermLinkedDir;
41
42 public void start() throws Exception {
43 // TODO better define application id, make it configurable
44 String applicationID;
45 if (Files.exists(cmsState.getStatePath("dev.properties"))) { // in Eclipse
46 applicationID = cmsState.getStatePath("").getFileName().toString();
47 } else {
48 applicationID = cmsState.getStatePath("").getParent().getFileName().toString();
49 }
50
51 // TODO centralise state run dir
52 stateRunDir = OS.getRunDir().resolve(applicationID);
53
54 jshBase = stateRunDir.resolve(JShellClient.JSH);
55 Files.createDirectories(jshBase);
56 jshLinkedDir = Files.createSymbolicLink(cmsState.getStatePath(JShellClient.JSH), jshBase);
57
58 jtermBase = stateRunDir.resolve(JShellClient.JTERM);
59 Files.createDirectories(jtermBase);
60 jtermLinkedDir = Files.createSymbolicLink(cmsState.getStatePath(JShellClient.JTERM), jtermBase);
61
62 log.info("Local JShell on " + jshBase + ", linked to " + jshLinkedDir);
63 log.info("Local JTerm on " + jtermBase + ", linked to " + jtermLinkedDir);
64
65 new Thread(() -> {
66 try {
67 WatchService watchService = FileSystems.getDefault().newWatchService();
68
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);
74 }
75 }
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);
81 }
82 }
83
84 WatchKey key;
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();
89 // sessions
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())) {
95 }
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())) {
101 }
102 } else {
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");
107 continue events;
108 }
109 try {
110 UUID.fromString(path.getFileName().toString());
111 } catch (IllegalArgumentException e) {
112 log.warn("Ignoring " + path + " as it is not named as UUID");
113 continue events;
114 }
115
116 boolean interactive;
117 if (Files.isSameFile(jshBase, parent.getParent())) {
118 interactive = false;
119 } else if (Files.isSameFile(jtermBase, parent.getParent())) {
120 interactive = true;
121 } else {
122 log.warn("Ignoring " + path + " as we don't know whether it is interactive or not");
123 continue events;
124 }
125 Path bundleIdDir = bundleDirs.get(parent);
126 LocalJShellSession localSession = new LocalJShellSession(path, bundleIdDir,
127 interactive);
128 localSessions.put(path, localSession);
129 } else if (StandardWatchEventKinds.ENTRY_DELETE.equals(event.kind())) {
130 localSessions.remove(path);
131 }
132 }
133 }
134 key.reset();
135 }
136 } catch (IOException | InterruptedException e) {
137 e.printStackTrace();
138 }
139 }, "JShell local sessions watcher").start();
140 }
141
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);
148 return;
149 }
150 Long bundleId = fromBundle.getBundleId();
151 Path bundleIdDir = stateRunDir.resolve(bundleId.toString());
152 Files.createDirectories(bundleIdDir);
153 bundleDirs.put(bundleSnDir, bundleIdDir);
154
155 bundleSnDir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE);
156 }
157
158 public void stop() {
159 try {
160 Files.delete(jshLinkedDir);
161 } catch (IOException e) {
162 log.error("Cannot remove " + jshLinkedDir);
163 }
164 try {
165 Files.delete(jtermLinkedDir);
166 } catch (IOException e) {
167 log.error("Cannot remove " + jtermLinkedDir);
168 }
169 }
170
171 public void setCmsState(CmsState cmsState) {
172 this.cmsState = cmsState;
173 }
174 }