/* * Copyright (C) 2006 Alfresco, Inc. * * Licensed under the Mozilla Public License version 1.1 * with a permitted attribution clause. You may obtain a * copy of the License at * * http://www.alfresco.org/legal/license.txt * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific * language governing permissions and limitations under the * License. */ package org.alfresco.repo.admin; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.importer.ImporterBootstrap; import org.alfresco.repo.node.index.FullIndexRecoveryComponent.RecoveryMode; import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.InvalidStoreRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.AbstractLifecycleBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; /** * Component to perform a bootstrap check of the alignment of the * database, Lucene indexes and content store. *

* The algorithm is: *

* If any of the steps fail then the bootstrap bean will fail, except if * the indexes are marked for full recovery. In this case, the Lucene * checks are not required as the indexes will be due for a rebuild. * * @author Derek Hulley */ public class ConfigurationChecker extends AbstractLifecycleBean { private static Log logger = LogFactory.getLog(ConfigurationChecker.class); private static final String WARN_RELATIVE_DIR_ROOT = "system.config_check.warn.dir_root"; private static final String MSG_DIR_ROOT = "system.config_check.msg.dir_root"; private static final String ERR_DUPLICATE_ROOT_NODE = "system.config_check.err.indexes.duplicate_root_node"; private static final String ERR_MISSING_INDEXES = "system.config_check.err.missing_index"; private static final String ERR_MISSING_CONTENT = "system.config_check.err.missing_content"; private static final String ERR_FIX_DIR_ROOT = "system.config_check.err.fix_dir_root"; private static final String MSG_HOWTO_INDEX_RECOVER = "system.config_check.msg.howto_index_recover"; private static final String WARN_STARTING_WITH_ERRORS = "system.config_check.warn.starting_with_errors"; private boolean strict; private RecoveryMode indexRecoveryMode; private String dirRoot; private ImporterBootstrap systemBootstrap; private TransactionService transactionService; private NamespaceService namespaceService; private NodeService nodeService; private SearchService searchService; private ContentService contentService; public ConfigurationChecker() { } @Override public String toString() { StringBuilder sb = new StringBuilder(50); sb.append("ConfigurationChecker") .append("[indexRecoveryMode=").append(indexRecoveryMode) .append("]"); return sb.toString(); } /** * This flag controls the behaviour of the component in the event of problems being found. * Generally, the system should be strict, but this can be changed if indexes are * going to be recovered, or if missing content is acceptable. * * @param strict true to prevent system startup if problems are found, otherwise * false to allow the system to startup regardless. */ public void setStrict(boolean strict) { this.strict = strict; } /** * Set the index recovery mode. If this is * {@link org.alfresco.repo.node.index.FullIndexRecoveryComponent.RecoveryMode#VALIDATE FULL} * then the index checks are ignored as the indexes will be scheduled for a rebuild * anyway. * * @see org.alfresco.repo.node.index.FullIndexRecoveryComponent.RecoveryMode */ public void setIndexRecoveryMode(String indexRecoveryMode) { this.indexRecoveryMode = RecoveryMode.valueOf(indexRecoveryMode); } public void setDirRoot(String dirRoot) { this.dirRoot = dirRoot; } public void setSystemBootstrap(ImporterBootstrap systemBootstrap) { this.systemBootstrap = systemBootstrap; } public void setTransactionService(TransactionService transactionService) { this.transactionService = transactionService; } public void setNamespaceService(NamespaceService namespaceService) { this.namespaceService = namespaceService; } public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } public void setSearchService(SearchService searchService) { this.searchService = searchService; } public void setContentService(ContentService contentService) { this.contentService = contentService; } @Override protected void onBootstrap(ApplicationEvent event) { TransactionWork checkWork = new TransactionWork() { public Object doWork() throws Exception { check(); return null; } }; TransactionUtil.executeInUserTransaction(transactionService, checkWork); } /** * Performs the check work. */ private void check() { if (logger.isDebugEnabled()) { logger.debug("Starting bootstrap configuration check: " + this); } // check the dir.root boolean isRelativeRoot = dirRoot.startsWith("."); if (isRelativeRoot) { String msg = I18NUtil.getMessage(WARN_RELATIVE_DIR_ROOT, dirRoot); logger.warn(msg); } File dirRootFile = new File(dirRoot); String msgDirRoot = I18NUtil.getMessage(MSG_DIR_ROOT, dirRootFile); logger.info(msgDirRoot); // get all root nodes from the NodeService, i.e. database List storeRefs = nodeService.getStores(); List missingIndexStoreRefs = new ArrayList(0); for (StoreRef storeRef : storeRefs) { NodeRef rootNodeRef = null; try { rootNodeRef = nodeService.getRootNode(storeRef); } catch (InvalidStoreRefException e) { // the store is invalid and will therefore not have a root node entry continue; } if (indexRecoveryMode != RecoveryMode.FULL) { if (logger.isDebugEnabled()) { logger.debug("Checking index for store: " + storeRef); } // perform a Lucene query for the root node SearchParameters sp = new SearchParameters(); sp.addStore(storeRef); sp.setLanguage(SearchService.LANGUAGE_LUCENE); sp.setQuery("ID:" + LuceneQueryParser.escape(rootNodeRef.toString())); ResultSet results = null; int size = 0; try { results = searchService.query(sp); size = results.length(); } finally { try { results.close(); } catch (Throwable e) {} } if (size == 0) { // indexes missing for root node missingIndexStoreRefs.add(storeRef); // debug if (logger.isDebugEnabled()) { logger.debug("Index missing for store: \n" + " store: " + storeRef); } } else if (size > 1) { // there are duplicates String msg = I18NUtil.getMessage(ERR_DUPLICATE_ROOT_NODE, storeRef); throw new AlfrescoRuntimeException(msg); } } } // check for the system version properties content snippet boolean versionPropertiesContentAvailable = true; NodeRef descriptorNodeRef = getSystemDescriptor(); if (descriptorNodeRef != null) { // get the version properties ContentReader reader = contentService.getReader( descriptorNodeRef, ContentModel.PROP_SYS_VERSION_PROPERTIES); if (reader != null && !reader.exists()) { // the property is there, but the content is not versionPropertiesContentAvailable = false; } } // check for missing indexes int missingStoreIndexes = missingIndexStoreRefs.size(); if (missingStoreIndexes > 0) { String msg = I18NUtil.getMessage(ERR_MISSING_INDEXES, missingStoreIndexes); logger.error(msg); String msgRecover = I18NUtil.getMessage(MSG_HOWTO_INDEX_RECOVER); logger.info(msgRecover); } // check for missing content if (!versionPropertiesContentAvailable) { String msg = I18NUtil.getMessage(ERR_MISSING_CONTENT); logger.error(msg); } // handle either content or indexes missing if (missingStoreIndexes > 0 || !versionPropertiesContentAvailable) { String msg = I18NUtil.getMessage(ERR_FIX_DIR_ROOT, dirRootFile); logger.error(msg); // Now determine the failure behaviour if (strict) { throw new AlfrescoRuntimeException(msg); } else { String warn = I18NUtil.getMessage(WARN_STARTING_WITH_ERRORS); logger.warn(warn); } } } /** * @return Returns the system descriptor node or null */ public NodeRef getSystemDescriptor() { StoreRef systemStoreRef = systemBootstrap.getStoreRef(); List nodeRefs = null; if (nodeService.exists(systemStoreRef)) { Properties systemProperties = systemBootstrap.getConfiguration(); String path = systemProperties.getProperty("system.descriptor.current.childname"); String searchPath = "/" + path; NodeRef rootNodeRef = nodeService.getRootNode(systemStoreRef); nodeRefs = searchService.selectNodes(rootNodeRef, searchPath, null, namespaceService, false); if (nodeRefs.size() > 0) { NodeRef descriptorNodeRef = nodeRefs.get(0); return descriptorNodeRef; } } return null; } @Override protected void onShutdown(ApplicationEvent event) { // nothing here } }