fix == true, collect the repaired
+ * {@linkplain NodePropBundle bundles} here
+ */
+ protected void checkBundleConsistency(NodeId id, NodePropBundle bundle, boolean fix, Collection modifications) {
+ //log.info(name + ": checking bundle '" + id + "'");
+
+ // look at the node's children
+ Collection missingChildren = new ArrayList();
+ Iterator iter = bundle.getChildNodeEntries().iterator();
+ while (iter.hasNext()) {
+ NodePropBundle.ChildNodeEntry entry = (NodePropBundle.ChildNodeEntry) iter.next();
+
+ // skip check for system nodes (root, system root, version storage, nodetypes)
+ if (entry.getId().toString().endsWith("babecafebabe")) {
+ continue;
+ }
+ if (id.toString().endsWith("babecafebabe")) {
+ continue;
+ }
+
+ try {
+ // analyze child node bundles
+ NodePropBundle child = loadBundle(entry.getId(), true);
+ if (child == null) {
+ log.error("NodeState '" + id + "' references inexistent child '" + entry.getName() + "' with id '" + entry.getId() + "'");
+ missingChildren.add(entry);
+ } else {
+ NodeId cp = child.getParentId();
+ if (cp == null) {
+ log.error("ChildNode has invalid parent uuid: schemaObjectPrefix does only consist of
* characters that are allowed in names on the target database. Illegal
@@ -881,8 +985,25 @@
*/
protected synchronized NodePropBundle loadBundle(NodeId id)
throws ItemStateException {
+ return loadBundle(id, false);
+ }
+
+ /**
+ * Loads a bundle from the underlying system and optionally performs
+ * a check on the bundle first.
+ *
+ * @param id the node id of the bundle
+ * @param checkBeforeLoading check the bundle before loading it and log
+ * detailed informations about it (slower)
+ * @return the loaded bundle or null if the bundle does not
+ * exist.
+ * @throws ItemStateException if an error while loading occurs.
+ */
+ protected synchronized NodePropBundle loadBundle(NodeId id, boolean checkBeforeLoading)
+ throws ItemStateException {
ResultSet rs = null;
InputStream in = null;
+ byte[] bytes = null;
try {
Statement stmt = connectionManager.executeStmt(bundleSelectSQL, getKey(id.getUUID()));
rs = stmt.getResultSet();
@@ -892,13 +1013,24 @@
Blob b = rs.getBlob(1);
// JCR-1039: pre-fetch/buffer blob data
long length = b.length();
- byte[] bytes = new byte[(int) length];
+ bytes = new byte[(int) length];
in = b.getBinaryStream();
int read, pos = 0;
while ((read = in.read(bytes, pos, bytes.length - pos)) > 0) {
pos += read;
}
DataInputStream din = new DataInputStream(new ByteArrayInputStream(bytes));
+
+ if (checkBeforeLoading) {
+ if (binding.checkBundle(din)) {
+ // reset stream for readBundle()
+ din = new DataInputStream(new ByteArrayInputStream(bytes));
+ } else {
+ // gets wrapped as proper ItemStateException below
+ throw new Exception("invalid bundle, see previous BundleBinding error log entry");
+ }
+ }
+
NodePropBundle bundle = binding.readBundle(din, id);
bundle.setSize(length);
return bundle;
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java (revision 631607)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java (working copy)
@@ -701,4 +701,11 @@
bundles.put(bundle);
}
}
+
+ /**
+ * This implementation does nothing.
+ *
+ * {@inheritDoc}
+ */
+ public void checkConsistency(String[] uuids, boolean recursive, boolean fix) {}
}
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (revision 631607)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (working copy)
@@ -1145,6 +1145,78 @@
return new SharedItemStateManager(persistMgr, rootNodeId, ntReg, true, cacheFactory);
}
+ /**
+ * Checks the consistency of the given nodes in the given workspace or the
+ * versioning subtree. Use jcr:system as workspace name for
+ * checking the versioning tree. The recursive flag indicates
+ * whether the tree below the given nodes shall be traversed and checked as
+ * well.
+ *
+ * + * Note: This must be called right after Jackrabbit startup before the + * workspace in question (or the system tree) was accessed, ie. a session + * was opened. Otherwise Jackrabbit will be in an inconsistent state and + * various errors will happen. Be also sure to have logging enabled before + * this method is called to catch all messages of the check. + * + *
+ * To use this method, you will have to subclass {@link RepositoryImpl}, + * make it public and use an instance of the subclass as the repository. + * + *
+ * The check is currently restricted to {@link PersistenceManager}s, more
+ * specifically the {@link BundleDbPersistenceManager}s. They will check
+ * for parent-child relationships of nodes, where either parent or child
+ * nodes no longer exist. In the case of missing child nodes, these will be
+ * repaired by removing them. Any other problems will be logged with ERROR
+ * log level for further analysis. When bundles are fixed, ie.
+ * fix==true, set INFO logging on the specific persistence
+ * manager, to see which bundles get repaired:
+ *
+ *
+ * log4j.logger.org.apache.jackrabbit.core.persistence.bundle.BundleDbPersistenceManager=INFO, stdout + *+ * + * When more details for all checked node bundles shall be displayed, enable + * INFO logging for the BundleBinding: + * + *
+ * log4j.logger.org.apache.jackrabbit.core.persistence.bundle.util.BundleBinding=INFO, stdout + *+ * + * @param workspaceName + * name of the workspace to be checked. Use + *
jcr:system to check the versioning subtree.
+ * @param uuids
+ * list of UUIDs of nodes to be checked. if null, all nodes in
+ * the workspace will be checked (and the recursive flag will be
+ * ignored)
+ * @param recursive
+ * if true, the tree(s) below the given node(s) will be traversed
+ * and checked as well
+ * @param fix
+ * if true, any problems found that can be repaired will be
+ * repaired. if false, no data will be modified, instead all
+ * inconsistencies will only get logged
+ *
+ * @throws NoSuchWorkspaceException
+ * if the workspace does not exist
+ * @throws IllegalStateException
+ * if this repository has been shut down
+ * @throws RepositoryException
+ * if the persistence manager could not be initialized
+ */
+ protected void checkConsistency(String workspaceName, String[] uuids, boolean recursive, boolean fix) throws IllegalStateException, NoSuchWorkspaceException, RepositoryException {
+ if ("jcr:system".equals(workspaceName)) {
+ PersistenceManager pm = vMgr.getPersistenceManager();
+ pm.checkConsistency(uuids, recursive, fix);
+ } else {
+ PersistenceManager pm = getWorkspaceInfo(workspaceName).getPersistenceManager();
+ pm.checkConsistency(uuids, recursive, fix);
+ }
+ }
+
//-----------------------------------------------------------< Repository >
/**
* {@inheritDoc}
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (revision 631607)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (working copy)
@@ -196,6 +196,14 @@
public DynamicESCFactory getEscFactory() {
return escFactory;
}
+
+ /**
+ * Returns the persistence manager used by the version manager.
+ * @return the persistence manager used by the version manager
+ */
+ public PersistenceManager getPersistenceManager() {
+ return pMgr;
+ }
/**
* {@inheritDoc}