Initial commit

This commit is contained in:
Alan Davis 2018-03-07 14:39:07 +00:00
commit 42e7e192b5
43 changed files with 3706 additions and 0 deletions

4
.gitbugtraq Normal file
View File

@ -0,0 +1,4 @@
# For SmartGit
[bugtraq "jira"]
url = https://issues.alfresco.com/jira/browse/%BUGID%
logRegex = ([A-Z]+-\\d+)

37
.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
*.class
# Eclipse
.classpath
.settings
.project
# Intellij
.idea/
*.iml
*.iws
# Mac
.DS_Store
# Maven
target
*.log
*.log.*
# Mobile Tools for Java (J2ME)
.mtj
.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
alf_data
/src/main/resources/alfresco-global.properties
/src/main/resources/alfresco/extension/custom-log4j.properties

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# Project that generates Docker images to perform ACS transformations
* alfresco-transformer-base - contains code that is common to all the transformers. This includes
the streaming of content to and from the docker images.
* alfresco-docker-<name> - contains code to generate an image for a transformer called <name>.
## Building and testing
The project can be built by running the Maven command:
~~~
mvn clean install
~~~
The build plan in Bamboo is PLAT-TRANS
## Contributing guide
Please use [this guide](https://github.com/Alfresco/alfresco-jodconverter/blob/master/CONTRIBUTING.md) to make a
contribution to the project.

View File

@ -0,0 +1,66 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformer-alfresco-pdf-renderer</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-transformer-alfresco-pdf-renderer</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${dependency.spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-base</artifactId>
<version>${dependency.alfresco-transformer-base.version}</version>
</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>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-core</artifactId>
<version>${dependency.alfresco-core.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.0.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,155 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer;
import org.alfresco.transformer.base.AbstractTransformerController;
import org.alfresco.transformer.base.LogEntry;
import org.alfresco.util.exec.RuntimeExec;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
/**
* Controller for the Docker based alfresco-pdf-renderer transformer.
*
* Status Codes:
*
* 200 Success
* 400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)
* 400 Bad Request: Request parameter <name> is of the wrong type
* 400 Bad Request: Transformer exit code was not 0 (possible problem with the source file)
* 400 Bad Request: The source filename was not supplied
* 500 Internal Server Error: (no message with low level IO problems)
* 500 Internal Server Error: The target filename was not supplied (should not happen as targetExtension is checked)
* 500 Internal Server Error: Transformer version check exit code was not 0
* 500 Internal Server Error: Transformer version check failed to create any output
* 500 Internal Server Error: Could not read the target file
* 500 Internal Server Error: The target filename was malformed (should not happen because of other checks)
* 500 Internal Server Error: Transformer failed to create an output file (the exit code was 0, so there should be some content)
* 500 Internal Server Error: Filename encoding error
* 507 Insufficient Storage: Failed to store the source file
*/
@Controller
public class AlfrescoPdfRendererController extends AbstractTransformerController
{
private static final String EXE = "/usr/bin/alfresco-pdf-renderer";
@Autowired
public AlfrescoPdfRendererController()
{
logger = LogFactory.getLog(AlfrescoPdfRendererController.class);
setTransformCommand(createTransformCommand());
setCheckCommand(createCheckCommand());
}
private static RuntimeExec createTransformCommand()
{
RuntimeExec runtimeExec = new RuntimeExec();
Map<String, String[]> commandsAndArguments = new HashMap<>();
commandsAndArguments.put(".*", new String[]{EXE, "SPLIT:${options}", "${source}", "${target}"});
runtimeExec.setCommandsAndArguments(commandsAndArguments);
Map<String, String> defaultProperties = new HashMap<>();
defaultProperties.put("key", null);
runtimeExec.setDefaultProperties(defaultProperties);
runtimeExec.setErrorCodes("1");
return runtimeExec;
}
private static RuntimeExec createCheckCommand()
{
RuntimeExec runtimeExec = new RuntimeExec();
Map<String, String[]> commandsAndArguments = new HashMap<>();
commandsAndArguments.put(".*", new String[]{EXE, "--version"});
runtimeExec.setCommandsAndArguments(commandsAndArguments);
return runtimeExec;
}
@PostMapping("/transform")
public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile,
@RequestParam("targetExtension") String targetExtension,
@RequestParam(value = "timeout", required = false) Long timeout,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "width", required = false) Integer width,
@RequestParam(value = "height", required = false) Integer height,
@RequestParam(value = "allowEnlargement", required = false) Boolean allowEnlargement,
@RequestParam(value = "maintainAspectRatio", required = false) Boolean maintainAspectRatio)
{
String targetFilename = createTargetFileName(sourceMultipartFile, targetExtension);
File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename);
// Both files are deleted by TransformInterceptor.afterCompletion
StringJoiner args = new StringJoiner(" ");
if (width != null && width >= 0)
{
args.add("--width=" + width);
}
if (height != null && height >= 0)
{
args.add("--height=" + height);
}
if (allowEnlargement != null && allowEnlargement)
{
args.add("--allow-enlargement");
}
if (maintainAspectRatio != null && maintainAspectRatio)
{
args.add("--maintain-aspect-ratio");
}
if (page != null && page >= 0)
{
args.add("--page=" + page);
}
String options = args.toString();
LogEntry.setOptions(options);
Map<String, String> properties = new HashMap<String, String>(5);
properties.put("options", options);
properties.put("source", sourceFile.getAbsolutePath());
properties.put("target", targetFile.getAbsolutePath());
executeTransformCommand(properties, targetFile, timeout);
return createAttachment(targetFilename, targetFile);
}
}

View File

@ -0,0 +1,38 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.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);
}
}

View File

@ -0,0 +1,33 @@
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<div>
<h2>Alfresco PDF Renderer Test Transformation</h2>
<form method="POST" enctype="multipart/form-data" action="/transform">
<table>
<tr><td><div>file *</div></td><td><input type="file" name="file" /></td></tr>
<tr><td><div>targetExtension *</div></td><td><input type="text" name="targetExtension" value="" /></td></tr>
<tr><td><div>timeout</div></td><td><input type="text" name="timeout" value="" /></td></tr>
<tr><td><div>page</div></td><td><input type="text" name="page" value="" /></td></tr>
<tr><td><div>width</div></td><td><input type="text" name="width" value="" /></td></tr>
<tr><td><div>height</div></td><td><input type="text" name="height" value="" /></td></tr>
<tr><td><div>allowEnlargement</div></td><td><input type="checkbox" name="allowEnlargement" value="true" /></td></tr>
<tr><td><div>maintainAspectRatio</div></td><td><input type="checkbox" name="maintainAspectRatio" value="true" /></td></tr>
<tr><td></td><td><input type="submit" value="Transform" /></td></tr>
</table>
</form>
</div>
<div>
<a href="/log">Log entries</a>
</div>
</body>
</html>

View File

@ -0,0 +1,20 @@
# Image provides a container in which to run alfresco-pdf-renderer transformations for Alfresco Content Services.
FROM quay.io/alfresco/alfresco-base-java:9
ENV ALFRESCO_PDF_RENDERER_LIB_RPM_URL=https://nexus.alfresco.com/nexus/service/local/repositories/releases/content/org/alfresco/alfresco-pdf-renderer/1.1/alfresco-pdf-renderer-1.1-linux.tgz
COPY target/alfresco-transformer-alfresco-pdf-renderer*.jar /usr/bin
RUN ln /usr/bin/alfresco-transformer-alfresco-pdf-renderer*.jar /usr/bin/alfresco-transformer-alfresco-pdf-renderer.jar && \
yum install -y wget && \
wget $ALFRESCO_PDF_RENDERER_LIB_RPM_URL && \
tar xf alfresco-pdf-renderer-*-linux.tgz -C /usr/bin && \
rm -f alfresco-pdf-renderer-*-linux.tgz && \
yum remove -y wget && \
yum clean all
EXPOSE 8090
ENTRYPOINT java -jar /usr/bin/alfresco-transformer-alfresco-pdf-renderer.jar

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,3 @@
# Welcome
This repository contains the Dockerfile that performs afresc-pdf-renderer transformations for the the ACS Repository.

