org.exoplatform.wcm.connector.FileUploadHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.exoplatform.wcm.connector.FileUploadHandler.java

Source

/*
 * Copyright (C) 2003-2009 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.wcm.connector;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;

import org.apache.commons.lang.StringUtils;
import org.exoplatform.common.http.HTTPStatus;
import org.exoplatform.ecm.connector.fckeditor.FCKMessage;
import org.exoplatform.ecm.connector.fckeditor.FCKUtils;
import org.exoplatform.ecm.utils.text.Text;
import org.exoplatform.ecm.webui.utils.LockUtil;
import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService;
import org.exoplatform.services.cms.mimetype.DMSMimeTypeResolver;
import org.exoplatform.services.cms.templates.TemplateService;
import org.exoplatform.services.listener.ListenerService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.wcm.core.NodetypeConstant;
import org.exoplatform.services.wcm.publication.WCMPublicationService;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.exoplatform.upload.UploadResource;
import org.exoplatform.upload.UploadService;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Created by The eXo Platform SAS
 * Author : Tran Nguyen Ngoc
 * ngoc.tran@exoplatform.com
 * Sep 4, 2009
 */
public class FileUploadHandler {

    /** Logger */
    private static final Log LOG = ExoLogger.getLogger(FileUploadHandler.class.getName());

    /** The Constant UPLOAD_ACTION. */
    public final static String UPLOAD_ACTION = "upload";

    /** The Constant PROGRESS_ACTION. */
    public final static String PROGRESS_ACTION = "progress";

    /** The Constant ABORT_ACTION. */
    public final static String ABORT_ACTION = "abort";

    /** The Constant DELETE_ACTION. */
    public final static String DELETE_ACTION = "delete";

    /** The Constant SAVE_ACTION. */
    public final static String SAVE_ACTION = "save";

    /** The Constant CHECK_EXIST. */
    public final static String CHECK_EXIST = "exist";

    /** The Constant REPLACE. */
    public final static String REPLACE = "replace";

    /** The Constant KEEP_BOTH. */
    public final static String KEEP_BOTH = "keepBoth";

    /** The Constant LAST_MODIFIED_PROPERTY. */
    private static final String LAST_MODIFIED_PROPERTY = "Last-Modified";

    /** The Constant IF_MODIFIED_SINCE_DATE_FORMAT. */
    private static final String IF_MODIFIED_SINCE_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";

    public final static String POST_CREATE_CONTENT_EVENT = "CmsService.event.postCreate";

    /** The upload service. */
    private UploadService uploadService;

    /** The listener service. */
    ListenerService listenerService;

    private ActivityCommonService activityService;

    /** The fck message. */
    private FCKMessage fckMessage;

    /** The uploadIds - time Map */
    private Map<String, Long> uploadIdTimeMap;

    /** The maximal life time for an upload */
    private long UPLOAD_LIFE_TIME;

    /**
     * Instantiates a new file upload handler.
     */
    public FileUploadHandler() {
        uploadService = WCMCoreUtils.getService(UploadService.class);
        listenerService = WCMCoreUtils.getService(ListenerService.class);
        activityService = WCMCoreUtils.getService(ActivityCommonService.class);
        fckMessage = new FCKMessage();
        uploadIdTimeMap = new Hashtable<String, Long>();
        UPLOAD_LIFE_TIME = System.getProperty("MULTI_UPLOAD_LIFE_TIME") == null ? 600 : //10 minutes
                Long.parseLong(System.getProperty("MULTI_UPLOAD_LIFE_TIME"));
    }

    /**
     * Upload
     * @param servletRequest The request to upload file
     * @param uploadId Upload Id
     * @param limit Limit size of upload file
     * @return
     * @throws Exception
     */
    public Response upload(HttpServletRequest servletRequest, String uploadId, Integer limit) throws Exception {
        uploadService.addUploadLimit(uploadId, limit);
        uploadService.createUploadResource(servletRequest);
        uploadIdTimeMap.put(uploadId, System.currentTimeMillis());
        CacheControl cacheControl = new CacheControl();
        cacheControl.setNoCache(true);
        cacheControl.setNoStore(true);
        DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
        return Response.ok(null, MediaType.TEXT_XML).cacheControl(cacheControl)
                .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
    }

