/* Copyright (c) 2012 GFT Appverse, S.L., Sociedad Unipersonal. This Source Code Form is subject to the terms of the Appverse Public License Version 2.0 (“APL v2.0”). If a copy of the APL was not distributed with this file, You can obtain one at http://www.appverse.mobi/licenses/apl_v2.0.pdf. [^] Redistribution and use in source and binary forms, with or without modification, are permitted provided that the conditions of the AppVerse Public License v2.0 are met. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. EXCEPT IN CASE OF WILLFUL MISCONDUCT OR GROSS NEGLIGENCE, IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.appverse.web.framework.backend.ecm.cmis.services.integration.impl.live; import org.apache.chemistry.opencmis.client.api.*; import org.apache.chemistry.opencmis.client.util.FileUtils; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.chemistry.opencmis.commons.enums.UnfileObject; import org.apache.chemistry.opencmis.commons.enums.VersioningState; import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; import org.apache.commons.io.IOUtils; import org.appverse.web.framework.backend.api.helpers.log.AutowiredLogger; import org.appverse.web.framework.backend.ecm.cmis.services.integration.ICMISSimpleNonVersionedDocumentService; import org.appverse.web.framework.backend.ecm.core.model.integration.AbstractDocumentIntegrationBean; import org.slf4j.Logger; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; /** * Base implementation service that provides simple operations for non versioned documents. * Basically uses the repository with the features that a file system would provide. */ public class CMISSimpleNonVersionedDocumentService<T extends AbstractDocumentIntegrationBean> extends CMISService<T> implements ICMISSimpleNonVersionedDocumentService<T> { // TODO: Try Leaving cache enabled but when an object is removed or moved make a session.removeObject to improve performance @AutowiredLogger private static Logger logger; @Override public void insert(String path, T document) throws Exception{ insert(path, document, getCmisSession()); } @Override public void insert(String path, T document, Session session) throws Exception { Document test = createDocument(path, document, session); FileUtils.printProperties(test); } @Override public T retrieve(final String path, final String documentName) throws Exception { return retrieve(path, documentName, getCmisSession()); } @Override public T retrieve(String path, String documentName, Session session) throws Exception { // Disable cache session.getDefaultContext().setCacheEnabled(false); Document object = (Document)FileUtils.getObject(path + documentName, session); ContentStream contentStream = object.getContentStream(); Class<T> classP = getClassP(); T document = classP.newInstance(); document.setContentStream(IOUtils.toByteArray(contentStream.getStream())); document.setContentStreamFilename(documentName); document.setContentStreamLenght(contentStream.getLength()); document.setContentStreamMimeType(contentStream.getMimeType()); // Enable cache session.getDefaultContext().setCacheEnabled(true); return document; } @Override public void move(String pathOrigin, String documentName, String pathDestination) throws Exception { move(pathOrigin, documentName, pathDestination, documentName, getCmisSession()); } @Override public void move(String pathOrigin, String documentName, String pathDestination, Session session) throws Exception { move(pathOrigin, documentName, pathDestination, documentName, session); } @Override public void move(String pathOrigin, String documentNameOrigin, String pathDestination, String documentNameDestination) throws Exception { move(pathOrigin, documentNameOrigin, pathDestination, documentNameDestination, getCmisSession()); } @Override public void move(String pathOrigin, String documentNameOrigin, String pathDestination, String documentNameDestination, Session session) throws Exception { // Disable cache session.getDefaultContext().setCacheEnabled(false); // Fetch the object FileableCmisObject fileableCmisObject = (FileableCmisObject) FileUtils.getObject(pathOrigin + documentNameOrigin, session); // ECM folder paths does not end with "/" if (pathOrigin.endsWith("/")) pathOrigin = pathOrigin.substring(0, pathOrigin.length()-1); if (pathDestination.endsWith("/")) pathDestination = pathDestination.substring(0, pathDestination.length()-1); // Fetch source folder CmisObject sourceObject = FileUtils.getObject(pathOrigin, session); // Fetch the destination folder // We need to make sure the target folder exists createFolder(pathDestination); CmisObject targetObject = FileUtils.getObject(pathDestination, session); if (documentNameDestination != documentNameOrigin){ fileableCmisObject.rename(documentNameDestination, true); } // Move the object fileableCmisObject.move(sourceObject, targetObject); // Enable cache session.getDefaultContext().setCacheEnabled(true); } @Override public void delete(String path, String documentName) throws Exception { delete(path, documentName, getCmisSession()); } @Override public void delete(String path, String documentName, Session session) throws Exception { // Disable cache session.getDefaultContext().setCacheEnabled(false); Document document = (Document)FileUtils.getObject(path + "/" + documentName, session); document.delete(); // Enable cache session.getDefaultContext().setCacheEnabled(true); } @Override public void deleteFolder(String path) throws Exception { deleteFolder(path, getCmisSession()); } @Override public void deleteFolder(String path, Session session) throws Exception { // Disable cache session.getDefaultContext().setCacheEnabled(false); if (path.endsWith("/")) path = path.substring(0, path.length()-1); Folder folder = (Folder)FileUtils.getObject(path, session); folder.deleteTree(true, UnfileObject.DELETE, true); // Enable cache session.getDefaultContext().setCacheEnabled(true); } private Folder createFolder(String path) throws Exception { String[] subFolderNames = path.split("/"); Folder folder = getCmisSession().getRootFolder(); for (String subfolder : subFolderNames){ String folderPath = folder.getPath(); if (folder.getPath().equals("/")){ folderPath = ""; } try{ folder = FileUtils.getFolder(folderPath + "/" + subfolder, getCmisSession()); logger.debug("Parent folder already exists, new folder value is: " + folder.getPath()); } catch (CmisObjectNotFoundException e){ // The folder does not existe, we create it here folder = FileUtils.createFolder(folder, subfolder, null); logger.debug("New folder created, new folder value is: " + folder.getPath()); } } return folder; } private Document createDocument(String path, T document, Session session) throws Exception { // Disable cache session.getDefaultContext().setCacheEnabled(false); Folder parent = createFolder(path); // CMIS properties // (minimal set: name and object type id) Map<String, Object> properties = new HashMap<String, Object>(); properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); properties.put(PropertyIds.NAME, document.getContentStreamFilename()); // content String mimeType = document.getContentStreamMimeType(); if (mimeType == null){ mimeType = document.getMimeTypeFromContentStreamFileName(); } InputStream stream = new ByteArrayInputStream(document.getContentStream()); ContentStream contentStream = new ContentStreamImpl(document.getContentStreamFilename(), BigInteger.valueOf(document.getContentStream().length), mimeType, stream); // If the document existed already we override it. In order to do so, we have to remove it and we create it again Document documentToRemove = null; try{ documentToRemove = (Document)FileUtils.getObject(path + document.getContentStreamFilename(), session); documentToRemove.delete(); } catch (CmisObjectNotFoundException e){ // The document does not exist } // Determine the VersioningState VersioningState versioningState = VersioningState.NONE; String typeId = (String)properties.get(PropertyIds.OBJECT_TYPE_ID); DocumentType docType = (DocumentType) session.getTypeDefinition(typeId); if (Boolean.TRUE.equals(docType.isVersionable())) { versioningState = VersioningState.MAJOR; } // Depending on the underlaying repository a "cmis:document" can be versionable or not. If it is versionable, it is mandatory to create a version. // For instance, in OpenCMIS in-memory repository a "cmis:document" is not versionable (accepts documents without versioning). // Alfresco repository "cmis:document" is versionable (requires a version) Document doc = parent.createDocument(properties, contentStream, versioningState); // Enable cache again session.getDefaultContext().setCacheEnabled(true); return doc; } private Class<T> getClassP() { Class<T> classP = null; final Type type = this.getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { final ParameterizedType pType = (ParameterizedType) type; if (pType.getActualTypeArguments()[0] instanceof Class) { classP = (Class<T>) pType.getActualTypeArguments()[0]; } } return classP; } }