Save point: [skip ci]

* HttpStatus
This commit is contained in:
alandavis
2022-07-15 18:34:23 +01:00
parent ea22605eba
commit e5728d5d44
55 changed files with 940 additions and 575 deletions

View File

@@ -29,21 +29,13 @@ package org.alfresco.transform.base;
import io.micrometer.core.instrument.MeterRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import java.util.Arrays;
import java.util.List;
import static org.alfresco.transform.base.logging.StandardMessages.COMMUNITY_LICENCE;
@SpringBootApplication
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class})

View File

@@ -111,7 +111,7 @@ public class QueueTransformService
catch (TransformException e)
{
logger.error(e.getMessage(), e);
replyWithError(replyToDestinationQueue, HttpStatus.valueOf(e.getStatusCode()),
replyWithError(replyToDestinationQueue, HttpStatus.valueOf(e.getStatusCode().value()),
e.getMessage(), correlationId);
return;
}
@@ -148,23 +148,21 @@ public class QueueTransformService
String message =
"MessageConversionException during T-Request deserialization of message with correlationID "
+ correlationId + ": ";
throw new TransformException(BAD_REQUEST.value(), message + e.getMessage());
throw new TransformException(BAD_REQUEST, message + e.getMessage());
}
catch (JMSException e)
{
String message =
"JMSException during T-Request deserialization of message with correlationID "
+ correlationId + ": ";
throw new TransformException(INTERNAL_SERVER_ERROR.value(),
message + e.getMessage());
throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage());
}
catch (Exception e)
{
String message =
"Exception during T-Request deserialization of message with correlationID "
+ correlationId + ": ";
throw new TransformException(INTERNAL_SERVER_ERROR.value(),
message + e.getMessage());
throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage());
}
}

View File

