/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
*
* This library 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 library 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.
*
* For further information about Alkacon Software GmbH, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.workplace.tools.searchindex.sourcesearch;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.i18n.CmsLocaleManager;
import org.opencms.lock.CmsLock;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.report.A_CmsReportThread;
import org.opencms.report.I_CmsReport;
import org.opencms.util.CmsStringUtil;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
/**
* Searches in sources.
* <p>
*
* @since 7.5.3
*/
public class CmsSourceSearchThread extends A_CmsReportThread {
/** The log object for this class. */
private static final Log LOG = CmsLog.getLog(CmsSourceSearchThread.class);
/** Number of errors while searching. */
private int m_errorSearch;
/** Number of errors while updating. */
private int m_errorUpdate;
/** Number of locked files during updating. */
private int m_lockedFiles;
/** The found resources. */
private List<CmsResource> m_matchedResources = new ArrayList<CmsResource>();
/** The current session. */
private HttpSession m_session;
/** Settings. */
private CmsSourceSearchSettings m_settings;
/**
* Creates a replace html tag Thread.<p>
*
* @param session the current session
* @param cms the current cms object
* @param settings the settings needed to perform the operation.
*/
public CmsSourceSearchThread(HttpSession session, CmsObject cms, CmsSourceSearchSettings settings) {
super(cms, Messages.get().getBundle().key(Messages.GUI_SOURCESEARCH_THREAD_NAME_0));
initHtmlReport(cms.getRequestContext().getLocale());
m_session = session;
m_settings = settings;
}
/**
* @see org.opencms.report.A_CmsReportThread#getReportUpdate()
*/
@Override
public String getReportUpdate() {
return getReport().getReportUpdate();
}
/**
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
// get the report
I_CmsReport report = getReport();
boolean isError = false;
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_BEGIN_SEARCH_THREAD_0),
I_CmsReport.FORMAT_HEADLINE);
// write parameters to report
report.println(Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_0), I_CmsReport.FORMAT_HEADLINE);
// the paths
if (!m_settings.getPaths().isEmpty()) {
// iterate over the paths
Iterator<String> iter = m_settings.getPaths().iterator();
while (iter.hasNext()) {
String path = iter.next();
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_RESOURCE_PATH_1, path),
I_CmsReport.FORMAT_NOTE);
}
} else {
// no paths selected
isError = true;
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_EMPTY_RESOURCE_PATHS_0),
I_CmsReport.FORMAT_ERROR);
}
// the search pattern
if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_settings.getSearchpattern())) {
// there is a search pattern
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_PARAMETERS_SEARCHPATTERN_1,
CmsStringUtil.escapeHtml(m_settings.getSearchpattern())),
I_CmsReport.FORMAT_NOTE);
} else {
// empty search pattern
isError = true;
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_EMPTY_SEARCHPATTERN_0),
I_CmsReport.FORMAT_ERROR);
}
// the replace pattern
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_PARAMETERS_REPLACEPATTERN_1,
CmsStringUtil.escapeHtml(m_settings.getReplacepattern())),
I_CmsReport.FORMAT_NOTE);
// the project
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_PROJECT_1, m_settings.getProject()),
I_CmsReport.FORMAT_NOTE);
// remarks for search/replace dependent od the replace pattern and the selected project
// in the online project search is possible only
// in other projects there is replaced, if the replace pattern is not empty
boolean replace = false;
if (CmsStringUtil.isEmpty(m_settings.getReplacepattern())) {
// empty search pattern, search only
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_EMPTY_REPLACEPATTERN_0),
I_CmsReport.FORMAT_NOTE);
} else {
// not empty search pattern, search and replace
replace = true;
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_NOTEMPTY_REPLACEPATTERN_0),
I_CmsReport.FORMAT_NOTE);
}
// make an OpenCms object copy if replace is active
CmsObject cmsObject = getCms();
if (replace && !m_settings.getProject().equals(cmsObject.getRequestContext().getCurrentProject().getName())) {
try {
cmsObject = OpenCms.initCmsObject(getCms());
CmsProject cmsProject = getCms().readProject(m_settings.getProject());
cmsObject.getRequestContext().setCurrentProject(cmsProject);
} catch (CmsException e) {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_WRONG_ENVIRONMENT_REPLACING_0),
I_CmsReport.FORMAT_NOTE);
replace = false;
}
}
// search the resources and replace the patterns
if (!isError && searchResources(report, replace, cmsObject)) {
// show the resources
// save the matched file list in the session
m_session.setAttribute(CmsSourceSearchSettings.ATTRIBUTE_NAME_SOURCESEARCH_RESULT_LIST, m_matchedResources);
} else {
// do not show the resources, because there were errors while searching
}
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_END_SEARCH_THREAD_0),
I_CmsReport.FORMAT_HEADLINE);
}
/**
* Locks the current resource.<p>
*
* @param cmsObject the current CmsObject
* @param cmsResource the resource to lock
* @param report the report
*
* @return <code>true</code> if the resource could be locked
*
* @throws CmsException if some goes wrong
*/
private boolean lockResource(CmsObject cmsObject, CmsResource cmsResource, I_CmsReport report) throws CmsException {
CmsLock lock = cmsObject.getLock(cmsObject.getSitePath(cmsResource));
// check the lock
if ((lock != null)
&& lock.isOwnedBy(cmsObject.getRequestContext().getCurrentUser())
&& lock.isOwnedInProjectBy(
cmsObject.getRequestContext().getCurrentUser(),
cmsObject.getRequestContext().getCurrentProject())) {
// prove is current lock from current user in current project
return true;
} else if ((lock != null)
&& !lock.isUnlocked()
&& !lock.isOwnedBy(cmsObject.getRequestContext().getCurrentUser())) {
// the resource is not locked by the current user, so can not lock it
m_lockedFiles += 1;
return false;
} else if ((lock != null)
&& !lock.isUnlocked()
&& lock.isOwnedBy(cmsObject.getRequestContext().getCurrentUser())
&& !lock.isOwnedInProjectBy(
cmsObject.getRequestContext().getCurrentUser(),
cmsObject.getRequestContext().getCurrentProject())) {
// prove is current lock from current user but not in current project
// file is locked by current user but not in current project
// change the lock
cmsObject.changeLock(cmsObject.getSitePath(cmsResource));
} else if ((lock != null) && lock.isUnlocked()) {
// lock resource from current user in current project
cmsObject.lockResource(cmsObject.getSitePath(cmsResource));
}
lock = cmsObject.getLock(cmsObject.getSitePath(cmsResource));
if ((lock != null)
&& lock.isOwnedBy(cmsObject.getRequestContext().getCurrentUser())
&& !lock.isOwnedInProjectBy(
cmsObject.getRequestContext().getCurrentUser(),
cmsObject.getRequestContext().getCurrentProject())) {
// resource could not be locked
m_lockedFiles += 1;
return false;
}
// resource is locked successfully
return true;
}
/**
* Search the resources.<p>
*
* @param report the report.
* @param replace true, if search and replace. False is search only.
* @param cmsObject the CmsObject using to write files
*
* @return true, if searching was successful, otherwise false.
*/
private boolean searchResources(I_CmsReport report, boolean replace, CmsObject cmsObject) {
// collect all file contents in the selected folder
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_START_COLLECTING_FILES_TO_SEARCH_IN_0),
I_CmsReport.FORMAT_HEADLINE);
List<CmsResource> resources = new ArrayList<CmsResource>();
// iterate over all selected paths
Iterator<String> iterPaths = m_settings.getPaths().iterator();
while (iterPaths.hasNext()) {
String path = iterPaths.next();
try {
// only read resources which are files and not deleted, which are in the current time range window and where the current
// user has the sufficient permissions to read them
List<CmsResource> tmpResources = getCms().readResources(
path,
CmsResourceFilter.ALL.addRequireFile().addExcludeState(CmsResource.STATE_DELETED).addRequireTimerange().addRequireVisible());
if ((tmpResources != null) && !tmpResources.isEmpty()) {
resources.addAll(tmpResources);
}
} catch (CmsException e) {
// an error occured
LOG.error(Messages.get().container(Messages.RPT_SOURCESEARCH_ERROR_READING_RESOURCES_1, path), e);
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_ERROR_READING_RESOURCES_1, path),
I_CmsReport.FORMAT_ERROR);
return false;
}
}
if (resources.isEmpty()) {
// no resources found, so search is not possible
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_NO_FILES_TO_SEARCH_IN_0),
I_CmsReport.FORMAT_NOTE);
return true;
}
// at least one file in the select path could be read
// number of files to update
int nrOfFiles = resources.size();
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_NR_OF_FILES_TO_SEARCH_IN_1, new Integer(nrOfFiles)),
I_CmsReport.FORMAT_NOTE);
if (replace) {
// start searching and replacing
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_START_SEARCHING_REPLACING_0),
I_CmsReport.FORMAT_HEADLINE);
} else {
// start searching
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_START_SEARCHING_0),
I_CmsReport.FORMAT_HEADLINE);
}
// iterate over the files in the selected path
Iterator<CmsResource> iterResources = resources.iterator();
// the file counter
int fileCounter = 0;
int matchedFiles = 0;
while (iterResources.hasNext()) {
CmsResource cmsResource = iterResources.next();
fileCounter += 1;
CmsFile cmsFile = null;
try {
cmsFile = getCms().readFile(cmsResource);
} catch (CmsException e) {
report.print(
org.opencms.report.Messages.get().container(Messages.RPT_SOURCESEARCH_COULD_NOT_READ_FILE_0),
I_CmsReport.FORMAT_ERROR);
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0),
I_CmsReport.FORMAT_ERROR);
m_errorSearch += 1;
continue;
}
// report entries
report.print(
org.opencms.report.Messages.get().container(
org.opencms.report.Messages.RPT_SUCCESSION_2,
String.valueOf(fileCounter),
String.valueOf(nrOfFiles)),
I_CmsReport.FORMAT_NOTE);
report.print(org.opencms.report.Messages.get().container(
org.opencms.report.Messages.RPT_ARGUMENT_1,
report.removeSiteRoot(cmsResource.getRootPath())));
report.print(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
I_CmsReport.FORMAT_DEFAULT);
byte[] contents = cmsFile.getContents();
String encoding = CmsLocaleManager.getResourceEncoding(cmsObject, cmsFile);
String content = null;
try {
content = new String(contents, encoding);
} catch (UnsupportedEncodingException e1) {
report.print(
org.opencms.report.Messages.get().container(Messages.RPT_SOURCESEARCH_COULD_NOT_READ_FILE_0),
I_CmsReport.FORMAT_ERROR);
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0),
I_CmsReport.FORMAT_ERROR);
m_errorSearch += 1;
continue;
}
boolean matched = false;
Matcher matcher = null;
try {
matcher = Pattern.compile(m_settings.getSearchpattern()).matcher(content);
if (matcher.find()) {
// search pattern did match here, so take this file in the list with matches resources
m_matchedResources.add(cmsResource);
matched = true;
matchedFiles += 1;
if (replace) {
report.print(
Messages.get().container(Messages.RPT_SOURCESEARCH_MATCHED_0),
I_CmsReport.FORMAT_OK);
} else {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_MATCHED_0),
I_CmsReport.FORMAT_OK);
}
} else {
// search pattern did not match
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_NOT_MATCHED_0),
I_CmsReport.FORMAT_NOTE);
}
} catch (Exception e) {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_APPLY_PATTERN_ERROR_1, e),
I_CmsReport.FORMAT_ERROR);
m_errorSearch += 1;
continue;
}
// replace if matched and configured
if (replace && matched) {
// get current lock from file
try {
// try to lock the resource
if (!lockResource(cmsObject, cmsResource, report)) {
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_LOCKED_FILE_0,
cmsObject.getSitePath(cmsResource)),
I_CmsReport.FORMAT_ERROR);
continue;
}
} catch (CmsException e) {
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_LOCKED_FILE_0,
cmsObject.getSitePath(cmsResource)),
I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
continue;
}
// replace the content
content = matcher.replaceAll(m_settings.getReplacepattern());
// write the resource
try {
cmsFile.setContents(content.getBytes(encoding));
cmsObject.writeFile(cmsFile);
} catch (Exception e) {
m_errorUpdate += 1;
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0),
I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.toString());
}
continue;
}
// unlock the resource
try {
cmsObject.unlockResource(cmsObject.getSitePath(cmsResource));
} catch (CmsException e) {
m_errorUpdate += 1;
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_UNLOCK_FILE_0),
I_CmsReport.FORMAT_WARNING);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
continue;
}
// successfully updated
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
I_CmsReport.FORMAT_OK);
}
}
// report entries
if (replace) {
// finish searching and replacing
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_END_SEARCHING_REPLACING_0),
I_CmsReport.FORMAT_HEADLINE);
} else {
// finish searching
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_END_SEARCHING_0),
I_CmsReport.FORMAT_HEADLINE);
}
// the results are written in the report
report.println(Messages.get().container(Messages.RPT_SOURCESEARCH_RESULT_0), I_CmsReport.FORMAT_HEADLINE);
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_NR_OF_FILES_TO_SEARCH_IN_1,
new Integer(nrOfFiles).toString()),
I_CmsReport.FORMAT_NOTE);
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_NR_OF_FILES_MATCHED_1,
new Integer(matchedFiles).toString()),
I_CmsReport.FORMAT_NOTE);
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_NUMBER_OF_SEARCH_ERRORS_1,
new Integer(m_errorSearch).toString()),
I_CmsReport.FORMAT_NOTE);
if (replace) {
// replace report entries
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_NUMBER_OF_REPLACE_ERRORS_1,
new Integer(m_errorUpdate).toString()),
I_CmsReport.FORMAT_NOTE);
report.println(
Messages.get().container(
Messages.RPT_SOURCESEARCH_LOCKED_FILES_1,
new Integer(m_lockedFiles).toString()),
I_CmsReport.FORMAT_NOTE);
if (matchedFiles == 0) {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_NO_FILES_TO_REPLACE_FOUND_0),
I_CmsReport.FORMAT_OK);
} else {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_CLICK_OK_TO_GET_LIST_0),
I_CmsReport.FORMAT_OK);
}
if (m_lockedFiles > 0) {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_REPLACE_FAILED_0),
I_CmsReport.FORMAT_ERROR);
} else {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_REPLACE_SUCCESS_0),
I_CmsReport.FORMAT_OK);
}
} else {
// search report entries
if (matchedFiles == 0) {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_NO_FILES_FOUND_0),
I_CmsReport.FORMAT_OK);
} else {
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_CLICK_OK_TO_GET_LIST_0),
I_CmsReport.FORMAT_OK);
}
if (m_errorSearch > 0) {
// only searching failed
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_SEARCH_FAILED_0),
I_CmsReport.FORMAT_ERROR);
} else {
// only searching was successful
report.println(
Messages.get().container(Messages.RPT_SOURCESEARCH_SEARCH_SUCCESS_0),
I_CmsReport.FORMAT_OK);
}
}
// searching was successful
return true;
}
}