ATS-175 : T-Engine code cleanup

This commit is contained in:
Cezar.Leahu
2018-10-25 18:21:50 +03:00
parent ba8707c762
commit d85c03d362
42 changed files with 2042 additions and 1475 deletions

View File

@@ -12,6 +12,8 @@
package org.alfresco.transformer;
import io.micrometer.core.instrument.MeterRegistry;
import org.alfresco.transformer.executors.LibreOfficeJavaExecutor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
@@ -27,9 +29,16 @@ public class Application
@Value("${container.name}")
private String containerName;
@Bean MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags("containerName", containerName);
}
@Bean
public LibreOfficeJavaExecutor javaExecutor()
{
return new LibreOfficeJavaExecutor();
}
public static void main(String[] args)
{

View File

@@ -11,19 +11,23 @@
*/
package org.alfresco.transformer;
import static org.alfresco.transformer.fs.FileManager.createAttachment;
import static org.alfresco.transformer.fs.FileManager.createSourceFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFileName;
import static org.alfresco.transformer.logging.StandardMessages.ENTERPRISE_LICENCE;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.alfresco.transformer.executors.LibreOfficeJavaExecutor;
import org.alfresco.transformer.logging.LogEntry;
import org.alfresco.transformer.probes.ProbeTestTransform;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.OfficeException;
import org.artofsolving.jodconverter.office.OfficeManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
@@ -33,8 +37,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import com.sun.star.task.ErrorCodeIOException;
/**
* Controller for the Docker based LibreOffice transformer.
*
@@ -59,86 +61,48 @@ import com.sun.star.task.ErrorCodeIOException;
@Controller
public class LibreOfficeController extends AbstractTransformerController
{
private static final String OFFICE_HOME = "/opt/libreoffice5.4";
private static final int JODCONVERTER_TRANSFORMATION_ERROR_CODE = 3088;
private JodConverter jodconverter;
private static final Log logger = LogFactory.getLog(LibreOfficeController.class);
@Autowired
public LibreOfficeController() throws Exception
private LibreOfficeJavaExecutor javaExecutor;
@Autowired
public LibreOfficeController()
{
logger = LogFactory.getLog(LibreOfficeController.class);
logger.info("-------------------------------------------------------------------------------------------------------------------------------------------------------");
logEnterpriseLicenseMessage();
Arrays.stream(ENTERPRISE_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("-------------------------------------------------------------------------------------------------------------------------------------------------------");
}
private static JodConverter createJodConverter(Long taskExecutionTimeout)
{
String timeout = taskExecutionTimeout == null || taskExecutionTimeout <= 0 ? "120000" : taskExecutionTimeout.toString();
JodConverterSharedInstance jodconverter = new JodConverterSharedInstance();
jodconverter.setOfficeHome(OFFICE_HOME); // jodconverter.officeHome
jodconverter.setMaxTasksPerProcess("200"); // jodconverter.maxTasksPerProcess
jodconverter.setTaskExecutionTimeout(timeout); // jodconverter.maxTaskExecutionTimeout
jodconverter.setTaskQueueTimeout("30000"); // jodconverter.taskQueueTimeout
jodconverter.setConnectTimeout("28000"); // jodconverter.connectTimeout
jodconverter.setPortNumbers("8100"); // jodconverter.portNumbers
jodconverter.setTemplateProfileDir(""); // jodconverter.templateProfileDir
jodconverter.setEnabled("true"); // jodconverter.enabled
jodconverter.afterPropertiesSet();
return jodconverter;
}
public void setJodConverter(JodConverter jodconverter)
{
this.jodconverter = jodconverter;
}
/**
* Jodconverter timeouts are per OfficeManager, so we would need multiple OfficeManagers if we
* have different timeouts. Alfresco only has one. So we delay building it until the first request.
* This was not done previously.
*/
private synchronized void setJodConverterOnFirstRequest(Long timeout)
{
if (jodconverter == null)
{
setJodConverter(createJodConverter(timeout));
}
}
@Override
protected String getTransformerName()
public String getTransformerName()
{
return "LibreOffice";
}
@Override
protected String version()
public String version()
{
return "LibreOffice available";
}
@Override
protected ProbeTestTransform getProbeTestTransform()
public ProbeTestTransform getProbeTestTransform()
{
// 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, logger, "quick.doc", "quick.pdf",
11817, 1024, 150, 10240, 60*30+1, 60*15+20)
{
@Override
protected void executeTransformCommand(File sourceFile, File targetFile)
{
LibreOfficeController.this.executeTransformCommand(sourceFile, targetFile, null);
javaExecutor.call(sourceFile, targetFile);
}
};
}
//todo: the "timeout" request parameter is ignored; the timeout is preset at JodConverter creation
@PostMapping(value = "/transform", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam("file") MultipartFile sourceMultipartFile,
@@ -147,122 +111,25 @@ public class LibreOfficeController extends AbstractTransformerController
@RequestParam(value = "testDelay", required = false) Long testDelay)
{
String targetFilename = createTargetFileName(sourceMultipartFile.getOriginalFilename(), targetExtension);
getProbeTestTransform().incrementTransformerCount();
File sourceFile = createSourceFile(request, sourceMultipartFile);
File targetFile = createTargetFile(request, targetFilename);
// Both files are deleted by TransformInterceptor.afterCompletion
executeTransformCommand(sourceFile, targetFile, timeout);
javaExecutor.call(sourceFile, targetFile);
return createAttachment(targetFilename, targetFile, testDelay);
final ResponseEntity<Resource> body = createAttachment(targetFilename, targetFile);
LogEntry.setTargetSize(targetFile.length());
long time = LogEntry.setStatusCodeAndMessage(200, "Success");
time += LogEntry.addDelay(testDelay);
getProbeTestTransform().recordTransformTime(time);
return body;
}
@Override
protected void processTransform(File sourceFile, File targetFile,
public void processTransform(File sourceFile, File targetFile,
Map<String, String> transformOptions, Long timeout)
{
executeTransformCommand(sourceFile, targetFile, timeout);
}
protected void executeTransformCommand(File sourceFile, File targetFile, Long timeout)
{
timeout = timeout != null && timeout > 0 ? timeout : 0;
try
{
convert(sourceFile, targetFile, timeout);
}
catch (OfficeException e)
{
throw new TransformException(400, "LibreOffice server conversion failed: \n"+
" from file: " + sourceFile + "\n" +
" to file: " + targetFile,
e);
}
catch (Throwable throwable)
{
// Because of the known bug with empty Spreadsheets in JodConverter try to catch exception and produce empty pdf file
if (throwable.getCause() instanceof ErrorCodeIOException &&
((ErrorCodeIOException) throwable.getCause()).ErrCode == JODCONVERTER_TRANSFORMATION_ERROR_CODE)
{
logger.warn("Transformation failed: \n" +
"from file: " + sourceFile + "\n" +
"to file: " + targetFile +
"Source file " + sourceFile + " has no content");
produceEmptyPdfFile(targetFile);
}
else
{
throw throwable;
}
}
if (!targetFile.exists() || targetFile.length() == 0L)
{
throw new TransformException(500, "Transformer failed to create an output file");
}
}
void convert(File sourceFile, File targetFile, long timeout)
{
setJodConverterOnFirstRequest(timeout);
OfficeManager officeManager = jodconverter.getOfficeManager();
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
converter.convert(sourceFile, targetFile);
}
/**
* This method produces an empty PDF file at the specified File location.
* Apache's PDFBox is used to create the PDF file.
*/
private void produceEmptyPdfFile(File targetFile)
{
// If improvement PDFBOX-914 is incorporated, we can do this with a straight call to
// org.apache.pdfbox.TextToPdf.createPDFFromText(new StringReader(""));
// https://issues.apache.org/jira/browse/PDFBOX-914
PDDocument pdfDoc = null;
PDPageContentStream contentStream = null;
try
{
pdfDoc = new PDDocument();
PDPage pdfPage = new PDPage();
// Even though, we want an empty PDF, some libs (e.g. PDFRenderer) object to PDFs
// that have literally nothing in them. So we'll put a content stream in it.
contentStream = new PDPageContentStream(pdfDoc, pdfPage);
pdfDoc.addPage(pdfPage);
// Now write the in-memory PDF document into the temporary file.
pdfDoc.save(targetFile.getAbsolutePath());
}
catch (IOException iox)
{
throw new TransformException(500, "Error creating empty PDF file", iox);
}
finally
{
if (contentStream != null)
{
try
{
contentStream.close();
}
catch (IOException ignored)
{
// Intentionally empty
}
}
if (pdfDoc != null)
{
try
{
pdfDoc.close();
}
catch (IOException ignored)
{
// Intentionally empty.
}
}
}
javaExecutor.call(sourceFile, targetFile);
}
}