View File

@ -0,0 +1,59 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformer-alfresco-pdf-renderer</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-docker-builder-alfresco-pdf-renderer</artifactId>
<name>Alfresco Content Services alfresco-pdf-renderer Docker image builder</name>
<packaging>pom</packaging>
<properties>
<dependency.alfresco-transformer-alfresco-pdf-renderer.version>${project.version}</dependency.alfresco-transformer-alfresco-pdf-renderer.version>
</properties>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-alfresco-pdf-renderer</artifactId>
<version>${dependency.alfresco-transformer-alfresco-pdf-renderer.version}</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-alfresco-pdf-renderer</artifactId>
<version>${dependency.alfresco-transformer-alfresco-pdf-renderer.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,21 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformers</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-docker-transformer-alfresco-pdf-renderer</artifactId>
<name>Alfresco Docker Transformer Alfresco PDF Renderer</name>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>alfresco-transformer-alfresco-pdf-renderer</module>
<module>docker</module>
</modules>
</project>

View File

@ -0,0 +1,66 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformer-imagemagick</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-transformer-imagemagick</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${dependency.spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-base</artifactId>
<version>${dependency.alfresco-transformer-base.version}</version>
</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>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-core</artifactId>
<version>${dependency.alfresco-core.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.0.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,38 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.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);
}
}

View File

@ -0,0 +1,270 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer;
import org.alfresco.transformer.base.AbstractTransformerController;
import org.alfresco.transformer.base.LogEntry;
import org.alfresco.transformer.base.TransformException;
import org.alfresco.util.exec.RuntimeExec;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.*;
/**
* Controller for the Docker based ImageMagick transformer.
*
*
* Status Codes:
*
* 200 Success
* 400 Bad Request: Invalid cropGravity value (North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest, Center)
* 400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)
* 400 Bad Request: Request parameter <name> is of the wrong type
* 400 Bad Request: Transformer exit code was not 0 (possible problem with the source file)
* 400 Bad Request: The source filename was not supplied
* 500 Internal Server Error: (no message with low level IO problems)
* 500 Internal Server Error: The target filename was not supplied (should not happen as targetExtension is checked)
* 500 Internal Server Error: Transformer version check exit code was not 0
* 500 Internal Server Error: Transformer version check failed to create any output
* 500 Internal Server Error: Could not read the target file
* 500 Internal Server Error: The target filename was malformed (should not happen because of other checks)
* 500 Internal Server Error: Transformer failed to create an output file (the exit code was 0, so there should be some content)
* 500 Internal Server Error: Filename encoding error
* 507 Insufficient Storage: Failed to store the source file
*/
@Controller
public class ImageMagickController extends AbstractTransformerController
{
private static final String ROOT = "/usr/lib64/ImageMagick-7.0.7";
private static final String DYN = ROOT+"/lib";
private static final String EXE = "/usr/bin/convert";
private static final List<String> GRAVITY_VALUES = Arrays.asList(
"North", "NorthEast", "East", "SouthEast", "South", "SouthWest", "West", "NorthWest", "Center");
@Autowired
public ImageMagickController()
{
logger = LogFactory.getLog(ImageMagickController.class);
setTransformCommand(createTransformCommand());
setCheckCommand(createCheckCommand());
}
private static RuntimeExec createTransformCommand()
{
RuntimeExec runtimeExec = new RuntimeExec();
Map<String, String[]> commandsAndArguments = new HashMap<>();
commandsAndArguments.put(".*", new String[]{EXE, "${source}", "SPLIT:${options}", "-strip", "-quiet", "${target}"});
runtimeExec.setCommandsAndArguments(commandsAndArguments);
Map<String, String> processProperties = new HashMap<>();
processProperties.put("MAGICK_HOME", ROOT);
processProperties.put("DYLD_FALLBACK_LIBRARY_PATH", DYN);
processProperties.put("LD_LIBRARY_PATH", DYN);
runtimeExec.setProcessProperties(processProperties);
Map<String, String> defaultProperties = new HashMap<>();
defaultProperties.put("options", null);
runtimeExec.setDefaultProperties(defaultProperties);
runtimeExec.setErrorCodes("1,2,255,400,405,410,415,420,425,430,435,440,450,455,460,465,470,475,480,485,490,495,499,700,705,710,715,720,725,730,735,740,750,755,760,765,770,775,780,785,790,795,799");
return runtimeExec;
}
private static RuntimeExec createCheckCommand()
{
RuntimeExec runtimeExec = new RuntimeExec();
Map<String, String[]> commandsAndArguments = new HashMap<>();
commandsAndArguments.put(".*", new String[]{EXE, "-version"});
runtimeExec.setCommandsAndArguments(commandsAndArguments);
return runtimeExec;
}
@PostMapping("/transform")
public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile,
@RequestParam("targetExtension") String targetExtension,
@RequestParam(value = "timeout", required = false) Long timeout,
@RequestParam(value = "startPage", required = false) Integer startPage,
@RequestParam(value = "endPage", required = false) Integer endPage,
@RequestParam(value = "alphaRemove", required = false) Boolean alphaRemove,
@RequestParam(value = "autoOrient", required = false) Boolean autoOrient,
@RequestParam(value = "cropGravity", required = false) String cropGravity,
@RequestParam(value = "cropWidth", required = false) Integer cropWidth,
@RequestParam(value = "cropHeight", required = false) Integer cropHeight,
@RequestParam(value = "cropPercentage", required = false) Boolean cropPercentage,
@RequestParam(value = "cropXOffset", required = false) Integer cropXOffset,
@RequestParam(value = "cropYOffset", required = false) Integer cropYOffset,
@RequestParam(value = "thumbnail", required = false) Boolean thumbnail,
@RequestParam(value = "resizeWidth", required = false) Integer resizeWidth,
@RequestParam(value = "resizeHeight", required = false) Integer resizeHeight,
@RequestParam(value = "resizePercentage", required = false) Boolean resizePercentage,
@RequestParam(value = "allowEnlargement", required = false) Boolean allowEnlargement,
@RequestParam(value = "maintainAspectRatio", required = false) Boolean maintainAspectRatio)
{
if (cropGravity != null)
{
cropGravity = cropGravity.trim();
if (!cropGravity.isEmpty() && !GRAVITY_VALUES.contains(cropGravity))
{
throw new TransformException(400, "Invalid cropGravity value");
}
}
String targetFilename = createTargetFileName(sourceMultipartFile, targetExtension);
File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename);
// Both files are deleted by TransformInterceptor.afterCompletion
StringJoiner args = new StringJoiner(" ");
if (alphaRemove != null && alphaRemove)
{
args.add("-alpha");
args.add(("remove"));
}
if (autoOrient != null && autoOrient)
{
args.add("-auto-orient");
}
if (cropGravity != null || cropWidth != null || cropHeight != null || cropPercentage != null ||
cropXOffset != null || cropYOffset != null)
{
if (cropGravity != null)
{
args.add("-gravity");
args.add(cropGravity);
}
StringBuilder crop = new StringBuilder("");
if (cropWidth != null && cropWidth >= 0)
{
crop.append(cropWidth);
}
if (cropHeight != null && cropHeight >= 0)
{
crop.append('x');
crop.append(cropHeight);
}
if (cropPercentage != null && cropPercentage)
{
crop.append('%');
}
if (cropXOffset != null)
{
if (cropXOffset >= 0)
{
crop.append('+');
}
crop.append(cropXOffset);
}
if (cropYOffset != null)
{
if (cropYOffset >= 0)
{
crop.append('+');
}
crop.append(cropYOffset);
}
if (crop.length() > 1)
{
args.add("-crop");
args.add(crop);
}
args.add("+repage");
}
if (resizeHeight != null || resizeWidth != null || resizePercentage !=null || maintainAspectRatio != null)
{
args.add(thumbnail != null && thumbnail ? "-thumbnail" : "-resize");
StringBuilder resize = new StringBuilder("");
if (resizeWidth != null && resizeWidth >= 0)
{
resize.append(resizeWidth);
}
if (resizeHeight != null && resizeHeight >= 0)
{
resize.append('x');
resize.append(resizeHeight);
}
if (resizePercentage != null && resizePercentage)
{
resize.append('%');
}
if (allowEnlargement == null || !allowEnlargement)
{
resize.append('>');
}
if (maintainAspectRatio != null && maintainAspectRatio)
{
resize.append('!');
}
if (resize.length() > 1)
{
args.add(resize);
}
}
String pageRange =
startPage == null
? endPage == null
? ""
: "["+endPage+']'
: endPage == null || startPage.equals(endPage)
? "["+startPage+']'
: "["+startPage+'-'+endPage+']';
String options = args.toString();
LogEntry.setOptions(pageRange+(pageRange.isEmpty() ? "" : " ")+options);
Map<String, String> properties = new HashMap<String, String>(5);
properties.put("options", options);
properties.put("source", sourceFile.getAbsolutePath()+pageRange);
properties.put("target", targetFile.getAbsolutePath());
executeTransformCommand(properties, targetFile, timeout);
return createAttachment(targetFilename, targetFile);
}
}

