From 4e40f9793cc18f08f1fa6c96d9bb4f42408997b4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Hannu=20Niemist=C3=B6?= Date: Mon, 15 Aug 2016 15:15:56 +0300 Subject: [PATCH] added org.simantics.backup --- bundles/org.simantics.backup/.classpath | 7 ++ bundles/org.simantics.backup/.project | 28 +++++ .../.settings/org.eclipse.jdt.core.prefs | 7 ++ .../org.simantics.backup/META-INF/MANIFEST.MF | 10 ++ bundles/org.simantics.backup/build.properties | 4 + .../src/org/simantics/backup/Activator.java | 32 +++++ .../org/simantics/backup/BackupException.java | 24 ++++ .../backup/BackupProviderService.java | 116 ++++++++++++++++++ .../src/org/simantics/backup/Backups.java | 96 +++++++++++++++ .../org/simantics/backup/IBackupProvider.java | 49 ++++++++ 10 files changed, 373 insertions(+) create mode 100644 bundles/org.simantics.backup/.classpath create mode 100644 bundles/org.simantics.backup/.project create mode 100644 bundles/org.simantics.backup/.settings/org.eclipse.jdt.core.prefs create mode 100644 bundles/org.simantics.backup/META-INF/MANIFEST.MF create mode 100644 bundles/org.simantics.backup/build.properties create mode 100644 bundles/org.simantics.backup/src/org/simantics/backup/Activator.java create mode 100644 bundles/org.simantics.backup/src/org/simantics/backup/BackupException.java create mode 100644 bundles/org.simantics.backup/src/org/simantics/backup/BackupProviderService.java create mode 100644 bundles/org.simantics.backup/src/org/simantics/backup/Backups.java create mode 100644 bundles/org.simantics.backup/src/org/simantics/backup/IBackupProvider.java diff --git a/bundles/org.simantics.backup/.classpath b/bundles/org.simantics.backup/.classpath new file mode 100644 index 000000000..b862a296d --- /dev/null +++ b/bundles/org.simantics.backup/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.backup/.project b/bundles/org.simantics.backup/.project new file mode 100644 index 000000000..261955090 --- /dev/null +++ b/bundles/org.simantics.backup/.project @@ -0,0 +1,28 @@ + + + org.simantics.backup + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + 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 index 000000000..295926d96 --- /dev/null +++ b/bundles/org.simantics.backup/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.simantics.backup/META-INF/MANIFEST.MF b/bundles/org.simantics.backup/META-INF/MANIFEST.MF new file mode 100644 index 000000000..e8ab3ee36 --- /dev/null +++ b/bundles/org.simantics.backup/META-INF/MANIFEST.MF @@ -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 index 000000000..41eb6ade2 --- /dev/null +++ b/bundles/org.simantics.backup/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . 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 index 000000000..b52c875e4 --- /dev/null +++ b/bundles/org.simantics.backup/src/org/simantics/backup/Activator.java @@ -0,0 +1,32 @@ +package org.simantics.backup; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + public static final String BUNDLE_ID = "org.simantics.backup"; //$NON-NLS-1$ + + private static BundleContext context; + + static BundleContext getContext() { + return context; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext bundleContext) throws Exception { + Activator.context = bundleContext; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext bundleContext) throws Exception { + Activator.context = null; + } + +} 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 index 000000000..8f452f73a --- /dev/null +++ b/bundles/org.simantics.backup/src/org/simantics/backup/BackupException.java @@ -0,0 +1,24 @@ +package org.simantics.backup; + +public class BackupException extends Exception { + + private static final long serialVersionUID = -8745891620873007043L; + + public BackupException(String message, Throwable cause, + boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public BackupException(String message, Throwable cause) { + super(message, cause); + } + + public BackupException(String message) { + super(message); + } + + public BackupException(Throwable cause) { + super(cause); + } + +} 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 index 000000000..32230419a --- /dev/null +++ b/bundles/org.simantics.backup/src/org/simantics/backup/BackupProviderService.java @@ -0,0 +1,116 @@ +package org.simantics.backup; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Consumer; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +/** + * @author Jani Simomaa + */ +public class BackupProviderService { + + private static volatile int threadCounter = 0; + + public static void backup(String targetPath, int revision) throws BackupException { + backup(Paths.get(targetPath), revision, null); + } + + /** + * @param targetPath + * @param revision + * @param callback + * @throws BackupException + */ + public static void backup(Path targetPath, int revision, Consumer callback) throws BackupException { + List providers = getBackupProviders(); + try { + if (!Files.exists(targetPath)) + Files.createDirectories(targetPath); + Backups.lock(providers); + new Thread(() -> { + boolean unlockedAlready = false; + BackupException problem = null; + try { + List> backups = Backups.syncBackup(providers, targetPath, revision); + // Unlock providers at this stage + Backups.unlock(providers); + unlockedAlready = true; + + // Wait for all providers to complete their work. + List exceptions = new ArrayList<>(backups.size()); + for (Future f : backups) { + try { + Exception e = f.get(); + if (e != null) + exceptions.add(e); + } catch (InterruptedException | ExecutionException e) { + exceptions.add(e); + } + } + + // Throw BackupException if any of the backup operations failed. + if (!exceptions.isEmpty()) { + IStatus[] ss = exceptions.stream() + .map(e -> new Status(IStatus.ERROR, Activator.BUNDLE_ID, e.getMessage(), e)) + .toArray(IStatus[]::new); + problem = new BackupException(new CoreException(new MultiStatus(Activator.BUNDLE_ID, 0, ss, + "Backup operation(s) failed to complete.", null))); + } + + } catch (BackupException e) { + problem = e; + } catch (Throwable t) { + problem = new BackupException(t); + } finally { + if (!unlockedAlready) + Backups.unlock(providers); + } + if (callback != null) + callback.accept(problem); + }, "Backup thread " + (++threadCounter)).start(); + } catch (IOException e) { + throw new BackupException(e); + } + } + + /** + * @param fromPath + * @param revision + */ + public static void restore(Path fromPath, int revision) throws BackupException { + restore(getBackupProviders(), fromPath, revision); + } + + private static void restore(Collection providers, Path fromPath, int revision) throws BackupException { + for (IBackupProvider provider : providers) + provider.restore(fromPath, revision); + } + + private static List getBackupProviders() throws BackupException { + try { + List results = new ArrayList<>(); + Collection> backupProviders = Activator.getContext().getServiceReferences(IBackupProvider.class, null); + for (ServiceReference reference : backupProviders) { + results.add(Activator.getContext().getService(reference)); + } + return results; + } catch (InvalidSyntaxException e) { + throw new BackupException("Failed to enumerate backup providers.", e); + } + } + +} 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 index 000000000..b6842965d --- /dev/null +++ b/bundles/org.simantics.backup/src/org/simantics/backup/Backups.java @@ -0,0 +1,96 @@ +package org.simantics.backup; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Future; + +/** + * @author Tuukka Lehtonen + */ +public class Backups { + + /** + * + * @param providers + * @throws BackupException + */ + public static void lock(List providers) throws BackupException { + int i = 0; + int count = providers.size(); + try { + for (; i < count; ++i) { + providers.get(i).lock(); + } + } catch (BackupException e) { + for (int j = i-1; j >= 0; --j) { + try { + providers.get(j).unlock(); + } catch (BackupException ex) { + // TODO: proper logging. + ex.printStackTrace(); + } + } + throw e; + } + } + + public static List> syncBackup(List providers, Path targetPath, int revision) throws BackupException { + // Initiate all backup providers, possibly to execute concurrently. + List> futures = new ArrayList<>(providers.size()); + for (IBackupProvider provider : providers) + futures.add( provider.backup(targetPath, revision) ); + + return futures; + } + + public static void unlock(Collection providers) { + for (IBackupProvider provider : providers) { + try { + provider.unlock(); + } catch (BackupException e) { + // TODO: proper logging + e.printStackTrace(); + } + } + } + +// private static class BackupResultFuture implements Future { +// +// public static final BackupResultFuture SUCCESS = new BackupResultFuture(null); +// +// private final BackupException e; +// +// public BackupResultFuture(BackupException e) { +// this.e = e; +// } +// +// @Override +// public boolean cancel(boolean mayInterruptIfRunning) { +// return false; +// } +// +// @Override +// public boolean isCancelled() { +// return false; +// } +// +// @Override +// public boolean isDone() { +// return true; +// } +// +// @Override +// public BackupException get() throws InterruptedException, ExecutionException { +// return e; +// } +// +// @Override +// public BackupException get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { +// return e; +// } +// +// } + +} 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 index 000000000..9bab31b8b --- /dev/null +++ b/bundles/org.simantics.backup/src/org/simantics/backup/IBackupProvider.java @@ -0,0 +1,49 @@ +package org.simantics.backup; + +import java.nio.file.Path; +import java.util.concurrent.Future; + +/** + * Interface for providing backup capabilities to Simantics products. + * BackupProviders are used in {@link BackupProviderService}. + * + * @author Jani Simomaa + */ +public interface IBackupProvider { + + /** + * Lock the resources that are going to be backed up in a way that allows + * making an atomic and consistent copy of the backed up resources. + */ + void lock() throws BackupException; + + /** + * Initiates or executes the backing up procedure of this provider. The + * backup procedure is allowed to return from this method and complete + * asynchronously. A {@link Future} is returned to allow waiting for backup + * to complete. + * + * @param targetPath + * @param revision + * @return a future that can be waited upon to wait for the backup procedure + * to complete. + */ + Future backup(Path targetPath, int revision) throws BackupException; + + /** + * The counterpart of {@link #lock()} that must be invoked and invoked only + * after {@link #lock()} has been successfully invoked + */ + void unlock() throws BackupException; + + /** + * Restore implementation for this provider. + * + * @param fromPath + * @param revision + * + * TODO: change to return {@link Future} that can be waited upon + */ + void restore(Path fromPath, int revision) throws BackupException; + +} -- 2.47.1