    /**
     * Check status of uploaded file.
     * If any problem while uploading, error message is returned.
     * Returning null means no problem happen.
     * 
     * @param uploadId upload ID
     * @param language language for getting message
     * @return Response message is returned if any problem while uploading.
     * @throws Exception
     */
    public Response checkStatus(String uploadId, String language) throws Exception {
        CacheControl cacheControl = new CacheControl();
        cacheControl.setNoCache(true);
        cacheControl.setNoStore(true);
        DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);

        if ((StringUtils.isEmpty(uploadId)) || (uploadService.getUploadResource(uploadId) == null))
            return null;

        // If file size exceed limit, return message
        if (UploadResource.FAILED_STATUS == uploadService.getUploadResource(uploadId).getStatus()) {

            // Remove upload Id
            uploadService.removeUploadResource(uploadId);
            uploadIdTimeMap.remove(uploadId);
            // Get message warning upload exceed limit
            String uploadLimit = String.valueOf(uploadService.getUploadLimits().get(uploadId).getLimit());
            Document fileExceedLimit = fckMessage.createMessage(FCKMessage.FILE_EXCEED_LIMIT, FCKMessage.ERROR,
                    language, new String[] { uploadLimit });

            return Response.ok(new DOMSource(fileExceedLimit), MediaType.TEXT_XML).cacheControl(cacheControl)
                    .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
        }

