Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

MODE-1818 Repository configuration can now dictate garbage collection…

… options.

The garbage collection options can now be specified in the repository
configuration. Each repository registers its own garbage collection
processes with a named thread pool, and these processes are stopped
when the repository is shut down.
  • Loading branch information...
commit 38505e44e81b91cb0a7210d83a838d4949e68bc8 1 parent 788b580
@rhauch rhauch authored
View
30 integration/modeshape-jbossas-integration-tests/src/test/java/org/modeshape/test/integration/CDITest.java
@@ -59,6 +59,36 @@ public static WebArchive createWarDeployment() {
return archive;
}
+ /**
+ * Creates the following deployable:
+ *
+ * cdi-test-ear.ear
+ * /lib/cdi-ear-producer.jar
+ * /META-INF/beans.xml
+ * /cdi-ear-test.war
+ * /WEB-INF/beans.xml
+ * /META-INF/MANIFEST.MF
+ * @return the archive
+ */
+ @Deployment(name = "cdi-test-ear")
+ public static EnterpriseArchive createEarDeployment() {
+
+ JavaArchive providerLib = ShrinkWrap.create(JavaArchive.class, "cdi-ear-producer.jar")
+ .addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create(
+ "beans.xml"))
+ .addClass(CDIRepositoryProvider.class);
+
+ WebArchive webapp = ShrinkWrap.create(WebArchive.class, "cdi-ear-test.war")
+ .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))
+ .addClass(CDITest.class)
+ .addClass(CDIRepositoryConsumer.class);
+
+ return ShrinkWrap.create(EnterpriseArchive.class, "cdi-ear-test.ear")
+ .addAsModule(webapp)
+ .addAsLibraries(providerLib)
+ .addAsManifestResource(new File("src/main/webapp/META-INF/MANIFEST.MF"));
+ }
+
@Inject
private CDIRepositoryConsumer consumer;
View
3  modeshape-common/src/main/java/org/modeshape/common/util/ThreadPoolFactory.java
@@ -24,6 +24,7 @@
package org.modeshape.common.util;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
@@ -57,7 +58,7 @@
* @param name the name of the thread pool; may not be null
* @return the thread pool executor; never null
*/
- ExecutorService getScheduledThreadPool( String name );
+ ScheduledExecutorService getScheduledThreadPool( String name );
/**
* Performs a {@link java.util.concurrent.ExecutorService#shutdownNow()} on the given pool, if the pool has been created
View
8 modeshape-common/src/main/java/org/modeshape/common/util/ThreadPools.java
@@ -29,6 +29,7 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.modeshape.common.annotation.ThreadSafe;
@@ -54,9 +55,10 @@ public ExecutorService getCachedTreadPool( String name ) {
}
@Override
- public ExecutorService getScheduledThreadPool( String name ) {
- return getOrCreateNewPool(name,
- Executors.newScheduledThreadPool(DEFAULT_SCHEDULED_THREAD_COUNT, new NamedThreadFactory(name)));
+ public ScheduledExecutorService getScheduledThreadPool( String name ) {
+ return (ScheduledExecutorService)getOrCreateNewPool(name,
+ Executors.newScheduledThreadPool(DEFAULT_SCHEDULED_THREAD_COUNT,
+ new NamedThreadFactory(name)));
}
private ExecutorService getOrCreateNewPool( String name,
View
3  modeshape-jcr/src/main/java/org/modeshape/jcr/ExecutionContext.java
@@ -32,6 +32,7 @@
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.modeshape.common.SystemFailureException;
import org.modeshape.common.annotation.Immutable;
@@ -418,7 +419,7 @@ public ExecutorService getCachedTreadPool( String name ) {
}
@Override
- public ExecutorService getScheduledThreadPool( String name ) {
+ public ScheduledExecutorService getScheduledThreadPool( String name ) {
return this.threadPools.getScheduledThreadPool(name);
}
View
4 modeshape-jcr/src/main/java/org/modeshape/jcr/JcrI18n.java
@@ -198,6 +198,8 @@
public static I18n errorDuringInitialImport;
public static I18n nodeTypesNotFoundInXml;
+ public static I18n invalidGarbageCollectionInitialTime;
+
public static I18n failedToQueryForDerivedContent;
public static I18n systemSourceNameOptionValueDoesNotReferenceExistingSource;
@@ -461,7 +463,7 @@
public static I18n invalidProjectionPath;
public static I18n invalidProjectionExpression;
public static I18n projectedPathPointsTowardsInternalNode;
-
+
public static I18n reindexMissingNoIndexesExist;
public static I18n noReindex;
public static I18n reindexAll;
View
83 modeshape-jcr/src/main/java/org/modeshape/jcr/JcrRepository.java
@@ -44,6 +44,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -51,6 +52,7 @@
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Matcher;
import javax.jcr.AccessDeniedException;
import javax.jcr.Credentials;
import javax.jcr.LoginException;
@@ -87,6 +89,7 @@
import org.modeshape.jcr.RepositoryConfiguration.BinaryStorage;
import org.modeshape.jcr.RepositoryConfiguration.Component;
import org.modeshape.jcr.RepositoryConfiguration.FieldName;
+import org.modeshape.jcr.RepositoryConfiguration.GarbageCollection;
import org.modeshape.jcr.RepositoryConfiguration.JaasSecurity;
import org.modeshape.jcr.RepositoryConfiguration.QuerySystem;
import org.modeshape.jcr.RepositoryConfiguration.Security;
@@ -98,6 +101,7 @@
import org.modeshape.jcr.api.Workspace;
import org.modeshape.jcr.api.monitor.ValueMetric;
import org.modeshape.jcr.api.query.Query;
+import org.modeshape.jcr.api.value.DateTime;
import org.modeshape.jcr.bus.ChangeBus;
import org.modeshape.jcr.bus.ClusteredRepositoryChangeBus;
import org.modeshape.jcr.bus.RepositoryChangeBus;
@@ -134,6 +138,7 @@
import org.modeshape.jcr.txn.NoClientTransactions;
import org.modeshape.jcr.txn.SynchronizedTransactions;
import org.modeshape.jcr.txn.Transactions;
+import org.modeshape.jcr.value.DateTimeFactory;
import org.modeshape.jcr.value.Name;
import org.modeshape.jcr.value.NamespaceRegistry;
import org.modeshape.jcr.value.Property;
@@ -868,19 +873,6 @@ private void repositoryNameChanged() {
descriptors.put(Repository.REPOSITORY_NAME, repositoryName());
}
- /**
- * Clean up the repository content's garbage.
- *
- * @see ModeShapeEngine.GarbageCollectionTask#run()
- */
- void cleanUp() {
- RunningState running = runningState.get();
- if (running != null) {
- running.cleanUpLocks();
- running.cleanUpBinaryValues();
- }
- }
-
Collection<Cache<?, ?>> caches() {
RunningState running = runningState.get();
if (running == null) return Collections.emptyList();
@@ -960,6 +952,7 @@ public void notify( ChangeSet changeSet ) {
private final NodeTypesImporter nodeTypesImporter;
private final Connectors connectors;
private final RepositoryConfiguration.IndexRebuildOptions indexRebuildOptions;
+ private final List<ScheduledFuture<?>> gcProcesses = new ArrayList<ScheduledFuture<?>>();
private Transaction runningTransaction;
@@ -987,7 +980,7 @@ protected RunningState( JcrRepository.RunningState other,
this.statistics = other != null ? other.statistics : new RepositoryStatistics(tempContext);
if (this.config.getMonitoring().enabled()) {
// Start the Cron service, with a minimum of a single thread ...
- this.statsRollupService = (ScheduledExecutorService)tempContext.getScheduledThreadPool("modeshape-stats");
+ this.statsRollupService = tempContext.getScheduledThreadPool("modeshape-stats");
this.statistics.start(this.statsRollupService);
} else {
this.statsRollupService = null;
@@ -1309,6 +1302,25 @@ public Void call() throws Exception {
// atomic
this.transactions.resume(runningTransaction);
this.runningTransaction = null;
+
+ // Register the background processes ...
+ GarbageCollection gcConfig = config.getGarbageCollection();
+ String threadPoolName = gcConfig.getThreadPoolName();
+ ScheduledExecutorService garbageCollectionService = this.context.getScheduledThreadPool(threadPoolName);
+ long binaryGcInitialTime = determineInitialDelay(gcConfig.getInitialTimeExpression());
+ long binaryGcInterval = gcConfig.getIntervalInHours();
+ gcProcesses.add(garbageCollectionService.scheduleAtFixedRate(new LockGarbageCollectionTask(),
+ RepositoryConfiguration.LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD,
+ RepositoryConfiguration.LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD,
+ RepositoryConfiguration.LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT));
+ if (binaryGcInitialTime >= 0) {
+ gcProcesses.add(garbageCollectionService.scheduleAtFixedRate(new BinaryValueGarbageCollectionTask(),
+ binaryGcInitialTime,
+ binaryGcInterval,
+ TimeUnit.HOURS));
+ } else {
+ logger.warn(JcrI18n.invalidGarbageCollectionInitialTime, repositoryName(), gcConfig.getInitialTimeExpression());
+ }
}
protected final Sequencers sequencers() {
@@ -1506,6 +1518,11 @@ protected void shutdown() {
// shutdown the connectors
this.connectors.shutdown();
+ // Remove the scheduled operations ...
+ for (ScheduledFuture<?> future : gcProcesses) {
+ future.cancel(true);
+ }
+
// Unregister from JNDI ...
unbindFromJndi();
@@ -1645,7 +1662,7 @@ void terminateSessions() {
}
/**
- * @see JcrRepository#cleanUp()
+ * @see LockGarbageCollectionTask
*/
void cleanUpLocks() {
if (logger.isDebugEnabled()) {
@@ -1682,7 +1699,7 @@ void cleanUpLocks() {
}
/**
- * @see JcrRepository#cleanUp()
+ * @see BinaryValueGarbageCollectionTask
*/
void cleanUpBinaryValues() {
if (logger.isDebugEnabled()) {
@@ -1889,4 +1906,38 @@ public void logout() {
}
}
+ protected class BinaryValueGarbageCollectionTask implements Runnable {
+ @Override
+ public void run() {
+ runningState().cleanUpBinaryValues();
+ }
+ }
+
+ protected class LockGarbageCollectionTask implements Runnable {
+ @Override
+ public void run() {
+ runningState().cleanUpLocks();
+ }
+ }
+
+ protected long determineInitialDelay( String initialTimeExpression ) {
+ Matcher matcher = RepositoryConfiguration.INITIAL_TIME_PATTERN.matcher(initialTimeExpression);
+ if (matcher.matches()) {
+ int hours = Integer.decode(matcher.group(1));
+ int mins = Integer.decode(matcher.group(2));
+ DateTimeFactory factory = runningState().context().getValueFactories().getDateFactory();
+ DateTime now = factory.create();
+ DateTime initialTime = factory.create(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), hours, mins, 0, 0);
+ long delay = initialTime.getMilliseconds() - System.currentTimeMillis();
+ if (delay <= 0L) {
+ initialTime = initialTime.plusDays(1);
+ delay = initialTime.getMilliseconds() - System.currentTimeMillis();
+ }
+ assert delay >= 0;
+ return delay;
+ }
+ assert false;
+ return -1;
+ }
+
}
View
16 modeshape-jcr/src/main/java/org/modeshape/jcr/ModeShapeEngine.java
@@ -114,10 +114,6 @@ public void start() {
ThreadFactory cronThreadFactory = new NamedThreadFactory("modeshape-cron");
cronStarterService = new ScheduledThreadPoolExecutor(1, cronThreadFactory);
- // Add a Cron job that cleans up each repository ...
- cronStarterService.scheduleAtFixedRate(new GarbageCollectionTask(), RepositoryConfiguration.GARBAGE_COLLECTION_SWEEP_PERIOD,
- RepositoryConfiguration.GARBAGE_COLLECTION_SWEEP_PERIOD, TimeUnit.MILLISECONDS);
-
state = State.RUNNING;
} catch (RuntimeException e) {
state = State.NOT_RUNNING;
@@ -127,16 +123,6 @@ public void start() {
}
}
- protected class GarbageCollectionTask implements Runnable {
- @Override
- public void run() {
- for (JcrRepository repository : repositories()) {
- // Okay to call, even if not running ...
- repository.cleanUp();
- }
- }
- }
-
/**
* Shutdown this engine to stop all repositories, terminate any ongoing background operations (such as sequencing), and
* reclaim any resources that were acquired by this engine. This method may be called multiple times, but only the first time
@@ -613,7 +599,7 @@ protected JcrRepository deploy( final RepositoryConfiguration repositoryConfigur
} catch (Exception e) {
throw new ConfigurationException(problems, JcrI18n.repositoryConfigurationIsNotValid.text(repositoryName,
e.getMessage()), e);
- }
+ }
return new ImmediateFuture<JcrRepository>(repository);
//
// // Create an initializer that will start the repository ...
View
133 modeshape-jcr/src/main/java/org/modeshape/jcr/RepositoryConfiguration.java
@@ -156,13 +156,14 @@
private final static String PROJECTION_PATH_EXPRESSION_STRING = "(\\w+):((([/]([^/=]|(\\\\.))+)+)|[/])\\s*=>\\s*((([/]([^/]|(\\\\.))+)+)|[/])";
public final static Pattern PROJECTION_PATH_EXPRESSION_PATTERN = Pattern.compile(PROJECTION_PATH_EXPRESSION_STRING);
- final static TimeUnit GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT = TimeUnit.MINUTES;
+ final static TimeUnit LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT = TimeUnit.MINUTES;
/**
* The process of garbage collecting locks and binary values runs periodically, and this value controls how often it runs. The
* value is currently set to 5 minutes.
*/
- final static int GARBAGE_COLLECTION_SWEEP_PERIOD = (int)TimeUnit.MILLISECONDS.convert(5, GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
+ final static int LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD = (int)TimeUnit.MILLISECONDS.convert(5,
+ LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
/**
* Each time the garbage collection process runs, session-scoped locks that are still used by active sessions will have their
@@ -174,26 +175,28 @@
* slight deviation in the period does not cause locks to be expired prematurely.
* </p>
*/
- final static int LOCK_EXTENSION_INTERVAL_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(GARBAGE_COLLECTION_SWEEP_PERIOD * 2,
- GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
+ final static int LOCK_EXTENSION_INTERVAL_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD * 2,
+ LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
/**
* The amount of time that a lock may be expired before being removed. The sweep process will extend the locks for active
* sessions, so only unused locks will have an unmodified expiry time. The value is currently twice the sweep period.
*/
- final static int LOCK_EXPIRY_AGE_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(GARBAGE_COLLECTION_SWEEP_PERIOD * 2,
- GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
+ final static int LOCK_EXPIRY_AGE_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD * 2,
+ LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
/**
* As binary values are no longer used, they are quarantined in the binary store. When the garbage collection process runs,
* any binary values that have been quarantined longer than this duration will be removed.
* <p>
- * The age is generally twice the length of the period that the garbage collection process runs, ensuring that any slight
- * deviation in the period does not cause binary values to be removed prematurely.
+ * The age is 1 hour, to ensure that binary values are not removed prematurely (e.g., when one session removes a binary value
+ * from a property while another session shortly thereafter reuses it).
* </p>
*/
- final static int UNUSED_BINARY_VALUE_AGE_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(GARBAGE_COLLECTION_SWEEP_PERIOD * 2,
- GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
+ final static int UNUSED_BINARY_VALUE_AGE_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS);
+
+ final static String INITIAL_TIME_REGEX = "(\\d\\d):(\\d\\d)";
+ final static Pattern INITIAL_TIME_PATTERN = Pattern.compile(INITIAL_TIME_REGEX);
protected static final Document EMPTY = Schematic.newDocument();
@@ -412,6 +415,10 @@
public static final String PATH_EXPRESSIONS = "pathExpressions";
public static final String JDBC_DRIVER_CLASS = "driverClass";
public static final String CONNECTION_URL = "url";
+
+ public static final String GARBAGE_COLLECTION = "garbageCollection";
+ public static final String INITIAL_TIME = "initialTime";
+ public static final String INTERVAL_IN_HOURS = "intervalInHours";
/**
* The name for the field (under "sequencing" and "query") specifying the thread pool that should be used for sequencing.
* By default, all repository instances will use the same thread pool within the engine. To use a dedicated thread pool
@@ -466,7 +473,6 @@
public static final String REBUILD_INCLUDE_SYSTEM_CONTENT = "includeSystemContent";
public static final String REBUILD_MODE = "mode";
-
/**
* The name of the clustering top-level configuration document
*/
@@ -548,6 +554,7 @@
public static final String SEQUENCING_POOL = "modeshape-sequencer";
public static final String QUERY_THREAD_POOL = "modeshape-indexer";
+ public static final String GARBAGE_COLLECTION_POOL = "modeshape-gc";
public static final String INDEXING_ANALYZER = StandardAnalyzer.class.getName();
public static final String INDEXING_SIMILARITY = DefaultSimilarity.class.getName();
@@ -572,6 +579,9 @@
public static final String CLUSTER_NAME = "ModeShape-JCR";
public static final String CHANNEL_PROVIDER = DefaultChannelProvider.class.getName();
+
+ public static final String INITIAL_TIME = "00:00";
+ public static final int INTERVAL_IN_HOURS = 24;
}
public static final class FieldValue {
@@ -1702,7 +1712,7 @@ public String getThreadPoolName() {
/**
* Returns the options that should be used for rebuilding indexes when the repository starts up.
- *
+ *
* @return a {@code non-null} {@link IndexRebuildOptions} instance.
*/
public IndexRebuildOptions getIndexRebuildOptions() {
@@ -1843,7 +1853,7 @@ protected void setDefProp( Properties props,
protected IndexRebuildOptions( Document query ) {
assert query != null;
- //first parse the deprecated fields (we need to avoid breaking client compatibility)
+ // first parse the deprecated fields (we need to avoid breaking client compatibility)
QueryRebuild deprecatedQueryRebuild = QueryRebuild.IF_MISSING;
if (query.containsField(FieldName.REBUILD_UPON_STARTUP)) {
deprecatedQueryRebuild = QueryRebuild.valueOf(query.getString(FieldName.REBUILD_UPON_STARTUP).toUpperCase());
@@ -1852,7 +1862,8 @@ protected IndexRebuildOptions( Document query ) {
Boolean deprecatedIncludeSystemContent = false;
IndexingMode deprecatedIndexingMode = IndexingMode.SYNC;
if (query.containsField(FieldName.INDEXING_MODE_SYSTEM_CONTENT)) {
- deprecatedIndexingMode = IndexingMode.valueOf(query.getString(FieldName.INDEXING_MODE_SYSTEM_CONTENT).toUpperCase());
+ deprecatedIndexingMode = IndexingMode.valueOf(query.getString(FieldName.INDEXING_MODE_SYSTEM_CONTENT)
+ .toUpperCase());
switch (deprecatedIndexingMode) {
case SYNC: {
deprecatedIncludeSystemContent = true;
@@ -1863,14 +1874,14 @@ protected IndexRebuildOptions( Document query ) {
break;
}
case DISABLED: {
- //we don't support disabled in the new indexing mode, so fallback to the default
+ // we don't support disabled in the new indexing mode, so fallback to the default
deprecatedIndexingMode = IndexingMode.SYNC;
break;
}
}
}
- //look for the new document structure
+ // look for the new document structure
Document rebuildOnStartupDocument = null;
if (query.containsField(FieldName.INDEXING)) {
Document indexingDocument = query.getDocument(FieldName.INDEXING);
@@ -1878,22 +1889,25 @@ protected IndexRebuildOptions( Document query ) {
}
if (rebuildOnStartupDocument == null) {
- //there isn't the newer version of the rebuildOnStartupDocument present, so we need to use the old values
+ // there isn't the newer version of the rebuildOnStartupDocument present, so we need to use the old values
this.when = deprecatedQueryRebuild;
this.includeSystemContent = deprecatedIncludeSystemContent;
this.mode = deprecatedIndexingMode;
} else {
- String when = rebuildOnStartupDocument.getString(FieldName.REBUILD_WHEN, deprecatedQueryRebuild.name()).toUpperCase();
+ String when = rebuildOnStartupDocument.getString(FieldName.REBUILD_WHEN, deprecatedQueryRebuild.name())
+ .toUpperCase();
this.when = QueryRebuild.valueOf(when);
- this.includeSystemContent = rebuildOnStartupDocument.getBoolean(FieldName.REBUILD_INCLUDE_SYSTEM_CONTENT, deprecatedIncludeSystemContent.booleanValue());
- String mode = rebuildOnStartupDocument.getString(FieldName.REBUILD_MODE, deprecatedIndexingMode.name()).toUpperCase();
+ this.includeSystemContent = rebuildOnStartupDocument.getBoolean(FieldName.REBUILD_INCLUDE_SYSTEM_CONTENT,
+ deprecatedIncludeSystemContent.booleanValue());
+ String mode = rebuildOnStartupDocument.getString(FieldName.REBUILD_MODE, deprecatedIndexingMode.name())
+ .toUpperCase();
this.mode = IndexingMode.valueOf(mode);
}
}
/**
* Returns the value of the flag that controls whether system content should be reindexed or not at startup.
- *
+ *
* @return {@code true} if system content should be reindexed at startup; {@code false} otherwise
*/
public boolean includeSystemContent() {
@@ -1902,7 +1916,7 @@ public boolean includeSystemContent() {
/**
* Returns the mode in which indexes should be rebuilt.
- *
+ *
* @return a {@code non-null} {@link IndexingMode} instance
*/
public IndexingMode getMode() {
@@ -1911,7 +1925,7 @@ public IndexingMode getMode() {
/**
* Returns the strategy used for rebuilding indexes.
- *
+ *
* @return a {@code non-null} {@link QueryRebuild} instance
*/
public QueryRebuild getWhen() {
@@ -1976,6 +1990,60 @@ public Federation getFederation() {
}
/**
+ * Get the configuration for the security-related aspects of this repository.
+ *
+ * @return the security configuration; never null
+ */
+ public GarbageCollection getGarbageCollection() {
+ return new GarbageCollection(doc.getDocument(FieldName.GARBAGE_COLLECTION));
+ }
+
+ @Immutable
+ public class GarbageCollection {
+ private final Document gc;
+
+ protected GarbageCollection( Document garbageCollection ) {
+ this.gc = garbageCollection != null ? garbageCollection : EMPTY;
+ }
+
+ /**
+ * Get the name of the thread pool that should be used for garbage collection work.
+ *
+ * @return the thread pool name; never null
+ */
+ public String getThreadPoolName() {
+ return gc.getString(FieldName.THREAD_POOL, Default.GARBAGE_COLLECTION_POOL);
+ }
+
+ /**
+ * Get the time that the first garbage collection process should be run.
+ *
+ * @return the initial time; never null
+ */
+ public String getInitialTimeExpression() {
+ return gc.getString(FieldName.INITIAL_TIME, Default.INITIAL_TIME);
+ }
+
+ /**
+ * Get the garbage collection interval in hours.
+ *
+ * @return the interval; never null
+ */
+ public int getIntervalInHours() {
+ return gc.getInteger(FieldName.INTERVAL_IN_HOURS, Default.INTERVAL_IN_HOURS);
+ }
+
+ /**
+ * Get the minimum time that a binary value should be unused before it can be garbage collected.
+ *
+ * @return the minimum time; never null
+ */
+ public long getUnusedBinaryValueTimeInMillis() {
+ return UNUSED_BINARY_VALUE_AGE_IN_MILLIS;
+ }
+ }
+
+ /**
* The security-related configuration information.
*/
@Immutable
@@ -2077,7 +2145,8 @@ protected Federation( Document federation ) {
}
for (Object projectionExpression : externalSource.getArray(FieldName.PROJECTIONS)) {
- ProjectionConfiguration projectionConfiguration = new ProjectionConfiguration(sourceName, projectionExpression.toString());
+ ProjectionConfiguration projectionConfiguration = new ProjectionConfiguration(sourceName,
+ projectionExpression.toString());
String workspaceName = projectionConfiguration.getWorkspaceName();
List<ProjectionConfiguration> projectionsInWorkspace = projectionsByWorkspace.get(workspaceName);
@@ -2106,11 +2175,12 @@ protected Federation( Document federation ) {
/**
* Creates a new projection using a string expression
- *
+ *
* @param sourceName the source name
* @param pathExpression a {@code non-null} String
*/
- public ProjectionConfiguration( String sourceName, String pathExpression ) {
+ public ProjectionConfiguration( String sourceName,
+ String pathExpression ) {
Matcher expressionMatcher = PROJECTION_PATH_EXPRESSION_PATTERN.matcher(pathExpression);
// should be validated by the repository schema
if (expressionMatcher.matches()) {
@@ -2130,7 +2200,7 @@ public ProjectionConfiguration( String sourceName, String pathExpression ) {
/**
* Returns the projection's external path.
- *
+ *
* @return a {@code non-null} String
*/
public String getExternalPath() {
@@ -2139,7 +2209,7 @@ public String getExternalPath() {
/**
* Returns the projected path
- *
+ *
* @return a {@code non-null} String
*/
public String getProjectedPath() {
@@ -2148,7 +2218,7 @@ public String getProjectedPath() {
/**
* Returns the projection's workspace name
- *
+ *
* @return a {@code non-null} String
*/
public String getWorkspaceName() {
@@ -2157,7 +2227,7 @@ public String getWorkspaceName() {
/**
* Returns the alias of a projection.
- *
+ *
* @return a {@code non-null} String
*/
public String getAlias() {
@@ -2166,7 +2236,7 @@ public String getAlias() {
/**
* Returns the repository path
- *
+ *
* @return a {@code non-null} String
*/
public String getRepositoryPath() {
@@ -2175,6 +2245,7 @@ public String getRepositoryPath() {
/**
* Returns the name of the source for which the projection is configured
+ *
* @return a {@code non-null} String
*/
public String getSourceName() {
View
2  modeshape-jcr/src/main/java/org/modeshape/jcr/SystemContent.java
@@ -789,7 +789,7 @@ public void cleanUpLocks( Set<String> activeSessionIds ) {
// Create a new expiration date ...
DateTimeFactory dates = context().getValueFactories().getDateFactory();
DateTime now = dates.create();
- DateTime newExpiration = dates.create(now, RepositoryConfiguration.GARBAGE_COLLECTION_SWEEP_PERIOD);
+ DateTime newExpiration = dates.create(now, RepositoryConfiguration.LOCK_GARBAGE_COLLECTION_SWEEP_PERIOD);
DateTime expiry = dates.create(now, -RepositoryConfiguration.LOCK_EXPIRY_AGE_IN_MILLIS);
// Iterate over the locks ...
View
2  modeshape-jcr/src/main/resources/org/modeshape/jcr/JcrI18n.properties
@@ -195,6 +195,8 @@ errorImportingNodeTypeContent = Error importing node types from "{0}": {1}
errorDuringInitialImport = Error while importing initial content: {0}
nodeTypesNotFoundInXml = No valid node types elements found in the XML in "{0}"
+invalidGarbageCollectionInitialTime = The initial time specified for the garbage collection in repository '{0}' was not valid: {1}
+
failedToQueryForDerivedContent = Error while querying for content in workspace "{0}" derived from (re)moved paths, using this query: {1}
systemSourceNameOptionValueDoesNotReferenceExistingSource = The JCR Repository 'SYSTEM_SOURCE_NAME' option value "{0}" references an invalid or non-existant source "{1}"
View
25 modeshape-jcr/src/main/resources/org/modeshape/jcr/repository-config-schema.json
@@ -25,7 +25,7 @@
},
"monitoring" : {
"type" : "object",
- "description" : "The specification for the monitoring system for the repositories.",
+ "description" : "The specification for the monitoring system for the repository.",
"additionalProperties" : false,
"properties" : {
"enabled" : {
@@ -39,6 +39,29 @@
},
}
},
+ "garbageCollection" : {
+ "type" : "object",
+ "description" : "The specification for reclaiming unused persistent storage for the repository.",
+ "additionalProperties" : false,
+ "properties" : {
+ "threadPool" : {
+ "type" : "string",
+ "default" : "modeshape-gc",
+ "description" : "Name of the thread pool that should be used to asynchronously reclaim unused persistent storage. Thread pools are named globally within a single ModeShape engine, and by default all repositories use the same thread pool for garbage collection."
+ },
+ "initialTime" : {
+ "type" : "string",
+ "default" : "00:00",
+ "pattern" : "(([0-1][0-9])|([2][0-3])):[0-5][0-9]",
+ "description" : "The local time that the first garbage collection process should be run. Garbage collection may be relatively expensive, as it involves scanning all persisted content, so it's recommended that this be done durign off hours if possible. By default, garbage collection is run at midnight (local time) after the repository is started."
+ },
+ "intervalInHours" : {
+ "type" : "integer",
+ "default" : "24",
+ "description" : "The number of hours between garbage collection runs. By default the interval is 24 hours (meaning it runs once per day)."
+ },
+ }
+ },
"storage" : {
"type" : "object",
"description" : "The specification of how to obtain the Infinispan cache used for storage.",
View
5 modeshape-jcr/src/test/java/org/modeshape/jcr/RepositoryConfigurationTest.java
@@ -188,6 +188,11 @@ public void shouldSuccessfullyValidateFileSystemFederationConfiguration() {
}
@Test
+ public void shouldSuccessfullyValidateConfigurationWithGarbageCollection() {
+ assertValid("config/repo-config-garbage-collection.json");
+ }
+
+ @Test
public void shouldAlwaysReturnNonNullSecurityComponent() {
RepositoryConfiguration config = new RepositoryConfiguration("repoName");
assertThat(config.getSecurity(), is(notNullValue()));
View
13 modeshape-jcr/src/test/resources/config/repo-config-garbage-collection.json
@@ -0,0 +1,13 @@
+{
+ "name" : "Garbage collecting repository",
+ "workspaces" : {
+ "predefined" : ["ws1", "ws2"],
+ "default" : "default",
+ "allowCreation" : true
+ },
+ "garbageCollection" : {
+ "threadPool" : "modeshape-gc-pool",
+ "initialTime" : "04:01",
+ "intervalInHours" : 5,
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.