Merge branch 'master' into fix/mnt-20337_LO_6.1.6

This commit is contained in:
Cezar.Leahu 2019-08-19 18:43:27 +03:00
commit 2cdde6928c
64 changed files with 1559 additions and 1260 deletions

View File

@ -65,7 +65,6 @@
<artifactId>dom4j</artifactId> <artifactId>dom4j</artifactId>
<version>2.1.1</version> <version>2.1.1</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -30,11 +30,10 @@ import static org.alfresco.transformer.fs.FileManager.createAttachment;
import static org.alfresco.transformer.fs.FileManager.createSourceFile; import static org.alfresco.transformer.fs.FileManager.createSourceFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFile; import static org.alfresco.transformer.fs.FileManager.createTargetFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFileName; import static org.alfresco.transformer.fs.FileManager.createTargetFileName;
import static org.alfresco.transformer.logging.StandardMessages.LICENCE;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -46,7 +45,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -58,38 +56,30 @@ import org.springframework.web.multipart.MultipartFile;
* *
* Status Codes: * Status Codes:
* *
* 200 Success * 200 Success
* 400 Bad Request: Request parameter <name> is missing (missing mandatory parameter) * 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: 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: Transformer exit code was not 0 (possible problem with the source file)
* 400 Bad Request: The source filename was not supplied * 400 Bad Request: The source filename was not supplied
* 500 Internal Server Error: (no message with low level IO problems) * 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: 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 exit code was not 0
* 500 Internal Server Error: Transformer version check failed to create any output * 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: 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: 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: 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 * 500 Internal Server Error: Filename encoding error
* 507 Insufficient Storage: Failed to store the source file * 507 Insufficient Storage: Failed to store the source file
*/ */
@Controller @Controller
public class AlfrescoPdfRendererController extends AbstractTransformerController public class AlfrescoPdfRendererController extends AbstractTransformerController
{ {
private static final Logger logger = LoggerFactory.getLogger(AlfrescoPdfRendererController.class); private static final Logger logger = LoggerFactory.getLogger(
AlfrescoPdfRendererController.class);
@Autowired @Autowired
private PdfRendererCommandExecutor commandExecutor; private PdfRendererCommandExecutor commandExecutor;
@Autowired
public AlfrescoPdfRendererController()
{
logger.info("-----------------------------------------------------------------------------------------------------------------------------------------------------------");
Arrays.stream(LICENCE.split("\\n")).forEach(logger::info);
logger.info("alfresco-pdf-renderer uses the PDFium library from Google Inc. See the license at https://pdfium.googlesource.com/pdfium/+/master/LICENSE or in /pdfium.txt");
logger.info("-----------------------------------------------------------------------------------------------------------------------------------------------------------");
}
@Override @Override
public String getTransformerName() public String getTransformerName()
{ {
@ -137,20 +127,21 @@ public class AlfrescoPdfRendererController extends AbstractTransformerController
} }
@Deprecated @Deprecated
@PostMapping(value = "/transform", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/transform", consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Resource> transform(HttpServletRequest request, public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile, @RequestParam("file") MultipartFile sourceMultipartFile,
@RequestParam("targetExtension") String targetExtension, @RequestParam("targetExtension") String targetExtension,
@RequestParam(value = "timeout", required = false) Long timeout, @RequestParam(value = "timeout", required = false) Long timeout,
@RequestParam(value = "testDelay", required = false) Long testDelay, @RequestParam(value = "testDelay", required = false) Long testDelay,
@RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "width", required = false) Integer width, @RequestParam(value = "width", required = false) Integer width,
@RequestParam(value = "height", required = false) Integer height, @RequestParam(value = "height", required = false) Integer height,
@RequestParam(value = "allowPdfEnlargement", required = false) Boolean allowPdfEnlargement, @RequestParam(value = "allowPdfEnlargement", required = false) Boolean allowPdfEnlargement,
@RequestParam(value = "maintainPdfAspectRatio", required = false) Boolean maintainPdfAspectRatio) @RequestParam(value = "maintainPdfAspectRatio", required = false) Boolean maintainPdfAspectRatio)
{ {
String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(), targetExtension); String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(),
targetExtension);
getProbeTestTransform().incrementTransformerCount(); getProbeTestTransform().incrementTransformerCount();
File sourceFile = createSourceFile(request, sourceMultipartFile); File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename); File targetFile = createTargetFile(request, targetFilename);

View File

@ -26,30 +26,52 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static org.alfresco.transformer.logging.StandardMessages.LICENCE;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
@SpringBootApplication @SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class Application public class Application
{ {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
@Value("${container.name}") @Value("${container.name}")
private String containerName; private String containerName;
@Bean @Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() { public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags()
{
return registry -> registry.config().commonTags("containerName", containerName); return registry -> registry.config().commonTags("containerName", containerName);
} }
public static void main(String[] args) public static void main(String[] args)
{ {
SpringApplication.run(Application.class, args); SpringApplication.run(Application.class, args);
} }
@EventListener(ApplicationReadyEvent.class)
public void startup()
{
logger.info("-----------------------------------------------------------------------------------------------------------------------------------------------------------");
Arrays.stream(LICENCE.split("\\n")).forEach(logger::info);
logger.info("alfresco-pdf-renderer uses the PDFium library from Google Inc. See the license at https://pdfium.googlesource.com/pdfium/+/master/LICENSE or in /pdfium.txt");
logger.info("-----------------------------------------------------------------------------------------------------------------------------------------------------------");
logger.info("Starting application components... Done");
}
} }

View File

@ -44,9 +44,7 @@ final class OptionsBuilder
private Boolean allowPdfEnlargement; private Boolean allowPdfEnlargement;
private Boolean maintainPdfAspectRatio; private Boolean maintainPdfAspectRatio;
private OptionsBuilder() private OptionsBuilder() {}
{
}
public OptionsBuilder withPage(final String page) public OptionsBuilder withPage(final String page)
{ {

View File

@ -33,7 +33,7 @@ import org.alfresco.util.exec.RuntimeExec;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* CommandExecutor implementation for running PDF Renderer transformations. It runs the * CommandExecutor implementation for running PDF Renderer transformations. It runs the
* transformation logic as a separate Shell process. * transformation logic as a separate Shell process.
*/ */
@Component @Component

View File

@ -33,11 +33,19 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.http.HttpHeaders.ACCEPT;
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.APPLICATION_PDF_VALUE;
import static org.springframework.http.MediaType.IMAGE_PNG_VALUE;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.util.StringUtils.getFilenameExtension;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -64,14 +72,11 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.StringUtils;
/** /**
* Test the AlfrescoPdfRendererController without a server. * Test the AlfrescoPdfRendererController without a server.
@ -101,7 +106,7 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
{ {
ReflectionTestUtils.setField(commandExecutor, "transformCommand", mockTransformCommand); ReflectionTestUtils.setField(commandExecutor, "transformCommand", mockTransformCommand);
ReflectionTestUtils.setField(commandExecutor, "checkCommand", mockCheckCommand); ReflectionTestUtils.setField(commandExecutor, "checkCommand", mockCheckCommand);
mockTransformCommand("pdf", "png", "application/pdf", true); mockTransformCommand("pdf", "png", "application/pdf", true);
} }
@ -118,7 +123,8 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
expectedSourceSuffix = null; expectedSourceSuffix = null;
expectedSourceFileBytes = readTestFile(sourceExtension); expectedSourceFileBytes = readTestFile(sourceExtension);
expectedTargetFileBytes = readTargetFileBytes ? readTestFile(targetExtension) : null; expectedTargetFileBytes = readTargetFileBytes ? readTestFile(targetExtension) : null;
sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype,
expectedSourceFileBytes);
when(mockTransformCommand.execute(any(), anyLong())).thenAnswer( when(mockTransformCommand.execute(any(), anyLong())).thenAnswer(
(Answer<RuntimeExec.ExecutionResult>) invocation -> { (Answer<RuntimeExec.ExecutionResult>) invocation -> {
@ -128,14 +134,17 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
String actualOptions = actualProperties.get("options"); String actualOptions = actualProperties.get("options");
String actualSource = actualProperties.get("source"); String actualSource = actualProperties.get("source");
String actualTarget = actualProperties.get("target"); String actualTarget = actualProperties.get("target");
String actualTargetExtension = StringUtils.getFilenameExtension(actualTarget); String actualTargetExtension = getFilenameExtension(actualTarget);
assertNotNull(actualSource); assertNotNull(actualSource);
assertNotNull(actualTarget); assertNotNull(actualTarget);
if (expectedSourceSuffix != null) if (expectedSourceSuffix != null)
{ {
assertTrue("The source file \""+actualSource+"\" should have ended in \""+expectedSourceSuffix+"\"", actualSource.endsWith(expectedSourceSuffix)); assertTrue("The source file \"" + actualSource +
actualSource = actualSource.substring(0, actualSource.length()-expectedSourceSuffix.length()); "\" should have ended in \"" + expectedSourceSuffix + "\"",
actualSource.endsWith(expectedSourceSuffix));
actualSource = actualSource.substring(0,
actualSource.length() - expectedSourceSuffix.length());
} }
assertNotNull(actualOptions); assertNotNull(actualOptions);
@ -155,7 +164,7 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
int i = actualTarget.lastIndexOf('_'); int i = actualTarget.lastIndexOf('_');
if (i >= 0) if (i >= 0)
{ {
String testFilename = actualTarget.substring(i+1); String testFilename = actualTarget.substring(i + 1);
File testFile = getTestFile(testFilename, false); File testFile = getTestFile(testFilename, false);
File targetFile = new File(actualTarget); File targetFile = new File(actualTarget);
generateTargetFileFromResourceFile(actualTargetExtension, testFile, generateTargetFileFromResourceFile(actualTargetExtension, testFile,
@ -164,7 +173,8 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
// Check the supplied source file has not been changed. // Check the supplied source file has not been changed.
byte[] actualSourceFileBytes = Files.readAllBytes(new File(actualSource).toPath()); byte[] actualSourceFileBytes = Files.readAllBytes(new File(actualSource).toPath());
assertTrue("Source file is not the same", Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes)); assertTrue("Source file is not the same",
Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes));
return mockExecutionResult; return mockExecutionResult;
}); });
@ -174,7 +184,6 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
when(mockExecutionResult.getStdOut()).thenReturn("STDOUT"); when(mockExecutionResult.getStdOut()).thenReturn("STDOUT");
} }
@Override @Override
protected AbstractTransformerController getController() protected AbstractTransformerController getController()
{ {
@ -185,7 +194,9 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
public void optionsTest() throws Exception public void optionsTest() throws Exception
{ {
expectedOptions = "--width=321 --height=654 --allow-enlargement --maintain-aspect-ratio --page=2"; expectedOptions = "--width=321 --height=654 --allow-enlargement --maintain-aspect-ratio --page=2";
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform") mockMvc
.perform(MockMvcRequestBuilders
.multipart("/transform")
.file(sourceFile) .file(sourceFile)
.param("targetExtension", targetExtension) .param("targetExtension", targetExtension)
@ -195,17 +206,19 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
.param("height", "654") .param("height", "654")
.param("allowPdfEnlargement", "true") .param("allowPdfEnlargement", "true")
.param("maintainPdfAspectRatio", "true")) .param("maintainPdfAspectRatio", "true"))
.andExpect(status().is(OK.value()))
.andExpect(status().is(OK.value())) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(header().string("Content-Disposition",
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); "attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
public void optionsNegateBooleansTest() throws Exception public void optionsNegateBooleansTest() throws Exception
{ {
expectedOptions = "--width=321 --height=654 --page=2"; expectedOptions = "--width=321 --height=654 --page=2";
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform") mockMvc
.perform(MockMvcRequestBuilders
.multipart("/transform")
.file(sourceFile) .file(sourceFile)
.param("targetExtension", targetExtension) .param("targetExtension", targetExtension)
@ -215,10 +228,10 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
.param("height", "654") .param("height", "654")
.param("allowPdfEnlargement", "false") .param("allowPdfEnlargement", "false")
.param("maintainPdfAspectRatio", "false")) .param("maintainPdfAspectRatio", "false"))
.andExpect(status().is(OK.value()))
.andExpect(status().is(OK.value())) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(header().string("Content-Disposition",
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); "attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Override @Override
@ -226,8 +239,8 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
{ {
transformRequest.setSourceExtension("pdf"); transformRequest.setSourceExtension("pdf");
transformRequest.setTargetExtension("png"); transformRequest.setTargetExtension("png");
transformRequest.setSourceMediaType(MediaType.APPLICATION_PDF_VALUE); transformRequest.setSourceMediaType(APPLICATION_PDF_VALUE);
transformRequest.setTargetMediaType(MediaType.IMAGE_PNG_VALUE); transformRequest.setTargetMediaType(IMAGE_PNG_VALUE);
} }
@Test @Test
@ -237,7 +250,8 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", "xxx")) mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", "xxx"))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason(containsString("Transformer exit code was not 0: \nSTDERR"))); .andExpect(status()
.reason(containsString("Transformer exit code was not 0: \nSTDERR")));
} }
@Test @Test
@ -248,7 +262,6 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
File sourceFile = getTestFile("quick." + sourceExtension, true); File sourceFile = getTestFile("quick." + sourceExtension, true);
String targetFileRef = UUID.randomUUID().toString(); String targetFileRef = UUID.randomUUID().toString();
// Transformation Request POJO // Transformation Request POJO
TransformRequest transformRequest = new TransformRequest(); TransformRequest transformRequest = new TransformRequest();
transformRequest.setRequestId("1"); transformRequest.setRequestId("1");
@ -262,12 +275,13 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
// HTTP Request // HTTP Request
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=quick." + sourceExtension); headers.set(CONTENT_DISPOSITION, "attachment; filename=quick." + sourceExtension);
ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource( ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource(
sourceFile), headers, OK); sourceFile), headers, OK);
when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response); when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response);
when(alfrescoSharedFileStoreClient.saveFile(any())).thenReturn(new FileRefResponse(new FileRefEntity(targetFileRef))); when(alfrescoSharedFileStoreClient.saveFile(any()))
.thenReturn(new FileRefResponse(new FileRefEntity(targetFileRef)));
when(mockExecutionResult.getExitValue()).thenReturn(0); when(mockExecutionResult.getExitValue()).thenReturn(0);
// Update the Transformation Request with any specific params before sending it // Update the Transformation Request with any specific params before sending it
@ -275,18 +289,21 @@ public class AlfrescoPdfRendererControllerTest extends AbstractTransformerContro
// Serialize and call the transformer // Serialize and call the transformer
String tr = objectMapper.writeValueAsString(transformRequest); String tr = objectMapper.writeValueAsString(transformRequest);
String transformationReplyAsString = mockMvc.perform(MockMvcRequestBuilders.post("/transform") String transformationReplyAsString = mockMvc
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .perform(MockMvcRequestBuilders
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).content(tr)) .post("/transform")
.andExpect(status().is(HttpStatus.CREATED.value())) .header(ACCEPT, APPLICATION_JSON_VALUE)
.andReturn().getResponse().getContentAsString(); .header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.content(tr))
.andExpect(status().is(CREATED.value()))
.andReturn().getResponse().getContentAsString();
TransformReply transformReply = objectMapper.readValue(transformationReplyAsString, TransformReply.class); TransformReply transformReply = objectMapper.readValue(transformationReplyAsString,
TransformReply.class);
// Assert the reply // Assert the reply
assertEquals(transformRequest.getRequestId(), transformReply.getRequestId()); assertEquals(transformRequest.getRequestId(), transformReply.getRequestId());
assertEquals(transformRequest.getClientData(), transformReply.getClientData()); assertEquals(transformRequest.getClientData(), transformReply.getClientData());
assertEquals(transformRequest.getSchema(), transformReply.getSchema()); assertEquals(transformRequest.getSchema(), transformReply.getSchema());
} }
} }

View File

@ -48,7 +48,8 @@ public class AlfrescoPdfRendererQueueTransformServiceIT extends AbstractQueueTra
@Override @Override
protected TransformRequest buildRequest() protected TransformRequest buildRequest()
{ {
return TransformRequest.builder() return TransformRequest
.builder()
.withRequestId(UUID.randomUUID().toString()) .withRequestId(UUID.randomUUID().toString())
.withSourceMediaType(MIMETYPE_OPENXML_WORDPROCESSING) .withSourceMediaType(MIMETYPE_OPENXML_WORDPROCESSING)
.withTargetMediaType(MIMETYPE_PDF) .withTargetMediaType(MIMETYPE_PDF)

View File

@ -65,7 +65,6 @@
<artifactId>dom4j</artifactId> <artifactId>dom4j</artifactId>
<version>2.1.1</version> <version>2.1.1</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -26,30 +26,52 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static org.alfresco.transformer.logging.StandardMessages.LICENCE;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
@SpringBootApplication @SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class Application public class Application
{ {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
@Value("${container.name}") @Value("${container.name}")
private String containerName; private String containerName;
@Bean @Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() { MeterRegistryCustomizer<MeterRegistry> metricsCommonTags()
{
return registry -> registry.config().commonTags("containerName", containerName); return registry -> registry.config().commonTags("containerName", containerName);
} }
public static void main(String[] args) public static void main(String[] args)
{ {
SpringApplication.run(Application.class, args); SpringApplication.run(Application.class, args);
} }
@EventListener(ApplicationReadyEvent.class)
public void startup()
{
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
Arrays.stream(LICENCE.split("\\n")).forEach(logger::info);
logger.info("This transformer uses ImageMagick from ImageMagick Studio LLC. See the license at http://www.imagemagick.org/script/license.php or in /ImageMagick-license.txt");
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
logger.info("Starting application components... Done");
}
} }

View File

@ -30,24 +30,22 @@ import static org.alfresco.transformer.fs.FileManager.createAttachment;
import static org.alfresco.transformer.fs.FileManager.createSourceFile; import static org.alfresco.transformer.fs.FileManager.createSourceFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFile; import static org.alfresco.transformer.fs.FileManager.createTargetFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFileName; import static org.alfresco.transformer.fs.FileManager.createTargetFileName;
import static org.alfresco.transformer.logging.StandardMessages.LICENCE;
import static org.alfresco.transformer.util.Util.stringToInteger; import static org.alfresco.transformer.util.Util.stringToInteger;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.alfresco.transformer.executors.ImageMagickCommandExecutor; import org.alfresco.transformer.executors.ImageMagickCommandExecutor;
import org.alfresco.transformer.logging.LogEntry; import org.alfresco.transformer.logging.LogEntry;
import org.alfresco.transformer.probes.ProbeTestTransform; import org.alfresco.transformer.probes.ProbeTestTransform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -60,21 +58,21 @@ import org.springframework.web.multipart.MultipartFile;
* *
* Status Codes: * Status Codes:
* *
* 200 Success * 200 Success
* 400 Bad Request: Invalid cropGravity value (North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest, Center) * 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 missing (missing mandatory parameter)
* 400 Bad Request: Request parameter <name> is of the wrong type * 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: Transformer exit code was not 0 (possible problem with the source file)
* 400 Bad Request: The source filename was not supplied * 400 Bad Request: The source filename was not supplied
* 500 Internal Server Error: (no message with low level IO problems) * 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: 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 exit code was not 0
* 500 Internal Server Error: Transformer version check failed to create any output * 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: 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: 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: 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 * 500 Internal Server Error: Filename encoding error
* 507 Insufficient Storage: Failed to store the source file * 507 Insufficient Storage: Failed to store the source file
*/ */
@Controller @Controller
public class ImageMagickController extends AbstractTransformerController public class ImageMagickController extends AbstractTransformerController
@ -83,15 +81,6 @@ public class ImageMagickController extends AbstractTransformerController
@Autowired @Autowired
private ImageMagickCommandExecutor commandExecutor; private ImageMagickCommandExecutor commandExecutor;
@Autowired
public ImageMagickController()
{
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
Arrays.stream(LICENCE.split("\\n")).forEach(logger::info);
logger.info("This transformer uses ImageMagick from ImageMagick Studio LLC. See the license at http://www.imagemagick.org/script/license.php or in /ImageMagick-license.txt");
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
}
@Override @Override
public String getTransformerName() public String getTransformerName()
@ -110,7 +99,7 @@ public class ImageMagickController extends AbstractTransformerController
{ {
// See the Javadoc on this method and Probes.md for the choice of these values. // See the Javadoc on this method and Probes.md for the choice of these values.
return new ProbeTestTransform(this, "quick.jpg", "quick.png", return new ProbeTestTransform(this, "quick.jpg", "quick.png",
35593, 1024, 150, 1024, 60*15+1,60*15) 35593, 1024, 150, 1024, 60 * 15 + 1, 60 * 15)
{ {
@Override @Override
protected void executeTransformCommand(File sourceFile, File targetFile) protected void executeTransformCommand(File sourceFile, File targetFile)
@ -120,44 +109,44 @@ public class ImageMagickController extends AbstractTransformerController
}; };
} }
@PostMapping(value = "/transform", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/transform", consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Resource> transform(HttpServletRequest request, public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile, @RequestParam("file") MultipartFile sourceMultipartFile,
@RequestParam("targetExtension") String targetExtension, @RequestParam("targetExtension") String targetExtension,
@RequestParam(value = "timeout", required = false) Long timeout, @RequestParam(value = "timeout", required = false) Long timeout,
@RequestParam(value = "testDelay", required = false) Long testDelay, @RequestParam(value = "testDelay", required = false) Long testDelay,
@RequestParam(value = "startPage", required = false) Integer startPage, @RequestParam(value = "startPage", required = false) Integer startPage,
@RequestParam(value = "endPage", required = false) Integer endPage, @RequestParam(value = "endPage", required = false) Integer endPage,
@RequestParam(value = "alphaRemove", required = false) Boolean alphaRemove, @RequestParam(value = "alphaRemove", required = false) Boolean alphaRemove,
@RequestParam(value = "autoOrient", required = false) Boolean autoOrient, @RequestParam(value = "autoOrient", required = false) Boolean autoOrient,
@RequestParam(value = "cropGravity", required = false) String cropGravity, @RequestParam(value = "cropGravity", required = false) String cropGravity,
@RequestParam(value = "cropWidth", required = false) Integer cropWidth, @RequestParam(value = "cropWidth", required = false) Integer cropWidth,
@RequestParam(value = "cropHeight", required = false) Integer cropHeight, @RequestParam(value = "cropHeight", required = false) Integer cropHeight,
@RequestParam(value = "cropPercentage", required = false) Boolean cropPercentage, @RequestParam(value = "cropPercentage", required = false) Boolean cropPercentage,
@RequestParam(value = "cropXOffset", required = false) Integer cropXOffset, @RequestParam(value = "cropXOffset", required = false) Integer cropXOffset,
@RequestParam(value = "cropYOffset", required = false) Integer cropYOffset, @RequestParam(value = "cropYOffset", required = false) Integer cropYOffset,
@RequestParam(value = "thumbnail", required = false) Boolean thumbnail, @RequestParam(value = "thumbnail", required = false) Boolean thumbnail,
@RequestParam(value = "resizeWidth", required = false) Integer resizeWidth, @RequestParam(value = "resizeWidth", required = false) Integer resizeWidth,
@RequestParam(value = "resizeHeight", required = false) Integer resizeHeight, @RequestParam(value = "resizeHeight", required = false) Integer resizeHeight,
@RequestParam(value = "resizePercentage", required = false) Boolean resizePercentage, @RequestParam(value = "resizePercentage", required = false) Boolean resizePercentage,
@RequestParam(value = "allowEnlargement", required = false) Boolean allowEnlargement, @RequestParam(value = "allowEnlargement", required = false) Boolean allowEnlargement,
@RequestParam(value = "maintainAspectRatio", required = false) Boolean maintainAspectRatio, @RequestParam(value = "maintainAspectRatio", required = false) Boolean maintainAspectRatio,
// The commandOptions parameter is supported in ACS 6.0.1 because there may be // The commandOptions parameter is supported in ACS 6.0.1 because there may be
// custom renditions that use it. However the Transform service should // custom renditions that use it. However the Transform service should
// not support it as it provides the option to specify arbitrary command // not support it as it provides the option to specify arbitrary command
// options or even the option to run something else on the command line. // options or even the option to run something else on the command line.
// All Transform service options should be checked as is done for the other // All Transform service options should be checked as is done for the other
// request parameters. Setting this option in the rendition's // request parameters. Setting this option in the rendition's
// ImageTransformationOptions object is being deprecated for the point where // ImageTransformationOptions object is being deprecated for the point where
// The Transform service is being used for all transforms. In the case of // The Transform service is being used for all transforms. In the case of
// ACS 6.0, this is relatively safe as it requires an AMP to be installed // ACS 6.0, this is relatively safe as it requires an AMP to be installed
// which supplies the commandOptions. // which supplies the commandOptions.
@RequestParam(value = "commandOptions", required = false) String commandOptions) @RequestParam(value = "commandOptions", required = false) String commandOptions)
{ {
String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(), String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(),
targetExtension); targetExtension);
@ -238,11 +227,11 @@ public class ImageMagickController extends AbstractTransformerController
private static String calculatePageRange(Integer startPage, Integer endPage) private static String calculatePageRange(Integer startPage, Integer endPage)
{ {
return startPage == null return startPage == null
? endPage == null ? endPage == null
? "" ? ""
: "["+endPage+']' : "[" + endPage + ']'
: endPage == null || startPage.equals(endPage) : endPage == null || startPage.equals(endPage)
? "["+startPage+']' ? "[" + startPage + ']'
: "["+startPage+'-'+endPage+']'; : "[" + startPage + '-' + endPage + ']';
} }
} }

View File

