diff --git a/api/src/main/java/edu/cornell/library/scholars/webapp/controller/api/distribute/decorator/JavaScriptTransformDistributor.java b/api/src/main/java/edu/cornell/library/scholars/webapp/controller/api/distribute/decorator/JavaScriptTransformDistributor.java
deleted file mode 100644
index 5249681c7c..0000000000
--- a/api/src/main/java/edu/cornell/library/scholars/webapp/controller/api/distribute/decorator/JavaScriptTransformDistributor.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.library.scholars.webapp.controller.api.distribute.decorator;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.script.Invocable;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-import javax.servlet.ServletContext;
-
-import edu.cornell.library.scholars.webapp.controller.api.distribute.AbstractDataDistributor;
-import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributor;
-import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributorContext;
-import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
-import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Wrap a data distributor with a JavaScript function that will transform its
- * output.
- *
- *
- *
- * The child distributor might produce any arbitrary output. The JavaScript
- * function must be written to accept that output as a String and return a
- * String containing the transformed output.
- *
- *
- *
- * For example, the function might replace all occurences of a namespace with a
- * different namespace, like this:
- *
- *
- * function transform(rawData) {
- * return rawData.split("http://first/").join("http://second/");
- * }
- *
- *
- * The JavaScript method must be named 'transform', must accept a String as
- * argument, and must return a String as result.
- *
- *
- *
- * The JavaScript execution environment will include a global variable named
- * 'logger'. This is a binding of an org.apache.commons.logging.Log object, and
- * can be used to write to the VIVO log file.
- *
- *
- *
- * Note: this decorator is only scalable to a limited extent, since the
- * JavaScript function works with Strings instead of Streams.
- */
-public class JavaScriptTransformDistributor extends AbstractDataDistributor {
- private static final Log log = LogFactory
- .getLog(JavaScriptTransformDistributor.class);
-
- /** The content type to attach to the file. */
- private String contentType;
- private String script;
- private DataDistributor child;
- private List supportingScriptPaths = new ArrayList<>();
-
- @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#contentType", minOccurs = 1, maxOccurs = 1)
- public void setContentType(String cType) {
- contentType = cType;
- }
-
- @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#script", minOccurs = 1, maxOccurs = 1)
- public void setScript(String scriptIn) {
- script = scriptIn;
- }
-
- @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#child", minOccurs = 1, maxOccurs = 1)
- public void setChild(DataDistributor c) {
- child = c;
- }
-
- @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#supportingScript")
- public void addScriptPath(String path) {
- supportingScriptPaths.add(path);
- }
-
- @Override
- public String getContentType() throws DataDistributorException {
- return contentType;
- }
-
- @Override
- public void init(DataDistributorContext ddc)
- throws DataDistributorException {
- super.init(ddc);
- child.init(ddc);
- }
-
- /**
- */
- @Override
- public void writeOutput(OutputStream output)
- throws DataDistributorException {
- ScriptEngine engine = createScriptEngine();
- addLoggerToEngine(engine);
- loadSupportingScripts(engine);
- loadMainScript(engine);
-
- writeTransformedOutput(output,
- runTransformFunction(engine, runChildDistributor()));
- }
-
- private ScriptEngine createScriptEngine() {
- return new ScriptEngineManager().getEngineByName("nashorn");
- }
-
- private void addLoggerToEngine(ScriptEngine engine) {
- String loggerName = this.getClass().getName() + "." + actionName;
- Log jsLogger = LogFactory.getLog(loggerName);
- engine.put("logger", jsLogger);
- }
-
- private void loadSupportingScripts(ScriptEngine engine)
- throws DataDistributorException {
- log.debug("loading supporting scripts");
- for (String path : supportingScriptPaths) {
- loadSupportingScript(engine, path);
- log.debug("loaded supporting script: " + path);
- }
- }
-
- private void loadSupportingScript(ScriptEngine engine, String path)
- throws DataDistributorException {
- ServletContext ctx = ApplicationUtils.instance().getServletContext();
-
- InputStream resource = ctx.getResourceAsStream(path);
- if (resource == null) {
- throw new DataDistributorException(
- "Can't locate script resource for '" + path + "'");
- }
-
- try {
- engine.eval(new InputStreamReader(resource));
- } catch (ScriptException e) {
- throw new DataDistributorException(
- "Script at '" + path + "' contains syntax errors.", e);
- }
- }
-
- private void loadMainScript(ScriptEngine engine)
- throws DataDistributorException {
- try {
- engine.eval(script);
- } catch (ScriptException e) {
- throw new DataDistributorException("Script contains syntax errors.",
- e);
- }
- }
-
- private String runChildDistributor() throws DataDistributorException {
- ByteArrayOutputStream childOut = new ByteArrayOutputStream();
- try {
- child.writeOutput(childOut);
- log.debug("ran child distributor");
- } catch (Exception e) {
- throw new DataDistributorException(
- "Child distributor threw an exception", e);
- }
- try {
- return childOut.toString("UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("What? No UTF-8 Charset?", e);
- }
- }
-
- private String runTransformFunction(ScriptEngine engine, String childOutput)
- throws DataDistributorException {
- try {
- Invocable invocable = (Invocable) engine;
- Object result = invocable.invokeFunction("transform", childOutput);
- log.debug("ran transform function");
-
- if (result instanceof String) {
- return (String) result;
- } else {
- throw new ActionFailedException(
- "transform function must return a String");
- }
- } catch (NoSuchMethodException e) {
- throw new DataDistributorException(
- "Script must have a transform() function.", e);
- } catch (ScriptException e) {
- throw new DataDistributorException("Script contains syntax errors.",
- e);
- }
- }
-
- private void writeTransformedOutput(OutputStream output, String transformed)
- throws DataDistributorException {
- try {
- output.write(transformed.getBytes("UTF-8"));
- } catch (IOException e) {
- throw new DataDistributorException(e);
- }
- }
-
- @Override
- public void close() throws DataDistributorException {
- child.close();
- }
-
-}
diff --git a/api/src/main/java/edu/cornell/library/scholars/webapp/controller/api/distribute/examples/HelloDistributor.java b/api/src/main/java/edu/cornell/library/scholars/webapp/controller/api/distribute/examples/HelloDistributor.java
deleted file mode 100644
index 454cd82a13..0000000000
--- a/api/src/main/java/edu/cornell/library/scholars/webapp/controller/api/distribute/examples/HelloDistributor.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.library.scholars.webapp.controller.api.distribute.examples;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import edu.cornell.library.scholars.webapp.controller.api.distribute.AbstractDataDistributor;
-import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributorContext;
-
-/**
- * A simple example of a data distributor. It sends a greeting.
- */
-public class HelloDistributor extends AbstractDataDistributor {
-
- private static final Object NAME_PARAMETER_KEY = "name";
-
- /**
- * The instance is created to service one HTTP request, and init() is
- * called.
- *
- * The DataDistributorContext provides access to the request parameters, and
- * the triple-store connections.
- */
- @Override
- public void init(DataDistributorContext ddc)
- throws DataDistributorException {
- super.init(ddc);
- }
-
- /**
- * For this distributor, the browser should treat the output as simple text.
- */
- @Override
- public String getContentType() throws DataDistributorException {
- return "text/plain";
- }
-
- /**
- * The text written to the OutputStream will become the body of the HTTP
- * response.
- *
- * This will only be called once for a given instance.
- */
- @Override
- public void writeOutput(OutputStream output)
- throws DataDistributorException {
- try {
- if (parameters.containsKey(NAME_PARAMETER_KEY)) {
- output.write(String
- .format("Hello, %s!",
- parameters.get(NAME_PARAMETER_KEY)[0])
- .getBytes());
- } else {
- output.write("Hello, World!".getBytes());
- }
- } catch (IOException e) {
- throw new ActionFailedException(e);
- }
- }
-
- /**
- * Release any resources. In this case, none.
- *
- * Garbage collection is uncertain. On the other hand, you can be confident
- * that this will be called in a timely manner.
- */
- @Override
- public void close() throws DataDistributorException {
- // Nothing to do.
- }
-
-}
diff --git a/api/src/test/java/edu/cornell/library/scholars/webapp/controller/api/distribute/decorator/JavaScriptTransformDistributorTest.java b/api/src/test/java/edu/cornell/library/scholars/webapp/controller/api/distribute/decorator/JavaScriptTransformDistributorTest.java
deleted file mode 100644
index 64a2c4618b..0000000000
--- a/api/src/test/java/edu/cornell/library/scholars/webapp/controller/api/distribute/decorator/JavaScriptTransformDistributorTest.java
+++ /dev/null
@@ -1,422 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.library.scholars.webapp.controller.api.distribute.decorator;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-
-import javax.script.ScriptException;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import edu.cornell.library.scholars.webapp.controller.api.distribute.AbstractDataDistributor;
-import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributor.DataDistributorException;
-import edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributorContext;
-import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
-import org.apache.log4j.ConsoleAppender;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.apache.log4j.PatternLayout;
-import org.junit.Before;
-import org.junit.Test;
-import stubs.edu.cornell.library.scholars.webapp.controller.api.distribute.DataDistributorContextStub;
-import stubs.edu.cornell.mannlib.vitro.webapp.modules.ApplicationStub;
-import stubs.javax.servlet.ServletContextStub;
-
-/**
- * Test the basic functions of JavaScriptTransformDistributor.
- *
- * The simple transform just returns a hard-coded String.
- *
- * The full script accepts a JSON string, parses it, substitutes a value, and
- * returns the stringified result.
- *
- * The multi-script requires two additional scripts in order to assemble a
- * hard-coded String.
- */
-public class JavaScriptTransformDistributorTest extends AbstractTestClass {
- private static final String ACTION_NAME = "tester";
- private static final String JAVASCRIPT_TYPE = "text/javascript";
- private static final String TEXT_TYPE = "text/plain";
-
- private static final String BAD_SYNTAX_SCRIPT = "" //
- + "function transform( {}";
-
- private static final String WRONG_FUNCTION_SCRIPT = "" //
- + "function notTransform() { \n" //
- + " return 'true'; \n" //
- + "}";
-
- private static final String WRONG_RETURN_TYPE_SCRIPT = "" //
- + "function transform() { \n" //
- + " return 3; \n" //
- + "}";
-
- private static final String SIMPLE_SCRIPT = "" //
- + "function transform() { \n" //
- + " return 'true'; \n" //
- + "}";
-
- private static final String SIMPLE_EXPECTED_RESULT = "true";
-
- private static final String ECHO_SCRIPT = "" //
- + "function transform(data) { \n" //
- + " return data; \n" //
- + "}";
-
- private static final String UNICODE_STRING = "Lévesque";
-
- private static final String FULL_SCRIPT = "" //
- + "function transform(data) { \n" //
- + " var initial = JSON.parse(data); \n" //
- + " var result = {}; \n" //
- + " Object.keys(initial).forEach(populate); \n" //
- + " return JSON.stringify(result); \n" //
- + "\n" //
- + " function populate(key) { \n" //
- + " result[key] = (initial[key] == 'initial') ? \n" //
- + " 'transformed' : initial[key]; \n" //
- + " } \n" //
- + "}";
-
- private static final String TRANSFORMED_STRUCTURE = "{ 'a': 'transformed', 'b': 'constant' }"
- .replace('\'', '\"');
-
- private static final String INITIAL_STRUCTURE = "{ 'a': 'initial', 'b': 'constant' }"
- .replace('\'', '"');
-
- private static final String PATH_TO_MISSING_SCRIPT = "/no/script/here";
- private static final String PATH_TO_BAD_SYNTAX_SCRIPT = "/bad/syntax/script.js";
-
- private static final String PATH_TO_SUPPORTING_SCRIPT_1 = "/support/script1.js";
- private static final String PATH_TO_SUPPORTING_SCRIPT_2 = "/support/script2.js";
-
- private static final String SUPPORTING_SCRIPT_1 = "" //
- + "function one() { \n" //
- + " return '1'; \n" //
- + "}";
- private static final String SUPPORTING_SCRIPT_2 = "" //
- + "function two() { \n" //
- + " return '2'; \n" //
- + "}";
- private static final String SUPPORTED_SCRIPT = "" //
- + "function transform() { \n" //
- + " return one() + ' ' + two() + ' 3'; \n" //
- + "}";
- private static final String SUPPORTED_EXPECTED_RESULT = "1 2 3";
-
- private static final String LOGGING_SCRIPT = "" //
- + "function transform() { \n" //
- + " logger.debug('debug message'); \n" //
- + " logger.info('info message'); \n" //
- + " logger.warn('warn message'); \n" //
- + " logger.error('error message'); \n" //
- + " return ''; \n" //
- + "}";
- private static final String LOGGER_NAME = JavaScriptTransformDistributor.class
- .getName() + "." + ACTION_NAME;
-
- public JavaScriptTransformDistributor transformer;
- public TestDistributor child;
- public DataDistributorContext ddc;
- public ByteArrayOutputStream outputStream;
- public String logOutput;
-
- @Before
- public void setup() {
- ServletContextStub ctx = new ServletContextStub();
- ctx.setMockResource(PATH_TO_BAD_SYNTAX_SCRIPT, BAD_SYNTAX_SCRIPT);
- ctx.setMockResource(PATH_TO_SUPPORTING_SCRIPT_1, SUPPORTING_SCRIPT_1);
- ctx.setMockResource(PATH_TO_SUPPORTING_SCRIPT_2, SUPPORTING_SCRIPT_2);
- ApplicationStub.setup(ctx, null);
-
- ddc = new DataDistributorContextStub(null);
- outputStream = new ByteArrayOutputStream();
-
- transformer = new JavaScriptTransformDistributor();
- transformer.setContentType(JAVASCRIPT_TYPE);
- transformer.setActionName(ACTION_NAME);
- }
-
- // ----------------------------------------------------------------------
- // Basic tests
- // ----------------------------------------------------------------------
-
- @Test
- public void scriptSuntaxError_throwsException()
- throws DataDistributorException {
- expectException(DataDistributorException.class, "syntax",
- ScriptException.class, "but found");
- transformAndCheck("", BAD_SYNTAX_SCRIPT, "");
- }
-
- @Test
- public void noTransformFunction_throwsException()
- throws DataDistributorException {
- expectException(DataDistributorException.class, "must have",
- NoSuchMethodException.class, "transform");
- transformAndCheck("", WRONG_FUNCTION_SCRIPT, "");
- }
-
- @Test
- public void childThrowsException_throwsException()
- throws DataDistributorException {
- expectException(DataDistributorException.class, "Child",
- DataDistributorException.class, "forced");
- child = new TestDistributor(JAVASCRIPT_TYPE, "", true);
- transformAndCheck(SIMPLE_SCRIPT, "");
- }
-
- @Test
- public void wrongReturnType_throwsException()
- throws DataDistributorException {
- expectException(DataDistributorException.class, "must return a String");
- transformAndCheck("", WRONG_RETURN_TYPE_SCRIPT, "");
- }
-
- @Test
- public void mostBasicTransform() throws DataDistributorException {
- transformAndCheck("", SIMPLE_SCRIPT, SIMPLE_EXPECTED_RESULT);
- }
-
- @Test
- public void parseTransformAndStringify() throws DataDistributorException {
- transformAndCheck(INITIAL_STRUCTURE, FULL_SCRIPT,
- TRANSFORMED_STRUCTURE);
- }
-
- /**
- * This test is intended to check whether Unicode is handled properly
- * regardless of the system's default file encoding.
- *
- * However there might be failures that only show up if the default value of
- * the system property file.encoding is not UTF-8.
- *
- * The commented code is a hacky way of ensuring that the file encoding is
- * not UTF-8, but in Java 9 or later, it will cause warning messages.
- *
- * So we have a test that might pass in some environments and fail in
- * others.
- */
- @Test
- public void unicodeCharactersArePreserved()
- throws DataDistributorException, UnsupportedEncodingException {
- // try {
- // System.setProperty("file.encoding", "ANSI_X3.4-1968");
- // Field charset = Charset.class.getDeclaredField("defaultCharset");
- // charset.setAccessible(true);
- // charset.set(null, null);
- // } catch (Exception e) {
- // throw new RuntimeException(e);
- // }
-
- child = new TestDistributor(TEXT_TYPE, UNICODE_STRING);
- runTransformer(ECHO_SCRIPT);
- assertEquals(UNICODE_STRING,
- new String(outputStream.toByteArray(), "UTF-8"));
- }
-
- // ----------------------------------------------------------------------
- // Tests with additional scripts
- // ----------------------------------------------------------------------
-
- @Test
- public void additionalScriptNotFound_throwsException()
- throws DataDistributorException {
- expectException(DataDistributorException.class, "Can't locate");
- addScripts(PATH_TO_MISSING_SCRIPT);
- transformAndCheck("", SIMPLE_SCRIPT, "");
- }
-
- @Test
- public void additionalScriptSyntaxError_throwsException()
- throws DataDistributorException {
- expectException(DataDistributorException.class,
- PATH_TO_BAD_SYNTAX_SCRIPT, ScriptException.class, "but found");
- addScripts(PATH_TO_BAD_SYNTAX_SCRIPT);
- transformAndCheck("", SIMPLE_SCRIPT, "");
- }
-
- @Test
- public void useAdditionalScripts() throws DataDistributorException {
- addScripts(PATH_TO_SUPPORTING_SCRIPT_1, PATH_TO_SUPPORTING_SCRIPT_2);
- transformAndCheck("", SUPPORTED_SCRIPT, SUPPORTED_EXPECTED_RESULT);
- }
-
- // ----------------------------------------------------------------------
- // Tests with logging
- // ----------------------------------------------------------------------
-
- @Test
- public void loggingAtDebug_producedFourMessages()
- throws DataDistributorException {
- transformAndCountLogLines("", LOGGING_SCRIPT, Level.DEBUG, 4);
- }
-
- @Test
- public void loggingAtInfo_producesThreeMessages()
- throws DataDistributorException {
- transformAndCountLogLines("", LOGGING_SCRIPT, Level.INFO, 3);
- }
-
- @Test
- public void loggingAtWarn_producesTwoMessages()
- throws DataDistributorException {
- transformAndCountLogLines("", LOGGING_SCRIPT, Level.WARN, 2);
- }
-
- @Test
- public void loggingAtError_producesOneMessage()
- throws DataDistributorException {
- transformAndCountLogLines("", LOGGING_SCRIPT, Level.ERROR, 1);
- }
-
- @Test
- public void loggingAtOff_producesNoMessages()
- throws DataDistributorException {
- transformAndCountLogLines("", LOGGING_SCRIPT, Level.OFF, 0);
- }
-
- // ----------------------------------------------------------------------
- // Helper methods
- // ----------------------------------------------------------------------
-
- private void transformAndCheck(String initial, String script,
- String expected) throws DataDistributorException {
- child = new TestDistributor(JAVASCRIPT_TYPE, initial);
- transformAndCheck(script, expected);
- }
-
- private void transformAndCheck(String script, String expected)
- throws DataDistributorException {
- runTransformer(script);
- assertEquivalentJson(expected, new String(outputStream.toByteArray()));
- }
-
- private void transformAndCountLogLines(String initial, String script,
- Level level, int expectedCount) throws DataDistributorException {
- setLoggerLevel(LOGGER_NAME, level);
- logOutput = runTransformer(initial, script);
- assertNumberOfLines(logOutput, expectedCount);
- }
-
- private String runTransformer(String initial, String script)
- throws DataDistributorException {
- child = new TestDistributor(JAVASCRIPT_TYPE, initial);
- return runTransformer(script);
- }
-
- private String runTransformer(String script)
- throws DataDistributorException {
- try (Writer logCapture = new StringWriter()) {
- captureLogOutput(LOGGER_NAME, logCapture, true);
-
- transformer.setScript(script);
- transformer.setChild(child);
- transformer.init(ddc);
- transformer.writeOutput(outputStream);
-
- return logCapture.toString();
- } catch (IOException e) {
- throw new DataDistributorException(e);
- }
- }
-
- private void addScripts(String... paths) {
- for (String path : paths) {
- transformer.addScriptPath(path);
- }
- }
-
- private void assertEquivalentJson(String expected, String actual) {
- try {
- JsonNode expectedNode = new ObjectMapper().readTree(expected);
- JsonNode actualNode = new ObjectMapper().readTree(actual);
- assertEquals(expectedNode, actualNode);
- } catch (IOException e) {
- throw new RuntimeException("Failed to compare JSON", e);
- }
- }
-
- private void assertNumberOfLines(String s, int expected) {
- int actual = s.isEmpty() ? 0 : s.split("[\\n\\r]").length;
- assertEquals("number of lines", expected, actual);
- }
-
- /**
- * AbstractTextClasss has this method, but is not overloaded to accept a
- * String for the logger category.
- *
- * Capture the log for this class to this Writer. Choose whether or not to
- * suppress it from the console.
- */
- protected void captureLogOutput(String category, Writer writer,
- boolean suppress) {
- PatternLayout layout = new PatternLayout("%p %m%n");
-
- ConsoleAppender appender = new ConsoleAppender();
- appender.setWriter(writer);
- appender.setLayout(layout);
-
- Logger logger = Logger.getLogger(category);
- logger.removeAllAppenders();
- logger.setAdditivity(!suppress);
- logger.addAppender(appender);
- }
-
- // ----------------------------------------------------------------------
- // Helper classes
- // ----------------------------------------------------------------------
-
- /**
- * Just echoes a given string with a given contentType, or throws an
- * Exception, if you prefer.
- */
- private static class TestDistributor extends AbstractDataDistributor {
- private String contentType;
- private String outputString;
- private boolean throwException;
-
- public TestDistributor(String contentType, String outputString) {
- this(contentType, outputString, false);
- }
-
- public TestDistributor(String contentType, String outputString,
- boolean throwException) {
- this.contentType = contentType;
- this.outputString = outputString;
- this.throwException = throwException;
- }
-
- @Override
- public String getContentType() throws DataDistributorException {
- return contentType;
- }
-
- @Override
- public void writeOutput(OutputStream output)
- throws DataDistributorException {
- try {
- if (throwException) {
- throw new DataDistributorException("forced exception.");
- }
- output.write(outputString.getBytes("UTF-8"));
- } catch (IOException e) {
- throw new RuntimeException();
- }
- }
-
- @Override
- public void close() throws DataDistributorException {
- // Nothing to do.
- }
-
- }
-
-}