View File

@ -0,0 +1,46 @@
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<div>
<h2>ImageMagick 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">targetExtension *</div></td><td><input type="text" name="targetExtension" 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">startPage</div></td><td><input type="text" name="startPage" /></td></tr>
<tr><td><div style="text-align:right">endPage</div></td><td><input type="text" name="endPage" /></td></tr>
<tr><td><div style="text-align:right">alphaRemove</div></td><td><input type="text" name="alphaRemove" /></td></tr>
<tr><td><div style="text-align:right">autoOrient</div></td><td><input type="text" name="autoOrient" /></td></tr>
<tr><td><div style="text-align:right">cropGravity</div></td><td><input type="text" name="cropGravity" />North, NorthEast...Center</td></tr>
<tr><td><div style="text-align:right">cropWidth</div></td><td><input type="text" name="cropWidth" /></td></tr>
<tr><td><div style="text-align:right">cropHeight</div></td><td><input type="text" name="cropHeight" /></td></tr>
<tr><td><div style="text-align:right">cropPercentage</div></td><td><input type="checkbox" name="cropPercentage" value="true" /></td></tr>
<tr><td><div style="text-align:right">cropXOffset</div></td><td><input type="text" name="cropXOffset" /></td></tr>
<tr><td><div style="text-align:right">cropYOffset</div></td><td><input type="text" name="cropYOffset" /></td></tr>
<tr><td><div style="text-align:right">thumbnail</div></td><td><input type="checkbox" name="thumbnail" value="true" /></td></tr>
<tr><td><div style="text-align:right">resizeWidth</div></td><td><input type="text" name="resizeWidth" value="" /></td></tr>
<tr><td><div style="text-align:right">resizeHeight</div></td><td><input type="text" name="resizeHeight" value="" /></td></tr>
<tr><td><div style="text-align:right">resizePercentage</div></td><td><input type="checkbox" name="resizePercentage" value="true" /></td></tr>
<tr><td><div style="text-align:right">allowEnlargement</div></td><td><input type="checkbox" name="allowEnlargement" value="true" /></td></tr>
<tr><td><div style="text-align:right">maintainAspectRatio</div></td><td><input type="checkbox" name="maintainAspectRatio" value="true" /></td></tr>
<tr><td></td><td><input type="submit" value="Transform" /></td></tr>
</table>
</form>
</div>
<div>
<a href="/log">Log entries</a>
</div>
</body>
</html>

View File

@ -0,0 +1,21 @@
# Image provides a container in which to run ImageMagick transformations for Alfresco Content Services.
FROM quay.io/alfresco/alfresco-base-java:9
ENV IMAGEMAGICK_RPM_URL=https://www.imagemagick.org/download/linux/CentOS/x86_64/ImageMagick-7.0.7-25.x86_64.rpm
ENV IMAGEMAGICK_LIB_RPM_URL=https://www.imagemagick.org/download/linux/CentOS/x86_64/ImageMagick-libs-7.0.7-25.x86_64.rpm
COPY target/alfresco-transformer-imagemagick*.jar /usr/bin
RUN ln /usr/bin/alfresco-transformer-imagemagick*.jar /usr/bin/alfresco-transformer-imagemagick.jar && \
yum install -y wget && \
wget $IMAGEMAGICK_RPM_URL && \
wget $IMAGEMAGICK_LIB_RPM_URL && \
yum localinstall -y ImageMagick-*.x86_64.rpm && \
rm -f ImageMagick-*.x86_64.rpm && \
yum remove -y wget && \
yum clean all
EXPOSE 8090
ENTRYPOINT java -jar /usr/bin/alfresco-transformer-imagemagick.jar

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,3 @@
# Welcome
This repository contains the Dockerfile that performs ImageMagick transformations for the the ACS Repository.

View File

@ -0,0 +1,59 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformer-imagemagick</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-docker-builder-imagemagick</artifactId>
<name>Alfresco Content Services ImageMagick Docker image builder</name>
<packaging>pom</packaging>
<properties>
<dependency.alfresco-transformer-imagemagick.version>${project.version}</dependency.alfresco-transformer-imagemagick.version>
</properties>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-imagemagick</artifactId>
<version>${dependency.alfresco-transformer-imagemagick.version}</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-imagemagick</artifactId>
<version>${dependency.alfresco-transformer-imagemagick.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,21 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformers</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-docker-transformer-imagemagick</artifactId>
<name>Alfresco Docker Transformer ImageMagick</name>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>alfresco-transformer-imagemagick</module>
<module>docker</module>
</modules>
</project>

View File

@ -0,0 +1,76 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformer-libreoffice</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-transformer-libreoffice</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${dependency.spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-base</artifactId>
<version>${dependency.alfresco-transformer-base.version}</version>
</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>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-core</artifactId>
<version>${dependency.alfresco-core.version}</version>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-jodconverter-core</artifactId>
<version>3.0.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.0.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,38 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.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);
}
}

View File

@ -0,0 +1,45 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer;
import org.artofsolving.jodconverter.office.OfficeManager;
///////// THIS FILE IS A COPY OF THE CODE IN alfresco-repository /////////////
public interface JodConverter
{
/**
* Gets the JodConverter OfficeManager.
* @return
*/
public abstract OfficeManager getOfficeManager();
/**
* This method returns a boolean indicating whether the JodConverter connection to OOo is available.
* @return <code>true</code> if available, else <code>false</code>
*/
public abstract boolean isAvailable();
}

View File