@ -26,7 +26,6 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static java.util.Arrays.asList;
import static org.alfresco.transformer.util.Util.stringToBoolean; import static org.alfresco.transformer.util.Util.stringToBoolean;
import static org.alfresco.transformer.util.Util.stringToInteger; import static org.alfresco.transformer.util.Util.stringToInteger;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
@ -36,6 +35,8 @@ import java.util.StringJoiner;
import org.alfresco.transform.exceptions.TransformException; import org.alfresco.transform.exceptions.TransformException;
import com.google.common.collect.ImmutableList;
/** /**
* ImageMagick options builder. * ImageMagick options builder.
* *
@ -43,8 +44,8 @@ import org.alfresco.transform.exceptions.TransformException;
*/ */
final class OptionsBuilder final class OptionsBuilder
{ {
private static final List<String> GRAVITY_VALUES = asList("North", "NorthEast", "East", private static final List<String> GRAVITY_VALUES = ImmutableList.of("North", "NorthEast",
"SouthEast", "South", "SouthWest", "West", "NorthWest", "Center"); "East", "SouthEast", "South", "SouthWest", "West", "NorthWest", "Center");
private Integer startPage; private Integer startPage;
private Integer endPage; private Integer endPage;
@ -64,9 +65,7 @@ final class OptionsBuilder
private Boolean maintainAspectRatio; private Boolean maintainAspectRatio;
private String commandOptions; private String commandOptions;
private OptionsBuilder() private OptionsBuilder() {}
{
}
public OptionsBuilder withStartPage(final String startPage) public OptionsBuilder withStartPage(final String startPage)
{ {

View File

@ -33,7 +33,7 @@ import org.alfresco.util.exec.RuntimeExec;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* CommandExecutor implementation for running ImageMagick transformations. It runs the * CommandExecutor implementation for running ImageMagick transformations. It runs the
* transformation logic as a separate Shell process. * transformation logic as a separate Shell process.
*/ */
@Component @Component
@ -42,7 +42,7 @@ public class ImageMagickCommandExecutor extends AbstractCommandExecutor
private static final String ROOT = "/usr/lib64/ImageMagick-7.0.7"; private static final String ROOT = "/usr/lib64/ImageMagick-7.0.7";
private static final String DYN = ROOT + "/lib"; private static final String DYN = ROOT + "/lib";
private static final String EXE = "/usr/bin/convert"; private static final String EXE = "/usr/bin/convert";
@Override @Override
protected RuntimeExec createTransformCommand() protected RuntimeExec createTransformCommand()
{ {

View File

@ -33,11 +33,18 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.http.HttpHeaders.ACCEPT;
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.IMAGE_PNG_VALUE;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.util.StringUtils.getFilenameExtension;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -63,14 +70,11 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.StringUtils;
/** /**
* Test the ImageMagickController without a server. * Test the ImageMagickController without a server.
@ -91,7 +95,7 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
@SpyBean @SpyBean
private ImageMagickCommandExecutor commandExecutor; private ImageMagickCommandExecutor commandExecutor;
@SpyBean @SpyBean
private ImageMagickController controller; private ImageMagickController controller;
@ -117,7 +121,8 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
expectedSourceSuffix = null; expectedSourceSuffix = null;
expectedSourceFileBytes = readTestFile(sourceExtension); expectedSourceFileBytes = readTestFile(sourceExtension);
expectedTargetFileBytes = readTargetFileBytes ? readTestFile(targetExtension) : null; expectedTargetFileBytes = readTargetFileBytes ? readTestFile(targetExtension) : null;
sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype,
expectedSourceFileBytes);
when(mockTransformCommand.execute(any(), anyLong())).thenAnswer( when(mockTransformCommand.execute(any(), anyLong())).thenAnswer(
(Answer<RuntimeExec.ExecutionResult>) invocation -> { (Answer<RuntimeExec.ExecutionResult>) invocation -> {
@ -127,14 +132,17 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
String actualOptions = actualProperties.get("options"); String actualOptions = actualProperties.get("options");
String actualSource = actualProperties.get("source"); String actualSource = actualProperties.get("source");
String actualTarget = actualProperties.get("target"); String actualTarget = actualProperties.get("target");
String actualTargetExtension = StringUtils.getFilenameExtension(actualTarget); String actualTargetExtension = getFilenameExtension(actualTarget);
assertNotNull(actualSource); assertNotNull(actualSource);
assertNotNull(actualTarget); assertNotNull(actualTarget);
if (expectedSourceSuffix != null) if (expectedSourceSuffix != null)
{ {
assertTrue("The source file \""+actualSource+"\" should have ended in \""+expectedSourceSuffix+"\"", actualSource.endsWith(expectedSourceSuffix)); assertTrue(
actualSource = actualSource.substring(0, actualSource.length()-expectedSourceSuffix.length()); "The source file \"" + actualSource + "\" should have ended in \"" + expectedSourceSuffix + "\"",
actualSource.endsWith(expectedSourceSuffix));
actualSource = actualSource.substring(0,
actualSource.length() - expectedSourceSuffix.length());
} }
assertNotNull(actualOptions); assertNotNull(actualOptions);
@ -154,7 +162,7 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
int i = actualTarget.lastIndexOf('_'); int i = actualTarget.lastIndexOf('_');
if (i >= 0) if (i >= 0)
{ {
String testFilename = actualTarget.substring(i+1); String testFilename = actualTarget.substring(i + 1);
File testFile = getTestFile(testFilename, false); File testFile = getTestFile(testFilename, false);
File targetFile = new File(actualTarget); File targetFile = new File(actualTarget);
generateTargetFileFromResourceFile(actualTargetExtension, testFile, generateTargetFileFromResourceFile(actualTargetExtension, testFile,
@ -163,7 +171,8 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
// Check the supplied source file has not been changed. // Check the supplied source file has not been changed.
byte[] actualSourceFileBytes = Files.readAllBytes(new File(actualSource).toPath()); byte[] actualSourceFileBytes = Files.readAllBytes(new File(actualSource).toPath());
assertTrue("Source file is not the same", Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes)); assertTrue("Source file is not the same",
Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes));
return mockExecutionResult; return mockExecutionResult;
}); });
@ -173,7 +182,6 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
when(mockExecutionResult.getStdOut()).thenReturn("STDOUT"); when(mockExecutionResult.getStdOut()).thenReturn("STDOUT");
} }
@Override @Override
protected AbstractTransformerController getController() protected AbstractTransformerController getController()
{ {
@ -183,27 +191,32 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void cropGravityGoodTest() throws Exception public void cropGravityGoodTest() throws Exception
{ {
for (String value: new String[] {"North", "NorthEast", "East", "SouthEast", "South", "SouthWest", "West", "NorthWest", "Center"}) for (String value : new String[]{"North", "NorthEast", "East", "SouthEast", "South", "SouthWest", "West", "NorthWest", "Center"})
{ {
expectedOptions = "-gravity "+value+" +repage"; expectedOptions = "-gravity " + value + " +repage";
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform") mockMvc
.perform(MockMvcRequestBuilders
.multipart("/transform")
.file(sourceFile) .file(sourceFile)
.param("targetExtension", targetExtension) .param("targetExtension", targetExtension)
.param("cropGravity", value)) .param("cropGravity", value))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
} }
} }
@Test @Test
public void cropGravityBadTest() throws Exception public void cropGravityBadTest() throws Exception
{ {
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform") mockMvc
.perform(MockMvcRequestBuilders
.multipart("/transform")
.file(sourceFile) .file(sourceFile)
.param("targetExtension", targetExtension) .param("targetExtension", targetExtension)
.param("cropGravity", "badValue")) .param("cropGravity", "badValue"))
.andExpect(status().is(BAD_REQUEST.value())); .andExpect(status().is(BAD_REQUEST.value()));
} }
@Test @Test
@ -211,7 +224,9 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
{ {
expectedOptions = "-alpha remove -gravity SouthEast -crop 123x456%+90+12 +repage -thumbnail 321x654%!"; expectedOptions = "-alpha remove -gravity SouthEast -crop 123x456%+90+12 +repage -thumbnail 321x654%!";
expectedSourceSuffix = "[2-3]"; expectedSourceSuffix = "[2-3]";
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform") mockMvc
.perform(MockMvcRequestBuilders
.multipart("/transform")
.file(sourceFile) .file(sourceFile)
.param("targetExtension", targetExtension) .param("targetExtension", targetExtension)
@ -234,10 +249,10 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
.param("resizePercentage", "true") .param("resizePercentage", "true")
.param("allowEnlargement", "true") .param("allowEnlargement", "true")
.param("maintainAspectRatio", "true")) .param("maintainAspectRatio", "true"))
.andExpect(status().is(OK.value()))
.andExpect(status().is(OK.value())) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(header().string("Content-Disposition",
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); "attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
@ -245,7 +260,9 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
{ {
expectedOptions = "-auto-orient -gravity SouthEast -crop 123x456+90+12 +repage -resize 321x654>"; expectedOptions = "-auto-orient -gravity SouthEast -crop 123x456+90+12 +repage -resize 321x654>";
expectedSourceSuffix = "[2-3]"; expectedSourceSuffix = "[2-3]";
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform") mockMvc
.perform(MockMvcRequestBuilders
.multipart("/transform")
.file(sourceFile) .file(sourceFile)
.param("targetExtension", targetExtension) .param("targetExtension", targetExtension)
@ -268,10 +285,10 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
.param("resizePercentage", "false") .param("resizePercentage", "false")
.param("allowEnlargement", "false") .param("allowEnlargement", "false")
.param("maintainAspectRatio", "false")) .param("maintainAspectRatio", "false"))
.andExpect(status().is(OK.value()))
.andExpect(status().is(OK.value())) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(header().string("Content-Disposition",
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); "attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
@ -279,16 +296,19 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
{ {
// Example of why the commandOptions parameter is a bad idea. // Example of why the commandOptions parameter is a bad idea.
expectedOptions = "( horrible command / ); -resize 321x654>"; expectedOptions = "( horrible command / ); -resize 321x654>";
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform") mockMvc
.perform(MockMvcRequestBuilders
.multipart("/transform")
.file(sourceFile) .file(sourceFile)
.param("targetExtension", targetExtension) .param("targetExtension", targetExtension)
.param("thumbnail", "false") .param("thumbnail", "false")
.param("resizeWidth", "321") .param("resizeWidth", "321")
.param("resizeHeight", "654") .param("resizeHeight", "654")
.param("commandOptions", "( horrible command / );")) .param("commandOptions", "( horrible command / );"))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Override @Override
@ -296,10 +316,10 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
{ {
transformRequest.setSourceExtension("png"); transformRequest.setSourceExtension("png");
transformRequest.setTargetExtension("png"); transformRequest.setTargetExtension("png");
transformRequest.setSourceMediaType(MediaType.IMAGE_PNG_VALUE); transformRequest.setSourceMediaType(IMAGE_PNG_VALUE);
transformRequest.setTargetMediaType(MediaType.IMAGE_PNG_VALUE); transformRequest.setTargetMediaType(IMAGE_PNG_VALUE);
} }
@Test @Test
public void badExitCodeTest() throws Exception public void badExitCodeTest() throws Exception
{ {
@ -307,7 +327,8 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", "xxx")) mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", "xxx"))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason(containsString("Transformer exit code was not 0: \nSTDERR"))); .andExpect(
status().reason(containsString("Transformer exit code was not 0: \nSTDERR")));
} }
@Test @Test
@ -318,7 +339,6 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
File sourceFile = getTestFile("quick." + sourceExtension, true); File sourceFile = getTestFile("quick." + sourceExtension, true);
String targetFileRef = UUID.randomUUID().toString(); String targetFileRef = UUID.randomUUID().toString();
// Transformation Request POJO // Transformation Request POJO
TransformRequest transformRequest = new TransformRequest(); TransformRequest transformRequest = new TransformRequest();
transformRequest.setRequestId("1"); transformRequest.setRequestId("1");
@ -332,12 +352,13 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
// HTTP Request // HTTP Request
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=quick." + sourceExtension); headers.set(CONTENT_DISPOSITION, "attachment; filename=quick." + sourceExtension);
ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource( ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource(
sourceFile), headers, OK); sourceFile), headers, OK);
when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response); when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response);
when(alfrescoSharedFileStoreClient.saveFile(any())).thenReturn(new FileRefResponse(new FileRefEntity(targetFileRef))); when(alfrescoSharedFileStoreClient.saveFile(any()))
.thenReturn(new FileRefResponse(new FileRefEntity(targetFileRef)));
when(mockExecutionResult.getExitValue()).thenReturn(0); when(mockExecutionResult.getExitValue()).thenReturn(0);
// Update the Transformation Request with any specific params before sending it // Update the Transformation Request with any specific params before sending it
@ -345,18 +366,21 @@ public class ImageMagickControllerTest extends AbstractTransformerControllerTest
// Serialize and call the transformer // Serialize and call the transformer
String tr = objectMapper.writeValueAsString(transformRequest); String tr = objectMapper.writeValueAsString(transformRequest);
String transformationReplyAsString = mockMvc.perform(MockMvcRequestBuilders.post("/transform") String transformationReplyAsString = mockMvc
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .perform(MockMvcRequestBuilders
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).content(tr)) .post("/transform")
.andExpect(status().is(HttpStatus.CREATED.value())) .header(ACCEPT, APPLICATION_JSON_VALUE)
.andReturn().getResponse().getContentAsString(); .header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.content(tr))
.andExpect(status().is(CREATED.value()))
.andReturn().getResponse().getContentAsString();
TransformReply transformReply = objectMapper.readValue(transformationReplyAsString, TransformReply.class); TransformReply transformReply = objectMapper.readValue(transformationReplyAsString,
TransformReply.class);
// Assert the reply // Assert the reply
assertEquals(transformRequest.getRequestId(), transformReply.getRequestId()); assertEquals(transformRequest.getRequestId(), transformReply.getRequestId());
assertEquals(transformRequest.getClientData(), transformReply.getClientData()); assertEquals(transformRequest.getClientData(), transformReply.getClientData());
assertEquals(transformRequest.getSchema(), transformReply.getSchema()); assertEquals(transformRequest.getSchema(), transformReply.getSchema());
} }
} }

View File

@ -48,7 +48,8 @@ public class ImageMagickQueueTransformServiceIT extends AbstractQueueTransformSe
@Override @Override
protected TransformRequest buildRequest() protected TransformRequest buildRequest()
{ {
return TransformRequest.builder() return TransformRequest
.builder()
.withRequestId(UUID.randomUUID().toString()) .withRequestId(UUID.randomUUID().toString())
.withSourceMediaType(MIMETYPE_IMAGE_PNG) .withSourceMediaType(MIMETYPE_IMAGE_PNG)
.withTargetMediaType(MIMETYPE_IMAGE_JPEG) .withTargetMediaType(MIMETYPE_IMAGE_JPEG)
@ -56,6 +57,7 @@ public class ImageMagickQueueTransformServiceIT extends AbstractQueueTransformSe
.withSchema(1) .withSchema(1)
.withClientData("ACS") .withClientData("ACS")
.withSourceReference(UUID.randomUUID().toString()) .withSourceReference(UUID.randomUUID().toString())
.withSourceSize(32L).build(); .withSourceSize(32L)
.build();
} }
} }

View File

@ -26,30 +26,52 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import io.micrometer.core.instrument.MeterRegistry; import static org.alfresco.transformer.logging.StandardMessages.LICENCE;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import io.micrometer.core.instrument.MeterRegistry;
@SpringBootApplication @SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class Application public class Application
{ {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
@Value("${container.name}") @Value("${container.name}")
private String containerName; private String containerName;
@Bean @Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() { MeterRegistryCustomizer<MeterRegistry> metricsCommonTags()
{
return registry -> registry.config().commonTags("containerName", containerName); return registry -> registry.config().commonTags("containerName", containerName);
} }
public static void main(String[] args) public static void main(String[] args)
{ {
SpringApplication.run(Application.class, args); SpringApplication.run(Application.class, args);
} }
@EventListener(ApplicationReadyEvent.class)
public void startup()
{
logger.info("-------------------------------------------------------------------------------------------------------------------------------------------------------");
Arrays.stream(LICENCE.split("\\n")).forEach(logger::info);
logger.info("This transformer uses LibreOffice from The Document Foundation. See the license at https://www.libreoffice.org/download/license/ or in /libreoffice.txt");
logger.info("-------------------------------------------------------------------------------------------------------------------------------------------------------");
logger.info("Starting application components... Done");
}
} }

View File

@ -30,11 +30,10 @@ import static org.alfresco.transformer.fs.FileManager.createAttachment;
import static org.alfresco.transformer.fs.FileManager.createSourceFile; import static org.alfresco.transformer.fs.FileManager.createSourceFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFile; import static org.alfresco.transformer.fs.FileManager.createTargetFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFileName; import static org.alfresco.transformer.fs.FileManager.createTargetFileName;
import static org.alfresco.transformer.logging.StandardMessages.LICENCE;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -46,7 +45,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -59,20 +57,20 @@ import org.springframework.web.multipart.MultipartFile;
* *
* Status Codes: * Status Codes:
* *
* 200 Success * 200 Success
* 400 Bad Request: Request parameter <name> is missing (missing mandatory parameter) * 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: 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: Transformer exit code was not 0 (possible problem with the source file)
* 400 Bad Request: The source filename was not supplied * 400 Bad Request: The source filename was not supplied
* 500 Internal Server Error: (no message with low level IO problems) * 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: 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 exit code was not 0
* 500 Internal Server Error: Transformer version check failed to create any output * 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: 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: 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: 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 * 500 Internal Server Error: Filename encoding error
* 507 Insufficient Storage: Failed to store the source file * 507 Insufficient Storage: Failed to store the source file
*/ */
@Controller @Controller
public class LibreOfficeController extends AbstractTransformerController public class LibreOfficeController extends AbstractTransformerController
@ -81,15 +79,6 @@ public class LibreOfficeController extends AbstractTransformerController
@Autowired @Autowired
private LibreOfficeJavaExecutor javaExecutor; private LibreOfficeJavaExecutor javaExecutor;
@Autowired
public LibreOfficeController()
{
logger.info("-------------------------------------------------------------------------------------------------------------------------------------------------------");
Arrays.stream(LICENCE.split("\\n")).forEach(logger::info);
logger.info("This transformer uses LibreOffice from The Document Foundation. See the license at https://www.libreoffice.org/download/license/ or in /libreoffice.txt");
logger.info("-------------------------------------------------------------------------------------------------------------------------------------------------------");
}
@Override @Override
public String getTransformerName() public String getTransformerName()
@ -108,7 +97,7 @@ public class LibreOfficeController extends AbstractTransformerController
{ {
// See the Javadoc on this method and Probes.md for the choice of these values. // See the Javadoc on this method and Probes.md for the choice of these values.
return new ProbeTestTransform(this, "quick.doc", "quick.pdf", return new ProbeTestTransform(this, "quick.doc", "quick.pdf",
11817, 1024, 150, 10240, 60*30+1, 60*15+20) 11817, 1024, 150, 10240, 60 * 30 + 1, 60 * 15 + 20)
{ {
@Override @Override
protected void executeTransformCommand(File sourceFile, File targetFile) protected void executeTransformCommand(File sourceFile, File targetFile)
@ -119,14 +108,15 @@ public class LibreOfficeController extends AbstractTransformerController
} }
//todo: the "timeout" request parameter is ignored; the timeout is preset at JodConverter creation //todo: the "timeout" request parameter is ignored; the timeout is preset at JodConverter creation
@PostMapping(value = "/transform", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/transform", consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Resource> transform(HttpServletRequest request, public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile, @RequestParam("file") MultipartFile sourceMultipartFile,
@RequestParam("targetExtension") String targetExtension, @RequestParam("targetExtension") String targetExtension,
@RequestParam(value = "timeout", required = false) Long timeout, @RequestParam(value = "timeout", required = false) Long timeout,
@RequestParam(value = "testDelay", required = false) Long testDelay) @RequestParam(value = "testDelay", required = false) Long testDelay)
{ {
String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(), targetExtension); String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(),
targetExtension);
getProbeTestTransform().incrementTransformerCount(); getProbeTestTransform().incrementTransformerCount();
File sourceFile = createSourceFile(request, sourceMultipartFile); File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename); File targetFile = createTargetFile(request, targetFilename);

View File

@ -34,12 +34,14 @@ public interface JodConverter
{ {
/** /**
* Gets the JodConverter OfficeManager. * Gets the JodConverter OfficeManager.
*
* @return * @return
*/ */
OfficeManager getOfficeManager(); OfficeManager getOfficeManager();
/** /**
* This method returns a boolean indicating whether the JodConverter connection to OOo is available. * This method returns a boolean indicating whether the JodConverter connection to OOo is available.
*
* @return <code>true</code> if available, else <code>false</code> * @return <code>true</code> if available, else <code>false</code>
*/ */
boolean isAvailable(); boolean isAvailable();

View File

@ -128,7 +128,7 @@ public class JodConverterSharedInstance implements JodConverter
StringTokenizer tokenizer = new StringTokenizer(s, ","); StringTokenizer tokenizer = new StringTokenizer(s, ",");
int tokenCount = tokenizer.countTokens(); int tokenCount = tokenizer.countTokens();
portNumbers = new int[tokenCount]; portNumbers = new int[tokenCount];
for (int i = 0;tokenizer.hasMoreTokens();i++) for (int i = 0; tokenizer.hasMoreTokens(); i++)
{ {
try try
{ {
@ -163,7 +163,8 @@ public class JodConverterSharedInstance implements JodConverter
File tmp = new File(templateProfileDir); File tmp = new File(templateProfileDir);
if (!tmp.isDirectory()) if (!tmp.isDirectory())
{ {
throw new AlfrescoRuntimeException("OpenOffice template profile directory "+templateProfileDir+" does not exist."); throw new AlfrescoRuntimeException(
"OpenOffice template profile directory " + templateProfileDir + " does not exist.");
} }
this.templateProfileDir = tmp; this.templateProfileDir = tmp;
} }
@ -176,7 +177,7 @@ public class JodConverterSharedInstance implements JodConverter
void setConnectTimeout(String connectTimeout) void setConnectTimeout(String connectTimeout)
{ {
this.connectTimeout = parseStringForLong(connectTimeout.trim()); this.connectTimeout = parseStringForLong(connectTimeout.trim());
} }
void setEnabled(final String enabledStr) void setEnabled(final String enabledStr)
@ -254,8 +255,8 @@ public class JodConverterSharedInstance implements JodConverter
private int[] getPortNumbers() private int[] getPortNumbers()
{ {
return (enabled == null || !enabled) && deprecatedOooEnabled != null && deprecatedOooEnabled return (enabled == null || !enabled) && deprecatedOooEnabled != null && deprecatedOooEnabled
? deprecatedOooPortNumbers ? deprecatedOooPortNumbers
: portNumbers; : portNumbers;
} }
private Long parseStringForLong(String string) private Long parseStringForLong(String string)
@ -287,14 +288,15 @@ public class JodConverterSharedInstance implements JodConverter
@PostConstruct @PostConstruct
public void afterPropertiesSet() public void afterPropertiesSet()
{ {
// isAvailable defaults to false afterPropertiesSet. It only becomes true on successful completion of this method. // isAvailable defaults to false afterPropertiesSet. It only becomes true on successful completion of this method.
this.isAvailable = false; this.isAvailable = false;
int[] portNumbers = getPortNumbers(); int[] portNumbers = getPortNumbers();
String officeHome = getOfficeHome(); String officeHome = getOfficeHome();
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("JodConverter settings (null settings will be replaced by jodconverter defaults):"); logger.debug(
"JodConverter settings (null settings will be replaced by jodconverter defaults):");
logger.debug(" officeHome = {}", officeHome); logger.debug(" officeHome = {}", officeHome);
logger.debug(" enabled = {}", isEnabled()); logger.debug(" enabled = {}", isEnabled());
logger.debug(" portNumbers = {}", getString(portNumbers)); logger.debug(" portNumbers = {}", getString(portNumbers));
@ -390,7 +392,7 @@ public class JodConverterSharedInstance implements JodConverter
StringBuilder portInfo = new StringBuilder(); StringBuilder portInfo = new StringBuilder();
if (portNumbers != null) if (portNumbers != null)
{ {
for (int i = 0;i < portNumbers.length;i++) for (int i = 0; i < portNumbers.length; i++)
{ {
portInfo.append(portNumbers[i]); portInfo.append(portNumbers[i]);
if (i < portNumbers.length - 1) if (i < portNumbers.length - 1)
@ -404,78 +406,77 @@ public class JodConverterSharedInstance implements JodConverter
private void logAllSofficeFilesUnderOfficeHome() private void logAllSofficeFilesUnderOfficeHome()
{ {
if (!logger.isDebugEnabled()) if (!logger.isDebugEnabled())
{ {
return; return;
} }
String officeHome = getOfficeHome(); String officeHome = getOfficeHome();
File requestedOfficeHome = new File(officeHome); File requestedOfficeHome = new File(officeHome);
logger.debug("Some information on soffice* files and their permissions"); logger.debug("Some information on soffice* files and their permissions");
logFileInfo(requestedOfficeHome); logFileInfo(requestedOfficeHome);
for (File f : findSofficePrograms(requestedOfficeHome, new ArrayList<>(), 2)) for (File f : findSofficePrograms(requestedOfficeHome, new ArrayList<>(), 2))
{ {
logFileInfo(f); 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, private List<File> findSofficePrograms(File searchRoot, List<File> results,
int currentRecursionDepth, int maxRecursionDepth) int maxRecursionDepth)
{ {
if (currentRecursionDepth >= maxRecursionDepth) return this.findSofficePrograms(searchRoot, results, 0, maxRecursionDepth);
{ }
return results;
}
File[] matchingFiles = searchRoot.listFiles((dir, name) -> name.startsWith("soffice")); private List<File> findSofficePrograms(File searchRoot, List<File> results,
int currentRecursionDepth, int maxRecursionDepth)
{
if (currentRecursionDepth >= maxRecursionDepth)
{
return results;
}
File[] matchingFiles = searchRoot.listFiles((dir, name) -> name.startsWith("soffice"));
results.addAll(asList(matchingFiles)); results.addAll(asList(matchingFiles));
for (File dir : requireNonNull(searchRoot.listFiles(File::isDirectory))) for (File dir : requireNonNull(searchRoot.listFiles(File::isDirectory)))
{ {
findSofficePrograms(dir, results, currentRecursionDepth + 1, maxRecursionDepth); findSofficePrograms(dir, results, currentRecursionDepth + 1, maxRecursionDepth);
} }
return results; return results;
} }
/** /**
* Logs some information on the specified file, including name and r/w/x permissions. * Logs some information on the specified file, including name and r/w/x permissions.
*
* @param f the file to log. * @param f the file to log.
*/ */
private void logFileInfo(File f) private void logFileInfo(File f)
{ {
if (!logger.isDebugEnabled()) if (!logger.isDebugEnabled())
{ {
return; return;
} }
StringBuilder msg = new StringBuilder(); StringBuilder msg = new StringBuilder();
msg.append(f).append(" "); msg.append(f).append(" ");
if (f.exists()) if (f.exists() && f.canRead())
{ {
if (f.canRead()) msg.append("(")
{ .append(f.isDirectory() ? "d" : "-")
msg.append("(") .append(f.canRead() ? "r" : "-")
.append(f.isDirectory() ? "d" : "-") .append(f.canWrite() ? "w" : "-")
.append(f.canRead() ? "r" : "-") .append(f.canExecute() ? "x" : "-")
.append(f.canWrite() ? "w" : "-") .append(")");
.append(f.canExecute() ? "x" : "-") }
.append(")"); else
} {
} msg.append("does not exist");
else }
{ logger.debug(msg.toString());
msg.append("does not exist");
}
logger.debug(msg.toString());
} }
/* /*
@ -485,19 +486,19 @@ public class JodConverterSharedInstance implements JodConverter
@PreDestroy @PreDestroy
public void destroy() public void destroy()
{ {
this.isAvailable = false; this.isAvailable = false;
if (officeManager != null) if (officeManager != null)
{ {
// If there is an OfficeException when stopping the officeManager below, then there is // 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 // 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. // 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 // 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 // (see javadoc for method declaration). Therefore there is no handling here for
// exceptions from jodConverter. // exceptions from jodConverter.
officeManager.stop(); officeManager.stop();
} }
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.content.JodConverterWorker#getOfficeManager() * @see org.alfresco.repo.content.JodConverterWorker#getOfficeManager()

View File

@ -35,20 +35,20 @@ import java.io.IOException;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import org.alfresco.transform.exceptions.TransformException; import org.alfresco.transform.exceptions.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.artofsolving.jodconverter.OfficeDocumentConverter; import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.OfficeException; import org.artofsolving.jodconverter.office.OfficeException;
import org.artofsolving.jodconverter.office.OfficeManager; import org.artofsolving.jodconverter.office.OfficeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.sun.star.task.ErrorCodeIOException; import com.sun.star.task.ErrorCodeIOException;
/** /**
* JavaExecutor implementation for running LibreOffice transformations. It loads the * JavaExecutor implementation for running LibreOffice transformations. It loads the
* transformation logic in the same JVM (check the {@link JodConverter} implementation). * transformation logic in the same JVM (check the {@link JodConverter} implementation).
*/ */
@Component @Component

View File

@ -34,7 +34,15 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.http.HttpHeaders.ACCEPT;
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.IMAGE_PNG_VALUE;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.util.StringUtils.getFilenameExtension;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -59,13 +67,10 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.StringUtils;
/** /**
* Test the LibreOfficeController without a server. * Test the LibreOfficeController without a server.
@ -94,15 +99,18 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
// The following is based on super.mockTransformCommand(...) // The following is based on super.mockTransformCommand(...)
// This is because LibreOffice used JodConverter rather than a RuntimeExec // This is because LibreOffice used JodConverter rather than a RuntimeExec
expectedSourceFileBytes = Files.readAllBytes(getTestFile("quick." + sourceExtension, true).toPath()); expectedSourceFileBytes = Files.readAllBytes(
expectedTargetFileBytes = Files.readAllBytes(getTestFile("quick." + targetExtension, true).toPath()); getTestFile("quick." + sourceExtension, true).toPath());
sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype, expectedSourceFileBytes); expectedTargetFileBytes = Files.readAllBytes(
getTestFile("quick." + targetExtension, true).toPath());
sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype,
expectedSourceFileBytes);
doAnswer(invocation -> doAnswer(invocation ->
{ {
File sourceFile = invocation.getArgument(0); File sourceFile = invocation.getArgument(0);
File targetFile = invocation.getArgument(1); File targetFile = invocation.getArgument(1);
String actualTargetExtension = StringUtils.getFilenameExtension(targetFile.getAbsolutePath()); String actualTargetExtension = getFilenameExtension(targetFile.getAbsolutePath());
assertNotNull(sourceFile); assertNotNull(sourceFile);
assertNotNull(targetFile); assertNotNull(targetFile);
@ -119,7 +127,8 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
// Check the supplied source file has not been changed. // Check the supplied source file has not been changed.
byte[] actualSourceFileBytes = Files.readAllBytes(sourceFile.toPath()); byte[] actualSourceFileBytes = Files.readAllBytes(sourceFile.toPath());
assertTrue("Source file is not the same", Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes)); assertTrue("Source file is not the same",
Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes));
return null; return null;
}).when(javaExecutor).convert(any(), any()); }).when(javaExecutor).convert(any(), any());
@ -143,11 +152,14 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
{ {
doThrow(OfficeException.class).when(javaExecutor).convert(any(), any()); doThrow(OfficeException.class).when(javaExecutor).convert(any(), any());
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform") mockMvc
.file(sourceFile) .perform(MockMvcRequestBuilders
.param("targetExtension", "xxx")) .multipart("/transform")
.andExpect(status().is(400)) .file(sourceFile)
.andExpect(status().reason(containsString("LibreOffice - LibreOffice server conversion failed:"))); .param("targetExtension", "xxx"))
.andExpect(status().is(400))
.andExpect(status().reason(
containsString("LibreOffice - LibreOffice server conversion failed:")));
} }
@Override @Override
@ -156,7 +168,7 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
transformRequest.setSourceExtension("doc"); transformRequest.setSourceExtension("doc");
transformRequest.setTargetExtension("pdf"); transformRequest.setTargetExtension("pdf");
transformRequest.setSourceMediaType("application/msword"); transformRequest.setSourceMediaType("application/msword");
transformRequest.setTargetMediaType(MediaType.IMAGE_PNG_VALUE); transformRequest.setTargetMediaType(IMAGE_PNG_VALUE);
} }
@Test @Test
@ -180,14 +192,13 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
// HTTP Request // HTTP Request
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_DISPOSITION, headers.set(CONTENT_DISPOSITION, "attachment; filename=quick." + sourceExtension);
"attachment; filename=quick." + sourceExtension);
ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource( ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource(
sourceFile), headers, HttpStatus.OK); sourceFile), headers, OK);
when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response); when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response);
when(alfrescoSharedFileStoreClient.saveFile(any())).thenReturn( when(alfrescoSharedFileStoreClient.saveFile(any()))
new FileRefResponse(new FileRefEntity(targetFileRef))); .thenReturn(new FileRefResponse(new FileRefEntity(targetFileRef)));
when(mockExecutionResult.getExitValue()).thenReturn(0); when(mockExecutionResult.getExitValue()).thenReturn(0);
// Update the Transformation Request with any specific params before sending it // Update the Transformation Request with any specific params before sending it
@ -195,14 +206,14 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
// Serialize and call the transformer // Serialize and call the transformer
String tr = objectMapper.writeValueAsString(transformRequest); String tr = objectMapper.writeValueAsString(transformRequest);
String transformationReplyAsString = mockMvc.perform( String transformationReplyAsString = mockMvc
MockMvcRequestBuilders.post("/transform") .perform(MockMvcRequestBuilders
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .post("/transform")
.header(HttpHeaders.CONTENT_TYPE, .header(ACCEPT, APPLICATION_JSON_VALUE)
MediaType.APPLICATION_JSON_VALUE).content(tr)) .header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.andExpect( .content(tr))
status().is(HttpStatus.CREATED.value())) .andExpect(status().is(CREATED.value()))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformReply transformReply = objectMapper.readValue(transformationReplyAsString, TransformReply transformReply = objectMapper.readValue(transformationReplyAsString,
TransformReply.class); TransformReply.class);

View File

@ -47,7 +47,8 @@ public class LibreOfficeQueueTransformServiceIT extends AbstractQueueTransformSe
@Override @Override
protected TransformRequest buildRequest() protected TransformRequest buildRequest()
{ {
return TransformRequest.builder() return TransformRequest
.builder()
.withRequestId(UUID.randomUUID().toString()) .withRequestId(UUID.randomUUID().toString())
.withSourceMediaType(MIMETYPE_OPENXML_WORDPROCESSING) .withSourceMediaType(MIMETYPE_OPENXML_WORDPROCESSING)
.withTargetMediaType(MIMETYPE_OPENXML_WORDPROCESSING) .withTargetMediaType(MIMETYPE_OPENXML_WORDPROCESSING)
@ -55,6 +56,7 @@ public class LibreOfficeQueueTransformServiceIT extends AbstractQueueTransformSe
.withSchema(1) .withSchema(1)
.withClientData("ACS") .withClientData("ACS")
.withSourceReference(UUID.randomUUID().toString()) .withSourceReference(UUID.randomUUID().toString())
.withSourceSize(32L).build(); .withSourceSize(32L)
.build();
} }
} }

View File

@ -26,20 +26,30 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static org.alfresco.transformer.logging.StandardMessages.LICENCE;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
@SpringBootApplication @SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class Application public class Application
{ {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
@Value("${container.name}") @Value("${container.name}")
private String containerName; private String containerName;
@ -53,4 +63,15 @@ public class Application
{ {
SpringApplication.run(Application.class, args); SpringApplication.run(Application.class, args);
} }
@EventListener(ApplicationReadyEvent.class)
public void startup()
{
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
Arrays.stream(LICENCE.split("\\n")).forEach(logger::info);
logger.info("Tika is from Apache. See the license at http://www.apache.org/licenses/LICENSE-2.0. or in /Apache\\ 2.0.txt");
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
logger.info("Starting application components... Done");
}
} }

View File

@ -37,13 +37,12 @@ import static org.alfresco.transformer.fs.FileManager.createAttachment;
import static org.alfresco.transformer.fs.FileManager.createSourceFile; import static org.alfresco.transformer.fs.FileManager.createSourceFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFile; import static org.alfresco.transformer.fs.FileManager.createTargetFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFileName; import static org.alfresco.transformer.fs.FileManager.createTargetFileName;
import static org.alfresco.transformer.logging.StandardMessages.LICENCE;
import static org.alfresco.transformer.util.Util.stringToBoolean; import static org.alfresco.transformer.util.Util.stringToBoolean;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -56,7 +55,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -69,9 +67,9 @@ import org.springframework.web.multipart.MultipartFile;
* Status Codes: * Status Codes:
* *
* 200 Success * 200 Success
* 400 Bad Request: Invalid target mimetype &lt;mimetype> * 400 Bad Request: Invalid target mimetype <mimetype>
* 400 Bad Request: Request parameter &lt;name> is missing (missing mandatory parameter) * 400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)
* 400 Bad Request: Request parameter &lt;name> is of the wrong type * 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: Transformer exit code was not 0 (possible problem with the source file)
* 400 Bad Request: The source filename was not supplied * 400 Bad Request: The source filename was not supplied
* 500 Internal Server Error: (no message with low level IO problems) * 500 Internal Server Error: (no message with low level IO problems)
@ -92,15 +90,6 @@ public class TikaController extends AbstractTransformerController
@Autowired @Autowired
private TikaJavaExecutor javaExecutor; private TikaJavaExecutor javaExecutor;
@Autowired
public TikaController()
{
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
Arrays.stream(LICENCE.split("\\n")).forEach(logger::info);
logger.info("Tika is from Apache. See the license at http://www.apache.org/licenses/LICENSE-2.0. or in /Apache\\ 2.0.txt");
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
}
@Override @Override
public String getTransformerName() public String getTransformerName()
{ {
@ -130,7 +119,7 @@ public class TikaController extends AbstractTransformerController
}; };
} }
@PostMapping(value = "/transform", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/transform", consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Resource> transform(HttpServletRequest request, public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile, @RequestParam("file") MultipartFile sourceMultipartFile,
@RequestParam("targetExtension") String targetExtension, @RequestParam("targetExtension") String targetExtension,
@ -149,7 +138,8 @@ public class TikaController extends AbstractTransformerController
throw new TransformException(BAD_REQUEST.value(), "Invalid transform value"); throw new TransformException(BAD_REQUEST.value(), "Invalid transform value");
} }
String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(), targetExtension); String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(),
targetExtension);
getProbeTestTransform().incrementTransformerCount(); getProbeTestTransform().incrementTransformerCount();
File sourceFile = createSourceFile(request, sourceMultipartFile); File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename); File targetFile = createTargetFile(request, targetFilename);

View File

@ -26,7 +26,6 @@
*/ */
package org.alfresco.transformer.executors; package org.alfresco.transformer.executors;
import static java.util.Arrays.asList;
import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_HTML; import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_HTML;
import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_IMAGE_JPEG; import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_IMAGE_JPEG;
import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_IMAGE_PNG; import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_IMAGE_PNG;
@ -74,6 +73,8 @@ import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler; import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import com.google.common.collect.ImmutableList;
/** /**
* Stripped down command line Tika transformers. Not actually run as a separate process, but the code fits the patten * Stripped down command line Tika transformers. Not actually run as a separate process, but the code fits the patten
* used by transformers that do. * used by transformers that do.
@ -455,26 +456,26 @@ public class Tika
public static final String TIKA_AUTO = "TikaAuto"; public static final String TIKA_AUTO = "TikaAuto";
public static final String TEXT_MINING = "TextMining"; public static final String TEXT_MINING = "TextMining";
public static final List<String> TRANSFORM_NAMES = asList( public static final List<String> TRANSFORM_NAMES = ImmutableList.of(
ARCHIVE, OUTLOOK_MSG, PDF_BOX, POI_OFFICE, POI, POI_OO_XML, TIKA_AUTO, TEXT_MINING); ARCHIVE, OUTLOOK_MSG, PDF_BOX, POI_OFFICE, POI, POI_OO_XML, TIKA_AUTO, TEXT_MINING);
public static final String TARGET_MIMETYPE = "--targetMimetype="; public static final String TARGET_MIMETYPE = "--targetMimetype=";
public static final String TARGET_ENCODING = "--targetEncoding="; public static final String TARGET_ENCODING = "--targetEncoding=";
public static final String INCLUDE_CONTENTS = "--includeContents"; public static final String INCLUDE_CONTENTS = "--includeContents";
public static final String NOT_EXTRACT_BOOKMARKS_TEXT = "--notExtractBookmarksText"; public static final String NOT_EXTRACT_BOOKMARKS_TEXT = "--notExtractBookmarksText";
public static final String CSV = "csv"; public static final String CSV = "csv";
public static final String DOC = "doc"; public static final String DOC = "doc";
public static final String DOCX = "docx"; public static final String DOCX = "docx";
public static final String HTML = "html"; public static final String HTML = "html";
public static final String MSG = "msg"; public static final String MSG = "msg";
public static final String PDF = "pdf"; public static final String PDF = "pdf";
public static final String PPTX = "pptx"; public static final String PPTX = "pptx";
public static final String TXT = "txt"; public static final String TXT = "txt";
public static final String XHTML = "xhtml"; public static final String XHTML = "xhtml";
public static final String XSLX = "xslx"; public static final String XSLX = "xslx";
public static final String XML = "xml"; public static final String XML = "xml";
public static final String ZIP = "zip"; public static final String ZIP = "zip";
private final Parser packageParser = new PackageParser(); private final Parser packageParser = new PackageParser();
private final Parser pdfParser = new PDFParser(); private final Parser pdfParser = new PDFParser();
@ -484,9 +485,10 @@ public class Tika
private final Parser tikaOfficeDetectParser = new TikaOfficeDetectParser(); private final Parser tikaOfficeDetectParser = new TikaOfficeDetectParser();
private final PDFParserConfig pdfParserConfig = new PDFParserConfig(); private final PDFParserConfig pdfParserConfig = new PDFParserConfig();
private DocumentSelector pdfBoxEmbededDocumentSelector = new DocumentSelector() private final DocumentSelector pdfBoxEmbededDocumentSelector = new DocumentSelector()
{ {
private final List<String> disabledMediaTypes = asList(MIMETYPE_IMAGE_JPEG, MIMETYPE_IMAGE_TIFF, MIMETYPE_IMAGE_PNG); private final List<String> disabledMediaTypes = ImmutableList.of(MIMETYPE_IMAGE_JPEG,
MIMETYPE_IMAGE_TIFF, MIMETYPE_IMAGE_PNG);
@Override @Override
public boolean select(Metadata metadata) public boolean select(Metadata metadata)
@ -518,16 +520,16 @@ public class Tika
} }
catch (IllegalArgumentException e) catch (IllegalArgumentException e)
{ {
System.err.println("ERROR "+e.getMessage()); System.err.println("ERROR " + e.getMessage());
System.exit(-1); System.exit(-1);
} }
catch (IllegalStateException | TikaException | IOException | SAXException e) catch (IllegalStateException | TikaException | IOException | SAXException e)
{ {
System.err.println("ERROR "+e.getMessage()); System.err.println("ERROR " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
System.exit(-2); System.exit(-2);
} }
System.out.println("Finished in "+(System.currentTimeMillis()-start)+"ms"); System.out.println("Finished in " + (System.currentTimeMillis() - start) + "ms");
} }
// Extracts parameters form args // Extracts parameters form args
@ -541,7 +543,7 @@ public class Tika
Boolean includeContents = null; Boolean includeContents = null;
Boolean notExtractBookmarksText = null; Boolean notExtractBookmarksText = null;
for (String arg: args) for (String arg : args)
{ {
if (arg.startsWith("--")) if (arg.startsWith("--"))
{ {
@ -565,7 +567,7 @@ public class Tika
} }
else else
{ {
throw new IllegalArgumentException("Unexpected argument "+arg); throw new IllegalArgumentException("Unexpected argument " + arg);
} }
} }
else else
@ -584,7 +586,7 @@ public class Tika
} }
else else
{ {
throw new IllegalArgumentException("Unexpected argument "+arg); throw new IllegalArgumentException("Unexpected argument " + arg);
} }
} }
} }
@ -593,71 +595,73 @@ public class Tika
throw new IllegalArgumentException("Missing arguments"); throw new IllegalArgumentException("Missing arguments");
} }
includeContents = includeContents == null ? false : includeContents; includeContents = includeContents == null ? false : includeContents;
notExtractBookmarksText = notExtractBookmarksText == null ? false : notExtractBookmarksText; notExtractBookmarksText = notExtractBookmarksText == null ? false : notExtractBookmarksText;
transform(transform, includeContents, notExtractBookmarksText, sourceFilename, targetFilename, targetMimetype, targetEncoding); transform(transform, includeContents, notExtractBookmarksText, sourceFilename,
targetFilename, targetMimetype, targetEncoding);
} }
private String getValue(String arg, boolean valueExpected, Object value, String optionName) private String getValue(String arg, boolean valueExpected, Object value, String optionName)
{ {
if (value != null) if (value != null)
{ {
throw new IllegalArgumentException("Duplicate "+optionName); throw new IllegalArgumentException("Duplicate " + optionName);
} }
String stringValue = arg.substring(optionName.length()).trim(); String stringValue = arg.substring(optionName.length()).trim();
if (!valueExpected && stringValue.length() > 0) if (!valueExpected && stringValue.length() > 0)
{ {
throw new IllegalArgumentException("Unexpected value with "+optionName); throw new IllegalArgumentException("Unexpected value with " + optionName);
} }
if (valueExpected && stringValue.length() == 0) if (valueExpected && stringValue.length() == 0)
{ {
throw new IllegalArgumentException("Expected value with "+optionName); throw new IllegalArgumentException("Expected value with " + optionName);
} }
return stringValue; return stringValue;
} }
// Adds transform specific values such as parser and documentSelector. // Adds transform specific values such as parser and documentSelector.
private void transform(String transform, Boolean includeContents, private void transform(String transform, Boolean includeContents,
Boolean notExtractBookmarksText, Boolean notExtractBookmarksText,
String sourceFilename, String sourceFilename,
String targetFilename, String targetMimetype, String targetEncoding) String targetFilename, String targetMimetype, String targetEncoding)
{ {
Parser parser = null; Parser parser = null;
DocumentSelector documentSelector = null; DocumentSelector documentSelector = null;
switch(transform) switch (transform)
{ {
case ARCHIVE: case ARCHIVE:
parser = packageParser; parser = packageParser;
break; break;
case OUTLOOK_MSG: case OUTLOOK_MSG:
case POI_OFFICE: case POI_OFFICE:
case TEXT_MINING: case TEXT_MINING:
parser = officeParser; parser = officeParser;
break; break;
case PDF_BOX: case PDF_BOX:
parser = pdfParser; parser = pdfParser;
documentSelector = pdfBoxEmbededDocumentSelector; documentSelector = pdfBoxEmbededDocumentSelector;
break; break;
case POI: case POI:
parser = tikaOfficeDetectParser; parser = tikaOfficeDetectParser;
break; break;
case POI_OO_XML: case POI_OO_XML:
parser = ooXmlParser; parser = ooXmlParser;
break; break;
case TIKA_AUTO: case TIKA_AUTO:
parser = autoDetectParser; parser = autoDetectParser;
break; break;
} }
transform(parser, documentSelector, includeContents, notExtractBookmarksText, sourceFilename, targetFilename, targetMimetype, targetEncoding); transform(parser, documentSelector, includeContents, notExtractBookmarksText,
sourceFilename, targetFilename, targetMimetype, targetEncoding);
} }
private void transform(Parser parser, DocumentSelector documentSelector,
private void transform(Parser parser, DocumentSelector documentSelector, Boolean includeContents, Boolean includeContents,
Boolean notExtractBookmarksText, Boolean notExtractBookmarksText,
String sourceFilename, String sourceFilename,
String targetFilename, String targetMimetype, String targetEncoding) String targetFilename, String targetMimetype, String targetEncoding)
{ {
try (InputStream is = new BufferedInputStream(new FileInputStream(sourceFilename)); try (InputStream is = new BufferedInputStream(new FileInputStream(sourceFilename));
@ -688,7 +692,7 @@ public class Tika
} }
else else
{ {
SAXTransformerFactory factory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
TransformerHandler transformerHandler; TransformerHandler transformerHandler;
transformerHandler = factory.newTransformerHandler(); transformerHandler = factory.newTransformerHandler();
transformerHandler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes"); transformerHandler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");
@ -725,42 +729,52 @@ public class Tika
/** /**
* A wrapper around the normal Tika BodyContentHandler for CSV rather encoding than tab separated. * A wrapper around the normal Tika BodyContentHandler for CSV rather encoding than tab separated.
*/ */
protected static class CsvContentHandler extends BodyContentHandler { protected static class CsvContentHandler extends BodyContentHandler
private static final char[] comma = new char[]{ ',' }; {
private static final char[] comma = new char[]{','};
private static final Pattern all_nums = Pattern.compile("[\\d\\.\\-\\+]+"); private static final Pattern all_nums = Pattern.compile("[\\d\\.\\-\\+]+");
private boolean inCell = false; private boolean inCell = false;
private boolean needsComma = false; private boolean needsComma = false;
protected CsvContentHandler(Writer output) { protected CsvContentHandler(Writer output)
{
super(output); super(output);
} }
@Override @Override
public void ignorableWhitespace(char[] ch, int start, int length) public void ignorableWhitespace(char[] ch, int start, int length)
throws SAXException { throws SAXException
if(length == 1 && ch[0] == '\t') { {
if (length == 1 && ch[0] == '\t')
{
// Ignore tabs, as they mess up the CSV output // Ignore tabs, as they mess up the CSV output
} else { }
else
{
super.ignorableWhitespace(ch, start, length); super.ignorableWhitespace(ch, start, length);
} }
} }
@Override @Override
public void characters(char[] ch, int start, int length) public void characters(char[] ch, int start, int length)
throws SAXException { throws SAXException
if(inCell) { {
StringBuffer t = new StringBuffer(new String(ch,start,length)); if (inCell)
{
StringBuffer t = new StringBuffer(new String(ch, start, length));
// Quote if not all numbers // Quote if not all numbers
if(all_nums.matcher(t).matches()) if (all_nums.matcher(t).matches())
{ {
super.characters(ch, start, length); super.characters(ch, start, length);
} }
else else
{ {
for(int i=t.length()-1; i>=0; i--) { for (int i = t.length() - 1; i >= 0; i--)
if(t.charAt(i) == '\"') { {
if (t.charAt(i) == '\"')
{
// Double up double quotes // Double up double quotes
t.insert(i, '\"'); t.insert(i, '\"');
i--; i--;
@ -771,33 +785,45 @@ public class Tika
char[] c = t.toString().toCharArray(); char[] c = t.toString().toCharArray();
super.characters(c, 0, c.length); super.characters(c, 0, c.length);
} }
} else { }
else
{
super.characters(ch, start, length); super.characters(ch, start, length);
} }
} }
@Override @Override
public void startElement(String uri, String localName, String name, public void startElement(String uri, String localName, String name,
Attributes atts) throws SAXException { Attributes atts) throws SAXException
if(localName.equals("td")) { {
if (localName.equals("td"))
{
inCell = true; inCell = true;
if(needsComma) { if (needsComma)
{
super.characters(comma, 0, 1); super.characters(comma, 0, 1);
needsComma = true; needsComma = true;
} }
} else { }
else
{
super.startElement(uri, localName, name, atts); super.startElement(uri, localName, name, atts);
} }
} }
@Override @Override
public void endElement(String uri, String localName, String name) public void endElement(String uri, String localName, String name)
throws SAXException { throws SAXException
if(localName.equals("td")) { {
if (localName.equals("td"))
{
needsComma = true; needsComma = true;
inCell = false; inCell = false;
} else { }
if(localName.equals("tr")) { else
{
if (localName.equals("tr"))
{
needsComma = false; needsComma = false;
} }
super.endElement(uri, localName, name); super.endElement(uri, localName, name);
@ -830,5 +856,4 @@ public class Tika
return context; return context;
} }
} }

View File

@ -42,7 +42,7 @@ import org.springframework.stereotype.Component;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
* JavaExecutor implementation for running TIKA transformations. It loads the * JavaExecutor implementation for running TIKA transformations. It loads the
* transformation logic in the same JVM (check {@link Tika}). * transformation logic in the same JVM (check {@link Tika}).
*/ */
@Component @Component

View File

@ -48,21 +48,23 @@ import org.xml.sax.SAXException;
/** /**
* <a href="http://tika.apache.org/Apache Tika">Apache Tika</a> assumes that * <a href="http://tika.apache.org/Apache Tika">Apache Tika</a> assumes that
* you either know exactly what your content is, or that * you either know exactly what your content is, or that
* you'll leave it to auto-detection. * you'll leave it to auto-detection.
* Within Alfresco, we usually do know. However, from time * Within Alfresco, we usually do know. However, from time
* to time, we don't know if we have one of the old or one * to time, we don't know if we have one of the old or one
* of the new office files (eg .xls and .xlsx). * of the new office files (eg .xls and .xlsx).
* This class allows automatically selects the appropriate * This class allows automatically selects the appropriate
* old (OLE2) or new (OOXML) Tika parser as required. * old (OLE2) or new (OOXML) Tika parser as required.
* *
* @author Nick Burch * @author Nick Burch
*/ */
public class TikaOfficeDetectParser implements Parser { public class TikaOfficeDetectParser implements Parser
{
private final Parser ole2Parser = new OfficeParser(); private final Parser ole2Parser = new OfficeParser();
private final Parser ooxmlParser = new OOXMLParser(); private final Parser ooxmlParser = new OOXMLParser();
public Set<MediaType> getSupportedTypes(ParseContext parseContext) { public Set<MediaType> getSupportedTypes(ParseContext parseContext)
{
Set<MediaType> types = new HashSet<>(); Set<MediaType> types = new HashSet<>();
types.addAll(ole2Parser.getSupportedTypes(parseContext)); types.addAll(ole2Parser.getSupportedTypes(parseContext));
types.addAll(ooxmlParser.getSupportedTypes(parseContext)); types.addAll(ooxmlParser.getSupportedTypes(parseContext));
@ -70,9 +72,9 @@ public class TikaOfficeDetectParser implements Parser {
} }
public void parse(InputStream stream, public void parse(InputStream stream,
ContentHandler handler, Metadata metadata, ContentHandler handler, Metadata metadata,
ParseContext parseContext) throws IOException, SAXException, ParseContext parseContext) throws IOException, SAXException,
TikaException TikaException
{ {
byte[] initial4 = new byte[4]; byte[] initial4 = new byte[4];
InputStream wrapped; InputStream wrapped;
@ -93,10 +95,10 @@ public class TikaOfficeDetectParser implements Parser {
} }
// Which is it? // Which is it?
if(initial4[0] == POIFSConstants.OOXML_FILE_HEADER[0] && if (initial4[0] == POIFSConstants.OOXML_FILE_HEADER[0] &&
initial4[1] == POIFSConstants.OOXML_FILE_HEADER[1] && initial4[1] == POIFSConstants.OOXML_FILE_HEADER[1] &&
initial4[2] == POIFSConstants.OOXML_FILE_HEADER[2] && initial4[2] == POIFSConstants.OOXML_FILE_HEADER[2] &&
initial4[3] == POIFSConstants.OOXML_FILE_HEADER[3]) initial4[3] == POIFSConstants.OOXML_FILE_HEADER[3])
{ {
ooxmlParser.parse(wrapped, handler, metadata, parseContext); ooxmlParser.parse(wrapped, handler, metadata, parseContext);
} }
@ -110,8 +112,8 @@ public class TikaOfficeDetectParser implements Parser {
* @deprecated This method will be removed in Apache Tika 1.0. * @deprecated This method will be removed in Apache Tika 1.0.
*/ */
public void parse(InputStream stream, public void parse(InputStream stream,
ContentHandler handler, Metadata metadata) ContentHandler handler, Metadata metadata)
throws IOException, SAXException, TikaException throws IOException, SAXException, TikaException
{ {
parse(stream, handler, metadata, new ParseContext()); parse(stream, handler, metadata, new ParseContext());
} }

View File

@ -64,10 +64,18 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.http.HttpHeaders.ACCEPT;
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.APPLICATION_PDF_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.util.StringUtils.getFilenameExtension;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -93,15 +101,12 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.StringUtils;
/** /**
* Test the TikaController without a server. * Test the TikaController without a server.
@ -112,7 +117,7 @@ import org.springframework.util.StringUtils;
public class TikaControllerTest extends AbstractTransformerControllerTest public class TikaControllerTest extends AbstractTransformerControllerTest
{ {
private static final String EXPECTED_XHTML_CONTENT_CONTAINS = "<p>The quick brown fox jumps over the lazy dog</p>"; private static final String EXPECTED_XHTML_CONTENT_CONTAINS = "<p>The quick brown fox jumps over the lazy dog</p>";
private static final String EXPECTED_TEXT_CONTENT_CONTAINS = "The quick brown fox jumps over the lazy dog"; private static final String EXPECTED_TEXT_CONTENT_CONTAINS = "The quick brown fox jumps over the lazy dog";
private static final String EXPECTED_MSG_CONTENT_CONTAINS = "Recipients\n" + private static final String EXPECTED_MSG_CONTENT_CONTAINS = "Recipients\n" +
"\tmark.rogers@alfresco.com; speedy@quick.com; mrquick@nowhere.com\n" + "\tmark.rogers@alfresco.com; speedy@quick.com; mrquick@nowhere.com\n" +
"\n" + "\n" +
@ -130,7 +135,7 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@SpyBean @SpyBean
private TikaJavaExecutor javaExecutor; private TikaJavaExecutor javaExecutor;
@SpyBean @SpyBean
private TikaController controller; private TikaController controller;
@ -169,7 +174,7 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
String actualOptions = actualProperties.get("options"); String actualOptions = actualProperties.get("options");
String actualSource = actualProperties.get("source"); String actualSource = actualProperties.get("source");
String actualTarget = actualProperties.get("target"); String actualTarget = actualProperties.get("target");
String actualTargetExtension = StringUtils.getFilenameExtension(actualTarget); String actualTargetExtension = getFilenameExtension(actualTarget);
assertNotNull(actualSource); assertNotNull(actualSource);
assertNotNull(actualTarget); assertNotNull(actualTarget);
@ -226,34 +231,39 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
} }
private void transform(String transform, String sourceExtension, String targetExtension, private void transform(String transform, String sourceExtension, String targetExtension,
String sourceMimetype, String targetMimetype, String sourceMimetype, String targetMimetype,
Boolean includeContents, String expectedContentContains) throws Exception Boolean includeContents, String expectedContentContains) throws Exception
{ {
// We don't use targetFileBytes as some of the transforms contain different date text based on the os being used. // We don't use targetFileBytes as some of the transforms contain different date text based on the os being used.
mockTransformCommand(sourceExtension, targetExtension, sourceMimetype, false); mockTransformCommand(sourceExtension, targetExtension, sourceMimetype, false);
this.transform = transform; this.transform = transform;
this.targetMimetype = targetMimetype; this.targetMimetype = targetMimetype;
System.out.println("Test "+transform+" "+ sourceExtension +" to "+targetExtension); System.out.println("Test " + transform + " " + sourceExtension + " to " + targetExtension);
MockHttpServletRequestBuilder requestBuilder = includeContents == null MockHttpServletRequestBuilder requestBuilder = includeContents == null
? mockMvcRequest("/transform", sourceFile, "targetExtension", this.targetExtension) ? mockMvcRequest("/transform", sourceFile,
: mockMvcRequest("/transform", sourceFile, "targetExtension", this.targetExtension, "includeContents", includeContents.toString()); "targetExtension", this.targetExtension)
: mockMvcRequest("/transform", sourceFile,
"targetExtension", this.targetExtension, "includeContents", includeContents.toString());
MvcResult result = mockMvc.perform(requestBuilder) MvcResult result = mockMvc.perform(requestBuilder)
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick." + this.targetExtension)). .andExpect(header().string("Content-Disposition",
andReturn(); "attachment; filename*= UTF-8''quick." + this.targetExtension)).
andReturn();
String content = result.getResponse().getContentAsString(); String content = result.getResponse().getContentAsString();
assertTrue("The content did not include \""+expectedContentContains, content.contains(expectedContentContains)); assertTrue("The content did not include \"" + expectedContentContains,
content.contains(expectedContentContains));
} }
@Override @Override
// Add extra required parameters to the request. // Add extra required parameters to the request.
protected MockHttpServletRequestBuilder mockMvcRequest(String url, MockMultipartFile sourceFile, String... params) protected MockHttpServletRequestBuilder mockMvcRequest(String url, MockMultipartFile sourceFile,
String... params)
{ {
return super.mockMvcRequest(url, sourceFile, params) return super.mockMvcRequest(url, sourceFile, params)
.param("transform", transform) .param("transform", transform)
.param("targetEncoding", targetEncoding) .param("targetEncoding", targetEncoding)
.param("targetMimetype", targetMimetype); .param("targetMimetype", targetMimetype);
} }
@Test @Test
@ -337,8 +347,9 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
{ {
mockTransformCommand(PDF, TXT, MIMETYPE_PDF, true); mockTransformCommand(PDF, TXT, MIMETYPE_PDF, true);
targetEncoding = "rubbish"; targetEncoding = "rubbish";
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension)) mockMvc.perform(
.andExpect(status().is(INTERNAL_SERVER_ERROR.value())); mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(INTERNAL_SERVER_ERROR.value()));
} }
// --- Archive --- // --- Archive ---
@ -346,56 +357,56 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void zipToTextArchiveTest() throws Exception public void zipToTextArchiveTest() throws Exception
{ {
transform(ARCHIVE, ZIP, TXT, MIMETYPE_ZIP, MIMETYPE_TEXT_PLAIN,false, transform(ARCHIVE, ZIP, TXT, MIMETYPE_ZIP, MIMETYPE_TEXT_PLAIN, false,
"quick.html\n" + "quick.html\n" +
"\n" + "\n" +
"\n" + "\n" +
"quick.pdf\n" + "quick.pdf\n" +
"\n" + "\n" +
"\n"); "\n");
} }
@Test @Test
public void zipToTextIncludeArchiveTest() throws Exception public void zipToTextIncludeArchiveTest() throws Exception
{ {
transform(ARCHIVE, ZIP, TXT, MIMETYPE_ZIP, MIMETYPE_TEXT_PLAIN,true, transform(ARCHIVE, ZIP, TXT, MIMETYPE_ZIP, MIMETYPE_TEXT_PLAIN, true,
"quick.html\n" + "quick.html\n" +
"\n" + "\n" +
"\n" + "\n" +
"The quick brown fox jumps over the lazy dog\n" + "The quick brown fox jumps over the lazy dog\n" +
"\n" + "\n" +
"\n" + "\n" +
"\n" + "\n" +
"quick.pdf\n" + "quick.pdf\n" +
"\n" + "\n" +
"\n" + "\n" +
"The quick brown fox jumps over the lazy dog" + "The quick brown fox jumps over the lazy dog" +
"\n" + "\n" +
"\n"); "\n");
} }
@Test @Test
public void zipToTextExcludeArchiveTest() throws Exception public void zipToTextExcludeArchiveTest() throws Exception
{ {
transform(ARCHIVE, ZIP, TXT, MIMETYPE_ZIP, MIMETYPE_TEXT_PLAIN, transform(ARCHIVE, ZIP, TXT, MIMETYPE_ZIP, MIMETYPE_TEXT_PLAIN,
false, "\n" + false, "\n" +
"folder/subfolder/quick.jpg\n" + "folder/subfolder/quick.jpg\n" +
"\n" + "\n" +
"\n" + "\n" +
"quick.doc\n" + "quick.doc\n" +
"\n" + "\n" +
"\n" + "\n" +
"quick.html\n" + "quick.html\n" +
"\n" + "\n" +
"\n" + "\n" +
"quick.pdf\n" + "quick.pdf\n" +
"\n" + "\n" +
"\n" + "\n" +
"quick.txt\n" + "quick.txt\n" +
"\n" + "\n" +
"\n" + "\n" +
"quick.xml\n" + "quick.xml\n" +
"\n"); "\n");
} }
// --- OutlookMsg --- // --- OutlookMsg ---
@ -403,7 +414,8 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void msgToTxtOutlookMsgTest() throws Exception public void msgToTxtOutlookMsgTest() throws Exception
{ {
transform(OUTLOOK_MSG, MSG, TXT, MIMETYPE_OUTLOOK_MSG, MIMETYPE_TEXT_PLAIN, null, EXPECTED_MSG_CONTENT_CONTAINS); transform(OUTLOOK_MSG, MSG, TXT, MIMETYPE_OUTLOOK_MSG, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_MSG_CONTENT_CONTAINS);
} }
// --- PdfBox --- // --- PdfBox ---
@ -411,31 +423,36 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void pdfToTxtPdfBoxTest() throws Exception public void pdfToTxtPdfBoxTest() throws Exception
{ {
transform(PDF_BOX, PDF, TXT, MIMETYPE_PDF, MIMETYPE_TEXT_PLAIN, null, EXPECTED_TEXT_CONTENT_CONTAINS); transform(PDF_BOX, PDF, TXT, MIMETYPE_PDF, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_TEXT_CONTENT_CONTAINS);
} }
@Test @Test
public void pdfToCsvPdfBoxTest() throws Exception public void pdfToCsvPdfBoxTest() throws Exception
{ {
transform(PDF_BOX, PDF, CSV, MIMETYPE_PDF, MIMETYPE_TEXT_CSV, null, EXPECTED_TEXT_CONTENT_CONTAINS); // Yes it is just text transform(PDF_BOX, PDF, CSV, MIMETYPE_PDF, MIMETYPE_TEXT_CSV, null,
EXPECTED_TEXT_CONTENT_CONTAINS); // Yes it is just text
} }
@Test @Test
public void pdfToXmlPdfBoxTest() throws Exception public void pdfToXmlPdfBoxTest() throws Exception
{ {
transform(PDF_BOX, PDF, XML, MIMETYPE_PDF, MIMETYPE_XML, null, EXPECTED_XHTML_CONTENT_CONTAINS); // Yes it is just XHTML transform(PDF_BOX, PDF, XML, MIMETYPE_PDF, MIMETYPE_XML, null,
EXPECTED_XHTML_CONTENT_CONTAINS); // Yes it is just XHTML
} }
@Test @Test
public void pdfToXhtmlPdfBoxTest() throws Exception public void pdfToXhtmlPdfBoxTest() throws Exception
{ {
transform(PDF_BOX, PDF, XHTML, MIMETYPE_PDF, MIMETYPE_XHTML, null, EXPECTED_XHTML_CONTENT_CONTAINS); transform(PDF_BOX, PDF, XHTML, MIMETYPE_PDF, MIMETYPE_XHTML, null,
EXPECTED_XHTML_CONTENT_CONTAINS);
} }
@Test @Test
public void pdfToHtmlPdfBoxTest() throws Exception public void pdfToHtmlPdfBoxTest() throws Exception
{ {
transform(PDF_BOX, PDF, HTML, MIMETYPE_PDF, MIMETYPE_HTML, null, EXPECTED_XHTML_CONTENT_CONTAINS); // Yes it is just XHTML transform(PDF_BOX, PDF, HTML, MIMETYPE_PDF, MIMETYPE_HTML, null,
EXPECTED_XHTML_CONTENT_CONTAINS); // Yes it is just XHTML
} }
// --- Office --- // --- Office ---
@ -443,13 +460,15 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void msgToTxtOfficeTest() throws Exception public void msgToTxtOfficeTest() throws Exception
{ {
transform(POI_OFFICE, MSG, TXT, MIMETYPE_OUTLOOK_MSG, MIMETYPE_TEXT_PLAIN, null, EXPECTED_MSG_CONTENT_CONTAINS); transform(POI_OFFICE, MSG, TXT, MIMETYPE_OUTLOOK_MSG, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_MSG_CONTENT_CONTAINS);
} }
@Test @Test
public void docToTxtOfficeTest() throws Exception public void docToTxtOfficeTest() throws Exception
{ {
transform(POI_OFFICE, DOC, TXT, MIMETYPE_WORD, MIMETYPE_TEXT_PLAIN, null, EXPECTED_TEXT_CONTENT_CONTAINS); transform(POI_OFFICE, DOC, TXT, MIMETYPE_WORD, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_TEXT_CONTENT_CONTAINS);
} }
// --- Poi --- // --- Poi ---
@ -457,7 +476,8 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void xslxToCsvPoiTest() throws Exception public void xslxToCsvPoiTest() throws Exception
{ {
transform(POI, XSLX, CSV, MIMETYPE_OPENXML_SPREADSHEET, MIMETYPE_TEXT_CSV, null, EXPECTED_CSV_CONTENT_CONTAINS); transform(POI, XSLX, CSV, MIMETYPE_OPENXML_SPREADSHEET, MIMETYPE_TEXT_CSV, null,
EXPECTED_CSV_CONTENT_CONTAINS);
} }
// --- OOXML --- // --- OOXML ---
@ -465,13 +485,15 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void docxToTxtOoXmlTest() throws Exception public void docxToTxtOoXmlTest() throws Exception
{ {
transform(POI_OO_XML, DOCX, TXT, MIMETYPE_OPENXML_WORDPROCESSING, MIMETYPE_TEXT_PLAIN, null, EXPECTED_TEXT_CONTENT_CONTAINS); transform(POI_OO_XML, DOCX, TXT, MIMETYPE_OPENXML_WORDPROCESSING, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_TEXT_CONTENT_CONTAINS);
} }
@Test @Test
public void pptxToTxtOoXmlTest() throws Exception public void pptxToTxtOoXmlTest() throws Exception
{ {
transform(POI_OO_XML, PPTX, TXT, MIMETYPE_OPENXML_PRESENTATION, MIMETYPE_TEXT_PLAIN, null, EXPECTED_TEXT_CONTENT_CONTAINS); transform(POI_OO_XML, PPTX, TXT, MIMETYPE_OPENXML_PRESENTATION, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_TEXT_CONTENT_CONTAINS);
} }
// --- TikaAuto --- // --- TikaAuto ---
@ -479,13 +501,15 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void ppxtToTxtTikaAutoTest() throws Exception public void ppxtToTxtTikaAutoTest() throws Exception
{ {
transform(TIKA_AUTO, PPTX, TXT, MIMETYPE_OPENXML_PRESENTATION, MIMETYPE_TEXT_PLAIN, null, EXPECTED_TEXT_CONTENT_CONTAINS); transform(TIKA_AUTO, PPTX, TXT, MIMETYPE_OPENXML_PRESENTATION, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_TEXT_CONTENT_CONTAINS);
} }
@Test @Test
public void doctToTxtTikaAutoTest() throws Exception public void doctToTxtTikaAutoTest() throws Exception
{ {
transform(TIKA_AUTO, DOCX, TXT, MIMETYPE_OPENXML_WORDPROCESSING, MIMETYPE_TEXT_PLAIN, null, EXPECTED_TEXT_CONTENT_CONTAINS); transform(TIKA_AUTO, DOCX, TXT, MIMETYPE_OPENXML_WORDPROCESSING, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_TEXT_CONTENT_CONTAINS);
} }
// --- TextMining --- // --- TextMining ---
@ -493,16 +517,20 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void docToTxtTextMiningTest() throws Exception public void docToTxtTextMiningTest() throws Exception
{ {
transform(TEXT_MINING, DOC, TXT, MIMETYPE_WORD, MIMETYPE_TEXT_PLAIN, null, EXPECTED_TEXT_CONTENT_CONTAINS); transform(TEXT_MINING, DOC, TXT, MIMETYPE_WORD, MIMETYPE_TEXT_PLAIN, null,
EXPECTED_TEXT_CONTENT_CONTAINS);
} }
@Test @Test
public void pdfToTxtExtractBookmarksTest() throws Exception public void pdfToTxtExtractBookmarksTest() throws Exception
{ {
mockTransformCommand(PDF, TXT, MIMETYPE_PDF, true); mockTransformCommand(PDF, TXT, MIMETYPE_PDF, true);
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension).param("notExtractBookmarksText", "true")) mockMvc.perform(
.andExpect(status().is(OK.value())) mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension).param(
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick." + targetExtension)); "notExtractBookmarksText", "true"))
.andExpect(status().is(OK.value()))
.andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Override @Override
@ -510,13 +538,13 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
{ {
transformRequest.setSourceExtension(sourceExtension); transformRequest.setSourceExtension(sourceExtension);
transformRequest.setTargetExtension(targetExtension); transformRequest.setTargetExtension(targetExtension);
transformRequest.setSourceMediaType(MediaType.APPLICATION_PDF_VALUE); transformRequest.setSourceMediaType(APPLICATION_PDF_VALUE);
transformRequest.setTargetMediaType(MediaType.TEXT_PLAIN_VALUE); transformRequest.setTargetMediaType(TEXT_PLAIN_VALUE);
transformRequest.getTransformRequestOptions().put("transform", "PdfBox"); transformRequest.getTransformRequestOptions().put("transform", "PdfBox");
transformRequest.getTransformRequestOptions().put("targetMimetype", MediaType.TEXT_PLAIN_VALUE); transformRequest.getTransformRequestOptions().put("targetMimetype", TEXT_PLAIN_VALUE);
transformRequest.getTransformRequestOptions().put("targetEncoding", "UTF-8"); transformRequest.getTransformRequestOptions().put("targetEncoding", "UTF-8");
} }
@Test @Test
public void testPojoTransform() throws Exception public void testPojoTransform() throws Exception
{ {
@ -525,7 +553,6 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
File sourceFile = getTestFile("quick." + sourceExtension, true); File sourceFile = getTestFile("quick." + sourceExtension, true);
String targetFileRef = UUID.randomUUID().toString(); String targetFileRef = UUID.randomUUID().toString();
// Transformation Request POJO // Transformation Request POJO
TransformRequest transformRequest = new TransformRequest(); TransformRequest transformRequest = new TransformRequest();
transformRequest.setRequestId("1"); transformRequest.setRequestId("1");
@ -539,12 +566,13 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
// HTTP Request // HTTP Request
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=quick." + sourceExtension); headers.set(CONTENT_DISPOSITION, "attachment; filename=quick." + sourceExtension);
ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource( ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource(
sourceFile), headers, OK); sourceFile), headers, OK);
when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response); when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response);
when(alfrescoSharedFileStoreClient.saveFile(any())).thenReturn(new FileRefResponse(new FileRefEntity(targetFileRef))); when(alfrescoSharedFileStoreClient.saveFile(any()))
.thenReturn(new FileRefResponse(new FileRefEntity(targetFileRef)));
when(mockExecutionResult.getExitValue()).thenReturn(0); when(mockExecutionResult.getExitValue()).thenReturn(0);
// Update the Transformation Request with any specific params before sending it // Update the Transformation Request with any specific params before sending it
@ -552,18 +580,21 @@ public class TikaControllerTest extends AbstractTransformerControllerTest
// Serialize and call the transformer // Serialize and call the transformer
String tr = objectMapper.writeValueAsString(transformRequest); String tr = objectMapper.writeValueAsString(transformRequest);
String transformationReplyAsString = mockMvc.perform(MockMvcRequestBuilders.post("/transform") String transformationReplyAsString = mockMvc
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .perform(MockMvcRequestBuilders
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).content(tr)) .post("/transform")
.andExpect(status().is(HttpStatus.CREATED.value())) .header(ACCEPT, APPLICATION_JSON_VALUE)
.andReturn().getResponse().getContentAsString(); .header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.content(tr))
.andExpect(status().is(CREATED.value()))
.andReturn().getResponse().getContentAsString();
TransformReply transformReply = objectMapper.readValue(transformationReplyAsString, TransformReply.class); TransformReply transformReply = objectMapper.readValue(transformationReplyAsString,
TransformReply.class);
// Assert the reply // Assert the reply
assertEquals(transformRequest.getRequestId(), transformReply.getRequestId()); assertEquals(transformRequest.getRequestId(), transformReply.getRequestId());
assertEquals(transformRequest.getClientData(), transformReply.getClientData()); assertEquals(transformRequest.getClientData(), transformReply.getClientData());
assertEquals(transformRequest.getSchema(), transformReply.getSchema()); assertEquals(transformRequest.getSchema(), transformReply.getSchema());
} }
} }

View File

@ -48,7 +48,8 @@ public class TikaQueueTransformServiceIT extends AbstractQueueTransformServiceIT
@Override @Override
protected TransformRequest buildRequest() protected TransformRequest buildRequest()
{ {
return TransformRequest.builder() return TransformRequest
.builder()
.withRequestId(UUID.randomUUID().toString()) .withRequestId(UUID.randomUUID().toString())
.withSourceMediaType(MIMETYPE_OPENXML_WORDPROCESSING) .withSourceMediaType(MIMETYPE_OPENXML_WORDPROCESSING)
.withTargetMediaType(MIMETYPE_TEXT_PLAIN) .withTargetMediaType(MIMETYPE_TEXT_PLAIN)

View File

@ -26,24 +26,32 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import io.micrometer.core.instrument.MeterRegistry; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import io.micrometer.core.instrument.MeterRegistry;
@SpringBootApplication @SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class Application public class Application
{ {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
@Value("${container.name}") @Value("${container.name}")
private String containerName; private String containerName;
@Bean @Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() { MeterRegistryCustomizer<MeterRegistry> metricsCommonTags()
{
return registry -> registry.config().commonTags("containerName", containerName); return registry -> registry.config().commonTags("containerName", containerName);
} }
@ -51,4 +59,16 @@ public class Application
{ {
SpringApplication.run(Application.class, args); SpringApplication.run(Application.class, args);
} }
@EventListener(ApplicationReadyEvent.class)
public void startup()
{
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
logger.info("The transformers in this project use libraries from Apache. See the license at http://www.apache.org/licenses/LICENSE-2.0. or in /Apache\\\\ 2.0.txt");
logger.info("Additional libraries used:");
logger.info("* htmlparser http://htmlparser.sourceforge.net/license.html");
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
logger.info("Starting application components... Done");
}
} }

View File

@ -64,15 +64,6 @@ public class MiscController extends AbstractTransformerController
@Autowired @Autowired
private SelectingTransformer transformer; private SelectingTransformer transformer;
public MiscController()
{
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
logger.info("The transformers in this project use libraries from Apache. See the license at http://www.apache.org/licenses/LICENSE-2.0. or in /Apache\\\\ 2.0.txt");
logger.info("Additional libraries used:");
logger.info("* htmlparser http://htmlparser.sourceforge.net/license.html");
logger.info("--------------------------------------------------------------------------------------------------------------------------------------------------------------");
}
@Override @Override
public String getTransformerName() public String getTransformerName()
{ {
@ -91,8 +82,8 @@ public class MiscController extends AbstractTransformerController
// HtmlParserContentTransformer html -> text // HtmlParserContentTransformer html -> text
// See the Javadoc on this method and Probes.md for the choice of these values. // See the Javadoc on this method and Probes.md for the choice of these values.
return new ProbeTestTransform(this, "quick.html", "quick.txt", return new ProbeTestTransform(this, "quick.html", "quick.txt",
119, 30, 150, 1024, 119, 30, 150, 1024,
60*2+1,60*2) 60 * 2 + 1, 60 * 2)
{ {
@Override @Override
protected void executeTransformCommand(File sourceFile, File targetFile) protected void executeTransformCommand(File sourceFile, File targetFile)
@ -100,40 +91,47 @@ public class MiscController extends AbstractTransformerController
Map<String, String> parameters = new HashMap<>(); Map<String, String> parameters = new HashMap<>();
parameters.put(SOURCE_ENCODING, "UTF-8"); parameters.put(SOURCE_ENCODING, "UTF-8");
parameters.put(TARGET_ENCODING, "UTF-8"); parameters.put(TARGET_ENCODING, "UTF-8");
transformer.transform(sourceFile, targetFile, MIMETYPE_HTML, MIMETYPE_TEXT_PLAIN, parameters); transformer.transform(sourceFile, targetFile, MIMETYPE_HTML, MIMETYPE_TEXT_PLAIN,
parameters);
} }
}; };
} }
@Override @Override
public void processTransform(File sourceFile, File targetFile, Map<String, String> transformOptions, Long timeout) public void processTransform(File sourceFile, File targetFile,
Map<String, String> transformOptions, Long timeout)
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Processing request with: sourceFile '{}', targetFile '{}', transformOptions" + logger.debug(
" '{}', timeout {} ms", sourceFile, targetFile, transformOptions, timeout); "Processing request with: sourceFile '{}', targetFile '{}', transformOptions" +
" '{}', timeout {} ms", sourceFile, targetFile, transformOptions, timeout);
} }
String sourceMimetype = transformOptions.get("sourceMimetype"); String sourceMimetype = transformOptions.get("sourceMimetype");
String targetMimetype = transformOptions.get("targetMimetype"); String targetMimetype = transformOptions.get("targetMimetype");
transformer.transform(sourceFile, targetFile, sourceMimetype, targetMimetype, transformOptions); transformer.transform(sourceFile, targetFile, sourceMimetype, targetMimetype,
transformOptions);
} }
@PostMapping(value = "/transform", consumes = MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/transform", consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Resource> transform(HttpServletRequest request, public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile, @RequestParam("file") MultipartFile sourceMultipartFile,
@RequestParam(value = "targetExtension") String targetExtension, @RequestParam(value = "targetExtension") String targetExtension,
@RequestParam(value = "targetMimetype") String targetMimetype, @RequestParam(value = "targetMimetype") String targetMimetype,
@RequestParam(value = "sourceMimetype") String sourceMimetype, @RequestParam(value = "sourceMimetype") String sourceMimetype,
@RequestParam(value = "testDelay", required = false) Long testDelay, @RequestParam(value = "testDelay", required = false) Long testDelay,
@RequestParam Map<String, String> parameters) @RequestParam Map<String, String> parameters)
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Processing request with: sourceMimetype '{}', targetMimetype '{}' , targetExtension '{}' " + logger.debug(
", parameters '{}'", sourceMimetype, targetMimetype, targetExtension, parameters); "Processing request with: sourceMimetype '{}', targetMimetype '{}' , targetExtension '{}' " +
", parameters '{}'", sourceMimetype, targetMimetype, targetExtension, parameters);
} }
String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(), targetExtension); String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(),
targetExtension);
getProbeTestTransform().incrementTransformerCount(); getProbeTestTransform().incrementTransformerCount();
File sourceFile = createSourceFile(request, sourceMultipartFile); File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename); File targetFile = createTargetFile(request, targetFilename);

View File

@ -26,12 +26,10 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import org.alfresco.error.AlfrescoRuntimeException; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IMAGE_JPEG;
import org.alfresco.transform.client.model.Mimetype; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_KEYNOTE;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_NUMBERS;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_PAGES;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
@ -39,13 +37,16 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_KEYNOTE; import org.alfresco.error.AlfrescoRuntimeException;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_NUMBERS; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_PAGES; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
/** /**
* Converts Apple iWorks files to JPEGs for thumbnailing & previewing. * Converts Apple iWorks files to JPEGs for thumbnailing & previewing.
@ -60,24 +61,25 @@ import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_PAGES;
*/ */
public class AppleIWorksContentTransformer implements SelectableTransformer public class AppleIWorksContentTransformer implements SelectableTransformer
{ {
private static final Logger logger = LoggerFactory.getLogger(AppleIWorksContentTransformer.class); private static final Logger logger = LoggerFactory.getLogger(
AppleIWorksContentTransformer.class);
// Apple's zip entry names for previews in iWorks have changed over time. // Apple's zip entry names for previews in iWorks have changed over time.
private static final List<String> PDF_PATHS = Arrays.asList( private static final List<String> PDF_PATHS = ImmutableList.of(
"QuickLook/Preview.pdf"); // iWorks 2008/9 "QuickLook/Preview.pdf"); // iWorks 2008/9
private static final List<String> JPG_PATHS = Arrays.asList( private static final List<String> JPG_PATHS = ImmutableList.of(
"QuickLook/Thumbnail.jpg", // iWorks 2008/9 "QuickLook/Thumbnail.jpg", // iWorks 2008/9
"preview.jpg"); // iWorks 2013/14 (720 x 552) We use the best quality image. Others are: "preview.jpg"); // iWorks 2013/14 (720 x 552) We use the best quality image. Others are:
// (225 x 173) preview-web.jpg // (225 x 173) preview-web.jpg
// (53 x 41) preview-micro.jpg // (53 x 41) preview-micro.jpg
@Override @Override
public boolean isTransformable(String sourceMimetype, String targetMimetype, Map<String, String> parameters) public boolean isTransformable(String sourceMimetype, String targetMimetype,
Map<String, String> parameters)
{ {
boolean transformable = MIMETYPE_IWORK_KEYNOTE.equals(sourceMimetype) return MIMETYPE_IWORK_KEYNOTE.equals(sourceMimetype) ||
|| MIMETYPE_IWORK_NUMBERS.equals(sourceMimetype) MIMETYPE_IWORK_NUMBERS.equals(sourceMimetype) ||
|| MIMETYPE_IWORK_PAGES.equals(sourceMimetype); MIMETYPE_IWORK_PAGES.equals(sourceMimetype);
return transformable;
} }
@Override @Override
@ -86,20 +88,19 @@ public class AppleIWorksContentTransformer implements SelectableTransformer
final String sourceMimetype = parameters.get(SOURCE_MIMETYPE); final String sourceMimetype = parameters.get(SOURCE_MIMETYPE);
final String targetMimetype = parameters.get(TARGET_MIMETYPE); final String targetMimetype = parameters.get(TARGET_MIMETYPE);
if(logger.isDebugEnabled()) logger.debug("Performing IWorks to jpeg transform with sourceMimetype={} targetMimetype={}",
{ sourceMimetype, targetMimetype);
logger.debug("Performing IWorks to jpeg transform with sourceMimetype=" + sourceMimetype
+ " targetMimetype=" + targetMimetype);
}
// iWorks files are zip (or package) files. // iWorks files are zip (or package) files.
// If it's not a zip file, the resultant ZipException will be caught as an IOException below. // If it's not a zip file, the resultant ZipException will be caught as an IOException below.
try (ZipArchiveInputStream iWorksZip = new ZipArchiveInputStream( new BufferedInputStream( new FileInputStream(sourceFile)))) try (ZipArchiveInputStream iWorksZip = new ZipArchiveInputStream(
new BufferedInputStream(new FileInputStream(sourceFile))))
{ {
// Look through the zip file entries for the preview/thumbnail. // Look through the zip file entries for the preview/thumbnail.
List<String> paths = Mimetype.MIMETYPE_IMAGE_JPEG.equals(targetMimetype) ? JPG_PATHS : PDF_PATHS; List<String> paths = MIMETYPE_IMAGE_JPEG.equals(targetMimetype) ? JPG_PATHS : PDF_PATHS;
ZipArchiveEntry entry; ZipArchiveEntry entry;
boolean found = false; boolean found = false;
while ((entry=iWorksZip.getNextZipEntry()) != null) while ((entry = iWorksZip.getNextZipEntry()) != null)
{ {
String name = entry.getName(); String name = entry.getName();
if (paths.contains(name)) if (paths.contains(name))
@ -110,14 +111,17 @@ public class AppleIWorksContentTransformer implements SelectableTransformer
} }
} }
if (! found) if (!found)
{ {
throw new AlfrescoRuntimeException("The source " + sourceMimetype + " file did not contain a " + targetMimetype + " preview"); throw new AlfrescoRuntimeException(
"The source " + sourceMimetype + " file did not contain a " + targetMimetype + " preview");
} }
} }
catch (IOException e) catch (IOException e)
{ {
throw new AlfrescoRuntimeException("Unable to transform " + sourceMimetype + " file. It should have been a zip format file.", e); throw new AlfrescoRuntimeException(
"Unable to transform " + sourceMimetype + " file. It should have been a zip format file.",
e);
} }
} }
} }

View File

@ -26,16 +26,12 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import org.htmlparser.Parser; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_HTML;
import org.htmlparser.beans.StringBean; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN;
import org.htmlparser.util.ParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Writer; import java.io.Writer;
import java.net.URLConnection; import java.net.URLConnection;
@ -43,8 +39,11 @@ import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.IllegalCharsetNameException;
import java.util.Map; import java.util.Map;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_HTML; import org.htmlparser.Parser;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN; import org.htmlparser.beans.StringBean;
import org.htmlparser.util.ParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Content transformer which wraps the HTML Parser library for * Content transformer which wraps the HTML Parser library for
@ -63,34 +62,36 @@ import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN;
* *
* <p> * <p>
* Tika Note - could be converted to use the Tika HTML parser, * Tika Note - could be converted to use the Tika HTML parser,
* but we'd potentially need a custom text handler to replicate * but we'd potentially need a custom text handler to replicate
* the current settings around links and non-breaking spaces. * the current settings around links and non-breaking spaces.
* </p> * </p>
* *
* @see <a href="http://htmlparser.sourceforge.net/">http://htmlparser.sourceforge.net</a>
* @see org.htmlparser.beans.StringBean
* @see <a href="http://sourceforge.net/tracker/?func=detail&aid=1644504&group_id=24399&atid=381401">HTML Parser</a>
*
* @author Derek Hulley * @author Derek Hulley
* @author eknizat * @author eknizat
* @see <a href="http://htmlparser.sourceforge.net/">http://htmlparser.sourceforge.net</a>
* @see org.htmlparser.beans.StringBean
* @see <a href="http://sourceforge.net/tracker/?func=detail&aid=1644504&group_id=24399&atid=381401">HTML Parser</a>
*/ */
public class HtmlParserContentTransformer implements SelectableTransformer public class HtmlParserContentTransformer implements SelectableTransformer
{ {
private static final Logger logger = LoggerFactory.getLogger(HtmlParserContentTransformer.class); private static final Logger logger = LoggerFactory.getLogger(
HtmlParserContentTransformer.class);
@Override @Override
public boolean isTransformable(String sourceMimetype, String targetMimetype, Map<String, String> parameters) public boolean isTransformable(String sourceMimetype, String targetMimetype,
Map<String, String> parameters)
{ {
return MIMETYPE_HTML.equals(sourceMimetype) && MIMETYPE_TEXT_PLAIN.equals(targetMimetype); return MIMETYPE_HTML.equals(sourceMimetype) && MIMETYPE_TEXT_PLAIN.equals(targetMimetype);
} }
@Override @Override
public void transform(File sourceFile, File targetFile, Map<String, String> parameters) throws Exception public void transform(File sourceFile, File targetFile, Map<String, String> parameters)
throws Exception
{ {
String sourceEncoding = parameters.get(SOURCE_ENCODING); String sourceEncoding = parameters.get(SOURCE_ENCODING);
checkEncodingParameter(sourceEncoding, SOURCE_ENCODING); checkEncodingParameter(sourceEncoding, SOURCE_ENCODING);
if(logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Performing HTML to text transform with sourceEncoding=" + sourceEncoding); logger.debug("Performing HTML to text transform with sourceEncoding=" + sourceEncoding);
} }
@ -105,42 +106,42 @@ public class HtmlParserContentTransformer implements SelectableTransformer
String text = extractor.getStrings(); String text = extractor.getStrings();
// write it to the writer // write it to the writer
try ( Writer writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(targetFile)))) try (Writer writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(targetFile))))
{ {
writer.write(text); writer.write(text);
} }
} }
private void checkEncodingParameter(String encoding, String paramterName) private void checkEncodingParameter(String encoding, String parameterName)
{ {
try try
{ {
if (encoding != null && !Charset.isSupported(encoding)) if (encoding != null && !Charset.isSupported(encoding))
{ {
throw new IllegalArgumentException(paramterName + "=" + encoding + " is not supported by the JVM."); throw new IllegalArgumentException(
parameterName + "=" + encoding + " is not supported by the JVM.");
} }
} }
catch (IllegalCharsetNameException e) catch (IllegalCharsetNameException e)
{ {
throw new IllegalArgumentException(paramterName + "=" + encoding + " is not a valid encoding."); throw new IllegalArgumentException(
parameterName + "=" + encoding + " is not a valid encoding.");
} }
} }
/** /**
* * <p>
* <p> * This code is based on a class of the same name, originally implemented in alfresco-repository.
* This code is based on a class of the same name, originally implemented in alfresco-repository. * </p>
* </p>
* *
* A version of {@link StringBean} which allows control of the * A version of {@link StringBean} which allows control of the
* encoding in the underlying HTML Parser. * encoding in the underlying HTML Parser.
* Unfortunately, StringBean doesn't allow easy over-riding of * Unfortunately, StringBean doesn't allow easy over-riding of
* this, so we have to duplicate some code to control this. * this, so we have to duplicate some code to control this.
* This allows us to correctly handle HTML files where the encoding * This allows us to correctly handle HTML files where the encoding
* is specified against the content property (rather than in the * is specified against the content property (rather than in the
* HTML Head Meta), see ALF-10466 for details. * HTML Head Meta), see ALF-10466 for details.
*
*
*/ */
private class EncodingAwareStringBean extends StringBean private class EncodingAwareStringBean extends StringBean
{ {
@ -148,9 +149,9 @@ public class HtmlParserContentTransformer implements SelectableTransformer
/** /**
* Sets the File to extract strings from, and the encoding * Sets the File to extract strings from, and the encoding
* it's in (if known to Alfresco) * it's in (if known to Alfresco)
* *
* @param file The File that text should be fetched from. * @param file The File that text should be fetched from.
* @param encoding The encoding of the input * @param encoding The encoding of the input
*/ */
public void setURL(File file, String encoding) public void setURL(File file, String encoding)
@ -158,7 +159,7 @@ public class HtmlParserContentTransformer implements SelectableTransformer
String previousURL = getURL(); String previousURL = getURL();
String newURL = file.getAbsolutePath(); String newURL = file.getAbsolutePath();
if ( (previousURL == null) || (!newURL.equals(previousURL)) ) if (previousURL == null || !newURL.equals(previousURL))
{ {
try try
{ {
@ -178,8 +179,10 @@ public class HtmlParserContentTransformer implements SelectableTransformer
mParser.setEncoding(encoding); mParser.setEncoding(encoding);
} }
mPropertySupport.firePropertyChange(StringBean.PROP_URL_PROPERTY, previousURL, getURL()); mPropertySupport.firePropertyChange(StringBean.PROP_URL_PROPERTY, previousURL,
mPropertySupport.firePropertyChange(StringBean.PROP_CONNECTION_PROPERTY, conn, mParser.getConnection()); getURL());
mPropertySupport.firePropertyChange(StringBean.PROP_CONNECTION_PROPERTY, conn,
mParser.getConnection());
setStrings(); setStrings();
} }
catch (ParserException pe) catch (ParserException pe)
@ -189,7 +192,8 @@ public class HtmlParserContentTransformer implements SelectableTransformer
} }
} }
public String getEncoding(){ public String getEncoding()
{
return mParser.getEncoding(); return mParser.getEncoding();
} }
} }

View File

@ -26,8 +26,36 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IMAGE_JPEG;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION_ADDIN;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION_SLIDE;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION_SLIDE_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION_TEMPLATE;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_PRESENTATION_TEMPLATE_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_SPREADSHEET;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_SPREADSHEET_ADDIN_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_SPREADSHEET_BINARY_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_SPREADSHEET_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_WORDPROCESSING;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_WORDPROCESSING_MACRO;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_WORD_TEMPLATE;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_OPENXML_WORD_TEMPLATE_MACRO;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.transform.client.model.Mimetype;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.PackageRelationship;
@ -36,74 +64,70 @@ import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.File; import com.google.common.collect.ImmutableList;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/** /**
* Extracts out Thumbnail JPEGs from OOXML files for thumbnailing & previewing. * Extracts out Thumbnail JPEGs from OOXML files for thumbnailing & previewing.
* This transformer will only work for OOXML files where thumbnailing was enabled, * This transformer will only work for OOXML files where thumbnailing was enabled,
* which isn't on by default on Windows, but is more common on Mac. * which isn't on by default on Windows, but is more common on Mac.
* *
* @author Nick Burch * @author Nick Burch
* @author eknizat * @author eknizat
*
*/ */
public class OOXMLThumbnailContentTransformer implements SelectableTransformer public class OOXMLThumbnailContentTransformer implements SelectableTransformer
{ {
private static final Logger logger = LoggerFactory.getLogger(OOXMLThumbnailContentTransformer.class); private static final Logger logger = LoggerFactory.getLogger(
OOXMLThumbnailContentTransformer.class);
private static final List<String> OOXML_MIMETYPES = Arrays.asList(new String[]{ private static final List<String> OOXML_MIMETYPES = ImmutableList.of(
Mimetype.MIMETYPE_OPENXML_WORDPROCESSING, MIMETYPE_OPENXML_WORDPROCESSING,
Mimetype.MIMETYPE_OPENXML_WORDPROCESSING_MACRO, MIMETYPE_OPENXML_WORDPROCESSING_MACRO,
Mimetype.MIMETYPE_OPENXML_WORD_TEMPLATE, MIMETYPE_OPENXML_WORD_TEMPLATE,
Mimetype.MIMETYPE_OPENXML_WORD_TEMPLATE_MACRO, MIMETYPE_OPENXML_WORD_TEMPLATE_MACRO,
Mimetype.MIMETYPE_OPENXML_PRESENTATION, MIMETYPE_OPENXML_PRESENTATION,
Mimetype.MIMETYPE_OPENXML_PRESENTATION_MACRO, MIMETYPE_OPENXML_PRESENTATION_MACRO,
Mimetype.MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW, MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW,
Mimetype.MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW_MACRO, MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW_MACRO,
Mimetype.MIMETYPE_OPENXML_PRESENTATION_TEMPLATE, MIMETYPE_OPENXML_PRESENTATION_TEMPLATE,
Mimetype.MIMETYPE_OPENXML_PRESENTATION_TEMPLATE_MACRO, MIMETYPE_OPENXML_PRESENTATION_TEMPLATE_MACRO,
Mimetype.MIMETYPE_OPENXML_PRESENTATION_ADDIN, MIMETYPE_OPENXML_PRESENTATION_ADDIN,
Mimetype.MIMETYPE_OPENXML_PRESENTATION_SLIDE, MIMETYPE_OPENXML_PRESENTATION_SLIDE,
Mimetype.MIMETYPE_OPENXML_PRESENTATION_SLIDE_MACRO, MIMETYPE_OPENXML_PRESENTATION_SLIDE_MACRO,
Mimetype.MIMETYPE_OPENXML_SPREADSHEET, MIMETYPE_OPENXML_SPREADSHEET,
Mimetype.MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE, MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE,
Mimetype.MIMETYPE_OPENXML_SPREADSHEET_MACRO, MIMETYPE_OPENXML_SPREADSHEET_MACRO,
Mimetype.MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE_MACRO, MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE_MACRO,
Mimetype.MIMETYPE_OPENXML_SPREADSHEET_ADDIN_MACRO, MIMETYPE_OPENXML_SPREADSHEET_ADDIN_MACRO,
Mimetype.MIMETYPE_OPENXML_SPREADSHEET_BINARY_MACRO}); MIMETYPE_OPENXML_SPREADSHEET_BINARY_MACRO);
@Override @Override
public boolean isTransformable(String sourceMimetype, String targetMimetype, Map<String, String> parameters) public boolean isTransformable(String sourceMimetype, String targetMimetype,
Map<String, String> parameters)
{ {
// only support [OOXML] -> JPEG // only support [OOXML] -> JPEG
return Mimetype.MIMETYPE_IMAGE_JPEG.equals(targetMimetype) && OOXML_MIMETYPES.contains(sourceMimetype); return MIMETYPE_IMAGE_JPEG.equals(targetMimetype) &&
OOXML_MIMETYPES.contains(sourceMimetype);
} }
@Override @Override
public void transform(File sourceFile, File targetFile, Map<String, String> parameters) throws Exception public void transform(File sourceFile, File targetFile, Map<String, String> parameters)
throws Exception
{ {
final String sourceMimetype = parameters.get(SOURCE_MIMETYPE); final String sourceMimetype = parameters.get(SOURCE_MIMETYPE);
final String targetMimetype = parameters.get(TARGET_MIMETYPE); final String targetMimetype = parameters.get(TARGET_MIMETYPE);
if(logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Performing OOXML to jpeg transform with sourceMimetype=" + sourceMimetype logger.debug("Performing OOXML to jpeg transform with sourceMimetype=" + sourceMimetype
+ " targetMimetype=" + targetMimetype); + " targetMimetype=" + targetMimetype);
} }
try (OPCPackage pkg = OPCPackage.open(sourceFile.getPath())) try (OPCPackage pkg = OPCPackage.open(sourceFile.getPath()))
{ {
// Does it have a thumbnail? // Does it have a thumbnail?
PackageRelationshipCollection rels = pkg.getRelationshipsByType(PackageRelationshipTypes.THUMBNAIL); PackageRelationshipCollection rels = pkg.getRelationshipsByType(
PackageRelationshipTypes.THUMBNAIL);
if (rels.size() > 0) if (rels.size() > 0)
{ {
// Get the thumbnail part // Get the thumbnail part
@ -118,7 +142,8 @@ public class OOXMLThumbnailContentTransformer implements SelectableTransformer
else else
{ {
logger.debug("No thumbnail present in file."); logger.debug("No thumbnail present in file.");
throw new Exception("No thumbnail present in file, unable to generate " + targetMimetype); throw new Exception(
"No thumbnail present in file, unable to generate " + targetMimetype);
} }
} }
catch (IOException e) catch (IOException e)

View File

@ -26,15 +26,10 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import org.alfresco.transformer.logging.LogEntry;
import java.io.File; import java.io.File;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
/** /**
*
* Implemented by transformers used by {@link SelectingTransformer}. * Implemented by transformers used by {@link SelectingTransformer}.
* *
* @author eknizat * @author eknizat
@ -48,21 +43,23 @@ public interface SelectableTransformer
/** /**
* Implementation of the actual transformation. * Implementation of the actual transformation.
*
* @param sourceFile * @param sourceFile
* @param targetFile * @param targetFile
* @param parameters * @param parameters
* @throws Exception * @throws Exception
*/ */
public void transform(File sourceFile, File targetFile, Map<String, String> parameters) throws Exception; void transform(File sourceFile, File targetFile, Map<String, String> parameters)
throws Exception;
/** /**
* Determine whether this transformer is applicable for the given MIME types. * Determine whether this transformer is applicable for the given MIME types.
*
* @param sourceMimetype * @param sourceMimetype
* @param targetMimetype * @param targetMimetype
* @param parameters * @param parameters
* @return * @return
*/ */
public boolean isTransformable(String sourceMimetype, String targetMimetype, Map<String, String> parameters); boolean isTransformable(String sourceMimetype, String targetMimetype,
Map<String, String> parameters);
} }

View File

@ -26,6 +26,14 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.transform.exceptions.TransformException; import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.logging.LogEntry; import org.alfresco.transformer.logging.LogEntry;
@ -33,52 +41,47 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.File; import com.google.common.collect.ImmutableList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
/** /**
*
* The SelectingTransformer selects a registered {@link SelectableTransformer} * The SelectingTransformer selects a registered {@link SelectableTransformer}
* and delegates the transformation to its implementation. * and delegates the transformation to its implementation.
* *
* @author eknizat * @author eknizat
*
*/ */
@Component @Component
public class SelectingTransformer public class SelectingTransformer
{ {
private static final Logger logger = LoggerFactory.getLogger(SelectingTransformer.class); private static final Logger logger = LoggerFactory.getLogger(SelectingTransformer.class);
private List<SelectableTransformer> transformers = new LinkedList<>(); private final List<SelectableTransformer> transformers;
public SelectingTransformer() public SelectingTransformer()
{ {
transformers.add(new AppleIWorksContentTransformer()); transformers = ImmutableList.of(
transformers.add(new HtmlParserContentTransformer()); new AppleIWorksContentTransformer(),
transformers.add(new StringExtractingContentTransformer()); new HtmlParserContentTransformer(),
transformers.add(new TextToPdfContentTransformer()); new StringExtractingContentTransformer(),
// transformers.add(new OOXMLThumbnailContentTransformer()); // Doesn't work with java 11, transformer and test disabled new TextToPdfContentTransformer()
// new OOXMLThumbnailContentTransformer()); // Doesn't work with java 11, transformer and test disabled
);
} }
/** /**
* Performs a transform using a transformer selected based on the provided sourceMimetype and targetMimetype * Performs a transform using a transformer selected based on the provided sourceMimetype and targetMimetype
* @param sourceFile File to transform from *
* @param targetFile File to transform to * @param sourceFile File to transform from
* @param targetFile File to transform to
* @param sourceMimetype Mimetype of the source file * @param sourceMimetype Mimetype of the source file
* @throws TransformException * @throws TransformException
*/ */
public void transform(File sourceFile, File targetFile, String sourceMimetype, String targetMimetype, public void transform(File sourceFile, File targetFile, String sourceMimetype,
Map<String, String> parameters) throws TransformException String targetMimetype, Map<String, String> parameters) throws TransformException
{ {
try try
{ {
SelectableTransformer transformer = selectTransformer(sourceMimetype, targetMimetype, parameters); final SelectableTransformer transformer = selectTransformer(sourceMimetype,
targetMimetype, parameters);
logOptions(sourceFile, targetFile, parameters); logOptions(sourceFile, targetFile, parameters);
transformer.transform(sourceFile, targetFile, parameters); transformer.transform(sourceFile, targetFile, parameters);
} }
@ -93,32 +96,30 @@ public class SelectingTransformer
if (!targetFile.exists()) if (!targetFile.exists())
{ {
throw new TransformException(INTERNAL_SERVER_ERROR.value(), throw new TransformException(INTERNAL_SERVER_ERROR.value(),
"Transformer failed to create an output file. Target file does not exist."); "Transformer failed to create an output file. Target file does not exist.");
} }
if (sourceFile.length() > 0 && targetFile.length() == 0) if (sourceFile.length() > 0 && targetFile.length() == 0)
{ {
throw new TransformException(INTERNAL_SERVER_ERROR.value(), throw new TransformException(INTERNAL_SERVER_ERROR.value(),
"Transformer failed to create an output file. Target file is empty but source file was not empty."); "Transformer failed to create an output file. Target file is empty but source file was not empty.");
} }
} }
private SelectableTransformer selectTransformer(String sourceMimetype, String targetMimetype, private SelectableTransformer selectTransformer(String sourceMimetype, String targetMimetype,
Map<String, String> parameters) Map<String, String> parameters)
{ {
for (SelectableTransformer transformer : transformers) for (SelectableTransformer transformer : transformers)
{ {
if (transformer.isTransformable(sourceMimetype, targetMimetype, parameters)) if (transformer.isTransformable(sourceMimetype, targetMimetype, parameters))
{ {
if (logger.isDebugEnabled()) logger.debug("Using {} to transform from {} to {}",
{ transformer.getClass().getName(), sourceMimetype, targetMimetype);
logger.debug("Using " + transformer.getClass().getName()
+ " to transform from " + sourceMimetype + " to " + targetMimetype );
}
return transformer; return transformer;
} }
} }
throw new AlfrescoRuntimeException( "Could not select a transformer for sourceMimetype=" + sourceMimetype throw new AlfrescoRuntimeException(
+ " targetMimetype=" + targetMimetype); "Could not select a transformer for sourceMimetype=" + sourceMimetype
+ " targetMimetype=" + targetMimetype);
} }
private static String getMessage(Exception e) private static String getMessage(Exception e)
@ -129,7 +130,8 @@ public class SelectingTransformer
private void logOptions(File sourceFile, File targetFile, Map<String, String> parameters) private void logOptions(File sourceFile, File targetFile, Map<String, String> parameters)
{ {
StringJoiner sj = new StringJoiner(" "); StringJoiner sj = new StringJoiner(" ");
parameters.forEach( (k, v) -> sj.add("--" + k + "=" + v)); // keeping the existing style used in other T-Engines parameters.forEach((k, v) -> sj.add(
"--" + k + "=" + v)); // keeping the existing style used in other T-Engines
sj.add(getExtension(sourceFile)); sj.add(getExtension(sourceFile));
sj.add(getExtension(targetFile)); sj.add(getExtension(targetFile));
LogEntry.setOptions(sj.toString()); LogEntry.setOptions(sj.toString());
@ -137,9 +139,8 @@ public class SelectingTransformer
private String getExtension(File file) private String getExtension(File file)
{ {
String name = file.getName(); final String name = file.getName();
int i = name.lastIndexOf('.'); int i = name.lastIndexOf('.');
String ext = i == -1 ? "???" : name.substring(i + 1); return i == -1 ? "???" : name.substring(i + 1);
return ext;
} }
} }

View File

@ -26,29 +26,25 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import org.apache.commons.logging.Log; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_DITA;
import org.apache.commons.logging.LogFactory; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_JAVASCRIPT;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Reader; import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer; import java.io.Writer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.IllegalCharsetNameException;
import java.util.Map; import java.util.Map;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_DITA; import org.apache.commons.logging.Log;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_JAVASCRIPT; import org.apache.commons.logging.LogFactory;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN;
/** /**
* Converts any textual format to plain text. * Converts any textual format to plain text.
@ -60,7 +56,6 @@ import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN;
* This code is based on a class of the same name originally implemented in alfresco-repository. * This code is based on a class of the same name originally implemented in alfresco-repository.
* </p> * </p>
* *
*
* @author Derek Hulley * @author Derek Hulley
* @author eknizat * @author eknizat
*/ */
@ -70,13 +65,13 @@ public class StringExtractingContentTransformer implements SelectableTransformer
private static final Log logger = LogFactory.getLog(StringExtractingContentTransformer.class); private static final Log logger = LogFactory.getLog(StringExtractingContentTransformer.class);
@Override @Override
public boolean isTransformable(String sourceMimetype, String targetMimetype, Map<String, String> parameters) public boolean isTransformable(String sourceMimetype, String targetMimetype,
Map<String, String> parameters)
{ {
boolean transformable = (sourceMimetype.startsWith("text/") return (sourceMimetype.startsWith("text/")
|| MIMETYPE_JAVASCRIPT.equals(sourceMimetype) || MIMETYPE_JAVASCRIPT.equals(sourceMimetype)
|| MIMETYPE_DITA.equals(sourceMimetype)) || MIMETYPE_DITA.equals(sourceMimetype))
&& MIMETYPE_TEXT_PLAIN.equals(targetMimetype); && MIMETYPE_TEXT_PLAIN.equals(targetMimetype);
return transformable;
} }
/** /**
@ -88,16 +83,17 @@ public class StringExtractingContentTransformer implements SelectableTransformer
* be unformatted but valid. * be unformatted but valid.
*/ */
@Override @Override
public void transform(File sourceFile, File targetFile, Map<String, String> parameters) throws Exception public void transform(File sourceFile, File targetFile, Map<String, String> parameters)
throws Exception
{ {
String sourceEncoding = parameters.get(SOURCE_ENCODING); String sourceEncoding = parameters.get(SOURCE_ENCODING);
String targetEncoding = parameters.get(TARGET_ENCODING); String targetEncoding = parameters.get(TARGET_ENCODING);
if(logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Performing text to text transform with sourceEncoding=" + sourceEncoding logger.debug("Performing text to text transform with sourceEncoding=" + sourceEncoding
+ " targetEncoding=" + targetEncoding); + " targetEncoding=" + targetEncoding);
} }
Reader charReader = null; Reader charReader = null;
@ -107,23 +103,27 @@ public class StringExtractingContentTransformer implements SelectableTransformer
// Build reader // Build reader
if (sourceEncoding == null) if (sourceEncoding == null)
{ {
charReader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile))); charReader = new BufferedReader(
new InputStreamReader(new FileInputStream(sourceFile)));
} }
else else
{ {
checkEncodingParameter(sourceEncoding, SOURCE_ENCODING); checkEncodingParameter(sourceEncoding, SOURCE_ENCODING);
charReader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), sourceEncoding)); charReader = new BufferedReader(
new InputStreamReader(new FileInputStream(sourceFile), sourceEncoding));
} }
// Build writer // Build writer
if (targetEncoding == null) if (targetEncoding == null)
{ {
charWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile))); charWriter = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(targetFile)));
} }
else else
{ {
checkEncodingParameter( targetEncoding, TARGET_ENCODING); checkEncodingParameter(targetEncoding, TARGET_ENCODING);
charWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile), targetEncoding)); charWriter = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(targetFile), targetEncoding));
} }
// copy from the one to the other // copy from the one to the other
@ -157,12 +157,14 @@ public class StringExtractingContentTransformer implements SelectableTransformer
{ {
if (!Charset.isSupported(encoding)) if (!Charset.isSupported(encoding))
{ {
throw new IllegalArgumentException(paramterName + "=" + encoding + " is not supported by the JVM."); throw new IllegalArgumentException(
paramterName + "=" + encoding + " is not supported by the JVM.");
} }
} }
catch (IllegalCharsetNameException e) catch (IllegalCharsetNameException e)
{ {
throw new IllegalArgumentException(paramterName + "=" + encoding + " is not a valid encoding."); throw new IllegalArgumentException(
paramterName + "=" + encoding + " is not a valid encoding.");
} }
} }
} }

View File

@ -26,14 +26,11 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import org.alfresco.error.AlfrescoRuntimeException; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_DITA;
import org.apache.pdfbox.pdmodel.PDDocument; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_PDF;
import org.apache.pdfbox.pdmodel.PDPage; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_CSV;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN;
import org.apache.pdfbox.pdmodel.font.PDType1Font; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_XML;
import org.apache.pdfbox.tools.TextToPDF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
@ -49,14 +46,16 @@ import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_DITA; import org.alfresco.error.AlfrescoRuntimeException;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_PDF; import org.apache.pdfbox.pdmodel.PDDocument;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_CSV; import org.apache.pdfbox.pdmodel.PDPage;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_XML; import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.tools.TextToPDF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
*
* <p> * <p>
* This code is based on a class of the same name originally implemented in alfresco-repository. * This code is based on a class of the same name originally implemented in alfresco-repository.
* </p> * </p>
@ -65,14 +64,14 @@ import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_XML;
* *
* @author Derek Hulley * @author Derek Hulley
* @author eknizat * @author eknizat
*/ */
public class TextToPdfContentTransformer implements SelectableTransformer public class TextToPdfContentTransformer implements SelectableTransformer
{ {
private static final Logger logger = LoggerFactory.getLogger(TextToPdfContentTransformer.class); private static final Logger logger = LoggerFactory.getLogger(TextToPdfContentTransformer.class);
public static final String PAGE_LIMIT = "pageLimit"; public static final String PAGE_LIMIT = "pageLimit";
private PagedTextToPDF transformer; private final PagedTextToPDF transformer;
public TextToPdfContentTransformer() public TextToPdfContentTransformer()
{ {
@ -83,11 +82,12 @@ public class TextToPdfContentTransformer implements SelectableTransformer
{ {
try try
{ {
transformer.setFont(transformer.getStandardFont(fontName)); transformer.setFont(PagedTextToPDF.getStandardFont(fontName));
} }
catch (Throwable e) catch (Throwable e)
{ {
throw new AlfrescoRuntimeException("Unable to set Standard Font for PDF generation: " + fontName, e); throw new AlfrescoRuntimeException(
"Unable to set Standard Font for PDF generation: " + fontName, e);
} }
} }
@ -99,29 +99,30 @@ public class TextToPdfContentTransformer implements SelectableTransformer
} }
catch (Throwable e) catch (Throwable e)
{ {
throw new AlfrescoRuntimeException("Unable to set Font Size for PDF generation: " + fontSize); throw new AlfrescoRuntimeException(
"Unable to set Font Size for PDF generation: " + fontSize);
} }
} }
@Override @Override
public boolean isTransformable(String sourceMimetype, String targetMimetype, Map<String, String> parameters) public boolean isTransformable(String sourceMimetype, String targetMimetype,
Map<String, String> parameters)
{ {
boolean transformable = ( (MIMETYPE_TEXT_PLAIN.equals(sourceMimetype) return (MIMETYPE_TEXT_PLAIN.equals(sourceMimetype) ||
|| MIMETYPE_TEXT_CSV.equals(sourceMimetype) MIMETYPE_TEXT_CSV.equals(sourceMimetype) ||
|| MIMETYPE_DITA.equals(sourceMimetype) MIMETYPE_DITA.equals(sourceMimetype) ||
|| MIMETYPE_XML.equals(sourceMimetype) ) MIMETYPE_XML.equals(sourceMimetype)) &&
&& MIMETYPE_PDF.equals(targetMimetype)); MIMETYPE_PDF.equals(targetMimetype);
return transformable;
} }
@Override @Override
public void transform(File sourceFile, File targetFile, Map<String, String> parameters) throws Exception public void transform(File sourceFile, File targetFile, Map<String, String> parameters)
throws Exception
{ {
String sourceEncoding = parameters.get(SOURCE_ENCODING); String sourceEncoding = parameters.get(SOURCE_ENCODING);
String stringPageLimit = parameters.get(PAGE_LIMIT); String stringPageLimit = parameters.get(PAGE_LIMIT);
int pageLimit = -1; int pageLimit = -1;
if ( stringPageLimit != null) if (stringPageLimit != null)
{ {
pageLimit = parseInt(stringPageLimit, PAGE_LIMIT); pageLimit = parseInt(stringPageLimit, PAGE_LIMIT);
} }
@ -148,18 +149,19 @@ public class TextToPdfContentTransformer implements SelectableTransformer
protected InputStreamReader buildReader(InputStream is, String encoding) protected InputStreamReader buildReader(InputStream is, String encoding)
{ {
// If they gave an encoding, try to use it // If they gave an encoding, try to use it
if(encoding != null) if (encoding != null)
{ {
Charset charset = null; Charset charset = null;
try try
{ {
charset = Charset.forName(encoding); charset = Charset.forName(encoding);
} catch(Exception e) }
catch (Exception e)
{ {
logger.warn("JVM doesn't understand encoding '" + encoding + logger.warn("JVM doesn't understand encoding '" + encoding +
"' when transforming text to pdf"); "' when transforming text to pdf");
} }
if(charset != null) if (charset != null)
{ {
logger.debug("Processing plain text in encoding " + charset.displayName()); logger.debug("Processing plain text in encoding " + charset.displayName());
return new InputStreamReader(is, charset); return new InputStreamReader(is, charset);
@ -175,24 +177,31 @@ public class TextToPdfContentTransformer implements SelectableTransformer
{ {
// REPO-1066: duplicating the following lines from org.apache.pdfbox.tools.TextToPDF because they made them private // REPO-1066: duplicating the following lines from org.apache.pdfbox.tools.TextToPDF because they made them private
// before the upgrade to pdfbox 2.0.8, in pdfbox 1.8, this piece of code was public in org.apache.pdfbox.pdmodel.font.PDType1Font // before the upgrade to pdfbox 2.0.8, in pdfbox 1.8, this piece of code was public in org.apache.pdfbox.pdmodel.font.PDType1Font
static PDType1Font getStandardFont(String name) { static PDType1Font getStandardFont(String name)
return (PDType1Font)STANDARD_14.get(name); {
return STANDARD_14.get(name);
} }
private static final Map<String, PDType1Font> STANDARD_14 = new HashMap<String, PDType1Font>();
private static final Map<String, PDType1Font> STANDARD_14 = new HashMap<>();
static static
{ {
STANDARD_14.put(PDType1Font.TIMES_ROMAN.getBaseFont(), PDType1Font.TIMES_ROMAN); STANDARD_14.put(PDType1Font.TIMES_ROMAN.getBaseFont(), PDType1Font.TIMES_ROMAN);
STANDARD_14.put(PDType1Font.TIMES_BOLD.getBaseFont(), PDType1Font.TIMES_BOLD); STANDARD_14.put(PDType1Font.TIMES_BOLD.getBaseFont(), PDType1Font.TIMES_BOLD);
STANDARD_14.put(PDType1Font.TIMES_ITALIC.getBaseFont(), PDType1Font.TIMES_ITALIC); STANDARD_14.put(PDType1Font.TIMES_ITALIC.getBaseFont(), PDType1Font.TIMES_ITALIC);
STANDARD_14.put(PDType1Font.TIMES_BOLD_ITALIC.getBaseFont(), PDType1Font.TIMES_BOLD_ITALIC); STANDARD_14.put(PDType1Font.TIMES_BOLD_ITALIC.getBaseFont(),
PDType1Font.TIMES_BOLD_ITALIC);
STANDARD_14.put(PDType1Font.HELVETICA.getBaseFont(), PDType1Font.HELVETICA); STANDARD_14.put(PDType1Font.HELVETICA.getBaseFont(), PDType1Font.HELVETICA);
STANDARD_14.put(PDType1Font.HELVETICA_BOLD.getBaseFont(), PDType1Font.HELVETICA_BOLD); STANDARD_14.put(PDType1Font.HELVETICA_BOLD.getBaseFont(), PDType1Font.HELVETICA_BOLD);
STANDARD_14.put(PDType1Font.HELVETICA_OBLIQUE.getBaseFont(), PDType1Font.HELVETICA_OBLIQUE); STANDARD_14.put(PDType1Font.HELVETICA_OBLIQUE.getBaseFont(),
STANDARD_14.put(PDType1Font.HELVETICA_BOLD_OBLIQUE.getBaseFont(), PDType1Font.HELVETICA_BOLD_OBLIQUE); PDType1Font.HELVETICA_OBLIQUE);
STANDARD_14.put(PDType1Font.HELVETICA_BOLD_OBLIQUE.getBaseFont(),
PDType1Font.HELVETICA_BOLD_OBLIQUE);
STANDARD_14.put(PDType1Font.COURIER.getBaseFont(), PDType1Font.COURIER); STANDARD_14.put(PDType1Font.COURIER.getBaseFont(), PDType1Font.COURIER);
STANDARD_14.put(PDType1Font.COURIER_BOLD.getBaseFont(), PDType1Font.COURIER_BOLD); STANDARD_14.put(PDType1Font.COURIER_BOLD.getBaseFont(), PDType1Font.COURIER_BOLD);
STANDARD_14.put(PDType1Font.COURIER_OBLIQUE.getBaseFont(), PDType1Font.COURIER_OBLIQUE); STANDARD_14.put(PDType1Font.COURIER_OBLIQUE.getBaseFont(), PDType1Font.COURIER_OBLIQUE);
STANDARD_14.put(PDType1Font.COURIER_BOLD_OBLIQUE.getBaseFont(), PDType1Font.COURIER_BOLD_OBLIQUE); STANDARD_14.put(PDType1Font.COURIER_BOLD_OBLIQUE.getBaseFont(),
PDType1Font.COURIER_BOLD_OBLIQUE);
STANDARD_14.put(PDType1Font.SYMBOL.getBaseFont(), PDType1Font.SYMBOL); STANDARD_14.put(PDType1Font.SYMBOL.getBaseFont(), PDType1Font.SYMBOL);
STANDARD_14.put(PDType1Font.ZAPF_DINGBATS.getBaseFont(), PDType1Font.ZAPF_DINGBATS); STANDARD_14.put(PDType1Font.ZAPF_DINGBATS.getBaseFont(), PDType1Font.ZAPF_DINGBATS);
} }
@ -202,7 +211,7 @@ public class TextToPdfContentTransformer implements SelectableTransformer
// checks for page limits. // checks for page limits.
// The calling code must close the PDDocument once finished with it. // The calling code must close the PDDocument once finished with it.
public PDDocument createPDFFromText(Reader text, int pageLimit) public PDDocument createPDFFromText(Reader text, int pageLimit)
throws IOException throws IOException
{ {
//int pageLimit = (int)pageLimits.getValue(); //int pageLimit = (int)pageLimits.getValue();
PDDocument doc = null; PDDocument doc = null;
@ -210,23 +219,23 @@ public class TextToPdfContentTransformer implements SelectableTransformer
try try
{ {
final int margin = 40; final int margin = 40;
float height = getFont().getFontDescriptor().getFontBoundingBox().getHeight()/1000; float height = getFont().getFontDescriptor().getFontBoundingBox().getHeight() / 1000;
//calculate font height and increase by 5 percent. //calculate font height and increase by 5 percent.
height = height*getFontSize()*1.05f; height = height * getFontSize() * 1.05f;
doc = new PDDocument(); doc = new PDDocument();
BufferedReader data = new BufferedReader( text ); BufferedReader data = new BufferedReader(text);
String nextLine = null; String nextLine;
PDPage page = new PDPage(); PDPage page = new PDPage();
PDPageContentStream contentStream = null; PDPageContentStream contentStream = null;
float y = -1; float y = -1;
float maxStringLength = page.getMediaBox().getWidth() - 2*margin; float maxStringLength = page.getMediaBox().getWidth() - 2 * margin;
// There is a special case of creating a PDF document from an empty string. // There is a special case of creating a PDF document from an empty string.
boolean textIsEmpty = true; boolean textIsEmpty = true;
outer: outer:
while( (nextLine = data.readLine()) != null ) while ((nextLine = data.readLine()) != null)
{ {
// The input text is nonEmpty. New pages will be created and added // The input text is nonEmpty. New pages will be created and added
@ -234,29 +243,30 @@ public class TextToPdfContentTransformer implements SelectableTransformer
// the text. // the text.
textIsEmpty = false; textIsEmpty = false;
String[] lineWords = nextLine.trim().split( " " ); String[] lineWords = nextLine.trim().split(" ");
int lineIndex = 0; int lineIndex = 0;
while( lineIndex < lineWords.length ) while (lineIndex < lineWords.length)
{ {
StringBuffer nextLineToDraw = new StringBuffer(); final StringBuilder nextLineToDraw = new StringBuilder();
float lengthIfUsingNextWord = 0; float lengthIfUsingNextWord = 0;
do do
{ {
nextLineToDraw.append( lineWords[lineIndex] ); nextLineToDraw.append(lineWords[lineIndex]);
nextLineToDraw.append( " " ); nextLineToDraw.append(" ");
lineIndex++; lineIndex++;
if( lineIndex < lineWords.length ) if (lineIndex < lineWords.length)
{ {
String lineWithNextWord = nextLineToDraw.toString() + lineWords[lineIndex]; String lineWithNextWord = nextLineToDraw.toString() + lineWords[lineIndex];
lengthIfUsingNextWord = lengthIfUsingNextWord =
(getFont().getStringWidth( lineWithNextWord )/1000) * getFontSize(); (getFont().getStringWidth(
lineWithNextWord) / 1000) * getFontSize();
} }
} }
while( lineIndex < lineWords.length && while (lineIndex < lineWords.length &&
lengthIfUsingNextWord < maxStringLength ); lengthIfUsingNextWord < maxStringLength);
if( y < margin ) if (y < margin)
{ {
int test = pageCount +1; int test = pageCount + 1;
if (pageLimit > 0 && (pageCount++ >= pageLimit)) if (pageLimit > 0 && (pageCount++ >= pageLimit))
{ {
// pageLimits.getAction().throwIOExceptionIfRequired("Page limit ("+pageLimit+ // pageLimits.getAction().throwIOExceptionIfRequired("Page limit ("+pageLimit+
@ -267,8 +277,8 @@ public class TextToPdfContentTransformer implements SelectableTransformer
// We have crossed the end-of-page boundary and need to extend the // We have crossed the end-of-page boundary and need to extend the
// document by another page. // document by another page.
page = new PDPage(); page = new PDPage();
doc.addPage( page ); doc.addPage(page);
if( contentStream != null ) if (contentStream != null)
{ {
contentStream.endText(); contentStream.endText();
contentStream.close(); contentStream.close();
@ -277,18 +287,17 @@ public class TextToPdfContentTransformer implements SelectableTransformer
contentStream.setFont(getFont(), getFontSize()); contentStream.setFont(getFont(), getFontSize());
contentStream.beginText(); contentStream.beginText();
y = page.getMediaBox().getHeight() - margin + height; y = page.getMediaBox().getHeight() - margin + height;
contentStream.moveTextPositionByAmount( contentStream.moveTextPositionByAmount(margin, y);
margin, y );
} }
//System.out.println( "Drawing string at " + x + "," + y ); //System.out.println( "Drawing string at " + x + "," + y );
if( contentStream == null ) if (contentStream == null)
{ {
throw new IOException( "Error:Expected non-null content stream." ); throw new IOException("Error:Expected non-null content stream.");
} }
contentStream.moveTextPositionByAmount( 0, -height); contentStream.moveTextPositionByAmount(0, -height);
y -= height; y -= height;
contentStream.drawString( nextLineToDraw.toString() ); contentStream.drawString(nextLineToDraw.toString());
} }
} }
@ -300,15 +309,15 @@ public class TextToPdfContentTransformer implements SelectableTransformer
doc.addPage(page); doc.addPage(page);
} }
if( contentStream != null ) if (contentStream != null)
{ {
contentStream.endText(); contentStream.endText();
contentStream.close(); contentStream.close();
} }
} }
catch( IOException io ) catch (IOException io)
{ {
if( doc != null ) if (doc != null)
{ {
doc.close(); doc.close();
} }

View File

@ -26,30 +26,7 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import org.alfresco.error.AlfrescoRuntimeException; import static java.nio.charset.StandardCharsets.UTF_8;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transformer.transformers.AppleIWorksContentTransformer;
import org.alfresco.transformer.transformers.HtmlParserContentTransformer;
import org.alfresco.transformer.transformers.SelectingTransformer;
import org.alfresco.transformer.transformers.StringExtractingContentTransformer;
import org.alfresco.transformer.transformers.TextToPdfContentTransformer;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_HTML; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_HTML;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IMAGE_JPEG; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IMAGE_JPEG;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_KEYNOTE; import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_IWORK_KEYNOTE;
@ -64,18 +41,37 @@ import static org.springframework.http.HttpStatus.OK;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transformer.transformers.SelectingTransformer;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@WebMvcTest(MiscController.class) @WebMvcTest(MiscController.class)
@Import({SelectingTransformer.class}) @Import({SelectingTransformer.class})
public class MiscControllerTest extends AbstractTransformerControllerTest public class MiscControllerTest extends AbstractTransformerControllerTest
{ {
@Autowired @Autowired
private MiscController controller; private MiscController controller;
private String sourceEncoding = "UTF-8"; private final String sourceEncoding = "UTF-8";
private String targetEncoding = "UTF-8"; private final String targetEncoding = "UTF-8";
private String targetMimetype = MIMETYPE_TEXT_PLAIN; private final String targetMimetype = MIMETYPE_TEXT_PLAIN;
@Before @Before
public void before() throws Exception public void before() throws Exception
@ -88,12 +84,13 @@ public class MiscControllerTest extends AbstractTransformerControllerTest
expectedSourceFileBytes = readTestFile(sourceExtension); expectedSourceFileBytes = readTestFile(sourceExtension);
expectedTargetFileBytes = readTestFile(targetExtension); expectedTargetFileBytes = readTestFile(targetExtension);
//expectedTargetFileBytes = null; //expectedTargetFileBytes = null;
sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype,
expectedSourceFileBytes);
} }
@Override @Override
protected void mockTransformCommand(String sourceExtension, String targetExtension, String sourceMimetype, boolean readTargetFileBytes) throws IOException protected void mockTransformCommand(String sourceExtension, String targetExtension,
String sourceMimetype, boolean readTargetFileBytes) throws IOException
{ {
} }
@ -110,13 +107,14 @@ public class MiscControllerTest extends AbstractTransformerControllerTest
@Override @Override
// Add extra required parameters to the request. // Add extra required parameters to the request.
protected MockHttpServletRequestBuilder mockMvcRequest(String url, MockMultipartFile sourceFile, String... params) protected MockHttpServletRequestBuilder mockMvcRequest(String url, MockMultipartFile sourceFile,
String... params)
{ {
return super.mockMvcRequest(url, sourceFile, params) return super.mockMvcRequest(url, sourceFile, params)
.param("targetEncoding", targetEncoding) .param("targetEncoding", targetEncoding)
.param("sourceEncoding", sourceEncoding) .param("sourceEncoding", sourceEncoding)
.param("targetMimetype", targetMimetype) .param("targetMimetype", targetMimetype)
.param("sourceMimetype", sourceMimetype); .param("sourceMimetype", sourceMimetype);
} }
@Test @Test
@ -130,38 +128,39 @@ public class MiscControllerTest extends AbstractTransformerControllerTest
@Test @Test
public void testHTMLtoString() throws Exception public void testHTMLtoString() throws Exception
{ {
final String NEWLINE = System.getProperty ("line.separator"); final String NEWLINE = System.getProperty("line.separator");
final String TITLE = "Testing!"; final String TITLE = "Testing!";
final String TEXT_P1 = "This is some text in English"; final String TEXT_P1 = "This is some text in English";
final String TEXT_P2 = "This is more text in English"; final String TEXT_P2 = "This is more text in English";
final String TEXT_P3 = "C'est en Fran\u00e7ais et Espa\u00f1ol"; final String TEXT_P3 = "C'est en Fran\u00e7ais et Espa\u00f1ol";
String partA = "<html><head><title>" + TITLE + "</title></head>" + NEWLINE; String partA = "<html><head><title>" + TITLE + "</title></head>" + NEWLINE;
String partB = "<body><p>" + TEXT_P1 + "</p>" + NEWLINE + String partB = "<body><p>" + TEXT_P1 + "</p>" + NEWLINE +
"<p>" + TEXT_P2 + "</p>" + NEWLINE + "<p>" + TEXT_P2 + "</p>" + NEWLINE +
"<p>" + TEXT_P3 + "</p>" + NEWLINE; "<p>" + TEXT_P3 + "</p>" + NEWLINE;
String partC = "</body></html>"; String partC = "</body></html>";
final String expected = TITLE + NEWLINE + TEXT_P1 + NEWLINE + TEXT_P2 + NEWLINE + TEXT_P3 + NEWLINE; final String expected = TITLE + NEWLINE + TEXT_P1 + NEWLINE + TEXT_P2 + NEWLINE + TEXT_P3 + NEWLINE;
MvcResult result = sendText("html", MvcResult result = sendText("html",
"UTF-8", "UTF-8",
MIMETYPE_HTML, MIMETYPE_HTML,
"txt", "txt",
MIMETYPE_TEXT_PLAIN, MIMETYPE_TEXT_PLAIN,
"UTF-8", "UTF-8",
expected.getBytes()); expected.getBytes());
String contentResult = new String(result.getResponse().getContentAsByteArray(), targetEncoding); String contentResult = new String(result.getResponse().getContentAsByteArray(),
assertTrue("The content did not include \""+expected, contentResult.contains(expected)); targetEncoding);
assertTrue("The content did not include \"" + expected, contentResult.contains(expected));
} }
@Test @Test
public void testStringToString() throws Exception public void testStringToString() throws Exception
{ {
String expected = null; String expected;
byte[] content = null; byte[] content;
try try
{ {
content = "azAz10!<21>$%^&*()\t\r\n".getBytes("UTF-8"); content = "azAz10!<21>$%^&*()\t\r\n".getBytes(UTF_8);
expected = new String(content, "MacDingbat"); expected = new String(content, "MacDingbat");
} }
catch (UnsupportedEncodingException e) catch (UnsupportedEncodingException e)
@ -170,54 +169,55 @@ public class MiscControllerTest extends AbstractTransformerControllerTest
} }
MvcResult result = sendText("txt", MvcResult result = sendText("txt",
"MacDingbat", "MacDingbat",
MIMETYPE_TEXT_PLAIN, MIMETYPE_TEXT_PLAIN,
"txt", "txt",
MIMETYPE_TEXT_PLAIN, MIMETYPE_TEXT_PLAIN,
"UTF-8", "UTF-8",
content); content);
String contentResult = new String(result.getResponse().getContentAsByteArray(), targetEncoding); String contentResult = new String(result.getResponse().getContentAsByteArray(),
assertTrue("The content did not include \""+expected, contentResult.contains(expected)); targetEncoding);
assertTrue("The content did not include \"" + expected, contentResult.contains(expected));
} }
@Test @Test
public void testEmptyTextFileReturnsEmptyFile() throws Exception public void testEmptyTextFileReturnsEmptyFile() throws Exception
{ {
// Use empty content to create an empty source file // Use empty content to create an empty source file
byte[] content = new byte[0]; byte[] content = new byte[0];
MvcResult result = sendText("txt", MvcResult result = sendText("txt",
"UTF-8", "UTF-8",
MIMETYPE_TEXT_PLAIN, MIMETYPE_TEXT_PLAIN,
"txt", "txt",
MIMETYPE_TEXT_PLAIN, MIMETYPE_TEXT_PLAIN,
"UTF-8", "UTF-8",
content); content);
assertEquals("Returned content should be empty for an empty source file", 0, result.getResponse().getContentLength()); assertEquals("Returned content should be empty for an empty source file", 0,
result.getResponse().getContentLength());
} }
@Test @Test
public void textToPdf() throws Exception public void textToPdf() throws Exception
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
String expected = null; for (int i = 1; i <= 5; i++)
for (int i=1; i<=5; i++)
{ {
sb.append(i); sb.append(i);
sb.append(" I must not talk in class or feed my homework to my cat.\n"); sb.append(" I must not talk in class or feed my homework to my cat.\n");
} }
sb.append("\nBart\n"); sb.append("\nBart\n");
expected = sb.toString(); String expected = sb.toString();
MvcResult result = sendText("txt", MvcResult result = sendText("txt",
"UTF-8", "UTF-8",
MIMETYPE_TEXT_PLAIN, MIMETYPE_TEXT_PLAIN,
"pdf", "pdf",
MIMETYPE_PDF, MIMETYPE_PDF,
"UTF-8", "UTF-8",
expected.getBytes()); expected.getBytes());
// Read back in the PDF and check it // Read back in the PDF and check it
PDDocument doc = PDDocument.load(result.getResponse().getContentAsByteArray()); PDDocument doc = PDDocument.load(result.getResponse().getContentAsByteArray());
@ -257,46 +257,54 @@ public class MiscControllerTest extends AbstractTransformerControllerTest
imageBasedTransform("docx", MIMETYPE_OPENXML_WORDPROCESSING, MIMETYPE_IMAGE_JPEG, "jpeg"); imageBasedTransform("docx", MIMETYPE_OPENXML_WORDPROCESSING, MIMETYPE_IMAGE_JPEG, "jpeg");
} }
private void imageBasedTransform(String sourceExtension, String sourceMimetype, String targetMimetype, String targetExtension) throws Exception private void imageBasedTransform(String sourceExtension, String sourceMimetype,
String targetMimetype, String targetExtension) throws Exception
{ {
MockMultipartFile sourceFilex = new MockMultipartFile("file", "test_file." + sourceExtension, sourceMimetype, readTestFile(sourceExtension)); MockMultipartFile sourceFilex = new MockMultipartFile("file",
"test_file." + sourceExtension, sourceMimetype, readTestFile(sourceExtension));
MockHttpServletRequestBuilder requestBuilder = super.mockMvcRequest("/transform", sourceFilex) MockHttpServletRequestBuilder requestBuilder = super
.param("targetExtension", "jpeg") .mockMvcRequest("/transform", sourceFilex)
.param("targetMimetype", targetMimetype) .param("targetExtension", "jpeg")
.param("sourceMimetype", sourceMimetype); .param("targetMimetype", targetMimetype)
.param("sourceMimetype",
sourceMimetype);
MvcResult result = mockMvc.perform(requestBuilder) MvcResult result = mockMvc
.andExpect(status().is(OK.value())) .perform(requestBuilder)
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''test_file." + targetExtension)) .andExpect(status().is(OK.value()))
.andReturn(); .andExpect(header().string("Content-Disposition",
assertTrue("Expected image content but content is empty.",result.getResponse().getContentLengthLong() > 0L); "attachment; filename*= UTF-8''test_file." + targetExtension))
.andReturn();
assertTrue("Expected image content but content is empty.",
result.getResponse().getContentLengthLong() > 0L);
} }
private MvcResult sendText(String sourceExtension, private MvcResult sendText(String sourceExtension,
String sourceEncoding, String sourceEncoding,
String sourceMimetype, String sourceMimetype,
String targetExtension, String targetExtension,
String targetMimetype, String targetMimetype,
String targetEncoding, String targetEncoding,
byte[] content) throws Exception byte[] content) throws Exception
{ {
MockMultipartFile sourceFilex = new MockMultipartFile("file", "test_file." + sourceExtension, sourceMimetype, content); MockMultipartFile sourceFilex = new MockMultipartFile("file",
"test_file." + sourceExtension, sourceMimetype, content);
MockHttpServletRequestBuilder requestBuilder = super.mockMvcRequest("/transform", sourceFilex) MockHttpServletRequestBuilder requestBuilder = super
.param("targetExtension", targetExtension) .mockMvcRequest("/transform", sourceFilex)
.param("targetEncoding", targetEncoding) .param("targetExtension", targetExtension)
.param("targetMimetype", targetMimetype) .param("targetEncoding", targetEncoding)
.param("sourceEncoding", sourceEncoding) .param("targetMimetype", targetMimetype)
.param("sourceMimetype", sourceMimetype); .param("sourceEncoding", sourceEncoding)
.param("sourceMimetype", sourceMimetype);
return mockMvc
MvcResult result = mockMvc.perform(requestBuilder) .perform(requestBuilder)
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(header().string("Content-Disposition", "attachment; filename*= "+targetEncoding+"''test_file." + targetExtension)). .andExpect(header().string("Content-Disposition",
andReturn(); "attachment; filename*= " + targetEncoding + "''test_file." + targetExtension))
return result; .andReturn();
} }
private String clean(String text) private String clean(String text)
@ -307,5 +315,4 @@ public class MiscControllerTest extends AbstractTransformerControllerTest
text = text.replaceAll("\\n", ""); text = text.replaceAll("\\n", "");
return text; return text;
} }
} }

View File

@ -26,16 +26,16 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_HTML;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN;
import java.util.UUID;
import org.alfresco.transform.client.model.TransformRequest; import org.alfresco.transform.client.model.TransformRequest;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import java.util.UUID;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_HTML;
import static org.alfresco.transform.client.model.Mimetype.MIMETYPE_TEXT_PLAIN;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"activemq.url=nio://localhost:61616"}) properties = {"activemq.url=nio://localhost:61616"})
@ -44,14 +44,15 @@ public class MiscQueueTransformServiceIT extends AbstractQueueTransformServiceIT
@Override @Override
protected TransformRequest buildRequest() protected TransformRequest buildRequest()
{ {
return TransformRequest.builder() return TransformRequest
.withRequestId(UUID.randomUUID().toString()) .builder()
.withSourceMediaType(MIMETYPE_HTML) .withRequestId(UUID.randomUUID().toString())
.withTargetMediaType(MIMETYPE_TEXT_PLAIN) .withSourceMediaType(MIMETYPE_HTML)
.withTargetExtension("txt") .withTargetMediaType(MIMETYPE_TEXT_PLAIN)
.withSchema(1) .withTargetExtension("txt")
.withClientData("ACS") .withSchema(1)
.withSourceReference(UUID.randomUUID().toString()) .withClientData("ACS")
.withSourceSize(32L).build(); .withSourceReference(UUID.randomUUID().toString())
.withSourceSize(32L).build();
} }
} }

View File

@ -26,13 +26,8 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import org.junit.Test; import static org.alfresco.transformer.transformers.StringExtractingContentTransformer.SOURCE_ENCODING;
import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -41,9 +36,11 @@ import java.nio.file.Files;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.alfresco.transformer.transformers.StringExtractingContentTransformer.SOURCE_ENCODING; import org.junit.Test;
import static org.alfresco.transformer.transformers.StringExtractingContentTransformer.TARGET_ENCODING; import org.junit.runner.RunWith;
import static org.junit.Assert.*; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@Import(HtmlParserContentTransformer.class) @Import(HtmlParserContentTransformer.class)
@ -54,24 +51,24 @@ public class HtmlParserContentTransformerTest
/** /**
* Checks that we correctly handle text in different encodings, * Checks that we correctly handle text in different encodings,
* no matter if the encoding is specified on the Content Property * no matter if the encoding is specified on the Content Property
* or in a meta tag within the HTML itself. (ALF-10466) * or in a meta tag within the HTML itself. (ALF-10466)
* *
* On Windows, org.htmlparser.beans.StringBean.carriageReturn() appends a new system dependent new line * On Windows, org.htmlparser.beans.StringBean.carriageReturn() appends a new system dependent new line
* so we must be careful when checking the returned text * so we must be careful when checking the returned text
*/ */
@Test @Test
public void testEncodingHandling() throws Exception public void testEncodingHandling() throws Exception
{ {
final String NEWLINE = System.getProperty ("line.separator"); final String NEWLINE = System.getProperty("line.separator");
final String TITLE = "Testing!"; final String TITLE = "Testing!";
final String TEXT_P1 = "This is some text in English"; final String TEXT_P1 = "This is some text in English";
final String TEXT_P2 = "This is more text in English"; final String TEXT_P2 = "This is more text in English";
final String TEXT_P3 = "C'est en Fran\u00e7ais et Espa\u00f1ol"; final String TEXT_P3 = "C'est en Fran\u00e7ais et Espa\u00f1ol";
String partA = "<html><head><title>" + TITLE + "</title></head>" + NEWLINE; String partA = "<html><head><title>" + TITLE + "</title></head>" + NEWLINE;
String partB = "<body><p>" + TEXT_P1 + "</p>" + NEWLINE + String partB = "<body><p>" + TEXT_P1 + "</p>" + NEWLINE +
"<p>" + TEXT_P2 + "</p>" + NEWLINE + "<p>" + TEXT_P2 + "</p>" + NEWLINE +
"<p>" + TEXT_P3 + "</p>" + NEWLINE; "<p>" + TEXT_P3 + "</p>" + NEWLINE;
String partC = "</body></html>"; String partC = "</body></html>";
final String expected = TITLE + NEWLINE + TEXT_P1 + NEWLINE + TEXT_P2 + NEWLINE + TEXT_P3 + NEWLINE; final String expected = TITLE + NEWLINE + TEXT_P1 + NEWLINE + TEXT_P2 + NEWLINE + TEXT_P3 + NEWLINE;
@ -82,7 +79,7 @@ public class HtmlParserContentTransformerTest
{ {
// Content set to ISO 8859-1 // Content set to ISO 8859-1
tmpS = File.createTempFile("AlfrescoTestSource_", ".html"); tmpS = File.createTempFile("AlfrescoTestSource_", ".html");
writeToFile(tmpS, partA+partB+partC, "ISO-8859-1"); writeToFile(tmpS, partA + partB + partC, "ISO-8859-1");
tmpD = File.createTempFile("AlfrescoTestTarget_", ".txt"); tmpD = File.createTempFile("AlfrescoTestTarget_", ".txt");
@ -96,7 +93,7 @@ public class HtmlParserContentTransformerTest
// Content set to UTF-8 // Content set to UTF-8
tmpS = File.createTempFile("AlfrescoTestSource_", ".html"); tmpS = File.createTempFile("AlfrescoTestSource_", ".html");
writeToFile(tmpS, partA+partB+partC, "UTF-8"); writeToFile(tmpS, partA + partB + partC, "UTF-8");
tmpD = File.createTempFile("AlfrescoTestTarget_", ".txt"); tmpD = File.createTempFile("AlfrescoTestTarget_", ".txt");
parameters = new HashMap<>(); parameters = new HashMap<>();
@ -106,10 +103,9 @@ public class HtmlParserContentTransformerTest
tmpS.delete(); tmpS.delete();
tmpD.delete(); tmpD.delete();
// Content set to UTF-16 // Content set to UTF-16
tmpS = File.createTempFile("AlfrescoTestSource_", ".html"); tmpS = File.createTempFile("AlfrescoTestSource_", ".html");
writeToFile(tmpS, partA+partB+partC, "UTF-16"); writeToFile(tmpS, partA + partB + partC, "UTF-16");
tmpD = File.createTempFile("AlfrescoTestTarget_", ".txt"); tmpD = File.createTempFile("AlfrescoTestTarget_", ".txt");
parameters = new HashMap<>(); parameters = new HashMap<>();
@ -129,9 +125,9 @@ public class HtmlParserContentTransformerTest
// Content set to ISO 8859-1, meta set to UTF-8 // Content set to ISO 8859-1, meta set to UTF-8
tmpS = File.createTempFile("AlfrescoTestSource_", ".html"); tmpS = File.createTempFile("AlfrescoTestSource_", ".html");
String str = partA+ String str = partA +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">" +
partB+partC; partB + partC;
writeToFile(tmpS, str, "UTF-8"); writeToFile(tmpS, str, "UTF-8");
@ -144,7 +140,6 @@ public class HtmlParserContentTransformerTest
tmpS.delete(); tmpS.delete();
tmpD.delete(); tmpD.delete();
// Note - we can't test UTF-16 with only a meta encoding, // Note - we can't test UTF-16 with only a meta encoding,
// because without that the parser won't know about the // because without that the parser won't know about the
// 2 byte format so won't be able to identify the meta tag // 2 byte format so won't be able to identify the meta tag
@ -164,10 +159,8 @@ public class HtmlParserContentTransformerTest
} }
} }
private String readFromFile(File file, String encoding) throws Exception private String readFromFile(File file, final String encoding) throws Exception
{ {
String content = "wrong content"; return new String(Files.readAllBytes(file.toPath()), encoding);
content = new String(Files.readAllBytes(file.toPath()), encoding);
return content;
} }
} }

View File

@ -26,15 +26,8 @@
*/ */
package org.alfresco.transformer.transformers; package org.alfresco.transformer.transformers;
import org.apache.pdfbox.pdmodel.PDDocument; import static org.alfresco.transformer.transformers.TextToPdfContentTransformer.PAGE_LIMIT;
import org.apache.pdfbox.text.PDFTextStripper; import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -43,8 +36,14 @@ import java.io.StringWriter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.alfresco.transformer.transformers.TextToPdfContentTransformer.PAGE_LIMIT; import org.apache.pdfbox.pdmodel.PDDocument;
import static org.junit.Assert.*; import org.apache.pdfbox.text.PDFTextStripper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@Import(TextToPdfContentTransformer.class) @Import(TextToPdfContentTransformer.class)
@ -60,7 +59,6 @@ public class TextToPdfContentTransformerTest
transformer.setFontSize(20); transformer.setFontSize(20);
} }
@Test @Test
public void testUnlimitedPages() throws Exception public void testUnlimitedPages() throws Exception
{ {
@ -88,11 +86,11 @@ public class TextToPdfContentTransformerTest
private void transformTextAndCheckPageLength(int pageLimit) throws Exception private void transformTextAndCheckPageLength(int pageLimit) throws Exception
{ {
int pageLength = 32; int pageLength = 32;
int lines = (pageLength+10) * ((pageLimit > 0) ? pageLimit : 1); int lines = (pageLength + 10) * ((pageLimit > 0) ? pageLimit : 1);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
String checkText = null; String checkText = null;
int cutoff = pageLimit * pageLength; int cutoff = pageLimit * pageLength;
for (int i=1; i<=lines; i++) for (int i = 1; i <= lines; i++)
{ {
sb.append(i); sb.append(i);
sb.append(" I must not talk in class or feed my homework to my cat.\n"); sb.append(" I must not talk in class or feed my homework to my cat.\n");
@ -105,12 +103,12 @@ public class TextToPdfContentTransformerTest
transformTextAndCheck(text, "UTF-8", checkText, String.valueOf(pageLimit)); transformTextAndCheck(text, "UTF-8", checkText, String.valueOf(pageLimit));
} }
private void transformTextAndCheck(String text, String encoding, String checkText, String pageLimit) throws Exception private void transformTextAndCheck(String text, String encoding, String checkText,
String pageLimit) throws Exception
{ {
// Get a reader for the text // Get a reader for the text
File sourceFile = File.createTempFile("AlfrescoTestSource_", ".txt"); File sourceFile = File.createTempFile("AlfrescoTestSource_", ".txt");
writeToFile(sourceFile,text, encoding); writeToFile(sourceFile, text, encoding);
// And a temp writer // And a temp writer
File targetFile = File.createTempFile("AlfrescoTestTarget_", ".pdf"); File targetFile = File.createTempFile("AlfrescoTestTarget_", ".pdf");
@ -130,8 +128,8 @@ public class TextToPdfContentTransformerTest
String roundTrip = clean(textWriter.toString()); String roundTrip = clean(textWriter.toString());
assertEquals( assertEquals(
"Incorrect text in PDF when starting from text in " + encoding, "Incorrect text in PDF when starting from text in " + encoding,
checkText, roundTrip checkText, roundTrip
); );
sourceFile.delete(); sourceFile.delete();

View File

@ -26,20 +26,22 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static java.util.stream.Collectors.joining;
import static org.alfresco.transformer.fs.FileManager.buildFile; import static org.alfresco.transformer.fs.FileManager.buildFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFileName; import static org.alfresco.transformer.fs.FileManager.createTargetFileName;
import static org.alfresco.transformer.fs.FileManager.deleteFile; import static org.alfresco.transformer.fs.FileManager.deleteFile;
import static org.alfresco.transformer.fs.FileManager.getFilenameFromContentDisposition; import static org.alfresco.transformer.fs.FileManager.getFilenameFromContentDisposition;
import static org.alfresco.transformer.fs.FileManager.save; import static org.alfresco.transformer.fs.FileManager.save;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.util.StringUtils.getFilenameExtension;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.stream.Collectors;
import org.alfresco.transform.client.model.TransformReply; import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest; import org.alfresco.transform.client.model.TransformRequest;
@ -59,7 +61,6 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.validation.DirectFieldBindingResult; import org.springframework.validation.DirectFieldBindingResult;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -76,32 +77,34 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* *
* <p>Status Codes:</p> * <p>Status Codes:</p>
* <ul> * <ul>
* <li>200 Success</li> * <li>200 Success</li>
* <li>400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)</li> * <li>400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)</li>
* <li>400 Bad Request: Request parameter <name> is of the wrong type</li> * <li>400 Bad Request: Request parameter <name> is of the wrong type</li>
* <li>400 Bad Request: Transformer exit code was not 0 (possible problem with the source file)</li> * <li>400 Bad Request: Transformer exit code was not 0 (possible problem with the source file)</li>
* <li>400 Bad Request: The source filename was not supplied</li> * <li>400 Bad Request: The source filename was not supplied</li>
* <li>500 Internal Server Error: (no message with low level IO problems)</li> * <li>500 Internal Server Error: (no message with low level IO problems)</li>
* <li>500 Internal Server Error: The target filename was not supplied (should not happen as targetExtension is checked)</li> * <li>500 Internal Server Error: The target filename was not supplied (should not happen as targetExtension is checked)</li>
* <li>500 Internal Server Error: Transformer version check exit code was not 0</li> * <li>500 Internal Server Error: Transformer version check exit code was not 0</li>
* <li>500 Internal Server Error: Transformer version check failed to create any output</li> * <li>500 Internal Server Error: Transformer version check failed to create any output</li>
* <li>500 Internal Server Error: Could not read the target file</li> * <li>500 Internal Server Error: Could not read the target file</li>
* <li>500 Internal Server Error: The target filename was malformed (should not happen because of other checks)</li> * <li>500 Internal Server Error: The target filename was malformed (should not happen because of other checks)</li>
* <li>500 Internal Server Error: Transformer failed to create an output file (the exit code was 0, so there should be some content)</li> * <li>500 Internal Server Error: Transformer failed to create an output file (the exit code was 0, so there should be some content)</li>
* <li>500 Internal Server Error: Filename encoding error</li> * <li>500 Internal Server Error: Filename encoding error</li>
* <li>507 Insufficient Storage: Failed to store the source file</li> * <li>507 Insufficient Storage: Failed to store the source file</li>
* *
* <li>408 Request Timeout -- TODO implement general timeout mechanism rather than depend on transformer timeout * <li>408 Request Timeout -- TODO implement general timeout mechanism rather than depend on transformer timeout
* (might be possible for external processes)</li> * (might be possible for external processes)</li>
* <li>415 Unsupported Media Type -- TODO possibly implement a check on supported source and target mimetypes (probably not)</li> * <li>415 Unsupported Media Type -- TODO possibly implement a check on supported source and target mimetypes (probably not)</li>
* <li>429 Too Many Requests: Returned by liveness probe</li> * <li>429 Too Many Requests: Returned by liveness probe</li>
* </ul> * </ul>
* <p>Provides methods to help super classes perform /transform requests. Also responses to /version, /ready and /live * <p>Provides methods to help super classes perform /transform requests. Also responses to /version, /ready and /live
* requests.</p> * requests.</p>
*/ */
public abstract class AbstractTransformerController implements TransformController public abstract class AbstractTransformerController implements TransformController
{ {
private static final Logger logger = LoggerFactory.getLogger(AbstractTransformerController.class); private static final Logger logger = LoggerFactory.getLogger(
AbstractTransformerController.class);
private static String ENGINE_CONFIG = "engine_config.json"; private static String ENGINE_CONFIG = "engine_config.json";
@Autowired @Autowired
@ -160,9 +163,12 @@ public abstract class AbstractTransformerController implements TransformControll
final Errors errors = validateTransformRequest(request); final Errors errors = validateTransformRequest(request);
if (!errors.getAllErrors().isEmpty()) if (!errors.getAllErrors().isEmpty())
{ {
reply.setStatus(HttpStatus.BAD_REQUEST.value()); reply.setStatus(BAD_REQUEST.value());
reply.setErrorDetails(errors.getAllErrors().stream().map(Object::toString) reply.setErrorDetails(errors
.collect(Collectors.joining(", "))); .getAllErrors()
.stream()
.map(Object::toString)
.collect(joining(", ")));
logger.error("Invalid request, sending {}", reply); logger.error("Invalid request, sending {}", reply);
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus())); return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
@ -246,8 +252,8 @@ public abstract class AbstractTransformerController implements TransformControll
reply.setStatus(e.getStatusCode().value()); reply.setStatus(e.getStatusCode().value());
reply.setErrorDetails(messageWithCause("Failed at writing the transformed file. ", e)); reply.setErrorDetails(messageWithCause("Failed at writing the transformed file. ", e));
logger.error("Failed to save target file (HttpClientErrorException), sending {}" + logger.error("Failed to save target file (HttpClientErrorException), sending " + reply,
reply, e); e);
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus())); return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
} }
catch (Exception e) catch (Exception e)
@ -278,9 +284,9 @@ public abstract class AbstractTransformerController implements TransformControll
} }
reply.setTargetReference(targetRef.getEntry().getFileRef()); reply.setTargetReference(targetRef.getEntry().getFileRef());
reply.setStatus(HttpStatus.CREATED.value()); reply.setStatus(CREATED.value());
logger.info("Sending successful {}, timeout {} ms", reply); logger.info("Sending successful {}, timeout {} ms", reply, timeout);
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus())); return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
} }
@ -297,7 +303,7 @@ public abstract class AbstractTransformerController implements TransformControll
* @param sourceReference reference to the file in Alfresco Shared File Store * @param sourceReference reference to the file in Alfresco Shared File Store
* @return the file containing the source content for the transformation * @return the file containing the source content for the transformation
*/ */
private File loadSourceFile(final String sourceReference) throws Exception private File loadSourceFile(final String sourceReference)
{ {
ResponseEntity<Resource> responseEntity = alfrescoSharedFileStoreClient ResponseEntity<Resource> responseEntity = alfrescoSharedFileStoreClient
.retrieveFile(sourceReference); .retrieveFile(sourceReference);
@ -306,7 +312,7 @@ public abstract class AbstractTransformerController implements TransformControll
HttpHeaders headers = responseEntity.getHeaders(); HttpHeaders headers = responseEntity.getHeaders();
String filename = getFilenameFromContentDisposition(headers); String filename = getFilenameFromContentDisposition(headers);
String extension = StringUtils.getFilenameExtension(filename); String extension = getFilenameExtension(filename);
MediaType contentType = headers.getContentType(); MediaType contentType = headers.getContentType();
long size = headers.getContentLength(); long size = headers.getContentLength();
@ -314,7 +320,7 @@ public abstract class AbstractTransformerController implements TransformControll
if (body == null) if (body == null)
{ {
String message = "Source file with reference: " + sourceReference + " is null or empty. " String message = "Source file with reference: " + sourceReference + " is null or empty. "
+ "Transformation will fail and stop now as there is no content to be transformed."; + "Transformation will fail and stop now as there is no content to be transformed.";
logger.warn(message); logger.warn(message);
throw new TransformException(BAD_REQUEST.value(), message); throw new TransformException(BAD_REQUEST.value(), message);
} }
@ -322,7 +328,7 @@ public abstract class AbstractTransformerController implements TransformControll
logger.debug("Read source content {} length={} contentType={}", logger.debug("Read source content {} length={} contentType={}",
sourceReference, size, contentType); sourceReference, size, contentType);
save(body, file); save(body, file);
LogEntry.setSource(filename, size); LogEntry.setSource(filename, size);
return file; return file;

View File

@ -26,6 +26,11 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import java.util.Optional;
import javax.jms.Destination; import javax.jms.Destination;
import javax.jms.JMSException; import javax.jms.JMSException;
import javax.jms.Message; import javax.jms.Message;
@ -44,8 +49,6 @@ import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Optional;
/** /**
* Queue Transformer service. * Queue Transformer service.
* This service reads all the requests for the particular engine, forwards them to the worker * This service reads all the requests for the particular engine, forwards them to the worker
@ -56,12 +59,11 @@ import java.util.Optional;
* created on 18/12/2018 * created on 18/12/2018
*/ */
@Component @Component
@ConditionalOnProperty(name="activemq.url") @ConditionalOnProperty(name = "activemq.url")
public class QueueTransformService public class QueueTransformService
{ {
private static final Logger logger = LoggerFactory.getLogger(QueueTransformService.class); private static final Logger logger = LoggerFactory.getLogger(QueueTransformService.class);
// TODO: I know this is not smart but all the the transformation logic is in the Controller. // TODO: I know this is not smart but all the the transformation logic is in the Controller.
// The controller also manages the probes. There's tons of refactoring needed there, hence this. Sorry. // The controller also manages the probes. There's tons of refactoring needed there, hence this. Sorry.
@Autowired @Autowired
@ -98,7 +100,9 @@ public class QueueTransformService
} }
catch (JMSException e) catch (JMSException e)
{ {
logger.error("Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ", correlationId); logger.error(
"Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ",
correlationId);
return; return;
} }
@ -126,7 +130,7 @@ public class QueueTransformService
} }
TransformReply reply = transformController.transform(transformRequest.get(), null) TransformReply reply = transformController.transform(transformRequest.get(), null)
.getBody(); .getBody();
transformReplySender.send(replyToDestinationQueue, reply); transformReplySender.send(replyToDestinationQueue, reply);
} }
@ -150,36 +154,39 @@ public class QueueTransformService
{ {
String message = String message =
"MessageConversionException during T-Request deserialization of message with correlationID " "MessageConversionException during T-Request deserialization of message with correlationID "
+ correlationId + ": "; + correlationId + ": ";
throw new TransformException(HttpStatus.BAD_REQUEST.value(), message + e.getMessage()); throw new TransformException(BAD_REQUEST.value(), message + e.getMessage());
} }
catch (JMSException e) catch (JMSException e)
{ {
String message = String message =
"JMSException during T-Request deserialization of message with correlationID " "JMSException during T-Request deserialization of message with correlationID "
+ correlationId + ": "; + correlationId + ": ";
throw new TransformException(HttpStatus.INTERNAL_SERVER_ERROR.value(), throw new TransformException(INTERNAL_SERVER_ERROR.value(),
message + e.getMessage()); message + e.getMessage());
} }
catch (Exception e) catch (Exception e)
{ {
String message = String message =
"Exception during T-Request deserialization of message with correlationID " "Exception during T-Request deserialization of message with correlationID "
+ correlationId + ": "; + correlationId + ": ";
throw new TransformException(HttpStatus.INTERNAL_SERVER_ERROR.value(), throw new TransformException(INTERNAL_SERVER_ERROR.value(),
message + e.getMessage()); message + e.getMessage());
} }
} }
private void replyWithInternalSvErr(final Destination destination, final String msg, final String correlationId) private void replyWithInternalSvErr(final Destination destination, final String msg,
{
replyWithError(destination, HttpStatus.INTERNAL_SERVER_ERROR, msg, correlationId);
}
private void replyWithError(final Destination destination, final HttpStatus status, final String msg,
final String correlationId) final String correlationId)
{ {
final TransformReply reply = TransformReply.builder() replyWithError(destination, INTERNAL_SERVER_ERROR, msg, correlationId);
}
private void replyWithError(final Destination destination, final HttpStatus status,
final String msg,
final String correlationId)
{
final TransformReply reply = TransformReply
.builder()
.withStatus(status.value()) .withStatus(status.value())
.withErrorDetails(msg) .withErrorDetails(msg)
.build(); .build();

View File

@ -53,7 +53,7 @@ public class TransformInterceptor extends HandlerInterceptorAdapter
@Override @Override
public void afterCompletion(HttpServletRequest request, public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) HttpServletResponse response, Object handler, Exception ex)
{ {
// TargetFile cannot be deleted until completion, otherwise 0 bytes are sent. // TargetFile cannot be deleted until completion, otherwise 0 bytes are sent.
deleteFile(request, SOURCE_FILE); deleteFile(request, SOURCE_FILE);

View File

@ -26,6 +26,9 @@
*/ */
package org.alfresco.transformer.clients; package org.alfresco.transformer.clients;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import java.io.File; import java.io.File;
import org.alfresco.transform.exceptions.TransformException; import org.alfresco.transform.exceptions.TransformException;
@ -36,8 +39,6 @@ import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
@ -87,11 +88,11 @@ public class AlfrescoSharedFileStoreClient
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>(); LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("file", value); map.add("file", value);
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA); headers.setContentType(MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map,
headers); headers);
ResponseEntity<FileRefResponse> responseEntity = restTemplate ResponseEntity<FileRefResponse> responseEntity = restTemplate
.exchange(fileStoreUrl, HttpMethod.POST, requestEntity, FileRefResponse.class); .exchange(fileStoreUrl, POST, requestEntity, FileRefResponse.class);
return responseEntity.getBody(); return responseEntity.getBody();
} }
catch (HttpClientErrorException e) catch (HttpClientErrorException e)

