You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2013/08/12 17:33:12 UTC
svn commit: r1513170 - in /ace/trunk:
org.apache.ace.range.api/src/org/apache/ace/range/
org.apache.ace.range.api/test/org/apache/ace/range/
org.apache.ace.repository/src/org/apache/ace/repository/
org.apache.ace.repository/src/org/apache/ace/repositor...
Author: marrs
Date: Mon Aug 12 15:33:12 2013
New Revision: 1513170
URL: http://svn.apache.org/r1513170
Log:
ACE-331 Repositories can now be configured to store a limited number of versions. Replication takes this into account during synchronisation. Extended the SortedRangeSet with some new features for this.
Modified:
ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/Range.java
ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/RangeIterator.java
ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/SortedRangeSet.java
ace/trunk/org.apache.ace.range.api/test/org/apache/ace/range/SortedRangeSetTest.java
ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java
ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java
ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java
ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java
ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java
ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java
ace/trunk/org.apache.ace.repository/test/org/apache/ace/repository/impl/RepositoryImplTest.java
Modified: ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/Range.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/Range.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/Range.java (original)
+++ ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/Range.java Mon Aug 12 15:33:12 2013
@@ -21,8 +21,7 @@ package org.apache.ace.range;
/**
* Class that captures a simple, modifiable range.
*/
-public class Range
-{
+public class Range {
private long m_low;
private long m_high;
@@ -133,7 +132,8 @@ public class Range
/**
* Converts the range to a string representation that can be parsed
* back to a new <code>Range</code> object.
- * @return
+ *
+ * @return string representation
*/
public String toRepresentation() {
if (m_low == m_high) {
@@ -143,4 +143,9 @@ public class Range
return Long.toString(m_low) + '-' + Long.toString(m_high);
}
}
+
+ @Override
+ public String toString() {
+ return "Range[" + toRepresentation() + "]";
+ }
}
\ No newline at end of file
Modified: ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/RangeIterator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/RangeIterator.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/RangeIterator.java (original)
+++ ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/RangeIterator.java Mon Aug 12 15:33:12 2013
@@ -18,7 +18,7 @@
*/
package org.apache.ace.range;
-import java.util.Iterator;
+import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
@@ -28,17 +28,26 @@ import java.util.NoSuchElementException;
* is not thread-safe and results are unpredictable if the underlying set is
* modified.
*/
-public class RangeIterator
-{
- private final Iterator m_iterator;
+public class RangeIterator {
+ private final ListIterator m_iterator;
private Range m_current;
private long m_number;
+ private boolean m_reverseOrder;
- RangeIterator(Iterator iterator) {
+ RangeIterator(ListIterator iterator, boolean reverseOrder) {
m_iterator = iterator;
+ m_reverseOrder = reverseOrder;
}
public boolean hasNext() {
+ return m_reverseOrder ? hasPreviousElement() : hasNextElement();
+ }
+
+ public long next() {
+ return m_reverseOrder ? previousElement() : nextElement();
+ }
+
+ private boolean hasNextElement() {
if (m_current == null) {
return m_iterator.hasNext();
}
@@ -49,8 +58,20 @@ public class RangeIterator
return true;
}
}
+
+ private boolean hasPreviousElement() {
+ if (m_current == null) {
+ return m_iterator.hasPrevious();
+ }
+ if (m_number == m_current.getLow()) {
+ return m_iterator.hasPrevious();
+ }
+ else {
+ return true;
+ }
+ }
- public long next() {
+ private long nextElement() {
if (m_current == null) {
if (m_iterator.hasNext()) {
m_current = (Range) m_iterator.next();
@@ -73,4 +94,28 @@ public class RangeIterator
}
throw new NoSuchElementException();
}
+
+ private long previousElement() {
+ if (m_current == null) {
+ if (m_iterator.hasPrevious()) {
+ m_current = (Range) m_iterator.previous();
+ m_number = m_current.getHigh();
+ return m_number;
+ }
+ }
+ else {
+ if (m_number == m_current.getLow()) {
+ if (m_iterator.hasPrevious()) {
+ m_current = (Range) m_iterator.previous();
+ m_number = m_current.getHigh();
+ return m_number;
+ }
+ }
+ else {
+ m_number--;
+ return m_number;
+ }
+ }
+ throw new NoSuchElementException();
+ }
}
\ No newline at end of file
Modified: ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/SortedRangeSet.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/SortedRangeSet.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/SortedRangeSet.java (original)
+++ ace/trunk/org.apache.ace.range.api/src/org/apache/ace/range/SortedRangeSet.java Mon Aug 12 15:33:12 2013
@@ -19,6 +19,7 @@
package org.apache.ace.range;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -28,8 +29,7 @@ import java.util.StringTokenizer;
* Collection that stores a sorted set of ranges and is able to represent them
* as a string.
*/
-public class SortedRangeSet
-{
+public class SortedRangeSet {
/**
* A static set which contains all possible values.
*/
@@ -55,19 +55,23 @@ public class SortedRangeSet
}
/**
- * Creates a new instance from an array of longs.
+ * Creates a new instance from an array of longs. The array can contain the longs in random order,
+ * and duplicates will be filtered out.
*
* @param items Array of longs
*/
public SortedRangeSet(long[] items) {
- // TODO: deal with items not being in ascending order
+ Arrays.sort(items);
Range r = null;
for (int i = 0; i < items.length; i++) {
if (r == null) {
r = new Range(items[i]);
}
else {
- if (items[i] == r.getHigh() + 1) {
+ if (items[i] == r.getHigh()) {
+ // ignore this duplicate
+ }
+ else if (items[i] == r.getHigh() + 1) {
r.setHigh(items[i]);
}
else {
@@ -107,6 +111,7 @@ public class SortedRangeSet
* <code>result = dest \ this</code>,<br>
* that is, if <code>dest = {1, 2}</code> and <code>this = {2, 3}</code>, then
* <code>result = {1, 2} \ {2, 3} = {1}</code>
+ *
* @param dest The set from which this set should be 'set-minussed'.
* @return The resulting set after the diff.
*/
@@ -155,7 +160,7 @@ public class SortedRangeSet
long low = r.getLow();
long high = r.getHigh();
if (number < low) {
- if (number == low - 1) {
+ if (number == (low - 1)) {
r.setLow(number);
return;
}
@@ -165,11 +170,11 @@ public class SortedRangeSet
return;
}
}
- if (number == high + 1) {
+ if (number == (high + 1)) {
r.setHigh(number);
if (i.hasNext()) {
Range nr = (Range) i.next();
- if (number == low - 1) {
+ if (number == nr.getLow() - 1) {
r.setHigh(nr.getHigh());
i.remove();
}
@@ -187,7 +192,16 @@ public class SortedRangeSet
* @return a range iterator
*/
public RangeIterator iterator() {
- return new RangeIterator(m_ranges.iterator());
+ return new RangeIterator(m_ranges.listIterator(), false);
+ }
+
+ /**
+ * Returns an iterator that iterates over all the ranges in this set in reverse order.
+ *
+ * @return a range iterator
+ */
+ public RangeIterator reverseIterator() {
+ return new RangeIterator(m_ranges.listIterator(m_ranges.size()), true);
}
/**
@@ -215,4 +229,30 @@ public class SortedRangeSet
return 0;
}
}
+
+ /**
+ * Returns the union of this set and the provided set.
+ *
+ * @param dest a set to union with ourselves
+ * @return the resulting set
+ */
+ public SortedRangeSet union(SortedRangeSet dest) {
+ SortedRangeSet result = new SortedRangeSet();
+ RangeIterator i = dest.iterator();
+ while (i.hasNext()) {
+ long number = i.next();
+ result.add(number);
+ }
+ i = iterator();
+ while (i.hasNext()) {
+ long number = i.next();
+ result.add(number);
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "SortedRangeSet[" + toRepresentation() + "]";
+ }
}
\ No newline at end of file
Modified: ace/trunk/org.apache.ace.range.api/test/org/apache/ace/range/SortedRangeSetTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.range.api/test/org/apache/ace/range/SortedRangeSetTest.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.range.api/test/org/apache/ace/range/SortedRangeSetTest.java (original)
+++ ace/trunk/org.apache.ace.range.api/test/org/apache/ace/range/SortedRangeSetTest.java Mon Aug 12 15:33:12 2013
@@ -22,6 +22,7 @@ import static org.apache.ace.test.utils.
import java.util.Iterator;
+import org.testng.Assert;
import org.testng.annotations.Test;
public class SortedRangeSetTest {
@@ -68,6 +69,12 @@ public class SortedRangeSetTest {
assert new SortedRangeSet("1-20").diffDest(new SortedRangeSet("5-25")).toRepresentation().equals("21-25") : "Result of diff should be 21-25";
assert new SortedRangeSet(new long[] {1,3,5,7,9}).diffDest(new SortedRangeSet("1-10")).toRepresentation().equals("2,4,6,8,10") : "Result of diff should be 2,4,6,8,10";
assert new SortedRangeSet("1-5,8,12").diffDest(new SortedRangeSet("1-5,7,9,12,20")).toRepresentation().equals("7,9,20") : "Result of diff should be 7,9,20";
+ assert new SortedRangeSet("1").union(new SortedRangeSet("2")).toRepresentation().equals("1-2") : "Result of union should be 1-2";
+ assert new SortedRangeSet("1-4").union(new SortedRangeSet("6-9")).toRepresentation().equals("1-4,6-9") : "Result of union should be 1-4,6-9";
+ Assert.assertEquals(new SortedRangeSet("1-3").union(new SortedRangeSet("4")).toRepresentation(), "1-4", "Result of union failed.");
+ Assert.assertEquals(new SortedRangeSet("5-10").union(new SortedRangeSet("4")).toRepresentation(), "4-10", "Result of union failed.");
+ Assert.assertEquals(new SortedRangeSet("1-3,5-10").union(new SortedRangeSet("4")).toRepresentation(), "1-10", "Result of union failed.");
+ Assert.assertEquals(new SortedRangeSet("1-5,8,12").union(new SortedRangeSet("4-8,9-11")).toRepresentation(), "1-12", "Result of union failed.");
}
@Test(groups = { UNIT })
@@ -86,6 +93,51 @@ public class SortedRangeSetTest {
SortedRangeSet srs3 = new SortedRangeSet("");
assert !srs3.iterator().hasNext() : "Iterator should be empty.";
}
+
+ @Test(groups = { UNIT })
+ public void validateReverseRangeIterators() throws Exception {
+ SortedRangeSet srs1 = new SortedRangeSet("1-10");
+ RangeIterator i1 = srs1.reverseIterator();
+ for (long i = 10; i > 0; i--) {
+ Assert.assertEquals(i1.next(), i);
+ }
+ Assert.assertFalse(i1.hasNext(), "We should have iterated over all elements of our simple range.");
+ SortedRangeSet srs2 = new SortedRangeSet("1-5,8,10-15");
+ RangeIterator i2 = srs2.reverseIterator();
+ long[] i2s = { 15, 14, 13, 12, 11, 10, 8, 5, 4, 3, 2, 1};
+ for (int i = 0; i < i2s.length; i++) {
+ Assert.assertEquals(i2.next(), i2s[i]);
+ }
+ Assert.assertFalse(i2.hasNext(), "We should have iterated over all elements of our complex range.");
+
+ SortedRangeSet srs3 = new SortedRangeSet("");
+ assert !srs3.reverseIterator().hasNext() : "Iterator should be empty.";
+ }
+
+ @Test(groups = { UNIT })
+ public void validateSortedRangeSetArrayConstructor() throws Exception {
+ long[] a1 = new long[] {1,2,3,5,10};
+ SortedRangeSet s1 = new SortedRangeSet(a1);
+ RangeIterator i1 = s1.iterator();
+ for (int i = 0; i < a1.length; i++) {
+ Assert.assertEquals(i1.next(), a1[i]);
+ }
+ Assert.assertEquals(s1.toRepresentation(), "1-3,5,10");
+ Assert.assertFalse(i1.hasNext(), "We should have iterated over all elements of our range.");
+
+ long[] a2 = new long[] {10,2,3,5,10,2,1,5,1};
+ long[] a2s = new long[] {1,2,3,5,10};
+ SortedRangeSet s2 = new SortedRangeSet(a2);
+ RangeIterator i2 = s2.iterator();
+ for (int i = 0; i < a2s.length; i++) {
+ Assert.assertEquals(i2.next(), a2s[i]);
+ }
+ Assert.assertEquals(s2.toRepresentation(), "1-3,5,10");
+ Assert.assertFalse(i2.hasNext(), "We should have iterated over all elements of our range.");
+ Assert.assertEquals((new SortedRangeSet(new long[] {})).toRepresentation(), (new SortedRangeSet(new long[] {})).toRepresentation());
+ Assert.assertEquals((new SortedRangeSet(new long[] {1})).toRepresentation(), (new SortedRangeSet(new long[] {1,1,1})).toRepresentation());
+ Assert.assertEquals((new SortedRangeSet(new long[] {3,2,1})).toRepresentation(), (new SortedRangeSet(new long[] {1,2,3,2,1})).toRepresentation());
+ }
@Test(groups = { UNIT }, expectedExceptions = IllegalArgumentException.class)
public void invalidRange() {
Modified: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java (original)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java Mon Aug 12 15:33:12 2013
@@ -53,4 +53,9 @@ public interface RepositoryReplication
* @throws IllegalArgumentException If the version number is not greater than 0.
*/
public boolean put(InputStream data, long version) throws IOException, IllegalArgumentException;
+
+ /**
+ * Returns the maximum number of versions this repository will store.
+ */
+ public long getLimit();
}
\ No newline at end of file
Modified: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java (original)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java Mon Aug 12 15:33:12 2013
@@ -145,6 +145,20 @@ public class RepositoryFactory implement
if ((m_baseDir != null) && !m_baseDir.isDirectory() && !m_baseDir.mkdirs()) {
throw new IllegalArgumentException("Unable to create base directory (" + m_baseDir.getAbsolutePath() + ")");
}
+
+ String limit = (String) dict.get(RepositoryConstants.REPOSITORY_LIMIT);
+ long limitValue = Long.MAX_VALUE;
+ if (limit != null) {
+ try {
+ limitValue = Long.parseLong(limit);
+ }
+ catch (NumberFormatException nfe) {
+ throw new ConfigurationException(RepositoryConstants.REPOSITORY_LIMIT, "Limit has to be a number, was: " + limit);
+ }
+ if (limitValue < 1) {
+ throw new ConfigurationException(RepositoryConstants.REPOSITORY_LIMIT, "Limit has to be at least 1, was " + limit);
+ }
+ }
String initialContents = (String) dict.get(RepositoryConstants.REPOSITORY_INITIAL_CONTENT);
if (m_prefs == null) {
@@ -173,7 +187,7 @@ public class RepositoryFactory implement
if (service == null) {
// new instance
File dir = new File(m_baseDir, pid);
- RepositoryImpl store = new RepositoryImpl(dir, m_tempDir, fileExtension, isMaster);
+ RepositoryImpl store = new RepositoryImpl(dir, m_tempDir, fileExtension, isMaster, limitValue);
if ((initialContents != null) && isMaster) {
try {
store.commit(new ByteArrayInputStream(initialContents.getBytes()), 0);
@@ -192,7 +206,7 @@ public class RepositoryFactory implement
else {
// update existing instance
RepositoryImpl store = (RepositoryImpl) service.getService();
- store.updated(isMaster);
+ store.updated(isMaster, limitValue);
}
}
}
Modified: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java (original)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java Mon Aug 12 15:33:12 2013
@@ -31,6 +31,7 @@ import java.util.Arrays;
import org.apache.ace.range.SortedRangeSet;
import org.apache.ace.repository.Repository;
import org.apache.ace.repository.RepositoryReplication;
+import org.apache.ace.repository.impl.constants.RepositoryConstants;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.log.LogService;
@@ -49,6 +50,7 @@ public class RepositoryImpl implements R
private volatile LogService m_log; /* will be injected by dependency manager */
private volatile boolean m_isMaster;
+ private volatile long m_limit;
private final File m_tempDir;
private final File m_dir;
@@ -78,6 +80,35 @@ public class RepositoryImpl implements R
* directory.
*/
public RepositoryImpl(File dir, File temp, String fileExtension, boolean isMaster) {
+ this(dir, temp, fileExtension, isMaster, Long.MAX_VALUE);
+ }
+
+ /**
+ * Creates a new repository.
+ *
+ * @param dir Directory to be used for storage of the repository data, will be created if needed.
+ * @param temp Directory to be used as temp directory, will be created if needed.
+ * @param isMaster True if this repository is a master repository, false otherwise.
+ * @param limit The maximum number of versions to store in this repository.
+ * @throws IllegalArgumentException If <code>dir</code> and/or <code>temp</code> could not be created or is not a
+ * directory.
+ */
+ public RepositoryImpl(File dir, File temp, boolean isMaster, long limit) {
+ this(dir, temp, "", isMaster, limit);
+ }
+
+ /**
+ * Creates a new repository.
+ *
+ * @param dir Directory to be used for storage of the repository data, will be created if needed.
+ * @param temp Directory to be used as temp directory, will be created if needed.
+ * @param fileExtension Extension to be used for repository files.
+ * @param isMaster True if this repository is a master repository, false otherwise.
+ * @param limit The maximum number of versions to store in this repository.
+ * @throws IllegalArgumentException If <code>dir</code> and/or <code>temp</code> could not be created or is not a
+ * directory.
+ */
+ public RepositoryImpl(File dir, File temp, String fileExtension, boolean isMaster, long limit) {
m_isMaster = isMaster;
if (!dir.isDirectory() && !dir.mkdirs()) {
throw new IllegalArgumentException("Repository location is not a valid directory (" + dir.getAbsolutePath() + ")");
@@ -88,10 +119,15 @@ public class RepositoryImpl implements R
if (fileExtension == null) {
throw new IllegalArgumentException("File extension must not be null");
}
+ if (limit < 1) {
+ throw new IllegalArgumentException("Limit must be at least 1, was " + limit);
+ }
m_tempDir = temp;
m_dir = dir;
m_fileExtension = fileExtension;
+ m_limit = limit;
}
+
public InputStream get(long version) throws IOException, IllegalArgumentException {
return checkout(version);
@@ -176,6 +212,13 @@ public class RepositoryImpl implements R
long lastVersion = versions[versions.length - 1];
if (lastVersion == fromVersion) {
put(data, fromVersion + 1);
+ long length = versions.length + 1;
+ int index = 0;
+ while (length > m_limit) {
+ delete(versions[index]);
+ index++;
+ length--;
+ }
return true;
}
else {
@@ -214,8 +257,33 @@ public class RepositoryImpl implements R
* @param isMaster True if the repository is a master repository, false otherwise.
* @throws ConfigurationException If it was impossible to use the new configuration.
*/
- public void updated(boolean isMaster) throws ConfigurationException {
+ public void updated(boolean isMaster, long limit) throws ConfigurationException {
+ if (limit < 1) {
+ throw new ConfigurationException(RepositoryConstants.REPOSITORY_LIMIT, "Limit must be at least 1, was " + limit);
+ }
m_isMaster = isMaster;
+ if (limit < m_limit) {
+ // limit was decreased, we might need to delete some old versions
+ try {
+ long[] versions = getVersions();
+ int length = versions.length;
+ int index = 0;
+ while (length > limit) {
+ delete(versions[index]);
+ length--;
+ index++;
+ }
+ }
+ catch (IOException e) {
+ throw new ConfigurationException(RepositoryConstants.REPOSITORY_LIMIT, "Could not set new limit to " + limit, e);
+ }
+ }
+ m_limit = limit;
+ }
+
+ @Override
+ public long getLimit() {
+ return m_limit;
}
/**
@@ -316,4 +384,12 @@ public class RepositoryImpl implements R
// Ignored...
}
}
+
+ private boolean delete(long version) throws IOException, IllegalArgumentException {
+ if (version <= 0) {
+ throw new IllegalArgumentException("Version must be greater than 0.");
+ }
+ File file = new File(m_dir, String.valueOf(version) + m_fileExtension);
+ return file.delete();
+ }
}
Modified: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java (original)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java Mon Aug 12 15:33:12 2013
@@ -26,4 +26,5 @@ public interface RepositoryConstants
public static final String REPOSITORY_INITIAL_CONTENT = "initial";
public static final String REPOSITORY_BASE_DIR = "basedir";
public static final String REPOSITORY_FILE_EXTENSION = "fileextension";
+ public static final String REPOSITORY_LIMIT = "limit";
}
Modified: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java (original)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java Mon Aug 12 15:33:12 2013
@@ -22,6 +22,7 @@ import java.util.Properties;
import org.apache.ace.connectionfactory.ConnectionFactory;
import org.apache.ace.discovery.Discovery;
+import org.apache.ace.repository.RepositoryReplication;
import org.apache.ace.scheduler.constants.SchedulerConstants;
import org.apache.felix.dm.DependencyActivatorBase;
import org.apache.felix.dm.DependencyManager;
@@ -41,6 +42,7 @@ public class Activator extends Dependenc
.setImplementation(RepositoryReplicationTask.class)
.add(createServiceDependency().setService(Discovery.class).setRequired(true))
.add(createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
+ .add(createServiceDependency().setService(RepositoryReplication.class).setRequired(false).setCallbacks("add", "remove"))
.add(createServiceDependency().setService(LogService.class).setRequired(false))
);
}
Modified: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java (original)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java Mon Aug 12 15:33:12 2013
@@ -19,9 +19,16 @@
package org.apache.ace.repository.task;
import java.io.BufferedReader;
+import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
import java.net.URL;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
import javax.servlet.http.HttpServletResponse;
@@ -45,69 +52,93 @@ public class RepositoryReplicationTask i
private volatile Discovery m_discovery;
private volatile ConnectionFactory m_connectionFactory;
private volatile LogService m_log;
-
+ private final Map<ServiceReference, RepositoryReplication> m_replicators = new HashMap<ServiceReference, RepositoryReplication>();
+
+ public void add(ServiceReference ref, RepositoryReplication service) {
+ synchronized (m_replicators) {
+ m_replicators.put(ref, service);
+ }
+ }
+
+ public void remove(ServiceReference ref) {
+ synchronized (m_replicators) {
+ m_replicators.remove(ref);
+ }
+ }
+
public void run() {
+ Entry<ServiceReference,RepositoryReplication>[] replicators;
+ synchronized (m_replicators) {
+ Set<Entry<ServiceReference,RepositoryReplication>> entries = m_replicators.entrySet();
+ replicators = entries.toArray(new Entry[entries.size()]);
+ }
+
try {
- ServiceReference[] refs = m_context.getServiceReferences(RepositoryReplication.class.getName(), null);
- if (refs == null) {
- return;
+ for (Entry<ServiceReference,RepositoryReplication> entry : replicators) {
+ replicate(entry);
}
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Error while replicating", e);
+ }
+ }
- for (ServiceReference ref : refs) {
- RepositoryReplication repository = (RepositoryReplication) m_context.getService(ref);
-
- try {
- String filter = getQueryFilter(ref);
- URL host = m_discovery.discover();
- URL query = new URL(host, "/replication/query?" + filter);
-
- HttpURLConnection connection = (HttpURLConnection) m_connectionFactory.createConnection(query);
-
- if (connection.getResponseCode() == HttpServletResponse.SC_OK) {
- SortedRangeSet localRange = repository.getRange();
-
- BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
- try {
- String line = reader.readLine();
- int i = line.lastIndexOf(',');
- if (i > 0) {
- SortedRangeSet remoteRange = new SortedRangeSet(line.substring(i + 1));
- SortedRangeSet delta = localRange.diffDest(remoteRange);
- RangeIterator iterator = delta.iterator();
-
- while (iterator.hasNext()) {
- long version = iterator.next();
- URL get = new URL(host, "/replication/get?" + filter + "&version=" + version);
-
- HttpURLConnection connection2 = (HttpURLConnection) m_connectionFactory.createConnection(get);
-
- repository.put(connection2.getInputStream(), version);
- }
- }
- }
- catch (Exception e) {
- m_log.log(LogService.LOG_WARNING, "Error parsing remote range", e);
+ private void replicate(Entry<ServiceReference, RepositoryReplication> entry) throws MalformedURLException, IOException {
+ ServiceReference ref = entry.getKey();
+ RepositoryReplication repository = entry.getValue();
+ String filter = "customer=" + ref.getProperty("customer") + "&name=" + ref.getProperty("name");
+ URL host = m_discovery.discover();
+ URL query = new URL(host, "/replication/query?" + filter);
+
+ HttpURLConnection connection = (HttpURLConnection) m_connectionFactory.createConnection(query);
+ if (connection.getResponseCode() == HttpServletResponse.SC_OK) {
+ SortedRangeSet localRange = repository.getRange();
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ try {
+ String line = reader.readLine();
+ int i = line.lastIndexOf(',');
+ if (i > 0) {
+ SortedRangeSet remoteRange = new SortedRangeSet(line.substring(i + 1));
+
+ // check the limit of the repository
+ long limit = repository.getLimit();
+ if (limit == Long.MAX_VALUE) {
+ // no limit, sync all
+ SortedRangeSet delta = localRange.diffDest(remoteRange);
+ RangeIterator iterator = delta.iterator();
+ while (iterator.hasNext()) {
+ long version = iterator.next();
+ URL get = new URL(host, "/replication/get?" + filter + "&version=" + version);
+ HttpURLConnection connection2 = (HttpURLConnection) m_connectionFactory.createConnection(get);
+ repository.put(connection2.getInputStream(), version);
}
}
else {
- m_log.log(LogService.LOG_WARNING, "Could not sync repository for customer: " + ref.getProperty("customer") + ", name: " + ref.getProperty("name") + ", because: " + connection.getResponseMessage() + " (" + connection.getResponseCode() + ")");
+ // limit, try to get the the 'limit' newest versions
+ SortedRangeSet union = localRange.union(remoteRange);
+ RangeIterator iterator = union.reverseIterator();
+ while (iterator.hasNext() && limit > 0) {
+ long version = iterator.next();
+ if (!localRange.contains(version)) {
+ URL get = new URL(host, "/replication/get?" + filter + "&version=" + version);
+ HttpURLConnection connection2 = (HttpURLConnection) m_connectionFactory.createConnection(get);
+ repository.put(connection2.getInputStream(), version);
+ }
+ limit--;
+ }
}
}
- finally {
- m_context.ungetService(ref);
- }
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Error parsing remote range", e);
+ }
+ finally {
+ reader.close();
}
}
- catch (Exception e) {
- m_log.log(LogService.LOG_WARNING, "Error while replicating", e);
+ else {
+ m_log.log(LogService.LOG_WARNING, "Could not sync repository for customer: " + ref.getProperty("customer") + ", name: " + ref.getProperty("name") + ", because: " + connection.getResponseMessage() + " (" + connection.getResponseCode() + ")");
}
}
-
- /**
- * @param ref
- * @return
- */
- public String getQueryFilter(ServiceReference ref) {
- return "customer=" + ref.getProperty("customer") + "&name=" + ref.getProperty("name");
- }
}
\ No newline at end of file
Modified: ace/trunk/org.apache.ace.repository/test/org/apache/ace/repository/impl/RepositoryImplTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/test/org/apache/ace/repository/impl/RepositoryImplTest.java?rev=1513170&r1=1513169&r2=1513170&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository/test/org/apache/ace/repository/impl/RepositoryImplTest.java (original)
+++ ace/trunk/org.apache.ace.repository/test/org/apache/ace/repository/impl/RepositoryImplTest.java Mon Aug 12 15:33:12 2013
@@ -28,6 +28,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import org.apache.ace.range.SortedRangeSet;
+import static org.testng.Assert.*;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -129,7 +131,7 @@ public class RepositoryImplTest {
@Test(groups = { UNIT }, expectedExceptions = { IllegalStateException.class })
public void testUpdated() throws Exception {
RepositoryImpl repo = new RepositoryImpl(new File(m_baseDir, "data"), new File(m_baseDir, "tmp"), true);
- repo.updated(false);
+ repo.updated(false, Long.MAX_VALUE);
assert !repo.commit(new ByteArrayInputStream("abc".getBytes()), 0) : "Committing should not be allowed on slave repositories.";
assert repo.put(new ByteArrayInputStream("abc".getBytes()), 1) : "'put'ting a replica should be allowed on slave repositories.";
File file = new File(m_baseDir, "newLocation" + File.separator + "1");
@@ -147,4 +149,32 @@ public class RepositoryImplTest {
BufferedReader reader = new BufferedReader(new FileReader(file));
assert "abc".equals(reader.readLine()) : "File " + file.getAbsolutePath() + " should have contained 'abc'.";
}
+
+ @Test(groups = { UNIT })
+ public void testCommitMultiple() throws Exception {
+ RepositoryImpl repo = new RepositoryImpl(new File(m_baseDir, "data"), new File(m_baseDir, "tmp"), true);
+ assertTrue(repo.commit(new ByteArrayInputStream("abc-1".getBytes()), 0), "Commit should have worked.");
+ assertTrue(repo.commit(new ByteArrayInputStream("abc-2".getBytes()), 1), "Commit should have worked.");
+ assertTrue(repo.commit(new ByteArrayInputStream("abc-3".getBytes()), 2), "Commit should have worked.");
+ SortedRangeSet range = repo.getRange();
+ assertTrue(range.getHigh() == 3, "We should have 3 versions in the repository.");
+ }
+
+ @Test(groups = { UNIT })
+ public void testCommitToLimitedRepository() throws Exception {
+ RepositoryImpl repo = new RepositoryImpl(new File(m_baseDir, "data"), new File(m_baseDir, "tmp"), true, 2);
+ assertTrue(repo.commit(new ByteArrayInputStream("abc-1".getBytes()), 0), "Commit should have worked.");
+ assertTrue(repo.commit(new ByteArrayInputStream("abc-2".getBytes()), 1), "Commit should have worked.");
+ assertTrue(repo.commit(new ByteArrayInputStream("abc-3".getBytes()), 2), "Commit should have worked.");
+ assertNotNull(repo.checkout(3));
+ assertNotNull(repo.checkout(2));
+ assertNull(repo.checkout(1));
+ repo.updated(true, 3);
+ assertTrue(repo.commit(new ByteArrayInputStream("abc-4".getBytes()), 3), "Commit should have worked.");
+ assertNotNull(repo.checkout(2));
+ repo.updated(true, 1);
+ assertNull(repo.checkout(2));
+ assertNull(repo.checkout(3));
+ assertNotNull(repo.checkout(4));
+ }
}