@ -0,0 +1,526 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.alfresco.error.AlfrescoRuntimeException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
import org.artofsolving.jodconverter.office.OfficeException;
import org.artofsolving.jodconverter.office.OfficeManager;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
///////// THIS FILE IS A COPY OF THE CODE IN alfresco-repository /////////////
/**
* Makes use of the JodConverter library and an installed
* OpenOffice application to perform OpenOffice-driven conversions.
*
* @author Neil McErlean
*/
public class JodConverterSharedInstance implements InitializingBean, DisposableBean, JodConverter
{
private static Log logger = LogFactory.getLog(JodConverterSharedInstance.class);
private OfficeManager officeManager;
boolean isAvailable = false;
// JodConverter's built-in configuration settings.
//
// These properties are set by Spring dependency injection at system startup in the usual way.
// If the values are changed via the JMX console at runtime, then the subsystem will be stopped
// and can be restarted with the new values - meaning that JodConverter will also be stopped and restarted.
// Therefore there is no special handling required for changes to e.g. portNumbers which determines
// the number of OOo instances there should be in the pool.
//
// Numeric parameters have to be handled as Strings, as that is what Spring gives us for missing values
// e.g. if jodconverter.maxTasksPerProcess is not specified in the properties file, the value
// "${jodconverter.maxTasksPerProcess}" will be injected.
private Integer maxTasksPerProcess;
private String officeHome;
private int[] portNumbers;
private Long taskExecutionTimeout;
private Long taskQueueTimeout;
private File templateProfileDir;
private Boolean enabled;
private Long connectTimeout;
private String deprecatedOooExe;
private Boolean deprecatedOooEnabled;
private int[] deprecatedOooPortNumbers;
public void setMaxTasksPerProcess(String maxTasksPerProcess)
{
Long l = parseStringForLong(maxTasksPerProcess.trim());
if (l != null)
{
this.maxTasksPerProcess = l.intValue();
}
}
public void setOfficeHome(String officeHome)
{
this.officeHome = officeHome == null ? "" : officeHome.trim();
}
public void setDeprecatedOooExe(String deprecatedOooExe)
{
this.deprecatedOooExe = deprecatedOooExe == null ? "" : deprecatedOooExe.trim();
}
public void setPortNumbers(String s)
{
portNumbers = parsePortNumbers(s, "jodconverter");
}
public void setDeprecatedOooPort(String s)
{
deprecatedOooPortNumbers = parsePortNumbers(s, "ooo");
}
private int[] parsePortNumbers(String s, String sys)
{
int[] portNumbers = null;
s = s == null ? null : s.trim();
if (s != null && !s.isEmpty())
{
StringTokenizer tokenizer = new StringTokenizer(s, ",");
int tokenCount = tokenizer.countTokens();
portNumbers = new int[tokenCount];
for (int i = 0;tokenizer.hasMoreTokens();i++)
{
try
{
portNumbers[i] = Integer.parseInt(tokenizer.nextToken().trim());
}
catch (NumberFormatException e)
{
// Logging this as an error as this property would prevent JodConverter & therefore
// OOo from starting as specified
if (logger.isErrorEnabled())
{
logger.error("Unparseable value for property '" + sys + ".portNumbers': " + s);
}
// We'll not rethrow the exception, instead allowing the problem to be picked up
// when the OOoJodConverter subsystem is started.
}
}
}
return portNumbers;
}
public void setTaskExecutionTimeout(String taskExecutionTimeout)
{
this.taskExecutionTimeout = parseStringForLong(taskExecutionTimeout.trim());
}
public void setTemplateProfileDir(String templateProfileDir)
{
if (templateProfileDir == null || templateProfileDir.trim().length() == 0)
{
this.templateProfileDir = null;
}
else
{
File tmp = new File(templateProfileDir);
if (!tmp.isDirectory())
{
throw new AlfrescoRuntimeException("OpenOffice template profile directory "+templateProfileDir+" does not exist.");
}
this.templateProfileDir = tmp;
}
}
public void setTaskQueueTimeout(String taskQueueTimeout)
{
this.taskQueueTimeout = parseStringForLong(taskQueueTimeout.trim());
}
public void setConnectTimeout(String connectTimeout)
{
this.connectTimeout = parseStringForLong(connectTimeout.trim());
}
public void setEnabled(String enabled)
{
this.enabled = parseEnabled(enabled);
// If this is a request from the Enterprise Admin console to disable the JodConverter.
if (this.enabled == false && (deprecatedOooEnabled == null || deprecatedOooEnabled == false))
{
// We need to change isAvailable to false so we don't make calls to a previously started OfficeManger.
// In the case of Enterprise it is very unlikely that ooo.enabled will have been set to true.
this.isAvailable = false;
}
}
public void setDeprecatedOooEnabled(String deprecatedOooEnabled)
{
this.deprecatedOooEnabled = parseEnabled(deprecatedOooEnabled);
// No need to worry about isAvailable as this setting cannot be changed via the Admin console.
}
private Boolean parseEnabled(String enabled)
{
enabled = enabled == null ? "" : enabled.trim();
return Boolean.parseBoolean(enabled);
}
// So that Community systems <= Alfresco 6.0.1-ea keep working on upgrade, we may need to use the deprecated
// ooo.exe setting rather than the jodconverter.officeHome setting if we don't have the jod setting as
// oooDirect was replaced by jodconverter after this release.
String getOfficeHome()
{
String officeHome = this.officeHome;
if ((officeHome == null || officeHome.isEmpty()) && (deprecatedOooExe != null && !deprecatedOooExe.isEmpty()))
{
// It will only be possible to use the ooo.exe value if it includes a path, which itself has the officeHome
// value in it.
// jodconverter.officeHome=/opt/libreoffice5.4/
// ooo.exe=/opt/libreoffice5.4/program/soffice.bin
// jodconverter.officeHome=C:/noscan/installs/521~1.1/LIBREO~1/App/libreoffice
// ooo.exe=C:/noscan/installs/COMMUN~1.0-E/LIBREO~1/App/libreoffice/program/soffice.exe
File oooExe = new File(deprecatedOooExe);
File parent = oooExe.getParentFile();
if (parent != null && "program".equals(parent.getName()))
{
File grandparent = parent.getParentFile();
if (grandparent != null)
{
officeHome = grandparent.getPath();
}
}
}
return officeHome;
}
// So that Community systems <= Alfresco 6.0.1-ea keep working on upgrade, we may need to use the deprecated
// ooo.enabled setting if true rather than the jodconverter.enabled setting as oooDirect was replaced by
// jodconverter after this release.
// If ooo.enabled is true the JodConverter will be enabled.
// If ooo.enabled is false or unset the jodconverter.enabled value is used.
// Community set properties via alfresco-global.properties.
// Enterprise may do the same but may also reset jodconverter.enabled them via the Admin console.
// In the case of Enterprise it is very unlikely that ooo.enabled will be set to true.
boolean isEnabled()
{
return (deprecatedOooEnabled != null && deprecatedOooEnabled) || (enabled != null && enabled);
}
// So that Community systems <= Alfresco 6.0.1-ea keep working on upgrade, we may need to use the deprecated
// ooo.port setting rather than the jodconverter.portNumbers if ooo.enabled is true and jodconverter.enabled
// is false.
int[] getPortNumbers()
{
return (enabled == null || !enabled) && deprecatedOooEnabled != null && deprecatedOooEnabled
? deprecatedOooPortNumbers
: portNumbers;
}
private Long parseStringForLong(String string)
{
Long result = null;
try
{
long l = Long.parseLong(string);
result = new Long(l);
}
catch (NumberFormatException nfe)
{
if (logger.isDebugEnabled())
{
logger.debug("Cannot parse numerical value from " + string);
}
// else intentionally empty
}
return result;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.content.JodConverter#isAvailable()
*/
public boolean isAvailable()
{
final boolean result = isAvailable && officeManager != null;
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception
{
// isAvailable defaults to false afterPropertiesSet. It only becomes true on successful completion of this method.
this.isAvailable = false;
int[] portNumbers = getPortNumbers();
String officeHome = getOfficeHome();
if (logger.isDebugEnabled())
{
logger.debug("JodConverter settings (null settings will be replaced by jodconverter defaults):");
logger.debug(" officeHome = " + officeHome);
logger.debug(" enabled = " + isEnabled());
logger.debug(" portNumbers = " + getString(portNumbers));
logger.debug(" ooo.exe = " + deprecatedOooExe);
logger.debug(" ooo.enabled = " + deprecatedOooEnabled);
logger.debug(" ooo.port = " + getString(deprecatedOooPortNumbers));
logger.debug(" jodConverter.enabled = " + enabled);
logger.debug(" jodconverter.portNumbers = " + getString(this.portNumbers));
logger.debug(" jodconverter.officeHome = " + this.officeHome);
logger.debug(" jodconverter.maxTasksPerProcess = " + maxTasksPerProcess);
logger.debug(" jodconverter.taskExecutionTimeout = " + taskExecutionTimeout);
logger.debug(" jodconverter.taskQueueTimeout = " + taskQueueTimeout);
logger.debug(" jodconverter.connectTimeout = " + connectTimeout);
}
// Only start the JodConverter instance(s) if the subsystem is enabled.
if (isEnabled() == false)
{
return;
}
logAllSofficeFilesUnderOfficeHome();
try
{
DefaultOfficeManagerConfiguration defaultOfficeMgrConfig = new DefaultOfficeManagerConfiguration();
if (maxTasksPerProcess != null && maxTasksPerProcess > 0)
{
defaultOfficeMgrConfig.setMaxTasksPerProcess(maxTasksPerProcess);
}
if (officeHome != null)
{
defaultOfficeMgrConfig.setOfficeHome(officeHome);
}
if (portNumbers != null && portNumbers.length != 0)
{
defaultOfficeMgrConfig.setPortNumbers(portNumbers);
}
if (taskExecutionTimeout != null && taskExecutionTimeout > 0)
{
defaultOfficeMgrConfig.setTaskExecutionTimeout(taskExecutionTimeout);
}
if (taskQueueTimeout != null && taskQueueTimeout > 0)
{
defaultOfficeMgrConfig.setTaskQueueTimeout(taskQueueTimeout);
}
if (templateProfileDir != null)
{
defaultOfficeMgrConfig.setTemplateProfileDir(templateProfileDir);
}
if (connectTimeout != null)
{
defaultOfficeMgrConfig.setConnectTimeout(connectTimeout);
}
// Try to configure and start the JodConverter library.
officeManager = defaultOfficeMgrConfig.buildOfficeManager();
officeManager.start();
}
catch (IllegalStateException isx)
{
if (logger.isErrorEnabled())
{
logger.error("Unable to pre-initialise JodConverter library. "
+ "The following error is shown for informational purposes only.", isx);
}
return;
}
catch (OfficeException ox)
{
if (logger.isErrorEnabled())
{
logger.error("Unable to start JodConverter library. "
+ "The following error is shown for informational purposes only.", ox);
}
// We need to let it continue (comment-out return statement) even if an error occurs. See MNT-13706 and associated issues.
//return;
}
catch (Exception x)
{
if (logger.isErrorEnabled())
{
logger.error("Unexpected error in configuring or starting the JodConverter library."
+ "The following error is shown for informational purposes only.",x);
}
return;
}
// If any exceptions are thrown in the above code, then isAvailable
// should remain false, hence the return statements.
this.isAvailable = true;
}
private String getString(int[] portNumbers)
{
StringBuilder portInfo = new StringBuilder();
if (portNumbers != null)
{
for (int i = 0;i < portNumbers.length;i++)
{
portInfo.append(portNumbers[i]);
if (i < portNumbers.length - 1)
{
portInfo.append(", ");
}
}
}
return portInfo.toString();
}
private void logAllSofficeFilesUnderOfficeHome()
{
if (logger.isDebugEnabled() == false)
{
return;
}
String officeHome = getOfficeHome();
File requestedOfficeHome = new File(officeHome);
logger.debug("Some information on soffice* files and their permissions");
logFileInfo(requestedOfficeHome);
for (File f : findSofficePrograms(requestedOfficeHome, new ArrayList<File>(), 2))
{
logFileInfo(f);
}
}
private List<File> findSofficePrograms(File searchRoot, List<File> results, int maxRecursionDepth)
{
return this.findSofficePrograms(searchRoot, results, 0, maxRecursionDepth);
}
private List<File> findSofficePrograms(File searchRoot, List<File> results,
int currentRecursionDepth, int maxRecursionDepth)
{
if (currentRecursionDepth >= maxRecursionDepth)
{
return results;
}
File[] matchingFiles = searchRoot.listFiles(new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
return name.startsWith("soffice");
}
});
for (File f : matchingFiles)
{
results.add(f);
}
for (File dir : searchRoot.listFiles(new FileFilter()
{
@Override
public boolean accept(File f) {
return f.isDirectory();
}
}))
{
findSofficePrograms(dir, results, currentRecursionDepth + 1, maxRecursionDepth);
}
return results;
}
/**
* Logs some information on the specified file, including name and r/w/x permissions.
* @param f the file to log.
*/
private void logFileInfo(File f)
{
if (logger.isDebugEnabled() == false)
{
return;
}
StringBuilder msg = new StringBuilder();
msg.append(f).append(" ");
if (f.exists())
{
if (f.canRead())
{
msg.append("(")
.append(f.isDirectory() ? "d" : "-")
.append(f.canRead() ? "r" : "-")
.append(f.canWrite() ? "w" : "-")
.append(f.canExecute() ? "x" : "-")
.append(")");
}
}
else
{
msg.append("does not exist");
}
logger.debug(msg.toString());
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
this.isAvailable = false;
if (officeManager != null)
{
// If there is an OfficeException when stopping the officeManager below, then there is
// little that can be done other than logging the exception and carrying on. The JodConverter-based
// libraries will not be used in any case, as isAvailable is false.
//
// Any exception thrown out of this method will be logged and swallowed by Spring
// (see javadoc for method declaration). Therefore there is no handling here for
// exceptions from jodConverter.
officeManager.stop();
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.content.JodConverterWorker#getOfficeManager()
*/
public OfficeManager getOfficeManager()
{
return officeManager;
}
}

View File

@ -0,0 +1,220 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer;
import com.sun.star.task.ErrorCodeIOException;
import org.alfresco.transformer.base.AbstractTransformerController;
import org.alfresco.transformer.base.TransformException;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.OfficeException;
import org.artofsolving.jodconverter.office.OfficeManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
/**
* Controller for the Docker based LibreOffice transformer.
*
*
* Status Codes:
*
* 200 Success
* 400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)
* 400 Bad Request: Request parameter <name> is of the wrong type
* 400 Bad Request: Transformer exit code was not 0 (possible problem with the source file)
* 400 Bad Request: The source filename was not supplied
* 500 Internal Server Error: (no message with low level IO problems)
* 500 Internal Server Error: The target filename was not supplied (should not happen as targetExtension is checked)
* 500 Internal Server Error: Transformer version check exit code was not 0
* 500 Internal Server Error: Transformer version check failed to create any output
* 500 Internal Server Error: Could not read the target file
* 500 Internal Server Error: The target filename was malformed (should not happen because of other checks)
* 500 Internal Server Error: Transformer failed to create an output file (the exit code was 0, so there should be some content)
* 500 Internal Server Error: Filename encoding error
* 507 Insufficient Storage: Failed to store the source file
*/
@Controller
public class LibreOfficeController extends AbstractTransformerController
{
private static final String OFFICE_HOME = "/opt/libreoffice5.4";
private static final int JODCONVERTER_TRANSFORMATION_ERROR_CODE = 3088;
private JodConverter jodconverter;
@Autowired
public LibreOfficeController() throws Exception
{
logger = LogFactory.getLog(LibreOfficeController.class);
setJodConverter(createJodConverter());
}
private static JodConverter createJodConverter() throws Exception
{
JodConverterSharedInstance jodconverter = new JodConverterSharedInstance();
jodconverter.setOfficeHome(OFFICE_HOME); // jodconverter.officeHome
jodconverter.setMaxTasksPerProcess("200"); // jodconverter.maxTasksPerProcess
jodconverter.setTaskExecutionTimeout("120000"); // jodconverter.maxTasksPerProcess
jodconverter.setTaskQueueTimeout("30000"); // jodconverter.taskQueueTimeout
jodconverter.setConnectTimeout("28000"); // jodconverter.connectTimeout
jodconverter.setPortNumbers("8100"); // jodconverter.portNumbers
jodconverter.setTemplateProfileDir(""); // jodconverter.templateProfileDir
jodconverter.setEnabled("true"); // jodconverter.enabled
jodconverter.afterPropertiesSet();
return jodconverter;
}
public void setJodConverter(JodConverter jodconverter)
{
this.jodconverter = jodconverter;
}
@PostMapping("/transform")
public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile,
@RequestParam("targetExtension") String targetExtension,
@RequestParam(value = "timeout", required = false) Long timeout)
{
String targetFilename = createTargetFileName(sourceMultipartFile, targetExtension);
File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename);
// Both files are deleted by TransformInterceptor.afterCompletion
executeTransformCommand(sourceFile, targetFile);
return createAttachment(targetFilename, targetFile);
}
protected void executeTransformCommand(File sourceFile, File targetFile)
{
try
{
OfficeManager officeManager = jodconverter.getOfficeManager();
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
converter.convert(sourceFile, targetFile);
}
catch (OfficeException e)
{
throw new TransformException(500, "LibreOffice server conversion failed: \n"+
" from file: " + sourceFile + "\n" +
" to file: " + targetFile,
e);
}
catch (Throwable throwable)
{
// Because of the known bug with empty Spreadsheets in JodConverter try to catch exception and produce empty pdf file
if (throwable.getCause() instanceof ErrorCodeIOException &&
((ErrorCodeIOException) throwable.getCause()).ErrCode == JODCONVERTER_TRANSFORMATION_ERROR_CODE)
{
logger.warn("Transformation failed: \n" +
"from file: " + sourceFile + "\n" +
"to file: " + targetFile +
"Source file " + sourceFile + " has no content");
produceEmptyPdfFile(targetFile);
}
else
{
throw throwable;
}
}
if (!targetFile.exists() || targetFile.length() == 0L)
{
throw new TransformException(500, "Transformer failed to create an output file");
}
}
/**
* This method produces an empty PDF file at the specified File location.
* Apache's PDFBox is used to create the PDF file.
*/
private void produceEmptyPdfFile(File targetFile)
{
// If improvement PDFBOX-914 is incorporated, we can do this with a straight call to
// org.apache.pdfbox.TextToPdf.createPDFFromText(new StringReader(""));
// https://issues.apache.org/jira/browse/PDFBOX-914
PDDocument pdfDoc = null;
PDPageContentStream contentStream = null;
try
{
pdfDoc = new PDDocument();
PDPage pdfPage = new PDPage();
// Even though, we want an empty PDF, some libs (e.g. PDFRenderer) object to PDFs
// that have literally nothing in them. So we'll put a content stream in it.
contentStream = new PDPageContentStream(pdfDoc, pdfPage);
pdfDoc.addPage(pdfPage);
// Now write the in-memory PDF document into the temporary file.
pdfDoc.save(targetFile.getAbsolutePath());
}
catch (IOException iox)
{
throw new TransformException(500, "Error creating empty PDF file", iox);
}
finally
{
if (contentStream != null)
{
try
{
contentStream.close();
}
catch (IOException ignored)
{
// Intentionally empty
}
}
if (pdfDoc != null)
{
try
{
pdfDoc.close();
}
catch (IOException ignored)
{
// Intentionally empty.
}
}
}
}
}