View File

@ -26,12 +26,12 @@
*/ */
package org.alfresco.transformer.config; package org.alfresco.transformer.config;
import org.alfresco.transform.client.model.TransformRequestValidator;
import org.alfresco.transformer.TransformInterceptor; import org.alfresco.transformer.TransformInterceptor;
import org.alfresco.transformer.clients.AlfrescoSharedFileStoreClient; import org.alfresco.transformer.clients.AlfrescoSharedFileStoreClient;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.alfresco.transform.client.model.TransformRequestValidator;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@ -40,16 +40,18 @@ public class WebApplicationConfig implements WebMvcConfigurer
{ {
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry)
registry.addInterceptor(transformInterceptor()).addPathPatterns("/transform", "/live", "/ready"); {
registry.addInterceptor(transformInterceptor()).addPathPatterns("/transform", "/live",
"/ready");
} }
@Bean @Bean
public TransformInterceptor transformInterceptor() { public TransformInterceptor transformInterceptor()
{
return new TransformInterceptor(); return new TransformInterceptor();
} }
@Bean @Bean
public RestTemplate restTemplate() public RestTemplate restTemplate()
{ {
@ -57,7 +59,8 @@ public class WebApplicationConfig implements WebMvcConfigurer
} }
@Bean @Bean
public AlfrescoSharedFileStoreClient alfrescoSharedFileStoreClient(){ public AlfrescoSharedFileStoreClient alfrescoSharedFileStoreClient()
{
return new AlfrescoSharedFileStoreClient(); return new AlfrescoSharedFileStoreClient();
} }

