added org.simantics.backup
authorHannu Niemistö <hannu.niemisto@iki.fi>
Mon, 15 Aug 2016 12:15:56 +0000 (15:15 +0300)
committerHannu Niemistö <hannu.niemisto@iki.fi>
Mon, 15 Aug 2016 12:15:56 +0000 (15:15 +0300)
bundles/org.simantics.backup/.classpath [new file with mode: 0644]
bundles/org.simantics.backup/.project [new file with mode: 0644]
bundles/org.simantics.backup/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
bundles/org.simantics.backup/META-INF/MANIFEST.MF [new file with mode: 0644]
bundles/org.simantics.backup/build.properties [new file with mode: 0644]
bundles/org.simantics.backup/src/org/simantics/backup/Activator.java [new file with mode: 0644]
bundles/org.simantics.backup/src/org/simantics/backup/BackupException.java [new file with mode: 0644]
bundles/org.simantics.backup/src/org/simantics/backup/BackupProviderService.java [new file with mode: 0644]
bundles/org.simantics.backup/src/org/simantics/backup/Backups.java [new file with mode: 0644]
bundles/org.simantics.backup/src/org/simantics/backup/IBackupProvider.java [new file with mode: 0644]

diff --git a/bundles/org.simantics.backup/.classpath b/bundles/org.simantics.backup/.classpath
new file mode 100644 (file)
index 0000000..b862a29
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+       <classpathentry kind="src" path="src"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/bundles/org.simantics.backup/.project b/bundles/org.simantics.backup/.project
new file mode 100644 (file)
index 0000000..2619550
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>org.simantics.backup</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.ManifestBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.SchemaBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.pde.PluginNature</nature>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/bundles/org.simantics.backup/.settings/org.eclipse.jdt.core.prefs b/bundles/org.simantics.backup/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..295926d
--- /dev/null
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\r
+org.eclipse.jdt.core.compiler.compliance=1.8\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.8\r
diff --git a/bundles/org.simantics.backup/META-INF/MANIFEST.MF b/bundles/org.simantics.backup/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..e8ab3ee
--- /dev/null
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Backup
+Bundle-SymbolicName: org.simantics.backup
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.simantics.backup.Activator
+Require-Bundle: org.eclipse.core.runtime
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Export-Package: org.simantics.backup
diff --git a/bundles/org.simantics.backup/build.properties b/bundles/org.simantics.backup/build.properties
new file mode 100644 (file)
index 0000000..41eb6ad
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+               .\r
diff --git a/bundles/org.simantics.backup/src/org/simantics/backup/Activator.java b/bundles/org.simantics.backup/src/org/simantics/backup/Activator.java
new file mode 100644 (file)
index 0000000..b52c875
--- /dev/null
@@ -0,0 +1,32 @@
+package org.simantics.backup;\r
+\r
+import org.osgi.framework.BundleActivator;\r
+import org.osgi.framework.BundleContext;\r
+\r
+public class Activator implements BundleActivator {\r
+\r
+       public static final String BUNDLE_ID = "org.simantics.backup"; //$NON-NLS-1$\r
+\r
+       private static BundleContext context;\r
+\r
+       static BundleContext getContext() {\r
+               return context;\r
+       }\r
+\r
+       /*\r
+        * (non-Javadoc)\r
+        * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)\r
+        */\r
+       public void start(BundleContext bundleContext) throws Exception {\r
+               Activator.context = bundleContext;\r
+       }\r
+\r
+       /*\r
+        * (non-Javadoc)\r
+        * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)\r
+        */\r
+       public void stop(BundleContext bundleContext) throws Exception {\r
+               Activator.context = null;\r
+       }\r
+\r
+}\r
diff --git a/bundles/org.simantics.backup/src/org/simantics/backup/BackupException.java b/bundles/org.simantics.backup/src/org/simantics/backup/BackupException.java
new file mode 100644 (file)
index 0000000..8f452f7
--- /dev/null
@@ -0,0 +1,24 @@
+package org.simantics.backup;\r
+\r
+public class BackupException extends Exception {\r
+\r
+    private static final long serialVersionUID = -8745891620873007043L;\r
+\r
+    public BackupException(String message, Throwable cause,\r
+            boolean enableSuppression, boolean writableStackTrace) {\r
+        super(message, cause, enableSuppression, writableStackTrace);\r
+    }\r
+\r
+    public BackupException(String message, Throwable cause) {\r
+        super(message, cause);\r
+    }\r
+\r
+    public BackupException(String message) {\r
+        super(message);\r
+    }\r
+\r
+    public BackupException(Throwable cause) {\r
+        super(cause);\r
+    }\r
+\r
+}\r
diff --git a/bundles/org.simantics.backup/src/org/simantics/backup/BackupProviderService.java b/bundles/org.simantics.backup/src/org/simantics/backup/BackupProviderService.java
new file mode 100644 (file)
index 0000000..3223041
--- /dev/null
@@ -0,0 +1,116 @@
+package org.simantics.backup;\r
+\r
+import java.io.IOException;\r
+import java.nio.file.Files;\r
+import java.nio.file.Path;\r
+import java.nio.file.Paths;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.List;\r
+import java.util.concurrent.ExecutionException;\r
+import java.util.concurrent.Future;\r
+import java.util.function.Consumer;\r
+\r
+import org.eclipse.core.runtime.CoreException;\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.MultiStatus;\r
+import org.eclipse.core.runtime.Status;\r
+import org.osgi.framework.InvalidSyntaxException;\r
+import org.osgi.framework.ServiceReference;\r
+\r
+/**\r
+ * @author Jani Simomaa\r
+ */\r
+public class BackupProviderService {\r
+\r
+    private static volatile int threadCounter = 0;\r
+\r
+    public static void backup(String targetPath, int revision) throws BackupException {\r
+       backup(Paths.get(targetPath), revision, null);\r
+    }\r
+    \r
+    /**\r
+     * @param targetPath\r
+     * @param revision\r
+     * @param callback \r
+     * @throws BackupException \r
+     */\r
+    public static void backup(Path targetPath, int revision, Consumer<BackupException> callback) throws BackupException {\r
+        List<IBackupProvider> providers = getBackupProviders();\r
+        try {\r
+            if (!Files.exists(targetPath))\r
+                Files.createDirectories(targetPath);\r
+            Backups.lock(providers);\r
+            new Thread(() -> {\r
+                boolean unlockedAlready = false;\r
+                BackupException problem = null;\r
+                try {\r
+                    List<Future<BackupException>> backups = Backups.syncBackup(providers, targetPath, revision);\r
+                    // Unlock providers at this stage\r
+                    Backups.unlock(providers);\r
+                    unlockedAlready = true;\r
+                    \r
+                    // Wait for all providers to complete their work.\r
+                    List<Exception> exceptions = new ArrayList<>(backups.size());\r
+                    for (Future<BackupException> f : backups) {\r
+                        try {\r
+                            Exception e = f.get();\r
+                            if (e != null)\r
+                                exceptions.add(e);\r
+                        } catch (InterruptedException | ExecutionException e) {\r
+                            exceptions.add(e);\r
+                        }\r
+                    }\r
+\r
+                    // Throw BackupException if any of the backup operations failed.\r
+                    if (!exceptions.isEmpty()) {\r
+                        IStatus[] ss = exceptions.stream()\r
+                                .map(e -> new Status(IStatus.ERROR, Activator.BUNDLE_ID, e.getMessage(), e))\r
+                                .toArray(IStatus[]::new);\r
+                        problem = new BackupException(new CoreException(new MultiStatus(Activator.BUNDLE_ID, 0, ss,\r
+                                "Backup operation(s) failed to complete.", null)));\r
+                    }\r
+\r
+                } catch (BackupException e) {\r
+                    problem = e;\r
+                } catch (Throwable t) {\r
+                    problem = new BackupException(t);\r
+                } finally {\r
+                    if (!unlockedAlready)\r
+                        Backups.unlock(providers);\r
+                }\r
+                if (callback != null)\r
+                    callback.accept(problem);\r
+            }, "Backup thread " + (++threadCounter)).start();\r
+        } catch (IOException e) {\r
+            throw new BackupException(e);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param fromPath\r
+     * @param revision\r
+     */\r
+    public static void restore(Path fromPath, int revision) throws BackupException {\r
+        restore(getBackupProviders(), fromPath, revision);\r
+    }\r
+\r
+    private static void restore(Collection<IBackupProvider> providers, Path fromPath, int revision) throws BackupException {\r
+        for (IBackupProvider provider : providers)\r
+            provider.restore(fromPath, revision);\r
+    }\r
+\r
+    private static List<IBackupProvider> getBackupProviders() throws BackupException {\r
+        try {\r
+            List<IBackupProvider> results = new ArrayList<>();\r
+            Collection<ServiceReference<IBackupProvider>> backupProviders = Activator.getContext().getServiceReferences(IBackupProvider.class, null);\r
+            for (ServiceReference<IBackupProvider> reference : backupProviders) {\r
+                results.add(Activator.getContext().getService(reference));\r
+            }\r
+            return results;\r
+        } catch (InvalidSyntaxException e) {\r
+            throw new BackupException("Failed to enumerate backup providers.", e);\r
+        }\r
+    }\r
+\r
+}\r
diff --git a/bundles/org.simantics.backup/src/org/simantics/backup/Backups.java b/bundles/org.simantics.backup/src/org/simantics/backup/Backups.java
new file mode 100644 (file)
index 0000000..b684296
--- /dev/null
@@ -0,0 +1,96 @@
+package org.simantics.backup;\r
+\r
+import java.nio.file.Path;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.List;\r
+import java.util.concurrent.Future;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class Backups {\r
+\r
+    /**\r
+     * \r
+     * @param providers\r
+     * @throws BackupException\r
+     */\r
+    public static void lock(List<IBackupProvider> providers) throws BackupException {\r
+        int i = 0;\r
+        int count = providers.size();\r
+        try {\r
+            for (; i < count; ++i) {\r
+                providers.get(i).lock();\r
+            }\r
+        } catch (BackupException e) {\r
+            for (int j = i-1; j >= 0; --j) {\r
+                try {\r
+                    providers.get(j).unlock();\r
+                } catch (BackupException ex) {\r
+                    // TODO: proper logging.\r
+                    ex.printStackTrace();\r
+                }\r
+            }\r
+            throw e;\r
+        }\r
+    }\r
+\r
+    public static List<Future<BackupException>> syncBackup(List<IBackupProvider> providers, Path targetPath, int revision) throws BackupException {\r
+        // Initiate all backup providers, possibly to execute concurrently.\r
+        List<Future<BackupException>> futures = new ArrayList<>(providers.size());\r
+        for (IBackupProvider provider : providers)\r
+            futures.add( provider.backup(targetPath, revision) );\r
+\r
+        return futures;\r
+    }\r
+\r
+    public static void unlock(Collection<IBackupProvider> providers) {\r
+        for (IBackupProvider provider : providers) {\r
+            try {\r
+                provider.unlock();\r
+            } catch (BackupException e) {\r
+                // TODO: proper logging\r
+                e.printStackTrace();\r
+            }\r
+        }\r
+    }\r
+\r
+//    private static class BackupResultFuture implements Future<BackupException> {\r
+//\r
+//        public static final BackupResultFuture SUCCESS = new BackupResultFuture(null);\r
+//\r
+//        private final BackupException e;\r
+//\r
+//        public BackupResultFuture(BackupException e) {\r
+//            this.e = e;\r
+//        }\r
+//\r
+//        @Override\r
+//        public boolean cancel(boolean mayInterruptIfRunning) {\r
+//            return false;\r
+//        }\r
+//\r
+//        @Override\r
+//        public boolean isCancelled() {\r
+//            return false;\r
+//        }\r
+//\r
+//        @Override\r
+//        public boolean isDone() {\r
+//            return true;\r
+//        }\r
+//\r
+//        @Override\r
+//        public BackupException get() throws InterruptedException, ExecutionException {\r
+//            return e;\r
+//        }\r
+//\r
+//        @Override\r
+//        public BackupException get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {\r
+//            return e;\r
+//        }\r
+//\r
+//    }\r
+\r
+}\r
diff --git a/bundles/org.simantics.backup/src/org/simantics/backup/IBackupProvider.java b/bundles/org.simantics.backup/src/org/simantics/backup/IBackupProvider.java
new file mode 100644 (file)
index 0000000..9bab31b
--- /dev/null
@@ -0,0 +1,49 @@
+package org.simantics.backup;\r
+\r
+import java.nio.file.Path;\r
+import java.util.concurrent.Future;\r
+\r
+/**\r
+ * Interface for providing backup capabilities to Simantics products.\r
+ * BackupProviders are used in {@link BackupProviderService}.\r
+ * \r
+ * @author Jani Simomaa\r
+ */\r
+public interface IBackupProvider {\r
+\r
+    /**\r
+     * Lock the resources that are going to be backed up in a way that allows\r
+     * making an atomic and consistent copy of the backed up resources.\r
+     */\r
+    void lock() throws BackupException;\r
+\r
+    /**\r
+     * Initiates or executes the backing up procedure of this provider. The\r
+     * backup procedure is allowed to return from this method and complete\r
+     * asynchronously. A {@link Future} is returned to allow waiting for backup\r
+     * to complete.\r
+     * \r
+     * @param targetPath\r
+     * @param revision\r
+     * @return a future that can be waited upon to wait for the backup procedure\r
+     *         to complete.\r
+     */\r
+    Future<BackupException> backup(Path targetPath, int revision) throws BackupException;\r
+\r
+    /**\r
+     * The counterpart of {@link #lock()} that must be invoked and invoked only\r
+     * after {@link #lock()} has been successfully invoked\r
+     */\r
+    void unlock() throws BackupException;\r
+\r
+    /**\r
+     * Restore implementation for this provider.\r
+     * \r
+     * @param fromPath\r
+     * @param revision\r
+     * \r
+     * TODO: change to return {@link Future} that can be waited upon\r
+     */\r
+    void restore(Path fromPath, int revision) throws BackupException;\r
+\r
+}\r