/* * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> * */ package org.nuxeo.ecm.platform.io.impl; 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.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.CoreInstance; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentLocation; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentRef; import org.nuxeo.ecm.core.api.DocumentTreeIterator; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.ecm.core.io.DocumentReader; import org.nuxeo.ecm.core.io.DocumentReaderFactory; import org.nuxeo.ecm.core.io.DocumentTranslationMap; import org.nuxeo.ecm.core.io.DocumentWriter; import org.nuxeo.ecm.core.io.DocumentWriterFactory; import org.nuxeo.ecm.core.io.DocumentsExporter; import org.nuxeo.ecm.core.io.DocumentsImporter; import org.nuxeo.ecm.core.io.IODocumentManager; import org.nuxeo.ecm.core.io.impl.DocumentTranslationMapImpl; import org.nuxeo.ecm.core.io.impl.IODocumentManagerImpl; import org.nuxeo.ecm.platform.io.api.IOManager; import org.nuxeo.ecm.platform.io.api.IOResourceAdapter; import org.nuxeo.ecm.platform.io.api.IOResources; import org.nuxeo.runtime.api.Framework; /** * IOManager implementation * * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> */ public class IOManagerImpl implements IOManager { private static final long serialVersionUID = 5789086884484295921L; private static final Log log = LogFactory.getLog(IOManagerImpl.class); protected final Map<String, IOResourceAdapter> adaptersRegistry; public IOManagerImpl() { adaptersRegistry = new HashMap<>(); } @Override public IOResourceAdapter getAdapter(String name) { return adaptersRegistry.get(name); } @Override public void addAdapter(String name, IOResourceAdapter adapter) { if (DOCUMENTS_ADAPTER_NAME.equals(name)) { log.error("Cannot register adapter with name " + DOCUMENTS_ADAPTER_NAME); return; } adaptersRegistry.put(name, adapter); } @Override public void removeAdapter(String name) { adaptersRegistry.remove(name); } public void exportDocumentsAndResources(OutputStream out, String repo, final String format, Collection<String> ioAdapters, final DocumentReader customDocReader) throws IOException { DocumentsExporter docsExporter = new DocumentsExporter() { @Override public DocumentTranslationMap exportDocs(OutputStream out) throws IOException { IODocumentManager docManager = new IODocumentManagerImpl(); DocumentTranslationMap map = docManager.exportDocuments(out, customDocReader, format); return map; } }; exportDocumentsAndResources(out, repo, docsExporter, ioAdapters); } @Override public void exportDocumentsAndResources(OutputStream out, final String repo, final Collection<DocumentRef> sources, final boolean recurse, final String format, final Collection<String> ioAdapters) throws IOException { DocumentsExporter docsExporter = new DocumentsExporter() { @Override public DocumentTranslationMap exportDocs(OutputStream out) throws IOException { IODocumentManager docManager = new IODocumentManagerImpl(); DocumentTranslationMap map = docManager.exportDocuments(out, repo, sources, recurse, format); return map; } }; exportDocumentsAndResources(out, repo, docsExporter, ioAdapters); } void exportDocumentsAndResources(OutputStream out, String repo, DocumentsExporter docsExporter, Collection<String> ioAdapters) throws IOException { List<String> doneAdapters = new ArrayList<>(); ZipOutputStream zip = new ZipOutputStream(out); zip.setMethod(ZipOutputStream.DEFLATED); zip.setLevel(9); ByteArrayOutputStream docsZip = new ByteArrayOutputStream(); DocumentTranslationMap map = docsExporter.exportDocs(docsZip); ZipEntry docsEntry = new ZipEntry(DOCUMENTS_ADAPTER_NAME + ".zip"); zip.putNextEntry(docsEntry); zip.write(docsZip.toByteArray()); zip.closeEntry(); docsZip.close(); doneAdapters.add(DOCUMENTS_ADAPTER_NAME); Collection<DocumentRef> allSources = map.getDocRefMap().keySet(); if (ioAdapters != null && !ioAdapters.isEmpty()) { for (String adapterName : ioAdapters) { String filename = adapterName + ".xml"; IOResourceAdapter adapter = getAdapter(adapterName); if (adapter == null) { log.warn("Adapter " + adapterName + " not found"); continue; } if (doneAdapters.contains(adapterName)) { log.warn("Export for adapter " + adapterName + " already done"); continue; } IOResources resources = adapter.extractResources(repo, allSources); resources = adapter.translateResources(repo, resources, map); ByteArrayOutputStream adapterOut = new ByteArrayOutputStream(); adapter.getResourcesAsXML(adapterOut, resources); ZipEntry adapterEntry = new ZipEntry(filename); zip.putNextEntry(adapterEntry); zip.write(adapterOut.toByteArray()); zip.closeEntry(); doneAdapters.add(adapterName); adapterOut.close(); } } try { zip.close(); } catch (ZipException e) { // empty zip file, do nothing } } @Override public void importDocumentsAndResources(InputStream in, final String repo, final DocumentRef root) throws IOException { DocumentsImporter docsImporter = new DocumentsImporter() { @Override public DocumentTranslationMap importDocs(InputStream sourceStream) throws IOException { IODocumentManager docManager = new IODocumentManagerImpl(); return docManager.importDocuments(sourceStream, repo, root); } }; importDocumentsAndResources(docsImporter, in, repo); } public void importDocumentsAndResources(InputStream in, final String repo, final DocumentRef root, final DocumentWriter customDocWriter) throws IOException { DocumentsImporter docsImporter = new DocumentsImporter() { @Override public DocumentTranslationMap importDocs(InputStream sourceStream) throws IOException { IODocumentManager docManager = new IODocumentManagerImpl(); return docManager.importDocuments(sourceStream, customDocWriter); } }; importDocumentsAndResources(docsImporter, in, repo); } void importDocumentsAndResources(DocumentsImporter docsImporter, InputStream in, String repo) throws IOException { ZipInputStream zip = new ZipInputStream(in); // first entry will be documents ZipEntry zentry = zip.getNextEntry(); String docZipFilename = DOCUMENTS_ADAPTER_NAME + ".zip"; if (zentry == null || !docZipFilename.equals(zentry.getName())) { zip.close(); throw new NuxeoException("Invalid archive"); } // fill in a new stream File temp = Framework.createTempFile("nuxeo-import-adapters-", ".zip"); try (FileOutputStream outDocs = new FileOutputStream(temp)) { IOUtils.copy(zip, outDocs); } zip.closeEntry(); InputStream tempIn = new FileInputStream(temp.getPath()); DocumentTranslationMap map = docsImporter.importDocs(tempIn); tempIn.close(); temp.delete(); while ((zentry = zip.getNextEntry()) != null) { String entryName = zentry.getName(); if (entryName.endsWith(".xml")) { String ioAdapterName = entryName.substring(0, entryName.length() - 4); IOResourceAdapter adapter = getAdapter(ioAdapterName); if (adapter == null) { log.warn("Adapter " + ioAdapterName + " not available. Unable to import associated resources."); continue; } IOResources resources = adapter.loadResourcesFromXML(zip); IOResources newResources = adapter.translateResources(repo, resources, map); log.info("store resources with adapter " + ioAdapterName); adapter.storeResources(newResources); } else { log.warn("skip entry: " + entryName); } try { // we might have an undesired stream close in the client zip.closeEntry(); } catch (IOException e) { log.error("Please check code handling entry " + entryName, e); } } zip.close(); } @Override public Collection<DocumentRef> copyDocumentsAndResources(String repo, Collection<DocumentRef> sources, DocumentLocation targetLocation, Collection<String> ioAdapters) { if (sources == null || sources.isEmpty()) { return null; } String newRepo = targetLocation.getServerName(); if (!repo.equals(newRepo)) { // TODO: maybe import and export (?), assume copy is recursive. throw new NuxeoException("Cannot copy to different server"); } List<DocumentRef> roots = new ArrayList<>(); try (CoreSession session = CoreInstance.openCoreSession(repo)) { for (DocumentRef source : sources) { DocumentTranslationMap map = new DocumentTranslationMapImpl(repo, repo); DocumentModel sourceDoc = session.getDocument(source); DocumentModel destDoc = session.copy(source, targetLocation.getDocRef(), null); roots.add(destDoc.getRef()); // iterate on each tree to build translation map DocumentTreeIterator sourceIt = new DocumentTreeIterator(session, sourceDoc); DocumentTreeIterator destIt = new DocumentTreeIterator(session, destDoc); while (sourceIt.hasNext()) { DocumentModel sourceItem = sourceIt.next(); DocumentRef sourceRef = sourceItem.getRef(); if (!destIt.hasNext()) { map.put(sourceRef, null); } else { DocumentModel destItem = destIt.next(); DocumentRef destRef = destItem.getRef(); map.put(sourceRef, destRef); } } Collection<DocumentRef> allSources = map.getDocRefMap().keySet(); if (ioAdapters != null && !ioAdapters.isEmpty()) { for (String adapterName : ioAdapters) { IOResourceAdapter adapter = getAdapter(adapterName); if (adapter == null) { log.warn("Adapter " + adapterName + " not found"); continue; } IOResources resources = adapter.extractResources(repo, allSources); IOResources newResources = adapter.translateResources(repo, resources, map); adapter.storeResources(newResources); } } session.save(); } } return roots; } private static DocumentWriter createDocWriter(String docWriterFactoryName, Map<String, Object> factoryParams) { // create a custom writer using factory instance Object factoryObj; try { Class<?> clazz = Class.forName(docWriterFactoryName); factoryObj = clazz.newInstance(); } catch (ReflectiveOperationException e) { throw new NuxeoException("cannot instantiate factory " + docWriterFactoryName, e); } DocumentWriter customDocWriter; if (factoryObj instanceof DocumentWriterFactory) { customDocWriter = ((DocumentWriterFactory) factoryObj).createDocWriter(factoryParams); } else { throw new NuxeoException("bad class type: " + factoryObj); } if (customDocWriter == null) { throw new NuxeoException("null DocumentWriter created by " + docWriterFactoryName); } return customDocWriter; } private static DocumentReader createDocReader(String docReaderFactoryName, Map<String, Object> factoryParams) { // create a custom reader using factory instance Object factoryObj; try { Class<?> clazz = Class.forName(docReaderFactoryName); factoryObj = clazz.newInstance(); } catch (ReflectiveOperationException e) { throw new NuxeoException("cannot instantiate factory " + docReaderFactoryName, e); } DocumentReader customDocReader; if (factoryObj instanceof DocumentReaderFactory) { customDocReader = ((DocumentReaderFactory) factoryObj).createDocReader(factoryParams); } else { throw new NuxeoException("bad class type: " + factoryObj); } if (customDocReader == null) { throw new NuxeoException("null DocumentReader created by " + docReaderFactoryName); } return customDocReader; } @Override public void importFromStream(InputStream in, DocumentLocation targetLocation, String docReaderFactoryClassName, Map<String, Object> rFactoryParams, String docWriterFactoryClassName, Map<String, Object> wFactoryParams) { DocumentWriter customDocWriter = createDocWriter(docWriterFactoryClassName, wFactoryParams); DocumentReader customDocReader = null; try { if (rFactoryParams == null) { rFactoryParams = new HashMap<>(); } rFactoryParams.put("source_stream", in); customDocReader = createDocReader(docReaderFactoryClassName, rFactoryParams); IODocumentManager docManager = new IODocumentManagerImpl(); DocumentTranslationMap map = docManager.importDocuments(customDocReader, customDocWriter); } finally { if (customDocReader != null) { customDocReader.close(); } customDocWriter.close(); if (in != null) { try { in.close(); } catch (IOException e) { log.error(e); } } } } }