diff --git a/alfresco-transformer-base/README.md b/alfresco-transformer-base/README.md index 474ae0c3..6dd7139c 100644 --- a/alfresco-transformer-base/README.md +++ b/alfresco-transformer-base/README.md @@ -2,19 +2,17 @@ This project contains code that is common between all the ACS transformers that run within their own Docker containers. It performs common actions such as logging, throttling requests and handling the -streaming of content to and from the container. It also provides structure and hook points to allow -specific transformers to simply check request parameter and perform the transformation using either -files or a pair of InputStream and OutputStream. +streaming of content to and from the container. A transformer project is expected to provide the following files: ~~~ src/main/resources/templates/transformForm.html -src/main/java/org/alfresco/transformer/Controller.java +src/main/java/org/alfresco/transformer/Controller.java src/main/java/org/alfresco/transformer/Application.java ~~~ -* transformerForm.html - A simple test page using [thymeleaf](http://www.thymeleaf.org) that gathers request +* transformForm.html - A simple test page using [thymeleaf](http://www.thymeleaf.org) that gathers request parameters so they may be used to test the transformer. ~~~ @@ -25,12 +23,13 @@ src/main/java/org/alfresco/transformer/Application.java
- - - - - - + + + + + + +
file *
targetFilename *
width
height
allowPdfEnlargement
maintainPdfAspectRatio
page
file *
sourceExtension *
targetExtension *
sourceMimetype *
targetMimetype *
abc:width
abc:height
timeout
@@ -44,131 +43,86 @@ src/main/java/org/alfresco/transformer/Application.java ~~~ * *TransformerName*Controller.java - A [Spring Boot](https://projects.spring.io/spring-boot/) Controller that - extends AbstractTransformerController to handel a POST request to *"/transform"*. + extends AbstractTransformerController to handel requests. It implements a few methods including *transformImpl* + which is intended to perform the actual transform. Generally the transform is done in a sub class of + *JavaExecutor*, when a Java library is being used or *AbstractCommandExecutor*, when an external process is used. + Both are sub interfaces of *Transformer*. ~~~ ... @Controller -public class AlfrescoPdfRendererController extends AbstractTransformerController +public class TransformerNameController extends AbstractTransformerController { - ... + private static final Logger logger = LoggerFactory.getLogger(TransformerNameController.class); - @PostMapping("/transform") - public ResponseEntity transform(HttpServletRequest request, - @RequestParam("file") MultipartFile sourceMultipartFile, - @RequestParam("targetFilename") String targetFilename, - @RequestParam(value = "width", required = false) Integer width, - @RequestParam(value = "height", required = false) Integer height, - @RequestParam(value = "allowPdfEnlargement", required = false) Boolean allowPdfEnlargement, - @RequestParam(value = "maintainPdfAspectRatio", required = false) Boolean maintainPdfAspectRatio, - @RequestParam(value = "page", required = false) Integer page, - @RequestParam(value = "timeout", required = false) Long timeout) + TransformerNameExecutor executor; + + @PostConstruct + private void init() { - try + executor = new TransformerNameExecutor(); + } + + @Override + public String getTransformerName() + { + return "Transformer Name"; + } + + @Override + public String version() + { + return commandExecutor.version(); + } + + @Override + public ProbeTestTransform getProbeTestTransform() + { + // See the Javadoc on this method and Probes.md for the choice of these values. + return new ProbeTestTransform(this, "quick.pdf", "quick.png", + 7455, 1024, 150, 10240, 60 * 20 + 1, 60 * 15 - 15) { - File sourceFile = createSourceFile(request, sourceMultipartFile); - File targetFile = createTargetFile(request, targetFilename); - // Both files are deleted by TransformInterceptor.afterCompletion + @Override + protected void executeTransformCommand(File sourceFile, File targetFile) + { + transformImpl(null, null, null, Collections.emptyMap(), sourceFile, targetFile); + } + }; + } - StringJoiner args = new StringJoiner(" "); - if (width != null) - { - args.add("--width=" + width); - } - if (height != null) - { - args.add("--height=" + height); - } - if (allowPdfEnlargement != null && allowPdfEnlargement) - { - args.add("--allow-enlargement"); - } - if (maintainPdfAspectRatio != null && maintainPdfAspectRatio) - { - args.add("--maintain-aspect-ratio"); - } - if (page != null) - { - args.add("--page=" + page); - } - String options = args.toString(); - LogEntry.setOptions(options); - - Map properties = new HashMap<>(); - properties.put("options", options); - properties.put("source", sourceFile.getAbsolutePath()); - properties.put("target", targetFile.getAbsolutePath()); - - executeTransformCommand(properties, targetFile, timeout); - - return createAttachment(targetFilename, targetFile); - } - catch (UnsupportedEncodingException e) - { - throw new TransformException(500, "Filename encoding error", e); - } + @Override + public void transformImpl(String transformName, String sourceMimetype, String targetMimetype, + Map transformOptions, File sourceFile, File targetFile) + { + executor.transform(sourceMimetype, targetMimetype, transformOptions, sourceFile, targetFile); } } ~~~ -* *TransformerName*Controller#processTransform(File sourceFile, File targetFile, Map transformOptions, Long timeout) - -### /transform (Consumes: `application/json`, Produces: `application/json`) -The new *consumes* and *produces* arguments have been specified in order to differentiate this endpoint from the previous one (which **consumes** `multipart/form-data`) - -The endpoint should **always** receive a `TransformationRequest` and should **always** respond with a `TransformationReply`. - -As specific transformers require specific arguments (e.g. `transform` for the **Tika transformer**) the request body should include this in the `transformRequestOptions` via the `Map transformRequestOptions`. - -**Example request body** -```javascript -var transformRequest = { - "requestId": "1", - "sourceReference": "2f9ed237-c734-4366-8c8b-6001819169a4", - "sourceMediaType": "pdf", - "sourceSize": 123456, - "sourceExtension": "pdf", - "targetMediaType": "txt", - "targetExtension": "txt", - "clientType": "ACS", - "clientData": "Yo No Soy Marinero, Soy Capitan, Soy Capitan!", - "schema": 1, - "transformRequestOptions": { - "targetMimetype": "text/plain", - "targetEncoding": "UTF-8", - "transform": "PdfBox" - } -} -``` - -**Example response body** - -```javascript -var transformReply = { - "requestId": "1", - "status": 201, - "errorDetails": null, - "sourceReference": "2f9ed237-c734-4366-8c8b-6001819169a4", - "targetReference": "34d69ff0-7eaa-4741-8a9f-e1915e6995bf", - "clientType": "ACS", - "clientData": "Yo No Soy Marinero, Soy Capitan, Soy Capitan!", - "schema": 1 -} -``` - -### processTransform method -```java -public abstract class AbstractTransformerController +* *TransformerName*Executer.java - *JavaExecutor* and *CommandExecutor* sub classes need to extract values from + *transformOptions* and use them in a call to an external process or as parameters to a library call. +~~~ +... +public class TransformerNameExecutor extends AbstractCommandExecutor { - void processTransform(File sourceFile, File targetFile, Map transformOptions, Long timeout) { /* Perform the transformation*/ } + ... + @Override + public void transform(String transformName, String sourceMimetype, String targetMimetype, + Map transformOptions, + File sourceFile, File targetFile) throws TransformException + { + final String options = TransformerNameOptionsBuilder + .builder() + .withWidth(transformOptions.get(WIDTH_REQUEST_PARAM)) + .withHeight(transformOptions.get(HEIGHT_REQUEST_PARAM)) + .build(); + + Long timeout = stringToLong(transformOptions.get(TIMEOUT)); + + run(options, sourceFile, targetFile, timeout); + } } -``` - -The **abstract** method is declared in the *AbstractTransformerController* and must be implemented by the specific controllers. - -This method is called by the *AbstractTransformerController* directly in the **new** `/transform` endpoint which **consumes** `application/json` and **produces** `application/json`. - -The method is responsible for performing the transformation. Upon a **successful** transformation it updates the `targetFile` parameter. +~~~ * Application.java - [Spring Boot](https://projects.spring.io/spring-boot/) expects to find an Application in a project's source files. The following may be used: @@ -189,6 +143,49 @@ public class Application } ~~~ +Transform requests are handled by the *AbstractTransformerController*, but are either: + * POST requests (a direct http request from a client) where the transform options are passed as parameters, the source is supplied as a multipart file and + the response is a file download. + * POST request (a request via a message queue) where the transform options are supplied as JSON and the response is also JSON. + The source and target content is read from a location accessible to both the client and the transfomer. + +**Example JSON request body** +```javascript +var transformRequest = { + "requestId": "1", + "sourceReference": "2f9ed237-c734-4366-8c8b-6001819169a4", + "sourceMediaType": "application/pdf", + "sourceSize": 123456, + "sourceExtension": "pdf", + "targetMediaType": "text/plain", + "targetExtension": "txt", + "clientType": "ACS", + "clientData": "Yo No Soy Marinero, Soy Capitan, Soy Capitan!", + "schema": 1, + "transformRequestOptions": { + "targetMimetype": "text/plain", + "targetEncoding": "UTF-8", + "abc:width": "120", + "abc:height": "200" + } +} +``` + +**Example JSON response body** + +```javascript +var transformReply = { + "requestId": "1", + "status": 201, + "errorDetails": null, + "sourceReference": "2f9ed237-c734-4366-8c8b-6001819169a4", + "targetReference": "34d69ff0-7eaa-4741-8a9f-e1915e6995bf", + "clientType": "ACS", + "clientData": "Yo No Soy Marinero, Soy Capitan, Soy Capitan!", + "schema": 1 +} +``` + ## Building and testing The project can be built by running the Maven command: