/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.impl;
import org.exoplatform.services.jcr.BaseStandaloneTest;
import org.exoplatform.services.jcr.access.AccessControlList;
import org.exoplatform.services.jcr.config.LockManagerEntry;
import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
import org.exoplatform.services.jcr.config.WorkspaceEntry;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.NodeData;
import org.exoplatform.services.jcr.datamodel.QPath;
import org.exoplatform.services.jcr.impl.checker.InconsistencyRepair;
import org.exoplatform.services.jcr.impl.checker.NodeRemover;
import org.exoplatform.services.jcr.impl.checker.RepositoryCheckController;
import org.exoplatform.services.jcr.impl.core.ItemImpl;
import org.exoplatform.services.jcr.impl.core.NodeImpl;
import org.exoplatform.services.jcr.impl.core.PropertyImpl;
import org.exoplatform.services.jcr.impl.core.SessionImpl;
import org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl;
import org.exoplatform.services.jcr.impl.core.nodetype.NodeTypeDataManagerImpl;
import org.exoplatform.services.jcr.impl.core.query.SearchManager;
import org.exoplatform.services.jcr.impl.core.query.SystemSearchManager;
import org.exoplatform.services.jcr.impl.dataflow.TransientNodeData;
import org.exoplatform.services.jcr.impl.dataflow.TransientPropertyData;
import org.exoplatform.services.jcr.impl.dataflow.TransientValueData;
import org.exoplatform.services.jcr.impl.storage.jdbc.DBConstants;
import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCStorageConnection;
import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer;
import org.exoplatform.services.jcr.impl.storage.jdbc.db.WorkspaceStorageConnectionFactory;
import org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.db.SybaseJDBCConnectionHelper;
import org.exoplatform.services.jcr.impl.storage.value.fs.FileValueStorage;
import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
import org.exoplatform.services.jcr.util.IdGenerator;
import org.exoplatform.services.jcr.util.TesterConfigurationHelper;
import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jcr.LoginException;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/**
* @author <a href="mailto:skarpenko@exoplatform.com">Sergiy Karpenko</a>
* @version $Id: exo-jboss-codetemplates.xml 34360 10.10.2011 skarpenko $
*
*/
public class TestRepositoryCheckController extends BaseStandaloneTest
{
private static boolean SHARED_CACHE = true;
private static boolean NOT_SHARED_CACHE = false;
private static boolean MULTI_DB = true;
private static boolean SINGLE_DB = false;
private static boolean CACHE_ENABLED = true;
private static boolean CACHE_DISABLED = false;
private final TesterConfigurationHelper helper = TesterConfigurationHelper.getInstance();
protected String getRepositoryName()
{
return null;
}
public void tearDown() throws Exception
{
// remove generated reports
for (File file : new File(".").listFiles())
{
if (file.getName().startsWith("report"))
{
file.delete();
}
}
super.tearDown();
}
public void testCheckDataBase() throws Exception
{
TesterRepositoryCheckController checkController =
new TesterRepositoryCheckController(repositoryService.getRepository("db1"));
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
public void testLockUsecases() throws Exception
{
checkConsistentLocksInDataBase(helper.createRepository(container, SINGLE_DB, CACHE_ENABLED, NOT_SHARED_CACHE));
checkConsistentLocksInDataBase(helper.createRepository(container, MULTI_DB, CACHE_ENABLED, NOT_SHARED_CACHE));
checkConsistentLocksInDataBase(helper.createRepository(container, SINGLE_DB, CACHE_ENABLED, SHARED_CACHE));
checkConsistentLocksInDataBase(helper.createRepository(container, MULTI_DB, CACHE_ENABLED, SHARED_CACHE));
checkInconsistentLocksInLockTable(helper.createRepository(container, SINGLE_DB, CACHE_ENABLED, NOT_SHARED_CACHE));
checkInconsistentLocksInLockTable(helper.createRepository(container, MULTI_DB, CACHE_ENABLED, NOT_SHARED_CACHE));
checkInconsistentLocksInLockTable(helper.createRepository(container, SINGLE_DB, CACHE_ENABLED, SHARED_CACHE));
checkInconsistentLocksInLockTable(helper.createRepository(container, MULTI_DB, CACHE_ENABLED, SHARED_CACHE));
}
private void checkConsistentLocksInDataBase(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
lockNode(node);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removePropertyInDB(repository, (PropertyImpl)node.getProperty("jcr:lockIsDeep"));
removePropertyInDB(repository, (PropertyImpl)node.getProperty("jcr:lockOwner"));
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
private void checkInconsistentLocksInLockTable(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
lockNode(node);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
clearLockTable(repository);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
public void testCheckValueStorage() throws Exception
{
TesterRepositoryCheckController checkController =
new TesterRepositoryCheckController(repositoryService.getRepository("db1"));
assertTrue(checkController.checkValueStorage().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
public void testCheckIndex() throws Exception
{
TesterRepositoryCheckController checkController =
new TesterRepositoryCheckController(repositoryService.getRepository("db1"));
assertTrue(checkController.checkIndex().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
public void testCheckAll() throws Exception
{
TesterRepositoryCheckController checkController =
new TesterRepositoryCheckController(repositoryService.getRepository("db1"));
assertTrue(checkController.checkAll().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
/**
* Index contains documents that was already removed from DB.
*/
public void testIndexUsecaseWrongDocumentId() throws Exception
{
ManageableRepository repository = null;
try
{
repository = helper.createRepository(container, SINGLE_DB, CACHE_DISABLED);
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
assertTrue(checkController.checkIndex().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removeNodeInDB(repository, node);
assertTrue(checkController.checkIndex().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Index contains multiple documents.
*/
public void testIndexUsecaseMultipleDocuments() throws Exception
{
ManageableRepository repository = null;
try
{
repository = helper.createRepository(container, SINGLE_DB, CACHE_DISABLED);
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
assertTrue(checkController.checkIndex().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
indexNode(repository, node, ItemState.ADDED);
assertTrue(checkController.checkIndex().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Index doesn't contain document which stored in DB.
*/
public void testIndexUsecaseDocumentNotExists() throws Exception
{
ManageableRepository repository = null;
try
{
repository = helper.createRepository(container, SINGLE_DB, CACHE_DISABLED);
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
assertTrue(checkController.checkIndex().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
indexNode(repository, node, ItemState.DELETED);
assertTrue(checkController.checkIndex().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase: property doens't have have parent node.
*/
public void testDBUsecasesTheParentIdIsIdOfThisNode() throws Exception
{
checkDBUsecasesTheParentIdIsIdOfThisNode(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesTheParentIdIsIdOfThisNode2(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesTheParentIdIsIdOfThisNode(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
checkDBUsecasesTheParentIdIsIdOfThisNode2(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkDBUsecasesTheParentIdIsIdOfThisNode(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
assingItsOwnParent(repository, (ItemImpl)node);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
private void checkDBUsecasesTheParentIdIsIdOfThisNode2(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
Property prop = addTestProperty(repository, node);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
assingItsOwnParent(repository, (ItemImpl)prop);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase: property doens't have have parent node.
*/
public void testDBUsecasesSeveralVersionsOfSameItem() throws Exception
{
checkSeveralVersionsOfSameItem(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkSeveralVersionsOfSameItem(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkSeveralVersionsOfSameItem(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
NodeImpl node1 = (NodeImpl)addTestNode(repository);
NodeImpl node2 = (NodeImpl)addTestNode(repository);
PropertyImpl prop = (PropertyImpl)addTestProperty(repository, node1);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
updateNodeRecord(repository, node2.getInternalIdentifier(), 1, 1);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
insertPropertyRecord(repository, prop.getInternalIdentifier(), prop.getParentIdentifier(), prop.getName());
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase: property doens't have have parent node.
*/
public void testDBUsecasesPropertyWithoutParent() throws Exception
{
checkDBUsecasesPropertyWithoutParent(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesPropertyWithoutParent(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkDBUsecasesPropertyWithoutParent(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
insertPropertyRecord(repository, IdGenerator.generate(), IdGenerator.generate(), "testName");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase: Incorrect JCR_VALUE records.
*/
public void testDBUsecasesIncorrectValueRecords() throws Exception
{
checkDBUsecasesIncorrectValueRecords(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesIncorrectValueRecords(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkDBUsecasesIncorrectValueRecords(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
PropertyImpl prop = (PropertyImpl)addTestProperty(repository, node);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
updateValueRecord(repository, prop.getInternalIdentifier());
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase: value records has no item record.
*/
public void testDBUsecasesValueRecordHasNoItemRecord() throws Exception
{
checkDBUsecasesValueRecordHasNoItemRecord(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesValueRecordHasNoItemRecord(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkDBUsecasesValueRecordHasNoItemRecord(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
PropertyImpl prop = (PropertyImpl)addTestProperty(repository, node);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removeItemRecord(repository, prop.getInternalIdentifier());
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase: properties that have not value record.
*/
public void testDBUsecasesPrimaryTypePropertyHasNoValueRecord() throws Exception
{
checkDBUsecasesPrimaryTypePropertyHasNoValueRecor(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesPrimaryTypePropertyHasNoValueRecor(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkDBUsecasesPrimaryTypePropertyHasNoValueRecor(ManageableRepository repository) throws Exception
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
PropertyImpl prop = (PropertyImpl)node.getProperty("jcr:primaryType");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removeValueRecord(repository, prop.getInternalIdentifier());
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
helper.removeRepository(container, repository.getConfiguration().getName());
}
/**
* Usecase: properties that have not value record.
*/
public void testDBUsecasesPropertiesHasNoValueRecord() throws Exception
{
checkDBUsecasesPropertiesHasNoSingleValueRecord(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesPropertiesHasEmptyMultiValueRecord(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesPropertiesHasNoSingleValueRecord(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
checkDBUsecasesPropertiesHasEmptyMultiValueRecord(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkDBUsecasesPropertiesHasNoSingleValueRecord(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
PropertyImpl prop = (PropertyImpl)addTestProperty(repository, node);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removeValueRecord(repository, prop.getInternalIdentifier());
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
private void checkDBUsecasesPropertiesHasEmptyMultiValueRecord(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
node.setProperty("prop", new String[]{});
node.save();
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase: reference properties without reference records.
*/
public void testDBUsecasesReferencePropertyWithoutReferenceRecord() throws Exception
{
checkDBUsecasesReferencePropertyWithoutReferenceRecord(helper.createRepository(container, SINGLE_DB,
CACHE_DISABLED));
checkDBUsecasesReferencePropertyWithoutReferenceRecord(helper.createRepository(container, MULTI_DB,
CACHE_DISABLED));
}
private void checkDBUsecasesReferencePropertyWithoutReferenceRecord(ManageableRepository repository)
throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
Node node2 = addTestNode(repository);
PropertyImpl prop = (PropertyImpl)node2.setProperty("prop", node);
node2.save();
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removeReferenceRecord(repository, prop.getInternalIdentifier());
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase when node doesn't have at primary type property.
*/
public void testDBUsecasesNodeHasNoProperties() throws Exception
{
checkDBUsecasesNodeHasNotPrimaryTypeProperties(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkDBUsecasesNodeHasNotPrimaryTypeProperties(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkDBUsecasesNodeHasNotPrimaryTypeProperties(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removePropertyInDB(repository, (PropertyImpl)node.getProperty("jcr:primaryType"));
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairDataBase("yes");
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
/**
* Usecase when tree of nodes do't have primary type property.
*/
public void testDBUsecasesTreeOfNodeHasNoProperties() throws Exception
{
checkDBUsecasesTreeOfNodeHasNotPrimaryTypeProperties1(helper.createRepository(container, SINGLE_DB,
CACHE_DISABLED));
checkDBUsecasesTreeOfNodeHasNotPrimaryTypeProperties1(helper
.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkDBUsecasesTreeOfNodeHasNotPrimaryTypeProperties1(ManageableRepository repository) throws Exception
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
NodeImpl node1 = (NodeImpl)addTestNode(repository);
NodeImpl node2 = (NodeImpl)addTestNode(repository, node1.getUUID());
NodeImpl node3 = (NodeImpl)addTestNode(repository, node2.getUUID());
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removePropertyInDB(repository, (PropertyImpl)node3.getProperty("jcr:primaryType"));
removePropertyInDB(repository, (PropertyImpl)node2.getProperty("jcr:primaryType"));
removePropertyInDB(repository, (PropertyImpl)node1.getProperty("jcr:primaryType"));
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
InconsistencyRepair repair = getNodeRemover(repository);
// check correctness repairing child with parent without primaryType property
Connection conn = getConnection(repository);
ResultSet resultSet = getResultSetWithNode(repository, conn, node3);
resultSet.next();
repair.doRepair(resultSet);
resultSet.close();
conn.close();
// check correctness repairing parent with child without primaryType property
repair = getNodeRemover(repository);
conn = getConnection(repository);
resultSet = getResultSetWithNode(repository, conn, node1);
resultSet.next();
repair.doRepair(resultSet);
resultSet.close();
conn.close();
// check correctness repairing node already removed in previous check
repair = getNodeRemover(repository);
Map<String, String> strFields = new HashMap<String, String>();
strFields.put(DBConstants.COLUMN_PARENTID, "already-removed-parentId");
strFields.put(DBConstants.COLUMN_NAME, "[]" + node2.getName());
strFields.put(DBConstants.COLUMN_ID, node2.getIdentifier());
Map<String, Integer> intFields = new HashMap<String, Integer>();
intFields.put(DBConstants.COLUMN_NORDERNUM, 1);
intFields.put(DBConstants.COLUMN_VERSION, 0);
intFields.put(DBConstants.COLUMN_INDEX, 0);
resultSet = new FakeResultSet(strFields, intFields);
repair.doRepair(resultSet);
assertTrue(checkController.checkDataBase().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
helper.removeRepository(container, repository.getConfiguration().getName());
}
/**
* Usescase when STORAGE_DESC field in VALUE table is not empty but there is no file in the value storage.
*/
public void testValueStorageUsecases() throws Exception
{
checkValueStorageUsecases(helper.createRepository(container, SINGLE_DB, CACHE_DISABLED));
checkValueStorageUsecases(helper.createRepository(container, MULTI_DB, CACHE_DISABLED));
}
private void checkValueStorageUsecases(ManageableRepository repository) throws Exception
{
try
{
TesterRepositoryCheckController checkController = new TesterRepositoryCheckController(repository);
Node node = addTestNode(repository);
PropertyImpl prop = (PropertyImpl)node.setProperty("prop", new FileInputStream(createBLOBTempFile(300)));
node.save();
assertTrue(checkController.checkValueStorage().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
removeFileFromVS(repository, prop.getInternalIdentifier());
assertTrue(checkController.checkValueStorage().startsWith(
RepositoryCheckController.REPORT_NOT_CONSISTENT_MESSAGE));
checkController.repairValueStorage("yes");
assertTrue(checkController.checkValueStorage().startsWith(RepositoryCheckController.REPORT_CONSISTENT_MESSAGE));
}
finally
{
if (repository != null)
{
helper.removeRepository(container, repository.getConfiguration().getName());
}
}
}
private Node addTestNode(ManageableRepository repository) throws LoginException, NoSuchWorkspaceException,
RepositoryException
{
return addTestNode(repository, Constants.ROOT_UUID);
}
private Node addTestNode(ManageableRepository repository, String parentId) throws LoginException,
NoSuchWorkspaceException, RepositoryException
{
SessionImpl session =
(SessionImpl)repository.login(credentials, repository.getConfiguration().getSystemWorkspaceName());
NodeImpl parent = (NodeImpl)session.getNodeByIdentifier(parentId);
NodeImpl node = (NodeImpl)parent.addNode("testNode");
node.addMixin("mix:referenceable");
session.save();
return node;
}
private Property addTestProperty(ManageableRepository repository, Node node) throws LoginException,
NoSuchWorkspaceException, RepositoryException
{
Property prop = node.setProperty("testProp", "value");
node.save();
return prop;
}
private void lockNode(Node node) throws LoginException, NoSuchWorkspaceException, RepositoryException
{
node.addMixin("mix:lockable");
node.save();
node.lock(false, false);
}
private void removeNodeInDB(ManageableRepository repository, Node node) throws SQLException,
RepositoryConfigurationException, NamingException, UnsupportedRepositoryOperationException, RepositoryException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
String vTable = "JCR_" + (isMultiDb ? "M" : "S") + "VALUE";
String iTable = "JCR_" + (isMultiDb ? "M" : "S") + "ITEM";
String nodeId = (isMultiDb ? "" : wsEntry.getName()) + node.getUUID();
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
ResultSet resultSet =
conn.prepareStatement("SELECT * FROM " + iTable + " WHERE PARENT_ID='" + nodeId + "'").executeQuery();
while (resultSet.next())
{
String propertyId = resultSet.getString(DBConstants.COLUMN_ID);
conn.prepareStatement("DELETE FROM " + vTable + " WHERE PROPERTY_ID = '" + propertyId + "'").execute();
conn.prepareStatement("DELETE FROM " + iTable + " WHERE ID = '" + propertyId + "'").execute();
}
conn.prepareStatement("DELETE FROM " + iTable + " WHERE ID='" + nodeId + "'").execute();
conn.commit();
conn.close();
}
private void removePropertyInDB(ManageableRepository repository, PropertyImpl property) throws SQLException,
RepositoryConfigurationException, NamingException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
String vTable = "JCR_" + (isMultiDb ? "M" : "S") + "VALUE";
String iTable = "JCR_" + (isMultiDb ? "M" : "S") + "ITEM";
String propertyId = (isMultiDb ? "" : wsEntry.getName()) + property.getInternalIdentifier();
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
conn.prepareStatement("DELETE FROM " + vTable + " WHERE PROPERTY_ID = '" + propertyId + "'").execute();
conn.prepareStatement("DELETE FROM " + iTable + " WHERE ID = '" + propertyId + "'").execute();
conn.commit();
conn.close();
}
private void removeValueRecord(ManageableRepository repository, String propId) throws SQLException,
RepositoryConfigurationException, NamingException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
String vTable = "JCR_" + (isMultiDb ? "M" : "S") + "VALUE";
propId = (isMultiDb ? "" : wsEntry.getName()) + propId;
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
conn.prepareStatement("DELETE FROM " + vTable + " WHERE PROPERTY_ID = '" + propId + "'").execute();
conn.prepareStatement(
"ALTER TABLE " + vTable + " DROP CONSTRAINT JCR_FK_" + (isMultiDb ? "M" : "S") + "VALUE_PROPERTY").execute();
conn.commit();
conn.close();
}
private void removeReferenceRecord(ManageableRepository repository, String propId) throws SQLException,
RepositoryConfigurationException, NamingException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
String rTable = "JCR_" + (isMultiDb ? "M" : "S") + "REF";
propId = (isMultiDb ? "" : wsEntry.getName()) + propId;
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
conn.prepareStatement("DELETE FROM " + rTable + " WHERE PROPERTY_ID = '" + propId + "'").execute();
conn.commit();
conn.close();
}
private void removeItemRecord(ManageableRepository repository, String propId) throws SQLException,
RepositoryConfigurationException, NamingException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
String vTable = "JCR_" + (isMultiDb ? "M" : "S") + "VALUE";
String iTable = "JCR_" + (isMultiDb ? "M" : "S") + "ITEM";
propId = (isMultiDb ? "" : wsEntry.getName()) + propId;
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
conn.prepareStatement(
"ALTER TABLE " + vTable + " DROP CONSTRAINT JCR_FK_" + (isMultiDb ? "M" : "S") + "VALUE_PROPERTY").execute();
conn.prepareStatement(
"ALTER TABLE " + iTable + " DROP CONSTRAINT JCR_FK_" + (isMultiDb ? "M" : "S") + "ITEM_PARENT").execute();
conn.prepareStatement("DELETE FROM " + iTable + " WHERE ID = '" + propId + "'").execute();
conn.commit();
conn.close();
}
private void assingItsOwnParent(ManageableRepository repository, ItemImpl item) throws SQLException,
RepositoryConfigurationException, NamingException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String iTable = "JCR_" + (isMultiDb ? "M" : "S") + "ITEM";
String itemId = (isMultiDb ? "" : wsEntry.getName()) + item.getInternalIdentifier();
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
conn.prepareStatement("DROP INDEX JCR_IDX_" + (isMultiDb ? "M" : "S") + "ITEM_PARENT").execute();
conn.prepareStatement("DROP INDEX JCR_IDX_" + (isMultiDb ? "M" : "S") + "ITEM_PARENT_NAME").execute();
conn.prepareStatement("UPDATE " + iTable + " SET PARENT_ID='" + itemId + "' WHERE ID='" + itemId + "'").execute();
conn.commit();
conn.close();
}
private void clearLockTable(ManageableRepository repository) throws RepositoryConfigurationException, SQLException,
NamingException
{
WorkspaceEntry workspaceEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
LockManagerEntry lockManagerEntry = workspaceEntry.getLockManager();
String sourceName = null;
String queryStatement = null;
if (helper.ispnCacheEnabled())
{
sourceName = lockManagerEntry.getParameterValue("infinispan-cl-cache.jdbc.datasource");
queryStatement =
"DELETE FROM " + lockManagerEntry.getParameterValue("infinispan-cl-cache.jdbc.table.name") + "_" + "L"
+ workspaceEntry.getUniqueName().replace("_", "").replace("-", "_");
}
else
{
sourceName = lockManagerEntry.getParameterValue(CacheableLockManagerImpl.JBOSSCACHE_JDBC_CL_DATASOURCE);
if (lockManagerEntry.getParameterBoolean("jbosscache-shareable"))
{
queryStatement =
"DELETE FROM " + lockManagerEntry.getParameterValue(CacheableLockManagerImpl.JBOSSCACHE_JDBC_TABLE_NAME)
+ " WHERE PARENT='/" + workspaceEntry.getUniqueName() + "/" + CacheableLockManagerImpl.LOCKS + "'";
}
else
{
queryStatement =
"DELETE FROM " + lockManagerEntry.getParameterValue(CacheableLockManagerImpl.JBOSSCACHE_JDBC_TABLE_NAME)
+ " WHERE PARENT='/" + CacheableLockManagerImpl.LOCKS + "'";
}
}
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
conn.prepareStatement(queryStatement).execute();
conn.commit();
conn.close();
}
private void indexNode(ManageableRepository repository, Node node, int state)
throws UnsupportedRepositoryOperationException, RepositoryException
{
// Indexing one more document with same UUID
List<SearchManager> searchManagers =
repository.getWorkspaceContainer(repository.getConfiguration().getSystemWorkspaceName())
.getComponentInstancesOfType(SearchManager.class);
PlainChangesLog log = new PlainChangesLogImpl();
NodeData data =
new TransientNodeData(QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName("", "testNode")),
node.getUUID(), -1, Constants.NT_UNSTRUCTURED, null, 0, null, new AccessControlList());
TransientPropertyData primaryType =
new TransientPropertyData(QPath.makeChildPath(data.getQPath(), Constants.JCR_PRIMARYTYPE),
IdGenerator.generate(), -1, PropertyType.NAME, data.getIdentifier(), false, new TransientValueData(
Constants.NT_UNSTRUCTURED));
log.add(new ItemState(data, ItemState.ADDED, false, null));
log.add(new ItemState(primaryType, ItemState.ADDED, false, null));
for (SearchManager searchManager : searchManagers)
{
if (!(searchManager instanceof SystemSearchManager))
{
searchManager.onSaveItems(log);
break;
}
}
}
private void insertPropertyRecord(ManageableRepository repository, String id, String parentId, String name)
throws RepositoryConfigurationException, SQLException, NamingException, RepositoryException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String iTable = "JCR_" + (isMultiDb ? "M" : "S") + "ITEM";
String vTable = "JCR_" + (isMultiDb ? "M" : "S") + "VALUE";
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
// add another item with new persisted version
String propId = (isMultiDb ? "" : wsEntry.getName()) + IdGenerator.generate();
parentId = (isMultiDb ? "" : wsEntry.getName()) + parentId;
conn.prepareStatement(
"ALTER TABLE " + iTable + " DROP CONSTRAINT JCR_FK_" + (isMultiDb ? "M" : "S") + "ITEM_PARENT").execute();
if (isMultiDb)
{
conn.prepareStatement(
"INSERT INTO " + iTable + " VALUES ('" + propId + "','" + parentId + "','[]" + name
+ "',1,2,1,NULL,1,FALSE)").execute();
}
else
{
conn.prepareStatement(
"INSERT INTO " + iTable + " VALUES ('" + propId + "','" + parentId + "','[]" + name + "',1,'"
+ wsEntry.getName() + "',2,1,NULL,1,FALSE)").execute();
}
conn.prepareStatement("INSERT INTO " + vTable + " VALUES ('10000','data','1','" + propId + "',NULL)").execute();
conn.commit();
conn.close();
}
private void updateValueRecord(ManageableRepository repository, String propId)
throws RepositoryConfigurationException, SQLException, NamingException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
String vTable = "JCR_" + (isMultiDb ? "M" : "S") + "VALUE";
propId = (isMultiDb ? "" : wsEntry.getName()) + propId;
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
conn.prepareStatement(
"UPDATE " + vTable + " SET STORAGE_DESC = 'unexisted-desc' WHERE PROPERTY_ID = '" + propId + "'").execute();
conn.commit();
conn.close();
}
private InconsistencyRepair getNodeRemover(ManageableRepository repository) throws Exception
{
String wsName = repository.getConfiguration().getSystemWorkspaceName();
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
WorkspaceContainerFacade wsContainer = repository.getWorkspaceContainer(wsName);
NodeTypeDataManagerImpl nodeTypeManager =
(NodeTypeDataManagerImpl)wsContainer.getComponent(NodeTypeDataManagerImpl.class);
JDBCWorkspaceDataContainer jdbcDataContainer =
(JDBCWorkspaceDataContainer)wsContainer.getComponent(JDBCWorkspaceDataContainer.class);
String iTable = "JCR_" + (isMultiDb ? "M" : "S") + "ITEM";
InconsistencyRepair repair =
new NodeRemover(new FakeConnectionFactory(jdbcDataContainer), iTable, nodeTypeManager);
return repair;
}
private ResultSet getResultSetWithNode(ManageableRepository repository, Connection conn, NodeImpl node)
throws Exception
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String iTable = "JCR_" + (isMultiDb ? "M" : "S") + "ITEM";
String nodeId = (isMultiDb ? "" : wsEntry.getName()) + node.getInternalIdentifier();
return conn.prepareStatement("SELECT * FROM " + iTable + " WHERE ID = '" + nodeId + "'").executeQuery();
}
private Connection getConnection(ManageableRepository repository) throws Exception
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
return ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
}
private void updateNodeRecord(ManageableRepository repository, String nodeId, int newPersistedVersion, int newIndex)
throws RepositoryConfigurationException, SQLException, NamingException
{
WorkspaceEntry wsEntry = repository.getConfiguration().getWorkspaceEntries().get(0);
boolean isMultiDb = wsEntry.getContainer().getParameterBoolean(JDBCWorkspaceDataContainer.MULTIDB);
String sourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);
String iTable = "JCR_" + (isMultiDb ? "M" : "S") + "ITEM";
nodeId = (isMultiDb ? "" : wsEntry.getName()) + nodeId;
Connection conn = ((DataSource)new InitialContext().lookup(sourceName)).getConnection();
conn.prepareStatement(
"UPDATE " + iTable + " SET VERSION=" + newPersistedVersion + ", I_INDEX=" + newIndex + " WHERE ID = '"
+ nodeId + "'").execute();
conn.commit();
conn.close();
}
private void removeFileFromVS(ManageableRepository repository, String propId)
throws RepositoryConfigurationException
{
String vsPath =
repository.getConfiguration().getWorkspaceEntries().get(0).getContainer().getValueStorages().get(0)
.getParameterValue(FileValueStorage.PATH);
File vsFile = new File(vsPath, propId + "0");
assertTrue(vsFile.exists());
assertTrue(vsFile.delete());
}
private class FakeResultSet extends SybaseJDBCConnectionHelper.EmptyResultSet
{
private final Map<String, String> strFields;
private final Map<String, Integer> intFields;
FakeResultSet(Map<String, String> strFields, Map<String, Integer> intFields)
{
this.strFields = strFields;
this.intFields = intFields;
}
public String getString(String columnName) throws SQLException
{
String value = strFields.get(columnName);
if (value == null)
{
throw new SQLException("Field not found");
}
return value;
}
public int getInt(String columnName) throws SQLException
{
Integer value = intFields.get(columnName);
if (value == null)
{
throw new SQLException("Field not found");
}
return value;
}
}
private class FakeConnectionFactory implements WorkspaceStorageConnectionFactory
{
private final WorkspaceStorageConnection conn;
FakeConnectionFactory(JDBCWorkspaceDataContainer jdbcDataContainer) throws RepositoryException
{
this.conn = jdbcDataContainer.openConnection(false);
}
public WorkspaceStorageConnection openConnection() throws RepositoryException
{
return conn;
}
public WorkspaceStorageConnection openConnection(boolean readOnly) throws RepositoryException
{
return conn;
}
public Connection getJdbcConnection() throws RepositoryException
{
return ((JDBCStorageConnection)conn).getJdbcConnection();
}
public Connection getJdbcConnection(boolean readOnly) throws RepositoryException
{
return ((JDBCStorageConnection)conn).getJdbcConnection();
}
}
}