diff options
author | Steffen Pingel | 2012-02-24 00:29:07 +0000 |
---|---|---|
committer | Steffen Pingel | 2012-02-24 00:29:07 +0000 |
commit | 8c46405ded77db1f6fd71f4de6da24d24f988d5d (patch) | |
tree | 2d702c7cec6b87327b1501dc04d8d988416c17e4 | |
parent | a847c34384fe171d9dcadcbc34b50e58579a8889 (diff) | |
download | org.eclipse.mylyn.commons-8c46405ded77db1f6fd71f4de6da24d24f988d5d.tar.gz org.eclipse.mylyn.commons-8c46405ded77db1f6fd71f4de6da24d24f988d5d.tar.xz org.eclipse.mylyn.commons-8c46405ded77db1f6fd71f4de6da24d24f988d5d.zip |
NEW - bug 371984: [api] provide a generic storage API
https://bugs.eclipse.org/bugs/show_bug.cgi?id=371984
Change-Id: I25a6b48962d68b40c5702d9100f92eed60165350
9 files changed, 433 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.commons.core/META-INF/MANIFEST.MF b/org.eclipse.mylyn.commons.core/META-INF/MANIFEST.MF index 6a3e5567..f0b38801 100644 --- a/org.eclipse.mylyn.commons.core/META-INF/MANIFEST.MF +++ b/org.eclipse.mylyn.commons.core/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Export-Package: org.eclipse.mylyn.commons.core, org.eclipse.mylyn.commons.core.io, org.eclipse.mylyn.commons.core.net, org.eclipse.mylyn.commons.core.operations, + org.eclipse.mylyn.commons.core.storage;x-internal:=true, org.eclipse.mylyn.internal.commons.core;x-internal:=true, org.eclipse.mylyn.internal.commons.core.operations;x-internal:=true, org.eclipse.mylyn.internal.provisional.commons.core;x-internal:=true diff --git a/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/CoreUtil.java b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/CoreUtil.java index 96c26ec3..cfc56ce5 100644 --- a/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/CoreUtil.java +++ b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/CoreUtil.java @@ -219,4 +219,22 @@ public class CoreUtil { return new Version(FRAMEWORK_VERSION); } + /** + * Returns a representation of <code>name</code> that is a valid file name. + * + * @since 3.7 + */ + public static String asFileName(String name) { + StringBuffer sb = new StringBuffer(name.length()); + char[] chars = name.toCharArray(); + for (char c : chars) { + if (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '.') { + sb.append(c); + } else { + sb.append("%" + Integer.toHexString(c).toUpperCase()); //$NON-NLS-1$ + } + } + return sb.toString(); + } + } diff --git a/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/CommonStorable.java b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/CommonStorable.java new file mode 100644 index 00000000..ae14eed0 --- /dev/null +++ b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/CommonStorable.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2012 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.core.storage; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * @author Steffen Pingel + */ +class CommonStorable implements ICommonStorable { + + private final File path; + + private final CommonStore store; + + public CommonStorable(CommonStore store, File path) { + this.store = store; + this.path = path; + } + + public void delete(String item) throws CoreException { + getFile(item).delete(); + } + + public boolean exists(String handle) { + if (!path.exists()) { + return false; + } + return getFile(handle).exists(); + } + + public IStatus flush() { + return Status.OK_STATUS; + } + + public File getPath() { + return path; + } + + public boolean isDirty() { + return false; + } + + public InputStream read(String item, IProgressMonitor monitor) throws IOException { + File file = getFile(item); + return new FileInputStream(file); + } + + public void release() { + store.release(this); + } + + public OutputStream write(String item, IProgressMonitor monitor) throws IOException { + File file = getFile(item); + return new FileOutputStream(file); + } + + private File getFile(String item) { + File file = new File(path, item); + if (!file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + return file; + } + +} diff --git a/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/CommonStore.java b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/CommonStore.java new file mode 100644 index 00000000..3b7f09f2 --- /dev/null +++ b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/CommonStore.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2012 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.core.storage; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.mylyn.commons.core.StatusHandler; +import org.eclipse.mylyn.internal.commons.core.CommonsCorePlugin; + +/** + * @author Steffen Pingel + */ +public class CommonStore { + + /** + * Delays writing of mementos to avoid blocking UI thread. + */ + private class FlushJob extends Job { + + public FlushJob() { + super("Flush context mementos"); //$NON-NLS-1$ + setSystem(true); + setPriority(Job.SHORT); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + flushPending(); + return Status.OK_STATUS; + } + + } + + private static final long FLUSH_DELAY = 500; + + private boolean scheduled; + + private FlushJob flushJob; + + private final Map<File, CommonStorable> storableByLocation; + + private File location; + + public CommonStore(File location) { + Assert.isNotNull(location); + this.storableByLocation = new HashMap<File, CommonStorable>(); + this.location = location; + } + + public synchronized ICommonStorable get(IPath path) { + File file = getFile(path); + CommonStorable storable = storableByLocation.get(file); + if (storable == null) { + storable = new CommonStorable(this, file); + storableByLocation.put(file, storable); + } + return storable; + } + + public synchronized void copy(IPath source, IPath target, boolean overwrite) { + // FIXME + } + + public File getLocation() { + return location; + } + + public void setLocation(File location) { + Assert.isNotNull(location); + this.location = location; + } + + public void stop() { + synchronized (this) { + if (flushJob != null) { + flushJob.cancel(); + flushJob = null; + } + } + flushPending(); + } + + private File getFile(IPath path) { + File file = new File(location, path.toOSString()); + if (!file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + return file; + } + + synchronized void schedule() { + if (!scheduled) { + if (flushJob == null) { + flushJob = new FlushJob(); + } + flushJob.schedule(FLUSH_DELAY); + } + } + + synchronized void flushPending() { + MultiStatus status = new MultiStatus(CommonsCorePlugin.ID_PLUGIN, 0, "Failed to save storable", null); //$NON-NLS-1$ + for (CommonStorable memento : storableByLocation.values()) { + if (memento.isDirty()) { + IStatus result = memento.flush(); + status.add(result); + } + } + if (!status.isOK()) { + StatusHandler.log(status); + } + } + + synchronized void release(CommonStorable storable) { + storableByLocation.remove(storable.getPath()); + } + +} diff --git a/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/ICommonStorable.java b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/ICommonStorable.java new file mode 100644 index 00000000..dc5bedbe --- /dev/null +++ b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/storage/ICommonStorable.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2012 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.core.storage; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * @author Steffen Pingel + */ +public interface ICommonStorable { + + public void delete(String handle) throws CoreException; + + public boolean exists(String handle); + + public InputStream read(String handle, IProgressMonitor monitor) throws IOException, CoreException; + + public OutputStream write(String handle, IProgressMonitor monitor) throws IOException, CoreException; + + public void release(); + +} diff --git a/org.eclipse.mylyn.commons.sdk.util/src/org/eclipse/mylyn/commons/sdk/util/CommonTestUtil.java b/org.eclipse.mylyn.commons.sdk.util/src/org/eclipse/mylyn/commons/sdk/util/CommonTestUtil.java index 41e10861..bb52cc00 100644 --- a/org.eclipse.mylyn.commons.sdk.util/src/org/eclipse/mylyn/commons/sdk/util/CommonTestUtil.java +++ b/org.eclipse.mylyn.commons.sdk.util/src/org/eclipse/mylyn/commons/sdk/util/CommonTestUtil.java @@ -122,6 +122,13 @@ public class CommonTestUtil { return stateLocation.toFile(); } + public static File createTempFolder(String prefix) throws IOException { + File location = File.createTempFile(prefix, null); + location.delete(); + location.mkdirs(); + return location; + } + public static void delete(File file) { if (file.exists()) { for (int i = 0; i < MAX_RETRY; i++) { @@ -158,6 +165,7 @@ public class CommonTestUtil { } } } + path.delete(); } public static CertificateCredentials getCertificateCredentials() { diff --git a/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/AllCommonsTests.java b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/AllCommonsTests.java index 1f4210df..9eb53986 100644 --- a/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/AllCommonsTests.java +++ b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/AllCommonsTests.java @@ -18,6 +18,7 @@ import org.eclipse.mylyn.commons.tests.core.AuthenticatedProxyTest; import org.eclipse.mylyn.commons.tests.core.CommonListenerListTest; import org.eclipse.mylyn.commons.tests.core.CoreUtilTest; import org.eclipse.mylyn.commons.tests.core.ExtensionPointReaderTest; +import org.eclipse.mylyn.commons.tests.core.storage.CommonStoreTest; import org.eclipse.mylyn.commons.tests.net.NetUtilTest; import org.eclipse.mylyn.commons.tests.net.SslProtocolSocketFactoryTest; import org.eclipse.mylyn.commons.tests.net.TimeoutInputStreamTest; @@ -42,6 +43,7 @@ public class AllCommonsTests { suite.addTestSuite(BrowserUtilTest.class); suite.addTestSuite(ExtensionPointReaderTest.class); suite.addTestSuite(CommonListenerListTest.class); + suite.addTestSuite(CommonStoreTest.class); return suite; } diff --git a/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/CoreUtilTest.java b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/CoreUtilTest.java index a729f9b3..aec0326b 100644 --- a/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/CoreUtilTest.java +++ b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/CoreUtilTest.java @@ -184,4 +184,20 @@ public class CoreUtilTest extends TestCase { } } + public void testAsFileName() { + assertEquals("abc", CoreUtil.asFileName("abc")); + assertEquals("a.b.c", CoreUtil.asFileName("a.b.c")); + assertEquals("", CoreUtil.asFileName("")); + } + + public void testAsFileNameSpaces() { + assertEquals("%20%20", CoreUtil.asFileName(" ")); + assertEquals(".%20", CoreUtil.asFileName(". ")); + } + + public void testAsFileNamePercent() { + assertEquals("%25abc", CoreUtil.asFileName("%abc")); + assertEquals("%2525abc", CoreUtil.asFileName("%25abc")); + } + } diff --git a/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/storage/CommonStoreTest.java b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/storage/CommonStoreTest.java new file mode 100644 index 00000000..eb11e304 --- /dev/null +++ b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/storage/CommonStoreTest.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2012 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.tests.core.storage; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; + +import junit.framework.TestCase; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.mylyn.commons.core.storage.CommonStore; +import org.eclipse.mylyn.commons.core.storage.ICommonStorable; +import org.eclipse.mylyn.commons.sdk.util.CommonTestUtil; + +/** + * @author Steffen Pingel + */ +public class CommonStoreTest extends TestCase { + + private File location; + + private CommonStore store; + + public void testDelete() throws Exception { + ICommonStorable storable = store.get(Path.EMPTY); + assertFalse(storable.exists("handle")); + + OutputStream out = storable.write("handle", null); + out.close(); + assertTrue(storable.exists("handle")); + assertEquals(Collections.singletonList(new File(location, "handle")), Arrays.asList(location.listFiles())); + + storable.delete("handle"); + assertFalse(storable.exists("handle")); + assertEquals(Collections.emptyList(), Arrays.asList(location.listFiles())); + } + + public void testExists() throws Exception { + ICommonStorable storable = store.get(Path.EMPTY); + assertFalse(storable.exists("handle")); + + OutputStream out = storable.write("handle", null); + out.close(); + assertTrue(storable.exists("handle")); + } + + public void testGetPathWrite() throws Exception { + ICommonStorable storable = store.get(new Path("sub")); + writeHello(storable, "handle"); + File subFile = new File(location, "sub"); + assertEquals(Collections.singletonList(subFile), Arrays.asList(location.listFiles())); + assertEquals(Collections.singletonList(new File(subFile, "handle")), Arrays.asList(subFile.listFiles())); + } + + public void testGet() throws Exception { + ICommonStorable storable = store.get(Path.EMPTY); + ICommonStorable storable2 = store.get(Path.EMPTY); + assertSame(storable, storable2); + } + + public void testGetPath() { + ICommonStorable storable = store.get(new Path("sub")); + ICommonStorable storable2 = store.get(Path.EMPTY); + assertNotSame(storable, storable2); + storable2 = store.get(new Path("sub")); + assertSame(storable, storable2); + } + + public void testGetPathLazyCreate() { + ICommonStorable storable = store.get(new Path("sub")); + assertEquals(Collections.emptyList(), Arrays.asList(location.listFiles())); + assertFalse(storable.exists("handle")); + assertEquals(Collections.emptyList(), Arrays.asList(location.listFiles())); + } + + public void testRelease() throws Exception { + ICommonStorable storable = store.get(Path.EMPTY); + storable.release(); + ICommonStorable storable2 = store.get(Path.EMPTY); + assertNotSame(storable, storable2); + } + + public void testWriteRead() throws Exception { + ICommonStorable storable = store.get(Path.EMPTY); + writeHello(storable, "handle"); + assertTrue(storable.exists("handle")); + + InputStream in = storable.read("handle", null); + try { + byte[] buffer = new byte[5]; + in.read(buffer); + assertEquals("hello", new String(buffer)); + } finally { + in.close(); + } + } + + private void writeHello(ICommonStorable storable, String handle) throws IOException, CoreException { + OutputStream out = storable.write(handle, null); + try { + out.write("hello".getBytes()); + } finally { + out.close(); + } + } + + @Override + protected void setUp() throws Exception { + location = CommonTestUtil.createTempFolder(CommonStoreTest.class.getName()); + store = new CommonStore(location); + } + + @Override + protected void tearDown() throws Exception { + CommonTestUtil.deleteFolderRecursively(location); + } + +} |