View File

@@ -9,11 +9,11 @@
* agreement is prohibited.
* #L%
*/
package org.alfresco.transformer;
package org.alfresco.transformer.executors;
import org.artofsolving.jodconverter.office.OfficeManager;
///////// THIS FILE IS A COPY OF THE CODE IN alfresco-repository /////////////
///////// THIS FILE WAS A COPY OF THE CODE IN alfresco-repository /////////////
public interface JodConverter
{
@@ -21,11 +21,11 @@ public interface JodConverter
* Gets the JodConverter OfficeManager.
* @return
*/
public abstract OfficeManager getOfficeManager();
OfficeManager getOfficeManager();
/**
* This method returns a boolean indicating whether the JodConverter connection to OOo is available.
* @return <code>true</code> if available, else <code>false</code>
*/
public abstract boolean isAvailable();
boolean isAvailable();
}

View File

@@ -23,12 +23,11 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.transformer;
package org.alfresco.transformer.executors;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
@@ -41,7 +40,7 @@ import org.artofsolving.jodconverter.office.OfficeManager;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
///////// THIS FILE IS A COPY OF THE CODE IN alfresco-repository /////////////
///////// THIS FILE WAS A COPY OF THE CODE IN alfresco-repository /////////////
/**
* Makes use of the JodConverter library and an installed
@@ -51,10 +50,10 @@ import org.springframework.beans.factory.InitializingBean;
*/
public class JodConverterSharedInstance implements InitializingBean, DisposableBean, JodConverter
{
private static Log logger = LogFactory.getLog(JodConverterSharedInstance.class);
private static final Log logger = LogFactory.getLog(JodConverterSharedInstance.class);
private OfficeManager officeManager;
boolean isAvailable = false;
private boolean isAvailable = false;
// JodConverter's built-in configuration settings.
//
@@ -82,7 +81,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
private Boolean deprecatedOooEnabled;
private int[] deprecatedOooPortNumbers;
public void setMaxTasksPerProcess(String maxTasksPerProcess)
void setMaxTasksPerProcess(String maxTasksPerProcess)
{
Long l = parseStringForLong(maxTasksPerProcess.trim());
if (l != null)
@@ -96,7 +95,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
this.url = url;
}
public void setOfficeHome(String officeHome)
void setOfficeHome(String officeHome)
{
this.officeHome = officeHome == null ? "" : officeHome.trim();
}
@@ -106,7 +105,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
this.deprecatedOooExe = deprecatedOooExe == null ? "" : deprecatedOooExe.trim();
}
public void setPortNumbers(String s)
void setPortNumbers(String s)
{
portNumbers = parsePortNumbers(s, "jodconverter");
}
@@ -147,12 +146,12 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
return portNumbers;
}
public void setTaskExecutionTimeout(String taskExecutionTimeout)
void setTaskExecutionTimeout(String taskExecutionTimeout)
{
this.taskExecutionTimeout = parseStringForLong(taskExecutionTimeout.trim());
}
public void setTemplateProfileDir(String templateProfileDir)
void setTemplateProfileDir(String templateProfileDir)
{
if (templateProfileDir == null || templateProfileDir.trim().length() == 0)
{
@@ -169,26 +168,26 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
}
}
public void setTaskQueueTimeout(String taskQueueTimeout)
void setTaskQueueTimeout(String taskQueueTimeout)
{
this.taskQueueTimeout = parseStringForLong(taskQueueTimeout.trim());
}
public void setConnectTimeout(String connectTimeout)
void setConnectTimeout(String connectTimeout)
{
this.connectTimeout = parseStringForLong(connectTimeout.trim());
}
public void setEnabled(String enabled)
void setEnabled(final String enabledStr)
{
this.enabled = parseEnabled(enabled);
enabled = parseEnabled(enabledStr);
// If this is a request from the Enterprise Admin console to disable the JodConverter.
if (this.enabled == false && (deprecatedOooEnabled == null || deprecatedOooEnabled == false))
if (!enabled && (deprecatedOooEnabled == null || !deprecatedOooEnabled))
{
// We need to change isAvailable to false so we don't make calls to a previously started OfficeManger.
// In the case of Enterprise it is very unlikely that ooo.enabled will have been set to true.
this.isAvailable = false;
isAvailable = false;
}
}
@@ -207,7 +206,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
// So that Community systems <= Alfresco 6.0.1-ea keep working on upgrade, we may need to use the deprecated
// ooo.exe setting rather than the jodconverter.officeHome setting if we don't have the jod setting as
// oooDirect was replaced by jodconverter after this release.
String getOfficeHome()
private String getOfficeHome()
{
String officeHome = this.officeHome;
if ((officeHome == null || officeHome.isEmpty()) && (deprecatedOooExe != null && !deprecatedOooExe.isEmpty()))
@@ -243,7 +242,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
// Community set properties via alfresco-global.properties.
// Enterprise may do the same but may also reset jodconverter.enabled them via the Admin console.
// In the case of Enterprise it is very unlikely that ooo.enabled will be set to true.
boolean isEnabled()
private boolean isEnabled()
{
return (deprecatedOooEnabled != null && deprecatedOooEnabled) || (enabled != null && enabled);
}
@@ -251,7 +250,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
// So that Community systems <= Alfresco 6.0.1-ea keep working on upgrade, we may need to use the deprecated
// ooo.port setting rather than the jodconverter.portNumbers if ooo.enabled is true and jodconverter.enabled
// is false.
int[] getPortNumbers()
private int[] getPortNumbers()
{
return (enabled == null || !enabled) && deprecatedOooEnabled != null && deprecatedOooEnabled
? deprecatedOooPortNumbers
@@ -260,11 +259,9 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
private Long parseStringForLong(String string)
{
Long result = null;
try
{
long l = Long.parseLong(string);
result = new Long(l);
return Long.parseLong(string);
}
catch (NumberFormatException nfe)
{
@@ -272,9 +269,8 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
{
logger.debug("Cannot parse numerical value from " + string);
}
// else intentionally empty
}
return result;
return null;
}
/*
@@ -283,14 +279,14 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
*/
public boolean isAvailable()
{
final boolean result = isAvailable && (officeManager != null || (url != null && !url.isEmpty()));
return result;
return isAvailable && (officeManager != null || (url != null && !url.isEmpty()));
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet()
{
// isAvailable defaults to false afterPropertiesSet. It only becomes true on successful completion of this method.
@@ -318,7 +314,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
}
// Only start the JodConverter instance(s) if the subsystem is enabled.
if (isEnabled() == false)
if (!isEnabled())
{
return;
}
@@ -418,7 +414,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
private void logAllSofficeFilesUnderOfficeHome()
{
if (logger.isDebugEnabled() == false)
if (!logger.isDebugEnabled())
{
return;
}
@@ -430,7 +426,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
logFileInfo(requestedOfficeHome);
for (File f : findSofficePrograms(requestedOfficeHome, new ArrayList<File>(), 2))
for (File f : findSofficePrograms(requestedOfficeHome, new ArrayList<>(), 2))
{
logFileInfo(f);
}
@@ -449,26 +445,11 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
return results;
}
File[] matchingFiles = searchRoot.listFiles(new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
return name.startsWith("soffice");
}
});
for (File f : matchingFiles)
{
results.add(f);
}
File[] matchingFiles = searchRoot.listFiles((dir, name) -> name.startsWith("soffice"));
Arrays.stream(matchingFiles)
.forEach(results::add);
for (File dir : searchRoot.listFiles(new FileFilter()
{
@Override
public boolean accept(File f) {
return f.isDirectory();
}
}))
for (File dir : searchRoot.listFiles(File::isDirectory))
{
findSofficePrograms(dir, results, currentRecursionDepth + 1, maxRecursionDepth);
}
@@ -482,7 +463,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
*/
private void logFileInfo(File f)
{
if (logger.isDebugEnabled() == false)
if (!logger.isDebugEnabled())
{
return;
}
@@ -512,7 +493,9 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
@Override
public void destroy()
{
this.isAvailable = false;
if (officeManager != null)
{
@@ -530,6 +513,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB
/* (non-Javadoc)
* @see org.alfresco.repo.content.JodConverterWorker#getOfficeManager()
*/
@Override
public OfficeManager getOfficeManager()
{
return officeManager;

View File

@@ -0,0 +1,118 @@
package org.alfresco.transformer.executors;
import java.io.File;
import java.io.IOException;
import org.alfresco.transformer.exceptions.TransformException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.OfficeException;
import org.artofsolving.jodconverter.office.OfficeManager;
import org.springframework.stereotype.Component;
import com.sun.star.task.ErrorCodeIOException;
@Component
public class LibreOfficeJavaExecutor implements JavaExecutor
{
private static final Log logger = LogFactory.getLog(LibreOfficeJavaExecutor.class);
private static final int JODCONVERTER_TRANSFORMATION_ERROR_CODE = 3088;
private static final String OFFICE_HOME = "/opt/libreoffice5.4";
private JodConverter jodconverter = createJodConverter();
private static JodConverter createJodConverter()
{
final String timeout = "120000";
JodConverterSharedInstance jodconverter = new JodConverterSharedInstance();
jodconverter.setOfficeHome(OFFICE_HOME); // jodconverter.officeHome
jodconverter.setMaxTasksPerProcess("200"); // jodconverter.maxTasksPerProcess
jodconverter.setTaskExecutionTimeout(timeout); // jodconverter.maxTaskExecutionTimeout
jodconverter.setTaskQueueTimeout("30000"); // jodconverter.taskQueueTimeout
jodconverter.setConnectTimeout("28000"); // jodconverter.connectTimeout
jodconverter.setPortNumbers("8100"); // jodconverter.portNumbers
jodconverter.setTemplateProfileDir(""); // jodconverter.templateProfileDir
jodconverter.setEnabled("true"); // jodconverter.enabled
jodconverter.afterPropertiesSet();
return jodconverter;
}
@Override
public void call(File sourceFile, File targetFile, String... args)
{
try
{
convert(sourceFile, targetFile);
}
catch (OfficeException e)
{
throw new TransformException(400, "LibreOffice server conversion failed: \n" +
" from file: " + sourceFile + "\n" +
" to file: " + targetFile, e);
}
catch (Throwable throwable)
{
// Because of the known bug with empty Spreadsheets in JodConverter try to catch exception and produce empty pdf file
if (throwable.getCause() instanceof ErrorCodeIOException &&
((ErrorCodeIOException) throwable.getCause()).ErrCode == JODCONVERTER_TRANSFORMATION_ERROR_CODE)
{
logger.warn("Transformation failed: \n" +
"from file: " + sourceFile + "\n" +
"to file: " + targetFile +
"Source file " + sourceFile + " has no content");
produceEmptyPdfFile(targetFile);
}
else
{
throw throwable;
}
}
if (!targetFile.exists() || targetFile.length() == 0L)
{
throw new TransformException(500, "Transformer failed to create an output file");
}
}
public void convert(File sourceFile, File targetFile)
{
OfficeManager officeManager = jodconverter.getOfficeManager();
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
converter.convert(sourceFile, targetFile);
}
/**
* This method produces an empty PDF file at the specified File location.
* Apache's PDFBox is used to create the PDF file.
*/
private static void produceEmptyPdfFile(File targetFile)
{
// If improvement PDFBOX-914 is incorporated, we can do this with a straight call to
// org.apache.pdfbox.TextToPdf.createPDFFromText(new StringReader(""));
// https://issues.apache.org/jira/browse/PDFBOX-914
PDPage pdfPage = new PDPage();
try (PDDocument pdfDoc = new PDDocument();
PDPageContentStream contentStream = new PDPageContentStream(pdfDoc, pdfPage))
{
// Even though, we want an empty PDF, some libs (e.g. PDFRenderer) object to PDFs
// that have literally nothing in them. So we'll put a content stream in it.
pdfDoc.addPage(pdfPage);
// Now write the in-memory PDF document into the temporary file.
pdfDoc.save(targetFile.getAbsolutePath());
}
catch (IOException iox)
{
throw new TransformException(500, "Error creating empty PDF file", iox);
}
}
}

View File

@@ -29,26 +29,38 @@ import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashMap;
import java.util.UUID;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transformer.executors.LibreOfficeJavaExecutor;
import org.alfresco.transformer.model.FileRefEntity;
import org.alfresco.transformer.model.FileRefResponse;
import org.alfresco.util.exec.RuntimeExec;
import org.artofsolving.jodconverter.office.OfficeException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.stubbing.Answer;
import org.mockito.Mock;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@@ -62,15 +74,18 @@ import org.springframework.util.StringUtils;
@WebMvcTest(LibreOfficeControllerTest.class)
public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
{
@Mock
private RuntimeExec.ExecutionResult mockExecutionResult;
@SpyBean
private LibreOfficeJavaExecutor javaExecutor;
@SpyBean
private LibreOfficeController controller;
@Before
public void before() throws IOException
{
controller.setAlfrescoSharedFileStoreClient(alfrescoSharedFileStoreClient);
super.controller = controller;
sourceExtension = "doc";
targetExtension = "pdf";
sourceMimetype = "application/msword";
@@ -78,11 +93,11 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
// The following is based on super.mockTransformCommand(...)
// This is because LibreOffice used JodConverter rather than a RuntimeExec
expectedSourceFileBytes = Files.readAllBytes(getTestFile("quick."+sourceExtension, true).toPath());
expectedTargetFileBytes = Files.readAllBytes(getTestFile("quick."+targetExtension, true).toPath());
sourceFile = new MockMultipartFile("file", "quick."+sourceExtension, sourceMimetype, expectedSourceFileBytes);
expectedSourceFileBytes = Files.readAllBytes(getTestFile("quick." + sourceExtension, true).toPath());
expectedTargetFileBytes = Files.readAllBytes(getTestFile("quick." + targetExtension, true).toPath());
sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype, expectedSourceFileBytes);
doAnswer((Answer) invocation ->
doAnswer(invocation ->
{
File sourceFile = invocation.getArgument(0);
File targetFile = invocation.getArgument(1);
@@ -91,19 +106,12 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
assertNotNull(sourceFile);
assertNotNull(targetFile);
Long actualTimeout = invocation.getArgument(2);
assertNotNull(actualTimeout);
if (expectedTimeout != null)
{
assertEquals("expectedTimeout", expectedTimeout, actualTimeout);
}
// Copy a test file into the target file location if it exists
String actualTarget = targetFile.getAbsolutePath();
int i = actualTarget.lastIndexOf('_');
if (i >= 0)
{
String testFilename = actualTarget.substring(i+1);
String testFilename = actualTarget.substring(i + 1);
File testFile = getTestFile(testFilename, false);
generateTargetFileFromResourceFile(actualTargetExtension, testFile, targetFile);
}
@@ -113,20 +121,32 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
assertTrue("Source file is not the same", Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes));
return null;
}).when(controller).convert(any(), any(), anyLong());
}).when(javaExecutor).convert(any(), any());
}
@Override
protected void mockTransformCommand(String sourceExtension, String targetExtension,
String sourceMimetype, boolean readTargetFileBytes)
{
throw new IllegalStateException();
}
@Override
protected AbstractTransformerController getController()
{
return controller;
}
@Test
@Override
public void badExitCodeTest() throws Exception
{
doThrow(OfficeException.class).when(controller).convert(any(), any(), anyLong());
doThrow(OfficeException.class).when(javaExecutor).convert(any(), any());
mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform")
.file(sourceFile)
.param("targetExtension", "xxx"))
.andExpect(status().is(400))
.andExpect(status().reason(containsString("LibreOffice - LibreOffice server conversion failed:")));
mockMvc.perform(MockMvcRequestBuilders.multipart("/transform")
.file(sourceFile)
.param("targetExtension", "xxx"))
.andExpect(status().is(400))
.andExpect(status().reason(containsString("LibreOffice - LibreOffice server conversion failed:")));
}
@Override
@@ -137,4 +157,58 @@ public class LibreOfficeControllerTest extends AbstractTransformerControllerTest
transformRequest.setSourceMediaType("application/msword");
transformRequest.setTargetMediaType(MediaType.IMAGE_PNG_VALUE);
}
@Test
public void testPojoTransform() throws Exception
{
// Files
String sourceFileRef = UUID.randomUUID().toString();
File sourceFile = getTestFile("quick." + sourceExtension, true);
String targetFileRef = UUID.randomUUID().toString();
// Transformation Request POJO
TransformRequest transformRequest = new TransformRequest();
transformRequest.setRequestId("1");
transformRequest.setSchema(1);
transformRequest.setClientData("Alfresco Digital Business Platform");
transformRequest.setTransformRequestOptions(new HashMap<>());
transformRequest.setSourceReference(sourceFileRef);
transformRequest.setSourceExtension(sourceExtension);
transformRequest.setSourceSize(sourceFile.length());
transformRequest.setTargetExtension(targetExtension);
// HTTP Request
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=quick." + sourceExtension);
ResponseEntity<Resource> response = new ResponseEntity<>(new FileSystemResource(
sourceFile), headers, HttpStatus.OK);
when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response);
when(alfrescoSharedFileStoreClient.saveFile(any())).thenReturn(
new FileRefResponse(new FileRefEntity(targetFileRef)));
when(mockExecutionResult.getExitValue()).thenReturn(0);
// Update the Transformation Request with any specific params before sending it
updateTransformRequestWithSpecificOptions(transformRequest);
// Serialize and call the transformer
String tr = objectMapper.writeValueAsString(transformRequest);
String transformationReplyAsString = mockMvc.perform(
MockMvcRequestBuilders.post("/transform")
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_JSON_VALUE).content(tr))
.andExpect(
status().is(HttpStatus.CREATED.value()))
.andReturn().getResponse().getContentAsString();
TransformReply transformReply = objectMapper.readValue(transformationReplyAsString,
TransformReply.class);
// Assert the reply
assertEquals(transformRequest.getRequestId(), transformReply.getRequestId());
assertEquals(transformRequest.getClientData(), transformReply.getClientData());
assertEquals(transformRequest.getSchema(), transformReply.getSchema());
}
}

View File

@@ -25,7 +25,6 @@
*/
package org.alfresco.transformer;
import org.alfresco.transformer.AbstractHttpRequestTest;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@@ -47,5 +46,5 @@ public class LibreOfficeHttpRequestTest extends AbstractHttpRequestTest
protected String getSourceExtension()
{
return "doc";
};
}
}