mirror of
https://github.com/Alfresco/alfresco-transform-core.git
synced 2025-05-12 17:04:48 +00:00
REPO-5201 Update README on how to create a T-Engine (#261)
Reflects changes to AbstractTransformerController that simplify the number of methods that need to be implemented.
This commit is contained in:
parent
76457cb6e8
commit
38f7a8cc9c
@ -2,19 +2,17 @@
|
|||||||
|
|
||||||
This project contains code that is common between all the ACS transformers that run within their own
|
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
|
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
|
streaming of content to and from the container.
|
||||||
specific transformers to simply check request parameter and perform the transformation using either
|
|
||||||
files or a pair of InputStream and OutputStream.
|
|
||||||
|
|
||||||
A transformer project is expected to provide the following files:
|
A transformer project is expected to provide the following files:
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
src/main/resources/templates/transformForm.html
|
src/main/resources/templates/transformForm.html
|
||||||
src/main/java/org/alfresco/transformer/<XXX>Controller.java
|
src/main/java/org/alfresco/transformer/<TransformerName>Controller.java
|
||||||
src/main/java/org/alfresco/transformer/Application.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.
|
parameters so they may be used to test the transformer.
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
@ -25,12 +23,13 @@ src/main/java/org/alfresco/transformer/Application.java
|
|||||||
<form method="POST" enctype="multipart/form-data" action="/transform">
|
<form method="POST" enctype="multipart/form-data" action="/transform">
|
||||||
<table>
|
<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">file *</div></td><td><input type="file" name="file" /></td></tr>
|
||||||
<tr><td><div style="text-align:right">targetFilename *</div></td><td><input type="text" name="targetFilename" value="" /></td></tr>
|
<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">width</div></td><td><input type="text" name="width" value="" /></td></tr>
|
<tr><td><div style="text-align:right">sourceExtension *</div></td><td><input type="text" name="sourceExtension" value="" /></td></tr>
|
||||||
<tr><td><div style="text-align:right">height</div></td><td><input type="text" name="height" value="" /></td></tr>
|
<tr><td><div style="text-align:right">targetExtension *</div></td><td><input type="text" name="targetExtension" value="" /></td></tr>
|
||||||
<tr><td><div style="text-align:right">allowPdfEnlargement</div></td><td><input type="checkbox" name="allowPdfEnlargement" value="true" /></td></tr>
|
<tr><td><div style="text-align:right">sourceMimetype *</div></td><td><input type="text" name="sourceMimetype" value="" /></td></tr>
|
||||||
<tr><td><div style="text-align:right">maintainPdfAspectRatio</div></td><td><input type="checkbox" name="maintainPdfAspectRatio" value="true" /></td></tr>
|
<tr><td><div style="text-align:right">targetMimetype *</div></td><td><input type="text" name="targetMimetype" value="" /></td></tr>
|
||||||
<tr><td><div style="text-align:right">page</div></td><td><input type="text" name="page" value="" /></td></tr>
|
<tr><td><div style="text-align:right">abc:width</div></td><td><input type="text" name="width" value="" /></td></tr>
|
||||||
|
<tr><td><div style="text-align:right">abc:height</div></td><td><input type="text" name="height" value="" /></td></tr>
|
||||||
<tr><td><div style="text-align:right">timeout</div></td><td><input type="text" name="timeout" value="" /></td></tr>
|
<tr><td><div style="text-align:right">timeout</div></td><td><input type="text" name="timeout" value="" /></td></tr>
|
||||||
<tr><td></td><td><input type="submit" value="Transform" /></td></tr>
|
<tr><td></td><td><input type="submit" value="Transform" /></td></tr>
|
||||||
</table>
|
</table>
|
||||||
@ -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
|
* *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
|
@Controller
|
||||||
public class AlfrescoPdfRendererController extends AbstractTransformerController
|
public class TransformerNameController extends AbstractTransformerController
|
||||||
{
|
{
|
||||||
...
|
private static final Logger logger = LoggerFactory.getLogger(TransformerNameController.class);
|
||||||
|
|
||||||
@PostMapping("/transform")
|
TransformerNameExecutor executor;
|
||||||
public ResponseEntity<Resource> transform(HttpServletRequest request,
|
|
||||||
@RequestParam("file") MultipartFile sourceMultipartFile,
|
@PostConstruct
|
||||||
@RequestParam("targetFilename") String targetFilename,
|
private void init()
|
||||||
@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)
|
|
||||||
{
|
{
|
||||||
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);
|
@Override
|
||||||
File targetFile = createTargetFile(request, targetFilename);
|
protected void executeTransformCommand(File sourceFile, File targetFile)
|
||||||
// Both files are deleted by TransformInterceptor.afterCompletion
|
{
|
||||||
|
transformImpl(null, null, null, Collections.emptyMap(), sourceFile, targetFile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
StringJoiner args = new StringJoiner(" ");
|
@Override
|
||||||
if (width != null)
|
public void transformImpl(String transformName, String sourceMimetype, String targetMimetype,
|
||||||
{
|
Map<String, String> transformOptions, File sourceFile, File targetFile)
|
||||||
args.add("--width=" + width);
|
{
|
||||||
}
|
executor.transform(sourceMimetype, targetMimetype, transformOptions, sourceFile, targetFile);
|
||||||
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<String, String> 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
* *TransformerName*Controller#processTransform(File sourceFile, File targetFile, Map<String, String> transformOptions, Long timeout)
|
* *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.
|
||||||
### /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`)
|
...
|
||||||
|
public class TransformerNameExecutor extends AbstractCommandExecutor
|
||||||
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<String,String> 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
|
|
||||||
{
|
{
|
||||||
void processTransform(File sourceFile, File targetFile, Map<String, String> transformOptions, Long timeout) { /* Perform the transformation*/ }
|
...
|
||||||
|
@Override
|
||||||
|
public void transform(String transformName, String sourceMimetype, String targetMimetype,
|
||||||
|
Map<String, String> 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
|
* 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:
|
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
|
## Building and testing
|
||||||
|
|
||||||
The project can be built by running the Maven command:
|
The project can be built by running the Maven command:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user