View File

@ -0,0 +1,26 @@
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<div>
<h2>LiberOffice 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">targetExtension *</div></td><td><input type="text" name="targetExtension" 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 entries</a>
</div>
</body>
</html>

View File

@ -0,0 +1,21 @@
# Image provides a container in which to run LibreOffice transformations for Alfresco Content Services.
FROM quay.io/alfresco/alfresco-base-java:9
ENV LIBREOFFICE_RPM_URL=http://download.documentfoundation.org/libreoffice/stable/5.4.5/rpm/x86_64/LibreOffice_5.4.5_Linux_x86-64_rpm.tar.gz
COPY target/alfresco-transformer-libreoffice*.jar /usr/bin
RUN ln /usr/bin/alfresco-transformer-libreoffice*.jar /usr/bin/alfresco-transformer-libreoffice.jar && \
yum install -y wget && \
yum install -y cairo cups-libs libSM && \
wget $LIBREOFFICE_RPM_URL && \
tar xzf LibreOffice_*_Linux_x86-64_rpm.tar.gz && \
yum localinstall -y LibreOffice*/RPMS/*.rpm && \
rm -rf LibreOffice_*_Linux_x86-64_rpm.tar.gz LibreOffice_*_Linux_x86-64_rpm && \
yum remove -y wget && \
yum clean all
EXPOSE 8090
ENTRYPOINT java -jar /usr/bin/alfresco-transformer-libreoffice.jar

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,3 @@
# Welcome
This repository contains the Dockerfile that performs LibreOffice transformations for the the ACS Repository.

View File

@ -0,0 +1,59 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformer-libreoffice</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-docker-builder-libreoffice</artifactId>
<name>Alfresco Content Services LibreOffice Docker image builder</name>
<packaging>pom</packaging>
<properties>
<dependency.alfresco-transformer-libreoffice.version>${project.version}</dependency.alfresco-transformer-libreoffice.version>
</properties>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-libreoffice</artifactId>
<version>${dependency.alfresco-transformer-libreoffice.version}</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-libreoffice</artifactId>
<version>${dependency.alfresco-transformer-libreoffice.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,21 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformers</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>alfresco-docker-transformer-libreoffice</artifactId>
<name>Alfresco Docker Transformer LibreOffice</name>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>alfresco-transformer-libreoffice</module>
<module>docker</module>
</modules>
</project>

View File

@ -0,0 +1,170 @@
# Common code for Docker based ACS transformers
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.
A transformer project is expected to provide the following files:
~~~
src/main/resources/templates/transformForm.html
src/main/java/org/alfresco/transformer/<XXX>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
parameters so they may be used to test the transformer.
~~~
<html xmlns:th="http://www.thymeleaf.org">
<body>
<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">targetFilename *</div></td><td><input type="text" name="targetFilename" value="" /></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">height</div></td><td><input type="text" name="height" value="" /></td></tr>
<tr><td><div style="text-align:right">allowEnlargement</div></td><td><input type="checkbox" name="allowEnlargement" value="true" /></td></tr>
<tr><td><div style="text-align:right">maintainAspectRatio</div></td><td><input type="checkbox" name="maintainAspectRatio" value="true" /></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">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 entries</a>
</div>
</body>
</html>
~~~
* *TransformerName*Controller.java - A [Spring Boot](https://projects.spring.io/spring-boot/) Controller that
extends AbstractTransformerController to handel a POST request to *"/transform"*.
~~~
...
@Controller
public class AlfrescoPdfRendererController extends AbstractTransformerController
{
...
@PostMapping("/transform")
public ResponseEntity<Resource> 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 = "allowEnlargement", required = false) Boolean allowEnlargement,
@RequestParam(value = "maintainAspectRatio", required = false) Boolean maintainAspectRatio,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "timeout", required = false) Long timeout)
{
try
{
File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename);
// Both files are deleted by TransformInterceptor.afterCompletion
StringJoiner args = new StringJoiner(" ");
if (width != null)
{
args.add("--width=" + width);
}
if (height != null)
{
args.add("--height=" + height);
}
if (allowEnlargement != null && allowEnlargement)
{
args.add("--allow-enlargement");
}
if (maintainAspectRatio != null && maintainAspectRatio)
{
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<String, String>(5);
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);
}
}
}
~~~
* 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);
}
}
~~~
## Building and testing
The project can be built by running the Maven command:
~~~
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-transformer-base</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 in Bamboo is PLAT-TB
## Contributing guide
Please use [this guide](https://github.com/Alfresco/alfresco-jodconverter/blob/master/CONTRIBUTING.md) to make a
contribution to the project.

View File

@ -0,0 +1,44 @@
<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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformers</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-transformer-base</artifactId>
<version>0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${dependency.spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<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>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-core</artifactId>
<version>${dependency.alfresco-core.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,38 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.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);
}
}

View File

@ -0,0 +1,330 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer.base;
import org.alfresco.util.TempFileProvider;
import org.alfresco.util.exec.RuntimeExec;
import org.apache.commons.logging.Log;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.Map;
/**
* Abstract Controller, provides structure and helper methods to sub-class transformer controllers.
*
* Status Codes:
*
* 200 Success
* 400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)
* 400 Bad Request: Request parameter <name> is of the wrong type
* 400 Bad Request: Transformer exit code was not 0 (possible problem with the source file)
* 400 Bad Request: The source filename was not supplied
* 500 Internal Server Error: (no message with low level IO problems)
* 500 Internal Server Error: The target filename was not supplied (should not happen as targetExtension is checked)
* 500 Internal Server Error: Transformer version check exit code was not 0
* 500 Internal Server Error: Transformer version check failed to create any output
* 500 Internal Server Error: Could not read the target file
* 500 Internal Server Error: The target filename was malformed (should not happen because of other checks)
* 500 Internal Server Error: Transformer failed to create an output file (the exit code was 0, so there should be some content)
* 500 Internal Server Error: Filename encoding error
* 507 Insufficient Storage: Failed to store the source file
*
* 408 Request Timeout -- TODO implement general timeout mechanism rather than depend on transformer timeout (might be possible for external processes)
* 415 Unsupported Media Type -- TODO possibly implement a check on supported source and target mimetypes (probably not)
* 429 Too Many Requests -- TODO implement general throttling mechanism (needs to be done)
*/
public abstract class AbstractTransformerController
{
public static final String SOURCE_FILE = "sourceFile";
public static final String TARGET_FILE = "targetFile";
protected static Log logger;
protected RuntimeExec transformCommand;
private RuntimeExec checkCommand;
public void setTransformCommand(RuntimeExec runtimeExec)
{
transformCommand = runtimeExec;
}
public void setCheckCommand(RuntimeExec runtimeExec)
{
checkCommand = runtimeExec;
}
@RequestMapping("/version")
@ResponseBody
String version()
{
String version = "Version not checked";
if (checkCommand != null)
{
RuntimeExec.ExecutionResult result = checkCommand.execute();
if (result.getExitValue() != 0 && result.getStdErr() != null && result.getStdErr().length() > 0)
{
throw new TransformException(500, "Transformer version check exit code was not 0: \n" + result);
}
version = result.getStdOut().trim();
if (version.isEmpty())
{
throw new TransformException(500, "Transformer version check failed to create any output");
}
}
return version;
}
@GetMapping("/")
public String transformForm(Model model)
{
return "transformForm"; // the name of the template
}
@GetMapping("/log")
public String log(Model model)
{
Collection<LogEntry> log = LogEntry.getLog();
if (!log.isEmpty())
{
model.addAttribute("log", log);
}
return "log"; // the name of the template
}
@ExceptionHandler(TypeMismatchException.class)
public void handleParamsTypeMismatch(HttpServletResponse response, MissingServletRequestParameterException e) throws IOException
{
String name = e.getParameterName();
String message = "Request parameter " + name + " is of the wrong type";
int statusCode = 400;
if (logger != null && logger.isErrorEnabled())
{
logger.error(message);
}
LogEntry.setStatusCodeAndMessage(statusCode, message);
response.sendError(statusCode, message);
}
@ExceptionHandler(MissingServletRequestParameterException.class)
public void handleMissingParams(HttpServletResponse response, MissingServletRequestParameterException e) throws IOException
{
String name = e.getParameterName();
String message = "Request parameter " + name + " is missing";
int statusCode = 400;
if (logger != null && logger.isErrorEnabled())
{
logger.error(message);
}
LogEntry.setStatusCodeAndMessage(statusCode, message);
response.sendError(statusCode, message);
}
@ExceptionHandler(TransformException.class)
public void transformExceptionWithMessage(HttpServletResponse response, TransformException e) throws IOException
{
String message = e.getMessage();
int statusCode = e.getStatusCode();
if (logger != null && logger.isErrorEnabled())
{
logger.error(message);
}
LogEntry.setStatusCodeAndMessage(statusCode, message);
response.sendError(statusCode, message);
}
protected String createTargetFileName(MultipartFile sourceMultipartFile, String targetExtension)
{
String targetFilename = null;
String sourceFilename = sourceMultipartFile.getOriginalFilename();
sourceFilename = StringUtils.getFilename(sourceFilename);
if (sourceFilename != null && !sourceFilename.isEmpty())
{
String ext = StringUtils.getFilenameExtension(sourceFilename);
if (ext != null && !ext.isEmpty())
{
targetFilename =sourceFilename.substring(0, sourceFilename.length()-ext.length()-1)+'.'+targetExtension;
}
}
return targetFilename;
}
/**
* Returns a File that holds the source content for a transformation.
*
* @param request
* @param multipartFile from the request
* @return a temporary File.
* @throws TransformException if there was no source filename.
*/
protected File createSourceFile(HttpServletRequest request, MultipartFile multipartFile)
{
String filename = multipartFile.getOriginalFilename();
long size = multipartFile.getSize();
filename = checkFilename( true, filename);
File file = TempFileProvider.createTempFile("source_", "_" + filename);
request.setAttribute(SOURCE_FILE, file);
save(multipartFile, file);
LogEntry.setSource(filename, size);
return file;
}
/**
* Returns a File to be used to store the result of a transformation.
*
* @param request
* @param filename The targetFilename supplied in the request. Only the filename if a path is used as part of the
* temporary filename.
* @return a temporary File.
* @throws TransformException if there was no target filename.
*/
protected File createTargetFile(HttpServletRequest request, String filename)
{
filename = checkFilename( false, filename);
LogEntry.setTarget(filename);
File file = TempFileProvider.createTempFile("target_", "_" + filename);
request.setAttribute(TARGET_FILE, file);
return file;
}
/**
* Checks the filename is okay to uses in a temporary file name.
*
* @param filename or path to be checked.
* @return the filename part of the supplied filename if it was a path.
* @throws TransformException if there was no target filename.
*/
private String checkFilename(boolean source, String filename)
{
filename = StringUtils.getFilename(filename);
if (filename == null || filename.isEmpty())
{
String sourceOrTarget = source ? "source" : "target";
int statusCode = source ? 400 : 500;
throw new TransformException(statusCode, "The " + sourceOrTarget + " filename was not supplied");
}
return filename;
}
private void save(MultipartFile multipartFile, File file)
{
try
{
Files.copy(multipartFile.getInputStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException e)
{
throw new TransformException(507, "Failed to store the source file", e);
}
}
private Resource load(File file)
{
try
{
Resource resource = new UrlResource(file.toURI());
if (resource.exists() || resource.isReadable())
{
return resource;
}
else
{
throw new TransformException(500, "Could not read the target file: " + file.getPath());
}
}
catch (MalformedURLException e)
{
throw new TransformException(500, "The target filename was malformed: " + file.getPath(), e);
}
}
protected void executeTransformCommand(Map<String, String> properties, File targetFile, Long timeout)
{
long timeoutMs = timeout != null && timeout > 0 ? timeout : 0;
RuntimeExec.ExecutionResult result = transformCommand.execute(properties, timeoutMs);
if (result.getExitValue() != 0 && result.getStdErr() != null && result.getStdErr().length() > 0)
{
throw new TransformException(400, "Transformer exit code was not 0: \n" + result);
}
if (!targetFile.exists() || targetFile.length() == 0)
{
throw new TransformException(500, "Transformer failed to create an output file");
}
}
protected ResponseEntity<Resource> createAttachment(String targetFilename, File targetFile)
{
try
{
Resource targetResource = load(targetFile);
targetFilename = UriUtils.encodePath(StringUtils.getFilename(targetFilename), "UTF-8");
ResponseEntity<Resource> body = ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename*= UTF-8''" + targetFilename).body(targetResource);
LogEntry.setTargetSize(targetFile.length());
LogEntry.setStatusCodeAndMessage(200, "Success");
return body;
}
catch (UnsupportedEncodingException e)
{
throw new TransformException(500, "Filename encoding error", e);
}
}
}

View File

@ -0,0 +1,248 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer.base;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Provides setter and getter methods to allow the current Thread to set various log properties and for these
* values to be retrieved. The {@link #complete()} method should be called at the end of a request to flush the
* current entry to an internal log Collection of the latest entries. The {@link #getLog()} method is used to obtain
* access to this collection.
*/
public class LogEntry
{
private static final AtomicInteger count = new AtomicInteger(0);
private static final Deque<LogEntry> log = new ConcurrentLinkedDeque<>();
private static final int MAX_LOG_SIZE = 10;
private static ThreadLocal<LogEntry> currentLogEntry = new ThreadLocal<LogEntry>()
{
@Override
protected LogEntry initialValue()
{
LogEntry logEntry = new LogEntry();
if (log.size() >= MAX_LOG_SIZE)
{
log.removeLast();
}
log.addFirst(logEntry);
return logEntry;
}
};
private final int id = count.incrementAndGet();
private final long start = System.currentTimeMillis();
private int statusCode;
private long durationStreamIn;
private long durationTransform;
private long durationStreamOut;
private String source;
private long sourceSize;
private String target;
private long targetSize;
private String options;
private String message;
public static Collection<LogEntry> getLog()
{
return log;
}
public static void start()
{
currentLogEntry.get();
}
public static void setSource(String source, long sourceSize)
{
LogEntry logEntry = currentLogEntry.get();
logEntry.source = getExtension(source);
logEntry.sourceSize = sourceSize;
logEntry.durationStreamIn = System.currentTimeMillis() - logEntry.start;
}
public static void setTarget(String target)
{
currentLogEntry.get().target = getExtension(target);
}
private static String getExtension(String filename)
{
int i = filename.lastIndexOf('.');
if (i != -1)
{
filename = filename.substring(i+1);
}
return filename;
}
public static void setTargetSize(long targetSize)
{
currentLogEntry.get().targetSize = targetSize;
}
public static void setOptions(String options)
{
currentLogEntry.get().options = options;
}
public static void setStatusCodeAndMessage(int statusCode, String message)
{
LogEntry logEntry = currentLogEntry.get();
logEntry.statusCode = statusCode;
logEntry.message = message;
logEntry.durationTransform = System.currentTimeMillis() - logEntry.start - logEntry.durationStreamIn;
}
public static void complete()
{
LogEntry logEntry = currentLogEntry.get();
logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start - logEntry.durationStreamIn - logEntry.durationTransform;
currentLogEntry.remove();
}
public int getId()
{
return id;
}
public Date getDate()
{
return new Date(start);
}
public int getStatusCode()
{
return statusCode;
}
public String getDuration()
{
return time(durationStreamIn + durationTransform + durationStreamOut)+" ("+
time(durationStreamIn)+' '+time(durationTransform)+' '+time(durationStreamOut)+")";
}
public long getDurationStreamIn()
{
return durationStreamIn;
}
public long getDurationTransform()
{
return durationTransform;
}
public long getDurationStreamOut()
{
return durationStreamOut;
}
public String getSource()
{
return source;
}
public String getSourceSize()
{
return size(sourceSize);
}
public String getTarget()
{
return target;
}
public String getTargetSize()
{
return size(targetSize);
}
public String getOptions()
{
return options;
}
public String getMessage()
{
return message;
}
private String time(long ms)
{
return size(ms, "1 ms",
new String[] { "ms", "s", "min", "hr" },
new long[] { 1000, 60*1000, 60*60*1000, Long.MAX_VALUE});
}
private String size(long size)
{
return size(size, "1 byte",
new String[] { "bytes", "KB", "MB", "GB", "TB" },
new long[] { 1024, 1024*1024, 1024*1024*1024, 1024*1024*1024*1024, Long.MAX_VALUE });
}
private String size(long size, String singleValue, String[] units, long[] dividers)
{
if (size == 1)
{
return singleValue;
}
long divider = 1;
for(int i = 0; i < units.length-1; i++)
{
long nextDivider = dividers[i];
if(size < nextDivider)
{
return unitFormat(size, divider, units[i]);
}
divider = nextDivider;
}
return unitFormat(size, divider, units[units.length-1]);
}
private String unitFormat(long size, long divider, String unit)
{
size = size * 10 / divider;
int decimalPoint = (int) size % 10;
StringBuilder sb = new StringBuilder();
sb.append(size/10);
if (decimalPoint != 0)
{
sb.append(".");
sb.append(decimalPoint);
}
sb.append(' ');
sb.append(unit);
return sb.toString();
}
}

View File

@ -0,0 +1,47 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer.base;
public class TransformException extends RuntimeException
{
private int statusCode;
public TransformException(int statusCode, String message)
{
super(message);
this.statusCode = statusCode;
}
public TransformException(int statusCode, String message, Throwable cause)
{
super(message, cause);
}
public int getStatusCode()
{
return statusCode;
}
}

View File

@ -0,0 +1,65 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer.base;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
public class TransformInterceptor extends HandlerInterceptorAdapter
{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception
{
LogEntry.start();
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception
{
// TargetFile cannot be deleted until completion, otherwise 0 bytes are sent.
deleteFile(request, "sourceFile");
deleteFile(request, "targetFile");
LogEntry.complete();
}
private void deleteFile(HttpServletRequest request, String attributeName)
{
File file = (File) request.getAttribute(attributeName);
if (file != null)
{
file.delete();
}
}
}

View File

@ -0,0 +1,45 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer.base;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebApplicationConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(transformInterceptor()).addPathPatterns("/transform");;
}
@Bean
public TransformInterceptor transformInterceptor() {
return new TransformInterceptor();
}
}

View File

@ -0,0 +1,7 @@
spring.http.multipart.max-file-size=8192MB
spring.http.multipart.max-request-size=8192MB
server.port = 8090
logging.level.org.alfresco.util.exec.RuntimeExec=debug
logging.level.org.alfresco.transformer.LibreOfficeController=debug
logging.level.org.alfresco.transformer.JodConverterSharedInstance=debug

View File

@ -0,0 +1,45 @@
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<h2>Log entries</h2>
<div th:if="${log}">
<table>
<tr>
<th>Id</th>
<th>Time</th>
<th>Status Code</th>
<th>Duration (ms)</th>
<th>Source</th>
<th></th>
<th>Target</th>
<th></th>
<th>Options</th>
<th>Message</th>
</tr>
<tr th:each="entry : ${log}">
<td th:text="${entry.id}"></td>
<td th:text="${#dates.format(entry.date, 'HH:mm:ss')}"></td>
<td th:text="${entry.statusCode}"></td>
<td th:text="${entry.duration}"></td>
<td th:text="${entry.source}"></td>
<td th:text="${entry.sourceSize}"></td>
<td th:text="${entry.target}"></td>
<td th:text="${entry.targetSize}"></td>
<td th:text="${entry.options}"></td>
<td th:text="${entry.message}"></td>
</tr>
</table>
</div>
<div>
<br/>
<a href="/">Test Transformation</a>
</div>
</body>
</html>

49
pom.xml Normal file
View File

@ -0,0 +1,49 @@
<?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>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-super-pom</artifactId>
<version>9</version>
</parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-docker-transformers</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<dependency.spring-boot.version>1.5.10.RELEASE</dependency.spring-boot.version>
<dependency.alfresco-core.version>6.18</dependency.alfresco-core.version>
<dependency.alfresco-transformer-base.version>${project.version}</dependency.alfresco-transformer-base.version>
</properties>
<modules>
<module>alfresco-transformer-base</module>
<module>alfresco-docker-alfresco-pdf-renderer</module>
<module>alfresco-docker-imagemagick</module>
<module>alfresco-docker-libreoffice</module>
</modules>
<scm>
<connection>scm:git@git.alfresco.com:Repository/alfresco-docker-transformers.git</connection>
<developerConnection>scm:git@git.alfresco.com:Repository/alfresco-docker-transformers.git</developerConnection>
<url>https://git.alfresco.com/Repository/alfresco-docker-transformers.git</url>
<tag>HEAD</tag>
</scm>
<distributionManagement>
<repository>
<id>alfresco-internal</id>
<url>https://artifacts.alfresco.com/nexus/content/repositories/releases</url>
</repository>
<snapshotRepository>
<id>alfresco-internal-snapshots</id>
<url>https://artifacts.alfresco.com/nexus/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
</project>