Make JShell shutdown more robust
[lgpl/argeo-commons.git] / org.argeo.cms.jshell / src / org / argeo / cms / jshell / CmsJShell.java
index e3718ae73119f693d99c78441e194d9bf7199255..f08a2a57e0035991072abd457d5c04b53234f304 100644 (file)
@@ -1,6 +1,7 @@
 package org.argeo.cms.jshell;
 
 import java.io.IOException;
+import java.nio.file.ClosedWatchServiceException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
@@ -16,6 +17,7 @@ import java.util.UUID;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.CmsState;
 import org.argeo.api.uuid.UuidFactory;
+import org.argeo.cms.util.FsUtils;
 import org.argeo.cms.util.OS;
 import org.argeo.internal.cms.jshell.osgi.OsgiExecutionControlProvider;
 import org.osgi.framework.Bundle;
@@ -38,27 +40,53 @@ public class CmsJShell {
        private Path jtermBase;
        private Path jtermLinkedDir;
 
+       private WatchService watchService;
+
        public void start() throws Exception {
                // TODO better define application id, make it configurable
-               String applicationID = cmsState.getStatePath("").getFileName().toString();
+               String applicationID;
+               if (Files.exists(cmsState.getStatePath("dev.properties"))) { // in Eclipse
+                       applicationID = cmsState.getStatePath("").getFileName().toString();
+               } else {
+                       applicationID = cmsState.getStatePath("").getParent().getFileName().toString();
+               }
 
                // TODO centralise state run dir
                stateRunDir = OS.getRunDir().resolve(applicationID);
 
+               // TODO factorise create/delete pattern
                jshBase = stateRunDir.resolve(JShellClient.JSH);
+               if (Files.exists(jshBase)) {
+                       log.warn(jshBase + " already exists, deleting it...");
+                       FsUtils.delete(jshBase);
+               }
                Files.createDirectories(jshBase);
-               jshLinkedDir = Files.createSymbolicLink(cmsState.getStatePath(JShellClient.JSH), jshBase);
+               jshLinkedDir = cmsState.getStatePath(JShellClient.JSH);
+               if (Files.exists(jshLinkedDir)) {
+                       log.warn(jshLinkedDir + " already exists, deleting it...");
+                       FsUtils.delete(jshLinkedDir);
+               }
+               Files.createSymbolicLink(jshLinkedDir, jshBase);
 
                jtermBase = stateRunDir.resolve(JShellClient.JTERM);
+               if (Files.exists(jtermBase)) {
+                       log.warn(jtermBase + " already exists, deleting it...");
+                       FsUtils.delete(jtermBase);
+               }
                Files.createDirectories(jtermBase);
-               jtermLinkedDir = Files.createSymbolicLink(cmsState.getStatePath(JShellClient.JTERM), jtermBase);
+               jtermLinkedDir = cmsState.getStatePath(JShellClient.JTERM);
+               if (Files.exists(jtermLinkedDir)) {
+                       log.warn(jtermLinkedDir + " already exists, deleting it...");
+                       FsUtils.delete(jtermLinkedDir);
+               }
+               Files.createSymbolicLink(jtermLinkedDir, jtermBase);
 
                log.info("Local JShell on " + jshBase + ", linked to " + jshLinkedDir);
-               log.info("Local JTerml on " + jtermBase + ", linked to " + jtermLinkedDir);
+               log.info("Local JTerm on " + jtermBase + ", linked to " + jtermLinkedDir);
 
                new Thread(() -> {
                        try {
-                               WatchService watchService = FileSystems.getDefault().newWatchService();
+                               watchService = FileSystems.getDefault().newWatchService();
 
                                jshBase.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
                                                StandardWatchEventKinds.ENTRY_DELETE);
@@ -127,8 +155,11 @@ public class CmsJShell {
                                        }
                                        key.reset();
                                }
+                       } catch (ClosedWatchServiceException e) {
+                               if (log.isTraceEnabled())
+                                       log.trace("JShell file watch service was closed");
                        } catch (IOException | InterruptedException e) {
-                               e.printStackTrace();
+                               log.error("Unexpected exception in JShell file watch service", e);
                        }
                }, "JShell local sessions watcher").start();
        }
@@ -137,7 +168,8 @@ public class CmsJShell {
                String symbolicName = bundleSnDir.getFileName().toString();
                Bundle fromBundle = OsgiExecutionControlProvider.getBundleFromSn(symbolicName);
                if (fromBundle == null) {
-                       log.error("Ignoring bundle " + symbolicName + " because it was not found");
+                       log.error("Removing directory for bundle " + symbolicName + " because it was not found in runtime...");
+                       FsUtils.delete(bundleSnDir);
                        return;
                }
                Long bundleId = fromBundle.getBundleId();
@@ -149,10 +181,31 @@ public class CmsJShell {
        }
 
        public void stop() {
+               if (watchService != null)
+                       try {
+                               watchService.close();
+                       } catch (IOException e) {
+                               log.error("Cannot close JShell watch service", e);
+                       }
                try {
                        Files.delete(jshLinkedDir);
                } catch (IOException e) {
-                       log.error("Cannot remove " + jshLinkedDir);
+                       log.error("Cannot remove " + jshLinkedDir, e);
+               }
+               try {
+                       FsUtils.delete(jshBase);
+               } catch (IOException e) {
+                       log.error("Cannot remove " + jshBase, e);
+               }
+               try {
+                       Files.delete(jtermLinkedDir);
+               } catch (IOException e) {
+                       log.error("Cannot remove " + jtermLinkedDir, e);
+               }
+               try {
+                       FsUtils.delete(jtermBase);
+               } catch (IOException e) {
+                       log.error("Cannot remove " + jtermBase, e);
                }
        }