/* * Copyright Siemens AG, 2013-2015. Part of the SW360 Portal Project. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.sw360.portal.common; import com.liferay.portal.kernel.portlet.PortletResponseUtil; import com.liferay.portal.kernel.upload.UploadPortletRequest; import com.liferay.portal.util.PortalUtil; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.Duration; import org.eclipse.sw360.datahandler.couchdb.AttachmentStreamConnector; import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.ThriftClients; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentService; import org.eclipse.sw360.datahandler.thrift.attachments.DatabaseAddress; import org.eclipse.sw360.datahandler.thrift.users.User; import org.apache.log4j.Logger; import org.apache.thrift.TException; import org.ektorp.DocumentNotFoundException; import javax.portlet.PortletRequest; import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static com.google.common.base.Strings.isNullOrEmpty; import static org.eclipse.sw360.datahandler.common.CommonUtils.closeQuietly; import static java.net.URLConnection.guessContentTypeFromName; import static java.net.URLConnection.guessContentTypeFromStream; /** * Portlet helpers * * @author cedric.bodet@tngtech.com * @author Johannes.Najjar@tngtech.com * @author daniele.fognini@tngtech.com * @author birgit.heydenreich@tngtech.com */ public class AttachmentPortletUtils { private static final Logger log = Logger.getLogger(AttachmentPortletUtils.class); private static final String DEFAULT_ATTACHMENT_BUNDLE_NAME = "AttachmentBundle.zip"; private final ThriftClients thriftClients; private AttachmentService.Iface client; private AttachmentStreamConnector connector; // TODO add Config class and DI private final Duration downloadTimeout = Duration.durationOf(30, TimeUnit.SECONDS); public AttachmentPortletUtils() { this(new ThriftClients()); } public AttachmentPortletUtils(ThriftClients thriftClients) { this.thriftClients = thriftClients; client = thriftClients.makeAttachmentClient(); } private synchronized void makeConnector() throws TException { if (connector == null) { try { connector = new AttachmentStreamConnector(downloadTimeout); } catch (MalformedURLException e) { log.error("Invalid database address received...", e); throw new TException(e); } } } private AttachmentStreamConnector getConnector() throws TException { if (connector == null) makeConnector(); return connector; } protected InputStream getStreamToServeAFile(List<AttachmentContent> attachments) throws TException, IOException { if(attachments == null || attachments.size() == 0){ throw new SW360Exception("Tried to download empty set of Attachments"); }else if(attachments.size() == 1){ return getConnector().getAttachmentStream(attachments.get(0)); } else { return getConnector().getAttachmentBundleStream(attachments.stream().collect(Collectors.toSet())); } } public void serveFile(ResourceRequest request, ResourceResponse response) { serveFile(request, response, Optional.empty()); } public void serveFile(ResourceRequest request, ResourceResponse response, Optional<String> downloadFileName) { String[] ids = request.getParameterValues(PortalConstants.ATTACHMENT_ID); if(ids != null && ids.length >= 1){ serveAttachmentBundle(new HashSet<>(Arrays.asList(ids)), request, response, downloadFileName); }else{ log.warn("no attachmentId was found in the request passed to serveFile"); response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "500"); } } public void serveAttachmentBundle(Collection<String> ids, ResourceRequest request, ResourceResponse response){ serveAttachmentBundle(ids, request, response, Optional.empty()); } public void serveAttachmentBundle(Collection<String> ids, ResourceRequest request, ResourceResponse response, Optional<String> downloadFileName){ List<AttachmentContent> attachments = new ArrayList<>(); try { for(String id : ids){ attachments.add(client.getAttachmentContent(id)); } serveAttachmentBundle(attachments, request, response, downloadFileName); } catch (TException e) { log.error("Problem getting the AttachmentContents from the backend", e); response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "500"); } } public void serveAttachmentBundle(List<AttachmentContent> attachments, ResourceRequest request, ResourceResponse response){ serveAttachmentBundle(attachments, request, response, Optional.empty()); } public void serveAttachmentBundle(List<AttachmentContent> attachments, ResourceRequest request, ResourceResponse response, Optional<String> downloadFileName){ String filename; String contentType; if(attachments.size() == 1){ filename = downloadFileName .orElse(attachments.get(0).getFilename()); contentType = attachments.get(0).getContentType(); } else { filename = downloadFileName .orElse(DEFAULT_ATTACHMENT_BUNDLE_NAME); contentType = "application/zip"; } try (InputStream attachmentStream = getStreamToServeAFile(attachments)) { PortletResponseUtil.sendFile(request, response, filename, attachmentStream, contentType); } catch (TException e) { log.error("Problem getting the attachment content from the backend", e); response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "500"); } catch (IOException e) { log.error("cannot finish writing response", e); response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "500"); } } public boolean uploadAttachmentPartFromRequest(PortletRequest request, String fileUploadName) throws IOException, TException { final UploadPortletRequest uploadPortletRequest = PortalUtil.getUploadPortletRequest(request); final InputStream stream = uploadPortletRequest.getFileAsStream(fileUploadName); final ResumableUpload resumableUpload = ResumableUpload.from(uploadPortletRequest); AttachmentContent attachment = null; if (resumableUpload.isValid()) { final AttachmentStreamConnector attachmentStreamConnector = getConnector(); attachment = getAttachmentContent(resumableUpload, stream); if (attachment != null) { try { attachmentStreamConnector.uploadAttachmentPart(attachment, resumableUpload.getChunkNumber(), stream); } catch (TException e) { log.error("Error saving attachment part", e); return false; } } } return attachment != null; } private AttachmentContent getAttachmentContent(ResumableUpload resumableUpload, InputStream stream) throws IOException, TException { if (!resumableUpload.isValid()) { return null; } final AttachmentContent attachment = getAttachmentContent(resumableUpload); if (resumableUpload.getChunkNumber() == 1) { String fileName = resumableUpload.getFilename(); String contentType = resumableUpload.getFileType(); if (isNullOrEmpty(contentType)) { contentType = guessContentTypeFromStream(stream); } if (isNullOrEmpty(contentType)) { contentType = guessContentTypeFromName(fileName); } if (isNullOrEmpty(contentType)) { contentType = "text"; } int partsCount = resumableUpload.getTotalChunks(); attachment.setContentType(contentType) .setFilename(fileName) .setOnlyRemote(false) .setPartsCount(Integer.toString(partsCount)); return updateAttachmentContent(attachment); } else { return attachment; } } private AttachmentContent getAttachmentContent(ResumableUpload resumableUpload) { AttachmentContent attachment = null; if (resumableUpload.hasAttachmentId()) { try { AttachmentService.Iface client = thriftClients.makeAttachmentClient(); attachment = client.getAttachmentContent(resumableUpload.getAttachmentId()); } catch (TException e) { log.error("Error retrieving attachment", e); } } return attachment; } private AttachmentContent updateAttachmentContent(AttachmentContent attachment) throws TException { try { AttachmentService.Iface client = thriftClients.makeAttachmentClient(); client.updateAttachmentContent(attachment); } catch (SW360Exception e) { log.error("Error updating attachment", e); return null; } return attachment; } public AttachmentContent createAttachmentContent(ResourceRequest request) throws IOException { String filename = request.getParameter("fileName"); AttachmentContent attachmentContent = new AttachmentContent() .setContentType("application/octet-stream") .setFilename(filename); try { AttachmentService.Iface client = thriftClients.makeAttachmentClient(); attachmentContent = client.makeAttachmentContent(attachmentContent); } catch (TException e) { log.error("Error creating attachment", e); attachmentContent = null; } return attachmentContent; } public boolean uploadAttachmentPart(PortletRequest request, String fileUploadName) throws IOException { try { return uploadAttachmentPartFromRequest(request, fileUploadName); } catch (TException e) { log.error("Error getting attachment and saving it", e); return false; } } public RequestStatus cancelUpload(ResourceRequest request) { String attachmentId = request.getParameter(PortalConstants.ATTACHMENT_ID); try { AttachmentService.Iface client = thriftClients.makeAttachmentClient(); return client.deleteAttachmentContent(attachmentId); } catch (TException e) { log.error("Error deleting attachment from backend", e); return RequestStatus.FAILURE; } } public boolean checkAttachmentExistsFromRequest(ResourceRequest request) { ResumableUpload resumableUpload = ResumableUpload.from(request); final AttachmentContent attachment = getAttachmentContent(resumableUpload); return alreadyHavePart(attachment, resumableUpload); } private boolean alreadyHavePart(AttachmentContent attachment, ResumableUpload resumableUpload) { if (attachment == null || !resumableUpload.isValid()) { return false; } AttachmentStreamConnector attachmentStreamConnector; try { attachmentStreamConnector = getConnector(); } catch (TException e) { log.error("no connector", e); return false; } try { attachmentStreamConnector.getAttachmentPartStream(attachment, resumableUpload.getChunkNumber()).close(); return true; } catch (SW360Exception e) { log.error("cannot check if part already exists", e); return false; } catch (IOException | DocumentNotFoundException ignored) { return false; } } public Attachment getAttachmentForDisplay(User user, String attachmentContentId) { try { String filename = client.getAttachmentContent(attachmentContentId).getFilename(); return CommonUtils.getNewAttachment(user, attachmentContentId, filename); } catch (TException e) { log.error("Could not get attachment content", e); } return null; } public void deleteAttachments(Set<String> attachmentContentIds){ try { for(String id: attachmentContentIds) { client.deleteAttachmentContent(id); } } catch (TException e){ log.error("Could not delete attachments from database.",e); } } }