001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.importexport; 029 030import org.opencms.configuration.CmsConfigurationManager; 031import org.opencms.file.CmsFile; 032import org.opencms.main.CmsLog; 033import org.opencms.main.OpenCms; 034import org.opencms.util.CmsFileUtil; 035import org.opencms.util.CmsXmlSaxWriter; 036 037import java.io.File; 038import java.io.FileOutputStream; 039import java.io.FileWriter; 040import java.io.IOException; 041import java.io.StringWriter; 042import java.io.Writer; 043import java.util.zip.ZipEntry; 044import java.util.zip.ZipOutputStream; 045 046import org.apache.commons.logging.Log; 047 048import org.dom4j.io.SAXWriter; 049import org.xml.sax.SAXException; 050 051/** 052 * Wrapper to write exported OpenCms resources either to a .ZIP file or to the file system.<p> 053 * 054 * @since 7.5.1 055 */ 056public class CmsExportHelper { 057 058 /** Length that can be safely written to ZIP output. */ 059 private static final int SUB_LENGTH = 4096; 060 061 private static final Log LOG = CmsLog.getLog(CmsExportHelper.class); 062 063 /** The main export path. */ 064 private String m_exportPath; 065 066 /** The export ZIP stream to write resources to. */ 067 private ZipOutputStream m_exportZipStream; 068 069 /** Indicates if the resources are exported in one export .ZIP file or as individual files. */ 070 private boolean m_isExportAsFiles; 071 072 /** The SAX writer for the Manifest file. */ 073 private SAXWriter m_saxWriter; 074 075 /** 076 * Creates a new export helper.<p> 077 * 078 * @param exportPath the export path 079 * @param exportAsFiles indicates if the resources should be exported as individual files or in one big ZIP file 080 * @param validateXml indicates of the manifest.xml should be validated 081 * 082 * @throws SAXException in case of issues creating the manifest.xml 083 * @throws IOException in case of file access issues 084 */ 085 public CmsExportHelper(String exportPath, boolean exportAsFiles, boolean validateXml) 086 throws SAXException, IOException { 087 088 m_exportPath = exportPath; 089 m_isExportAsFiles = exportAsFiles; 090 091 removeOldExport(exportPath); 092 093 Writer writer; 094 if (m_isExportAsFiles) { 095 m_exportPath = m_exportPath + "/"; 096 // write to file system directly 097 String fileName = getRfsFileName(CmsImportExportManager.EXPORT_MANIFEST); 098 File rfsFile = new File(fileName); 099 rfsFile.getParentFile().mkdirs(); 100 rfsFile.createNewFile(); 101 writer = new FileWriter(rfsFile); 102 } else { 103 // make sure parent folders exist 104 File rfsFile = new File(m_exportPath); 105 rfsFile.getParentFile().mkdirs(); 106 // create the export ZIP stream 107 m_exportZipStream = new ZipOutputStream(new FileOutputStream(m_exportPath)); 108 // delegate writing to a String writer 109 writer = new StringWriter(SUB_LENGTH); 110 } 111 112 // generate the SAX XML writer 113 CmsXmlSaxWriter saxHandler = new CmsXmlSaxWriter(writer, OpenCms.getSystemInfo().getDefaultEncoding()); 114 saxHandler.setEscapeXml(true); 115 saxHandler.setEscapeUnknownChars(true); 116 // start the document 117 saxHandler.startDocument(); 118 119 // set the doctype if needed 120 if (validateXml) { 121 saxHandler.startDTD( 122 CmsImportExportManager.N_EXPORT, 123 null, 124 CmsConfigurationManager.DEFAULT_DTD_PREFIX + CmsImportVersion7.DTD_FILENAME); 125 saxHandler.endDTD(); 126 } 127 // initialize the dom4j writer object 128 m_saxWriter = new SAXWriter(saxHandler, saxHandler); 129 } 130 131 /** 132 * Ensures the zip stream is closed (if there is one). 133 */ 134 public void ensureZipStreamClosed() { 135 136 if (m_exportZipStream != null) { 137 try { 138 m_exportZipStream.close(); 139 } catch (Exception e) { 140 LOG.info(e.getLocalizedMessage(), e); 141 } 142 } 143 } 144 145 /** 146 * Returns the SAX writer for the Manifest file.<p> 147 * 148 * @return the SAX writer for the Manifest file 149 */ 150 public SAXWriter getSaxWriter() { 151 152 return m_saxWriter; 153 } 154 155 /** 156 * Writes a single OpenCms VFS file to the export.<p> 157 * 158 * @param file the OpenCms VFS file to write 159 * @param name the name of the file in the export 160 * 161 * @throws IOException in case of file access issues 162 */ 163 public void writeFile(CmsFile file, String name) throws IOException { 164 165 if (m_isExportAsFiles) { 166 writeFile2Rfs(file, name); 167 } else { 168 writeFile2Zip(file, name); 169 } 170 } 171 172 /** 173 * Writes the OpenCms manifest.xml file to the export.<p> 174 * 175 * @param xmlSaxWriter the SAX writer to use 176 * 177 * @throws SAXException in case of issues creating the manifest.xml 178 * @throws IOException in case of file access issues 179 */ 180 public void writeManifest(CmsXmlSaxWriter xmlSaxWriter) throws IOException, SAXException { 181 182 if (m_isExportAsFiles) { 183 writeManifest2Rfs(xmlSaxWriter); 184 } else { 185 writeManifest2Zip(xmlSaxWriter); 186 } 187 } 188 189 /** 190 * Returns the RFS file name for the given OpenCms VFS file name.<p> 191 * 192 * @param name the OpenCms VFS file name 193 * 194 * @return the RFS file name for the given OpenCms VFS file name 195 */ 196 protected String getRfsFileName(String name) { 197 198 return m_exportPath + name; 199 } 200 201 /** 202 * Removes the old export output, which may be an existing file or directory.<p> 203 * 204 * @param exportPath the export output path 205 */ 206 protected void removeOldExport(String exportPath) { 207 208 File output = new File(exportPath); 209 if (output.exists()) { 210 // the output already exists 211 if (output.isDirectory()) { 212 // purge the complete directory 213 CmsFileUtil.purgeDirectory(output); 214 } else { 215 // remove the existing file 216 if (m_isExportAsFiles) { 217 // in case we write to a file we can just overwrite, 218 // but for a folder we must remove an existing file 219 output.delete(); 220 } 221 } 222 } 223 } 224 225 /** 226 * Writes a single OpenCms VFS file to the RFS export.<p> 227 * 228 * @param file the OpenCms VFS file to write 229 * @param name the name of the file in the export 230 * 231 * @throws IOException in case of file access issues 232 */ 233 protected void writeFile2Rfs(CmsFile file, String name) throws IOException { 234 235 String fileName = getRfsFileName(name); 236 File rfsFile = new File(fileName); 237 if (!rfsFile.getParentFile().exists()) { 238 rfsFile.getParentFile().mkdirs(); 239 } 240 rfsFile.createNewFile(); 241 FileOutputStream rfsFileOut = new FileOutputStream(rfsFile); 242 rfsFileOut.write(file.getContents()); 243 rfsFileOut.close(); 244 } 245 246 /** 247 * Writes a single OpenCms VFS file to the ZIP export.<p> 248 * 249 * @param file the OpenCms VFS file to write 250 * @param name the name of the file in the export 251 * 252 * @throws IOException in case of file access issues 253 */ 254 protected void writeFile2Zip(CmsFile file, String name) throws IOException { 255 256 ZipEntry entry = new ZipEntry(name); 257 // save the time of the last modification in the zip 258 entry.setTime(file.getDateLastModified()); 259 m_exportZipStream.putNextEntry(entry); 260 m_exportZipStream.write(file.getContents()); 261 m_exportZipStream.closeEntry(); 262 } 263 264 /** 265 * Writes the OpenCms manifest.xml file to the RFS export.<p> 266 * 267 * In case of the RFS export the file is directly written to a file output stream, 268 * so calling this method just closes the XML and finishes the stream.<p> 269 * 270 * @param xmlSaxWriter the SAX writer to use 271 * 272 * @throws SAXException in case of issues creating the manifest.xml 273 * @throws IOException in case of issues closing the file writer 274 */ 275 protected void writeManifest2Rfs(CmsXmlSaxWriter xmlSaxWriter) throws SAXException, IOException { 276 277 // close the document - this will also trigger flushing the contents to the file system 278 xmlSaxWriter.endDocument(); 279 xmlSaxWriter.getWriter().close(); 280 } 281 282 /** 283 * Writes the OpenCms manifest.xml file to the ZIP export.<p> 284 * 285 * In case of the ZIP export the manifest is written to an internal StringBuffer 286 * first, which is then stored in the ZIP file when this method is called.<p> 287 * 288 * @param xmlSaxWriter the SAX writer to use 289 * 290 * @throws SAXException in case of issues creating the manifest.xml 291 * @throws IOException in case of file access issues 292 */ 293 protected void writeManifest2Zip(CmsXmlSaxWriter xmlSaxWriter) throws IOException, SAXException { 294 295 // close the document 296 xmlSaxWriter.endDocument(); 297 xmlSaxWriter.getWriter().close(); 298 299 // create ZIP entry for the manifest XML document 300 ZipEntry entry = new ZipEntry(CmsImportExportManager.EXPORT_MANIFEST); 301 m_exportZipStream.putNextEntry(entry); 302 303 // complex substring operation is required to ensure handling for very large export manifest files 304 StringBuffer result = ((StringWriter)xmlSaxWriter.getWriter()).getBuffer(); 305 int steps = result.length() / SUB_LENGTH; 306 int rest = result.length() % SUB_LENGTH; 307 int pos = 0; 308 for (int i = 0; i < steps; i++) { 309 String sub = result.substring(pos, pos + SUB_LENGTH); 310 m_exportZipStream.write(sub.getBytes(OpenCms.getSystemInfo().getDefaultEncoding())); 311 pos += SUB_LENGTH; 312 } 313 if (rest > 0) { 314 String sub = result.substring(pos, pos + rest); 315 m_exportZipStream.write(sub.getBytes(OpenCms.getSystemInfo().getDefaultEncoding())); 316 } 317 318 // close the zip entry for the manifest XML document 319 m_exportZipStream.closeEntry(); 320 321 // finally close the zip stream 322 m_exportZipStream.close(); 323 } 324}