001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     bstefanescu
011 *
012 * $Id: NuxeoArchiveWriter.java 29029 2008-01-14 18:38:14Z ldoguin $
013 */
014
015package org.nuxeo.ecm.core.io.impl.plugins;
016
017import java.io.BufferedOutputStream;
018import java.io.File;
019import java.io.FileOutputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.OutputStream;
023import java.util.Map;
024import java.util.zip.Deflater;
025import java.util.zip.ZipEntry;
026import java.util.zip.ZipOutputStream;
027
028import org.apache.commons.io.IOUtils;
029import org.dom4j.Document;
030import org.dom4j.io.OutputFormat;
031import org.dom4j.io.XMLWriter;
032import org.nuxeo.ecm.core.api.Blob;
033import org.nuxeo.ecm.core.api.DocumentLocation;
034import org.nuxeo.ecm.core.api.DocumentRef;
035import org.nuxeo.ecm.core.io.DocumentTranslationMap;
036import org.nuxeo.ecm.core.io.ExportConstants;
037import org.nuxeo.ecm.core.io.ExportedDocument;
038import org.nuxeo.ecm.core.io.impl.AbstractDocumentWriter;
039import org.nuxeo.ecm.core.io.impl.DWord;
040import org.nuxeo.ecm.core.io.impl.DocumentTranslationMapImpl;
041
042/**
043 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
044 */
045public class NuxeoArchiveWriter extends AbstractDocumentWriter {
046
047    protected ZipOutputStream out;
048
049    public NuxeoArchiveWriter(File destination) throws IOException {
050        this(new BufferedOutputStream(new FileOutputStream(destination)), Deflater.DEFAULT_COMPRESSION);
051    }
052
053    public NuxeoArchiveWriter(File destination, int compressionLevel) throws IOException {
054        this(new BufferedOutputStream(new FileOutputStream(destination)), compressionLevel);
055    }
056
057    public NuxeoArchiveWriter(OutputStream out) throws IOException {
058        this(new ZipOutputStream(out), Deflater.DEFAULT_COMPRESSION);
059    }
060
061    public NuxeoArchiveWriter(OutputStream out, int compressionLevel) throws IOException {
062        this(new ZipOutputStream(out), compressionLevel);
063    }
064
065    public NuxeoArchiveWriter(ZipOutputStream out) throws IOException {
066        this(out, Deflater.DEFAULT_COMPRESSION);
067    }
068
069    public NuxeoArchiveWriter(ZipOutputStream out, int compressionLevel) throws IOException {
070        this.out = out;
071        this.out.setLevel(compressionLevel);
072        setComment("");
073        // write the marker entry
074        writeMarker();
075    }
076
077    public void setComment(String comment) {
078        if (out != null) {
079            out.setComment(ExportConstants.ZIP_HEADER + "\r\n" + comment);
080        }
081    }
082
083    @Override
084    public DocumentTranslationMap write(ExportedDocument doc) throws IOException {
085        String path = doc.getPath().toString();
086        writeDocument(path, doc);
087        // keep location unchanged
088        DocumentLocation oldLoc = doc.getSourceLocation();
089        String oldServerName = oldLoc.getServerName();
090        DocumentRef oldDocRef = oldLoc.getDocRef();
091        DocumentTranslationMap map = new DocumentTranslationMapImpl(oldServerName, oldServerName);
092        map.put(oldDocRef, oldDocRef);
093        return map;
094    }
095
096    @Override
097    public void close() {
098        if (out != null) {
099            try {
100                out.close();
101            } catch (IOException e) {
102                // do nothing
103            } finally {
104                out = null;
105            }
106        }
107    }
108
109    protected void writeDocument(String path, ExportedDocument doc) throws IOException {
110
111        if (path.equals("/") || path.length() == 0) {
112            path = "";
113        } else { // avoid adding a root entry
114            path += '/';
115            ZipEntry entry = new ZipEntry(path);
116            // store the number of child as an extra info on the entry
117            entry.setExtra(new DWord(doc.getFilesCount()).getBytes());
118            out.putNextEntry(entry);
119            out.closeEntry();
120            // System.out.println(">> add entry: "+entry.getName());
121        }
122
123        // write metadata
124        ZipEntry entry = new ZipEntry(path + ExportConstants.DOCUMENT_FILE);
125        out.putNextEntry(entry);
126        try {
127            writeXML(doc.getDocument(), out);
128        } finally {
129            out.closeEntry();
130            // System.out.println(">> add entry: "+entry.getName());
131        }
132
133        // write external documents
134        for (Map.Entry<String, Document> ext : doc.getDocuments().entrySet()) {
135            String fileName = ext.getKey() + ".xml";
136            entry = new ZipEntry(path + fileName);
137            out.putNextEntry(entry);
138            try {
139                writeXML(ext.getValue(), out);
140            } finally {
141                out.closeEntry();
142            }
143        }
144
145        // write blobs
146        Map<String, Blob> blobs = doc.getBlobs();
147        for (Map.Entry<String, Blob> blobEntry : blobs.entrySet()) {
148            String fileName = blobEntry.getKey();
149            entry = new ZipEntry(path + fileName);
150            out.putNextEntry(entry);
151            try (InputStream in = blobEntry.getValue().getStream()) {
152                IOUtils.copy(in, out);
153            }
154            // DO NOT CALL out.close(), we want to keep writing to it
155            out.closeEntry();
156        }
157    }
158
159    protected static void writeXML(Document doc, OutputStream out) throws IOException {
160        OutputFormat format = AbstractDocumentWriter.createPrettyPrint();
161        XMLWriter writer = new XMLWriter(out, format);
162        writer.write(doc);
163    }
164
165    protected void writeMarker() throws IOException {
166        ZipEntry entry = new ZipEntry(ExportConstants.MARKER_FILE);
167        out.putNextEntry(entry);
168        out.closeEntry();
169    }
170}