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

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>