/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * bstefanescu * * $Id: NuxeoArchiveReader.java 29029 2008-01-14 18:38:14Z ldoguin $ */ package org.eclipse.ecr.core.io.impl.plugins; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.io.SAXReader; import org.eclipse.ecr.core.api.Blob; import org.eclipse.ecr.core.api.impl.blob.StreamingBlob; import org.eclipse.ecr.core.io.ExportConstants; import org.eclipse.ecr.core.io.ExportedDocument; import org.eclipse.ecr.core.io.impl.AbstractDocumentReader; import org.eclipse.ecr.core.io.impl.DWord; import org.eclipse.ecr.core.io.impl.ExportedDocumentImpl; import org.eclipse.ecr.runtime.services.streaming.FileSource; import org.eclipse.ecr.runtime.services.streaming.ZipEntrySource; import org.nuxeo.common.utils.FileUtils; import org.nuxeo.common.utils.Path; /** * Reads nuxeo archives generated using {@link NuxeoArchiveWriter}. * * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class NuxeoArchiveReader extends AbstractDocumentReader { private ZipInputStream in; private String file; private final Collection<File> filesToDelete = new ArrayList<File>(); public NuxeoArchiveReader(URL url) throws IOException { this(url.openStream()); if (url.getProtocol().equals("file")) { file = FileUtils.getFileFromURL(url).getAbsolutePath(); } } public NuxeoArchiveReader(File file) throws IOException { this(new FileInputStream(file)); this.file = file.getAbsolutePath(); } public NuxeoArchiveReader(InputStream in) throws IOException { this(new ZipInputStream(in)); } public NuxeoArchiveReader(ZipInputStream in) throws IOException { this(in, true); } /** * Package-visible constructor used by {@link ZipReader}. * * @param in * @param checkMarker * @throws IOException */ NuxeoArchiveReader(ZipInputStream in, boolean checkMarker) throws IOException { this.in = in; if (checkMarker) { checkMarker(); } } @Override public ExportedDocument read() throws IOException { ZipEntry entry = in.getNextEntry(); if (entry == null) { return null; } if (!entry.isDirectory()) { if (entry.getName().equals(ExportConstants.DOCUMENT_FILE)) { // the repository ROOT! TODO: how to handle root? it doesn't // have a dir .. ExportedDocument xdoc = new ExportedDocumentImpl(); xdoc.setPath(new Path("/")); xdoc.setDocument(loadXML(entry)); return xdoc; } else { throw new IOException("Invalid Nuxeo archive"); } } int count = getFilesCount(entry); if (count == 0) { return read(); // empty dir -> try next directory } String name = entry.getName(); ExportedDocument xdoc = new ExportedDocumentImpl(); xdoc.setPath(new Path(name).removeTrailingSeparator()); for (int i = 0; i < count; i++) { entry = in.getNextEntry(); name = entry.getName(); if (name.endsWith(ExportConstants.DOCUMENT_FILE)) { xdoc.setDocument(loadXML(entry)); } else if (name.endsWith(".xml")) { // external doc file xdoc.putDocument(FileUtils.getFileNameNoExt(entry.getName()), loadXML(entry)); } else { // should be a blob xdoc.putBlob(FileUtils.getFileName(entry.getName()), createBlob(entry)); } } return xdoc; } @Override public void close() { if (in != null) { try { in.close(); } catch (IOException e) { // do nothing } finally { in = null; } } for (File file : filesToDelete) { file.delete(); } } private static int getFilesCount(ZipEntry entry) throws IOException { byte[] bytes = entry.getExtra(); if (bytes == null) { return 0; } else if (bytes.length != 4) { throw new IOException("Invalid Nuxeo Archive"); } else { return new DWord(bytes).getInt(); } } private Document loadXML(ZipEntry entry) throws IOException { try { // the SAXReader is closing the stream so that we need to copy the // content somewhere ByteArrayOutputStream baos = new ByteArrayOutputStream(); FileUtils.copy(in, baos); return new SAXReader().read(new ByteArrayInputStream( baos.toByteArray())); } catch (DocumentException e) { IOException ioe = new IOException("Failed to read zip entry " + entry.getName() + ": " + e.getMessage()); ioe.setStackTrace(e.getStackTrace()); throw ioe; } } private Blob createBlob(ZipEntry entry) throws IOException { if (file != null) { // the zip is a file : optimize blob loading -> do // not decompress blobs ZipEntrySource src = new ZipEntrySource(file, entry.getName()); return new StreamingBlob(src); } else { // should decompress since this is a generic stream File file = File.createTempFile("nuxeo-import", "blob"); filesToDelete.add(file); OutputStream out = new FileOutputStream(file); try { FileUtils.copy(in, out); } finally { out.close(); } FileSource src = new FileSource(file); return new StreamingBlob(src); } } private void checkMarker() throws IOException { ZipEntry entry = in.getNextEntry(); if (entry == null) { throw new IOException( "Not a valid Nuxeo Archive - no marker file found (unexpected end of zip)"); } if (!isMarkerEntry(entry)) { throw new IOException( "Not a valid Nuxeo Archive - no marker file found"); } } public static boolean isMarkerEntry(ZipEntry entry) { return entry.getName().equals(ExportConstants.MARKER_FILE); } }