/*
* 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.content.convertxml;
import org.opencms.db.CmsResourceState;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.i18n.CmsEncoder;
import org.opencms.lock.CmsLock;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.publish.CmsPublishManager;
import org.opencms.report.A_CmsReportThread;
import org.opencms.report.I_CmsReport;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import org.opencms.util.CmsXsltUtil;
import org.opencms.xml.CmsXmlException;
import org.opencms.xml.content.CmsXmlContent;
import org.opencms.xml.content.CmsXmlContentFactory;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
/**
* Converting xml contents according to new schema.
* <p>
*
* @since 7.0.5
*/
public class CmsConvertXmlThread extends A_CmsReportThread {
/** The log object for this class. */
private static final Log LOG = CmsLog.getLog(CmsConvertXmlThread.class);
/** Number of files transformed already. */
private int m_alreadyTransformed;
/** Current CmsObject. */
private CmsObject m_cmsObject;
/** Number of errors while transforming. */
private int m_errorTransform;
/** Number of locked files during of transformation. */
private int m_lockedFiles;
/** Number of files where encoding type could not become get. */
private int m_missingEncodingType;
/** Settings. */
private CmsConvertXmlSettings m_settings;
/**
* Creates a replace html tag Thread.<p>
*
* @param cms the current cms context.
*
* @param settings the settings needed to perform the operation.
*/
public CmsConvertXmlThread(CmsObject cms, CmsConvertXmlSettings settings) {
super(cms, Messages.get().getBundle().key(Messages.GUI_CONVERTXML_THREAD_NAME_0));
initHtmlReport(cms.getRequestContext().getLocale());
m_cmsObject = cms;
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() {
I_CmsReport report = getReport();
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_BEGIN_TRANSFORM_THREAD_0),
I_CmsReport.FORMAT_HEADLINE);
try {
// convert xml contents
mainTransform(
report,
m_settings.getResourceType(),
m_settings.getVfsFolder(),
m_settings.getIncludeSubFolders(),
m_settings.getXslFile(),
m_cmsObject,
m_settings.getOnlyCountFiles());
} catch (Throwable f) {
m_errorTransform += 1;
report.println(Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORMATION_ERROR_0));
if (LOG.isErrorEnabled()) {
LOG.error(f.toString());
}
}
// append runtime statistics to report
getReport().print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_STAT_0));
getReport().println(
org.opencms.report.Messages.get().container(
org.opencms.report.Messages.RPT_STAT_DURATION_1,
getReport().formatRuntime()));
getReport().println(Messages.get().container(Messages.RPT_CONVERTXML_THREAD_END_0), I_CmsReport.FORMAT_HEADLINE);
}
/**
* Locks the current resource.<p>
*
* @param cms the current CmsObject
* @param cmsProject the current project
* @param cmsResource the resourcfe to lock
* @param report the report
*
* @throws CmsException if some goes wrong
*/
private boolean lockResource(CmsObject cms, CmsProject cmsProject, CmsResource cmsResource, I_CmsReport report)
throws CmsException {
cms.getRequestContext().setCurrentProject(cmsProject);
CmsLock lock = cms.getLock(getCms().getSitePath(cmsResource));
// check the lock
if ((lock != null)
&& lock.isOwnedBy(getCms().getRequestContext().getCurrentUser())
&& lock.isOwnedInProjectBy(getCms().getRequestContext().getCurrentUser(), cmsProject)) {
// prove is current lock from current user in current project
return true;
} else if ((lock != null)
&& !lock.isUnlocked()
&& !lock.isOwnedBy(getCms().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(getCms().getRequestContext().getCurrentUser())
&& !lock.isOwnedInProjectBy(getCms().getRequestContext().getCurrentUser(), cmsProject)) {
// 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
cms.changeLock(getCms().getSitePath(cmsResource));
} else if ((lock != null) && lock.isUnlocked()) {
// lock resource from current user in current project
cms.lockResource(getCms().getSitePath(cmsResource));
}
lock = cms.getLock(getCms().getSitePath(cmsResource));
if ((lock != null)
&& lock.isOwnedBy(getCms().getRequestContext().getCurrentUser())
&& !lock.isOwnedInProjectBy(getCms().getRequestContext().getCurrentUser(), cmsProject)) {
// resource could not be locked
m_lockedFiles += 1;
return false;
}
// resource is locked successfully
return true;
}
/**
* Main method to transforms xml contents from files of one format because
* of new xsd file.<p>
*
* @param report I_CmsReport
* @param fileFormat File format of xml contents to transform
* @param resourcePath Path where to transform xml contents
* @param inclSubFolder True, if also transform xml contents in sub folders
* @param xsltFile XLST file which includes logic for transforming
* @param cmsObject Current CmsObject
* @param countFilesToTransformOnly Only count files to transform
*
* @return True if transformation of all xml contents was successful
*/
@SuppressWarnings("unchecked")
private boolean mainTransform(
I_CmsReport report,
int fileFormat,
String resourcePath,
boolean inclSubFolder,
String xsltFile,
CmsObject cmsObject,
boolean countFilesToTransformOnly) {
boolean transformSuccess = true;
boolean transformConditions = true;
// write parameters to report
report.println(Messages.get().container(Messages.RPT_CONVERTXML_BEGIN_TRANSFORM_0), I_CmsReport.FORMAT_NOTE);
report.println(Messages.get().container(Messages.RPT_CONVERTXML_PARAMETERS_0), I_CmsReport.FORMAT_NOTE);
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_PARAMETERS_RESOURCE_PATH_1, resourcePath),
I_CmsReport.FORMAT_NOTE);
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_PARAMETERS_INC_SUBFOLDERS_1,
new Boolean(inclSubFolder).toString()), I_CmsReport.FORMAT_NOTE);
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_PARAMETERS_XSLT_FILE_1, xsltFile),
I_CmsReport.FORMAT_NOTE);
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_PARAMETERS_ONLY_COUNT_1,
new Boolean(countFilesToTransformOnly).toString()), I_CmsReport.FORMAT_NOTE);
// check if xslt file is available
if (CmsStringUtil.isEmpty(xsltFile)) {
report.println(Messages.get().container(Messages.RPT_CONVERTXML_NO_XSLT_FILE_0), I_CmsReport.FORMAT_ERROR);
transformConditions = false;
}
// check if new xsd main file is available
String newXsdMainFile = "";
String xsltString = "";
try {
xsltString = new String(cmsObject.readFile(xsltFile).getContents());
} catch (CmsException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
}
// get main xsd file string in xml content in format: xsi:noNamespaceSchemaLocation="opencms://system/modules/org.opencms.frontend.templatetwo.demo/schemas/article.xsd"
int posMainFileBegin = xsltString.indexOf("xsi:noNamespaceSchemaLocation=\"");
if (posMainFileBegin > 0) {
String fileName = xsltString.substring(posMainFileBegin + "xsi:noNamespaceSchemaLocation=\"".length());
int posMainFileEnd = fileName.indexOf("\"");
if (posMainFileEnd > 0) {
newXsdMainFile = fileName.substring(0, posMainFileEnd);
}
}
// check file
int fileLength = newXsdMainFile.length();
if ((fileLength < 5) || !newXsdMainFile.substring(fileLength - 4, fileLength).toUpperCase().equals(".XSD")) {
newXsdMainFile = "";
}
if (CmsStringUtil.isEmpty(newXsdMainFile)) {
report.println(Messages.get().container(Messages.RPT_CONVERTXML_NO_XSD_FILE_0), I_CmsReport.FORMAT_ERROR);
transformConditions = false;
} else {
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_PARAMETERS_NEW_XSD_MAINFILE_1,
newXsdMainFile), I_CmsReport.FORMAT_NOTE);
}
// check if vfs folder is set
if (CmsStringUtil.isEmpty(resourcePath)) {
report.println(Messages.get().container(Messages.RPT_CONVERTXML_NO_VFS_FOLDER_0), I_CmsReport.FORMAT_ERROR);
transformConditions = false;
}
// only start actions if all conditions are okay
if (!transformConditions) {
return false;
}
// read all files to transform
report.println(Messages.get().container(Messages.RPT_CONVERTXML_START_SEARCHING_0), I_CmsReport.FORMAT_NOTE);
List<CmsResource> files2Transform = null;
try {
files2Transform = cmsObject.readResources(
resourcePath,
CmsResourceFilter.requireType(fileFormat),
inclSubFolder);
} catch (CmsException e) {
m_errorTransform += 1;
report.println(Messages.get().container(Messages.RPT_CONVERTXML_SEARCH_ERROR_0), I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
report.println(Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORM_END_0), I_CmsReport.FORMAT_NOTE);
return false;
}
int file2Transform = 0;
if (files2Transform != null) {
file2Transform = files2Transform.size();
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_FOUND_FILES_1,
new Integer(file2Transform).toString()), I_CmsReport.FORMAT_OK);
} else {
report.println(Messages.get().container(Messages.RPT_CONVERTXML_NO_FILES_FOUND_0), I_CmsReport.FORMAT_OK);
return false;
}
if (countFilesToTransformOnly || (file2Transform < 1)) {
report.println(Messages.get().container(Messages.RPT_CONVERTXML_NO_FILES_FOUND_0), I_CmsReport.FORMAT_OK);
return false;
}
// transform and write files
CmsObject cmsObject2Publish = transformAndWriteFiles(
files2Transform,
xsltFile,
cmsObject,
newXsdMainFile,
report);
// publish files in project
report.println(Messages.get().container(Messages.RPT_CONVERTXML_PUBLISHING_FILES_0), I_CmsReport.FORMAT_NOTE);
CmsPublishManager cmsPublishManager = OpenCms.getPublishManager();
try {
cmsPublishManager.publishProject(cmsObject2Publish);
} catch (Exception e) {
m_errorTransform += 1;
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORMATION_ERROR_0),
I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.toString());
}
}
// output from the results
report.println(Messages.get().container(Messages.RPT_CONVERTXML_RESULT_0), I_CmsReport.FORMAT_NOTE);
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_FOUND_FILES_1,
new Integer(file2Transform).toString()), I_CmsReport.FORMAT_NOTE);
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_FILES_ALREADY_TRANSFORMED_1,
new Integer(m_alreadyTransformed).toString()), I_CmsReport.FORMAT_NOTE);
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_TRANSFORM_NUMBER_ERRORS_1,
new Integer(m_errorTransform).toString()), I_CmsReport.FORMAT_NOTE);
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_LOCKED_FILES_1,
new Integer(m_lockedFiles).toString()), I_CmsReport.FORMAT_NOTE);
if ((m_lockedFiles > 0) || (m_errorTransform > 0)) {
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORMING_FAILED_0),
I_CmsReport.FORMAT_ERROR);
} else {
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORMING_SUCCESS_0),
I_CmsReport.FORMAT_OK);
}
report.println(Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORM_END_0), I_CmsReport.FORMAT_NOTE);
return transformSuccess;
}
/**
* Gets file xml content.<p>
*
* @param cmsResource current resource CmsResource
* @param cmsFile current CmsFile
* @param cmsObject current CmsObject
* @param xmlContent xml content to write
* @param encodingType encoding type
* @param report I_CmsReport
*/
private void setXmlContentFromFile(
CmsResource cmsResource,
CmsFile cmsFile,
CmsObject cmsObject,
String xmlContent,
String encodingType,
I_CmsReport report) {
try {
byte[] fileContent = xmlContent.getBytes(encodingType);
cmsFile.setContents(fileContent);
// write into file
cmsObject.writeFile(cmsFile);
// unlock resource
try {
cmsObject.unlockResource(cmsObject.getSitePath(cmsResource));
} catch (CmsException e) {
m_errorTransform += 1;
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_UNLOCK_FILE_1,
cmsObject.getSitePath(cmsResource)), I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
}
} catch (Exception e) {
m_errorTransform += 1;
String reportContent = "<br/>";
reportContent = reportContent + CmsEncoder.escapeXml(xmlContent);
reportContent = reportContent.replaceAll("\r\n", "<br/>");
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_WRITE_ERROR_1, reportContent),
I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.toString());
}
}
}
/**
* Transforms and write files.<p>
*
* @param files2Transform Files to transform
* @param xsltFile XLST file which includes logic for transforming
* @param cmsObject Current CmsObject
* @param newXsdMainFile New xsd main file
* @param report I_CmsReport
*
* @return Project with files to publish
*
* @throws CmsException Can become thrown while creating temporary OpenCms Projects
*/
private CmsObject transformAndWriteFiles(
List<CmsResource> files2Transform,
String xsltFile,
CmsObject cmsObject,
String newXsdMainFile,
I_CmsReport report) {
// the CmsObject to publish resources
CmsObject cms1 = null;
// the CmsObject to handle resources which are not to publish
CmsObject cms2 = null;
// the publish project
CmsProject project2Publish = null;
// initialize the CmsObjects and the publish project
try {
cms1 = OpenCms.initCmsObject(cmsObject);
cms2 = OpenCms.initCmsObject(cmsObject);
cms1.copyResourceToProject("/");
project2Publish = cms1.createTempfileProject(); // init new
cms1.getRequestContext().setCurrentProject(project2Publish);
} catch (CmsException e) {
report.println(Messages.get().container(Messages.RPT_CONVERTXML_INITIALIZE_CMS_ERROR_0));
if (LOG.isErrorEnabled()) {
LOG.error(e.toString());
}
return cms1;
}
// iterate over all the resources to transform
Iterator<CmsResource> iter = files2Transform.iterator();
while (iter.hasNext()) {
// get the next resource to transform
CmsResource cmsResource = iter.next();
// check if the resource has to be published after transforming
boolean resource2Publish = false;
// get info if resource shall become published
CmsResourceState cmsResourceState = cmsResource.getState();
if (!(cmsResourceState.equals(CmsResourceState.STATE_CHANGED) || cmsResourceState.equals(CmsResourceState.STATE_NEW))) {
// resource is not touched or is not new
resource2Publish = true;
}
// get current lock from file
if (resource2Publish) {
// lock the resource in the publish project
try {
// try to lock the resource
if (!lockResource(cms1, project2Publish, cmsResource, report)) {
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_LOCKED_FILE_1,
cmsObject.getSitePath(cmsResource)), I_CmsReport.FORMAT_ERROR);
continue;
}
} catch (CmsException e) {
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_LOCKED_FILE_1,
cmsObject.getSitePath(cmsResource)), I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
continue;
}
} else {
// lock the resource in the project where the resource was last modified in
try {
// get the project id from the project where the resource is last modified in
CmsUUID pid = cmsResource.getProjectLastModified();
CmsProject fileProject = cms2.readProject(pid);
cms2.getRequestContext().setCurrentProject(fileProject);
// try to lock the resource
if (!lockResource(cms2, fileProject, cmsResource, report)) {
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_LOCKED_FILE_1,
cmsObject.getSitePath(cmsResource)), I_CmsReport.FORMAT_ERROR);
continue;
}
} catch (CmsException e) {
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_LOCKED_FILE_1,
cmsObject.getSitePath(cmsResource)), I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
continue;
}
}
// get CmsFile object and the xml content
CmsFile cmsFile = null;
String fileXmlContent = "";
try {
cmsFile = cmsObject.readFile(cmsResource);
CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), cmsFile);
fileXmlContent = xmlContent.toString();
} catch (CmsException e) {
m_errorTransform += 1;
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORMATION_ERROR_0),
I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
continue;
}
// get encoding per resource
String encodingType = "";
try {
encodingType = cmsObject.readPropertyObject(
cmsResource.getRootPath(),
CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING,
true).getValue(OpenCms.getSystemInfo().getDefaultEncoding());
} catch (CmsException e) {
encodingType = OpenCms.getSystemInfo().getDefaultEncoding();
}
// check transform conditions per resource
// encoding type given?
if (CmsStringUtil.isEmpty(encodingType)) {
m_missingEncodingType += 1;
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_MISSION_ENCODING_TYPE_1,
cmsResource.getRootPath()), I_CmsReport.FORMAT_ERROR);
continue;
}
// already transformed?
if (fileXmlContent.toUpperCase().contains(newXsdMainFile.toUpperCase())) {
m_alreadyTransformed += 1;
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_FILE_ALREADY_TRANSFORMED_1,
cmsResource.getRootPath()), I_CmsReport.FORMAT_OK);
continue;
}
// create and write the changed xml content
try {
String transformedXmlContent = CmsXsltUtil.transformXmlContent(cmsObject, xsltFile, fileXmlContent);
transformedXmlContent = "<?xml version=\"1.0\" encoding=\"".concat(encodingType).concat("\"?>").concat(
transformedXmlContent);
// write file xml content
if (resource2Publish) {
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_TRANSFORM_CURRENT_FILE_NAME2_2,
cmsResource.getRootPath(),
encodingType), I_CmsReport.FORMAT_OK);
cms1.getRequestContext().setCurrentProject(project2Publish);
setXmlContentFromFile(cmsResource, cmsFile, cms1, transformedXmlContent, encodingType, report);
} else {
report.println(Messages.get().container(
Messages.RPT_CONVERTXML_TRANSFORM_CURRENT_FILE_NAME_2,
cmsResource.getRootPath(),
encodingType), I_CmsReport.FORMAT_OK);
setXmlContentFromFile(cmsResource, cmsFile, cms2, transformedXmlContent, encodingType, report);
}
} catch (CmsXmlException e) {
m_errorTransform += 1;
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORMATION_ERROR_0),
I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
} catch (CmsException e) {
m_errorTransform += 1;
report.println(
Messages.get().container(Messages.RPT_CONVERTXML_TRANSFORMATION_ERROR_0),
I_CmsReport.FORMAT_ERROR);
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessageContainer(), e);
}
}
}
return cms1;
}
}