View File

@ -37,12 +37,13 @@ import org.alfresco.util.exec.RuntimeExec;
import org.alfresco.util.exec.RuntimeExec.ExecutionResult; import org.alfresco.util.exec.RuntimeExec.ExecutionResult;
/** /**
*
*/ */
public abstract class AbstractCommandExecutor implements CommandExecutor public abstract class AbstractCommandExecutor implements CommandExecutor
{ {
private final RuntimeExec transformCommand = createTransformCommand(); private final RuntimeExec transformCommand = createTransformCommand();
private final RuntimeExec checkCommand = createCheckCommand(); private final RuntimeExec checkCommand = createCheckCommand();
protected abstract RuntimeExec createTransformCommand(); protected abstract RuntimeExec createTransformCommand();
protected abstract RuntimeExec createCheckCommand(); protected abstract RuntimeExec createCheckCommand();

View File

@ -34,7 +34,7 @@ import org.alfresco.transformer.logging.LogEntry;
/** /**
* Basic interface for executing transformations via Shell commands * Basic interface for executing transformations via Shell commands
* *
* @author Cezar Leahu * @author Cezar Leahu
*/ */
public interface CommandExecutor public interface CommandExecutor

View File

@ -32,7 +32,7 @@ import org.alfresco.transform.exceptions.TransformException;
/** /**
* Basic interface for executing transformations inside Java/JVM * Basic interface for executing transformations inside Java/JVM
* *
* @author Cezar Leahu * @author Cezar Leahu
*/ */
public interface JavaExecutor public interface JavaExecutor

View File

@ -26,9 +26,12 @@
*/ */
package org.alfresco.transformer.fs; package org.alfresco.transformer.fs;
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE; import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.util.StringUtils.getFilename;
import static org.springframework.util.StringUtils.getFilenameExtension;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -46,11 +49,11 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource; import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils; import org.springframework.web.util.UriUtils;
/** /**
*
*/ */
public class FileManager public class FileManager
{ {
@ -76,7 +79,7 @@ public class FileManager
public static File buildFile(String filename) public static File buildFile(String filename)
{ {
filename = checkFilename( false, filename); filename = checkFilename(false, filename);
LogEntry.setTarget(filename); LogEntry.setTarget(filename);
return TempFileProvider.createTempFile("target_", "_" + filename); return TempFileProvider.createTempFile("target_", "_" + filename);
} }
@ -98,12 +101,13 @@ public class FileManager
*/ */
private static String checkFilename(boolean source, String filename) private static String checkFilename(boolean source, String filename)
{ {
filename = StringUtils.getFilename(filename); filename = getFilename(filename);
if (filename == null || filename.isEmpty()) if (filename == null || filename.isEmpty())
{ {
String sourceOrTarget = source ? "source" : "target"; String sourceOrTarget = source ? "source" : "target";
int statusCode = source ? BAD_REQUEST.value() : INTERNAL_SERVER_ERROR.value(); int statusCode = source ? BAD_REQUEST.value() : INTERNAL_SERVER_ERROR.value();
throw new TransformException(statusCode, "The " + sourceOrTarget + " filename was not supplied"); throw new TransformException(statusCode,
"The " + sourceOrTarget + " filename was not supplied");
} }
return filename; return filename;
} }
@ -112,7 +116,8 @@ public class FileManager
{ {
try try
{ {
Files.copy(multipartFile.getInputStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(multipartFile.getInputStream(), file.toPath(),
StandardCopyOption.REPLACE_EXISTING);
} }
catch (IOException e) catch (IOException e)
{ {
@ -129,7 +134,8 @@ public class FileManager
} }
catch (IOException e) catch (IOException e)
{ {
throw new TransformException(INSUFFICIENT_STORAGE.value(), "Failed to store the source file", e); throw new TransformException(INSUFFICIENT_STORAGE.value(),
"Failed to store the source file", e);
} }
} }
@ -158,7 +164,7 @@ public class FileManager
public static String getFilenameFromContentDisposition(HttpHeaders headers) public static String getFilenameFromContentDisposition(HttpHeaders headers)
{ {
String filename = ""; String filename = "";
String contentDisposition = headers.getFirst(HttpHeaders.CONTENT_DISPOSITION); String contentDisposition = headers.getFirst(CONTENT_DISPOSITION);
if (contentDisposition != null) if (contentDisposition != null)
{ {
String[] strings = contentDisposition.split("; *"); String[] strings = contentDisposition.split("; *");
@ -171,28 +177,31 @@ public class FileManager
return filename; return filename;
} }
/** /**
* Returns the file name for the target file * Returns the file name for the target file
* *
* @param fileName Desired file name * @param fileName Desired file name
* @param targetExtension File extension * @param targetExtension File extension
* @return Target file name * @return Target file name
*/ */
public static String createTargetFileName(String fileName, String targetExtension) public static String createTargetFileName(final String fileName, final String targetExtension)
{ {
String targetFilename = null; final String sourceFilename = getFilename(fileName);
String sourceFilename = fileName;
sourceFilename = StringUtils.getFilename(sourceFilename); if (sourceFilename == null || sourceFilename.isEmpty())
if (sourceFilename != null && !sourceFilename.isEmpty())
{ {
String ext = StringUtils.getFilenameExtension(sourceFilename); return null;
targetFilename = (ext != null && !ext.isEmpty()
? sourceFilename.substring(0, sourceFilename.length()-ext.length()-1)
: sourceFilename)+
'.'+targetExtension;
} }
return targetFilename;
final String ext = getFilenameExtension(sourceFilename);
if (ext == null || ext.isEmpty())
{
return sourceFilename + '.' + targetExtension;
}
return sourceFilename.substring(0, sourceFilename.length() - ext.length() - 1) +
'.' + targetExtension;
} }
/** /**
@ -207,7 +216,7 @@ public class FileManager
{ {
String filename = multipartFile.getOriginalFilename(); String filename = multipartFile.getOriginalFilename();
long size = multipartFile.getSize(); long size = multipartFile.getSize();
filename = checkFilename( true, filename); filename = checkFilename(true, filename);
File file = TempFileProvider.createTempFile("source_", "_" + filename); File file = TempFileProvider.createTempFile("source_", "_" + filename);
request.setAttribute(SOURCE_FILE, file); request.setAttribute(SOURCE_FILE, file);
save(multipartFile, file); save(multipartFile, file);
@ -228,9 +237,8 @@ public class FileManager
targetFile) targetFile)
{ {
Resource targetResource = load(targetFile); Resource targetResource = load(targetFile);
targetFilename = UriUtils.encodePath(StringUtils.getFilename(targetFilename), "UTF-8"); targetFilename = UriUtils.encodePath(getFilename(targetFilename), "UTF-8");
return ResponseEntity.ok().header(HttpHeaders return ResponseEntity.ok().header(CONTENT_DISPOSITION,
.CONTENT_DISPOSITION,
"attachment; filename*= UTF-8''" + targetFilename).body(targetResource); "attachment; filename*= UTF-8''" + targetFilename).body(targetResource);
} }
} }

View File

@ -135,7 +135,7 @@ public final class LogEntry
int i = filename.lastIndexOf('.'); int i = filename.lastIndexOf('.');
if (i != -1) if (i != -1)
{ {
filename = filename.substring(i+1); filename = filename.substring(i + 1);
} }
return filename; return filename;
} }
@ -201,7 +201,8 @@ public final class LogEntry
if (logEntry.statusCode == OK.value()) if (logEntry.statusCode == OK.value())
{ {
logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start - logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start -
logEntry.durationStreamIn - max(logEntry.durationTransform, 0) - max(logEntry.durationDelay, 0); logEntry.durationStreamIn - max(logEntry.durationTransform,
0) - max(logEntry.durationDelay, 0);
} }
currentLogEntry.remove(); currentLogEntry.remove();
@ -228,17 +229,19 @@ public final class LogEntry
public String getDuration() public String getDuration()
{ {
long duration = durationStreamIn + max(durationTransform, 0) + max(durationDelay, 0) + max(durationStreamOut, 0); long duration = durationStreamIn + max(durationTransform, 0) + max(durationDelay, 0) + max(
durationStreamOut, 0);
return duration <= 5 return duration <= 5
? "" ? ""
: time(duration)+ : time(duration) +
" ("+ " (" +
(time(durationStreamIn)+' '+ (time(durationStreamIn) + ' ' +
time(durationTransform)+' '+ time(durationTransform) + ' ' +
(durationDelay > 0 (durationDelay > 0
? time(durationDelay)+' '+(durationStreamOut < 0 ? "-" : time(durationStreamOut)) ? time(durationDelay) + ' ' + (durationStreamOut < 0 ? "-" : time(
: time(durationStreamOut))).trim()+ durationStreamOut))
")"; : time(durationStreamOut))).trim() +
")";
} }
public String getSource() public String getSource()
@ -274,16 +277,16 @@ public final class LogEntry
private String time(long ms) private String time(long ms)
{ {
return ms == -1 ? "" : size(ms, "1ms", return ms == -1 ? "" : size(ms, "1ms",
new String[] { "ms", "s", "min", "hr" }, new String[]{"ms", "s", "min", "hr"},
new long[] { 1000, 60*1000, 60*60*1000, Long.MAX_VALUE}); new long[]{1000, 60 * 1000, 60 * 60 * 1000, Long.MAX_VALUE});
} }
private String size(long size) private String size(long size)
{ {
// TODO fix numeric overflow in TB expression // TODO fix numeric overflow in TB expression
return size == -1 ? "" : size(size, "1 byte", return size == -1 ? "" : size(size, "1 byte",
new String[] { "bytes", " KB", " MB", " GB", " TB" }, new String[]{"bytes", " KB", " MB", " GB", " TB"},
new long[] { 1024, 1024*1024, 1024*1024*1024, 1024*1024*1024*1024, Long.MAX_VALUE }); 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) private String size(long size, String singleValue, String[] units, long[] dividers)
@ -293,16 +296,16 @@ public final class LogEntry
return singleValue; return singleValue;
} }
long divider = 1; long divider = 1;
for(int i = 0; i < units.length-1; i++) for (int i = 0; i < units.length - 1; i++)
{ {
long nextDivider = dividers[i]; long nextDivider = dividers[i];
if(size < nextDivider) if (size < nextDivider)
{ {
return unitFormat(size, divider, units[i]); return unitFormat(size, divider, units[i]);
} }
divider = nextDivider; divider = nextDivider;
} }
return unitFormat(size, divider, units[units.length-1]); return unitFormat(size, divider, units[units.length - 1]);
} }
private String unitFormat(long size, long divider, String unit) private String unitFormat(long size, long divider, String unit)
@ -311,7 +314,7 @@ public final class LogEntry
int decimalPoint = (int) size % 10; int decimalPoint = (int) size % 10;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(size/10); sb.append(size / 10);
if (decimalPoint != 0) if (decimalPoint != 0)
{ {
sb.append("."); sb.append(".");

View File

@ -31,5 +31,5 @@ public interface StandardMessages
String LICENCE = String LICENCE =
"License rights for this program may be obtained from Alfresco Software, Ltd. pursuant to a written agreement\n" + "License rights for this program may be obtained from Alfresco Software, Ltd. pursuant to a written agreement\n" +
"and any use of this program without such an agreement is prohibited.\n" + "and any use of this program without such an agreement is prohibited.\n" +
"\n" ; "\n";
} }

View File

@ -98,7 +98,8 @@ public class MessagingConfig implements JmsListenerConfigurer
@Bean @Bean
@ConditionalOnProperty(name = "activemq.url") @ConditionalOnProperty(name = "activemq.url")
public Queue engineRequestQueue(@Value("${queue.engineRequestQueue}") String engineRequestQueueValue) public Queue engineRequestQueue(
@Value("${queue.engineRequestQueue}") String engineRequestQueueValue)
{ {
return new ActiveMQQueue(engineRequestQueueValue); return new ActiveMQQueue(engineRequestQueueValue);
} }

View File

@ -90,13 +90,13 @@ public class TransformMessageConverter implements MessageConverter
@NonNull final Object object, @NonNull final Object object,
@NonNull final Session session) throws JMSException, MessageConversionException @NonNull final Session session) throws JMSException, MessageConversionException
{ {
return converter.toMessage(object, session); return converter.toMessage(object, session);
} }
@Override @Override
@NonNull @NonNull
public Object fromMessage(@NonNull final Message message) throws JMSException public Object fromMessage(@NonNull final Message message) throws JMSException
{ {
return converter.fromMessage(message); return converter.fromMessage(message);
} }
} }

View File

@ -56,7 +56,8 @@ public class TransformReplySender
send(destination, reply, reply.getRequestId()); send(destination, reply, reply.getRequestId());
} }
public void send(final Destination destination, final TransformReply reply, final String correlationId) public void send(final Destination destination, final TransformReply reply,
final String correlationId)
{ {
try try
{ {

View File

@ -37,25 +37,19 @@ public class FileRefEntity
{ {
private String fileRef; private String fileRef;
public FileRefEntity() public FileRefEntity() {}
{
}
public FileRefEntity(String fileRef) public FileRefEntity(String fileRef)
{ {
this.fileRef = fileRef; this.fileRef = fileRef;
} }
public void setFileRef(String fileRef){ public void setFileRef(String fileRef)
{
this.fileRef = fileRef; this.fileRef = fileRef;
} }
public String getFileRef()
{
return fileRef;
}
@Override public String getFileRef()
public String toString()
{ {
return fileRef; return fileRef;
} }
@ -63,16 +57,10 @@ public class FileRefEntity
@Override @Override
public boolean equals(Object o) public boolean equals(Object o)
{ {
if (this == o) if (this == o) return true;
{ if (o == null || getClass() != o.getClass()) return false;
return true; FileRefEntity that = (FileRefEntity) o;
} return Objects.equals(fileRef, that.fileRef);
if (o == null || getClass() != o.getClass())
{
return false;
}
FileRefEntity fileRef = (FileRefEntity) o;
return Objects.equals(this.fileRef, fileRef.fileRef);
} }
@Override @Override
@ -80,4 +68,10 @@ public class FileRefEntity
{ {
return Objects.hash(fileRef); return Objects.hash(fileRef);
} }
@Override
public String toString()
{
return fileRef;
}
} }

View File

@ -35,9 +35,7 @@ public class FileRefResponse
{ {
private FileRefEntity entry; private FileRefEntity entry;
public FileRefResponse() public FileRefResponse() {}
{
}
public FileRefResponse(FileRefEntity entry) public FileRefResponse(FileRefEntity entry)
{ {

View File

@ -43,8 +43,8 @@ import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.alfresco.transformer.AbstractTransformerController;
import org.alfresco.transform.exceptions.TransformException; import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.AbstractTransformerController;
import org.alfresco.transformer.logging.LogEntry; import org.alfresco.transformer.logging.LogEntry;
import org.alfresco.util.TempFileProvider; import org.alfresco.util.TempFileProvider;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -65,11 +65,11 @@ import org.slf4j.LoggerFactory;
* code resulting in the k8s pod being terminated, after a predefined number of transformations have been performed or * code resulting in the k8s pod being terminated, after a predefined number of transformations have been performed or
* if any transformation takes a long time. These are controlled by environment variables:</p> * if any transformation takes a long time. These are controlled by environment variables:</p>
* <ul> * <ul>
* <li>livenessPercent - The percentage slower the small test transform must be to indicate there is a problem.</li> * <li>livenessPercent - The percentage slower the small test transform must be to indicate there is a problem.</li>
* <li>livenessTransformPeriodSeconds - As liveness probes should be frequent, not every request should result in * <li>livenessTransformPeriodSeconds - As liveness probes should be frequent, not every request should result in
* a test transformation. This value defines the gap between transformations.</li> * a test transformation. This value defines the gap between transformations.</li>
* <li>maxTransforms - the maximum number of transformation to be performed before a restart.</li> * <li>maxTransforms - the maximum number of transformation to be performed before a restart.</li>
* <li>maxTransformSeconds - the maximum time for a transformation, including failed ones.</li> * <li>maxTransformSeconds - the maximum time for a transformation, including failed ones.</li>
* </ul> * </ul>
*/ */
public abstract class ProbeTestTransform public abstract class ProbeTestTransform
@ -111,28 +111,30 @@ public abstract class ProbeTestTransform
/** /**
* See Probes.md for more info. * See Probes.md for more info.
* @param expectedLength was the length of the target file during testing *
* @param plusOrMinus simply allows for some variation in the transformed size caused by new versions of dates * @param expectedLength was the length of the target file during testing
* @param livenessPercent indicates that for this type of transform a variation up to 2 and a half times is not * @param plusOrMinus simply allows for some variation in the transformed size caused by new versions of dates
* unreasonable under load * @param livenessPercent indicates that for this type of transform a variation up to 2 and a half times is not
* @param maxTransforms default values normally supplied by helm. Not identical so we can be sure which value is used. * unreasonable under load
* @param maxTransformSeconds default values normally supplied by helm. Not identical so we can be sure which value is used. * @param maxTransforms default values normally supplied by helm. Not identical so we can be sure which value is used.
* @param livenessTransformPeriodSeconds default values normally supplied by helm. Not identical so we can be sure which value is used. * @param maxTransformSeconds default values normally supplied by helm. Not identical so we can be sure which value is used.
* @param livenessTransformPeriodSeconds default values normally supplied by helm. Not identical so we can be sure which value is used.
*/ */
public ProbeTestTransform(AbstractTransformerController controller, public ProbeTestTransform(AbstractTransformerController controller,
String sourceFilename, String targetFilename, long expectedLength, long plusOrMinus, String sourceFilename, String targetFilename, long expectedLength, long plusOrMinus,
int livenessPercent, long maxTransforms, long maxTransformSeconds, int livenessPercent, long maxTransforms, long maxTransformSeconds,
long livenessTransformPeriodSeconds) long livenessTransformPeriodSeconds)
{ {
this.sourceFilename = sourceFilename; this.sourceFilename = sourceFilename;
this.targetFilename = targetFilename; this.targetFilename = targetFilename;
minExpectedLength = Math.max(0, expectedLength-plusOrMinus); minExpectedLength = Math.max(0, expectedLength - plusOrMinus);
maxExpectedLength = expectedLength+plusOrMinus; maxExpectedLength = expectedLength + plusOrMinus;
this.livenessPercent = (int) getPositiveLongEnv("livenessPercent", livenessPercent); this.livenessPercent = (int) getPositiveLongEnv("livenessPercent", livenessPercent);
maxTransformCount = getPositiveLongEnv("maxTransforms", maxTransforms); maxTransformCount = getPositiveLongEnv("maxTransforms", maxTransforms);
maxTransformTime = getPositiveLongEnv("maxTransformSeconds", maxTransformSeconds)*1000; maxTransformTime = getPositiveLongEnv("maxTransformSeconds", maxTransformSeconds) * 1000;
livenessTransformPeriod = getPositiveLongEnv("livenessTransformPeriodSeconds", livenessTransformPeriodSeconds)*1000; livenessTransformPeriod = getPositiveLongEnv("livenessTransformPeriodSeconds",
livenessTransformPeriodSeconds) * 1000;
livenessTransformEnabled = getBooleanEnvVar("livenessTransformEnabled", false); livenessTransformEnabled = getBooleanEnvVar("livenessTransformEnabled", false);
} }
@ -182,9 +184,9 @@ public abstract class ProbeTestTransform
} }
return (isLiveProbe && livenessTransformPeriod > 0 && return (isLiveProbe && livenessTransformPeriod > 0 &&
(transCount <= AVERAGE_OVER_TRANSFORMS || nextTransformTime < System.currentTimeMillis())) (transCount <= AVERAGE_OVER_TRANSFORMS || nextTransformTime < System.currentTimeMillis()))
|| !initialised.get() || !initialised.get()
? doTransform(request, isLiveProbe) ? doTransform(request, isLiveProbe)
: doNothing(isLiveProbe); : doNothing(isLiveProbe);
} }
private String doNothing(boolean isLiveProbe) private String doNothing(boolean isLiveProbe)
@ -238,39 +240,39 @@ public abstract class ProbeTestTransform
checkMaxTransformTimeAndCount(isLiveProbe); checkMaxTransformTimeAndCount(isLiveProbe);
return getProbeMessage(isLiveProbe)+message; return getProbeMessage(isLiveProbe) + message;
} }
private void checkMaxTransformTimeAndCount(boolean isLiveProbe) private void checkMaxTransformTimeAndCount(boolean isLiveProbe)
{ {
if (die.get()) if (die.get())
{ {
throw new TransformException(TOO_MANY_REQUESTS.value(), getMessagePrefix(isLiveProbe) + throw new TransformException(TOO_MANY_REQUESTS.value(),
"Transformer requested to die. A transform took longer than "+ getMessagePrefix(isLiveProbe) + "Transformer requested to die. A transform took " +
(maxTransformTime *1000)+" seconds"); "longer than " + (maxTransformTime * 1000) + " seconds");
} }
if (maxTransformCount > 0 && transformCount.get() > maxTransformCount) if (maxTransformCount > 0 && transformCount.get() > maxTransformCount)
{ {
throw new TransformException(TOO_MANY_REQUESTS.value(), getMessagePrefix(isLiveProbe) + throw new TransformException(TOO_MANY_REQUESTS.value(),
"Transformer requested to die. It has performed more than "+ getMessagePrefix(isLiveProbe) + "Transformer requested to die. It has performed " +
maxTransformCount+" transformations"); "more than " + maxTransformCount + " transformations");
} }
} }
private File getSourceFile(HttpServletRequest request, boolean isLiveProbe) private File getSourceFile(HttpServletRequest request, boolean isLiveProbe)
{ {
incrementTransformerCount(); incrementTransformerCount();
File sourceFile = TempFileProvider.createTempFile("source_", "_"+ sourceFilename); File sourceFile = TempFileProvider.createTempFile("source_", "_" + sourceFilename);
request.setAttribute(SOURCE_FILE, sourceFile); request.setAttribute(SOURCE_FILE, sourceFile);
try (InputStream inputStream = this.getClass().getResourceAsStream('/'+sourceFilename)) try (InputStream inputStream = this.getClass().getResourceAsStream('/' + sourceFilename))
{ {
Files.copy(inputStream, sourceFile.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(inputStream, sourceFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} }
catch (IOException e) catch (IOException e)
{ {
throw new TransformException(INSUFFICIENT_STORAGE.value(), getMessagePrefix(isLiveProbe)+ throw new TransformException(INSUFFICIENT_STORAGE.value(),
"Failed to store the source file", e); getMessagePrefix(isLiveProbe) + "Failed to store the source file", e);
} }
long length = sourceFile.length(); long length = sourceFile.length();
LogEntry.setSource(sourceFilename, length); LogEntry.setSource(sourceFilename, length);
@ -279,7 +281,7 @@ public abstract class ProbeTestTransform
private File getTargetFile(HttpServletRequest request) private File getTargetFile(HttpServletRequest request)
{ {
File targetFile = TempFileProvider.createTempFile("target_", "_"+targetFilename); File targetFile = TempFileProvider.createTempFile("target_", "_" + targetFilename);
request.setAttribute(TARGET_FILE, targetFile); request.setAttribute(TARGET_FILE, targetFile);
LogEntry.setTarget(targetFilename); LogEntry.setTarget(targetFilename);
return targetFile; return targetFile;
@ -302,13 +304,15 @@ public abstract class ProbeTestTransform
String message = getMessagePrefix(isLiveProbe) + "Success - Transform " + time + "ms"; String message = getMessagePrefix(isLiveProbe) + "Success - Transform " + time + "ms";
if (++transCount > 1) if (++transCount > 1)
{ {
normalTime = (normalTime * (transCount-2) + time) / (transCount-1); normalTime = (normalTime * (transCount - 2) + time) / (transCount - 1);
maxTime = (normalTime * (livenessPercent + 100)) / 100; maxTime = (normalTime * (livenessPercent + 100)) / 100;
if ((!isLiveProbe && !readySent.getAndSet(true)) || transCount > AVERAGE_OVER_TRANSFORMS) if ((!isLiveProbe && !readySent.getAndSet(
true)) || transCount > AVERAGE_OVER_TRANSFORMS)
{ {
nextTransformTime = System.currentTimeMillis() + livenessTransformPeriod; nextTransformTime = System.currentTimeMillis() + livenessTransformPeriod;
logger.trace("{} - {}ms+{}%={}ms", message, normalTime, livenessPercent, maxTime); logger.trace("{} - {}ms+{}%={}ms", message, normalTime, livenessPercent,
maxTime);
} }
} }
else if (!isLiveProbe && !readySent.getAndSet(true)) else if (!isLiveProbe && !readySent.getAndSet(true))
@ -362,7 +366,6 @@ public abstract class ProbeTestTransform
public long getNormalTime() public long getNormalTime()
{ {
return normalTime; return normalTime;
} }
} }

View File

@ -27,6 +27,7 @@
package org.alfresco.transformer.util; package org.alfresco.transformer.util;
/** /**
*
*/ */
public class Util public class Util
{ {
@ -49,6 +50,6 @@ public class Util
*/ */
public static Boolean stringToBoolean(String param) public static Boolean stringToBoolean(String param)
{ {
return param == null? null : Boolean.parseBoolean(param); return param == null ? null : Boolean.parseBoolean(param);
} }
} }

View File

@ -26,16 +26,20 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static org.junit.Assert.assertEquals;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*; import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import static org.junit.Assert.assertEquals;
import static org.springframework.test.util.AssertionErrors.assertTrue;
/** /**
* Super class for testing controllers with a server. Includes tests for the AbstractTransformerController itself. * Super class for testing controllers with a server. Includes tests for the AbstractTransformerController itself.
* Note: Currently uses json rather than HTML as json is returned by this spring boot test harness. * Note: Currently uses json rather than HTML as json is returned by this spring boot test harness.
@ -64,7 +68,8 @@ public abstract class AbstractHttpRequestTest
@Test @Test
public void logPageExists() public void logPageExists()
{ {
String result = restTemplate.getForObject("http://localhost:" + port + "/log", String.class); String result = restTemplate.getForObject("http://localhost:" + port + "/log",
String.class);
String title = getTransformerName() + ' ' + "Log"; String title = getTransformerName() + ' ' + "Log";
assertTrue("\"" + title + "\" should be part of the page title", result.contains(title)); assertTrue("\"" + title + "\" should be part of the page title", result.contains(title));
@ -73,10 +78,12 @@ public abstract class AbstractHttpRequestTest
@Test @Test
public void errorPageExists() public void errorPageExists()
{ {
String result = restTemplate.getForObject("http://localhost:" + port + "/error", String.class); String result = restTemplate.getForObject("http://localhost:" + port + "/error",
String.class);
String title = getTransformerName() + ' ' + "Error Page"; String title = getTransformerName() + ' ' + "Error Page";
assertTrue("\"" + title + "\" should be part of the page title", result.contains("Error Page")); assertTrue("\"" + title + "\" should be part of the page title",
result.contains("Error Page"));
} }
@Test @Test
@ -84,7 +91,7 @@ public abstract class AbstractHttpRequestTest
{ {
// Transformer name is not part of the title as this is checked by another handler // Transformer name is not part of the title as this is checked by another handler
assertTransformError(false, assertTransformError(false,
"Required request part 'file' is not present"); "Required request part 'file' is not present");
} }
@Test @Test
@ -96,7 +103,7 @@ public abstract class AbstractHttpRequestTest
private void assertMissingParameter(String name) private void assertMissingParameter(String name)
{ {
assertTransformError(true, assertTransformError(true,
getTransformerName() + " - Request parameter '" + name + "' is missing"); getTransformerName() + " - Request parameter '" + name + "' is missing");
} }
private void assertTransformError(boolean addFile, String errorMessage) private void assertTransformError(boolean addFile, String errorMessage)
@ -104,12 +111,15 @@ public abstract class AbstractHttpRequestTest
LinkedMultiValueMap<String, Object> parameters = new LinkedMultiValueMap<>(); LinkedMultiValueMap<String, Object> parameters = new LinkedMultiValueMap<>();
if (addFile) if (addFile)
{ {
parameters.add("file", new org.springframework.core.io.ClassPathResource("quick."+getSourceExtension())); parameters.add("file",
new org.springframework.core.io.ClassPathResource("quick." + getSourceExtension()));
} }
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA); headers.setContentType(MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<>(parameters, headers); HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<>(parameters,
ResponseEntity<String> response = restTemplate.exchange("/transform", HttpMethod.POST, entity, String.class, ""); headers);
ResponseEntity<String> response = restTemplate.exchange("/transform", POST, entity,
String.class, "");
assertEquals(errorMessage, getErrorMessage(response.getBody())); assertEquals(errorMessage, getErrorMessage(response.getBody()));
} }
@ -125,7 +135,7 @@ public abstract class AbstractHttpRequestTest
int j = content.indexOf("\",\"path\":", i); int j = content.indexOf("\",\"path\":", i);
if (j != -1) if (j != -1)
{ {
message = content.substring(i+11, j); message = content.substring(i + 11, j);
} }
} }
return message; return message;

View File

@ -54,11 +54,12 @@ public abstract class AbstractQueueTransformServiceIT
@Autowired @Autowired
private JmsTemplate jmsTemplate; private JmsTemplate jmsTemplate;
private final ActiveMQQueue testingQueue = new ActiveMQQueue("org.alfresco.transform.engine.IT"); private final ActiveMQQueue testingQueue = new ActiveMQQueue(
"org.alfresco.transform.engine.IT");
@Test @Test
public void queueTransformServiceIT() { public void queueTransformServiceIT()
{
TransformRequest request = buildRequest(); TransformRequest request = buildRequest();
jmsTemplate.convertAndSend(engineRequestQueue, request, m -> { jmsTemplate.convertAndSend(engineRequestQueue, request, m -> {

View File

@ -30,9 +30,13 @@ import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.springframework.http.HttpHeaders.ACCEPT;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -62,8 +66,6 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
@ -107,7 +109,8 @@ public abstract class AbstractTransformerControllerTest
protected abstract AbstractTransformerController getController(); protected abstract AbstractTransformerController getController();
protected abstract void updateTransformRequestWithSpecificOptions(TransformRequest transformRequest); protected abstract void updateTransformRequestWithSpecificOptions(
TransformRequest transformRequest);
/** /**
* This method ends up being the core of the mock. * This method ends up being the core of the mock.
@ -151,14 +154,17 @@ public abstract class AbstractTransformerControllerTest
URL testFileUrl = classLoader.getResource(testFilename); URL testFileUrl = classLoader.getResource(testFilename);
if (required && testFileUrl == null) if (required && testFileUrl == null)
{ {
throw new IOException("The test file " + testFilename + " does not exist in the resources directory"); throw new IOException("The test file " + testFilename +
" does not exist in the resources directory");
} }
return testFileUrl == null ? null : new File(testFileUrl.getFile()); return testFileUrl == null ? null : new File(testFileUrl.getFile());
} }
protected MockHttpServletRequestBuilder mockMvcRequest(String url, MockMultipartFile sourceFile, String... params) protected MockHttpServletRequestBuilder mockMvcRequest(String url, MockMultipartFile sourceFile,
String... params)
{ {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart("/transform").file(sourceFile); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart("/transform").file(
sourceFile);
if (params.length % 2 != 0) if (params.length % 2 != 0)
{ {
@ -175,20 +181,24 @@ public abstract class AbstractTransformerControllerTest
@Test @Test
public void simpleTransformTest() throws Exception public void simpleTransformTest() throws Exception
{ {
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension)) mockMvc.perform(
mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick." + targetExtension)); .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
public void testDelayTest() throws Exception public void testDelayTest() throws Exception
{ {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension, "testDelay", "400")) mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension,
"testDelay", "400"))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick." + targetExtension)); .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
long ms = System.currentTimeMillis() - start; long ms = System.currentTimeMillis() - start;
System.out.println("Transform incluing test delay was " + ms); System.out.println("Transform incluing test delay was " + ms);
assertTrue("Delay sending the result back was too small " + ms, ms >= 400); assertTrue("Delay sending the result back was too small " + ms, ms >= 400);
@ -206,24 +216,30 @@ public abstract class AbstractTransformerControllerTest
// Looks dangerous but is okay as we only use the final filename // Looks dangerous but is okay as we only use the final filename
public void dotDotSourceFilenameTest() throws Exception public void dotDotSourceFilenameTest() throws Exception
{ {
sourceFile = new MockMultipartFile("file", "../quick." + sourceExtension, sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "../quick." + sourceExtension, sourceMimetype,
expectedSourceFileBytes);
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension)) mockMvc.perform(
mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick." + targetExtension)); .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
// Is okay, as the target filename is built up from the whole source filename and the targetExtenstion // Is okay, as the target filename is built up from the whole source filename and the targetExtenstion
public void noExtensionSourceFilenameTest() throws Exception public void noExtensionSourceFilenameTest() throws Exception
{ {
sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype,
expectedSourceFileBytes);
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension)) mockMvc.perform(
mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick." + targetExtension)); .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
@ -232,7 +248,8 @@ public abstract class AbstractTransformerControllerTest
{ {
sourceFile = new MockMultipartFile("file", "abc/", sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "abc/", sourceMimetype, expectedSourceFileBytes);
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension)) mockMvc.perform(
mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason(containsString("The source filename was not supplied"))); .andExpect(status().reason(containsString("The source filename was not supplied")));
} }
@ -242,7 +259,8 @@ public abstract class AbstractTransformerControllerTest
{ {
sourceFile = new MockMultipartFile("file", "", sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "", sourceMimetype, expectedSourceFileBytes);
mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension)) mockMvc.perform(
mockMvcRequest("/transform", sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason(containsString("The source filename was not supplied"))); .andExpect(status().reason(containsString("The source filename was not supplied")));
} }
@ -252,7 +270,8 @@ public abstract class AbstractTransformerControllerTest
{ {
mockMvc.perform(mockMvcRequest("/transform", sourceFile)) mockMvc.perform(mockMvcRequest("/transform", sourceFile))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason(containsString("Request parameter 'targetExtension' is missing"))); .andExpect(status().reason(
containsString("Request parameter 'targetExtension' is missing")));
} }
@Test @Test
@ -295,13 +314,14 @@ public abstract class AbstractTransformerControllerTest
String transformationReplyAsString = mockMvc String transformationReplyAsString = mockMvc
.perform(MockMvcRequestBuilders .perform(MockMvcRequestBuilders
.post("/transform") .post("/transform")
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .header(ACCEPT, APPLICATION_JSON_VALUE)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.content(tr)) .content(tr))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformReply transformReply = objectMapper.readValue(transformationReplyAsString, TransformReply.class); TransformReply transformReply = objectMapper.readValue(transformationReplyAsString,
TransformReply.class);
// Assert the reply // Assert the reply
assertEquals(BAD_REQUEST.value(), transformReply.getStatus()); assertEquals(BAD_REQUEST.value(), transformReply.getStatus());
@ -317,9 +337,10 @@ public abstract class AbstractTransformerControllerTest
ReflectionTestUtils ReflectionTestUtils
.setField(AbstractTransformerController.class, "ENGINE_CONFIG", "engine_config.json"); .setField(AbstractTransformerController.class, "ENGINE_CONFIG", "engine_config.json");
String response = mockMvc.perform(MockMvcRequestBuilders.get("/transform/config")) String response = mockMvc
.andExpect(status().is(OK.value())).andExpect( .perform(MockMvcRequestBuilders.get("/transform/config"))
header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
@ -335,9 +356,10 @@ public abstract class AbstractTransformerControllerTest
ReflectionTestUtils.setField(AbstractTransformerController.class, "ENGINE_CONFIG", ReflectionTestUtils.setField(AbstractTransformerController.class, "ENGINE_CONFIG",
"engine_config_with_duplicates.json"); "engine_config_with_duplicates.json");
String response = mockMvc.perform(MockMvcRequestBuilders.get("/transform/config")) String response = mockMvc
.andExpect(status().is(OK.value())).andExpect( .perform(MockMvcRequestBuilders.get("/transform/config"))
header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
@ -349,7 +371,6 @@ public abstract class AbstractTransformerControllerTest
transformConfig.getTransformers().get(0).getSupportedSourceAndTargetList().size()); transformConfig.getTransformers().get(0).getSupportedSourceAndTargetList().size());
assertEquals(1, assertEquals(1,
transformConfig.getTransformers().get(0).getTransformOptions().size()); transformConfig.getTransformers().get(0).getTransformOptions().size());
} }
@Test @Test
@ -362,9 +383,10 @@ public abstract class AbstractTransformerControllerTest
ReflectionTestUtils.setField(AbstractTransformerController.class, "ENGINE_CONFIG", ReflectionTestUtils.setField(AbstractTransformerController.class, "ENGINE_CONFIG",
"engine_config_incomplete.json"); "engine_config_incomplete.json");
String response = mockMvc.perform(MockMvcRequestBuilders.get("/transform/config")) String response = mockMvc
.andExpect(status().is(OK.value())).andExpect( .perform(MockMvcRequestBuilders.get("/transform/config"))
header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
@ -384,9 +406,10 @@ public abstract class AbstractTransformerControllerTest
ReflectionTestUtils.setField(AbstractTransformerController.class, "ENGINE_CONFIG", ReflectionTestUtils.setField(AbstractTransformerController.class, "ENGINE_CONFIG",
"engine_config_no_transform_options.json"); "engine_config_no_transform_options.json");
String response = mockMvc.perform(MockMvcRequestBuilders.get("/transform/config")) String response = mockMvc
.andExpect(status().is(OK.value())).andExpect( .perform(MockMvcRequestBuilders.get("/transform/config"))
header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
@ -405,7 +428,8 @@ public abstract class AbstractTransformerControllerTest
new TransformOptionValue(false, "page"), new TransformOptionValue(false, "page"),
new TransformOptionValue(false, "width"), new TransformOptionValue(false, "width"),
new TransformOptionGroup(false, transformOptionGroup)); new TransformOptionGroup(false, transformOptionGroup));
Map<String, Set<TransformOption>> transformOptionsMap = ImmutableMap.of("engineXOptions", transformOptions); Map<String, Set<TransformOption>> transformOptionsMap = ImmutableMap.of("engineXOptions",
transformOptions);
Transformer transformer = buildTransformer("application/pdf", "image/png", "engineXOptions", Transformer transformer = buildTransformer("application/pdf", "image/png", "engineXOptions",
"engineX"); "engineX");

View File

@ -27,11 +27,18 @@
package org.alfresco.transformer; package org.alfresco.transformer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import org.alfresco.transform.client.model.TransformReply; import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest; import org.alfresco.transform.client.model.TransformRequest;
@ -48,10 +55,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConversionException;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
public class QueueTransformServiceTest public class QueueTransformServiceTest
{ {
@Mock @Mock
@ -98,10 +101,13 @@ public class QueueTransformServiceTest
ActiveMQQueue destination = new ActiveMQQueue(); ActiveMQQueue destination = new ActiveMQQueue();
msg.setJMSReplyTo(destination); msg.setJMSReplyTo(destination);
TransformReply reply = TransformReply.builder() TransformReply reply = TransformReply
.withStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()).withErrorDetails( .builder()
.withStatus(INTERNAL_SERVER_ERROR.value())
.withErrorDetails(
"JMS exception during T-Request deserialization of message with correlationID " "JMS exception during T-Request deserialization of message with correlationID "
+ msg.getCorrelationId() + ": null").build(); + msg.getCorrelationId() + ": null")
.build();
doReturn(null).when(transformMessageConverter).fromMessage(msg); doReturn(null).when(transformMessageConverter).fromMessage(msg);
@ -122,10 +128,13 @@ public class QueueTransformServiceTest
ActiveMQQueue destination = new ActiveMQQueue(); ActiveMQQueue destination = new ActiveMQQueue();
msg.setJMSReplyTo(destination); msg.setJMSReplyTo(destination);
TransformReply reply = TransformReply.builder().withStatus(HttpStatus.BAD_REQUEST.value()) TransformReply reply = TransformReply
.builder()
.withStatus(BAD_REQUEST.value())
.withErrorDetails( .withErrorDetails(
"Message conversion exception during T-Request deserialization of message with correlationID" "Message conversion exception during T-Request deserialization of message with correlationID"
+ msg.getCorrelationId() + ": null").build(); + msg.getCorrelationId() + ": null")
.build();
doThrow(MessageConversionException.class).when(transformMessageConverter).fromMessage(msg); doThrow(MessageConversionException.class).when(transformMessageConverter).fromMessage(msg);
@ -146,10 +155,13 @@ public class QueueTransformServiceTest
ActiveMQQueue destination = new ActiveMQQueue(); ActiveMQQueue destination = new ActiveMQQueue();
msg.setJMSReplyTo(destination); msg.setJMSReplyTo(destination);
TransformReply reply = TransformReply.builder() TransformReply reply = TransformReply
.withStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()).withErrorDetails( .builder()
"JMSException during T-Request deserialization of message with correlationID " + msg .withStatus(INTERNAL_SERVER_ERROR.value())
.getCorrelationId() + ": null").build(); .withErrorDetails(
"JMSException during T-Request deserialization of message with correlationID " +
msg.getCorrelationId() + ": null")
.build();
doThrow(JMSException.class).when(transformMessageConverter).fromMessage(msg); doThrow(JMSException.class).when(transformMessageConverter).fromMessage(msg);
@ -169,7 +181,9 @@ public class QueueTransformServiceTest
msg.setJMSReplyTo(destination); msg.setJMSReplyTo(destination);
TransformRequest request = new TransformRequest(); TransformRequest request = new TransformRequest();
TransformReply reply = TransformReply.builder().withStatus(HttpStatus.CREATED.value()) TransformReply reply = TransformReply
.builder()
.withStatus(CREATED.value())
.build(); .build();
doReturn(request).when(transformMessageConverter).fromMessage(msg); doReturn(request).when(transformMessageConverter).fromMessage(msg);
@ -208,7 +222,9 @@ public class QueueTransformServiceTest
doReturn(destination).when(msg).getJMSReplyTo(); doReturn(destination).when(msg).getJMSReplyTo();
TransformRequest request = new TransformRequest(); TransformRequest request = new TransformRequest();
TransformReply reply = TransformReply.builder().withStatus(HttpStatus.CREATED.value()) TransformReply reply = TransformReply
.builder()
.withStatus(CREATED.value())
.build(); .build();
doReturn(request).when(transformMessageConverter).fromMessage(msg); doReturn(request).when(transformMessageConverter).fromMessage(msg);

15
pom.xml
View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version> <version>2.1.7.RELEASE</version>
<relativePath /> <relativePath />
</parent> </parent>
@ -14,9 +14,11 @@
<version>2.1.0-EA5-SNAPSHOT</version> <version>2.1.0-EA5-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>
<java.version>1.8</java.version> <java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<image.tag>latest</image.tag> <image.tag>latest</image.tag>
<dependency.pdfbox.version>2.0.16</dependency.pdfbox.version> <dependency.pdfbox.version>2.0.16</dependency.pdfbox.version>
<dependency.alfresco-core.version>7.17</dependency.alfresco-core.version> <dependency.alfresco-core.version>7.17</dependency.alfresco-core.version>
@ -26,6 +28,7 @@
<dependency.alfresco-transform-model.version>1.0.2.3</dependency.alfresco-transform-model.version> <dependency.alfresco-transform-model.version>1.0.2.3</dependency.alfresco-transform-model.version>
<dependency.activemq.version>5.15.9</dependency.activemq.version> <dependency.activemq.version>5.15.9</dependency.activemq.version>
<dependency.jackson.version>2.9.9</dependency.jackson.version> <dependency.jackson.version>2.9.9</dependency.jackson.version>
<dependency.jackson-databind.version>2.9.9.2</dependency.jackson-databind.version>
<dependency.cxf.version>3.2.9</dependency.cxf.version> <dependency.cxf.version>3.2.9</dependency.cxf.version>
</properties> </properties>
@ -46,7 +49,6 @@
</scm> </scm>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
@ -68,7 +70,6 @@
<artifactId>dom4j</artifactId> <artifactId>dom4j</artifactId>
<version>2.1.1</version> <version>2.1.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-data-model</artifactId> <artifactId>alfresco-data-model</artifactId>
@ -129,7 +130,7 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>${dependency.jackson.version}</version> <version>${dependency.jackson-databind.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
@ -307,7 +308,7 @@
<plugin> <plugin>
<groupId>io.fabric8</groupId> <groupId>io.fabric8</groupId>
<artifactId>fabric8-maven-plugin</artifactId> <artifactId>fabric8-maven-plugin</artifactId>
<version>4.1.0</version> <version>4.2.0</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>io.fabric8</groupId> <groupId>io.fabric8</groupId>