@@ -195,7 +195,7 @@ public class TransformController
}
@PostMapping(value = ENDPOINT_TRANSFORM, consumes = MULTIPART_FORM_DATA_VALUE)
public StreamingResponseBody transform(HttpServletRequest request,
public ResponseEntity<StreamingResponseBody> transform(HttpServletRequest request,
@RequestParam(value = FILE, required = false) MultipartFile sourceMultipartFile,
@RequestParam(value = SOURCE_MIMETYPE, required = false) String sourceMimetype,
@RequestParam(value = TARGET_MIMETYPE, required = false) String targetMimetype,
@@ -206,7 +206,7 @@ public class TransformController
}
@PostMapping(value = ENDPOINT_TEST, consumes = MULTIPART_FORM_DATA_VALUE)
public StreamingResponseBody testTransform(HttpServletRequest request,
public ResponseEntity<StreamingResponseBody> testTransform(HttpServletRequest request,
@RequestParam(value = FILE, required = false) MultipartFile sourceMultipartFile,
@RequestParam(value = SOURCE_MIMETYPE, required = false) String sourceMimetype,
@RequestParam(value = TARGET_MIMETYPE, required = false) String targetMimetype,
@@ -274,7 +274,7 @@ public class TransformController
throws IOException
{
final String message = e.getMessage();
final int statusCode = e.getStatusCode();
final int statusCode = e.getStatusCode().value();
logger.error(message);
long time = LogEntry.setStatusCodeAndMessage(statusCode, message);

View File

@@ -35,6 +35,7 @@ import org.alfresco.transform.base.util.OutputStreamLengthRecorder;
import org.alfresco.transform.client.model.InternalContext;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.common.ExtensionService;
import org.alfresco.transform.common.TransformException;
import org.alfresco.transform.common.TransformerDebug;
import org.alfresco.transform.messages.TransformRequestValidator;
@@ -43,9 +44,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.validation.DirectFieldBindingResult;
@@ -62,6 +63,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
@@ -70,23 +72,18 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.stream.Collectors.joining;
import static org.alfresco.transform.base.fs.FileManager.TempFileProvider.createTempFile;
import static org.alfresco.transform.base.fs.FileManager.createTargetFile;
import static org.alfresco.transform.base.fs.FileManager.deleteFile;
import static org.alfresco.transform.base.fs.FileManager.getDirectAccessUrlInputStream;
import static org.alfresco.transform.base.fs.FileManager.getFilenameFromContentDisposition;
import static org.alfresco.transform.base.fs.FileManager.save;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_ENCODING;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_EXTENSION;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_MIMETYPE;
import static org.alfresco.transform.common.RequestParamMap.TARGET_ENCODING;
import static org.alfresco.transform.common.RequestParamMap.TARGET_MIMETYPE;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.util.StringUtils.getFilenameExtension;
/**
* Handles the transform requests from either http or a message.
@@ -178,39 +175,44 @@ public class TransformHandler
return probeTestTransform;
}
public StreamingResponseBody handleHttpRequest(HttpServletRequest request, MultipartFile sourceMultipartFile,
String sourceMimetype, String targetMimetype, Map<String, String> requestParameters)
public ResponseEntity<StreamingResponseBody> handleHttpRequest(HttpServletRequest request,
MultipartFile sourceMultipartFile, String sourceMimetype, String targetMimetype,
Map<String, String> requestParameters)
{
if (logger.isDebugEnabled())
return createResponseEntity(targetMimetype, os ->
{
logger.debug("Processing request via HTTP endpoint. Params: sourceMimetype: '{}', targetMimetype: '{}', "
+ "requestParameters: {}", sourceMimetype, targetMimetype, requestParameters);
}
probeTestTransform.incrementTransformerCount();
TransformManagerImpl transformManager = null;
String reference = "e" + httpRequestCount.getAndIncrement();
final String directUrl = requestParameters.getOrDefault(DIRECT_ACCESS_URL, "");
InputStream inputStream = new BufferedInputStream(directUrl.isBlank()
? FileManager.getMultipartFileInputStream(sourceMultipartFile)
: getDirectAccessUrlInputStream(directUrl));
long sourceSizeInBytes = -1L; // TODO pass in t-options or just ignore for http request as the repo will have checked.
Map<String, String> transformOptions = getTransformOptions(requestParameters);
String transformName = getTransformerName(sourceSizeInBytes, sourceMimetype, targetMimetype, transformOptions);
CustomTransformer customTransformer = getCustomTransformer(transformName);
String reference = "e"+httpRequestCount.getAndIncrement();
transformerDebug.pushTransform(reference, sourceMimetype, targetMimetype, sourceSizeInBytes, transformName);
transformerDebug.logOptions(reference, requestParameters);
return os -> {
OutputStreamLengthRecorder outputStream = new OutputStreamLengthRecorder(os);
try
{
TransformManagerImpl transformManager = TransformManagerImpl.builder()
.withRequest(request)
.withSourceMimetype(sourceMimetype)
.withTargetMimetype(targetMimetype)
.withInputStream(inputStream)
.withOutputStream(outputStream)
.build();
if (logger.isDebugEnabled())
{
logger.debug("Processing request via HTTP endpoint. Params: sourceMimetype: '{}', targetMimetype: '{}', "
+ "requestParameters: {}", sourceMimetype, targetMimetype, requestParameters);
}
probeTestTransform.incrementTransformerCount();
final String directUrl = requestParameters.getOrDefault(DIRECT_ACCESS_URL, "");
InputStream inputStream = new BufferedInputStream(directUrl.isBlank() ?
FileManager.getMultipartFileInputStream(sourceMultipartFile) :
getDirectAccessUrlInputStream(directUrl));
long sourceSizeInBytes = -1L; // Ignore for http requests as the Alfresco repo will have checked.
Map<String, String> transformOptions = getTransformOptions(requestParameters);
String transformName = getTransformerName(sourceSizeInBytes, sourceMimetype, targetMimetype, transformOptions);
CustomTransformer customTransformer = getCustomTransformer(transformName);
transformerDebug.pushTransform(reference, sourceMimetype, targetMimetype, sourceSizeInBytes, transformName);
transformerDebug.logOptions(reference, requestParameters);
OutputStreamLengthRecorder outputStream = new OutputStreamLengthRecorder(os);
transformManager = TransformManagerImpl.builder()
.withRequest(request)
.withSourceMimetype(sourceMimetype)
.withTargetMimetype(targetMimetype)
.withInputStream(inputStream)
.withOutputStream(outputStream)
.build();
transformManager.setOutputStream(outputStream);
customTransformer.transform(sourceMimetype, inputStream,
targetMimetype, outputStream, transformOptions, transformManager);
@@ -220,127 +222,122 @@ public class TransformHandler
LogEntry.setTargetSize(outputStream.getLength());
long time = LogEntry.setStatusCodeAndMessage(OK.value(), "Success");
transformManager.deleteSourceFileIfExists();
transformManager.deleteTargetFileIfExists();
probeTestTransform.recordTransformTime(time);
transformerDebug.popTransform(reference, time);
}
catch (TransformException e)
{
transformerDebug.logFailure(reference, e.getMessage());
throw e;
}
catch (Exception e)
{
transformerDebug.logFailure(reference, e.getMessage());
throw new RuntimeException(e);
}
};
finally
{
deleteTmpFiles(transformManager);
}
});
}
public ResponseEntity<TransformReply> handleMessageRequest(TransformRequest request, Long timeout)
{
long start = System.currentTimeMillis();
logger.trace("Received {}, timeout {} ms", request, timeout);
probeTestTransform.incrementTransformerCount();
TransformReply reply = createBasicTransformReply(request);
if (isTransformRequestValid(request, reply) == false)
{
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
}
InputStream inputStream = getInputStream(request, reply);
if (inputStream == null)
{
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
}
InputStream inputStream = null;
TransformManagerImpl transformManager = null;
TransformReply reply = createBasicTransformReply(request);;
try
{
logger.trace("Received {}, timeout {} ms", request, timeout);
probeTestTransform.incrementTransformerCount();
checkTransformRequestValid(request, reply);
inputStream = getInputStream(request, reply);
String targetMimetype = request.getTargetMediaType();
String sourceMimetype = request.getSourceMediaType();
File targetFile = createTargetFile(null, sourceMimetype, targetMimetype);
transformerDebug.pushTransform(request);
long sourceSizeInBytes = request.getSourceSize();
Map<String, String> transformOptions = getTransformOptions(request.getTransformRequestOptions());
transformerDebug.logOptions(request);
String transformName = getTransformerName(sourceSizeInBytes, sourceMimetype, targetMimetype, transformOptions);
CustomTransformer customTransformer = getCustomTransformer(transformName);
try (OutputStreamLengthRecorder outputStream = new OutputStreamLengthRecorder(new BufferedOutputStream(
new FileOutputStream(targetFile))))
{
long sourceSizeInBytes = request.getSourceSize();
Map<String, String> transformOptions = getTransformOptions(request.getTransformRequestOptions());
transformerDebug.logOptions(request);
String transformName = getTransformerName(sourceSizeInBytes, sourceMimetype, targetMimetype, transformOptions);
CustomTransformer customTransformer = getCustomTransformer(transformName);
transformManager = TransformManagerImpl.builder()
.withSourceMimetype(sourceMimetype)
.withTargetMimetype(targetMimetype)
.withInputStream(inputStream)
.withOutputStream(outputStream)
.withTargetFile(targetFile)
.build();
TransformManagerImpl transformManager = TransformManagerImpl.builder()
.withSourceMimetype(sourceMimetype)
.withTargetMimetype(targetMimetype)
.withInputStream(inputStream)
.withOutputStream(outputStream)
.withTargetFile(targetFile)
.build();
customTransformer.transform(sourceMimetype, inputStream,
targetMimetype, outputStream, transformOptions, transformManager);
customTransformer.transform(sourceMimetype, inputStream, targetMimetype, outputStream, transformOptions,
transformManager);
transformManager.ifUsedCopyTargetFileToOutputStream();
reply.getInternalContext().setCurrentSourceSize(outputStream.getLength());
if (saveTargetFileInSharedFileStore(targetFile, reply) == false)
{
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
}
transformManager.deleteSourceFileIfExists();
transformManager.deleteTargetFileIfExists();
probeTestTransform.recordTransformTime(System.currentTimeMillis()-start);
transformerDebug.popTransform(reply);
logger.trace("Sending successful {}, timeout {} ms", reply, timeout);
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
}
catch (TransformException e)
{
reply.setStatus(e.getStatusCode());
reply.setErrorDetails(messageWithCause("Failed at processing transformation", e));
transformerDebug.logFailure(reply);
logger.trace("Failed to perform transform (TransformException), sending " + reply, e);
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
}
catch (Exception e)
{
reply.setStatus(INTERNAL_SERVER_ERROR.value());
reply.setErrorDetails(messageWithCause("Failed at processing transformation", e));
transformerDebug.logFailure(reply);
logger.trace("Failed to perform transform (Exception), sending " + reply, e);
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
saveTargetFileInSharedFileStore(targetFile, reply);
}
}
catch (TransformException e)
{
return createFailedResponseEntity(reply, e, e.getStatusCode().value());
}
catch (Exception e)
{
return createFailedResponseEntity(reply, e, INTERNAL_SERVER_ERROR.value());
}
finally
{
deleteTmpFiles(transformManager);
closeInputStreamWithoutException(inputStream);
probeTestTransform.recordTransformTime(System.currentTimeMillis()-start);
transformerDebug.popTransform(reply);
logger.trace("Sending successful {}, timeout {} ms", reply, timeout);
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
}
}
private boolean isTransformRequestValid(TransformRequest request, TransformReply reply)
private ResponseEntity<TransformReply> createFailedResponseEntity(TransformReply reply, Exception e,
int status) {
reply.setStatus(status);
reply.setErrorDetails(messageWithCause("Transform failed", e));
transformerDebug.logFailure(reply);
logger.trace("Transform failed. Sending " + reply, e);
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
}
private void deleteTmpFiles(TransformManagerImpl transformManager)
{
if (transformManager != null)
{
transformManager.deleteSourceFileIfExists();
transformManager.deleteTargetFileIfExists();
}
}
private void checkTransformRequestValid(TransformRequest request, TransformReply reply)
{
final Errors errors = validateTransformRequest(request);
validateInternalContext(request, errors);
reply.setInternalContext(request.getInternalContext());
if (!errors.getAllErrors().isEmpty())
{
reply.setStatus(BAD_REQUEST.value());
reply.setErrorDetails(errors
.getAllErrors()
.stream()
.map(Object::toString)
.collect(joining(", ")));
transformerDebug.logFailure(reply);
logger.trace("Invalid request, sending {}", reply);
return false;
String errorDetails = errors.getAllErrors().stream().map(Object::toString).collect(joining(", "));
throw new TransformException(BAD_REQUEST, errorDetails);
}
return true;
}
private TransformReply createBasicTransformReply(TransformRequest request)
@@ -393,7 +390,7 @@ public class TransformHandler
{
String message = "Source file with reference: " + sourceReference + " is null or empty.";
logger.warn(message);
throw new TransformException(BAD_REQUEST.value(), message);
throw new TransformException(BAD_REQUEST, message);
}
try
@@ -404,40 +401,30 @@ public class TransformHandler
{
String message = "Shared File Store reference is invalid.";
logger.warn(message);
throw new TransformException(BAD_REQUEST.value(), message, e);
throw new TransformException(BAD_REQUEST, message, e);
}
}
private InputStream getInputStream(TransformRequest request, TransformReply reply)
{
final String directUrl = request.getTransformRequestOptions().getOrDefault(DIRECT_ACCESS_URL, "");
InputStream inputStream = null;
try
{
inputStream = new BufferedInputStream(directUrl.isBlank()
return new BufferedInputStream(directUrl.isBlank()
? getSharedFileStoreInputStream(request.getSourceReference())
: getDirectAccessUrlInputStream(directUrl));
}
catch (TransformException e)
{
reply.setStatus(e.getStatusCode());
reply.setErrorDetails(messageWithCause("Failed at reading the source file", e));
transformerDebug.logFailure(reply);
logger.trace("Failed to load source file (TransformException), sending " + reply);
throw new TransformException(e.getStatusCode(), messageWithCause("Failed to read the source", e));
}
catch (HttpClientErrorException e)
{
reply.setStatus(e.getStatusCode().value());
reply.setErrorDetails(messageWithCause("Failed at reading the source file", e));
transformerDebug.logFailure(reply);
logger.trace("Failed to load source file (HttpClientErrorException), sending " + reply, e);
throw new TransformException(e.getStatusCode(), messageWithCause("Failed to read the source", e));
}
return inputStream;
}
private boolean saveTargetFileInSharedFileStore(File targetFile, TransformReply reply)
private void saveTargetFileInSharedFileStore(File targetFile, TransformReply reply)
{
FileRefResponse targetRef;
try
@@ -446,81 +433,19 @@ public class TransformHandler
}
catch (TransformException e)
{
reply.setStatus(e.getStatusCode());
reply.setErrorDetails(messageWithCause("Failed at writing the transformed file", e));
transformerDebug.logFailure(reply);
logger.trace("Failed to save target file (TransformException), sending " + reply, e);
return false;
throw new TransformException(e.getStatusCode(), messageWithCause("Failed writing to SFS", e));
}
catch (HttpClientErrorException e)
{
reply.setStatus(e.getStatusCode().value());
reply.setErrorDetails(messageWithCause("Failed at writing the transformed file. ", e));
transformerDebug.logFailure(reply);
logger.trace("Failed to save target file (HttpClientErrorException), sending " + reply, e);
return false;
throw new TransformException(e.getStatusCode(), messageWithCause("Failed writing to SFS", e));
}
catch (Exception e)
{
reply.setStatus(INTERNAL_SERVER_ERROR.value());
reply.setErrorDetails(messageWithCause("Failed at writing the transformed file. ", e));
transformerDebug.logFailure(reply);
logger.trace("Failed to save target file (Exception), sending " + reply, e);
return false;
}
try
{
deleteFile(targetFile);
}
catch (Exception e)
{
logger.error("Failed to delete local temp target file. Error will be ignored ", e);
throw new TransformException(INTERNAL_SERVER_ERROR, messageWithCause("Failed writing to SFS", e));
}
reply.setTargetReference(targetRef.getEntry().getFileRef());
reply.setStatus(CREATED.value());
return true;
}
/**
* Loads the file with the specified sourceReference from Alfresco Shared File Store
*
* @param sourceReference reference to the file in Alfresco Shared File Store
* @param sourceExtension default extension if the file in Alfresco Shared File Store has none
* @return the file containing the source content for the transformation
*/
private File loadSourceFile(final String sourceReference, final String sourceExtension)
{
ResponseEntity<Resource> responseEntity = alfrescoSharedFileStoreClient.retrieveFile(sourceReference);
HttpHeaders headers = responseEntity.getHeaders();
String filename = getFilenameFromContentDisposition(headers);
String extension = getFilenameExtension(filename) != null ? getFilenameExtension(filename) : sourceExtension;
MediaType contentType = headers.getContentType();
long size = headers.getContentLength();
final Resource body = responseEntity.getBody();
if (body == null)
{
String message = "Source file with reference: " + sourceReference + " is null or empty. "
+ "Transformation will fail and stop now as there is no content to be transformed.";
logger.warn(message);
throw new TransformException(BAD_REQUEST.value(), message);
}
final File file = createTempFile("source_", "." + extension);
logger.debug("Read source content {} length={} contentType={}",
sourceReference, size, contentType);
save(body, file);
LogEntry.setSource(filename, size);
return file;
}
private static String messageWithCause(final String prefix, Throwable e)
@@ -554,7 +479,7 @@ public class TransformHandler
sourceSizeInBytes, targetMimetype, transformOptions, null);
if (transformerName == null)
{
throw new TransformException(BAD_REQUEST.value(), "No transforms were able to handle the request");
throw new TransformException(BAD_REQUEST, "No transforms were able to handle the request");
}
return transformerName;
}
@@ -572,19 +497,35 @@ public class TransformHandler
CustomTransformer customTransformer = customTransformersByName.get(transformName);
if (customTransformer == null)
{
throw new TransformException(BAD_REQUEST.value(), "Custom Transformer "+customTransformer+" not found");
throw new TransformException(INTERNAL_SERVER_ERROR, "Custom Transformer "+transformName+" not found");
}
return customTransformer;
}
private void closeInputStreamWithoutException(InputStream inputStream) {
try
private void closeInputStreamWithoutException(InputStream inputStream)
{
if (inputStream != null)
{
inputStream.close();
}
catch (IOException e)
{
throw new RuntimeException(e);
try
{
inputStream.close();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
private ResponseEntity<StreamingResponseBody> createResponseEntity(String targetMimetype,
StreamingResponseBody body)
{
String extension = ExtensionService.getExtensionForMimetype(targetMimetype);
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(
ContentDisposition.attachment()
.filename("transform."+ extension, StandardCharsets.UTF_8)
.build());
return ResponseEntity.ok().headers(headers).body(body);
}
}

View File

@@ -70,7 +70,7 @@ public class AlfrescoSharedFileStoreClient
}
catch (HttpClientErrorException e)
{
throw new TransformException(e.getStatusCode().value(), e.getMessage(), e);
throw new TransformException(e.getStatusCode(), e.getMessage(), e);
}
}
@@ -97,7 +97,7 @@ public class AlfrescoSharedFileStoreClient
}
catch (HttpClientErrorException e)
{
throw new TransformException(e.getStatusCode().value(), e.getMessage(), e);
throw new TransformException(e.getStatusCode(), e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* #%L
* Alfresco Transform Core
* %%
* Copyright (C) 2022 - 2022 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.transform.base.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer
{
private static final Logger logger = LoggerFactory.getLogger(SpringAsyncConfig.class);
@Value("${async-task-executor.core-pool-size:1}")
int corePoolSize;
@Value("${async-task-executor.max-pool-size:"+Integer.MAX_VALUE+"}")
int maxPoolSize;
@Value("${async-task-executor.keep-alive-seconds:60}")
int keepAliveSeconds;
@Value("${async-task-executor.queue-capacity:"+Integer.MAX_VALUE+"}")
int queueCapacity;
@Value("${async-task-executor.allow-core-thread-time-out:false}")
boolean allowCoreThreadTimeOut;
@Value("${async-task-executor.prestart-all-core-threads:false}")
boolean prestartAllCoreThreads;
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor()
{
logger.debug("async-task-executor:");
logger.debug(" corePoolSize="+corePoolSize);
logger.debug(" max-pool-size: "+maxPoolSize);
logger.debug(" keep-alive-seconds: "+keepAliveSeconds);
logger.debug(" queue-capacity: "+queueCapacity);
logger.debug(" allow-core-thread-time-out: "+allowCoreThreadTimeOut);
logger.debug(" prestart-all-core-threads: "+prestartAllCoreThreads);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setQueueCapacity(queueCapacity);
executor.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut);
executor.setPrestartAllCoreThreads(prestartAllCoreThreads);
return executor;
}
}

View File

@@ -35,6 +35,7 @@ import org.alfresco.transform.registry.TransformServiceRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -42,7 +43,9 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
@Configuration
@ComponentScan(basePackages = {"org.alfresco.transform"})
@ComponentScan(
basePackages = {"org.alfresco.transform"},
excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Test.*"))
public class WebApplicationConfig implements WebMvcConfigurer
{
@Override

View File

@@ -52,14 +52,12 @@ public abstract class AbstractCommandExecutor implements CommandExecutor
if (result.getExitValue() != 0 && result.getStdErr() != null && result.getStdErr().length() > 0)
{
throw new TransformException(BAD_REQUEST.value(),
"Transformer exit code was not 0: \n" + result.getStdErr());
throw new TransformException(BAD_REQUEST, "Transformer exit code was not 0: \n" + result.getStdErr());
}
if (!targetFile.exists() || targetFile.length() == 0)
{
throw new TransformException(INTERNAL_SERVER_ERROR.value(),
"Transformer failed to create an output file");
throw new TransformException(INTERNAL_SERVER_ERROR, "Transformer failed to create an output file");
}
}
}

View File

@@ -30,8 +30,11 @@ import org.alfresco.transform.base.logging.LogEntry;
import org.alfresco.transform.common.ExtensionService;
import org.alfresco.transform.common.TransformException;
import org.springframework.core.io.Resource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
@@ -39,6 +42,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays;
@@ -71,7 +75,7 @@ public class FileManager
}
catch (Exception e)
{
throw new TransformException(INSUFFICIENT_STORAGE.value(), "Failed to store the source file", e);
throw new TransformException(INSUFFICIENT_STORAGE, "Failed to store the source file", e);
}
}
@@ -90,7 +94,7 @@ public class FileManager
}
catch (Exception e)
{
throw new TransformException(INSUFFICIENT_STORAGE.value(), "Failed to create the target file", e);
throw new TransformException(INSUFFICIENT_STORAGE, "Failed to create the target file", e);
}
}
@@ -110,8 +114,7 @@ public class FileManager
}
catch (IOException e)
{
throw new TransformException(INSUFFICIENT_STORAGE.value(),
"Failed to store the source file", e);
throw new TransformException(INSUFFICIENT_STORAGE, "Failed to store the source file", e);
}
}
@@ -136,7 +139,7 @@ public class FileManager
InputStream inputStream;
if (sourceMultipartFile == null)
{
throw new TransformException(BAD_REQUEST.value(), "Required request part 'file' is not present");
throw new TransformException(BAD_REQUEST, "Required request part 'file' is not present");
}
try
{
@@ -144,7 +147,7 @@ public class FileManager
}
catch (IOException e)
{
throw new TransformException(BAD_REQUEST.value(), "Unable to read the sourceMultipartFile.", e);
throw new TransformException(BAD_REQUEST, "Unable to read the sourceMultipartFile.", e);
}
return inputStream;
}
@@ -157,11 +160,11 @@ public class FileManager
}
catch (IllegalArgumentException e)
{
throw new TransformException(BAD_REQUEST.value(), "Direct Access Url is invalid.", e);
throw new TransformException(BAD_REQUEST, "Direct Access Url is invalid.", e);
}
catch (IOException e)
{
throw new TransformException(BAD_REQUEST.value(), "Direct Access Url not found.", e);
throw new TransformException(BAD_REQUEST, "Direct Access Url not found.", e);
}
}
@@ -173,7 +176,7 @@ public class FileManager
}
catch (IOException e)
{
throw new TransformException(INTERNAL_SERVER_ERROR.value(), "Failed to copy targetFile to outputStream.", e);
throw new TransformException(INTERNAL_SERVER_ERROR, "Failed to copy targetFile to outputStream.", e);
}
}

