/**
* Copyright (C) 2012 BonitaSoft S.A.
* BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation
* version 2.1 of the License.
* This library 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 Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301, USA.
**/
package org.bonitasoft.forms.server;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.bonitasoft.console.common.server.utils.BPMEngineAPIUtil;
import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;
import org.bonitasoft.console.common.server.utils.FormsResourcesUtils;
import org.bonitasoft.console.common.server.utils.UnauthorizedFolderException;
import org.bonitasoft.engine.api.ProcessAPI;
import org.bonitasoft.engine.bpm.document.ArchivedDocument;
import org.bonitasoft.engine.bpm.document.Document;
import org.bonitasoft.engine.bpm.document.DocumentNotFoundException;
import org.bonitasoft.engine.session.APISession;
import org.bonitasoft.forms.server.api.FormAPIFactory;
import org.bonitasoft.forms.server.api.IFormWorkflowAPI;
/**
* Servlet allowing to download process instances attachments
*
* @author Anthony Birembaut
*/
public class DocumentDownloadServlet extends HttpServlet {
/**
* UID
*/
private static final long serialVersionUID = 5209516978177786895L;
/**
* process ID
*/
protected static final String PROCESS_ID_PARAM = "process";
/**
* instance ID
*/
protected static final String INSTANCE_ID_PARAM = "instance";
/**
* task ID
*/
protected static final String TASK_ID_PARAM = "task";
/**
* document id of the document to download
*/
protected static final String DOCUMENT_ID_PARAM = "document";
/**
* attachment : indicate the path of the process attachment
*/
protected static final String FILE_PATH_PARAM = "filePath";
/**
* attachment : indicate the file name of the process attachment
*/
protected static final String FILE_NAME_PARAM = "fileName";
/**
* resource : indicate the file name of the process resource
*/
protected static final String RESOURCE_FILE_NAME_PARAM = "resourceFileName";
/**
* The engine API session param key name
*/
protected static final String API_SESSION_PARAM_KEY = "apiSession";
/**
* the name of the directory in which the resources are stored in the business archive (in /resources/forms)
*/
protected static final String BUSINESS_ARCHIVE_RESOURCES_DIRECTORY = "documents";
/**
* content storage id of the document downloaded
*/
protected static final String CONTENT_STORAGE_ID_PARAM = "contentStorageId";
/**
* Util class allowing to work with the BPM engine API
*/
protected BPMEngineAPIUtil bpmEngineAPIUtil = new BPMEngineAPIUtil();
/**
* Logger
*/
private static final Logger LOGGER = Logger.getLogger(DocumentDownloadServlet.class.getName());
/**
* {@inheritDoc}
*/
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {
final String filePath = request.getParameter(FILE_PATH_PARAM);
String fileName = request.getParameter(FILE_NAME_PARAM);
final String resourcePath = request.getParameter(RESOURCE_FILE_NAME_PARAM);
final String documentId = request.getParameter(DOCUMENT_ID_PARAM);
String contentStorageId = request.getParameter(CONTENT_STORAGE_ID_PARAM);
final APISession apiSession = (APISession) request.getSession().getAttribute(API_SESSION_PARAM_KEY);
byte[] fileContent = null;
if (filePath != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "attachmentPath: " + filePath);
}
final BonitaHomeFolderAccessor tempFolderAccessor = new BonitaHomeFolderAccessor();
try {
final File file = tempFolderAccessor.getTempFile(filePath, apiSession.getTenantId());
if (fileName == null) {
fileName = file.getName();
}
fileContent = getFileContent(file, filePath);
} catch (final UnauthorizedFolderException e) {
throw new ServletException(e.getMessage());
} catch (final IOException e) {
throw new ServletException(e);
}
} else if (fileName != null && contentStorageId != null) {
try {
fileContent = bpmEngineAPIUtil.getProcessAPI(apiSession).getDocumentContent(contentStorageId);
} catch (final Exception e) {
final String errorMessage = "Error while retrieving the document with content storage ID " + contentStorageId + " from the engine.";
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, errorMessage, e);
}
throw new ServletException(errorMessage, e);
}
} else if (documentId != null) {
try {
final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession);
try {
final Document document = processAPI.getDocument(Long.valueOf(documentId));
fileName = document.getContentFileName();
contentStorageId = document.getContentStorageId();
} catch (final DocumentNotFoundException dnfe) {
final ArchivedDocument archivedDocument = processAPI.getArchivedVersionOfProcessDocument(Long.valueOf(documentId));
fileName = archivedDocument.getDocumentContentFileName();
contentStorageId = archivedDocument.getContentStorageId();
}
if (contentStorageId != null && !contentStorageId.isEmpty()) {
fileContent = processAPI.getDocumentContent(contentStorageId);
}
} catch (final Exception e) {
final String errorMessage = "Error while retrieving the document with ID " + documentId + " from the engine.";
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, errorMessage, e);
}
throw new ServletException(errorMessage, e);
}
} else if (resourcePath != null) {
final String processIDStr = request.getParameter(PROCESS_ID_PARAM);
final String instanceIDStr = request.getParameter(INSTANCE_ID_PARAM);
final String taskIdStr = request.getParameter(TASK_ID_PARAM);
final IFormWorkflowAPI workflowAPI = FormAPIFactory.getFormWorkflowAPI();
long processDefinitionID = -1;
try {
if (processIDStr != null) {
processDefinitionID = Long.parseLong(processIDStr);
} else if (taskIdStr != null) {
processDefinitionID = workflowAPI.getProcessDefinitionIDFromActivityInstanceID(apiSession, Long.parseLong(taskIdStr));
} else if (instanceIDStr != null) {
processDefinitionID = workflowAPI.getProcessDefinitionIDFromProcessInstanceID(apiSession, Long.parseLong(instanceIDStr));
} else {
final String errorMessage = "Error while retrieving the resource " + resourcePath
+ " : Either a process, instance or task is required in the URL";
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, errorMessage);
}
throw new ServletException(errorMessage);
}
Date processDeployementDate = workflowAPI.getMigrationDate(apiSession, processDefinitionID);
if (processDeployementDate == null) {
processDeployementDate = workflowAPI.getProcessDefinitionDate(apiSession, processDefinitionID);
}
final File processDir = FormsResourcesUtils.getApplicationResourceDir(apiSession, processDefinitionID, processDeployementDate);
final File resource = new File(processDir, BUSINESS_ARCHIVE_RESOURCES_DIRECTORY + File.separator + resourcePath);
if (resource.exists()) {
fileName = resource.getName();
fileContent = getFileContent(resource, filePath);
} else {
final String errorMessage = "The target resource does not exist " + resource.getAbsolutePath();
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, errorMessage);
}
throw new IOException(errorMessage);
}
} catch (final Exception e) {
final String errorMessage = "Error while retrieving the resource " + resourcePath;
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, errorMessage, e);
}
throw new ServletException(errorMessage, e);
}
} else {
final String errorMessage = "Error while getting the file. either a document, a filePath or a resourcePath parameter is required.";
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, errorMessage);
}
throw new ServletException(errorMessage);
}
response.setContentType("application/octet-stream");
response.setCharacterEncoding("UTF-8");
try {
final String encodedfileName = URLEncoder.encode(fileName, "UTF-8");
final String userAgent = request.getHeader("User-Agent");
if (userAgent != null && userAgent.contains("Firefox")) {
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedfileName.replace("+", "%20"));
} else {
response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedfileName.replaceAll("\\+", " ") + "\"; filename*=UTF-8''"
+ encodedfileName.replace("+", "%20"));
}
final OutputStream out = response.getOutputStream();
if (fileContent == null) {
response.setContentLength(0);
} else {
response.setContentLength(fileContent.length);
out.write(fileContent);
}
out.close();
} catch (final IOException e) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, "Error while generating the response.", e);
}
throw new ServletException(e);
}
}
protected byte[] getFileContent(final File file, final String filePath) throws ServletException {
int fileLength = 0;
if (file.length() > Integer.MAX_VALUE) {
throw new ServletException("file " + filePath + " too big !");
} else {
fileLength = (int) file.length();
}
byte[] content;
try {
final InputStream fileInput = new FileInputStream(file);
final byte[] fileContent = new byte[fileLength];
try {
int offset = 0;
int length = fileLength;
while (length > 0) {
final int read = fileInput.read(fileContent, offset, length);
if (read <= 0) {
break;
}
length -= read;
offset += read;
}
content = fileContent;
} catch (final FileNotFoundException e) {
final String errorMessage = "Error while getting the attachment. The file " + filePath + " does not exist.";
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, errorMessage, e);
}
throw new ServletException(errorMessage, e);
} finally {
fileInput.close();
}
} catch (final IOException e) {
final String errorMessage = "Error while reading attachment (file : " + filePath;
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, errorMessage, e);
}
throw new ServletException(errorMessage, e);
}
return content;
}
}