errCodes;
+ private final Timer timer = new Timer(true);
+
+ /**
+ * Default constructor. Initialize this instance by setting individual properties.
+ */
+ public RuntimeExec()
+ {
+ // set default error codes
+ errCodes = new HashSet<>(2);
+ errCodes.add(1);
+ errCodes.add(2);
+ }
+
+ public String toString()
+ {
+ final StringBuilder sb = new StringBuilder(256);
+ sb.append("RuntimeExec:\n").append(" command: ");
+ if (command == null)
+ {
+ // command is 'null', so there's nothing to toString
+ sb.append("'null'\n");
+ }
+ else
+ {
+ for (String cmdStr : command)
+ {
+ sb.append(cmdStr).append(" ");
+ }
+ sb.append("\n");
+ }
+ sb.append(" env props: ").append(Arrays.toString(processProperties)).append("\n")
+ .append(" dir: ").append(processDirectory).append("\n")
+ .append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n");
+ return sb.toString();
+ }
+
+ /**
+ * Set the command to execute regardless of operating system
+ *
+ * @param command an array of strings representing the command (first entry) and arguments
+ * @since 3.0
+ */
+ public void setCommand(String[] command)
+ {
+ this.command = command;
+ }
+
+ /**
+ * Sets the assumed charset of OUT and ERR streams generated by the executed command.
+ * This defaults to the system default charset: {@link Charset#defaultCharset()}.
+ *
+ * @param charsetCode a supported character set code
+ * @throws UnsupportedCharsetException if the characterset code is not recognised by Java
+ */
+ public void setCharset(String charsetCode)
+ {
+ this.charset = Charset.forName(charsetCode);
+ }
+
+ /**
+ * Set whether to wait for completion of the command or not. If there is no wait for completion,
+ * then the return value of out and err buffers cannot be relied upon as the
+ * command may still be in progress. Failure is therefore not possible unless the calling thread
+ * waits for execution.
+ *
+ * @param waitForCompletion true (default) is to wait for the command to exit,
+ * or false to just return an exit code of 0 and whatever
+ * output is available at that point.
+ * @since 2.1
+ */
+ public void setWaitForCompletion(boolean waitForCompletion)
+ {
+ this.waitForCompletion = waitForCompletion;
+ }
+
+ /**
+ * Supply a choice of commands to execute based on a mapping from the os.name system
+ * property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used
+ * to get a command where there is not direct match to the operating system key.
+ *
+ * Each command is an array of strings, the first of which represents the command and all subsequent
+ * entries in the array represent the arguments. All elements of the array will be checked for
+ * the presence of any substitution parameters (e.g. '{dir}'). The parameters can be set using the
+ * {@link #setDefaultProperties(Map) defaults} or by passing the substitution values into the
+ * {@link #execute(Map)} command.
+ *
+ * If parameters passed may be multiple arguments, or if the values provided in the map are themselves
+ * collections of arguments (not recommended), then prefix the value with SPLIT: to ensure that
+ * the value is tokenized before being passed to the command. Any values that are not split, will be
+ * passed to the command as single arguments. For example:
+ * 'SPLIT: dir . ..' becomes 'dir', '.' and '..'.
+ * 'SPLIT: dir ${path}' (if path is '. ..') becomes 'dir', '.' and '..'.
+ * The splitting occurs post-subtitution. Where the arguments are known, it is advisable to avoid
+ * SPLIT:.
+ *
+ * @param commandsByOS a map of command string arrays, keyed by operating system names
+ * @see #setDefaultProperties(Map)
+ * @since 3.0
+ */
+ public void setCommandsAndArguments(Map commandsByOS)
+ {
+ // get the current OS
+ String serverOs = System.getProperty(KEY_OS_NAME);
+ // attempt to find a match
+ String[] command = commandsByOS.get(serverOs);
+ if (command == null)
+ {
+ // go through the commands keys, looking for one that matches by regular expression matching
+ for (String osName : commandsByOS.keySet())
+ {
+ // Ignore * options. It is dealt with later.
+ if (osName.equals(KEY_OS_DEFAULT))
+ {
+ continue;
+ }
+ // Do regex match
+ if (serverOs.matches(osName))
+ {
+ command = commandsByOS.get(osName);
+ break;
+ }
+ }
+ // if there is still no command, then check for the wildcard
+ if (command == null)
+ {
+ command = commandsByOS.get(KEY_OS_DEFAULT);
+ }
+ }
+ // check
+ if (command == null)
+ {
+ throw new RuntimeException(
+ "No command found for OS " + serverOs + " or '" + KEY_OS_DEFAULT + "': \n" +
+ " commands: " + commandsByOS);
+ }
+ this.command = command;
+ }
+
+ /**
+ * Supply a choice of commands to execute based on a mapping from the os.name system
+ * property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used
+ * to get a command where there is not direct match to the operating system key.
+ *
+ * @param commandsByOS a map of command string keyed by operating system names
+ * @deprecated Use {@link #setCommandsAndArguments(Map)}
+ */
+ public void setCommandMap(Map commandsByOS)
+ {
+ // This is deprecated, so issue a warning
+ logger.warn(
+ "The bean RuntimeExec property 'commandMap' has been deprecated;" +
+ " use 'commandsAndArguments' instead. See https://issues.alfresco.com/jira/browse/ETHREEOH-579.");
+ Map fixed = new LinkedHashMap<>();
+ for (Map.Entry entry : commandsByOS.entrySet())
+ {
+ String os = entry.getKey();
+ String unparsedCmd = entry.getValue();
+ StringTokenizer tokenizer = new StringTokenizer(unparsedCmd);
+ String[] cmd = new String[tokenizer.countTokens()];
+ for (int i = 0; i < cmd.length; i++)
+ {
+ cmd[i] = tokenizer.nextToken();
+ }
+ fixed.put(os, cmd);
+ }
+ setCommandsAndArguments(fixed);
+ }
+
+ /**
+ * Set the default command-line properties to use when executing the command.
+ * These are properties that substitute variables defined in the command string itself.
+ * Properties supplied during execution will overwrite the default properties.
+ *
+ * null
properties will be treated as an empty string for substitution
+ * purposes.
+ *
+ * @param defaultProperties property values
+ */
+ public void setDefaultProperties(Map defaultProperties)
+ {
+ this.defaultProperties = defaultProperties;
+ }
+
+ /**
+ * Set additional runtime properties (environment properties) that will used
+ * by the executing process.
+ *
+ * Any keys or properties that start and end with ${...} will be removed on the assumption
+ * that these are unset properties. null values are translated to empty strings.
+ * All keys and values are trimmed of leading and trailing whitespace.
+ *
+ * @param processProperties Runtime process properties
+ * @see Runtime#exec(String, String[], java.io.File)
+ */
+ public void setProcessProperties(Map processProperties)
+ {
+ ArrayList processPropList = new ArrayList<>(processProperties.size());
+ boolean hasPath = false;
+ String systemPath = System.getenv("PATH");
+ for (Map.Entry entry : processProperties.entrySet())
+ {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key == null)
+ {
+ continue;
+ }
+ if (value == null)
+ {
+ value = "";
+ }
+ key = key.trim();
+ value = value.trim();
+ if (key.startsWith(VAR_OPEN) && key.endsWith(VAR_CLOSE))
+ {
+ continue;
+ }
+ if (value.startsWith(VAR_OPEN) && value.endsWith(VAR_CLOSE))
+ {
+ continue;
+ }
+ // If a path is specified, prepend it to the existing path
+ if (key.equals("PATH"))
+ {
+ if (systemPath != null && systemPath.length() > 0)
+ {
+ processPropList.add(key + "=" + value + File.pathSeparator + systemPath);
+ }
+ else
+ {
+ processPropList.add(key + "=" + value);
+ }
+ hasPath = true;
+ }
+ else
+ {
+ processPropList.add(key + "=" + value);
+ }
+ }
+ // If a path was not specified, inherit the current one
+ if (!hasPath && systemPath != null && systemPath.length() > 0)
+ {
+ processPropList.add("PATH=" + systemPath);
+ }
+ this.processProperties = processPropList.toArray(new String[0]);
+ }
+
+ /**
+ * Adds a property to existed processProperties.
+ * Property should not be null or empty.
+ * If property with the same value already exists then no change is made.
+ * If property exists with a different value then old value is replaced with the new one.
+ *
+ * @param name - property name
+ * @param value - property value
+ */
+ public void setProcessProperty(String name, String value)
+ {
+ boolean set = false;
+
+ if (name == null || value == null)
+ {
+ return;
+ }
+
+ name = name.trim();
+ value = value.trim();
+
+ if (name.isEmpty() || value.isEmpty())
+ {
+ return;
+ }
+
+ String property = name + "=" + value;
+
+ for (String prop : this.processProperties)
+ {
+ if (prop.equals(property))
+ {
+ set = true;
+ break;
+ }
+
+ if (prop.startsWith(name))
+ {
+ String oldValue = prop.split("=")[1];
+ prop.replace(oldValue, value);
+ set = true;
+ }
+ }
+
+ if (!set)
+ {
+ String[] existedProperties = this.processProperties;
+ int epl = existedProperties.length;
+ String[] newProperties = Arrays.copyOf(existedProperties, epl + 1);
+ newProperties[epl] = property;
+ this.processProperties = newProperties;
+ }
+ }
+
+ /**
+ * Set the runtime location from which the command is executed.
+ *
+ * If the value is an unsubsititued variable (${...}) then it is ignored.
+ * If the location is not visible at the time of setting, a warning is issued only.
+ *
+ * @param processDirectory the runtime location from which to execute the command
+ */
+ public void setProcessDirectory(String processDirectory)
+ {
+ if (processDirectory.startsWith(VAR_OPEN) && processDirectory.endsWith(VAR_CLOSE))
+ {
+ this.processDirectory = null;
+ }
+ else
+ {
+ this.processDirectory = new File(processDirectory);
+ if (!this.processDirectory.exists())
+ {
+ logger.warn(
+ "The runtime process directory is not visible when setting property " +
+ "'processDirectory': \n{}", this);
+ }
+ }
+ }
+
+ /**
+ * A comma or space separated list of values that, if returned by the executed command,
+ * indicate an error value. This defaults to "1, 2".
+ *
+ * @param errCodesStr the error codes for the execution
+ */
+ public void setErrorCodes(String errCodesStr)
+ {
+ errCodes.clear();
+ StringTokenizer tokenizer = new StringTokenizer(errCodesStr, " ,");
+ while (tokenizer.hasMoreElements())
+ {
+ String errCodeStr = tokenizer.nextToken();
+ // attempt to convert it to an integer
+ try
+ {
+ int errCode = Integer.parseInt(errCodeStr);
+ this.errCodes.add(errCode);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new RuntimeException(
+ "Property 'errorCodes' must be comma-separated list of integers: " + errCodesStr);
+ }
+ }
+ }
+
+ /**
+ * Executes the command using the default properties
+ *
+ * @see #execute(Map)
+ */
+ public ExecutionResult execute()
+ {
+ return execute(defaultProperties);
+ }
+
+ /**
+ * Executes the statement that this instance was constructed with.
+ *
+ * @param properties the properties that the command might be executed with.
+ * null
properties will be treated as an empty string for substitution
+ * purposes.
+ * @return Returns the full execution results
+ */
+ public ExecutionResult execute(Map properties)
+ {
+ return execute(properties, -1);
+ }
+
+ /**
+ * Executes the statement that this instance was constructed with an optional
+ * timeout after which the command is asked to
+ *
+ * @param properties the properties that the command might be executed with.
+ * null
properties will be treated as an empty string for substitution
+ * purposes.
+ * @param timeoutMs a timeout after which {@link Process#destroy()} is called.
+ * ignored if less than or equal to zero. Note this method does not guarantee
+ * to terminate the process (it is not a kill -9).
+ * @return Returns the full execution results
+ */
+ public ExecutionResult execute(Map properties, final long timeoutMs)
+ {
+ int defaultFailureExitValue = errCodes.size() > 0 ? ((Integer) errCodes.toArray()[0]) : 1;
+
+ // check that the command has been set
+ if (command == null)
+ {
+ throw new RuntimeException("Runtime command has not been set: \n" + this);
+ }
+
+ // create the properties
+ Runtime runtime = Runtime.getRuntime();
+ Process process;
+ String[] commandToExecute = null;
+ try
+ {
+ // execute the command with full property replacement
+ commandToExecute = getCommand(properties);
+ final Process thisProcess = runtime.exec(commandToExecute, processProperties,
+ processDirectory);
+ process = thisProcess;
+ if (timeoutMs > 0)
+ {
+ final String[] command = commandToExecute;
+ timer.schedule(new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ // Only try to kill the process if it is still running
+ try
+ {
+ thisProcess.exitValue();
+ }
+ catch (IllegalThreadStateException stillRunning)
+ {
+ logger.debug(
+ "Process has taken too long ({} seconds). Killing process {}",
+ timeoutMs / 1000, Arrays.deepToString(command));
+ }
+ }
+ }, timeoutMs);
+ }
+ }
+ catch (IOException e)
+ {
+ // The process could not be executed here, so just drop out with an appropriate error state
+ String execOut = "";
+ String execErr = e.getMessage();
+ ExecutionResult result = new ExecutionResult(null, commandToExecute, errCodes,
+ defaultFailureExitValue, execOut, execErr);
+ logFullEnvironmentDump(result);
+ return result;
+ }
+
+ // create the stream gobblers
+ InputStreamReaderThread stdOutGobbler = new InputStreamReaderThread(
+ process.getInputStream(), charset);
+ InputStreamReaderThread stdErrGobbler = new InputStreamReaderThread(
+ process.getErrorStream(), charset);
+
+ // start gobbling
+ stdOutGobbler.start();
+ stdErrGobbler.start();
+
+ // wait for the process to finish
+ int exitValue = 0;
+ try
+ {
+ if (waitForCompletion)
+ {
+ exitValue = process.waitFor();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // process was interrupted - generate an error message
+ stdErrGobbler.addToBuffer(e.toString());
+ exitValue = defaultFailureExitValue;
+ }
+
+ if (waitForCompletion)
+ {
+ // ensure that the stream gobblers get to finish
+ stdOutGobbler.waitForCompletion();
+ stdErrGobbler.waitForCompletion();
+ }
+
+ // get the stream values
+ String execOut = stdOutGobbler.getBuffer();
+ String execErr = stdErrGobbler.getBuffer();
+
+ // construct the return value
+ ExecutionResult result = new ExecutionResult(process, commandToExecute, errCodes, exitValue,
+ execOut, execErr);
+
+ // done
+ logFullEnvironmentDump(result);
+ return result;
+ }
+
+ /**
+ * Dump the full environment in debug mode
+ */
+ private void logFullEnvironmentDump(ExecutionResult result)
+ {
+ if (logger.isTraceEnabled())
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(result);
+
+ // Environment variables modified by Alfresco
+ if (processProperties != null && processProperties.length > 0)
+ {
+ sb.append("\n modified environment: ");
+ for (String property : processProperties)
+ {
+ sb.append("\n ");
+ sb.append(property);
+ }
+ }
+
+ // Dump the full environment
+ sb.append("\n existing environment: ");
+ Map envVariables = System.getenv();
+ for (Map.Entry entry : envVariables.entrySet())
+ {
+ String name = entry.getKey();
+ String value = entry.getValue();
+ sb.append("\n ");
+ sb.append(name).append("=").append(value);
+ }
+
+ logger.trace(sb.toString());
+ }
+ logger.debug("Result: " + result.toString());
+
+ // close output stream (connected to input stream of native subprocess)
+ }
+
+ /**
+ * @return Returns the command that will be executed if no additional properties
+ * were to be supplied
+ */
+ public String[] getCommand()
+ {
+ return getCommand(defaultProperties);
+ }
+
+ /**
+ * Get the command that will be executed post substitution.
+ *
+ * null
properties will be treated as an empty string for substitution
+ * purposes.
+ *
+ * @param properties the properties that the command might be executed with
+ * @return Returns the command that will be executed should the additional properties
+ * be supplied
+ */
+ public String[] getCommand(Map properties)
+ {
+ Map execProperties;
+ if (properties == defaultProperties)
+ {
+ // we are just using the default properties
+ execProperties = defaultProperties;
+ }
+ else
+ {
+ execProperties = new HashMap<>(defaultProperties);
+ // overlay the supplied properties
+ execProperties.putAll(properties);
+ }
+ // Perform the substitution for each element of the command
+ ArrayList adjustedCommandElements = new ArrayList<>(20);
+ for (String s : command)
+ {
+ StringBuilder sb = new StringBuilder(s);
+ for (Map.Entry entry : execProperties.entrySet())
+ {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ // ignore null
+ if (value == null)
+ {
+ value = "";
+ }
+ // progressively replace the property in the command
+ key = (VAR_OPEN + key + VAR_CLOSE);
+ int index = sb.indexOf(key);
+ while (index > -1)
+ {
+ // replace
+ sb.replace(index, index + key.length(), value);
+ // get the next one
+ index = sb.indexOf(key, index + 1);
+ }
+ }
+ String adjustedValue = sb.toString();
+ // Now SPLIT: it
+ if (adjustedValue.startsWith(DIRECTIVE_SPLIT))
+ {
+ String unsplitAdjustedValue = sb.substring(DIRECTIVE_SPLIT.length());
+
+ // There may be quoted arguments here (see ALF-7482)
+ ExecParameterTokenizer quoteAwareTokenizer = new ExecParameterTokenizer(
+ unsplitAdjustedValue);
+ List tokens = quoteAwareTokenizer.getAllTokens();
+ adjustedCommandElements.addAll(tokens);
+ }
+ else
+ {
+ adjustedCommandElements.add(adjustedValue);
+ }
+ }
+ // done
+ return adjustedCommandElements.toArray(new String[0]);
+ }
+
+ /**
+ * Object to carry the results of an execution to the caller.
+ *
+ * @author Derek Hulley
+ */
+ public static class ExecutionResult
+ {
+ private final Process process;
+ private final String[] command;
+ private final Set errCodes;
+ private final int exitValue;
+ private final String stdOut;
+ private final String stdErr;
+
+ /**
+ * @param process the process attached to Java - null is allowed
+ */
+ private ExecutionResult(
+ final Process process,
+ final String[] command,
+ final Set errCodes,
+ final int exitValue,
+ final String stdOut,
+ final String stdErr)
+ {
+ this.process = process;
+ this.command = command;
+ this.errCodes = errCodes;
+ this.exitValue = exitValue;
+ this.stdOut = stdOut;
+ this.stdErr = stdErr;
+ }
+
+ @Override
+ public String toString()
+ {
+ String out = stdOut.length() > 250 ? stdOut.substring(0, 250) : stdOut;
+ String err = stdErr.length() > 250 ? stdErr.substring(0, 250) : stdErr;
+
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Execution result: \n")
+ .append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n")
+ .append(" command: ");
+ appendCommand(sb, command).append("\n")
+ .append(" succeeded: ").append(getSuccess()).append("\n")
+ .append(" exit code: ").append(exitValue).append("\n")
+ .append(" out: ").append(out).append("\n")
+ .append(" err: ").append(err);
+ return sb.toString();
+ }
+
+ /**
+ * Appends the command in a form that make running from the command line simpler.
+ * It is not a real attempt at making a command given all the operating system
+ * and shell options, but makes copy, paste and edit a bit simpler.
+ */
+ private StringBuilder appendCommand(StringBuilder sb, String[] command)
+ {
+ boolean arg = false;
+ for (String element : command)
+ {
+ if (element == null)
+ {
+ continue;
+ }
+
+ if (arg)
+ {
+ sb.append(' ');
+ }
+ else
+ {
+ arg = true;
+ }
+
+ boolean escape = element.indexOf(' ') != -1 || element.indexOf('>') != -1;
+ if (escape)
+ {
+ sb.append("\"");
+ }
+ sb.append(element);
+ if (escape)
+ {
+ sb.append("\"");
+ }
+ }
+ return sb;
+ }
+
+ /**
+ * A helper method to force a kill of the process that generated this result. This is
+ * useful in cases where the process started is not expected to exit, or doesn't exit
+ * quickly. If the {@linkplain RuntimeExec#setWaitForCompletion(boolean) "wait for completion"}
+ * flag is false then the process may still be running when this result is returned.
+ *
+ * @return true if the process was killed, otherwise false
+ */
+ public boolean killProcess()
+ {
+ if (process == null)
+ {
+ return true;
+ }
+ try
+ {
+ process.destroy();
+ return true;
+ }
+ catch (Throwable e)
+ {
+ logger.warn(e.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * @param exitValue the command exit value
+ * @return Returns true if the code is a listed failure code
+ * @see #setErrorCodes(String)
+ */
+ private boolean isFailureCode(int exitValue)
+ {
+ return errCodes.contains(exitValue);
+ }
+
+ /**
+ * @return Returns true if the command was deemed to be successful according to the
+ * failure codes returned by the execution.
+ */
+ public boolean getSuccess()
+ {
+ return !isFailureCode(exitValue);
+ }
+
+ public int getExitValue()
+ {
+ return exitValue;
+ }
+
+ public String getStdOut()
+ {
+ return stdOut;
+ }
+
+ public String getStdErr()
+ {
+ return stdErr;
+ }
+ }
+
+ /**
+ * Gobbles an InputStream
and writes it into a
+ * StringBuffer
+ *
+ * The reading of the input stream is buffered.
+ */
+ public static class InputStreamReaderThread extends Thread
+ {
+ private final InputStream is;
+ private final Charset charset;
+ private final StringBuffer buffer; // we require the synchronization
+ private boolean completed;
+
+ /**
+ * @param is an input stream to read - it will be wrapped in a buffer
+ * for reading
+ */
+ public InputStreamReaderThread(InputStream is, Charset charset)
+ {
+ super();
+ setDaemon(true); // must not hold up the VM if it is terminating
+ this.is = is;
+ this.charset = charset;
+ this.buffer = new StringBuffer(BUFFER_SIZE);
+ this.completed = false;
+ }
+
+ public synchronized void run()
+ {
+ completed = false;
+
+ byte[] bytes = new byte[BUFFER_SIZE];
+ try (InputStream tempIs = new BufferedInputStream(is, BUFFER_SIZE))
+ {
+ int count = -2;
+ while (count != -1)
+ {
+ // do we have something previously read?
+ if (count > 0)
+ {
+ String toWrite = new String(bytes, 0, count, charset.name());
+ buffer.append(toWrite);
+ }
+ // read the next set of bytes
+ count = tempIs.read(bytes);
+ }
+ // done
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to read stream", e);
+ }
+ finally
+ {
+ // The thread has finished consuming the stream
+ completed = true;
+ // Notify waiters
+ this.notifyAll(); // Note: Method is synchronized
+ }
+ }
+
+ /**
+ * Waits for the run to complete.
+ *
+ * Remember to start
the thread first
+ */
+ public synchronized void waitForCompletion()
+ {
+ while (!completed)
+ {
+ try
+ {
+ // release our lock and wait a bit
+ this.wait(1000L); // 200 ms
+ }
+ catch (InterruptedException ignore)
+ {
+ }
+ }
+ }
+
+ /**
+ * @param msg the message to add to the buffer
+ */
+ public void addToBuffer(String msg)
+ {
+ buffer.append(msg);
+ }
+
+ public boolean isComplete()
+ {
+ return completed;
+ }
+
+ /**
+ * @return Returns the current state of the buffer
+ */
+ public String getBuffer()
+ {
+ return buffer.toString();
+ }
+ }
+}
+
diff --git a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/fs/FileManager.java b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/fs/FileManager.java
index ba27fc05..746897b1 100644
--- a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/fs/FileManager.java
+++ b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/fs/FileManager.java
@@ -44,7 +44,6 @@ import javax.servlet.http.HttpServletRequest;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.logging.LogEntry;
-import org.alfresco.util.TempFileProvider;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
@@ -241,4 +240,44 @@ public class FileManager
return ResponseEntity.ok().header(CONTENT_DISPOSITION,
"attachment; filename*= UTF-8''" + targetFilename).body(targetResource);
}
+
+ /**
+ * TempFileProvider - Duplicated and adapted from alfresco-core.
+ */
+ public static class TempFileProvider
+ {
+ public static File createTempFile(final String prefix, final String suffix)
+ {
+ final File directory = getTempDir();
+ try
+ {
+ return File.createTempFile(prefix, suffix, directory);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(
+ "Failed to created temp file: \n prefix: " + prefix +
+ "\n suffix: " + suffix + "\n directory: " + directory, e);
+ }
+ }
+
+ private static File getTempDir()
+ {
+ final String dirName = "Alfresco";
+ final String systemTempDirPath = System.getProperty("java.io.tmpdir");
+ if (systemTempDirPath == null)
+ {
+ throw new RuntimeException("System property not available: java.io.tmpdir");
+ }
+
+ final File systemTempDir = new File(systemTempDirPath);
+ final File tempDir = new File(systemTempDir, dirName);
+ if (!tempDir.exists() && !tempDir.mkdirs() && !tempDir.exists())
+ {
+ throw new RuntimeException("Failed to create temp directory: " + tempDir);
+ }
+
+ return tempDir;
+ }
+ }
}
diff --git a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/probes/ProbeTestTransform.java b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/probes/ProbeTestTransform.java
index 3e4b3779..b6bfd431 100644
--- a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/probes/ProbeTestTransform.java
+++ b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/probes/ProbeTestTransform.java
@@ -28,6 +28,7 @@ package org.alfresco.transformer.probes;
import static org.alfresco.transformer.fs.FileManager.SOURCE_FILE;
import static org.alfresco.transformer.fs.FileManager.TARGET_FILE;
+import static org.alfresco.transformer.fs.FileManager.TempFileProvider.createTempFile;
import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.OK;
@@ -46,7 +47,6 @@ import javax.servlet.http.HttpServletRequest;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.AbstractTransformerController;
import org.alfresco.transformer.logging.LogEntry;
-import org.alfresco.util.TempFileProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -263,7 +263,7 @@ public abstract class ProbeTestTransform
private File getSourceFile(HttpServletRequest request, boolean isLiveProbe)
{
incrementTransformerCount();
- File sourceFile = TempFileProvider.createTempFile("source_", "_" + sourceFilename);
+ File sourceFile = createTempFile("source_", "_" + sourceFilename);
request.setAttribute(SOURCE_FILE, sourceFile);
try (InputStream inputStream = this.getClass().getResourceAsStream('/' + sourceFilename))
{
@@ -281,7 +281,7 @@ public abstract class ProbeTestTransform
private File getTargetFile(HttpServletRequest request)
{
- File targetFile = TempFileProvider.createTempFile("target_", "_" + targetFilename);
+ File targetFile = createTempFile("target_", "_" + targetFilename);
request.setAttribute(TARGET_FILE, targetFile);
LogEntry.setTarget(targetFilename);
return targetFile;
diff --git a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/util/MimetypeMap.java b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/util/MimetypeMap.java
new file mode 100644
index 00000000..6e414670
--- /dev/null
+++ b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/util/MimetypeMap.java
@@ -0,0 +1,165 @@
+/*
+ * #%L
+ * Alfresco Transform Core
+ * %%
+ * Copyright (C) 2005 - 2019 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.transformer.util;
+
+/**
+ * Partially duplicated from *alfresco-data-model*.
+ */
+public interface MimetypeMap
+{
+ String PREFIX_APPLICATION = "application/";
+ String PREFIX_AUDIO = "audio/";
+ String PREFIX_IMAGE = "image/";
+ String PREFIX_MESSAGE = "message/";
+ String PREFIX_MODEL = "model/";
+ String PREFIX_MULTIPART = "multipart/";
+ String PREFIX_TEXT = "text/";
+ String PREFIX_VIDEO = "video/";
+ String EXTENSION_BINARY = "bin";
+ String MACOS_RESOURCE_FORK_FILE_NAME_PREFIX = "._";
+ String MIMETYPE_MULTIPART_ALTERNATIVE = "multipart/alternative";
+ String MIMETYPE_TEXT_PLAIN = "text/plain";
+ String MIMETYPE_TEXT_MEDIAWIKI = "text/mediawiki";
+ String MIMETYPE_TEXT_CSS = "text/css";
+ String MIMETYPE_TEXT_CSV = "text/csv";
+ String MIMETYPE_TEXT_JAVASCRIPT = "text/javascript";
+ String MIMETYPE_XML = "text/xml";
+ String MIMETYPE_HTML = "text/html";
+ String MIMETYPE_XHTML = "application/xhtml+xml";
+ String MIMETYPE_PDF = "application/pdf";
+ String MIMETYPE_JSON = "application/json";
+ String MIMETYPE_WORD = "application/msword";
+ String MIMETYPE_EXCEL = "application/vnd.ms-excel";
+ String MIMETYPE_BINARY = "application/octet-stream";
+ String MIMETYPE_PPT = "application/vnd.ms-powerpoint";
+ String MIMETYPE_APP_DWG = "application/dwg";
+ String MIMETYPE_IMG_DWG = "image/vnd.dwg";
+ String MIMETYPE_VIDEO_AVI = "video/x-msvideo";
+ String MIMETYPE_VIDEO_QUICKTIME = "video/quicktime";
+ String MIMETYPE_VIDEO_WMV = "video/x-ms-wmv";
+ String MIMETYPE_VIDEO_3GP = "video/3gpp";
+ String MIMETYPE_VIDEO_3GP2 = "video/3gpp2";
+ String MIMETYPE_DITA = "application/dita+xml";
+ String MIMETYPE_FLASH = "application/x-shockwave-flash";
+ String MIMETYPE_VIDEO_FLV = "video/x-flv";
+ String MIMETYPE_APPLICATION_FLA = "application/x-fla";
+ String MIMETYPE_VIDEO_MPG = "video/mpeg";
+ String MIMETYPE_VIDEO_MP4 = "video/mp4";
+ String MIMETYPE_IMAGE_GIF = "image/gif";
+ String MIMETYPE_IMAGE_JPEG = "image/jpeg";
+ String MIMETYPE_IMAGE_RGB = "image/x-rgb";
+ String MIMETYPE_IMAGE_SVG = "image/svg+xml";
+ String MIMETYPE_IMAGE_PNG = "image/png";
+ String MIMETYPE_IMAGE_TIFF = "image/tiff";
+ String MIMETYPE_IMAGE_RAW_DNG = "image/x-raw-adobe";
+ String MIMETYPE_IMAGE_RAW_3FR = "image/x-raw-hasselblad";
+ String MIMETYPE_IMAGE_RAW_RAF = "image/x-raw-fuji";
+ String MIMETYPE_IMAGE_RAW_CR2 = "image/x-raw-canon";
+ String MIMETYPE_IMAGE_RAW_K25 = "image/x-raw-kodak";
+ String MIMETYPE_IMAGE_RAW_MRW = "image/x-raw-minolta";
+ String MIMETYPE_IMAGE_RAW_NEF = "image/x-raw-nikon";
+ String MIMETYPE_IMAGE_RAW_ORF = "image/x-raw-olympus";
+ String MIMETYPE_IMAGE_RAW_PEF = "image/x-raw-pentax";
+ String MIMETYPE_IMAGE_RAW_ARW = "image/x-raw-sony";
+ String MIMETYPE_IMAGE_RAW_X3F = "image/x-raw-sigma";
+ String MIMETYPE_IMAGE_RAW_RW2 = "image/x-raw-panasonic";
+ String MIMETYPE_IMAGE_RAW_RWL = "image/x-raw-leica";
+ String MIMETYPE_IMAGE_RAW_R3D = "image/x-raw-red";
+ String MIMETYPE_IMAGE_DWT = "image/x-dwt";
+ String MIMETYPE_APPLICATION_EPS = "application/eps";
+ String MIMETYPE_APPLICATION_PS = "application/postscript";
+ String MIMETYPE_JAVASCRIPT = "application/x-javascript";
+ String MIMETYPE_ZIP = "application/zip";
+ String MIMETYPE_OPENSEARCH_DESCRIPTION = "application/opensearchdescription+xml";
+ String MIMETYPE_ATOM = "application/atom+xml";
+ String MIMETYPE_RSS = "application/rss+xml";
+ String MIMETYPE_RFC822 = "message/rfc822";
+ String MIMETYPE_OUTLOOK_MSG = "application/vnd.ms-outlook";
+ String MIMETYPE_VISIO = "application/vnd.visio";
+ String MIMETYPE_VISIO_2013 = "application/vnd.visio2013";
+ String MIMETYPE_APPLICATION_ILLUSTRATOR = "application/illustrator";
+ String MIMETYPE_APPLICATION_PHOTOSHOP = "image/vnd.adobe.photoshop";
+ String MIMETYPE_ENCRYPTED_OFFICE = "application/x-tika-ooxml-protected";
+ String MIMETYPE_OPENDOCUMENT_TEXT = "application/vnd.oasis.opendocument.text";
+ String MIMETYPE_OPENDOCUMENT_TEXT_TEMPLATE = "application/vnd.oasis.opendocument.text-template";
+ String MIMETYPE_OPENDOCUMENT_GRAPHICS = "application/vnd.oasis.opendocument.graphics";
+ String MIMETYPE_OPENDOCUMENT_GRAPHICS_TEMPLATE = "application/vnd.oasis.opendocument.graphics-template";
+ String MIMETYPE_OPENDOCUMENT_PRESENTATION = "application/vnd.oasis.opendocument.presentation";
+ String MIMETYPE_OPENDOCUMENT_PRESENTATION_TEMPLATE = "application/vnd.oasis.opendocument.presentation-template";
+ String MIMETYPE_OPENDOCUMENT_SPREADSHEET = "application/vnd.oasis.opendocument.spreadsheet";
+ String MIMETYPE_OPENDOCUMENT_SPREADSHEET_TEMPLATE = "application/vnd.oasis.opendocument.spreadsheet-template";
+ String MIMETYPE_OPENDOCUMENT_CHART = "application/vnd.oasis.opendocument.chart";
+ String MIMETYPE_OPENDOCUMENT_CHART_TEMPLATE = "applicationvnd.oasis.opendocument.chart-template";
+ String MIMETYPE_OPENDOCUMENT_IMAGE = "application/vnd.oasis.opendocument.image";
+ String MIMETYPE_OPENDOCUMENT_IMAGE_TEMPLATE = "applicationvnd.oasis.opendocument.image-template";
+ String MIMETYPE_OPENDOCUMENT_FORMULA = "application/vnd.oasis.opendocument.formula";
+ String MIMETYPE_OPENDOCUMENT_FORMULA_TEMPLATE = "applicationvnd.oasis.opendocument.formula-template";
+ String MIMETYPE_OPENDOCUMENT_TEXT_MASTER = "application/vnd.oasis.opendocument.text-master";
+ String MIMETYPE_OPENDOCUMENT_TEXT_WEB = "application/vnd.oasis.opendocument.text-web";
+ String MIMETYPE_OPENDOCUMENT_DATABASE = "application/vnd.oasis.opendocument.database";
+ String MIMETYPE_OPENOFFICE1_WRITER = "application/vnd.sun.xml.writer";
+ String MIMETYPE_OPENOFFICE1_CALC = "application/vnd.sun.xml.calc";
+ String MIMETYPE_OPENOFFICE1_DRAW = "application/vnd.sun.xml.draw";
+ String MIMETYPE_OPENOFFICE1_IMPRESS = "application/vnd.sun.xml.impress";
+ String MIMETYPE_OPENXML_WORDPROCESSING = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
+ String MIMETYPE_OPENXML_WORDPROCESSING_MACRO = "application/vnd.ms-word.document.macroenabled.12";
+ String MIMETYPE_OPENXML_WORD_TEMPLATE = "application/vnd.openxmlformats-officedocument.wordprocessingml.template";
+ String MIMETYPE_OPENXML_WORD_TEMPLATE_MACRO = "application/vnd.ms-word.template.macroenabled.12";
+ String MIMETYPE_OPENXML_SPREADSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+ String MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE = "application/vnd.openxmlformats-officedocument.spreadsheetml.template";
+ String MIMETYPE_OPENXML_SPREADSHEET_MACRO = "application/vnd.ms-excel.sheet.macroenabled.12";
+ String MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE_MACRO = "application/vnd.ms-excel.template.macroenabled.12";
+ String MIMETYPE_OPENXML_SPREADSHEET_ADDIN_MACRO = "application/vnd.ms-excel.addin.macroenabled.12";
+ String MIMETYPE_OPENXML_SPREADSHEET_BINARY_MACRO = "application/vnd.ms-excel.sheet.binary.macroenabled.12";
+ String MIMETYPE_OPENXML_PRESENTATION = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
+ String MIMETYPE_OPENXML_PRESENTATION_MACRO = "application/vnd.ms-powerpoint.presentation.macroenabled.12";
+ String MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW = "application/vnd.openxmlformats-officedocument.presentationml.slideshow";
+ String MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW_MACRO = "application/vnd.ms-powerpoint.slideshow.macroenabled.12";
+ String MIMETYPE_OPENXML_PRESENTATION_TEMPLATE = "application/vnd.openxmlformats-officedocument.presentationml.template";
+ String MIMETYPE_OPENXML_PRESENTATION_TEMPLATE_MACRO = "application/vnd.ms-powerpoint.template.macroenabled.12";
+ String MIMETYPE_OPENXML_PRESENTATION_ADDIN = "application/vnd.ms-powerpoint.addin.macroenabled.12";
+ String MIMETYPE_OPENXML_PRESENTATION_SLIDE = "application/vnd.openxmlformats-officedocument.presentationml.slide";
+ String MIMETYPE_OPENXML_PRESENTATION_SLIDE_MACRO = "application/vnd.ms-powerpoint.slide.macroenabled.12";
+ String MIMETYPE_STAROFFICE5_DRAW = "application/vnd.stardivision.draw";
+ String MIMETYPE_STAROFFICE5_CALC = "application/vnd.stardivision.calc";
+ String MIMETYPE_STAROFFICE5_IMPRESS = "application/vnd.stardivision.impress";
+ String MIMETYPE_STAROFFICE5_IMPRESS_PACKED = "application/vnd.stardivision.impress-packed";
+ String MIMETYPE_STAROFFICE5_CHART = "application/vnd.stardivision.chart";
+ String MIMETYPE_STAROFFICE5_WRITER = "application/vnd.stardivision.writer";
+ String MIMETYPE_STAROFFICE5_WRITER_GLOBAL = "application/vnd.stardivision.writer-global";
+ String MIMETYPE_STAROFFICE5_MATH = "application/vnd.stardivision.math";
+ String MIMETYPE_IWORK_KEYNOTE = "application/vnd.apple.keynote";
+ String MIMETYPE_IWORK_NUMBERS = "application/vnd.apple.numbers";
+ String MIMETYPE_IWORK_PAGES = "application/vnd.apple.pages";
+ String MIMETYPE_APPLEFILE = "application/applefile";
+ String MIMETYPE_WORDPERFECT = "application/wordperfect";
+ String MIMETYPE_MP3 = "audio/mpeg";
+ String MIMETYPE_AUDIO_MP4 = "audio/mp4";
+ String MIMETYPE_VORBIS = "audio/vorbis";
+ String MIMETYPE_FLAC = "audio/x-flac";
+ String MIMETYPE_ACP = "application/acp";
+}
diff --git a/pom.xml b/pom.xml
index 8cb657b7..6648c256 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,8 +21,6 @@
latest
2.0.16
- 7.17
- 8.39
3.0.1.1
${project.version}
1.0.2.3
@@ -50,31 +48,11 @@
-
- org.alfresco
- alfresco-core
- ${dependency.alfresco-core.version}
-
-
-
- dom4j
- dom4j
-
-
-
org.dom4j
dom4j
2.1.1
-
- org.alfresco
- alfresco-data-model
- ${dependency.alfresco-data-model.version}
-
org.alfresco
alfresco-jodconverter-core