if (clusteringService != null) {
int minutesToWait = 10;
LOGGER.debug("Waiting at most for {0} minutes while verifying the status of the '{1}' repository", minutesToWait,
name);
if (!clusteringService.tryLock(minutesToWait, TimeUnit.MINUTES)) {
throw new SystemFailureException(JcrI18n.repositoryWasNeverInitializedAfterMinutes.text(name, minutesToWait));
}
LOGGER.debug("Repository '{0}' acquired clustered-wide lock for performing initialization or verifying status", name);
// at this point we should have a global cluster-wide lock
isHoldingClusterLock = true;
}
SchematicEntry repositoryInfo = this.documentStore.localStore().get(REPOSITORY_INFO_KEY);
boolean upgradeRequired = false;
if (repositoryInfo == null) {
// Create a UUID that we'll use as the string specifying who is doing the initialization ...
String initializerId = UUID.randomUUID().toString();
// Must be a new repository (or one created before 3.0.0.Final) ...
this.repoKey = NodeKey.keyForSourceName(this.name);
this.sourceKey = NodeKey.keyForSourceName(configuration.getStoreName());
DateTime now = context.getValueFactories().getDateFactory().create();
// Store this info in the repository info document ...
EditableDocument doc = Schematic.newDocument();
doc.setString(REPOSITORY_NAME_FIELD_NAME, this.name);
doc.setString(REPOSITORY_KEY_FIELD_NAME, this.repoKey);
doc.setString(REPOSITORY_SOURCE_NAME_FIELD_NAME, configuration.getStoreName());
doc.setString(REPOSITORY_SOURCE_KEY_FIELD_NAME, this.sourceKey);
doc.setDate(REPOSITORY_CREATED_AT_FIELD_NAME, now.toDate());
doc.setString(REPOSITORY_INITIALIZER_FIELD_NAME, initializerId);
doc.setString(REPOSITORY_CREATED_WITH_MODESHAPE_VERSION_FIELD_NAME, ModeShape.getVersion());
doc.setNumber(REPOSITORY_UPGRADE_ID_FIELD_NAME, upgrades.getLatestAvailableUpgradeId());
// store the repository info
if (this.documentStore.localStore().putIfAbsent(REPOSITORY_INFO_KEY, doc) != null) {
// if clustered, we should be holding a cluster-wide lock, so if some other process managed to write under this
// key,
// smth is seriously wrong. If not clustered, only 1 thread will always perform repository initialization.
// in either case, this should not happen
throw new SystemFailureException(JcrI18n.repositoryWasInitializedByOtherProcess.text(name));
}
repositoryInfo = this.documentStore.get(REPOSITORY_INFO_KEY);
// We're doing the initialization ...
initializingRepository = true;
LOGGER.debug("Initializing the '{0}' repository", name);
} else {
// Get the repository key and source key from the repository info document ...
Document info = repositoryInfo.getContent();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Repository '{0}' already initialized at '{1}'", name,
info.get(REPOSITORY_INITIALIZED_AT_FIELD_NAME));
}
String repoName = info.getString(REPOSITORY_NAME_FIELD_NAME, this.name);
String sourceName = info.getString(REPOSITORY_SOURCE_NAME_FIELD_NAME, configuration.getStoreName());
this.repoKey = info.getString(REPOSITORY_KEY_FIELD_NAME, NodeKey.keyForSourceName(repoName));
this.sourceKey = info.getString(REPOSITORY_SOURCE_KEY_FIELD_NAME, NodeKey.keyForSourceName(sourceName));
// See if this existing repository needs to be upgraded ...
lastUpgradeId = info.getInteger(REPOSITORY_UPGRADE_ID_FIELD_NAME, 0);
upgradeRequired = upgrades.isUpgradeRequired(lastUpgradeId);
if (upgradeRequired && info.getString(REPOSITORY_UPGRADER_FIELD_NAME) == null) {
int nextId = upgrades.getLatestAvailableUpgradeId();
LOGGER.debug("The content in repository '{0}' needs to be upgraded (steps {1}->{2})", name, lastUpgradeId, nextId);
// The repository does need to be upgraded and nobody is yet doing it. Note that we only want one process in the
// cluster to do this, so we need to update the document store in an atomic fashion. So, first attempt to
// lock the document ...
this.upgradingRepository = runInTransaction(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
LocalDocumentStore store = documentStore().localStore();
store.prepareDocumentsForUpdate(Collections.unmodifiableSet(REPOSITORY_INFO_KEY));
EditableDocument editor = store.edit(REPOSITORY_INFO_KEY, true);
if (editor.get(REPOSITORY_UPGRADER_FIELD_NAME) == null) {
// Make sure that some other process didn't sneak in and already upgrade ...
int lastUpgradeId = editor.getInteger(REPOSITORY_UPGRADE_ID_FIELD_NAME, 0);
if (upgrades.isUpgradeRequired(lastUpgradeId)) {
// An upgrade is still required, and we get to do it ...
final String upgraderId = UUID.randomUUID().toString();
editor.setString(REPOSITORY_UPGRADER_FIELD_NAME, upgraderId);
return true;
}
}
// Another process is upgrading (or has already upgraded) the repository ...
return false;
}
});
if (this.upgradingRepository) {
LOGGER.debug("This process will upgrade the content in repository '{0}'", name);
} else {
LOGGER.debug("The content in repository '{0}' does not need to be upgraded", name);
}
}
}
// If we're not doing the upgrade, then block for at most 10 minutes while another process does ...
if (upgradeRequired && !upgradingRepository) {
LOGGER.debug("Waiting at most for 10 minutes for another process in the cluster to upgrade the content in existing repository '{0}'",
name);
waitUntil(new Callable<Boolean>() {
@Override
public Boolean call() {
Document info = documentStore().localStore().get(REPOSITORY_INFO_KEY).getContent();
int lastUpgradeId = info.getInteger(REPOSITORY_UPGRADE_ID_FIELD_NAME, 0);
return !upgrades.isUpgradeRequired(lastUpgradeId);
}
}, 10, TimeUnit.MINUTES, JcrI18n.repositoryWasNeverUpgradedAfterMinutes);
LOGGER.debug("Content in existing repository '{0}' has been fully upgraded", name);
} else if (!initializingRepository) {
LOGGER.debug("Content in existing repository '{0}' does not need to be upgraded", name);
}
this.systemWorkspaceName = RepositoryConfiguration.SYSTEM_WORKSPACE_NAME;
String systemWorkspaceKey = NodeKey.keyForWorkspaceName(systemWorkspaceName);
this.systemMetadataKey = new NodeKey(this.sourceKey, systemWorkspaceKey, SYSTEM_METADATA_IDENTIFIER);
// Initialize the workspaces ..
refreshRepositoryMetadata(false);
this.changeBus = changeBus;
this.changeBus.registerInThread(new ChangesToWorkspacesListener());
// Make sure the system workspace is configured to have a 'jcr:system' node ...
SessionCache systemSession = createSession(context, systemWorkspaceName, false);
NodeKey systemRootKey = systemSession.getRootKey();
CachedNode systemRoot = systemSession.getNode(systemRootKey);
logger.debug("System root: {0}", systemRoot);
ChildReference systemRef = systemRoot.getChildReferences(systemSession).getChild(JcrLexicon.SYSTEM);
logger.debug("jcr:system child reference: {0}", systemRef);
CachedNode systemNode = systemRef != null ? systemSession.getNode(systemRef) : null;
logger.debug("System node: {0}", systemNode);
if (systemRef == null || systemNode == null) {
logger.debug("Creating the '{0}' workspace in repository '{1}'", systemWorkspaceName, name);
// We have to create the initial "/jcr:system" content ...
MutableCachedNode root = systemSession.mutable(systemRootKey);
if (initializer == null) {
initializer = NO_OP_INITIALIZER;
}
initializer.initialize(systemSession, root);
systemSession.save();
// Now we need to forcibly refresh the system workspace cache ...
refreshWorkspace(systemWorkspaceName);
systemSession = createSession(context, systemWorkspaceName, false);
systemRoot = systemSession.getNode(systemRootKey);
systemRef = systemRoot.getChildReferences(systemSession).getChild(JcrLexicon.SYSTEM);
if (systemRef == null) {
throw new SystemFailureException(JcrI18n.unableToInitializeSystemWorkspace.text(name));
}
} else {
logger.debug("Found existing '{0}' workspace in repository '{1}'", systemWorkspaceName, name);
}
this.systemKey = systemRef.getKey();