View File

@@ -238,7 +238,7 @@ public class ProbeTestTransform
if (time > maxTime)
{
throw new TransformException(INTERNAL_SERVER_ERROR.value(),
throw new TransformException(INTERNAL_SERVER_ERROR,
getMessagePrefix(isLiveProbe) +
message + " which is more than " + livenessPercent +
"% slower than the normal value of " + normalTime + "ms");
@@ -260,7 +260,7 @@ public class ProbeTestTransform
sourceSizeInBytes, targetMimetype, transformOptions, null);
if (transformerName == null)
{
throw new TransformException(BAD_REQUEST.value(), "No transforms were able to handle the request");
throw new TransformException(BAD_REQUEST, "No transforms were able to handle the request");
}
return transformerName;
}
@@ -269,14 +269,14 @@ public class ProbeTestTransform
{
if (die.get())
{
throw new TransformException(TOO_MANY_REQUESTS.value(),
throw new TransformException(TOO_MANY_REQUESTS,
getMessagePrefix(isLiveProbe) + "Transformer requested to die. A transform took " +
"longer than " + (maxTransformTime * 1000) + " seconds");
}
if (maxTransformCount > 0 && transformCount.get() > maxTransformCount)
{
throw new TransformException(TOO_MANY_REQUESTS.value(),
throw new TransformException(TOO_MANY_REQUESTS,
getMessagePrefix(isLiveProbe) + "Transformer requested to die. It has performed " +
"more than " + maxTransformCount + " transformations");
}
@@ -293,7 +293,7 @@ public class ProbeTestTransform
}
catch (IOException e)
{
throw new TransformException(INSUFFICIENT_STORAGE.value(),
throw new TransformException(INSUFFICIENT_STORAGE,
getMessagePrefix(isLiveProbe) + "Failed to store the source file", e);
}
long length = sourceFile.length();
@@ -349,13 +349,13 @@ public class ProbeTestTransform
String probeMessage = getProbeMessage(isLiveProbe);
if (!targetFile.exists() || !targetFile.isFile())
{
throw new TransformException(INTERNAL_SERVER_ERROR.value(),
throw new TransformException(INTERNAL_SERVER_ERROR,
probeMessage + "Target File \"" + targetFile.getAbsolutePath() + "\" did not exist");
}
long length = targetFile.length();
if (length < minExpectedLength || length > maxExpectedLength)
{
throw new TransformException(INTERNAL_SERVER_ERROR.value(),
throw new TransformException(INTERNAL_SERVER_ERROR,
probeMessage + "Target File \"" + targetFile.getAbsolutePath() +
"\" was the wrong size (" + length + "). Needed to be between " +
minExpectedLength + " and " + maxExpectedLength);

View File

@@ -59,3 +59,10 @@ management:
container:
name: ${HOSTNAME:t-engine}
async-task-executor:
core-pool-size: 1
max-pool-size: 2147483647
keep-alive-seconds: 60
queue-capacity: 2147483647
allow-core-thread-time-out: false
prestart-all-core-threads: false

View File

@@ -6,27 +6,27 @@
<table>
<tr><td><div style="text-align:right">file</div></td><td><input type="file" name="file" /></td></tr>
<tr><td><div style="text-align:right">Direct Url</div></td><td><input type="text" name="directAccessUrl"/></td></tr>
<tr><td><div style="text-align:right">sourceMimetype</div></td><td><input type="text" name="sourceMimetype" value="image/jpeg" /></td>
<tr><td><div style="text-align:right">sourceMimetype</div></td><td><input type="text" name="sourceMimetype" value="" /></td>
<td><select name="_sourceMimetype">
<option value="" >-- file extension --</option>
<option value="image/jpeg" >jpeg</option>
<option value="image/jpeg" >jpg</option>
<option value="image/png">png</option>
<option value="application/pdf">pdf</option>
<option value="application/vnd.openxmlformats-officedocument.wordprocessingml.document">docx</option>
<option value="application/vnd.openxmlformats-officedocument.presentationml.slideshow">ppsx</option>
<option value="text/html">html</option>
<option value="text/plain">text</option>
<option value="text/plain">txt</option>
</select></td></tr>
<tr><td><div style="text-align:right">targetMimetype</div></td><td><input type="text" name="targetMimetype" value="image/png" /></td>
<tr><td><div style="text-align:right">targetMimetype</div></td><td><input type="text" name="targetMimetype" value="" /></td>
<td><select name="_targetMimetype">
<option value="" >-- file extension --</option>
<option value="image/jpeg" >jpeg</option>
<option value="image/jpeg" >jpg</option>
<option value="image/png">png</option>
<option value="application/pdf">pdf</option>
<option value="application/vnd.openxmlformats-officedocument.wordprocessingml.document">docx</option>
<option value="application/vnd.openxmlformats-officedocument.presentationml.slideshow">ppsx</option>
<option value="text/html">html</option>
<option value="text/plain">text</option>
<option value="text/plain">txt</option>
</select></td></tr>
<tr><td><select name="name0">
<option value="sourceEncoding" >sourceEncoding</option>
@@ -87,7 +87,7 @@
<option value="includeContents">includeContents</option>
<option value="notExtractBookmarksText">notExtractBookmarksText</option>
<option value="pageLimit">pageLimit</option>
</select></td><td><input type="text" name="value1" value="200" /></td></tr>
</select></td><td><input type="text" name="value1" value="" /></td></tr>
<tr><td><select id="name2">
<option value="sourceEncoding">sourceEncoding</option>
<option value="targetEncoding">targetEncoding</option>
@@ -117,7 +117,7 @@
<option value="includeContents">includeContents</option>
<option value="notExtractBookmarksText">notExtractBookmarksText</option>
<option value="pageLimit">pageLimit</option>
</select></td><td><input type="text" name="value2" value="150" /></td></tr>
</select></td><td><input type="text" name="value2" value="" /></td></tr>
<tr><td><select name="name3">
<option value="sourceEncoding">sourceEncoding</option>
<option value="targetEncoding">targetEncoding</option>
@@ -147,7 +147,7 @@
<option value="includeContents">includeContents</option>
<option value="notExtractBookmarksText">notExtractBookmarksText</option>
<option value="pageLimit">pageLimit</option>
</select></td><td><input type="text" name="value3" value="true" /></td></tr>
</select></td><td><input type="text" name="value3" value="" /></td></tr>
<tr><td><select name="name4">
<option value="sourceEncoding">sourceEncoding</option>
<option value="targetEncoding">targetEncoding</option>

View File

@@ -46,6 +46,7 @@ import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.File;
@@ -80,12 +81,15 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@@ -96,10 +100,11 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* Super class for testing controllers without a server. Includes tests for the Controller itself.
* Super class for testing.
*/
//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes={org.alfresco.transform.base.config.WebApplicationConfig.class})
public abstract class AbstractTransformControllerTest
@SpringBootTest(classes={org.alfresco.transform.base.Application.class})
@AutoConfigureMockMvc
public abstract class AbstractBaseTest
{
@TempDir // added as part of ATS-702 to allow test resources to be read from the imported jar files to prevent test resource duplication
public File tempDir;
@@ -132,7 +137,7 @@ public abstract class AbstractTransformControllerTest
protected String expectedOptions;
protected String expectedSourceSuffix;
protected Long expectedTimeout = 0L;
protected byte[] expectedSourceFileBytes;
protected byte[] sourceFileBytes;
/**
* The expected result. Taken resting target quick file's bytes.
@@ -147,18 +152,12 @@ public abstract class AbstractTransformControllerTest
String targetExtension, String sourceMimetype,
boolean readTargetFileBytes) throws IOException;
protected TransformController getController()
{
return controller;
}
protected ProbeTestTransform getProbeTestTransform()
{
return controller.probeTestTransform;
}
protected abstract void updateTransformRequestWithSpecificOptions(
TransformRequest transformRequest);
protected abstract void updateTransformRequestWithSpecificOptions(TransformRequest transformRequest);
/**
* This method ends up being the core of the mock.
@@ -173,10 +172,14 @@ public abstract class AbstractTransformControllerTest
public void generateTargetFileFromResourceFile(String actualTargetExtension, File testFile,
File targetFile) throws IOException
{
if (testFile == null)
{
testFile = getTestFile("quick." + actualTargetExtension, false);
}
if (testFile != null)
{
try (var inputStream = new FileInputStream(testFile);
var outputStream = new FileOutputStream(targetFile))
var outputStream = new FileOutputStream(targetFile))
{
FileChannel source = inputStream.getChannel();
FileChannel target = outputStream.getChannel();
@@ -187,24 +190,6 @@ public abstract class AbstractTransformControllerTest
throw e;
}
}
else
{
testFile = getTestFile("quick." + actualTargetExtension, false);
if (testFile != null)
{
try (var inputStream = new FileInputStream(testFile);
var outputStream = new FileOutputStream(targetFile))
{
FileChannel source = inputStream.getChannel();
FileChannel target = outputStream.getChannel();
target.transferFrom(source, 0, source.size());
} catch (Exception e)
{
throw e;
}
}
}
}
protected byte[] readTestFile(String extension) throws IOException
@@ -233,8 +218,7 @@ public abstract class AbstractTransformControllerTest
return testFileUrl == null ? null : testFile;
}
protected MockHttpServletRequestBuilder mockMvcRequest(String url, MockMultipartFile sourceFile,
String... params)
protected MockHttpServletRequestBuilder mockMvcRequest(String url, MockMultipartFile sourceFile, String... params)
{
if (sourceFile == null)
{
@@ -246,10 +230,9 @@ public abstract class AbstractTransformControllerTest
}
}
private MockHttpServletRequestBuilder mockMvcRequestWithoutMockMultipartFile(String url,
String... params)
private MockHttpServletRequestBuilder mockMvcRequestWithoutMockMultipartFile(String url, String... params)
{
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(url);
if (params.length % 2 != 0)
{
@@ -266,8 +249,7 @@ public abstract class AbstractTransformControllerTest
private MockHttpServletRequestBuilder mockMvcRequestWithMockMultipartFile(String url, MockMultipartFile sourceFile,
String... params)
{
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM).file(
sourceFile);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM).file(sourceFile);
if (params.length % 2 != 0)
{
@@ -309,27 +291,13 @@ public abstract class AbstractTransformControllerTest
public void simpleTransformTest() throws Exception
{
mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value()))
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
}
@Test
public void testDelayTest() throws Exception
{
long start = System.currentTimeMillis();
mockMvc.perform(mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension,
"testDelay", "400"))
.andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
long ms = System.currentTimeMillis() - start;
System.out.println("Transform incluing test delay was " + ms);
assertTrue(ms >= 400, "Delay sending the result back was too small " + ms);
assertTrue(ms <= 500,"Delay sending the result back was too big " + ms);
"attachment; filename*=UTF-8''transform." + targetExtension));
}
@Test
@@ -343,40 +311,42 @@ public abstract class AbstractTransformControllerTest
// Looks dangerous but is okay as we only use the final filename
public void dotDotSourceFilenameTest() throws Exception
{
sourceFile = new MockMultipartFile("file", "../quick." + sourceExtension, sourceMimetype,
expectedSourceFileBytes);
sourceFile = new MockMultipartFile("file", "../quick." + sourceExtension, sourceMimetype, sourceFileBytes);
mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value()))
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
"attachment; filename*=UTF-8''transform." + targetExtension));
}
@Test
// Is okay, as the target filename is built up from the whole source filename and the targetExtension
public void noExtensionSourceFilenameTest() throws Exception
{
sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype,
expectedSourceFileBytes);
sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype, sourceFileBytes);
mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value()))
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
"attachment; filename*=UTF-8''transform." + targetExtension));
}
@Test
// Invalid file name that ends in /
public void badSourceFilenameTest() throws Exception
{
sourceFile = new MockMultipartFile("file", "abc/", sourceMimetype, expectedSourceFileBytes);
sourceFile = new MockMultipartFile("file", "abc/", sourceMimetype, sourceFileBytes);
mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason(containsString("The source filename was not supplied")));
}
@@ -384,26 +354,17 @@ public abstract class AbstractTransformControllerTest
@Test
public void blankSourceFilenameTest() throws Exception
{
sourceFile = new MockMultipartFile("file", "", sourceMimetype, expectedSourceFileBytes);
sourceFile = new MockMultipartFile("file", "", sourceMimetype, sourceFileBytes);
mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(status().is(BAD_REQUEST.value()));
}
@Test
public void noTargetExtensionTest() throws Exception
{
mockMvc.perform(mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason(
containsString("Request parameter 'targetExtension' is missing")));
}
@Test
public void calculateMaxTime() throws Exception
{
ProbeTestTransform probeTestTransform = getController().probeTestTransform;
ProbeTestTransform probeTestTransform = controller.probeTestTransform;
probeTestTransform.setLivenessPercent(110);
long[][] values = new long[][]{
@@ -479,7 +440,9 @@ public abstract class AbstractTransformControllerTest
String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG_LATEST))
.andExpect(status().is(OK.value()))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString();
@@ -500,7 +463,9 @@ public abstract class AbstractTransformControllerTest
String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG))
.andExpect(status().is(OK.value()))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString();
@@ -518,7 +483,9 @@ public abstract class AbstractTransformControllerTest
String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG))
.andExpect(status().is(OK.value()))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString();
@@ -545,7 +512,9 @@ public abstract class AbstractTransformControllerTest
String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG))
.andExpect(status().is(OK.value()))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString();
@@ -568,7 +537,9 @@ public abstract class AbstractTransformControllerTest
String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG))
.andExpect(status().is(OK.value()))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString();

View File

@@ -44,8 +44,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
/**
* Super class for testing controllers with a server. Includes tests for the Controller itself.
* Note: Currently uses json rather than HTML as json is returned by this spring boot test harness.
* Super class with a server test harness, which talks to the TransformController using http.
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes={org.alfresco.transform.base.Application.class})
public abstract class AbstractHttpRequestTest

View File

@@ -44,7 +44,6 @@ public class EngineClient
{
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MULTIPART_FORM_DATA);
//headers.setAccept(ImmutableList.of(MULTIPART_FORM_DATA));
final MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", new ClassPathResource(sourceFile));

View File

@@ -0,0 +1,99 @@
/*
* #%L
* Alfresco Transform Core
* %%
* Copyright (C) 2005 - 2022 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.transform.base;
import org.alfresco.transform.base.components.TestTransformEngineTwoTransformers;
import org.alfresco.transform.base.components.TestTransformerPdf2Png;
import org.alfresco.transform.base.components.TestTransformerTxT2Pdf;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.nio.charset.StandardCharsets;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_IMAGE_PNG;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_PDF;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_TEXT_PLAIN;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_MIMETYPE;
import static org.alfresco.transform.common.RequestParamMap.TARGET_MIMETYPE;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Testing base t-engine TransformController functionality.
*/
@AutoConfigureMockMvc
@SpringBootTest(classes={org.alfresco.transform.base.Application.class})
@ContextConfiguration(classes = {
TestTransformerTxT2Pdf.class,
TestTransformerPdf2Png.class,
TestTransformEngineTwoTransformers.class})
public class TransformControllerTestWithBasicConfig
{
@Autowired
protected MockMvc mockMvc;
private void assertGoodTransform(String originalValue, String expectedValue, String sourceMimetype, String targetMimetype,
String expectedTargetExtension) throws Exception
{
mockMvc.perform(
MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM)
.file(new MockMultipartFile("file", null, sourceMimetype,
originalValue.getBytes(StandardCharsets.UTF_8)))
.param(SOURCE_MIMETYPE, sourceMimetype)
.param(TARGET_MIMETYPE, targetMimetype))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk())
.andExpect(header().string("Content-Disposition",
"attachment; filename*=UTF-8''transform." + expectedTargetExtension))
.andExpect(content().string(expectedValue));
}
@Test
public void singleStepTransform() throws Exception
{
assertGoodTransform("Start", "Start -> TxT2Pdf()",
MIMETYPE_TEXT_PLAIN, MIMETYPE_PDF, "pdf");
}
@Test
public void pipelineTransform() throws Exception
{
assertGoodTransform("Start", "Start -> TxT2Pdf() -> Pdf2Png()",
MIMETYPE_TEXT_PLAIN, MIMETYPE_IMAGE_PNG, "png");
}
}

View File

@@ -0,0 +1,102 @@
/*
* #%L
* Alfresco Transform Core
* %%
* Copyright (C) 2005 - 2022 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.transform.base.components;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.alfresco.transform.base.TransformEngine;
import org.alfresco.transform.base.probes.ProbeTestTransform;
import org.alfresco.transform.config.SupportedSourceAndTarget;
import org.alfresco.transform.config.TransformConfig;
import org.alfresco.transform.config.TransformOptionValue;
import org.alfresco.transform.config.Transformer;
import org.springframework.boot.test.context.TestComponent;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_HTML;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_IMAGE_PNG;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_PDF;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_TEXT_PLAIN;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_ENCODING;
/**
* Subclass MUST be named TestTransformEngine\<something>.
*/
@TestComponent
public abstract class AbstractTestTransformEngine implements TransformEngine
{
@Override public String getTransformEngineName()
{
String simpleClassName = getClass().getSimpleName();
return simpleClassName.substring("TestTransformEngine".length());
}
@Override public String getStartupMessage()
{
return "Startup";
}
@Override public TransformConfig getTransformConfig()
{
String docOptions = "docOptions";
String imageOptions = "imageOptions";
return TransformConfig.builder()
// .withTransformOptions(ImmutableMap.of(
// docOptions, ImmutableSet.of(
// new TransformOptionValue(false, "page")),
// imageOptions, ImmutableSet.of(
// new TransformOptionValue(false, "width"),
// new TransformOptionValue(false, "height"))))
.withTransformers(ImmutableList.of(
Transformer.builder()
.withTransformerName("TxT2Pdf")
.withSupportedSourceAndTargetList(ImmutableSet.of(
SupportedSourceAndTarget.builder()
.withSourceMediaType(MIMETYPE_TEXT_PLAIN)
.withTargetMediaType(MIMETYPE_PDF)
.build()))
// .withTransformOptions(ImmutableSet.of(docOptions))
.build(),
Transformer.builder()
.withTransformerName("Pdf2Png")
.withSupportedSourceAndTargetList(ImmutableSet.of(
SupportedSourceAndTarget.builder()
.withSourceMediaType(MIMETYPE_PDF)
.withTargetMediaType(MIMETYPE_IMAGE_PNG)
.build()))
// .withTransformOptions(ImmutableSet.of(imageOptions))
.build()))
.build();
}
@Override public ProbeTestTransform getLivenessAndReadinessProbeTestTransform()
{
return new ProbeTestTransform("quick.html", "quick.txt",
MIMETYPE_HTML, MIMETYPE_TEXT_PLAIN, ImmutableMap.of(SOURCE_ENCODING, "UTF-8"),
119, 30, 150, 1024, 60 * 2 + 1, 60 * 2);
}
}

View File

@@ -0,0 +1,78 @@
/*
* #%L
* Alfresco Transform Core
* %%
* Copyright (C) 2005 - 2022 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.transform.base.components;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.TestComponent;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Subclass MUST be named TestTransformer\<something>. Appends the name of the CustomTransformer and any t-options
* to the output. The output is always a String regardless of the stated mimetypes.
*/
@TestComponent
public abstract class AbstractTestTransformer implements CustomTransformer
{
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public String getTransformerName()
{
String simpleClassName = getClass().getSimpleName();
return simpleClassName.substring("TestTransformer".length());
}
@Override
public void transform(String sourceMimetype, InputStream inputStream, String targetMimetype,
OutputStream outputStream, Map<String, String> transformOptions, TransformManager transformManager)
throws Exception
{
String oldValue = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
String newValue = new StringBuilder(oldValue)
.append(" -> ")
.append(getTransformerName())
.append("(")
.append(transformOptions.entrySet()
.stream()
.map(e -> e.getKey() + '=' + e.getValue())
.collect(Collectors.joining(", ")))
.append(')')
.toString();
logger.info(newValue);
byte[] bytes = newValue.getBytes(StandardCharsets.UTF_8);
outputStream.write(bytes, 0, bytes.length);
}
}

View File

@@ -0,0 +1,31 @@
/*
* #%L
* Alfresco Transform Core
* %%
* Copyright (C) 2005 - 2022 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.transform.base.components;
public class TestTransformEngineTwoTransformers extends AbstractTestTransformEngine
{
}

View File

@@ -0,0 +1,31 @@
/*
* #%L
* Alfresco Transform Core
* %%
* Copyright (C) 2005 - 2022 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.transform.base.components;
public class TestTransformerPdf2Png extends AbstractTestTransformer
{
}

View File

@@ -0,0 +1,31 @@
/*
* #%L
* Alfresco Transform Core
* %%
* Copyright (C) 2005 - 2022 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.transform.base.components;
public class TestTransformerTxT2Pdf extends AbstractTestTransformer
{
}

View File

@@ -0,0 +1,17 @@
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
<title>The quick brown fox jumps over the lazy dog</title>
<meta name="author" content="Nevin Nollop">
<meta name="keywords" content="Pangram, fox, dog">
<meta name="description" content="Gym class featuring a brown fox and lazy dog">
</head>
<body lang=EN-US>
The quick brown fox jumps over the lazy dog
</body>
</html>

View File

@@ -0,0 +1,18 @@
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog