mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-09-17 14:21:39 +00:00
REPO-4528: [MNT-20734] 0kb file when using REST API nodes/{nodeID}/content in a clustered ACS
This commit is contained in:
@@ -23,368 +23,383 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package org.alfresco.repo.web.scripts;
|
package org.alfresco.repo.web.scripts;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
import org.apache.chemistry.opencmis.commons.server.TempStoreOutputStream;
|
|
||||||
import org.apache.chemistry.opencmis.server.shared.TempStoreOutputStreamFactory;
|
|
||||||
import org.springframework.extensions.surf.util.Content;
|
import org.springframework.extensions.surf.util.Content;
|
||||||
import org.springframework.extensions.webscripts.Description.FormatStyle;
|
import org.springframework.extensions.webscripts.Description.FormatStyle;
|
||||||
import org.springframework.extensions.webscripts.Match;
|
import org.springframework.extensions.webscripts.Match;
|
||||||
import org.springframework.extensions.webscripts.Runtime;
|
import org.springframework.extensions.webscripts.Runtime;
|
||||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||||
import org.springframework.extensions.webscripts.WrappingWebScriptRequest;
|
import org.springframework.extensions.webscripts.WrappingWebScriptRequest;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
public class BufferedRequest implements WrappingWebScriptRequest
|
public class BufferedRequest implements WrappingWebScriptRequest
|
||||||
{
|
{
|
||||||
private TempStoreOutputStreamFactory streamFactory;
|
private TempOutputStreamFactory streamFactory;
|
||||||
private WebScriptRequest req;
|
private WebScriptRequest req;
|
||||||
private File requestBody;
|
private TempOutputStream bufferStream;
|
||||||
private InputStream contentStream;
|
private InputStream contentStream;
|
||||||
private BufferedReader contentReader;
|
private BufferedReader contentReader;
|
||||||
|
|
||||||
public BufferedRequest(WebScriptRequest req, TempStoreOutputStreamFactory streamFactory)
|
public BufferedRequest(WebScriptRequest req, TempOutputStreamFactory streamFactory)
|
||||||
{
|
{
|
||||||
this.req = req;
|
this.req = req;
|
||||||
this.streamFactory = streamFactory;
|
this.streamFactory = streamFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream bufferInputStream() throws IOException
|
private TempOutputStream getBufferedBodyAsTempStream() throws IOException
|
||||||
{
|
{
|
||||||
TempStoreOutputStream bufferStream = streamFactory.newOutputStream();
|
if (bufferStream == null)
|
||||||
|
{
|
||||||
try
|
bufferStream = streamFactory.createOutputStream();
|
||||||
{
|
|
||||||
FileCopyUtils.copy(req.getContent().getInputStream(), bufferStream);
|
try
|
||||||
}
|
{
|
||||||
catch (IOException e)
|
// Copy the stream
|
||||||
{
|
FileCopyUtils.copy(req.getContent().getInputStream(), bufferStream);
|
||||||
bufferStream.destroy(e); // remove temp file
|
}
|
||||||
throw e;
|
catch (IOException e)
|
||||||
}
|
{
|
||||||
|
bufferStream.destroy();
|
||||||
return bufferStream.getInputStream();
|
throw e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public void reset()
|
|
||||||
{
|
return bufferStream;
|
||||||
if (contentStream != null)
|
}
|
||||||
{
|
|
||||||
try
|
private InputStream bufferInputStream() throws IOException
|
||||||
{
|
{
|
||||||
contentStream.close();
|
if (contentReader != null)
|
||||||
}
|
{
|
||||||
catch (Exception e)
|
throw new IllegalStateException("Reader in use");
|
||||||
{
|
}
|
||||||
}
|
if (contentStream == null)
|
||||||
contentStream = null;
|
{
|
||||||
}
|
contentStream = getBufferedBodyAsTempStream().getInputStream();
|
||||||
if (contentReader != null)
|
}
|
||||||
{
|
|
||||||
try
|
return contentStream;
|
||||||
{
|
}
|
||||||
contentReader.close();
|
|
||||||
}
|
public void reset()
|
||||||
catch (Exception e)
|
{
|
||||||
{
|
if (contentStream != null)
|
||||||
}
|
{
|
||||||
contentReader = null;
|
try
|
||||||
}
|
{
|
||||||
}
|
contentStream.close();
|
||||||
|
}
|
||||||
public void close()
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
reset();
|
}
|
||||||
if (requestBody != null)
|
contentStream = null;
|
||||||
{
|
}
|
||||||
try
|
if (contentReader != null)
|
||||||
{
|
{
|
||||||
requestBody.delete();
|
try
|
||||||
}
|
{
|
||||||
catch (Exception e)
|
contentReader.close();
|
||||||
{
|
}
|
||||||
}
|
catch (Exception e)
|
||||||
requestBody = null;
|
{
|
||||||
}
|
}
|
||||||
}
|
contentReader = null;
|
||||||
|
}
|
||||||
/* (non-Javadoc)
|
}
|
||||||
* @see org.springframework.extensions.webscripts.WrappingWebScriptRequest#getNext()
|
|
||||||
*/
|
public void close()
|
||||||
@Override
|
{
|
||||||
public WebScriptRequest getNext()
|
reset();
|
||||||
{
|
if (bufferStream != null)
|
||||||
return req;
|
{
|
||||||
}
|
try
|
||||||
|
{
|
||||||
/* (non-Javadoc)
|
bufferStream.destroy();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#forceSuccessStatus()
|
}
|
||||||
*/
|
catch (Exception e)
|
||||||
@Override
|
{
|
||||||
public boolean forceSuccessStatus()
|
}
|
||||||
{
|
bufferStream = null;
|
||||||
return req.forceSuccessStatus();
|
}
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getAgent()
|
/* (non-Javadoc)
|
||||||
*/
|
* @see org.springframework.extensions.webscripts.WrappingWebScriptRequest#getNext()
|
||||||
@Override
|
*/
|
||||||
public String getAgent()
|
@Override
|
||||||
{
|
public WebScriptRequest getNext()
|
||||||
return req.getAgent();
|
{
|
||||||
}
|
return req;
|
||||||
/* (non-Javadoc)
|
}
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getContent()
|
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#forceSuccessStatus()
|
||||||
public Content getContent()
|
*/
|
||||||
{
|
@Override
|
||||||
final Content wrapped = req.getContent();
|
public boolean forceSuccessStatus()
|
||||||
return new Content(){
|
{
|
||||||
|
return req.forceSuccessStatus();
|
||||||
@Override
|
}
|
||||||
public String getContent() throws IOException
|
/* (non-Javadoc)
|
||||||
{
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getAgent()
|
||||||
return wrapped.getContent();
|
*/
|
||||||
}
|
@Override
|
||||||
|
public String getAgent()
|
||||||
@Override
|
{
|
||||||
public String getEncoding()
|
return req.getAgent();
|
||||||
{
|
}
|
||||||
return wrapped.getEncoding();
|
/* (non-Javadoc)
|
||||||
}
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getContent()
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getMimetype()
|
public Content getContent()
|
||||||
{
|
{
|
||||||
return wrapped.getMimetype();
|
final Content wrapped = req.getContent();
|
||||||
}
|
return new Content(){
|
||||||
|
|
||||||
|
@Override
|
||||||
@Override
|
public String getContent() throws IOException
|
||||||
public long getSize()
|
{
|
||||||
{
|
return wrapped.getContent();
|
||||||
return wrapped.getSize();
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public String getEncoding()
|
||||||
public InputStream getInputStream()
|
{
|
||||||
{
|
return wrapped.getEncoding();
|
||||||
if (BufferedRequest.this.contentReader != null)
|
}
|
||||||
{
|
|
||||||
throw new IllegalStateException("Reader in use");
|
@Override
|
||||||
}
|
public String getMimetype()
|
||||||
if (BufferedRequest.this.contentStream == null)
|
{
|
||||||
{
|
return wrapped.getMimetype();
|
||||||
try
|
}
|
||||||
{
|
|
||||||
BufferedRequest.this.contentStream = bufferInputStream();
|
|
||||||
}
|
@Override
|
||||||
catch (IOException e)
|
public long getSize()
|
||||||
{
|
{
|
||||||
throw new RuntimeException(e);
|
return wrapped.getSize();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return BufferedRequest.this.contentStream;
|
@Override
|
||||||
}
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
@Override
|
if (BufferedRequest.this.contentReader != null)
|
||||||
public BufferedReader getReader() throws IOException
|
{
|
||||||
{
|
throw new IllegalStateException("Reader in use");
|
||||||
if (BufferedRequest.this.contentStream != null)
|
}
|
||||||
{
|
if (BufferedRequest.this.contentStream == null)
|
||||||
throw new IllegalStateException("Input Stream in use");
|
{
|
||||||
}
|
try
|
||||||
if (BufferedRequest.this.contentReader == null)
|
{
|
||||||
{
|
BufferedRequest.this.contentStream = bufferInputStream();
|
||||||
String encoding = wrapped.getEncoding();
|
}
|
||||||
InputStream in = bufferInputStream();
|
catch (IOException e)
|
||||||
BufferedRequest.this.contentReader = new BufferedReader(new InputStreamReader(in, encoding == null ? "ISO-8859-1" : encoding));
|
{
|
||||||
}
|
throw new RuntimeException(e);
|
||||||
return BufferedRequest.this.contentReader;
|
}
|
||||||
}
|
}
|
||||||
};
|
return BufferedRequest.this.contentStream;
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getContentType()
|
@Override
|
||||||
*/
|
public BufferedReader getReader() throws IOException
|
||||||
@Override
|
{
|
||||||
public String getContentType()
|
if (BufferedRequest.this.contentStream != null)
|
||||||
{
|
{
|
||||||
return req.getContentType();
|
throw new IllegalStateException("Input Stream in use");
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
if (BufferedRequest.this.contentReader == null)
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getContextPath()
|
{
|
||||||
*/
|
String encoding = wrapped.getEncoding();
|
||||||
@Override
|
InputStream in = bufferInputStream();
|
||||||
public String getContextPath()
|
BufferedRequest.this.contentReader = new BufferedReader(new InputStreamReader(in, encoding == null ? "ISO-8859-1" : encoding));
|
||||||
{
|
}
|
||||||
return req.getContextPath();
|
return BufferedRequest.this.contentReader;
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
};
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getExtensionPath()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getContentType()
|
||||||
public String getExtensionPath()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getExtensionPath();
|
public String getContentType()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getContentType();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getFormat()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getContextPath()
|
||||||
public String getFormat()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getFormat();
|
public String getContextPath()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getContextPath();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getFormatStyle()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getExtensionPath()
|
||||||
public FormatStyle getFormatStyle()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getFormatStyle();
|
public String getExtensionPath()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getExtensionPath();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getHeader(java.lang.String)
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getFormat()
|
||||||
public String getHeader(String name)
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getHeader(name);
|
public String getFormat()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getFormat();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getHeaderNames()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getFormatStyle()
|
||||||
public String[] getHeaderNames()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getHeaderNames();
|
public FormatStyle getFormatStyle()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getFormatStyle();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getHeaderValues(java.lang.String)
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getHeader(java.lang.String)
|
||||||
public String[] getHeaderValues(String name)
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getHeaderValues(name);
|
public String getHeader(String name)
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getHeader(name);
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getJSONCallback()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getHeaderNames()
|
||||||
public String getJSONCallback()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getJSONCallback();
|
public String[] getHeaderNames()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getHeaderNames();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getParameter(java.lang.String)
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getHeaderValues(java.lang.String)
|
||||||
public String getParameter(String name)
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getParameter(name);
|
public String[] getHeaderValues(String name)
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getHeaderValues(name);
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getParameterNames()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getJSONCallback()
|
||||||
public String[] getParameterNames()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getParameterNames();
|
public String getJSONCallback()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getJSONCallback();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getParameterValues(java.lang.String)
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getParameter(java.lang.String)
|
||||||
public String[] getParameterValues(String name)
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getParameterValues(name);
|
public String getParameter(String name)
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getParameter(name);
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getPathInfo()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getParameterNames()
|
||||||
public String getPathInfo()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getPathInfo();
|
public String[] getParameterNames()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getParameterNames();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getQueryString()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getParameterValues(java.lang.String)
|
||||||
public String getQueryString()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getQueryString();
|
public String[] getParameterValues(String name)
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getParameterValues(name);
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getRuntime()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getPathInfo()
|
||||||
public Runtime getRuntime()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getRuntime();
|
public String getPathInfo()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getPathInfo();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getServerPath()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getQueryString()
|
||||||
public String getServerPath()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getServerPath();
|
public String getQueryString()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getQueryString();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getServiceContextPath()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getRuntime()
|
||||||
public String getServiceContextPath()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getServiceContextPath();
|
public Runtime getRuntime()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getRuntime();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getServiceMatch()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getServerPath()
|
||||||
public Match getServiceMatch()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getServiceMatch();
|
public String getServerPath()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getServerPath();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getServicePath()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getServiceContextPath()
|
||||||
public String getServicePath()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getServicePath();
|
public String getServiceContextPath()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getServiceContextPath();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#getURL()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getServiceMatch()
|
||||||
public String getURL()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.getURL();
|
public Match getServiceMatch()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getServiceMatch();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#isGuest()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getServicePath()
|
||||||
public boolean isGuest()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.isGuest();
|
public String getServicePath()
|
||||||
}
|
{
|
||||||
/* (non-Javadoc)
|
return req.getServicePath();
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptRequest#parseContent()
|
}
|
||||||
*/
|
/* (non-Javadoc)
|
||||||
@Override
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#getURL()
|
||||||
public Object parseContent()
|
*/
|
||||||
{
|
@Override
|
||||||
return req.parseContent();
|
public String getURL()
|
||||||
}
|
{
|
||||||
}
|
return req.getURL();
|
||||||
|
}
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#isGuest()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isGuest()
|
||||||
|
{
|
||||||
|
return req.isGuest();
|
||||||
|
}
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.springframework.extensions.webscripts.WebScriptRequest#parseContent()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object parseContent()
|
||||||
|
{
|
||||||
|
return req.parseContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,276 +1,274 @@
|
|||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* Alfresco Remote API
|
* Alfresco Remote API
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
* the paid license agreement will prevail. Otherwise, the software is
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
* provided under the following open source license terms:
|
* provided under the following open source license terms:
|
||||||
*
|
*
|
||||||
* Alfresco is free software: you can redistribute it and/or modify
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* Alfresco is distributed in the hope that it will be useful,
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Lesser General Public License for more details.
|
* GNU Lesser General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package org.alfresco.repo.web.scripts;
|
package org.alfresco.repo.web.scripts;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.apache.chemistry.opencmis.commons.server.TempStoreOutputStream;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.chemistry.opencmis.server.shared.TempStoreOutputStreamFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.commons.logging.Log;
|
import org.springframework.extensions.surf.util.StringBuilderWriter;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.springframework.extensions.webscripts.Cache;
|
||||||
import org.springframework.extensions.surf.util.StringBuilderWriter;
|
import org.springframework.extensions.webscripts.Runtime;
|
||||||
import org.springframework.extensions.webscripts.Cache;
|
import org.springframework.extensions.webscripts.WebScriptResponse;
|
||||||
import org.springframework.extensions.webscripts.Runtime;
|
import org.springframework.extensions.webscripts.WrappingWebScriptResponse;
|
||||||
import org.springframework.extensions.webscripts.WebScriptResponse;
|
import org.springframework.util.FileCopyUtils;
|
||||||
import org.springframework.extensions.webscripts.WrappingWebScriptResponse;
|
|
||||||
import org.springframework.util.FileCopyUtils;
|
/**
|
||||||
|
* Transactional Buffered Response
|
||||||
/**
|
*/
|
||||||
* Transactional Buffered Response
|
public class BufferedResponse implements WrappingWebScriptResponse
|
||||||
*/
|
{
|
||||||
public class BufferedResponse implements WrappingWebScriptResponse
|
// Logger
|
||||||
{
|
protected static final Log logger = LogFactory.getLog(BufferedResponse.class);
|
||||||
// Logger
|
|
||||||
protected static final Log logger = LogFactory.getLog(BufferedResponse.class);
|
private TempOutputStreamFactory streamFactory;
|
||||||
|
private WebScriptResponse res;
|
||||||
private TempStoreOutputStreamFactory streamFactory;
|
private int bufferSize;
|
||||||
private WebScriptResponse res;
|
private TempOutputStream outputStream = null;
|
||||||
private int bufferSize;
|
private StringBuilderWriter outputWriter = null;
|
||||||
private TempStoreOutputStream outputStream = null;
|
|
||||||
private StringBuilderWriter outputWriter = null;
|
|
||||||
|
/**
|
||||||
|
* Construct
|
||||||
/**
|
*
|
||||||
* Construct
|
* @param res WebScriptResponse
|
||||||
*
|
* @param bufferSize int
|
||||||
* @param res WebScriptResponse
|
*/
|
||||||
* @param bufferSize int
|
public BufferedResponse(WebScriptResponse res, int bufferSize, TempOutputStreamFactory streamFactory)
|
||||||
*/
|
{
|
||||||
public BufferedResponse(WebScriptResponse res, int bufferSize, TempStoreOutputStreamFactory streamFactory)
|
this.res = res;
|
||||||
{
|
this.bufferSize = bufferSize;
|
||||||
this.res = res;
|
this.streamFactory = streamFactory;
|
||||||
this.bufferSize = bufferSize;
|
}
|
||||||
this.streamFactory = streamFactory;
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WrappingWebScriptResponse#getNext()
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WrappingWebScriptResponse#getNext()
|
public WebScriptResponse getNext()
|
||||||
*/
|
{
|
||||||
public WebScriptResponse getNext()
|
return res;
|
||||||
{
|
}
|
||||||
return res;
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#addHeader(java.lang.String, java.lang.String)
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#addHeader(java.lang.String, java.lang.String)
|
public void addHeader(String name, String value)
|
||||||
*/
|
{
|
||||||
public void addHeader(String name, String value)
|
res.addHeader(name, value);
|
||||||
{
|
}
|
||||||
res.addHeader(name, value);
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#encodeScriptUrl(java.lang.String)
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#encodeScriptUrl(java.lang.String)
|
public String encodeScriptUrl(String url)
|
||||||
*/
|
{
|
||||||
public String encodeScriptUrl(String url)
|
return res.encodeScriptUrl(url);
|
||||||
{
|
}
|
||||||
return res.encodeScriptUrl(url);
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#getEncodeScriptUrlFunction(java.lang.String)
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#getEncodeScriptUrlFunction(java.lang.String)
|
public String getEncodeScriptUrlFunction(String name)
|
||||||
*/
|
{
|
||||||
public String getEncodeScriptUrlFunction(String name)
|
return res.getEncodeScriptUrlFunction(name);
|
||||||
{
|
}
|
||||||
return res.getEncodeScriptUrlFunction(name);
|
|
||||||
}
|
/* (non-Javadoc)
|
||||||
|
* @see org.springframework.extensions.webscripts.WebScriptResponse#encodeResourceUrl(java.lang.String)
|
||||||
/* (non-Javadoc)
|
*/
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptResponse#encodeResourceUrl(java.lang.String)
|
public String encodeResourceUrl(String url)
|
||||||
*/
|
{
|
||||||
public String encodeResourceUrl(String url)
|
return res.encodeResourceUrl(url);
|
||||||
{
|
}
|
||||||
return res.encodeResourceUrl(url);
|
|
||||||
}
|
/* (non-Javadoc)
|
||||||
|
* @see org.springframework.extensions.webscripts.WebScriptResponse#getEncodeResourceUrlFunction(java.lang.String)
|
||||||
/* (non-Javadoc)
|
*/
|
||||||
* @see org.springframework.extensions.webscripts.WebScriptResponse#getEncodeResourceUrlFunction(java.lang.String)
|
public String getEncodeResourceUrlFunction(String name)
|
||||||
*/
|
{
|
||||||
public String getEncodeResourceUrlFunction(String name)
|
return res.getEncodeResourceUrlFunction(name);
|
||||||
{
|
}
|
||||||
return res.getEncodeResourceUrlFunction(name);
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#getOutputStream()
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#getOutputStream()
|
public OutputStream getOutputStream() throws IOException
|
||||||
*/
|
{
|
||||||
public OutputStream getOutputStream() throws IOException
|
if (outputStream == null)
|
||||||
{
|
{
|
||||||
if (outputStream == null)
|
if (outputWriter != null)
|
||||||
{
|
{
|
||||||
if (outputWriter != null)
|
throw new AlfrescoRuntimeException("Already buffering output writer");
|
||||||
{
|
}
|
||||||
throw new AlfrescoRuntimeException("Already buffering output writer");
|
outputStream = streamFactory.createOutputStream();
|
||||||
}
|
}
|
||||||
outputStream = streamFactory.newOutputStream();
|
return outputStream;
|
||||||
}
|
}
|
||||||
return outputStream;
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#getRuntime()
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#getRuntime()
|
public Runtime getRuntime()
|
||||||
*/
|
{
|
||||||
public Runtime getRuntime()
|
return res.getRuntime();
|
||||||
{
|
}
|
||||||
return res.getRuntime();
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#getWriter()
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#getWriter()
|
public Writer getWriter() throws IOException
|
||||||
*/
|
{
|
||||||
public Writer getWriter() throws IOException
|
if (outputWriter == null)
|
||||||
{
|
{
|
||||||
if (outputWriter == null)
|
if (outputStream != null)
|
||||||
{
|
{
|
||||||
if (outputStream != null)
|
throw new AlfrescoRuntimeException("Already buffering output stream");
|
||||||
{
|
}
|
||||||
throw new AlfrescoRuntimeException("Already buffering output stream");
|
outputWriter = new StringBuilderWriter(bufferSize);
|
||||||
}
|
}
|
||||||
outputWriter = new StringBuilderWriter(bufferSize);
|
return outputWriter;
|
||||||
}
|
}
|
||||||
return outputWriter;
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#reset()
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#reset()
|
public void reset()
|
||||||
*/
|
{
|
||||||
public void reset()
|
if (outputStream != null)
|
||||||
{
|
{
|
||||||
if (outputStream != null)
|
outputStream = null;
|
||||||
{
|
}
|
||||||
outputStream = null;
|
else if (outputWriter != null)
|
||||||
}
|
{
|
||||||
else if (outputWriter != null)
|
outputWriter = null;
|
||||||
{
|
}
|
||||||
outputWriter = null;
|
res.reset();
|
||||||
}
|
}
|
||||||
res.reset();
|
|
||||||
}
|
/* (non-Javadoc)
|
||||||
|
* @see org./alfresco.web.scripts.WebScriptResponse#resetjava.lang.String)
|
||||||
/* (non-Javadoc)
|
*/
|
||||||
* @see org./alfresco.web.scripts.WebScriptResponse#resetjava.lang.String)
|
public void reset(String preserveHeadersPattern)
|
||||||
*/
|
{
|
||||||
public void reset(String preserveHeadersPattern)
|
if (outputStream != null)
|
||||||
{
|
{
|
||||||
if (outputStream != null)
|
outputStream = null;
|
||||||
{
|
}
|
||||||
outputStream = null;
|
else if (outputWriter != null)
|
||||||
}
|
{
|
||||||
else if (outputWriter != null)
|
outputWriter = null;
|
||||||
{
|
}
|
||||||
outputWriter = null;
|
res.reset(preserveHeadersPattern);
|
||||||
}
|
}
|
||||||
res.reset(preserveHeadersPattern);
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#setCache(org.alfresco.web.scripts.Cache)
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#setCache(org.alfresco.web.scripts.Cache)
|
public void setCache(Cache cache)
|
||||||
*/
|
{
|
||||||
public void setCache(Cache cache)
|
res.setCache(cache);
|
||||||
{
|
}
|
||||||
res.setCache(cache);
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String)
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String)
|
public void setContentType(String contentType)
|
||||||
*/
|
{
|
||||||
public void setContentType(String contentType)
|
res.setContentType(contentType);
|
||||||
{
|
}
|
||||||
res.setContentType(contentType);
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#setContentEncoding(java.lang.String)
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#setContentEncoding(java.lang.String)
|
public void setContentEncoding(String contentEncoding)
|
||||||
*/
|
{
|
||||||
public void setContentEncoding(String contentEncoding)
|
res.setContentEncoding(contentEncoding);
|
||||||
{
|
}
|
||||||
res.setContentEncoding(contentEncoding);
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#setHeader(java.lang.String, java.lang.String)
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#setHeader(java.lang.String, java.lang.String)
|
public void setHeader(String name, String value)
|
||||||
*/
|
{
|
||||||
public void setHeader(String name, String value)
|
res.setHeader(name, value);
|
||||||
{
|
}
|
||||||
res.setHeader(name, value);
|
|
||||||
}
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
/*
|
* @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int)
|
||||||
* (non-Javadoc)
|
*/
|
||||||
* @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int)
|
public void setStatus(int status)
|
||||||
*/
|
{
|
||||||
public void setStatus(int status)
|
res.setStatus(status);
|
||||||
{
|
}
|
||||||
res.setStatus(status);
|
|
||||||
}
|
/**
|
||||||
|
* Write buffered response to underlying response
|
||||||
/**
|
*/
|
||||||
* Write buffered response to underlying response
|
public void writeResponse()
|
||||||
*/
|
{
|
||||||
public void writeResponse()
|
try
|
||||||
{
|
{
|
||||||
try
|
if (logger.isDebugEnabled() && outputStream != null)
|
||||||
{
|
{
|
||||||
if (logger.isDebugEnabled() && outputStream != null)
|
logger.debug("Writing Transactional response: size=" + outputStream.getLength());
|
||||||
{
|
}
|
||||||
logger.debug("Writing Transactional response: size=" + outputStream.getLength());
|
|
||||||
}
|
if (outputWriter != null)
|
||||||
|
{
|
||||||
if (outputWriter != null)
|
outputWriter.flush();
|
||||||
{
|
res.getWriter().write(outputWriter.toString());
|
||||||
outputWriter.flush();
|
}
|
||||||
res.getWriter().write(outputWriter.toString());
|
else if (outputStream != null)
|
||||||
}
|
{
|
||||||
else if (outputStream != null)
|
if (logger.isDebugEnabled())
|
||||||
{
|
logger.debug("Writing Transactional response: size=" + outputStream.getLength());
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
logger.debug("Writing Transactional response: size=" + outputStream.getLength());
|
outputStream.flush();
|
||||||
|
FileCopyUtils.copy(outputStream.getInputStream(), res.getOutputStream());
|
||||||
outputStream.flush();
|
}
|
||||||
FileCopyUtils.copy(outputStream.getInputStream(), res.getOutputStream());
|
}
|
||||||
}
|
catch (IOException e)
|
||||||
}
|
{
|
||||||
catch (IOException e)
|
throw new AlfrescoRuntimeException("Failed to commit buffered response", e);
|
||||||
{
|
}
|
||||||
throw new AlfrescoRuntimeException("Failed to commit buffered response", e);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,383 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Remote API
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco 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, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.web.scripts;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.Key;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherInputStream;
|
||||||
|
import javax.crypto.CipherOutputStream;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
|
import org.alfresco.repo.content.ContentLimitViolationException;
|
||||||
|
import org.alfresco.util.TempFileProvider;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An output stream implementation that keeps the data in memory if is less then
|
||||||
|
* the specified <b>memoryThreshold</b> otherwise it writes it to a temp file.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* Close the stream before any call to
|
||||||
|
* {@link TempOutputStream}.getInputStream().
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* If <b>deleteTempFileOnClose</b> is false then use proper try-finally patterns
|
||||||
|
* to ensure that the temp file is destroyed after it is no longer needed.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <code>try
|
||||||
|
* {
|
||||||
|
* StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), tempOutputStream);
|
||||||
|
* tempOutputStream.close();
|
||||||
|
* }
|
||||||
|
* finally
|
||||||
|
* {
|
||||||
|
* tempOutputStream.destroy();
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public class TempOutputStream extends OutputStream
|
||||||
|
{
|
||||||
|
private static final Log logger = LogFactory.getLog(TempOutputStream.class);
|
||||||
|
|
||||||
|
private static final int DEFAULT_MEMORY_THRESHOLD = 4 * 1024 * 1024; // 4mb
|
||||||
|
private static final String ALGORITHM = "AES";
|
||||||
|
private static final String MODE = "CTR";
|
||||||
|
private static final String PADDING = "PKCS5Padding";
|
||||||
|
private static final String TRANSFORMATION = ALGORITHM + '/' + MODE + '/' + PADDING;
|
||||||
|
private static final int KEY_SIZE = 128;
|
||||||
|
public static final String TEMP_FILE_PREFIX = "tempStreamFile-";
|
||||||
|
|
||||||
|
private final File tempDir;
|
||||||
|
private final int memoryThreshold;
|
||||||
|
private final long maxContentSize;
|
||||||
|
private boolean encrypt;
|
||||||
|
private boolean deleteTempFileOnClose;
|
||||||
|
|
||||||
|
private long length = 0;
|
||||||
|
private OutputStream outputStream;
|
||||||
|
private File tempFile;
|
||||||
|
private TempByteArrayOutputStream tempStream;
|
||||||
|
|
||||||
|
private Key symKey;
|
||||||
|
private byte[] iv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TempOutputStream.
|
||||||
|
*
|
||||||
|
* @param tempDir
|
||||||
|
* the temporary directory, i.e. <code>isDir == true</code>, that
|
||||||
|
* will be used as * parent directory for creating temp file backed
|
||||||
|
* streams
|
||||||
|
* @param memoryThreshold
|
||||||
|
* the memory threshold in B
|
||||||
|
* @param maxContentSize
|
||||||
|
* the max content size in B
|
||||||
|
* @param encrypt
|
||||||
|
* true if temp files should be encrypted
|
||||||
|
* @param deleteTempFileOnClose
|
||||||
|
* true if temp files should be deleted on output stream close
|
||||||
|
* (useful if we need to cache the content for further reads). If
|
||||||
|
* this is false then we need to make sure we call
|
||||||
|
* {@link TempOutputStream}.destroy to clean up properly.
|
||||||
|
*/
|
||||||
|
public TempOutputStream(File tempDir, int memoryThreshold, long maxContentSize, boolean encrypt, boolean deleteTempFileOnClose)
|
||||||
|
{
|
||||||
|
this.tempDir = tempDir;
|
||||||
|
this.memoryThreshold = (memoryThreshold < 0) ? DEFAULT_MEMORY_THRESHOLD : memoryThreshold;
|
||||||
|
this.maxContentSize = maxContentSize;
|
||||||
|
this.encrypt = encrypt;
|
||||||
|
this.deleteTempFileOnClose = deleteTempFileOnClose;
|
||||||
|
|
||||||
|
this.tempStream = new TempByteArrayOutputStream();
|
||||||
|
this.outputStream = this.tempStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data as an InputStream
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream() throws IOException
|
||||||
|
{
|
||||||
|
if (tempFile != null)
|
||||||
|
{
|
||||||
|
if (encrypt)
|
||||||
|
{
|
||||||
|
final Cipher cipher;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cipher = Cipher.getInstance(TRANSFORMATION);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(iv));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
destroy();
|
||||||
|
|
||||||
|
if (logger.isErrorEnabled())
|
||||||
|
{
|
||||||
|
logger.error("Cannot initialize decryption cipher", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IOException("Cannot initialize decryption cipher", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BufferedInputStream(new CipherInputStream(new FileInputStream(tempFile), cipher));
|
||||||
|
}
|
||||||
|
return new BufferedInputStream(new FileInputStream(tempFile));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new ByteArrayInputStream(tempStream.getBuffer(), 0, tempStream.getCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException
|
||||||
|
{
|
||||||
|
update(1);
|
||||||
|
outputStream.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException
|
||||||
|
{
|
||||||
|
update(len);
|
||||||
|
outputStream.write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() throws IOException
|
||||||
|
{
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException
|
||||||
|
{
|
||||||
|
close(deleteTempFileOnClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the stream and removes the backing file (if present).
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* If <b>deleteTempFileOnClose</b> is false then use proper try-finally patterns
|
||||||
|
* to ensure that the temp file is destroyed after it is no longer needed.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <code>try
|
||||||
|
* {
|
||||||
|
* StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), tempOutputStream);
|
||||||
|
* tempOutputStream.close();
|
||||||
|
* }
|
||||||
|
* finally
|
||||||
|
* {
|
||||||
|
* tempOutputStream.destroy();
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public void destroy() throws IOException
|
||||||
|
{
|
||||||
|
close(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLength()
|
||||||
|
{
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeOutputStream()
|
||||||
|
{
|
||||||
|
if (outputStream != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Flushing the output stream failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Closing the output stream failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteTempFile()
|
||||||
|
{
|
||||||
|
if (tempFile != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
boolean isDeleted = tempFile.delete();
|
||||||
|
if (!isDeleted)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Temp file could not be deleted: " + tempFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Deleted temp file: " + tempFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tempFile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close(boolean deleteTempFileOnClose)
|
||||||
|
{
|
||||||
|
closeOutputStream();
|
||||||
|
|
||||||
|
if (deleteTempFileOnClose)
|
||||||
|
{
|
||||||
|
deleteTempFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BufferedOutputStream createOutputStream(File file) throws IOException
|
||||||
|
{
|
||||||
|
BufferedOutputStream fileOutputStream;
|
||||||
|
if (encrypt)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Generate a symmetric key
|
||||||
|
final KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
|
||||||
|
keyGen.init(KEY_SIZE);
|
||||||
|
symKey = keyGen.generateKey();
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, symKey);
|
||||||
|
|
||||||
|
iv = cipher.getIV();
|
||||||
|
|
||||||
|
fileOutputStream = new BufferedOutputStream(new CipherOutputStream(new FileOutputStream(file), cipher));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (logger.isErrorEnabled())
|
||||||
|
{
|
||||||
|
logger.error("Cannot initialize encryption cipher", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IOException("Cannot initialize encryption cipher", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fileOutputStream = new BufferedOutputStream(new FileOutputStream(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileOutputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(int len) throws IOException
|
||||||
|
{
|
||||||
|
if (maxContentSize > -1 && length + len > maxContentSize)
|
||||||
|
{
|
||||||
|
destroy();
|
||||||
|
throw new ContentLimitViolationException("Content size violation, limit = " + maxContentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempFile == null && (tempStream.getCount() + len) > memoryThreshold)
|
||||||
|
{
|
||||||
|
File file = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, ".bin", tempDir);
|
||||||
|
|
||||||
|
BufferedOutputStream fileOutputStream = createOutputStream(file);
|
||||||
|
fileOutputStream.write(this.tempStream.getBuffer(), 0, this.tempStream.getCount());
|
||||||
|
fileOutputStream.flush();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tempStream.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
// Ignore exception
|
||||||
|
}
|
||||||
|
tempStream = null;
|
||||||
|
|
||||||
|
tempFile = file;
|
||||||
|
outputStream = fileOutputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TempByteArrayOutputStream extends ByteArrayOutputStream
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return The internal buffer where data is stored
|
||||||
|
*/
|
||||||
|
public byte[] getBuffer()
|
||||||
|
{
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of valid bytes in the buffer.
|
||||||
|
*/
|
||||||
|
public int getCount()
|
||||||
|
{
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Remote API
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco 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, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.web.scripts;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link TempOutputStream}
|
||||||
|
*/
|
||||||
|
public class TempOutputStreamFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A temporary directory, i.e. <code>isDir == true</code>, that will be used as
|
||||||
|
* parent directory for creating temp file backed streams.
|
||||||
|
*/
|
||||||
|
private final File tempDir;
|
||||||
|
private int memoryThreshold;
|
||||||
|
private long maxContentSize;
|
||||||
|
private boolean encrypt;
|
||||||
|
private boolean deleteTempFileOnClose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link TempOutputStream} factory.
|
||||||
|
*
|
||||||
|
* @param tempDir
|
||||||
|
* the temporary directory, i.e. <code>isDir == true</code>, that
|
||||||
|
* will be used as * parent directory for creating temp file backed
|
||||||
|
* streams
|
||||||
|
* @param memoryThreshold
|
||||||
|
* the memory threshold in B
|
||||||
|
* @param maxContentSize
|
||||||
|
* the max content size in B
|
||||||
|
* @param encrypt
|
||||||
|
* true if temp files should be encrypted
|
||||||
|
* @param deleteTempFileOnClose
|
||||||
|
* true if temp files should be deleted on output stream close
|
||||||
|
* (useful if we need to cache the content for further reads). If
|
||||||
|
* this is false then we need to make sure we call
|
||||||
|
* {@link TempOutputStream}.destroy to clean up properly.
|
||||||
|
*/
|
||||||
|
public TempOutputStreamFactory(File tempDir, int memoryThreshold, long maxContentSize, boolean encrypt, boolean deleteTempFileOnClose)
|
||||||
|
{
|
||||||
|
this.tempDir = tempDir;
|
||||||
|
this.memoryThreshold = memoryThreshold;
|
||||||
|
this.maxContentSize = maxContentSize;
|
||||||
|
this.encrypt = encrypt;
|
||||||
|
this.deleteTempFileOnClose = deleteTempFileOnClose;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link TempOutputStream} object
|
||||||
|
*/
|
||||||
|
public TempOutputStream createOutputStream()
|
||||||
|
{
|
||||||
|
return new TempOutputStream(tempDir, memoryThreshold, maxContentSize, encrypt, deleteTempFileOnClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getTempDir()
|
||||||
|
{
|
||||||
|
return tempDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMemoryThreshold()
|
||||||
|
{
|
||||||
|
return memoryThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxContentSize()
|
||||||
|
{
|
||||||
|
return maxContentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEncrypt()
|
||||||
|
{
|
||||||
|
return encrypt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeleteTempFileOnClose()
|
||||||
|
{
|
||||||
|
return deleteTempFileOnClose;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,283 +1,309 @@
|
|||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* Alfresco Remote API
|
* Alfresco Remote API
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
* the paid license agreement will prevail. Otherwise, the software is
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
* provided under the following open source license terms:
|
* provided under the following open source license terms:
|
||||||
*
|
*
|
||||||
* Alfresco is free software: you can redistribute it and/or modify
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* Alfresco is distributed in the hope that it will be useful,
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Lesser General Public License for more details.
|
* GNU Lesser General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package org.alfresco.rest.framework.webscripts;
|
package org.alfresco.rest.framework.webscripts;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.Map;
|
||||||
import java.util.Map;
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.metrics.rest.RestMetricsReporter;
|
||||||
import org.alfresco.metrics.rest.RestMetricsReporter;
|
import org.alfresco.repo.tenant.TenantUtil;
|
||||||
import org.alfresco.repo.tenant.TenantUtil;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
import org.alfresco.repo.web.scripts.BufferedRequest;
|
||||||
import org.alfresco.repo.web.scripts.content.ContentStreamer;
|
import org.alfresco.repo.web.scripts.content.ContentStreamer;
|
||||||
import org.alfresco.rest.framework.Api;
|
import org.alfresco.rest.framework.Api;
|
||||||
import org.alfresco.rest.framework.core.HttpMethodSupport;
|
import org.alfresco.rest.framework.core.HttpMethodSupport;
|
||||||
import org.alfresco.rest.framework.core.ResourceInspector;
|
import org.alfresco.rest.framework.core.ResourceInspector;
|
||||||
import org.alfresco.rest.framework.core.ResourceLocator;
|
import org.alfresco.rest.framework.core.ResourceLocator;
|
||||||
import org.alfresco.rest.framework.core.ResourceOperation;
|
import org.alfresco.rest.framework.core.ResourceOperation;
|
||||||
import org.alfresco.rest.framework.core.ResourceWithMetadata;
|
import org.alfresco.rest.framework.core.ResourceWithMetadata;
|
||||||
import org.alfresco.rest.framework.core.exceptions.ApiException;
|
import org.alfresco.rest.framework.core.exceptions.ApiException;
|
||||||
import org.alfresco.rest.framework.resource.actions.ActionExecutor;
|
import org.alfresco.rest.framework.resource.actions.ActionExecutor;
|
||||||
import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
|
import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
|
||||||
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
|
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
|
||||||
import org.alfresco.rest.framework.resource.content.BinaryResource;
|
import org.alfresco.rest.framework.resource.content.BinaryResource;
|
||||||
import org.alfresco.rest.framework.resource.content.CacheDirective;
|
import org.alfresco.rest.framework.resource.content.CacheDirective;
|
||||||
import org.alfresco.rest.framework.resource.content.ContentInfo;
|
import org.alfresco.rest.framework.resource.content.ContentInfo;
|
||||||
import org.alfresco.rest.framework.resource.content.FileBinaryResource;
|
import org.alfresco.rest.framework.resource.content.FileBinaryResource;
|
||||||
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
|
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
|
||||||
import org.alfresco.rest.framework.resource.parameters.Params;
|
import org.alfresco.rest.framework.resource.parameters.Params;
|
||||||
import org.alfresco.rest.framework.tools.ResponseWriter;
|
import org.alfresco.rest.framework.tools.ResponseWriter;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.extensions.webscripts.WebScriptException;
|
import org.springframework.extensions.webscripts.WebScriptException;
|
||||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||||
import org.springframework.extensions.webscripts.WebScriptResponse;
|
import org.springframework.extensions.webscripts.WebScriptResponse;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Webscript that handles the request for and execution of a Resource
|
* Webscript that handles the request for and execution of a Resource
|
||||||
*
|
*
|
||||||
* 1) Finds a resource
|
* 1) Finds a resource
|
||||||
* 2) Extracts params
|
* 2) Extracts params
|
||||||
* 3) Executes params on a resource
|
* 3) Executes params on a resource
|
||||||
* 4) Post processes the response to add embeds or projected relationship
|
* 4) Post processes the response to add embeds or projected relationship
|
||||||
* 5) Renders the response
|
* 5) Renders the response
|
||||||
*
|
*
|
||||||
* @author Gethin James
|
* @author Gethin James
|
||||||
* @author janv
|
* @author janv
|
||||||
*/
|
*/
|
||||||
// TODO for requests that pass in input streams e.g. binary content for workflow, this is going to need a way to re-read the input stream a la
|
// TODO for requests that pass in input streams e.g. binary content for workflow, this is going to need a way to re-read the input stream a la
|
||||||
// code in RepositoryContainer due to retrying transaction logic
|
// code in RepositoryContainer due to retrying transaction logic
|
||||||
public abstract class AbstractResourceWebScript extends ApiWebScript implements HttpMethodSupport, ActionExecutor, ResponseWriter
|
public abstract class AbstractResourceWebScript extends ApiWebScript implements HttpMethodSupport, ActionExecutor, ResponseWriter
|
||||||
{
|
{
|
||||||
private static Log logger = LogFactory.getLog(AbstractResourceWebScript.class);
|
private static Log logger = LogFactory.getLog(AbstractResourceWebScript.class);
|
||||||
|
|
||||||
protected ResourceLocator locator;
|
protected ResourceLocator locator;
|
||||||
private HttpMethod httpMethod;
|
private HttpMethod httpMethod;
|
||||||
private ParamsExtractor paramsExtractor;
|
private ParamsExtractor paramsExtractor;
|
||||||
private ContentStreamer streamer;
|
private ContentStreamer streamer;
|
||||||
protected ResourceWebScriptHelper helper;
|
protected ResourceWebScriptHelper helper;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
public void execute(final Api api, final WebScriptRequest req, final WebScriptResponse res) throws IOException
|
public void execute(final Api api, final WebScriptRequest req, final WebScriptResponse res) throws IOException
|
||||||
{
|
{
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final Map<String, Object> respons = new HashMap<String, Object>();
|
final Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
|
||||||
final Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
|
final ResourceWithMetadata resource = locator.locateResource(api,templateVars, httpMethod);
|
||||||
final ResourceWithMetadata resource = locator.locateResource(api,templateVars, httpMethod);
|
final boolean isReadOnly = HttpMethod.GET==httpMethod;
|
||||||
final Params params = paramsExtractor.extractParams(resource.getMetaData(),req);
|
|
||||||
final boolean isReadOnly = HttpMethod.GET==httpMethod;
|
// MNT-20308 - allow write transactions for authentication api
|
||||||
|
RetryingTransactionHelper transHelper = getTransactionHelper(resource.getMetaData().getApi().getName());
|
||||||
//This execution usually takes place in a Retrying Transaction (see subclasses)
|
|
||||||
final Object toSerialize = execute(resource, params, res, isReadOnly);
|
// encapsulate script within transaction
|
||||||
|
RetryingTransactionHelper.RetryingTransactionCallback<Object> work = new RetryingTransactionHelper.RetryingTransactionCallback<Object>()
|
||||||
//Outside the transaction.
|
{
|
||||||
if (toSerialize != null)
|
@Override
|
||||||
{
|
public Object execute() throws Throwable
|
||||||
if (toSerialize instanceof BinaryResource)
|
{
|
||||||
{
|
try
|
||||||
// TODO review (experimental) - can we move earlier & wrap complete execute ? Also for QuickShare (in MT/Cloud) needs to be tenant for the nodeRef (TBC).
|
{
|
||||||
boolean noAuth = false;
|
final Params params = paramsExtractor.extractParams(resource.getMetaData(), req);
|
||||||
|
return AbstractResourceWebScript.this.execute(resource, params, res, isReadOnly);
|
||||||
if (BinaryResourceAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
|
}
|
||||||
{
|
catch (Exception e)
|
||||||
noAuth = resource.getMetaData().isNoAuth(BinaryResourceAction.Read.class);
|
{
|
||||||
}
|
if (req instanceof BufferedRequest)
|
||||||
else if (RelationshipResourceBinaryAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
|
{
|
||||||
{
|
// Reset the request in case of a transaction retry
|
||||||
noAuth = resource.getMetaData().isNoAuth(RelationshipResourceBinaryAction.Read.class);
|
((BufferedRequest) req).reset();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// re-throw original exception for retry
|
||||||
logger.warn("Unexpected");
|
throw e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (noAuth)
|
};
|
||||||
{
|
|
||||||
String networkTenantDomain = TenantUtil.getCurrentDomain();
|
//This execution usually takes place in a Retrying Transaction (see subclasses)
|
||||||
|
final Object toSerialize = transHelper.doInTransaction(work, isReadOnly, true);
|
||||||
TenantUtil.runAsSystemTenant(new TenantUtil.TenantRunAsWork<Void>()
|
|
||||||
{
|
//Outside the transaction.
|
||||||
public Void doWork() throws Exception
|
if (toSerialize != null)
|
||||||
{
|
{
|
||||||
streamResponse(req, res, (BinaryResource) toSerialize);
|
if (toSerialize instanceof BinaryResource)
|
||||||
return null;
|
{
|
||||||
}
|
// TODO review (experimental) - can we move earlier & wrap complete execute ? Also for QuickShare (in MT/Cloud) needs to be tenant for the nodeRef (TBC).
|
||||||
}, networkTenantDomain);
|
boolean noAuth = false;
|
||||||
}
|
|
||||||
else
|
if (BinaryResourceAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
|
||||||
{
|
{
|
||||||
streamResponse(req, res, (BinaryResource) toSerialize);
|
noAuth = resource.getMetaData().isNoAuth(BinaryResourceAction.Read.class);
|
||||||
}
|
}
|
||||||
}
|
else if (RelationshipResourceBinaryAction.Read.class.isAssignableFrom(resource.getResource().getClass()))
|
||||||
else
|
{
|
||||||
{
|
noAuth = resource.getMetaData().isNoAuth(RelationshipResourceBinaryAction.Read.class);
|
||||||
renderJsonResponse(res, toSerialize, assistant.getJsonHelper());
|
}
|
||||||
}
|
else
|
||||||
}
|
{
|
||||||
|
logger.warn("Unexpected");
|
||||||
}
|
}
|
||||||
catch (AlfrescoRuntimeException | ApiException | WebScriptException xception )
|
|
||||||
{
|
if (noAuth)
|
||||||
renderException(xception, res, assistant);
|
{
|
||||||
}
|
String networkTenantDomain = TenantUtil.getCurrentDomain();
|
||||||
catch (RuntimeException runtimeException)
|
|
||||||
{
|
TenantUtil.runAsSystemTenant(new TenantUtil.TenantRunAsWork<Void>()
|
||||||
renderException(runtimeException, res, assistant);
|
{
|
||||||
}
|
public Void doWork() throws Exception
|
||||||
finally
|
{
|
||||||
{
|
streamResponse(req, res, (BinaryResource) toSerialize);
|
||||||
reportExecutionTimeMetric(startTime, req.getServicePath());
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}, networkTenantDomain);
|
||||||
|
}
|
||||||
public Object execute(final ResourceWithMetadata resource, final Params params, final WebScriptResponse res, boolean isReadOnly)
|
else
|
||||||
{
|
{
|
||||||
final String entityCollectionName = ResourceInspector.findEntityCollectionNameName(resource.getMetaData());
|
streamResponse(req, res, (BinaryResource) toSerialize);
|
||||||
final ResourceOperation operation = resource.getMetaData().getOperation(getHttpMethod());
|
}
|
||||||
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), DEFAULT_JSON_CONTENT,CACHE_NEVER);
|
}
|
||||||
|
else
|
||||||
// MNT-20308 - allow write transactions for authentication api
|
{
|
||||||
RetryingTransactionHelper transHelper = getTransactionHelper(resource.getMetaData().getApi().getName());
|
renderJsonResponse(res, toSerialize, assistant.getJsonHelper());
|
||||||
|
}
|
||||||
Object toReturn = transHelper.doInTransaction(
|
}
|
||||||
new RetryingTransactionHelper.RetryingTransactionCallback<Object>()
|
|
||||||
{
|
}
|
||||||
@Override
|
catch (AlfrescoRuntimeException | ApiException | WebScriptException xception )
|
||||||
public Object execute() throws Throwable
|
{
|
||||||
{
|
renderException(xception, res, assistant);
|
||||||
|
}
|
||||||
Object result = executeAction(resource, params, callBack);
|
catch (RuntimeException runtimeException)
|
||||||
if (result instanceof BinaryResource)
|
{
|
||||||
{
|
renderException(runtimeException, res, assistant);
|
||||||
return result; //don't postprocess it.
|
}
|
||||||
}
|
finally
|
||||||
return helper.processAdditionsToTheResponse(res, resource.getMetaData().getApi(), entityCollectionName, params, result);
|
{
|
||||||
}
|
reportExecutionTimeMetric(startTime, req.getServicePath());
|
||||||
}, isReadOnly, true);
|
}
|
||||||
setResponse(res,callBack);
|
}
|
||||||
return toReturn;
|
|
||||||
}
|
public Object execute(final ResourceWithMetadata resource, final Params params, final WebScriptResponse res, boolean isReadOnly)
|
||||||
|
{
|
||||||
protected RetryingTransactionHelper getTransactionHelper(String api)
|
final String entityCollectionName = ResourceInspector.findEntityCollectionNameName(resource.getMetaData());
|
||||||
{
|
final ResourceOperation operation = resource.getMetaData().getOperation(getHttpMethod());
|
||||||
RetryingTransactionHelper transHelper = transactionService.getRetryingTransactionHelper();
|
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), DEFAULT_JSON_CONTENT,CACHE_NEVER);
|
||||||
if (api.equals("authentication"))
|
|
||||||
{
|
// MNT-20308 - allow write transactions for authentication api
|
||||||
transHelper.setForceWritable(true);
|
RetryingTransactionHelper transHelper = getTransactionHelper(resource.getMetaData().getApi().getName());
|
||||||
}
|
|
||||||
return transHelper;
|
Object toReturn = transHelper.doInTransaction(
|
||||||
}
|
new RetryingTransactionHelper.RetryingTransactionCallback<Object>()
|
||||||
|
{
|
||||||
protected void streamResponse(final WebScriptRequest req, final WebScriptResponse res, BinaryResource resource) throws IOException
|
@Override
|
||||||
{
|
public Object execute() throws Throwable
|
||||||
if (resource instanceof FileBinaryResource)
|
{
|
||||||
{
|
|
||||||
FileBinaryResource fileResource = (FileBinaryResource) resource;
|
Object result = executeAction(resource, params, callBack);
|
||||||
// if requested, set attachment
|
if (result instanceof BinaryResource)
|
||||||
boolean attach = StringUtils.isNotEmpty(fileResource.getAttachFileName());
|
{
|
||||||
Map<String, Object> model = getModelForCacheDirective(fileResource.getCacheDirective());
|
return result; //don't postprocess it.
|
||||||
streamer.streamContent(req, res, fileResource.getFile(), null, attach, fileResource.getAttachFileName(), model);
|
}
|
||||||
}
|
return helper.processAdditionsToTheResponse(res, resource.getMetaData().getApi(), entityCollectionName, params, result);
|
||||||
else if (resource instanceof NodeBinaryResource)
|
}
|
||||||
{
|
}, isReadOnly, false);
|
||||||
NodeBinaryResource nodeResource = (NodeBinaryResource) resource;
|
setResponse(res,callBack);
|
||||||
ContentInfo contentInfo = nodeResource.getContentInfo();
|
return toReturn;
|
||||||
setContentInfoOnResponse(res, contentInfo);
|
}
|
||||||
// if requested, set attachment
|
|
||||||
boolean attach = StringUtils.isNotEmpty(nodeResource.getAttachFileName());
|
protected RetryingTransactionHelper getTransactionHelper(String api)
|
||||||
Map<String, Object> model = getModelForCacheDirective(nodeResource.getCacheDirective());
|
{
|
||||||
streamer.streamContent(req, res, nodeResource.getNodeRef(), nodeResource.getPropertyQName(), attach, nodeResource.getAttachFileName(), model);
|
RetryingTransactionHelper transHelper = transactionService.getRetryingTransactionHelper();
|
||||||
}
|
if (api.equals("authentication"))
|
||||||
|
{
|
||||||
}
|
transHelper.setForceWritable(true);
|
||||||
|
}
|
||||||
private void reportExecutionTimeMetric(final long startTime, final String servicePath)
|
return transHelper;
|
||||||
{
|
}
|
||||||
try
|
|
||||||
{
|
protected void streamResponse(final WebScriptRequest req, final WebScriptResponse res, BinaryResource resource) throws IOException
|
||||||
final RestMetricsReporter restMetricsReporter = assistant.getRestMetricsReporter();
|
{
|
||||||
if (restMetricsReporter != null)
|
if (resource instanceof FileBinaryResource)
|
||||||
{
|
{
|
||||||
long delta = System.currentTimeMillis() - startTime;
|
FileBinaryResource fileResource = (FileBinaryResource) resource;
|
||||||
restMetricsReporter.reportRestRequestExecutionTime(delta, httpMethod.toString(), servicePath);
|
// if requested, set attachment
|
||||||
}
|
boolean attach = StringUtils.isNotEmpty(fileResource.getAttachFileName());
|
||||||
}
|
Map<String, Object> model = getModelForCacheDirective(fileResource.getCacheDirective());
|
||||||
catch (Exception e)
|
streamer.streamContent(req, res, fileResource.getFile(), null, attach, fileResource.getAttachFileName(), model);
|
||||||
{
|
}
|
||||||
if (logger.isDebugEnabled())
|
else if (resource instanceof NodeBinaryResource)
|
||||||
{
|
{
|
||||||
logger.debug("Could not report rest api metric:" + e.getMessage(), e);
|
NodeBinaryResource nodeResource = (NodeBinaryResource) resource;
|
||||||
}
|
ContentInfo contentInfo = nodeResource.getContentInfo();
|
||||||
}
|
setContentInfoOnResponse(res, contentInfo);
|
||||||
}
|
// if requested, set attachment
|
||||||
|
boolean attach = StringUtils.isNotEmpty(nodeResource.getAttachFileName());
|
||||||
private static Map<String, Object> getModelForCacheDirective(CacheDirective cacheDirective)
|
Map<String, Object> model = getModelForCacheDirective(nodeResource.getCacheDirective());
|
||||||
{
|
streamer.streamContent(req, res, nodeResource.getNodeRef(), nodeResource.getPropertyQName(), attach, nodeResource.getAttachFileName(), model);
|
||||||
if (cacheDirective != null)
|
}
|
||||||
{
|
|
||||||
return Collections.singletonMap(ContentStreamer.KEY_CACHE_DIRECTIVE, (Object) cacheDirective);
|
}
|
||||||
}
|
|
||||||
return null;
|
private void reportExecutionTimeMetric(final long startTime, final String servicePath)
|
||||||
}
|
{
|
||||||
|
try
|
||||||
public void setLocator(ResourceLocator locator)
|
{
|
||||||
{
|
final RestMetricsReporter restMetricsReporter = assistant.getRestMetricsReporter();
|
||||||
this.locator = locator;
|
if (restMetricsReporter != null)
|
||||||
}
|
{
|
||||||
|
long delta = System.currentTimeMillis() - startTime;
|
||||||
public void setHttpMethod(HttpMethod httpMethod)
|
restMetricsReporter.reportRestRequestExecutionTime(delta, httpMethod.toString(), servicePath);
|
||||||
{
|
}
|
||||||
this.httpMethod = httpMethod;
|
}
|
||||||
}
|
catch (Exception e)
|
||||||
|
{
|
||||||
public void setParamsExtractor(ParamsExtractor paramsExtractor)
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
this.paramsExtractor = paramsExtractor;
|
logger.debug("Could not report rest api metric:" + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public void setHelper(ResourceWebScriptHelper helper)
|
}
|
||||||
{
|
|
||||||
this.helper = helper;
|
private static Map<String, Object> getModelForCacheDirective(CacheDirective cacheDirective)
|
||||||
}
|
{
|
||||||
|
if (cacheDirective != null)
|
||||||
public HttpMethod getHttpMethod()
|
{
|
||||||
{
|
return Collections.singletonMap(ContentStreamer.KEY_CACHE_DIRECTIVE, (Object) cacheDirective);
|
||||||
return this.httpMethod;
|
}
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
public void setStreamer(ContentStreamer streamer)
|
|
||||||
{
|
public void setLocator(ResourceLocator locator)
|
||||||
this.streamer = streamer;
|
{
|
||||||
}
|
this.locator = locator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHttpMethod(HttpMethod httpMethod)
|
||||||
|
{
|
||||||
|
this.httpMethod = httpMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsExtractor(ParamsExtractor paramsExtractor)
|
||||||
|
{
|
||||||
|
this.paramsExtractor = paramsExtractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHelper(ResourceWebScriptHelper helper)
|
||||||
|
{
|
||||||
|
this.helper = helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpMethod getHttpMethod()
|
||||||
|
{
|
||||||
|
return this.httpMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStreamer(ContentStreamer streamer)
|
||||||
|
{
|
||||||
|
this.streamer = streamer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,147 +1,149 @@
|
|||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* Alfresco Remote API
|
* Alfresco Remote API
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
* the paid license agreement will prevail. Otherwise, the software is
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
* provided under the following open source license terms:
|
* provided under the following open source license terms:
|
||||||
*
|
*
|
||||||
* Alfresco is free software: you can redistribute it and/or modify
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* Alfresco is distributed in the hope that it will be useful,
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Lesser General Public License for more details.
|
* GNU Lesser General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package org.alfresco.rest.framework.webscripts;
|
package org.alfresco.rest.framework.webscripts;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.alfresco.repo.web.scripts.BufferedRequest;
|
import org.alfresco.repo.web.scripts.BufferedRequest;
|
||||||
import org.alfresco.repo.web.scripts.BufferedResponse;
|
import org.alfresco.repo.web.scripts.BufferedResponse;
|
||||||
import org.alfresco.rest.framework.Api;
|
import org.alfresco.repo.web.scripts.TempOutputStreamFactory;
|
||||||
import org.alfresco.rest.framework.tools.ApiAssistant;
|
import org.alfresco.rest.framework.Api;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.rest.framework.tools.ApiAssistant;
|
||||||
import org.alfresco.util.TempFileProvider;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.apache.chemistry.opencmis.server.shared.TempStoreOutputStreamFactory;
|
import org.alfresco.util.TempFileProvider;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.extensions.webscripts.AbstractWebScript;
|
import org.springframework.extensions.webscripts.AbstractWebScript;
|
||||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||||
import org.springframework.extensions.webscripts.WebScriptResponse;
|
import org.springframework.extensions.webscripts.WebScriptResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for API webscript. Supports version/scope as well
|
* Entry point for API webscript. Supports version/scope as well
|
||||||
* as discovery.
|
* as discovery.
|
||||||
*
|
*
|
||||||
* @author Gethin James
|
* @author Gethin James
|
||||||
*/
|
*/
|
||||||
public abstract class ApiWebScript extends AbstractWebScript
|
public abstract class ApiWebScript extends AbstractWebScript
|
||||||
{
|
{
|
||||||
private static Log logger = LogFactory.getLog(ApiWebScript.class);
|
private static Log logger = LogFactory.getLog(ApiWebScript.class);
|
||||||
protected ApiAssistant assistant;
|
protected ApiAssistant assistant;
|
||||||
protected boolean encryptTempFiles = false;
|
protected boolean encryptTempFiles = false;
|
||||||
protected String tempDirectoryName = null;
|
protected String tempDirectoryName = null;
|
||||||
protected int memoryThreshold = 4 * 1024 * 1024; // 4mb
|
protected int memoryThreshold = 4 * 1024 * 1024; // 4mb
|
||||||
protected long maxContentSize = (long) 4 * 1024 * 1024 * 1024; // 4gb
|
protected long maxContentSize = (long) 4 * 1024 * 1024 * 1024; // 4gb
|
||||||
protected TempStoreOutputStreamFactory streamFactory = null;
|
protected TempOutputStreamFactory streamFactory = null;
|
||||||
protected TransactionService transactionService;
|
protected TempOutputStreamFactory responseStreamFactory = null;
|
||||||
|
protected TransactionService transactionService;
|
||||||
public void setTransactionService(TransactionService transactionService)
|
|
||||||
{
|
public void setTransactionService(TransactionService transactionService)
|
||||||
this.transactionService = transactionService;
|
{
|
||||||
}
|
this.transactionService = transactionService;
|
||||||
|
}
|
||||||
public void setAssistant(ApiAssistant assistant) {
|
|
||||||
this.assistant = assistant;
|
public void setAssistant(ApiAssistant assistant) {
|
||||||
}
|
this.assistant = assistant;
|
||||||
|
}
|
||||||
public void setTempDirectoryName(String tempDirectoryName)
|
|
||||||
{
|
public void setTempDirectoryName(String tempDirectoryName)
|
||||||
this.tempDirectoryName = tempDirectoryName;
|
{
|
||||||
}
|
this.tempDirectoryName = tempDirectoryName;
|
||||||
|
}
|
||||||
public void setEncryptTempFiles(boolean encryptTempFiles)
|
|
||||||
{
|
public void setEncryptTempFiles(boolean encryptTempFiles)
|
||||||
this.encryptTempFiles = encryptTempFiles;
|
{
|
||||||
}
|
this.encryptTempFiles = encryptTempFiles;
|
||||||
|
}
|
||||||
public void setMemoryThreshold(int memoryThreshold)
|
|
||||||
{
|
public void setMemoryThreshold(int memoryThreshold)
|
||||||
this.memoryThreshold = memoryThreshold;
|
{
|
||||||
}
|
this.memoryThreshold = memoryThreshold;
|
||||||
|
}
|
||||||
public void setMaxContentSize(long maxContentSize)
|
|
||||||
{
|
public void setMaxContentSize(long maxContentSize)
|
||||||
this.maxContentSize = maxContentSize;
|
{
|
||||||
}
|
this.maxContentSize = maxContentSize;
|
||||||
|
}
|
||||||
public void setStreamFactory(TempStoreOutputStreamFactory streamFactory)
|
|
||||||
{
|
public void setStreamFactory(TempOutputStreamFactory streamFactory)
|
||||||
this.streamFactory = streamFactory;
|
{
|
||||||
}
|
this.streamFactory = streamFactory;
|
||||||
|
}
|
||||||
public void init()
|
|
||||||
{
|
public void init()
|
||||||
File tempDirectory = TempFileProvider.getTempDir(tempDirectoryName);
|
{
|
||||||
this.streamFactory = TempStoreOutputStreamFactory.newInstance(tempDirectory, memoryThreshold, maxContentSize, encryptTempFiles);
|
File tempDirectory = TempFileProvider.getTempDir(tempDirectoryName);
|
||||||
}
|
this.streamFactory = new TempOutputStreamFactory(tempDirectory, memoryThreshold, maxContentSize, false, false);
|
||||||
|
this.responseStreamFactory = new TempOutputStreamFactory(tempDirectory, memoryThreshold, maxContentSize, false, true);
|
||||||
@Override
|
}
|
||||||
public void execute(final WebScriptRequest req, final WebScriptResponse res) throws IOException
|
|
||||||
{
|
@Override
|
||||||
Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
|
public void execute(final WebScriptRequest req, final WebScriptResponse res) throws IOException
|
||||||
Api api = assistant.determineApi(templateVars);
|
{
|
||||||
|
Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
|
||||||
final BufferedRequest bufferedReq = getRequest(req);
|
Api api = assistant.determineApi(templateVars);
|
||||||
final BufferedResponse bufferedRes = getResponse(res);
|
|
||||||
|
final BufferedRequest bufferedReq = getRequest(req);
|
||||||
try
|
final BufferedResponse bufferedRes = getResponse(res);
|
||||||
{
|
|
||||||
execute(api, bufferedReq, bufferedRes);
|
try
|
||||||
}
|
{
|
||||||
finally
|
execute(api, bufferedReq, bufferedRes);
|
||||||
{
|
}
|
||||||
// Get rid of any temporary files
|
finally
|
||||||
if (bufferedReq != null)
|
{
|
||||||
{
|
// Get rid of any temporary files
|
||||||
bufferedReq.close();
|
if (bufferedReq != null)
|
||||||
}
|
{
|
||||||
}
|
bufferedReq.close();
|
||||||
|
}
|
||||||
// Ensure a response is always flushed after successful execution
|
}
|
||||||
if (bufferedRes != null)
|
|
||||||
{
|
// Ensure a response is always flushed after successful execution
|
||||||
bufferedRes.writeResponse();
|
if (bufferedRes != null)
|
||||||
}
|
{
|
||||||
}
|
bufferedRes.writeResponse();
|
||||||
|
}
|
||||||
protected BufferedRequest getRequest(final WebScriptRequest req)
|
}
|
||||||
{
|
|
||||||
// create buffered request and response that allow transaction retrying
|
protected BufferedRequest getRequest(final WebScriptRequest req)
|
||||||
final BufferedRequest bufferedReq = new BufferedRequest(req, streamFactory);
|
{
|
||||||
return bufferedReq;
|
// create buffered request and response that allow transaction retrying
|
||||||
}
|
final BufferedRequest bufferedReq = new BufferedRequest(req, streamFactory);
|
||||||
|
return bufferedReq;
|
||||||
protected BufferedResponse getResponse(final WebScriptResponse resp)
|
}
|
||||||
{
|
|
||||||
// create buffered request and response that allow transaction retrying
|
protected BufferedResponse getResponse(final WebScriptResponse resp)
|
||||||
final BufferedResponse bufferedRes = new BufferedResponse(resp, memoryThreshold, streamFactory);
|
{
|
||||||
return bufferedRes;
|
// create buffered request and response that allow transaction retrying
|
||||||
}
|
final BufferedResponse bufferedRes = new BufferedResponse(resp, memoryThreshold, streamFactory);
|
||||||
|
return bufferedRes;
|
||||||
public abstract void execute(final Api api, WebScriptRequest req, WebScriptResponse res) throws IOException;
|
}
|
||||||
|
|
||||||
}
|
public abstract void execute(final Api api, WebScriptRequest req, WebScriptResponse res) throws IOException;
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -67,7 +67,8 @@ import org.junit.runners.Suite;
|
|||||||
org.alfresco.rest.api.tests.TestPublicApiCaching.class,
|
org.alfresco.rest.api.tests.TestPublicApiCaching.class,
|
||||||
org.alfresco.rest.api.tests.TestUserPreferences.class,
|
org.alfresco.rest.api.tests.TestUserPreferences.class,
|
||||||
org.alfresco.rest.api.tests.WherePredicateApiTest.class,
|
org.alfresco.rest.api.tests.WherePredicateApiTest.class,
|
||||||
org.alfresco.rest.api.tests.TestRemovePermissions.class,
|
org.alfresco.rest.api.tests.TestRemovePermissions.class,
|
||||||
|
org.alfresco.rest.api.tests.TempOutputStreamTest.class,
|
||||||
org.alfresco.rest.api.tests.BufferedResponseTest.class,
|
org.alfresco.rest.api.tests.BufferedResponseTest.class,
|
||||||
org.alfresco.rest.workflow.api.tests.DeploymentWorkflowApiTest.class,
|
org.alfresco.rest.workflow.api.tests.DeploymentWorkflowApiTest.class,
|
||||||
org.alfresco.rest.workflow.api.tests.ProcessDefinitionWorkflowApiTest.class,
|
org.alfresco.rest.workflow.api.tests.ProcessDefinitionWorkflowApiTest.class,
|
||||||
|
@@ -1,90 +1,91 @@
|
|||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* Alfresco Remote API
|
* Alfresco Remote API
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
* the paid license agreement will prevail. Otherwise, the software is
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
* provided under the following open source license terms:
|
* provided under the following open source license terms:
|
||||||
*
|
*
|
||||||
* Alfresco is free software: you can redistribute it and/or modify
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* Alfresco is distributed in the hope that it will be useful,
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Lesser General Public License for more details.
|
* GNU Lesser General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package org.alfresco.rest.api.tests;
|
package org.alfresco.rest.api.tests;
|
||||||
|
|
||||||
|
|
||||||
import org.alfresco.rest.DeletedNodesTest;
|
import org.alfresco.rest.DeletedNodesTest;
|
||||||
import org.alfresco.rest.api.search.BasicSearchApiIntegrationTest;
|
import org.alfresco.rest.api.search.BasicSearchApiIntegrationTest;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Suite;
|
import org.junit.runners.Suite;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public V1 REST API tests
|
* Public V1 REST API tests
|
||||||
*
|
*
|
||||||
* @author steveglover
|
* @author steveglover
|
||||||
* @author janv
|
* @author janv
|
||||||
* @author Jamal Kaabi-Mofrad
|
* @author Jamal Kaabi-Mofrad
|
||||||
* @author Gethin James
|
* @author Gethin James
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@RunWith(Suite.class)
|
@RunWith(Suite.class)
|
||||||
@Suite.SuiteClasses({
|
@Suite.SuiteClasses({
|
||||||
|
|
||||||
NodeApiTest.class,
|
NodeApiTest.class,
|
||||||
NodeAssociationsApiTest.class,
|
NodeAssociationsApiTest.class,
|
||||||
NodeVersionsApiTest.class,
|
NodeVersionsApiTest.class,
|
||||||
BasicSearchApiIntegrationTest.class,
|
BasicSearchApiIntegrationTest.class,
|
||||||
QueriesNodesApiTest.class,
|
QueriesNodesApiTest.class,
|
||||||
QueriesPeopleApiTest.class,
|
QueriesPeopleApiTest.class,
|
||||||
QueriesSitesApiTest.class,
|
QueriesSitesApiTest.class,
|
||||||
RenditionsTest.class,
|
RenditionsTest.class,
|
||||||
SharedLinkApiTest.class,
|
SharedLinkApiTest.class,
|
||||||
ActivitiesPostingTest.class,
|
ActivitiesPostingTest.class,
|
||||||
DeletedNodesTest.class,
|
DeletedNodesTest.class,
|
||||||
AuthenticationsTest.class,
|
AuthenticationsTest.class,
|
||||||
ModulePackagesApiTest.class,
|
ModulePackagesApiTest.class,
|
||||||
WherePredicateApiTest.class,
|
WherePredicateApiTest.class,
|
||||||
DiscoveryApiTest.class,
|
DiscoveryApiTest.class,
|
||||||
TestSites.class,
|
TestSites.class,
|
||||||
TestNodeComments.class,
|
TestNodeComments.class,
|
||||||
TestFavouriteSites.class,
|
TestFavouriteSites.class,
|
||||||
TestSiteContainers.class,
|
TestSiteContainers.class,
|
||||||
TestNodeRatings.class,
|
TestNodeRatings.class,
|
||||||
TestUserPreferences.class,
|
TestUserPreferences.class,
|
||||||
TestTags.class,
|
TestTags.class,
|
||||||
TestNetworks.class,
|
TestNetworks.class,
|
||||||
TestActivities.class,
|
TestActivities.class,
|
||||||
GroupsTest.class,
|
GroupsTest.class,
|
||||||
TestPeople.class,
|
TestPeople.class,
|
||||||
TestSiteMembers.class,
|
TestSiteMembers.class,
|
||||||
TestPersonSites.class,
|
TestPersonSites.class,
|
||||||
TestSiteMembershipRequests.class,
|
TestSiteMembershipRequests.class,
|
||||||
TestFavourites.class,
|
TestFavourites.class,
|
||||||
TestPublicApi128.class,
|
TestPublicApi128.class,
|
||||||
TestPublicApiCaching.class,
|
TestPublicApiCaching.class,
|
||||||
TestDownloads.class,
|
TestDownloads.class,
|
||||||
AuditAppTest.class,
|
AuditAppTest.class,
|
||||||
BufferedResponseTest.class
|
TempOutputStreamTest.class,
|
||||||
})
|
BufferedResponseTest.class
|
||||||
public class ApiTest
|
})
|
||||||
{
|
public class ApiTest
|
||||||
@AfterClass
|
{
|
||||||
public static void after() throws Exception
|
@AfterClass
|
||||||
{
|
public static void after() throws Exception
|
||||||
// EnterprisePublicApiTestFixture.cleanup();
|
{
|
||||||
}
|
// EnterprisePublicApiTestFixture.cleanup();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@@ -27,8 +27,9 @@
|
|||||||
package org.alfresco.rest.api.tests;
|
package org.alfresco.rest.api.tests;
|
||||||
|
|
||||||
import org.alfresco.repo.web.scripts.BufferedResponse;
|
import org.alfresco.repo.web.scripts.BufferedResponse;
|
||||||
|
import org.alfresco.repo.web.scripts.TempOutputStream;
|
||||||
|
import org.alfresco.repo.web.scripts.TempOutputStreamFactory;
|
||||||
import org.alfresco.util.TempFileProvider;
|
import org.alfresco.util.TempFileProvider;
|
||||||
import org.apache.chemistry.opencmis.server.shared.TempStoreOutputStreamFactory;
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -54,7 +55,7 @@ public class BufferedResponseTest
|
|||||||
|
|
||||||
private static final String TEMP_DIRECTORY_NAME = "testLargeFile";
|
private static final String TEMP_DIRECTORY_NAME = "testLargeFile";
|
||||||
private static final String LARGE_FILE_NAME = "largeFile.tmp";
|
private static final String LARGE_FILE_NAME = "largeFile.tmp";
|
||||||
private static final String FILE_PREFIX = "opencmis";
|
private static final String FILE_PREFIX = TempOutputStream.TEMP_FILE_PREFIX;
|
||||||
|
|
||||||
private static final Integer LARGE_FILE_SIZE_BYTES = 5 * 1024 * 1024;
|
private static final Integer LARGE_FILE_SIZE_BYTES = 5 * 1024 * 1024;
|
||||||
private static final Integer MEMORY_THRESHOLD = 4 * 1024 * 1024;
|
private static final Integer MEMORY_THRESHOLD = 4 * 1024 * 1024;
|
||||||
@@ -81,12 +82,14 @@ public class BufferedResponseTest
|
|||||||
public void testOutputStream() throws IOException
|
public void testOutputStream() throws IOException
|
||||||
{
|
{
|
||||||
File bufferTempDirectory = TempFileProvider.getTempDir(TEMP_DIRECTORY_NAME);
|
File bufferTempDirectory = TempFileProvider.getTempDir(TEMP_DIRECTORY_NAME);
|
||||||
TempStoreOutputStreamFactory streamFactory = TempStoreOutputStreamFactory.newInstance(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE,false);
|
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false,true);
|
||||||
BufferedResponse response = new BufferedResponse(null, 0, streamFactory);
|
BufferedResponse response = new BufferedResponse(null, 0, streamFactory);
|
||||||
|
|
||||||
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX );
|
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX );
|
||||||
copyFileToOutputStream(response);
|
copyFileToOutputStream(response);
|
||||||
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
|
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
|
||||||
|
|
||||||
|
response.getOutputStream().close();
|
||||||
|
|
||||||
Assert.assertEquals(countBefore + 1, countAfter);
|
Assert.assertEquals(countBefore + 1, countAfter);
|
||||||
|
|
||||||
|
@@ -37,6 +37,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -2783,6 +2784,131 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
|
|||||||
post(postUrl, toJsonAsStringNonNull(d1), "?"+Nodes.PARAM_AUTO_RENAME+"=false", 409);
|
post(postUrl, toJsonAsStringNonNull(d1), "?"+Nodes.PARAM_AUTO_RENAME+"=false", 409);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateNodeConcurrentlyUsingInMemoryBacked() throws Exception
|
||||||
|
{
|
||||||
|
// Less than its memory threshold ( 4 MB )
|
||||||
|
updateNodeConcurrently(1024L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateNodeConcurrentlyUsingFileBacked() throws Exception
|
||||||
|
{
|
||||||
|
// Bigger than its memory threshold ( 5 > 4 MB )
|
||||||
|
updateNodeConcurrently(5 * 1024 * 1024L);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNodeConcurrently(Long contentSize) throws Exception
|
||||||
|
{
|
||||||
|
setRequestContext(user1);
|
||||||
|
|
||||||
|
// Create folder
|
||||||
|
String folder0Name = "f0-testUpdateNodeConcurrently-" + RUNID;
|
||||||
|
String f0Id = createFolder(Nodes.PATH_MY, folder0Name).getId();
|
||||||
|
|
||||||
|
// Create empty file
|
||||||
|
Document d1 = new Document();
|
||||||
|
d1.setName("d1.txt");
|
||||||
|
d1.setNodeType(TYPE_CM_CONTENT);
|
||||||
|
|
||||||
|
Map params = new HashMap<>();
|
||||||
|
params.put("majorVersion", "true");
|
||||||
|
|
||||||
|
Document documentResp = createEmptyTextFile(f0Id, d1.getName(), params, 201);
|
||||||
|
assertEquals("1.0", documentResp.getProperties().get("cm:versionLabel"));
|
||||||
|
String docId = documentResp.getId();
|
||||||
|
|
||||||
|
// Store the threads so that we can check if they are done
|
||||||
|
List<Thread> threads = new ArrayList<Thread>();
|
||||||
|
|
||||||
|
// Create threads
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
Runnable task = new UpdateNodeRunnable(docId, contentSize);
|
||||||
|
|
||||||
|
Thread worker = new Thread(task);
|
||||||
|
worker.setName(String.valueOf(i));
|
||||||
|
worker.start();
|
||||||
|
|
||||||
|
// Remember the thread for later usage
|
||||||
|
threads.add(worker);
|
||||||
|
}
|
||||||
|
int running = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
running = 0;
|
||||||
|
for (Thread thread : threads)
|
||||||
|
{
|
||||||
|
if (thread.isAlive())
|
||||||
|
{
|
||||||
|
running++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (running > 0);
|
||||||
|
|
||||||
|
HttpResponse response = getSingle(URL_NODES, docId, 200);
|
||||||
|
documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
|
||||||
|
|
||||||
|
assertTrue("File size is 0 bytes", documentResp.getContent().getSizeInBytes().intValue() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UpdateNodeRunnable implements Runnable
|
||||||
|
{
|
||||||
|
private final String docId;
|
||||||
|
private final Long contentSize;
|
||||||
|
|
||||||
|
UpdateNodeRunnable(String docId, Long contentSize)
|
||||||
|
{
|
||||||
|
this.docId = docId;
|
||||||
|
this.contentSize = contentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
setRequestContext(user1);
|
||||||
|
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("majorVersion", "true");
|
||||||
|
|
||||||
|
Document documentResp = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
documentResp = updateTextFileWithRandomContent(docId, contentSize, params);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
assertTrue(documentResp.getAspectNames().contains("cm:versionable"));
|
||||||
|
assertNotNull(documentResp.getProperties());
|
||||||
|
|
||||||
|
assertEquals(contentSize, documentResp.getContent().getSizeInBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Document updateTextFileWithRandomContent(String contentId, Long contentSize, Map<String, String> params) throws Exception
|
||||||
|
{
|
||||||
|
return updateTextFileWithRandomContent(contentId, contentSize, params, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Document updateTextFileWithRandomContent(String contentId, Long contentSize, Map<String, String> params, int expectedStatus) throws Exception
|
||||||
|
{
|
||||||
|
File txtFile = TempFileProvider.createTempFile(getClass().getSimpleName(), ".txt");
|
||||||
|
RandomAccessFile file = new RandomAccessFile(txtFile.getPath(), "rw");
|
||||||
|
file.setLength(contentSize);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
BinaryPayload payload = new BinaryPayload(txtFile);
|
||||||
|
|
||||||
|
HttpResponse response = putBinary(getNodeContentUrl(contentId), payload, null, params, expectedStatus);
|
||||||
|
if (expectedStatus != 200)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests update node info (file or folder)
|
* Tests update node info (file or folder)
|
||||||
* <p>PUT:</p>
|
* <p>PUT:</p>
|
||||||
|
@@ -0,0 +1,252 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Remote API
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2018 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco 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, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.rest.api.tests;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.alfresco.repo.content.ContentLimitViolationException;
|
||||||
|
import org.alfresco.repo.web.scripts.TempOutputStream;
|
||||||
|
import org.alfresco.repo.web.scripts.TempOutputStreamFactory;
|
||||||
|
import org.alfresco.util.TempFileProvider;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests basic {@link TempOutputStream} functionality
|
||||||
|
*/
|
||||||
|
public class TempOutputStreamTest
|
||||||
|
{
|
||||||
|
private static final String TEMP_DIRECTORY_NAME = "TempOutputStreamTest";
|
||||||
|
private static final String FILE_PREFIX = TempOutputStream.TEMP_FILE_PREFIX;
|
||||||
|
private static final int MEMORY_THRESHOLD = 4 * 1024 * 1024;
|
||||||
|
private static final long MAX_CONTENT_SIZE = 1024 * 1024 * 1024;
|
||||||
|
private static final File bufferTempDirectory = TempFileProvider.getTempDir(TEMP_DIRECTORY_NAME);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInMemoryStream() throws IOException
|
||||||
|
{
|
||||||
|
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, false);
|
||||||
|
|
||||||
|
File file = createTextFileWithRandomContent(MEMORY_THRESHOLD - 1024L);
|
||||||
|
{
|
||||||
|
TempOutputStream outputStream = streamFactory.createOutputStream();
|
||||||
|
|
||||||
|
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
|
||||||
|
// Copy the stream
|
||||||
|
StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), outputStream);
|
||||||
|
|
||||||
|
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
|
||||||
|
Assert.assertEquals(countBefore, countAfter);
|
||||||
|
outputStream.destroy();
|
||||||
|
}
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFileBackedStream() throws IOException
|
||||||
|
{
|
||||||
|
File file = createTextFileWithRandomContent(MEMORY_THRESHOLD + 1024L);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create stream factory that doesn't delete temp file on stream close
|
||||||
|
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, false);
|
||||||
|
TempOutputStream outputStream = streamFactory.createOutputStream();
|
||||||
|
|
||||||
|
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
|
||||||
|
StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), outputStream);
|
||||||
|
|
||||||
|
// Check that temp file was created
|
||||||
|
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore + 1, countAfter);
|
||||||
|
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
// Check that file wasn't deleted on output stream close
|
||||||
|
countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore + 1, countAfter);
|
||||||
|
|
||||||
|
outputStream.destroy();
|
||||||
|
|
||||||
|
// Check that file was deleted
|
||||||
|
countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore, countAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create stream factory that deletes temp file on stream close
|
||||||
|
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, true);
|
||||||
|
TempOutputStream outputStream = streamFactory.createOutputStream();
|
||||||
|
|
||||||
|
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
|
||||||
|
StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), outputStream);
|
||||||
|
|
||||||
|
// Check that temp file was created
|
||||||
|
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore + 1, countAfter);
|
||||||
|
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
// Check that file was deleted on close
|
||||||
|
countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore, countAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMaxContentSize() throws IOException
|
||||||
|
{
|
||||||
|
// In memory stream
|
||||||
|
{
|
||||||
|
long contentSize = MEMORY_THRESHOLD - 512;
|
||||||
|
long maxContentSize = MEMORY_THRESHOLD - 1024;
|
||||||
|
|
||||||
|
File file = createTextFileWithRandomContent(contentSize);
|
||||||
|
|
||||||
|
// Create stream factory that deletes temp file on stream close
|
||||||
|
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false, true);
|
||||||
|
TempOutputStream outputStream = streamFactory.createOutputStream();
|
||||||
|
|
||||||
|
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), outputStream);
|
||||||
|
Assert.fail("Content size limit violation exception was expected");
|
||||||
|
}
|
||||||
|
catch (ContentLimitViolationException e)
|
||||||
|
{
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that file was already deleted on close
|
||||||
|
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore, countAfter);
|
||||||
|
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// File backed stream
|
||||||
|
{
|
||||||
|
long contentSize = MEMORY_THRESHOLD + 1024;
|
||||||
|
long maxContentSize = MEMORY_THRESHOLD + 512;
|
||||||
|
|
||||||
|
File file = createTextFileWithRandomContent(contentSize);
|
||||||
|
|
||||||
|
// Create stream factory that deletes temp file on stream close
|
||||||
|
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false, true);
|
||||||
|
TempOutputStream outputStream = streamFactory.createOutputStream();
|
||||||
|
|
||||||
|
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), outputStream);
|
||||||
|
Assert.fail("Content size limit violation exception was expected");
|
||||||
|
}
|
||||||
|
catch (ContentLimitViolationException e)
|
||||||
|
{
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that file was already deleted on close
|
||||||
|
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore, countAfter);
|
||||||
|
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptContent() throws IOException
|
||||||
|
{
|
||||||
|
File file = createTextFileWithRandomContent(MEMORY_THRESHOLD + 1024L);
|
||||||
|
|
||||||
|
// Create stream factory that doesn't delete temp file on stream close
|
||||||
|
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, true, false);
|
||||||
|
|
||||||
|
TempOutputStream outputStream = streamFactory.createOutputStream();
|
||||||
|
|
||||||
|
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
|
||||||
|
StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), outputStream);
|
||||||
|
|
||||||
|
// Check that temp file was created
|
||||||
|
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore + 1, countAfter);
|
||||||
|
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
// Check that file wasn't deleted on output stream close
|
||||||
|
countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore + 1, countAfter);
|
||||||
|
|
||||||
|
// Compare content
|
||||||
|
String contentWriten = StreamUtils.copyToString(new BufferedInputStream(new FileInputStream(file)), Charset.defaultCharset());
|
||||||
|
String contentRead = StreamUtils.copyToString(outputStream.getInputStream(), Charset.defaultCharset());
|
||||||
|
Assert.assertEquals(contentWriten, contentRead);
|
||||||
|
|
||||||
|
outputStream.destroy();
|
||||||
|
|
||||||
|
// Check that file was deleted
|
||||||
|
countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
|
||||||
|
Assert.assertEquals(countBefore, countAfter);
|
||||||
|
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createTextFileWithRandomContent(long contentSize) throws IOException
|
||||||
|
{
|
||||||
|
File txtFile = TempFileProvider.createTempFile(getClass().getSimpleName(), ".txt");
|
||||||
|
txtFile.deleteOnExit();
|
||||||
|
|
||||||
|
RandomAccessFile file = new RandomAccessFile(txtFile.getPath(), "rw");
|
||||||
|
file.setLength(contentSize);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return txtFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long countFilesInDirectoryWithPrefix(File directory) throws IOException
|
||||||
|
{
|
||||||
|
Stream<File> fileStream = Arrays.stream(directory.listFiles());
|
||||||
|
return fileStream.filter(f -> f.getName().startsWith(FILE_PREFIX)).count();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user