Save point: [skip ci]

* example HelloTransformer
This commit is contained in:
alandavis
2022-07-20 17:34:16 +01:00
parent 6692946fb5
commit 79706f8510
22 changed files with 638 additions and 238 deletions

View File

@@ -78,8 +78,7 @@ public class AIOTransformEngine implements TransformEngine
@Override @Override
public ProbeTransform getProbeTransform() public ProbeTransform getProbeTransform()
{ {
return new ProbeTransform("quick.pdf", "quick.txt", return new ProbeTransform("quick.pdf", MIMETYPE_PDF, MIMETYPE_TEXT_PLAIN, Collections.emptyMap(),
MIMETYPE_PDF, MIMETYPE_TEXT_PLAIN, Collections.emptyMap(),
60, 16, 400, 10240, 60 * 30 + 1, 60 * 15 + 20); 60, 16, 400, 10240, 60 * 30 + 1, 60 * 15 + 20);
} }
} }

View File

@@ -1,233 +1,147 @@
# Common code for Transform Engines # Common base code for T-Engines
This project contains code that is common between all the ACS T-Engine transformers that run as Spring Boot process (optionally within their own This project provides a common base for T-Engines and supersedes the
Docker containers). It performs common actions such as logging, throttling requests and handling the streaming of content to and from the container. [original base](https://github.com/Alfresco/alfresco-transform-core/blob/master/deprecated/alfresco-transformer-base).
For more details on build a custom T-Engine, please refer to the current docs in ACS Packaging, including: This project provides a base Spring Boot application (as a jar) to which transform
specific code may be added. It includes actions such as communication between
components and logging.
For more details on build a custom T-Engine and T-Config, please refer to the docs in ACS Packaging, including:
* [ATS Configuration](https://github.com/Alfresco/acs-packaging/blob/master/docs/custom-transforms-and-renditions.md#ats-configuration) * [ATS Configuration](https://github.com/Alfresco/acs-packaging/blob/master/docs/custom-transforms-and-renditions.md#ats-configuration)
* [Creating a T-Engine](https://github.com/Alfresco/acs-packaging/blob/master/docs/creating-a-t-engine.md) * [Creating a T-Engine](https://github.com/Alfresco/acs-packaging/blob/master/docs/creating-a-t-engine.md)
## Overview ## Overview
A transformer project is expected to provide the following files: A T-Engine project which extends this base is expected to provide the following:
~~~ * An implementation of the [TransformEngine](https://github.com/Alfresco/alfresco-transform-core/blob/master/engines/base/src/main/java/org/alfresco/transform/base/TransformEngine.java)
src/main/resources/templates/test.html interface to describe the T-Engine.
src/main/java/org/alfresco/transformer/<TransformerName>Controller.java * Implementations of the [CustomTransformer](engines/base/src/main/java/org/alfresco/transform/base/CustomTransformer.java)
src/main/java/org/alfresco/transformer/Application.java interface with the actual transform code.
~~~
* test.html - A simple test page using [thymeleaf](http://www.thymeleaf.org) that gathers request The `TransformEngine` and `CustomTransformer` implementations should have an
parameters, so they may be used to test the transformer. `@Component` annotation and be in or below the`org.alfresco.transform` package, so
that they will be discovered by the base T-Engine.
~~~ The `TransformEngine.getTransformConfig()` method typically reads a `json` file.
<html xmlns:th="http://www.thymeleaf.org"> The names in the config should match the names returned by the `CustomTransformer`
<body> implementations.
<div>
<h2>Test Transformation</h2>
<form method="POST" enctype="multipart/form-data" action="/transform">
<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">sourceExtension *</div></td><td><input type="text" name="sourceExtension" 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">sourceMimetype *</div></td><td><input type="text" name="sourceMimetype" value="" /></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">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></td><td><input type="submit" value="Transform" /></td></tr>
</table>
</form>
</div>
<div>
<a href="/log">Log</a>
</div>
</body>
</html>
~~~
* *TransformerName*Controller.java - A [Spring Boot](https://projects.spring.io/spring-boot/) Controller that
extends TransformController 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*.
~~~ **Example TransformEngine**
...
@Controller The `TransformEngineName` is important if the config from multiple T-Engines is being
public class TransformerNameController extends TransformController combined as they are sorted by name.
```
package org.alfresco.transform.example;
import com.google.common.collect.ImmutableMap;
import org.alfresco.transform.base.TransformEngine;
import org.alfresco.transform.base.probes.ProbeTransform;
import org.alfresco.transform.common.TransformConfigResourceReader;
import org.alfresco.transform.config.TransformConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class HelloTransformEngine implements TransformEngine
{ {
private static final Logger logger = LoggerFactory.getLogger(TransformerNameController.class); @Autowired
private TransformConfigResourceReader transformConfigResourceReader;
TransformerNameExecutor executor; @Override
public String getTransformEngineName()
@PostConstruct
private void init()
{ {
executor = new TransformerNameExecutor(); return "0200_hello";
} }
@Override
public String getStartupMessage()
{
return "Startup "+getTransformEngineName()+"\nNo 3rd party licenses";
}
@Override
public TransformConfig getTransformConfig()
{
return transformConfigResourceReader.read("classpath:hello_engine_config.json");
}
@Override
public ProbeTransform getProbeTransform()
{
return new ProbeTransform("jane.txt", "text/plain", "text/plain",
ImmutableMap.of("sourceEncoding", "UTF-8", "language", "English"),
11, 10, 150, 1024, 1, 60 * 2);
}
}
```
**Example CustomTransformer**
```
package org.alfresco.transform.example;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
@Component
public class HelloTransformer implements CustomTransformer
{
@Override @Override
public String getTransformerName() public String getTransformerName()
{ {
return "Transformer Name"; return "hello";
} }
@Override @Override
public String version() public void transform(String sourceMimetype, InputStream inputStream, String targetMimetype,
OutputStream outputStream, Map<String, String> transformOptions, TransformManager transformManager)
throws Exception
{ {
return commandExecutor.version(); String name = new String(inputStream.readAllBytes(), transformOptions.get("sourceEncoding"));
String greeting = String.format(getGreeting(transformOptions.get("language")), name);
byte[] bytes = greeting.getBytes(transformOptions.get("sourceEncoding"));
outputStream.write(bytes, 0, bytes.length);
} }
@Override private String getGreeting(String language)
public ProbeTestTransform getProbeTestTransform()
{ {
// See the Javadoc on this method and Probes.md for the choice of these values. return "Hello %s";
return new ProbeTestTransform(this, "quick.pdf", "quick.png",
7455, 1024, 150, 10240, 60 * 20 + 1, 60 * 15 - 15)
{
@Override
protected void executeTransformCommand(File sourceFile, File targetFile)
{
transformImpl(null, null, null, Collections.emptyMap(), sourceFile, targetFile);
}
};
}
@Override
public void transformImpl(String transformName, String sourceMimetype, String targetMimetype,
Map<String, String> transformOptions, File sourceFile, File targetFile)
{
executor.transform(sourceMimetype, targetMimetype, transformOptions, sourceFile, targetFile);
}
}
~~~
* *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
{
...
@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);
}
}
~~~
* 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:
~~~
package org.alfresco.transformer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application
{
public static void main(String[] args)
{
SpringApplication.run(Application.class, args);
}
}
~~~
Transform requests are handled by the *TransformController*, 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** **Example T-Config** `resources/hello_engine_config.json`
```json
```javascript {
var transformReply = { "transformOptions": {
"requestId": "1", "helloOptions": [
"status": 201, {"value": {"name": "language"}},
"errorDetails": null, {"value": {"name": "sourceEncoding"}}
"sourceReference": "2f9ed237-c734-4366-8c8b-6001819169a4", ]
"targetReference": "34d69ff0-7eaa-4741-8a9f-e1915e6995bf", },
"clientType": "ACS", "transformers": [
"clientData": "Yo No Soy Marinero, Soy Capitan, Soy Capitan!", {
"schema": 1 "transformerName": "hello",
"supportedSourceAndTargetList": [
{"sourceMediaType": "text/plain", "targetMediaType": "text/plain" }
],
"transformOptions": [
"helloOptions"
]
}
]
} }
``` ```
## Building and testing **Example `ProbeTransform` test file** `jane.txt`
```json
The project can be built by running the Maven command: Jane
```
~~~
mvn clean install
~~~
## Artifacts
The artifacts can be obtained by:
* downloading from the [Alfresco repository](https://artifacts.alfresco.com/nexus/content/groups/public/)
* Adding a Maven dependency to your pom file.
~~~
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-base-t-engine</artifactId>
<version>1.0</version>
</dependency>
~~~
and the Alfresco Maven repository:
~~~
<repository>
<id>alfresco-maven-repo</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</repository>
~~~
The build plan is available in [TravisCI](https://travis-ci.com/Alfresco/alfresco-transform-core).
## Contributing guide
Please use [this guide](https://github.com/Alfresco/alfresco-repository/blob/master/CONTRIBUTING.md)
to make a contribution to the project.

View File

@@ -50,7 +50,9 @@ public interface TransformEngine
/** /**
* @return a definition of what the t-engine supports. Normally read from a json Resource on the classpath using a * @return a definition of what the t-engine supports. Normally read from a json Resource on the classpath using a
* {@link TransformConfigResourceReader}. * {@link TransformConfigResourceReader}. To combine to code from multiple t-engine into a single t-engine
* include all the TransformEngines and CustomTransform implementations, plus a wrapper TransformEngine for the
* others. The wrapper should return {@code null} from this method.
*/ */
TransformConfig getTransformConfig(); TransformConfig getTransformConfig();

View File

@@ -74,6 +74,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static org.alfresco.transform.base.fs.FileManager.createTargetFile; import static org.alfresco.transform.base.fs.FileManager.createTargetFile;
@@ -638,7 +639,10 @@ public class TransformHandler
sourceSizeInBytes, targetMimetype, transformOptions, null); sourceSizeInBytes, targetMimetype, transformOptions, null);
if (transformerName == null) if (transformerName == null)
{ {
throw new TransformException(BAD_REQUEST, "No transforms were able to handle the request"); throw new TransformException(BAD_REQUEST, "No transforms were able to handle the request: "+
sourceMimetype+" -> "+targetMimetype+transformOptions.entrySet().stream()
.map(entry -> entry.getKey()+"="+entry.getValue())
.collect(Collectors.joining(", ", " with ", "")));
} }
return transformerName; return transformerName;
} }

View File

@@ -51,9 +51,9 @@ import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
/** /**
* Provides test transformations and the logic used by k8 liveness and readiness probes. * Provides test transformations and the logic used by k8 liveness and readiness probes.
* *
* <p><b>K8s probes</b>: A readiness probe indicates if the pod should accept request. <b>It does not indicate that a pod is * <p><b>K8s probes</b>: A readiness probe indicates if the pod should accept request. <b>It does not indicate that a
* ready after startup</b>. The liveness probe indicates when to kill the pod. <b>Both probes are called throughout the * pod is ready after startup</b>. The liveness probe indicates when to kill the pod. <b>Both probes are called
* lifetime of the pod</b> and a <b>liveness probes can take place before a readiness probe.</b> The k8s * throughout the lifetime of the pod</b> and a <b>liveness probes can take place before a readiness probe.</b> The k8s
* <b>initialDelaySeconds field is not fully honoured</b> as it is multiplied by a random number, so is * <b>initialDelaySeconds field is not fully honoured</b> as it is multiplied by a random number, so is
* actually a maximum initial delay in seconds, but could be 0. </p> * actually a maximum initial delay in seconds, but could be 0. </p>
* *
@@ -81,7 +81,6 @@ public class ProbeTransform
private static final int AVERAGE_OVER_TRANSFORMS = 5; private static final int AVERAGE_OVER_TRANSFORMS = 5;
private final String sourceFilename; private final String sourceFilename;
private final String targetFilename;
private final String sourceMimetype; private final String sourceMimetype;
private final String targetMimetype; private final String targetMimetype;
private final Map<String, String> transformOptions; private final Map<String, String> transformOptions;
@@ -115,13 +114,11 @@ public class ProbeTransform
return maxTime; return maxTime;
} }
public ProbeTransform(String sourceFilename, String targetFilename, public ProbeTransform(String sourceFilename, String sourceMimetype, String targetMimetype, Map<String, String> transformOptions,
String sourceMimetype, String targetMimetype, Map<String, String> transformOptions,
long expectedLength, long plusOrMinus, int livenessPercent, long maxTransforms, long maxTransformSeconds, long expectedLength, long plusOrMinus, int livenessPercent, long maxTransforms, long maxTransformSeconds,
long livenessTransformPeriodSeconds) long livenessTransformPeriodSeconds)
{ {
this.sourceFilename = sourceFilename; this.sourceFilename = sourceFilename;
this.targetFilename = targetFilename;
this.sourceMimetype = sourceMimetype; this.sourceMimetype = sourceMimetype;
this.targetMimetype = targetMimetype; this.targetMimetype = targetMimetype;
this.transformOptions = new HashMap(transformOptions); this.transformOptions = new HashMap(transformOptions);
@@ -271,14 +268,14 @@ public class ProbeTransform
getMessagePrefix(isLiveProbe) + "Failed to store the source file", e); getMessagePrefix(isLiveProbe) + "Failed to store the source file", e);
} }
long length = sourceFile.length(); long length = sourceFile.length();
LogEntry.setSource(sourceFilename, length); LogEntry.setSource(sourceFile.getName(), length);
return sourceFile; return sourceFile;
} }
private File getTargetFile() private File getTargetFile()
{ {
File targetFile = createTempFile("target_", "_" + targetFilename); File targetFile = createTempFile("target_", "_" + sourceFilename);
LogEntry.setTarget(targetFilename); LogEntry.setTarget(targetFile.getName());
return targetFile; return targetFile;
} }

View File

@@ -46,11 +46,14 @@ import javax.jms.Destination;
import javax.jms.JMSException; import javax.jms.JMSException;
import javax.jms.Message; import javax.jms.Message;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
@@ -187,13 +190,13 @@ public class QueueTransformServiceTest
.build(); .build();
doReturn(request).when(transformMessageConverter).fromMessage(msg); doReturn(request).when(transformMessageConverter).fromMessage(msg);
doReturn(new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()))) doAnswer(invocation -> {transformReplySender.send(destination, reply); return null;})
.when(transformHandler).handleMessageRequest(request, null, null); .when(transformHandler).handleMessageRequest(request, null, destination);
queueTransformService.receive(msg); queueTransformService.receive(msg);
verify(transformMessageConverter).fromMessage(msg); verify(transformMessageConverter).fromMessage(msg);
verify(transformHandler).handleMessageRequest(request, null, null); verify(transformHandler).handleMessageRequest(request, null, destination);
verify(transformReplySender).send(destination, reply); verify(transformReplySender).send(destination, reply);
} }
@@ -228,13 +231,13 @@ public class QueueTransformServiceTest
.build(); .build();
doReturn(request).when(transformMessageConverter).fromMessage(msg); doReturn(request).when(transformMessageConverter).fromMessage(msg);
doReturn(new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()))) doAnswer(invocation -> {transformReplySender.send(destination, reply); return null;})
.when(transformHandler).handleMessageRequest(request, null, null); .when(transformHandler).handleMessageRequest(request, null, destination);
queueTransformService.receive(msg); queueTransformService.receive(msg);
verify(transformMessageConverter).fromMessage(msg); verify(transformMessageConverter).fromMessage(msg);
verify(transformHandler).handleMessageRequest(request, null, null); verify(transformHandler).handleMessageRequest(request, null, destination);
verify(transformReplySender).send(destination, reply); verify(transformReplySender).send(destination, reply);
} }
} }

View File

@@ -46,7 +46,8 @@ import static org.alfresco.transform.common.RequestParamMap.SOURCE_ENCODING;
public class FakeTransformEngineWithTwoCustomTransformers extends AbstractFakeTransformEngine public class FakeTransformEngineWithTwoCustomTransformers extends AbstractFakeTransformEngine
{ {
@Override public TransformConfig getTransformConfig() @Override
public TransformConfig getTransformConfig()
{ {
String docOptions = "docOptions"; String docOptions = "docOptions";
String imageOptions = "imageOptions"; String imageOptions = "imageOptions";
@@ -103,11 +104,11 @@ public class FakeTransformEngineWithTwoCustomTransformers extends AbstractFakeTr
.build(); .build();
} }
@Override public ProbeTransform getProbeTransform() @Override
public ProbeTransform getProbeTransform()
{ {
return new ProbeTransform("quick.txt", "quick.pdf", return new ProbeTransform("quick.txt", MIMETYPE_TEXT_PLAIN, MIMETYPE_PDF,
MIMETYPE_TEXT_PLAIN, MIMETYPE_PDF, ImmutableMap.of(SOURCE_ENCODING, "UTF-8"), ImmutableMap.of(SOURCE_ENCODING, "UTF-8"), 46, 0,
46, 0, 150, 1024, 1, 150, 1024, 1, 60 * 2);
60 * 2);
} }
} }

View File

@@ -0,0 +1 @@
target/docker/

View File

@@ -0,0 +1,26 @@
FROM alfresco/alfresco-base-java:jre11-centos7-202207110835
ARG EXIFTOOL_VERSION=12.25
ARG EXIFTOOL_FOLDER=Image-ExifTool-${EXIFTOOL_VERSION}
ARG EXIFTOOL_URL=https://nexus.alfresco.com/nexus/service/local/repositories/thirdparty/content/org/exiftool/image-exiftool/${EXIFTOOL_VERSION}/image-exiftool-${EXIFTOOL_VERSION}.tgz
ENV JAVA_OPTS=""
ARG GROUPNAME=Alfresco
ARG GROUPID=1000
ARG EXAMPLEUSERNAME=example
ARG USERID=33009
COPY target/${env.project_artifactId}-${env.project_version}.jar /usr/bin
RUN ln /usr/bin/${env.project_artifactId}-${env.project_version}.jar /usr/bin/${env.project_artifactId}.jar
RUN groupadd -g ${GROUPID} ${GROUPNAME} && \
useradd -u ${USERID} -G ${GROUPNAME} ${EXAMPLEUSERNAME} && \
chgrp -R ${GROUPNAME} /usr/bin/${env.project_artifactId}.jar
EXPOSE 8090
USER ${EXAMPLEUSERNAME}
ENTRYPOINT java $JAVA_OPTS -jar /usr/bin/${env.project_artifactId}.jar

300
engines/example/pom.xml Normal file
View File

@@ -0,0 +1,300 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-transform-example</artifactId>
<name>- Example</name>
<parent>
<artifactId>alfresco-transform-core</artifactId>
<groupId>org.alfresco</groupId>
<version>2.6.1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<properties>
<image.name>alfresco/alfresco-transform-example</image.name>
<image.registry>quay.io</image.registry>
<env.project_artifactId>${project.artifactId}</env.project_artifactId>
</properties>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-base-t-engine</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-base-t-engine</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>org.alfresco.transform.base.Application</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>docker-it-setup</id>
<!-- raises an ActiveMq container for the Integration Tests -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<executions>
<execution>
<id>integration-tests</id>
<goals>
<goal>start</goal>
<goal>stop</goal>
</goals>
<configuration>
<images>
<image>
<name>alfresco/alfresco-activemq:5.16.1</name>
<run>
<hostname>activemq</hostname>
<ports>
<port>8161:8161</port>
<port>5672:5672</port>
<port>61616:61616</port>
</ports>
<wait>
<log>Apache ActiveMQ 5.16.1 .* started</log>
<time>20000</time>
<kill>500</kill>
<shutdown>100</shutdown>
<exec>
<preStop>kill 1</preStop>
<preStop>kill -9 1</preStop>
</exec>
</wait>
</run>
</image>
<image>
<name>${image.name}:${image.tag}</name>
<run>
<ports>
<port>8090:8090</port>
</ports>
<wait>
<http>
<url>http://localhost:8090/transform/config</url>
<method>GET</method>
<status>200...299</status>
</http>
<time>300000</time>
<kill>500</kill>
<shutdown>100</shutdown>
<exec>
<preStop>kill 1</preStop>
<preStop>kill -9 1</preStop>
</exec>
</wait>
</run>
</image>
</images>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>local</id>
<build>
<plugins>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
<configuration>
<images>
<image>
<name>${image.name}:${image.tag}</name>
<build>
<contextDir>${project.basedir}</contextDir>
<buildOptions>
<squash>true</squash>
</buildOptions>
</build>
</image>
</images>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>internal</id>
<build>
<plugins>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<images>
<!-- QuayIO image -->
<image>
<name>${image.name}:${image.tag}</name>
<registry>${image.registry}</registry>
<build>
<contextDir>${project.basedir}</contextDir>
<buildOptions>
<squash>true</squash>
</buildOptions>
</build>
</image>
<!-- DockerHub image -->
<image>
<name>${image.name}:${image.tag}</name>
<build>
<contextDir>${project.basedir}</contextDir>
<buildOptions>
<squash>true</squash>
</buildOptions>
</build>
</image>
</images>
</configuration>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
<execution>
<id>push-image</id>
<phase>install</phase>
<goals>
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<images>
<!-- QuayIO image -->
<image>
<name>${image.name}:${project.version}</name>
<registry>${image.registry}</registry>
<build>
<contextDir>${project.basedir}</contextDir>
<buildOptions>
<squash>true</squash>
</buildOptions>
</build>
</image>
<!-- DockerHub image -->
<image>
<name>${image.name}:${project.version}</name>
<build>
<contextDir>${project.basedir}</contextDir>
<buildOptions>
<squash>true</squash>
</buildOptions>
</build>
</image>
</images>
</configuration>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,68 @@
/*
* #%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.example;
import com.google.common.collect.ImmutableMap;
import org.alfresco.transform.base.TransformEngine;
import org.alfresco.transform.base.probes.ProbeTransform;
import org.alfresco.transform.common.TransformConfigResourceReader;
import org.alfresco.transform.config.TransformConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class HelloTransformEngine implements TransformEngine
{
@Autowired
private TransformConfigResourceReader transformConfigResourceReader;
@Override
public String getTransformEngineName()
{
return "0200_hello";
}
@Override
public String getStartupMessage()
{
return "Startup "+getTransformEngineName()+"\nNo 3rd party licenses";
}
@Override
public TransformConfig getTransformConfig()
{
return transformConfigResourceReader.read("classpath:hello_engine_config.json");
}
@Override
public ProbeTransform getProbeTransform()
{
return new ProbeTransform("jane.txt", "text/plain", "text/plain",
ImmutableMap.of("sourceEncoding", "UTF-8", "language", "English"),
11, 10, 150, 1024, 1, 60 * 2);
}
}

View File

@@ -0,0 +1,61 @@
/*
* #%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.example;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
@Component
public class HelloTransformer implements CustomTransformer
{
@Override
public String getTransformerName()
{
return "hello";
}
@Override
public void transform(String sourceMimetype, InputStream inputStream, String targetMimetype,
OutputStream outputStream, Map<String, String> transformOptions, TransformManager transformManager)
throws Exception
{
String name = new String(inputStream.readAllBytes(), transformOptions.get("sourceEncoding"));
String greeting = String.format(getGreeting(transformOptions.get("language")), name);
byte[] bytes = greeting.getBytes(transformOptions.get("sourceEncoding"));
outputStream.write(bytes, 0, bytes.length);
}
private String getGreeting(String language)
{
return "Hello %s";
}
}

View File

@@ -0,0 +1,19 @@
{
"transformOptions": {
"helloOptions": [
{"value": {"name": "language"}},
{"value": {"name": "sourceEncoding"}}
]
},
"transformers": [
{
"transformerName": "hello",
"supportedSourceAndTargetList": [
{"sourceMediaType": "text/plain", "targetMediaType": "text/plain" }
],
"transformOptions": [
"helloOptions"
]
}
]
}

View File

@@ -0,0 +1 @@
Jane

View File

@@ -68,8 +68,7 @@ public class ImageMagickTransformEngine implements TransformEngine
@Override @Override
public ProbeTransform getProbeTransform() public ProbeTransform getProbeTransform()
{ {
return new ProbeTransform("quick.jpg", "quick.png", return new ProbeTransform("quick.jpg", MIMETYPE_IMAGE_JPEG, MIMETYPE_IMAGE_PNG, Collections.emptyMap(),
MIMETYPE_IMAGE_JPEG, MIMETYPE_IMAGE_PNG, Collections.emptyMap(),
35593, 1024, 150, 1024, 60 * 15 + 1, 60 * 15); 35593, 1024, 150, 1024, 60 * 15 + 1, 60 * 15);
} }
} }

View File

@@ -69,8 +69,7 @@ public class LibreOfficeTransformEngine implements TransformEngine
@Override @Override
public ProbeTransform getProbeTransform() public ProbeTransform getProbeTransform()
{ {
return new ProbeTransform("quick.doc", "quick.pdf", return new ProbeTransform("quick.doc", MIMETYPE_WORD, MIMETYPE_PDF, Collections.emptyMap(),
MIMETYPE_WORD, MIMETYPE_PDF, Collections.emptyMap(),
11817, 1024, 150, 10240, 60 * 30 + 1, 60 * 15 + 20); 11817, 1024, 150, 10240, 60 * 30 + 1, 60 * 15 + 20);
} }
} }

View File

@@ -73,8 +73,7 @@ public class MiscTransformEngine implements TransformEngine
@Override @Override
public ProbeTransform getProbeTransform() public ProbeTransform getProbeTransform()
{ {
return new ProbeTransform("quick.html", "quick.txt", return new ProbeTransform("quick.html", MIMETYPE_HTML, MIMETYPE_TEXT_PLAIN, transformOptions,
MIMETYPE_HTML, MIMETYPE_TEXT_PLAIN, transformOptions,
119, 30, 150, 1024, 60 * 2 + 1, 60 * 2); 119, 30, 150, 1024, 60 * 2 + 1, 60 * 2);
} }
} }

View File

@@ -67,8 +67,7 @@ public class PdfRendererTransformEngine implements TransformEngine
@Override @Override
public ProbeTransform getProbeTransform() public ProbeTransform getProbeTransform()
{ {
return new ProbeTransform("quick.pdf", "quick.png", return new ProbeTransform("quick.pdf", MIMETYPE_PDF, MIMETYPE_IMAGE_PNG, Collections.emptyMap(),
MIMETYPE_PDF, MIMETYPE_IMAGE_PNG, Collections.emptyMap(),
7455, 1024, 150, 10240, 60 * 20 + 1, 60 * 15 - 15); 7455, 1024, 150, 10240, 60 * 20 + 1, 60 * 15 - 15);
} }
} }

View File

@@ -68,8 +68,7 @@ public class TikaTransformEngine implements TransformEngine
@Override @Override
public ProbeTransform getProbeTransform() public ProbeTransform getProbeTransform()
{ {
return new ProbeTransform("quick.pdf", "quick.txt", return new ProbeTransform("quick.pdf", MIMETYPE_PDF, MIMETYPE_TEXT_PLAIN, Collections.emptyMap(),
MIMETYPE_PDF, MIMETYPE_TEXT_PLAIN, Collections.emptyMap(),
60, 16, 400, 10240, 60 * 30 + 1, 60 * 15 + 20); 60, 16, 400, 10240, 60 * 30 + 1, 60 * 15 + 20);
} }
} }

View File

@@ -87,9 +87,9 @@ class TransformRegistryHelper
renditionName = null; renditionName = null;
} }
final List<SupportedTransform> cachedTransformList = final List<SupportedTransform> cachedTransformList = renditionName == null
renditionName == null ? null : ? null
data.retrieveCached(renditionName, sourceMimetype); : data.retrieveCached(renditionName, sourceMimetype);
if (cachedTransformList != null) if (cachedTransformList != null)
{ {
return cachedTransformList; return cachedTransformList;

10
pom.xml
View File

@@ -50,6 +50,7 @@
<module>engines/pdfrenderer</module> <module>engines/pdfrenderer</module>
<module>engines/tika</module> <module>engines/tika</module>
<module>engines/aio</module> <module>engines/aio</module>
<module>engines/example</module>
<module>deprecated/alfresco-transformer-base</module> <module>deprecated/alfresco-transformer-base</module>
</modules> </modules>
</profile> </profile>
@@ -58,6 +59,7 @@
<modules> <modules>
<module>model</module> <module>model</module>
<module>engines/base</module> <module>engines/base</module>
<module>engines/example</module>
<module>deprecated/alfresco-transformer-base</module> <module>deprecated/alfresco-transformer-base</module>
</modules> </modules>
</profile> </profile>
@@ -110,6 +112,14 @@
<module>engines/aio</module> <module>engines/aio</module>
</modules> </modules>
</profile> </profile>
<profile>
<id>example</id>
<modules>
<module>model</module>
<module>engines/base</module>
<module>engines/example</module>
</modules>
</profile>
</profiles> </profiles>
<scm> <scm>