        return null;
    }

    /**
     * Control.
     *
     * @param uploadId the upload id
     * @param action the action
     *
     * @return the response
     *
     * @throws Exception the exception
     */
    public Response control(String uploadId, String action) throws Exception {
        CacheControl cacheControl = new CacheControl();
        cacheControl.setNoCache(true);
        cacheControl.setNoStore(true);
        DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);

        if (FileUploadHandler.PROGRESS_ACTION.equals(action)) {
            Document currentProgress = getProgress(uploadId);
            return Response.ok(new DOMSource(currentProgress), MediaType.TEXT_XML).cacheControl(cacheControl)
                    .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
        } else if (FileUploadHandler.ABORT_ACTION.equals(action)) {
            uploadService.removeUploadResource(uploadId);
            uploadIdTimeMap.remove(uploadId);
            return Response.ok(null, MediaType.TEXT_XML).cacheControl(cacheControl)
                    .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
        } else if (FileUploadHandler.DELETE_ACTION.equals(action)) {
            uploadService.removeUploadResource(uploadId);
            uploadIdTimeMap.remove(uploadId);
            return Response.ok(null, MediaType.TEXT_XML).cacheControl(cacheControl)
                    .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
        }
        return Response.status(HTTPStatus.BAD_REQUEST).cacheControl(cacheControl)
                .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
    }

    /**
     * checks if file already existed in parent folder
     *
     * @param parent the parent
     * @param fileName the file name
     * @return the response
     *
     * @throws Exception the exception
     */
    public Response checkExistence(Node parent, String fileName) throws Exception {
        CacheControl cacheControl = new CacheControl();
        cacheControl.setNoCache(true);
        DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);

        //create ret
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document fileExistence = builder.newDocument();
        Element rootElement = fileExistence.createElement(parent.hasNode(fileName) ? "Existed" : "NotExisted");
        fileExistence.appendChild(rootElement);
        //return ret;
        return Response.ok(new DOMSource(fileExistence), MediaType.TEXT_XML).cacheControl(cacheControl)
                .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
    }

    /**
     * Save as nt file.
     *
     * @param parent the parent
     * @param uploadId the upload id
     * @param fileName the file name
     * @param language the language
     *
     * @return the response
     *
     * @throws Exception the exception
     */
    public Response saveAsNTFile(Node parent, String uploadId, String fileName, String language, String siteName,
            String userId) throws Exception {
        return saveAsNTFile(parent, uploadId, fileName, language, siteName, userId, KEEP_BOTH);
    }

    /**
     * Save as nt file.
     *
     * @param parent the parent
     * @param uploadId the upload id
     * @param fileName the file name
     * @param language the language
     *
     * @return the response
     *
     * @throws Exception the exception
     */
    public Response saveAsNTFile(Node parent, String uploadId, String fileName, String language, String siteName,
            String userId, String existenceAction) throws Exception {
        try {
            CacheControl cacheControl = new CacheControl();
            cacheControl.setNoCache(true);
            UploadResource resource = uploadService.getUploadResource(uploadId);
            DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
            if (parent == null) {
                Document fileNotUploaded = fckMessage.createMessage(FCKMessage.FILE_NOT_UPLOADED, FCKMessage.ERROR,
                        language, null);
                return Response.ok(new DOMSource(fileNotUploaded), MediaType.TEXT_XML).cacheControl(cacheControl)
                        .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
            }
            if (!FCKUtils.hasAddNodePermission(parent)) {
                Object[] args = { parent.getPath() };
                Document message = fckMessage.createMessage(FCKMessage.FILE_UPLOAD_RESTRICTION, FCKMessage.ERROR,
                        language, args);
                return Response.ok(new DOMSource(message), MediaType.TEXT_XML).cacheControl(cacheControl)
                        .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
            }
            if ((fileName == null) || (fileName.length() == 0)) {
                fileName = resource.getFileName();
            }
            //add lock token
            if (parent.isLocked()) {
                parent.getSession().addLockToken(LockUtil.getLockToken(parent));
            }
            if (parent.hasNode(fileName)) {
                //      Object args[] = { fileName, parent.getPath() };
                //      Document fileExisted = fckMessage.createMessage(FCKMessage.FILE_EXISTED,
                //                                                      FCKMessage.ERROR,
                //                                                      language,
                //                                                      args);
                //      return Response.ok(new DOMSource(fileExisted), MediaType.TEXT_XML)
                //                     .cacheControl(cacheControl)
                //                     .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date()))
                //                     .build();
                if (REPLACE.equals(existenceAction)) {
                    //Broadcast the event when user move node to Trash
                    ListenerService listenerService = WCMCoreUtils.getService(ListenerService.class);
                    listenerService.broadcast(ActivityCommonService.FILE_REMOVE_ACTIVITY, parent,
                            parent.getNode(fileName));
                    parent.getNode(fileName).remove();
                    parent.save();
                }
            }
            String location = resource.getStoreLocation();
            //save node with name=fileName
            Node file = null;
            boolean fileCreated = false;
            fileName = Text.escapeIllegalJcrChars(fileName);
            String nodeName = fileName;
            int count = 0;
            do {
                try {
                    file = parent.addNode(nodeName, FCKUtils.NT_FILE);
                    fileCreated = true;
                } catch (ItemExistsException e) {//sameNameSibling is not allowed
                    nodeName = increaseName(fileName, ++count);
                }
            } while (!fileCreated);
            //--------------------------------------------------------
            if (!file.isNodeType(NodetypeConstant.MIX_REFERENCEABLE)) {
                file.addMixin(NodetypeConstant.MIX_REFERENCEABLE);
            }

            if (!file.isNodeType(NodetypeConstant.MIX_COMMENTABLE))
                file.addMixin(NodetypeConstant.MIX_COMMENTABLE);

            if (!file.isNodeType(NodetypeConstant.MIX_VOTABLE))
                file.addMixin(NodetypeConstant.MIX_VOTABLE);

            if (!file.isNodeType(NodetypeConstant.MIX_I18N))
                file.addMixin(NodetypeConstant.MIX_I18N);

            if (!file.hasProperty(NodetypeConstant.EXO_TITLE)) {
                file.setProperty(NodetypeConstant.EXO_TITLE, file.getName());
            }
            Node jcrContent = file.addNode("jcr:content", "nt:resource");
            //MimeTypeResolver mimeTypeResolver = new MimeTypeResolver();
            DMSMimeTypeResolver mimeTypeResolver = DMSMimeTypeResolver.getInstance();
            String mimetype = mimeTypeResolver.getMimeType(resource.getFileName());
            jcrContent.setProperty("jcr:data", new BufferedInputStream(new FileInputStream(new File(location))));
            jcrContent.setProperty("jcr:lastModified", new GregorianCalendar());
            jcrContent.setProperty("jcr:mimeType", mimetype);

            parent.getSession().refresh(true); // Make refreshing data
            uploadService.removeUploadResource(uploadId);
            uploadIdTimeMap.remove(uploadId);
            WCMPublicationService wcmPublicationService = WCMCoreUtils.getService(WCMPublicationService.class);
            wcmPublicationService.updateLifecyleOnChangeContent(file, siteName, userId);

            if (activityService.isBroadcastNTFileEvents(file)) {
                listenerService.broadcast(ActivityCommonService.FILE_CREATED_ACTIVITY, null, file);
            }
            file.getSession().save();
            return Response.ok(createDOMResponse("Result", mimetype), MediaType.TEXT_XML).cacheControl(cacheControl)
                    .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
        } catch (Exception exc) {
            LOG.error(exc.getMessage(), exc);
            return Response.serverError().entity(exc.getMessage()).build();
        }
    }

    public boolean isDocumentNodeType(Node node) throws Exception {
        TemplateService templateService = WCMCoreUtils.getService(TemplateService.class);
        return templateService.isManagedNodeType(node.getPrimaryNodeType().getName());
    }

    /**
     * increase the file name (not extension).
     * @param origin the original name
     * @param count the number add to file name
     * @return the new increased file name 
     */
    private String increaseName(String origin, int count) {
        int index = origin.indexOf('.');
        if (index == -1)
            return origin + count;
        return origin.substring(0, index) + count + origin.substring(index);
    }

    /**
     * get number of files uploading 
     * @return number of files uploading
     */
    public long getUploadingFileCount() {
        removeDeadUploads();
        return uploadIdTimeMap.size();
    }

    /**
     * removes dead uploads
     */
    private void removeDeadUploads() {
        Set<String> removedIds = new HashSet<String>();
        for (String id : uploadIdTimeMap.keySet()) {
            if ((System.currentTimeMillis() - uploadIdTimeMap.get(id)) > UPLOAD_LIFE_TIME * 1000) {
                removedIds.add(id);
            }
        }
        for (String id : removedIds) {
            uploadIdTimeMap.remove(id);
        }
    }

    /**
     * Gets the progress.
     *
     * @param uploadId the upload id
     *
     * @return the progress
     *
     * @throws Exception the exception
     */
    private Document getProgress(String uploadId) throws Exception {
        UploadResource resource = uploadService.getUploadResource(uploadId);
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.newDocument();
        Double percent = 0.0;
        if (resource != null) {
            if (resource.getStatus() == UploadResource.UPLOADING_STATUS) {
                percent = (resource.getUploadedSize() * 100) / resource.getEstimatedSize();
            } else {
                percent = 100.0;
            }
        }
        Element rootElement = doc.createElement("UploadProgress");
        rootElement.setAttribute("uploadId", uploadId);
        rootElement.setAttribute("fileName", resource == null ? "" : resource.getFileName());
        rootElement.setAttribute("percent", percent.intValue() + "");
        rootElement.setAttribute("uploadedSize", resource == null ? "0" : resource.getUploadedSize() + "");
        rootElement.setAttribute("totalSize", resource == null ? "0" : resource.getEstimatedSize() + "");
        rootElement.setAttribute("fileType", resource == null ? "null" : resource.getMimeType() + "");
        doc.appendChild(rootElement);
        return doc;
    }

    /**
     * returns a DOMSource object containing given message
     * @param message the message
     * @return DOMSource object
     * @throws Exception
     */
    private DOMSource createDOMResponse(String name, String mimeType) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.newDocument();
        Element rootElement = doc.createElement(name);
        rootElement.setAttribute("mimetype", mimeType);
        doc.appendChild(rootElement);
        return new DOMSource(doc);
    }

}