001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 016 * 017 * $Id: IOManagerImpl.java 27208 2007-11-14 19:59:25Z dmihalache $ 018 */ 019 020package org.nuxeo.ecm.platform.io.impl; 021 022import java.io.ByteArrayOutputStream; 023import java.io.File; 024import java.io.FileInputStream; 025import java.io.FileOutputStream; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.OutputStream; 029import java.util.ArrayList; 030import java.util.Collection; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034import java.util.zip.ZipEntry; 035import java.util.zip.ZipException; 036import java.util.zip.ZipInputStream; 037import java.util.zip.ZipOutputStream; 038 039import org.apache.commons.logging.Log; 040import org.apache.commons.logging.LogFactory; 041import org.nuxeo.common.utils.FileUtils; 042import org.nuxeo.ecm.core.api.CoreInstance; 043import org.nuxeo.ecm.core.api.CoreSession; 044import org.nuxeo.ecm.core.api.DocumentLocation; 045import org.nuxeo.ecm.core.api.DocumentModel; 046import org.nuxeo.ecm.core.api.DocumentRef; 047import org.nuxeo.ecm.core.api.DocumentTreeIterator; 048import org.nuxeo.ecm.core.api.NuxeoException; 049import org.nuxeo.ecm.core.io.DocumentReader; 050import org.nuxeo.ecm.core.io.DocumentReaderFactory; 051import org.nuxeo.ecm.core.io.DocumentTranslationMap; 052import org.nuxeo.ecm.core.io.DocumentWriter; 053import org.nuxeo.ecm.core.io.DocumentWriterFactory; 054import org.nuxeo.ecm.core.io.DocumentsExporter; 055import org.nuxeo.ecm.core.io.DocumentsImporter; 056import org.nuxeo.ecm.core.io.IODocumentManager; 057import org.nuxeo.ecm.core.io.impl.DocumentTranslationMapImpl; 058import org.nuxeo.ecm.core.io.impl.IODocumentManagerImpl; 059import org.nuxeo.ecm.platform.io.api.IOManager; 060import org.nuxeo.ecm.platform.io.api.IOResourceAdapter; 061import org.nuxeo.ecm.platform.io.api.IOResources; 062 063/** 064 * IOManager implementation 065 * 066 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 067 */ 068public class IOManagerImpl implements IOManager { 069 070 private static final long serialVersionUID = 5789086884484295921L; 071 072 private static final Log log = LogFactory.getLog(IOManagerImpl.class); 073 074 protected final Map<String, IOResourceAdapter> adaptersRegistry; 075 076 public IOManagerImpl() { 077 adaptersRegistry = new HashMap<String, IOResourceAdapter>(); 078 } 079 080 @Override 081 public IOResourceAdapter getAdapter(String name) { 082 return adaptersRegistry.get(name); 083 } 084 085 @Override 086 public void addAdapter(String name, IOResourceAdapter adapter) { 087 if (DOCUMENTS_ADAPTER_NAME.equals(name)) { 088 log.error("Cannot register adapter with name " + DOCUMENTS_ADAPTER_NAME); 089 return; 090 } 091 adaptersRegistry.put(name, adapter); 092 } 093 094 @Override 095 public void removeAdapter(String name) { 096 adaptersRegistry.remove(name); 097 } 098 099 public void exportDocumentsAndResources(OutputStream out, String repo, final String format, 100 Collection<String> ioAdapters, final DocumentReader customDocReader) throws IOException { 101 102 DocumentsExporter docsExporter = new DocumentsExporter() { 103 @Override 104 public DocumentTranslationMap exportDocs(OutputStream out) throws IOException { 105 IODocumentManager docManager = new IODocumentManagerImpl(); 106 DocumentTranslationMap map = docManager.exportDocuments(out, customDocReader, format); 107 return map; 108 } 109 }; 110 111 exportDocumentsAndResources(out, repo, docsExporter, ioAdapters); 112 } 113 114 @Override 115 public void exportDocumentsAndResources(OutputStream out, final String repo, final Collection<DocumentRef> sources, 116 final boolean recurse, final String format, final Collection<String> ioAdapters) throws IOException { 117 118 DocumentsExporter docsExporter = new DocumentsExporter() { 119 @Override 120 public DocumentTranslationMap exportDocs(OutputStream out) throws IOException { 121 IODocumentManager docManager = new IODocumentManagerImpl(); 122 DocumentTranslationMap map = docManager.exportDocuments(out, repo, sources, recurse, format); 123 return map; 124 } 125 }; 126 127 exportDocumentsAndResources(out, repo, docsExporter, ioAdapters); 128 } 129 130 void exportDocumentsAndResources(OutputStream out, String repo, DocumentsExporter docsExporter, 131 Collection<String> ioAdapters) throws IOException { 132 133 List<String> doneAdapters = new ArrayList<String>(); 134 135 ZipOutputStream zip = new ZipOutputStream(out); 136 zip.setMethod(ZipOutputStream.DEFLATED); 137 zip.setLevel(9); 138 139 ByteArrayOutputStream docsZip = new ByteArrayOutputStream(); 140 DocumentTranslationMap map = docsExporter.exportDocs(docsZip); 141 142 ZipEntry docsEntry = new ZipEntry(DOCUMENTS_ADAPTER_NAME + ".zip"); 143 zip.putNextEntry(docsEntry); 144 zip.write(docsZip.toByteArray()); 145 zip.closeEntry(); 146 docsZip.close(); 147 doneAdapters.add(DOCUMENTS_ADAPTER_NAME); 148 149 Collection<DocumentRef> allSources = map.getDocRefMap().keySet(); 150 151 if (ioAdapters != null && !ioAdapters.isEmpty()) { 152 for (String adapterName : ioAdapters) { 153 String filename = adapterName + ".xml"; 154 IOResourceAdapter adapter = getAdapter(adapterName); 155 if (adapter == null) { 156 log.warn("Adapter " + adapterName + " not found"); 157 continue; 158 } 159 if (doneAdapters.contains(adapterName)) { 160 log.warn("Export for adapter " + adapterName + " already done"); 161 continue; 162 } 163 IOResources resources = adapter.extractResources(repo, allSources); 164 resources = adapter.translateResources(repo, resources, map); 165 ByteArrayOutputStream adapterOut = new ByteArrayOutputStream(); 166 adapter.getResourcesAsXML(adapterOut, resources); 167 ZipEntry adapterEntry = new ZipEntry(filename); 168 zip.putNextEntry(adapterEntry); 169 zip.write(adapterOut.toByteArray()); 170 zip.closeEntry(); 171 doneAdapters.add(adapterName); 172 adapterOut.close(); 173 } 174 } 175 try { 176 zip.close(); 177 } catch (ZipException e) { 178 // empty zip file, do nothing 179 } 180 } 181 182 @Override 183 public void importDocumentsAndResources(InputStream in, final String repo, final DocumentRef root) 184 throws IOException { 185 186 DocumentsImporter docsImporter = new DocumentsImporter() { 187 188 @Override 189 public DocumentTranslationMap importDocs(InputStream sourceStream) throws IOException { 190 IODocumentManager docManager = new IODocumentManagerImpl(); 191 return docManager.importDocuments(sourceStream, repo, root); 192 } 193 194 }; 195 196 importDocumentsAndResources(docsImporter, in, repo); 197 } 198 199 public void importDocumentsAndResources(InputStream in, final String repo, final DocumentRef root, 200 final DocumentWriter customDocWriter) throws IOException { 201 202 DocumentsImporter docsImporter = new DocumentsImporter() { 203 204 @Override 205 public DocumentTranslationMap importDocs(InputStream sourceStream) throws IOException { 206 IODocumentManager docManager = new IODocumentManagerImpl(); 207 return docManager.importDocuments(sourceStream, customDocWriter); 208 } 209 210 }; 211 212 importDocumentsAndResources(docsImporter, in, repo); 213 } 214 215 void importDocumentsAndResources(DocumentsImporter docsImporter, InputStream in, String repo) throws IOException { 216 217 ZipInputStream zip = new ZipInputStream(in); 218 219 // first entry will be documents 220 ZipEntry zentry = zip.getNextEntry(); 221 String docZipFilename = DOCUMENTS_ADAPTER_NAME + ".zip"; 222 if (zentry == null || !docZipFilename.equals(zentry.getName())) { 223 zip.close(); 224 throw new NuxeoException("Invalid archive"); 225 } 226 227 // fill in a new stream 228 File temp = File.createTempFile("nuxeo-import-adapters-", ".zip"); 229 FileOutputStream outDocs = new FileOutputStream(temp); 230 try { 231 FileUtils.copy(zip, outDocs); 232 } finally { 233 outDocs.close(); 234 } 235 zip.closeEntry(); 236 237 InputStream tempIn = new FileInputStream(temp.getPath()); 238 DocumentTranslationMap map = docsImporter.importDocs(tempIn); 239 tempIn.close(); 240 temp.delete(); 241 242 while ((zentry = zip.getNextEntry()) != null) { 243 String entryName = zentry.getName(); 244 if (entryName.endsWith(".xml")) { 245 String ioAdapterName = entryName.substring(0, entryName.length() - 4); 246 IOResourceAdapter adapter = getAdapter(ioAdapterName); 247 if (adapter == null) { 248 log.warn("Adapter " + ioAdapterName + " not available. Unable to import associated resources."); 249 continue; 250 } 251 IOResources resources = adapter.loadResourcesFromXML(zip); 252 IOResources newResources = adapter.translateResources(repo, resources, map); 253 log.info("store resources with adapter " + ioAdapterName); 254 adapter.storeResources(newResources); 255 } else { 256 log.warn("skip entry: " + entryName); 257 } 258 try { 259 // we might have an undesired stream close in the client 260 zip.closeEntry(); 261 } catch (IOException e) { 262 log.error("Please check code handling entry " + entryName, e); 263 } 264 } 265 zip.close(); 266 } 267 268 @Override 269 public Collection<DocumentRef> copyDocumentsAndResources(String repo, Collection<DocumentRef> sources, 270 DocumentLocation targetLocation, Collection<String> ioAdapters) { 271 if (sources == null || sources.isEmpty()) { 272 return null; 273 } 274 275 String newRepo = targetLocation.getServerName(); 276 if (!repo.equals(newRepo)) { 277 // TODO: maybe import and export (?), assume copy is recursive. 278 throw new NuxeoException("Cannot copy to different server"); 279 } 280 281 List<DocumentRef> roots = new ArrayList<DocumentRef>(); 282 try (CoreSession session = CoreInstance.openCoreSession(repo)) { 283 for (DocumentRef source : sources) { 284 DocumentTranslationMap map = new DocumentTranslationMapImpl(repo, repo); 285 DocumentModel sourceDoc = session.getDocument(source); 286 DocumentModel destDoc = session.copy(source, targetLocation.getDocRef(), null); 287 roots.add(destDoc.getRef()); 288 // iterate on each tree to build translation map 289 DocumentTreeIterator sourceIt = new DocumentTreeIterator(session, sourceDoc); 290 DocumentTreeIterator destIt = new DocumentTreeIterator(session, destDoc); 291 while (sourceIt.hasNext()) { 292 DocumentModel sourceItem = sourceIt.next(); 293 DocumentRef sourceRef = sourceItem.getRef(); 294 if (!destIt.hasNext()) { 295 map.put(sourceRef, null); 296 } else { 297 DocumentModel destItem = destIt.next(); 298 DocumentRef destRef = destItem.getRef(); 299 map.put(sourceRef, destRef); 300 } 301 } 302 Collection<DocumentRef> allSources = map.getDocRefMap().keySet(); 303 if (ioAdapters != null && !ioAdapters.isEmpty()) { 304 for (String adapterName : ioAdapters) { 305 IOResourceAdapter adapter = getAdapter(adapterName); 306 if (adapter == null) { 307 log.warn("Adapter " + adapterName + " not found"); 308 continue; 309 } 310 IOResources resources = adapter.extractResources(repo, allSources); 311 IOResources newResources = adapter.translateResources(repo, resources, map); 312 adapter.storeResources(newResources); 313 } 314 } 315 session.save(); 316 } 317 } 318 return roots; 319 } 320 321 private static DocumentWriter createDocWriter(String docWriterFactoryName, Map<String, Object> factoryParams) 322 { 323 // create a custom writer using factory instance 324 325 Object factoryObj; 326 try { 327 Class<?> clazz = Class.forName(docWriterFactoryName); 328 factoryObj = clazz.newInstance(); 329 } catch (ReflectiveOperationException e) { 330 throw new NuxeoException("cannot instantiate factory " + docWriterFactoryName, e); 331 } 332 333 DocumentWriter customDocWriter; 334 if (factoryObj instanceof DocumentWriterFactory) { 335 customDocWriter = ((DocumentWriterFactory) factoryObj).createDocWriter(factoryParams); 336 } else { 337 throw new NuxeoException("bad class type: " + factoryObj); 338 } 339 340 if (customDocWriter == null) { 341 throw new NuxeoException("null DocumentWriter created by " + docWriterFactoryName); 342 } 343 344 return customDocWriter; 345 } 346 347 private static DocumentReader createDocReader(String docReaderFactoryName, Map<String, Object> factoryParams) 348 { 349 // create a custom reader using factory instance 350 351 Object factoryObj; 352 try { 353 Class<?> clazz = Class.forName(docReaderFactoryName); 354 factoryObj = clazz.newInstance(); 355 } catch (ReflectiveOperationException e) { 356 throw new NuxeoException("cannot instantiate factory " + docReaderFactoryName, e); 357 } 358 359 DocumentReader customDocReader; 360 if (factoryObj instanceof DocumentReaderFactory) { 361 customDocReader = ((DocumentReaderFactory) factoryObj).createDocReader(factoryParams); 362 } else { 363 throw new NuxeoException("bad class type: " + factoryObj); 364 } 365 366 if (customDocReader == null) { 367 throw new NuxeoException("null DocumentReader created by " + docReaderFactoryName); 368 } 369 370 return customDocReader; 371 } 372 373 @Override 374 public void importFromStream(InputStream in, DocumentLocation targetLocation, String docReaderFactoryClassName, 375 Map<String, Object> rFactoryParams, String docWriterFactoryClassName, Map<String, Object> wFactoryParams) 376 { 377 378 DocumentWriter customDocWriter = createDocWriter(docWriterFactoryClassName, wFactoryParams); 379 DocumentReader customDocReader = null; 380 381 try { 382 if (rFactoryParams == null) { 383 rFactoryParams = new HashMap<String, Object>(); 384 } 385 rFactoryParams.put("source_stream", in); 386 customDocReader = createDocReader(docReaderFactoryClassName, rFactoryParams); 387 388 IODocumentManager docManager = new IODocumentManagerImpl(); 389 DocumentTranslationMap map = docManager.importDocuments(customDocReader, customDocWriter); 390 } finally { 391 if (customDocReader != null) { 392 customDocReader.close(); 393 } 394 customDocWriter.close(); 395 396 if (in != null) { 397 try { 398 in.close(); 399 } catch (IOException e) { 400 log.error(e); 401 } 402 } 403 } 404 405 } 406 407}