You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/04/01 19:47:18 UTC
[3/4] incubator-freemarker-online-tester git commit: initial commit
initial commit
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/commit/abb26297
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/tree/abb26297
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/diff/abb26297
Branch: refs/heads/master
Commit: abb26297cab30cafcaf57b7d1d4a5131d782f503
Parents: 0fb645b
Author: nirfeldman <ni...@kenshoo.com>
Authored: Mon Mar 20 18:37:10 2017 +0200
Committer: nirfeldman <ni...@kenshoo.com>
Committed: Mon Mar 20 18:37:10 2017 +0200
----------------------------------------------------------------------
README.md | 23 +
build.gradle | 130 ++++
dependencies.gradle | 47 ++
gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49875 bytes
gradle/wrapper/gradle-wrapper.properties | 6 +
gradlew | 164 +++++
gradlew.bat | 90 +++
intellij.gradle | 37 ++
settings.gradle | 1 +
.../dropwizard/ApplicationStartup.java | 43 ++
.../healthchecks/MyProjectHealthCheck.java | 38 ++
.../com/kenshoo/freemarker/model/ErrorCode.java | 24 +
.../kenshoo/freemarker/model/ErrorResponse.java | 29 +
.../freemarker/model/ExecuteRequest.java | 77 +++
.../freemarker/model/ExecuteResourceField.java | 56 ++
.../model/ExecuteResourceProblem.java | 50 ++
.../freemarker/model/ExecuteResponse.java | 62 ++
.../freemarker/model/SelectionOption.java | 85 +++
.../FreeMarkerOnlineExecuteResource.java | 219 +++++++
.../resources/FreeMarkerOnlineResource.java | 65 ++
.../services/AllowedSettingValuesMaps.java | 112 ++++
.../freemarker/services/FreeMarkerService.java | 366 +++++++++++
.../services/FreeMarkerServiceException.java | 34 +
.../services/FreeMarkerServiceResponse.java | 70 +++
.../freemarker/util/DataModelParser.java | 264 ++++++++
.../util/DataModelParsingException.java | 35 ++
.../kenshoo/freemarker/util/ExceptionUtils.java | 49 ++
.../util/LengthLimitExceededException.java | 31 +
.../freemarker/util/LengthLimitedWriter.java | 83 +++
.../freemarker/view/FreeMarkerOnlineView.java | 156 +++++
.../core/FreeMarkerInternalsAccessor.java | 61 ++
src/main/resources/assets/css/main.css | 114 ++++
src/main/resources/assets/js/autosize.min.js | 6 +
.../resources/assets/js/jquery.autosize.min.js | 6 +
src/main/resources/assets/js/jquery.blockUI.js | 620 +++++++++++++++++++
src/main/resources/assets/js/script.js | 97 +++
src/main/resources/banner.txt | 10 +
src/main/resources/freemarker-online.yml | 22 +
src/main/resources/spring/bootstrap-context.xml | 19 +
src/main/resources/view/freemarker-online.ftl | 129 ++++
src/main/resources/view/utils.ftl | 13 +
.../platform/DropWizardServiceTest.java | 41 ++
.../platform/YamlPropertiesPersister.java | 93 +++
.../FreeMarkerOnlineExecuteResourceTest.java | 159 +++++
.../resources/FreeMarkerOnlineResourceTest.java | 72 +++
.../FreeMarkerServiceResponseBuilderTest.java | 63 ++
.../services/FreeMarkerServiceTest.java | 310 ++++++++++
.../freemarker/util/DataModelParserTest.java | 277 +++++++++
.../util/LengthLimitedWriterTest.java | 73 +++
.../view/FreeMarkerOnlineViewTest.java | 70 +++
src/test/resources/spring/test-context.xml | 7 +
51 files changed, 4708 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c33aea5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+freemarker-online
+====================
+
+freemarker-online is a tool for any freemarker users to evalaute their freemarker expressions with an input.
+You can use it for enabling non developers to develop freemarker templates and evaluate it's values without the need for a developement enviornment.
+For a deployed version of this tool you can visit http://freemarker-online.kenshoo.com/
+
+Development Instuctions
+------------------------
+* Clone the repository to a local directory
+* Currently the project has an old dependency, "com.berico:fallwizard:1.1.1", which can't be found in any public repositories I know of. Thus, you have to Maven-install it locally:
+ 1. Clone https://github.com/Berico-Technologies/Fallwizard.git
+ 2. Check out this old version: 7ed7803496
+ 3. `mvn install` it
+* Run "./gradlew build" from the cloned directory (use JDK 7, not 8!)
+* If you want to run it using IDEA run "./gradlew cleanidea idea" - this will generate the IDEA project for you.
+* For running the software from a command line, build `fatJar` (not `jar`) and then just hit "java -jar build/libs/freemarker-online-0.1.undef.jar server src/main/resources/freemarker-online.yml"
+
+
+License
+-------
+
+FreeMarker-Online is licensed under the Apache License, Version 2.0. See LICENSE.txt for details.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..890f3c2
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,130 @@
+group = 'com.kenshoo.freemarker'
+project.projectName = "freemarker-online"
+def BUILD_NUMBER = project.hasProperty('BUILD_NUMBER') ? "$BUILD_NUMBER" : 'undef'
+project.version = "0.1.$BUILD_NUMBER"
+project.yml = "$rootDir/src/main/resources/${projectName}.yml"
+project.jarName = "$rootDir/build/libs/${projectName}-${project.version}.jar"
+
+apply from: "intellij.gradle"
+apply from: "${rootDir}/dependencies.gradle"
+apply plugin: 'maven'
+apply plugin: 'java'
+apply plugin: 'jacoco'
+apply plugin: 'project-report'
+apply plugin: 'fatjar'
+apply plugin: 'fpm-packaging'
+
+// Because Spring 3 doesn't officially support Java 8, and indeed asm fails with it:
+// Must be after `apply plugin: 'java'`!
+project.sourceCompatibility = "1.7"
+project.targetCompatibility = "1.7"
+
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'eu.appsatori:gradle-fatjar-plugin:0.2-rc1'
+ classpath 'com.kenshoo:gradle-fpm:+'
+ }
+}
+
+repositories {
+ mavenCentral()
+
+ // For com.berico:fallwizard:1.1.1 that must be locally installed; see README.md!
+ mavenLocal()
+}
+
+dependencies {
+ compile libraries.dropwizard
+ compile libraries.dropwizrd_views
+ compile libraries.springCore
+ compile libraries.springContext
+ compile libraries.springContextSupport
+ compile libraries.springExpression
+ compile libraries.springSecurityCore
+ compile libraries.springTransaction
+ compile libraries.springBeans
+ compile libraries.fallwizard
+
+ testCompile libraries.dropwizard_testing
+ testCompile libraries.springTest
+ testCompile libraries.junit
+ testCompile libraries.mockito
+ testCompile libraries.hamcrest
+
+ compile libraries.freemarker
+ compile libraries.jackson_databind
+ compile libraries.commonLangs
+ compile libraries.findBugs
+
+ testCompile libraries.selenium_java
+ testCompile libraries.jersey_grrizle
+ testCompile libraries.jersey_client
+ testCompile libraries.springJersey
+}
+
+compileJava {
+ options.compilerArgs << "-Werror"
+}
+compileTestJava {
+ options.compilerArgs << "-Werror"
+}
+
+task wrapper(type: Wrapper) {
+ gradleVersion = '1.10'
+}
+
+fatJar {
+ exclude "META-INF/*.SF"
+ exclude "META-INF/*.DSA"
+ exclude "META-INF/*.RSA"
+ manifest {
+ attributes 'Main-Class': 'com.kenshoo.freemarker.dropwizard.ApplicationStartup'
+ attributes 'Implementation-Version': "$version"
+ }
+}
+
+// this merges the spring files into META-INF properly
+fatJarPrepareFiles {
+ include 'META-INF/spring.handlers'
+ include 'META-INF/spring.schemas'
+}
+
+// wrap jar, upstart and yml into deb package:
+
+def stagingDir = new File(project.buildDir, "staging")
+
+task stageArtifacts(type: Copy) {
+ stagingDir.mkdir()
+ // place yml file and jar under staging dir
+ into new File(stagingDir, "/opt/${projectName}")
+ from "build/resources/main/${projectName}.yml"
+ from 'build/libs'
+}
+
+task stageStartScript << {
+ new File(stagingDir, "opt/${projectName}/start").withWriter("UTF-8") {
+ it.println "#!/usr/bin/env sh\n" +
+ " set -e\n" +
+ " exec java \$* -jar ${project.name}-${project.version}.jar server ${project.name}.yml"
+ }
+}
+
+task stageFiles(dependsOn: [stageArtifacts, stageStartScript]) << {
+ println "staging debian files"
+}
+
+// configure plugin to package the staging dir we've
+// just created, and to declare java-7 dependency
+packaging {
+ dependencies = ['openjdk-7-jre']
+ baseDir = stagingDir
+}
+
+
+fatJar.dependsOn jar
+build.dependsOn fatJar
+stageFiles.dependsOn build
+debian.dependsOn stageFiles
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/dependencies.gradle
----------------------------------------------------------------------
diff --git a/dependencies.gradle b/dependencies.gradle
new file mode 100644
index 0000000..fc4bc9c
--- /dev/null
+++ b/dependencies.gradle
@@ -0,0 +1,47 @@
+project.ext.set("libraries", "")
+ext.dw_version = "0.6.2";
+ext.spring_version = "3.2.2.RELEASE";
+ext.jackson_version = "2.5.1";
+
+project.libraries = [
+ dropwizard: "com.yammer.dropwizard:dropwizard-core:$dw_version",
+ dropwizard_client: "com.yammer.dropwizard:dropwizard-client:$dw_version",
+ dropwizrd_views: "com.yammer.dropwizard:dropwizard-views:$dw_version",
+ guava: 'com.google.guava:guava:13.0.1',
+ jersey_core: 'com.sun.jersey:jersey-core:1.1.4.1',
+
+ //Spring
+ springCore: "org.springframework:spring-core:$spring_version",
+ springContext: "org.springframework:spring-context:$spring_version",
+ springContextSupport: "org.springframework:spring-context-support:$spring_version",
+ springExpression: "org.springframework:spring-expression:$spring_version",
+ springTransaction: "org.springframework:spring-tx:$spring_version",
+ springBeans: "org.springframework:spring-beans:$spring_version",
+ springJersey: "com.sun.jersey.contribs:jersey-spring:1.7",
+
+ // Security
+ // Spring and Spring Security support for dropwizard
+ fallwizard: "com.berico:fallwizard:1.1.1",
+ // adheres to fallwizard version of spring security
+ springSecurityCore: "org.springframework.security:spring-security-core:3.1.4.RELEASE",
+ dropwizardAuth: "com.yammer.dropwizard:dropwizard-auth:$dw_version",
+
+ // Spring test
+ springTest: "org.springframework:spring-test:$spring_version",
+
+ //Test libs
+ dropwizard_testing: "com.yammer.dropwizard:dropwizard-testing:$dw_version",
+ junit: 'junit:junit-dep:4.11',
+ mockito: 'org.mockito:mockito-core:1.9.0',
+ hamcrest: 'org.hamcrest:hamcrest-library:1.3',
+ selenium_java: 'org.seleniumhq.selenium:selenium-java:2.41.0',
+ jersey_client: 'com.sun.jersey:jersey-client:1.17.1',
+ jersey_grrizle: 'com.sun.jersey.jersey-test-framework:jersey-test-framework-grizzly:1.17',
+
+ // Others
+ freemarker: 'org.freemarker:freemarker:2.3.24-incubating',
+ jackson_databind: "com.fasterxml.jackson.core:jackson-databind:$jackson_version",
+ commonLangs: "org.apache.commons:commons-lang3:3.3.2",
+ findBugs: "com.google.code.findbugs:annotations:3.0.0"
+
+]
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/gradle/wrapper/gradle-wrapper.jar
----------------------------------------------------------------------
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..a7634b0
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/gradle/wrapper/gradle-wrapper.properties
----------------------------------------------------------------------
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..72f43b6
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Sep 10 11:20:58 IST 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.7-bin.zip
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/gradlew
----------------------------------------------------------------------
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/gradlew.bat
----------------------------------------------------------------------
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/intellij.gradle
----------------------------------------------------------------------
diff --git a/intellij.gradle b/intellij.gradle
new file mode 100644
index 0000000..4486da0
--- /dev/null
+++ b/intellij.gradle
@@ -0,0 +1,37 @@
+apply plugin: 'idea'
+
+idea {
+ project {
+ jdkName = '1.7'
+ }
+}
+
+idea.project.ipr {
+ withXml { provider ->
+ provider.node.component.find { it.@name == 'VcsDirectoryMappings' }.mapping.@vcs = 'Git'
+ }
+}
+
+idea.workspace.iws.withXml { provider ->
+ def runManager = provider.node.component.find { it.@name == 'RunManager' }
+ runManager.attributes().put('selected', "Application.MyProjectStartup")
+ Node list = runManager.list[0]
+ list.attributes().put('size', '1')
+ list.appendNode('item', [index: '0', class: 'java.lang.String', itemvalue: 'MyProjectStartup'])
+ def Application = runManager.appendNode('configuration', [default: 'false', name: "${projectName}", type: 'Application', factoryName: 'Application'])
+ Application.appendNode('extension', [name: 'coverage', enabled: 'false', merge: 'false', runner: 'idea'])
+ Application.appendNode('option', [name: 'MAIN_CLASS_NAME', value: "com.kenshoo.freemarker.dropwizard.ApplicationStartup"])
+ Application.appendNode('option', [name: 'VM_PARAMETERS', value: ""])
+ Application.appendNode('option', [name: 'PROGRAM_PARAMETERS', value: "server src/main/resources/${projectName}.yml"])
+ Application.appendNode('option', [name: 'WORKING_DIRECTORY', value: "$projectDir"])
+ Application.appendNode('option', [name: 'ALTERNATIVE_JRE_PATH_ENABLED', value: "false"])
+ Application.appendNode('option', [name: 'ALTERNATIVE_JRE_PATH', value: ""])
+ Application.appendNode('option', [name: 'ENABLE_SWING_INSPECTOR', value: "false"])
+ Application.appendNode('option', [name: 'ENV_VARIABLES'])
+ Application.appendNode('option', [name: 'PASS_PARENT_ENVS', value: "true"])
+ Application.appendNode('module', [name: "${projectName}"])
+ Application.appendNode('envs')
+ Application.appendNode('RunnerSettings', [RunnerId: 'Run'])
+ Application.appendNode('ConfigurationWrapper', [RunnerId: 'Run'])
+ Application.appendNode('method')
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/settings.gradle
----------------------------------------------------------------------
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..854e7d2
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'freemarker-online'
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/dropwizard/ApplicationStartup.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/dropwizard/ApplicationStartup.java b/src/main/java/com/kenshoo/freemarker/dropwizard/ApplicationStartup.java
new file mode 100644
index 0000000..1808ce2
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/dropwizard/ApplicationStartup.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.dropwizard;
+
+import com.berico.fallwizard.SpringConfiguration;
+import com.berico.fallwizard.SpringService;
+import com.yammer.dropwizard.assets.AssetsBundle;
+import com.yammer.dropwizard.config.Bootstrap;
+import com.yammer.dropwizard.views.ViewBundle;
+
+/**
+ * User: dekely
+ * Date: 3/17/13
+ * Time: 10:39 AM
+ */
+public class ApplicationStartup extends SpringService<SpringConfiguration> {
+
+ public static void main(String[] args) throws Exception {
+ new ApplicationStartup().run(args);
+ }
+
+ @Override
+ public void initialize(Bootstrap<SpringConfiguration> bootstrap) {
+ bootstrap.setName("freemarker-online");
+ bootstrap.addBundle(new ViewBundle());
+ bootstrap.addBundle(new AssetsBundle("/assets/css", "/css"));
+ bootstrap.addBundle(new AssetsBundle("/assets/js", "/js"));
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/healthchecks/MyProjectHealthCheck.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/healthchecks/MyProjectHealthCheck.java b/src/main/java/com/kenshoo/freemarker/healthchecks/MyProjectHealthCheck.java
new file mode 100644
index 0000000..d5b35ba
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/healthchecks/MyProjectHealthCheck.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.healthchecks;
+
+import com.yammer.metrics.core.HealthCheck;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: tzachz
+ * Date: 5/23/13
+ */
+@Component
+public class MyProjectHealthCheck extends HealthCheck {
+
+ // note that this is due to the default spring CTR
+ public MyProjectHealthCheck() {
+ super("MyProjectHealthCheck");
+ }
+
+ @Override
+ protected Result check() throws Exception {
+ return Result.healthy(); // we're always healthy!
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/model/ErrorCode.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/model/ErrorCode.java b/src/main/java/com/kenshoo/freemarker/model/ErrorCode.java
new file mode 100644
index 0000000..1e7f335
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/model/ErrorCode.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.model;
+
+/**
+ * Created by Pradeep on 8/30/2015.
+ */
+public enum ErrorCode {
+ FREEMARKER_SERVICE_TIMEOUT
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/model/ErrorResponse.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/model/ErrorResponse.java b/src/main/java/com/kenshoo/freemarker/model/ErrorResponse.java
new file mode 100644
index 0000000..063779a
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/model/ErrorResponse.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.model;
+
+/**
+ * Created by Pmuruge on 8/30/2015.
+ */
+public class ErrorResponse {
+ private ErrorCode errorCode;
+ private String errorDescription;
+
+ public ErrorResponse(ErrorCode errorCode, String errorDescription) {
+ this.errorCode = errorCode;
+ this.errorDescription = errorDescription;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/model/ExecuteRequest.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/model/ExecuteRequest.java b/src/main/java/com/kenshoo/freemarker/model/ExecuteRequest.java
new file mode 100644
index 0000000..b3c2163
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/model/ExecuteRequest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.model;
+
+/**
+ * Created by Pmuruge on 8/28/2015.
+ */
+public class ExecuteRequest {
+ private String template;
+ private String dataModel;
+ private String outputFormat;
+ private String locale;
+ private String timeZone;
+
+ public ExecuteRequest() {
+ }
+
+ public ExecuteRequest(String template, String dataModel) {
+ this.template = template;
+ this.dataModel = dataModel;
+ }
+
+ public String getDataModel() {
+ return dataModel;
+ }
+
+ public void setDataModel(String dataModel) {
+ this.dataModel = dataModel;
+ }
+
+ public String getTemplate() {
+
+ return template;
+ }
+
+ public void setTemplate(String template) {
+ this.template = template;
+ }
+
+ public String getOutputFormat() {
+ return outputFormat;
+ }
+
+ public void setOutputFormat(String outputFormat) {
+ this.outputFormat = outputFormat;
+ }
+
+ public String getLocale() {
+ return locale;
+ }
+
+ public void setLocale(String locale) {
+ this.locale = locale;
+ }
+
+ public String getTimeZone() {
+ return timeZone;
+ }
+
+ public void setTimeZone(String timeZone) {
+ this.timeZone = timeZone;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/model/ExecuteResourceField.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/model/ExecuteResourceField.java b/src/main/java/com/kenshoo/freemarker/model/ExecuteResourceField.java
new file mode 100644
index 0000000..05701ae
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/model/ExecuteResourceField.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Created by Pmuruge on 8/31/2015.
+ */
+public enum ExecuteResourceField {
+ DATA_MODEL("dataModel"),
+ TEMPLATE("template"),
+ OUTPUT_FORMAT("outputFormat"),
+ LOCALE("locale"),
+ TIME_ZONE("timeZone");
+
+ private final String fieldName;
+
+ private ExecuteResourceField(String filedName) {
+ this.fieldName = filedName;
+ }
+
+ public String toString() {
+ return getFieldName();
+ }
+
+ @JsonValue
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ @JsonCreator
+ public static ExecuteResourceField fromEnumString(String val) {
+ for(ExecuteResourceField field : values()) {
+ if(field.getFieldName().equals(val)) {
+ return field;
+ }
+ }
+ throw new IllegalArgumentException("Invalid string value passed: " + val);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/model/ExecuteResourceProblem.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/model/ExecuteResourceProblem.java b/src/main/java/com/kenshoo/freemarker/model/ExecuteResourceProblem.java
new file mode 100644
index 0000000..4dab2f9
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/model/ExecuteResourceProblem.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.kenshoo.freemarker.model;
+
+public class ExecuteResourceProblem {
+
+ private ExecuteResourceField field;
+ private String message;
+
+ // Needed for JSON unmarshalling
+ public ExecuteResourceProblem() {
+ //
+ }
+
+ public ExecuteResourceProblem(ExecuteResourceField field, String message) {
+ this.field = field;
+ this.message = message;
+ }
+
+ public ExecuteResourceField getField() {
+ return field;
+ }
+
+ public void setField(ExecuteResourceField field) {
+ this.field = field;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/model/ExecuteResponse.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/model/ExecuteResponse.java b/src/main/java/com/kenshoo/freemarker/model/ExecuteResponse.java
new file mode 100644
index 0000000..c366ea5
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/model/ExecuteResponse.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.model;
+
+import java.util.List;
+
+/**
+ * Created by Pmuruge on 8/29/2015.
+ */
+public class ExecuteResponse {
+ private String result;
+ private List<ExecuteResourceProblem> problems;
+ private boolean truncatedResult;
+
+ public ExecuteResponse(String result, List<ExecuteResourceProblem> problems, boolean truncatedResult) {
+ this.result = result;
+ this.problems = problems;
+ this.truncatedResult = truncatedResult;
+ }
+
+ public ExecuteResponse() {
+
+ }
+
+ public List<ExecuteResourceProblem> getProblems() {
+ return problems;
+ }
+
+ public void setProblems(List<ExecuteResourceProblem> problems) {
+ this.problems = problems;
+ }
+
+ public boolean isTruncatedResult() {
+ return truncatedResult;
+ }
+
+ public void setTruncatedResult(boolean truncatedResult) {
+ this.truncatedResult = truncatedResult;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ public void setResult(String result) {
+ this.result = result;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/model/SelectionOption.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/model/SelectionOption.java b/src/main/java/com/kenshoo/freemarker/model/SelectionOption.java
new file mode 100644
index 0000000..f911a22
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/model/SelectionOption.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.kenshoo.freemarker.model;
+
+public class SelectionOption implements Comparable<SelectionOption> {
+
+ private final String value;
+ private final String label;
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public SelectionOption(String value, String label) {
+ this.value = value;
+ this.label = label;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((label == null) ? 0 : label.hashCode());
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SelectionOption other = (SelectionOption) obj;
+ if (label == null) {
+ if (other.label != null) {
+ return false;
+ }
+ } else if (!label.equals(other.label)) {
+ return false;
+ }
+ if (value == null) {
+ if (other.value != null) {
+ return false;
+ }
+ } else if (!value.equals(other.value)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int compareTo(SelectionOption o) {
+ int r = label.compareTo(o.label);
+ if (r != 0) {
+ return r;
+ }
+
+ return value.compareTo(o.value);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineExecuteResource.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineExecuteResource.java b/src/main/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineExecuteResource.java
new file mode 100644
index 0000000..413463f
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineExecuteResource.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.resources;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.RejectedExecutionException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.kenshoo.freemarker.model.ErrorCode;
+import com.kenshoo.freemarker.model.ErrorResponse;
+import com.kenshoo.freemarker.model.ExecuteRequest;
+import com.kenshoo.freemarker.model.ExecuteResourceField;
+import com.kenshoo.freemarker.model.ExecuteResourceProblem;
+import com.kenshoo.freemarker.model.ExecuteResponse;
+import com.kenshoo.freemarker.services.AllowedSettingValuesMaps;
+import com.kenshoo.freemarker.services.FreeMarkerService;
+import com.kenshoo.freemarker.services.FreeMarkerServiceResponse;
+import com.kenshoo.freemarker.util.DataModelParser;
+import com.kenshoo.freemarker.util.DataModelParsingException;
+import com.kenshoo.freemarker.util.ExceptionUtils;
+
+import freemarker.core.OutputFormat;
+
+/**
+ * Created by pradeep on 8/28/2015.
+ */
+@Path("/api/execute")
+@Component
+public class FreeMarkerOnlineExecuteResource {
+ private static final int MAX_TEMPLATE_INPUT_LENGTH = 10000;
+
+ private static final int MAX_DATA_MODEL_INPUT_LENGTH = 10000;
+
+ private static final String MAX_TEMPLATE_INPUT_LENGTH_EXCEEDED_ERROR_MESSAGE
+ = "The template length has exceeded the {0} character limit set for this service.";
+
+ private static final String MAX_DATA_MODEL_INPUT_LENGTH_EXCEEDED_ERROR_MESSAGE
+ = "The data model length has exceeded the {0} character limit set for this service.";
+
+ private static final String UNKNOWN_OUTPUT_FORMAT_ERROR_MESSAGE = "Unknown output format: {0}";
+ private static final String UNKNOWN_LOCALE_ERROR_MESSAGE = "Unknown locale: {0}";
+ private static final String UNKNOWN_TIME_ZONE_ERROR_MESSAGE = "Unknown time zone: {0}";
+
+ private static final String SERVICE_OVERBURDEN_ERROR_MESSAGE
+ = "Sorry, the service is overburden and couldn't handle your request now. Try again later.";
+
+ static final String DATA_MODEL_ERROR_MESSAGE_HEADING = "Failed to parse data model:";
+ static final String DATA_MODEL_ERROR_MESSAGE_FOOTER = "Note: This is NOT a FreeMarker error message. "
+ + "The data model syntax is specific to this online service.";
+
+ @Autowired
+ private FreeMarkerService freeMarkerService;
+
+ @POST
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response formResult(
+ ExecuteRequest req) {
+ ExecuteResponse resp = new ExecuteResponse();
+
+ if (StringUtils.isBlank(req.getTemplate()) && StringUtils.isBlank(req.getDataModel())) {
+ return Response.status(400).entity("Empty Template & data").build();
+ }
+
+ List<ExecuteResourceProblem> problems = new ArrayList<ExecuteResourceProblem>();
+
+ String template = getTemplate(req, problems);
+ Map<String, Object> dataModel = getDataModel(req, problems);
+ OutputFormat outputFormat = getOutputFormat(req, problems);
+ Locale locale = getLocale(req, problems);
+ TimeZone timeZone = getTimeZone(req, problems);
+
+ if (!problems.isEmpty()) {
+ resp.setProblems(problems);
+ return buildFreeMarkerResponse(resp);
+ }
+
+ FreeMarkerServiceResponse freeMarkerServiceResponse;
+ try {
+ freeMarkerServiceResponse = freeMarkerService.calculateTemplateOutput(
+ template, dataModel,
+ outputFormat, locale, timeZone);
+ } catch (RejectedExecutionException e) {
+ String error = SERVICE_OVERBURDEN_ERROR_MESSAGE;
+ return Response.serverError().entity(new ErrorResponse(ErrorCode.FREEMARKER_SERVICE_TIMEOUT, error)).build();
+ }
+ if (!freeMarkerServiceResponse.isSuccesful()){
+ Throwable failureReason = freeMarkerServiceResponse.getFailureReason();
+ String error = ExceptionUtils.getMessageWithCauses(failureReason);
+ problems.add(new ExecuteResourceProblem(ExecuteResourceField.TEMPLATE, error));
+ resp.setProblems(problems);
+ return buildFreeMarkerResponse(resp);
+ }
+
+ String result = freeMarkerServiceResponse.getTemplateOutput();
+ resp.setResult(result);
+ resp.setTruncatedResult(freeMarkerServiceResponse.isTemplateOutputTruncated());
+ return buildFreeMarkerResponse(resp);
+ }
+
+ private String getTemplate(ExecuteRequest req, List<ExecuteResourceProblem> problems) {
+ String template = req.getTemplate();
+
+ if (template.length() > MAX_TEMPLATE_INPUT_LENGTH) {
+ String error = formatMessage(MAX_TEMPLATE_INPUT_LENGTH_EXCEEDED_ERROR_MESSAGE, MAX_TEMPLATE_INPUT_LENGTH);
+ problems.add(new ExecuteResourceProblem(ExecuteResourceField.TEMPLATE, error));
+ return null;
+ }
+
+ return template;
+ }
+
+ private Map<String, Object> getDataModel(ExecuteRequest req, List<ExecuteResourceProblem> problems) {
+ String dataModel = req.getDataModel();
+
+ if (dataModel.length() > MAX_DATA_MODEL_INPUT_LENGTH) {
+ String error = formatMessage(
+ MAX_DATA_MODEL_INPUT_LENGTH_EXCEEDED_ERROR_MESSAGE, MAX_DATA_MODEL_INPUT_LENGTH);
+ problems.add(new ExecuteResourceProblem(ExecuteResourceField.DATA_MODEL, error));
+ return null;
+ }
+
+ try {
+ return DataModelParser.parse(dataModel, freeMarkerService.getFreeMarkerTimeZone());
+ } catch (DataModelParsingException e) {
+ problems.add(new ExecuteResourceProblem(ExecuteResourceField.DATA_MODEL, decorateResultText(e.getMessage())));
+ return null;
+ }
+ }
+
+ private OutputFormat getOutputFormat(ExecuteRequest req, List<ExecuteResourceProblem> problems) {
+ String outputFormatStr = req.getOutputFormat();
+
+ if (StringUtils.isBlank(outputFormatStr)) {
+ return AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT;
+ }
+
+ OutputFormat outputFormat = AllowedSettingValuesMaps.OUTPUT_FORMAT_MAP.get(outputFormatStr);
+ if (outputFormat == null) {
+ problems.add(new ExecuteResourceProblem(
+ ExecuteResourceField.OUTPUT_FORMAT,
+ formatMessage(UNKNOWN_OUTPUT_FORMAT_ERROR_MESSAGE, outputFormatStr)));
+ }
+ return outputFormat;
+ }
+
+ private Locale getLocale(ExecuteRequest req, List<ExecuteResourceProblem> problems) {
+ String localeStr = req.getLocale();
+
+ if (StringUtils.isBlank(localeStr)) {
+ return AllowedSettingValuesMaps.DEFAULT_LOCALE;
+ }
+
+ Locale locale = AllowedSettingValuesMaps.LOCALE_MAP.get(localeStr);
+ if (locale == null) {
+ problems.add(new ExecuteResourceProblem(
+ ExecuteResourceField.LOCALE,
+ formatMessage(UNKNOWN_LOCALE_ERROR_MESSAGE, localeStr)));
+ }
+ return locale;
+ }
+
+ private TimeZone getTimeZone(ExecuteRequest req, List<ExecuteResourceProblem> problems) {
+ String timeZoneStr = req.getTimeZone();
+
+ if (StringUtils.isBlank(timeZoneStr)) {
+ return AllowedSettingValuesMaps.DEFAULT_TIME_ZONE;
+ }
+
+ TimeZone timeZone = AllowedSettingValuesMaps.TIME_ZONE_MAP.get(timeZoneStr);
+ if (timeZone == null) {
+ problems.add(new ExecuteResourceProblem(
+ ExecuteResourceField.TIME_ZONE,
+ formatMessage(UNKNOWN_TIME_ZONE_ERROR_MESSAGE, timeZoneStr)));
+ }
+ return timeZone;
+ }
+
+ private Response buildFreeMarkerResponse(ExecuteResponse executeResponse){
+ return Response.ok().entity(executeResponse).build();
+ }
+
+ private String decorateResultText(String resultText) {
+ return DATA_MODEL_ERROR_MESSAGE_HEADING + "\n\n" + resultText + "\n\n" + DATA_MODEL_ERROR_MESSAGE_FOOTER;
+ }
+
+ private String formatMessage(String key, Object... params) {
+ return new MessageFormat(key, Locale.US).format(params);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineResource.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineResource.java b/src/main/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineResource.java
new file mode 100644
index 0000000..f429981
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineResource.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.resources;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.stereotype.Component;
+
+import com.kenshoo.freemarker.view.FreeMarkerOnlineView;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: shlomis
+ * Date: 9/1/13
+ * Time: 4:35 PM
+ */
+@Path("/")
+@Component
+public class FreeMarkerOnlineResource {
+
+ @GET
+ @Produces(MediaType.TEXT_HTML)
+ public FreeMarkerOnlineView blankForm() {
+ return new FreeMarkerOnlineView();
+ }
+
+ @POST
+ @Produces(MediaType.TEXT_HTML)
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public FreeMarkerOnlineView formResult(
+ @FormParam("template") String template,
+ @FormParam("dataModel") String dataModel,
+ @FormParam("outputFormat") String outputFormat,
+ @FormParam("locale") String locale,
+ @FormParam("timeZone") String timeZone) {
+ FreeMarkerOnlineView view = new FreeMarkerOnlineView();
+ view.setTemplate(template);
+ view.setDataModel(dataModel);
+ view.setOutputFormat(outputFormat);
+ view.setLocale(locale);
+ view.setTimeZone(timeZone);
+ view.setExecute(true);
+ return view;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/services/AllowedSettingValuesMaps.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/services/AllowedSettingValuesMaps.java b/src/main/java/com/kenshoo/freemarker/services/AllowedSettingValuesMaps.java
new file mode 100644
index 0000000..962e7c9
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/services/AllowedSettingValuesMaps.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.kenshoo.freemarker.services;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import freemarker.core.HTMLOutputFormat;
+import freemarker.core.OutputFormat;
+import freemarker.core.PlainTextOutputFormat;
+import freemarker.core.RTFOutputFormat;
+import freemarker.core.UndefinedOutputFormat;
+import freemarker.core.XHTMLOutputFormat;
+import freemarker.core.XMLOutputFormat;
+
+/**
+ * Maps of the setting values the caller can chose from (these are the value shown in a dropdown on the UI).
+ */
+public class AllowedSettingValuesMaps {
+
+ public static final OutputFormat DEFAULT_OUTPUT_FORMAT = UndefinedOutputFormat.INSTANCE;
+ public static final String DEFAULT_OUTPUT_FORMAT_KEY = DEFAULT_OUTPUT_FORMAT.getName();
+ public static final Map<String, OutputFormat> OUTPUT_FORMAT_MAP;
+ static {
+ Map<String, OutputFormat> map = new HashMap<String, OutputFormat>();
+
+ addOutputFormatToMap(map, UndefinedOutputFormat.INSTANCE);
+ addOutputFormatToMap(map, HTMLOutputFormat.INSTANCE);
+ addOutputFormatToMap(map, XMLOutputFormat.INSTANCE);
+ addOutputFormatToMap(map, XHTMLOutputFormat.INSTANCE);
+ addOutputFormatToMap(map, RTFOutputFormat.INSTANCE);
+ addOutputFormatToMap(map, PlainTextOutputFormat.INSTANCE);
+
+ OUTPUT_FORMAT_MAP = Collections.unmodifiableMap(map);
+ }
+
+ private static void addOutputFormatToMap(Map<String, OutputFormat> map, OutputFormat outputFormat) {
+ map.put(outputFormat.getName(), outputFormat);
+ }
+
+ public static final Locale DEFAULT_LOCALE = Locale.US;
+ public static final String DEFAULT_LOCALE_KEY = DEFAULT_LOCALE.toString();
+ public static final Map<String, Locale> LOCALE_MAP;
+ static {
+ List<Locale> availableLocales = new ArrayList<Locale>(Arrays.asList(Locale.getAvailableLocales()));
+
+ for (Iterator<Locale> iterator = availableLocales.iterator(); iterator.hasNext();) {
+ Locale locale = iterator.next();
+ // Don't bloat the list with "variants"
+ if (!StringUtils.isBlank(locale.getVariant())) {
+ iterator.remove();
+ }
+ }
+
+ if (!availableLocales.contains(DEFAULT_LOCALE)) {
+ availableLocales.add(DEFAULT_LOCALE);
+ }
+
+ Map<String, Locale> map = new HashMap<String, Locale>();
+ for (Locale locale : availableLocales) {
+ map.put(locale.toString(), locale);
+ }
+
+ LOCALE_MAP = Collections.unmodifiableMap(map);
+ }
+
+ public static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("America/Los_Angeles");
+
+ public static final String DEFAULT_TIME_ZONE_KEY;
+
+ public static final Map<String, TimeZone> TIME_ZONE_MAP;
+ static {
+ String[] availableIDs = TimeZone.getAvailableIDs();
+
+ DEFAULT_TIME_ZONE_KEY = AllowedSettingValuesMaps.DEFAULT_TIME_ZONE.getID();
+ if (!ArrayUtils.contains(availableIDs, DEFAULT_TIME_ZONE_KEY)) {
+ ArrayUtils.add(availableIDs, DEFAULT_TIME_ZONE_KEY);
+ }
+
+ Map<String, TimeZone> map = new HashMap<String, TimeZone>();
+ for (String timeZoneId : availableIDs) {
+ map.put(timeZoneId, TimeZone.getTimeZone(timeZoneId));
+ }
+
+ TIME_ZONE_MAP = Collections.unmodifiableMap(map);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/services/FreeMarkerService.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/services/FreeMarkerService.java b/src/main/java/com/kenshoo/freemarker/services/FreeMarkerService.java
new file mode 100644
index 0000000..e594651
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/services/FreeMarkerService.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.services;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.TimeZone;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.eclipse.jetty.util.BlockingArrayQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import com.kenshoo.freemarker.util.LengthLimitExceededException;
+import com.kenshoo.freemarker.util.LengthLimitedWriter;
+
+import freemarker.core.FreeMarkerInternalsAccessor;
+import freemarker.core.OutputFormat;
+import freemarker.core.ParseException;
+import freemarker.core.TemplateClassResolver;
+import freemarker.core.TemplateConfiguration;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateExceptionHandler;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: nir
+ * Date: 4/12/14
+ * Time: 10:15 AM
+ */
+@Service
+public class FreeMarkerService {
+
+ private static final int DEFAULT_MAX_OUTPUT_LENGTH = 100000;
+ private static final int DEFAULT_MAX_THREADS = Math.max(2,
+ (int) Math.round(Runtime.getRuntime().availableProcessors() * 3.0 / 4));
+ /** Not implemented yet, will need 2.3.22, even then a _CoreAPI call. */
+ private static final long DEFAULT_MAX_TEMPLATE_EXECUTION_TIME = 2000;
+ private static final int MIN_DEFAULT_MAX_QUEUE_LENGTH = 2;
+ private static final int MAX_DEFAULT_MAX_QUEUE_LENGTH_MILLISECONDS = 30000;
+ private static final long THREAD_KEEP_ALIVE_TIME = 4 * 1000;
+ private static final long ABORTION_LOOP_TIME_LIMIT = 5000;
+ private static final long ABORTION_LOOP_INTERRUPTION_DISTANCE = 50;
+
+ private static final String MAX_OUTPUT_LENGTH_EXCEEDED_TERMINATION = "\n----------\n"
+ + "Aborted template processing, as the output length has exceeded the {0} character limit set for "
+ + "this service.";
+
+ private static final Logger logger = LoggerFactory.getLogger(FreeMarkerService.class);
+
+ private final Configuration freeMarkerConfig;
+
+ private ExecutorService templateExecutor;
+
+ private int maxOutputLength = DEFAULT_MAX_OUTPUT_LENGTH;
+
+ private int maxThreads = DEFAULT_MAX_THREADS;
+ private Integer maxQueueLength;
+ private long maxTemplateExecutionTime = DEFAULT_MAX_TEMPLATE_EXECUTION_TIME;
+
+ public FreeMarkerService() {
+ freeMarkerConfig = new Configuration(Configuration.getVersion());
+ freeMarkerConfig.setNewBuiltinClassResolver(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER);
+ freeMarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+ freeMarkerConfig.setLogTemplateExceptions(false);
+ freeMarkerConfig.setLocale(AllowedSettingValuesMaps.DEFAULT_LOCALE);
+ freeMarkerConfig.setTimeZone(AllowedSettingValuesMaps.DEFAULT_TIME_ZONE);
+ freeMarkerConfig.setOutputFormat(AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT);
+ freeMarkerConfig.setOutputEncoding("UTF-8");
+ }
+
+ /**
+ * @param templateSourceCode
+ * The FTL to execute; not {@code null}.
+ * @param dataModel
+ * The FreeMarker data-model to execute the template with; maybe {@code null}.
+ * @param outputFormat
+ * The output format to execute the template with; maybe {@code null}.
+ * @param locale
+ * The locale to execute the template with; maybe {@code null}.
+ * @param timeZone
+ * The time zone to execute the template with; maybe {@code null}.
+ *
+ * @return The result of the template parsing and evaluation. The method won't throw exception if that fails due to
+ * errors in the template provided, instead it indicates this fact in the response object. That's because
+ * this is a service for trying out the template language, so such errors are part of the normal operation.
+ *
+ * @throws RejectedExecutionException
+ * If the service is overburden and thus doing the calculation was rejected.
+ * @throws FreeMarkerServiceException
+ * If the calculation fails from a reason that's not a mistake in the template and doesn't fit the
+ * meaning of {@link RejectedExecutionException} either.
+ */
+ public FreeMarkerServiceResponse calculateTemplateOutput(
+ String templateSourceCode, Object dataModel, OutputFormat outputFormat, Locale locale, TimeZone timeZone)
+ throws RejectedExecutionException {
+ Objects.requireNonNull(templateExecutor, "templateExecutor was null - was postConstruct ever called?");
+
+ final CalculateTemplateOutput task = new CalculateTemplateOutput(
+ templateSourceCode, dataModel, outputFormat, locale, timeZone);
+ Future<FreeMarkerServiceResponse> future = templateExecutor.submit(task);
+
+ synchronized (task) {
+ while (!task.isTemplateExecutionStarted() && !task.isTaskEnded() && !future.isDone()) {
+ try {
+ task.wait(50); // Timeout is needed to periodically check future.isDone()
+ } catch (InterruptedException e) {
+ throw new FreeMarkerServiceException("Template execution task was interrupted.", e);
+ }
+ }
+ }
+
+ try {
+ return future.get(maxTemplateExecutionTime, TimeUnit.MILLISECONDS);
+ } catch (ExecutionException e) {
+ throw new FreeMarkerServiceException("Template execution task unexpectedly failed", e.getCause());
+ } catch (InterruptedException e) {
+ throw new FreeMarkerServiceException("Template execution task was interrupted.", e);
+ } catch (TimeoutException e) {
+ // Exactly one interruption should be enough, and it should abort template processing pretty much
+ // immediately. But to be on the safe side we will interrupt in a loop, with a timeout.
+ final long abortionLoopStartTime = System.currentTimeMillis();
+ long timeLeft = ABORTION_LOOP_TIME_LIMIT;
+ boolean templateExecutionEnded = false;
+ do {
+ synchronized (task) {
+ Thread templateExecutorThread = task.getTemplateExecutorThread();
+ if (templateExecutorThread == null) {
+ templateExecutionEnded = true;
+ } else {
+ FreeMarkerInternalsAccessor.interruptTemplateProcessing(templateExecutorThread);
+ logger.debug("Trying to interrupt overly long template processing (" + timeLeft + " ms left).");
+ }
+ }
+ if (!templateExecutionEnded) {
+ try {
+ timeLeft = ABORTION_LOOP_TIME_LIMIT - (System.currentTimeMillis() - abortionLoopStartTime);
+ if (timeLeft > 0) {
+ Thread.sleep(ABORTION_LOOP_INTERRUPTION_DISTANCE);
+ }
+ } catch (InterruptedException eInt) {
+ logger.error("Template execution abortion loop was interrupted", eInt);
+ timeLeft = 0;
+ }
+ }
+ } while (!templateExecutionEnded && timeLeft > 0);
+
+ if (templateExecutionEnded) {
+ logger.debug("Long template processing has ended.");
+ try {
+ return future.get();
+ } catch (InterruptedException | ExecutionException e1) {
+ throw new FreeMarkerServiceException("Failed to get result from template executor task", e);
+ }
+ } else {
+ throw new FreeMarkerServiceException(
+ "Couldn't stop long running template processing within " + ABORTION_LOOP_TIME_LIMIT
+ + " ms. It's possibly stuck forever. Such problems can exhaust the executor pool. "
+ + "Template (quoted): " + StringEscapeUtils.escapeJava(templateSourceCode));
+ }
+ }
+ }
+
+ public int getMaxOutputLength() {
+ return maxOutputLength;
+ }
+
+ public void setMaxOutputLength(int maxOutputLength) {
+ this.maxOutputLength = maxOutputLength;
+ }
+
+ public int getMaxThreads() {
+ return maxThreads;
+ }
+
+ public void setMaxThreads(int maxThreads) {
+ this.maxThreads = maxThreads;
+ }
+
+ public int getMaxQueueLength() {
+ return maxQueueLength;
+ }
+
+ public void setMaxQueueLength(int maxQueueLength) {
+ this.maxQueueLength = maxQueueLength;
+ }
+
+ public long getMaxTemplateExecutionTime() {
+ return maxTemplateExecutionTime;
+ }
+
+ public void setMaxTemplateExecutionTime(long maxTemplateExecutionTime) {
+ this.maxTemplateExecutionTime = maxTemplateExecutionTime;
+ }
+
+ /**
+ * Returns the time zone used by the FreeMarker templates.
+ */
+ public TimeZone getFreeMarkerTimeZone() {
+ return freeMarkerConfig.getTimeZone();
+ }
+
+ private FreeMarkerServiceResponse createFailureResponse(Throwable e) {
+ logger.debug("The template had error(s)", e);
+ return new FreeMarkerServiceResponse.Builder().buildForFailure(e);
+ }
+
+ @PostConstruct
+ public void postConstruct() {
+ int actualMaxQueueLength = maxQueueLength != null
+ ? maxQueueLength
+ : Math.max(
+ MIN_DEFAULT_MAX_QUEUE_LENGTH,
+ (int) (MAX_DEFAULT_MAX_QUEUE_LENGTH_MILLISECONDS / maxTemplateExecutionTime));
+ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
+ maxThreads, maxThreads,
+ THREAD_KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS,
+ new BlockingArrayQueue<Runnable>(actualMaxQueueLength));
+ threadPoolExecutor.allowCoreThreadTimeOut(true);
+ templateExecutor = threadPoolExecutor;
+ }
+
+ private class CalculateTemplateOutput implements Callable<FreeMarkerServiceResponse> {
+
+ private boolean templateExecutionStarted;
+ private Thread templateExecutorThread;
+ private final String templateSourceCode;
+ private final Object dataModel;
+ private final OutputFormat outputFormat;
+ private final Locale locale;
+ private final TimeZone timeZone;
+ private boolean taskEnded;
+
+ private CalculateTemplateOutput(String templateSourceCode, Object dataModel,
+ OutputFormat outputFormat, Locale locale, TimeZone timeZone) {
+ this.templateSourceCode = templateSourceCode;
+ this.dataModel = dataModel;
+ this.outputFormat = outputFormat;
+ this.locale = locale;
+ this.timeZone = timeZone;
+ }
+
+ @Override
+ public FreeMarkerServiceResponse call() throws Exception {
+ try {
+ Template template;
+ try {
+ TemplateConfiguration tCfg = new TemplateConfiguration();
+ tCfg.setParentConfiguration(freeMarkerConfig);
+ if (outputFormat != null) {
+ tCfg.setOutputFormat(outputFormat);
+ }
+ if (locale != null) {
+ tCfg.setLocale(locale);
+ }
+ if (timeZone != null) {
+ tCfg.setTimeZone(timeZone);
+ }
+
+ template = new Template(null, null,
+ new StringReader(templateSourceCode), freeMarkerConfig, tCfg, null);
+
+ tCfg.apply(template);
+ } catch (ParseException e) {
+ // Expected (part of normal operation)
+ return createFailureResponse(e);
+ } catch (Exception e) {
+ // Not expected
+ throw new FreeMarkerServiceException("Unexpected exception during template parsing", e);
+ }
+
+ FreeMarkerInternalsAccessor.makeTemplateInterruptable(template);
+
+ boolean resultTruncated;
+ StringWriter writer = new StringWriter();
+ try {
+ synchronized (this) {
+ templateExecutorThread = Thread.currentThread();
+ templateExecutionStarted = true;
+ notifyAll();
+ }
+ try {
+ template.process(dataModel, new LengthLimitedWriter(writer, maxOutputLength));
+ } finally {
+ synchronized (this) {
+ templateExecutorThread = null;
+ FreeMarkerInternalsAccessor.clearAnyPendingTemplateProcessingInterruption();
+ }
+ }
+ resultTruncated = false;
+ } catch (LengthLimitExceededException e) {
+ // Not really an error, we just cut the output here.
+ resultTruncated = true;
+ writer.write(new MessageFormat(MAX_OUTPUT_LENGTH_EXCEEDED_TERMINATION, AllowedSettingValuesMaps.DEFAULT_LOCALE)
+ .format(new Object[] { maxOutputLength }));
+ // Falls through
+ } catch (TemplateException e) {
+ // Expected (part of normal operation)
+ return createFailureResponse(e);
+ } catch (Exception e) {
+ if (FreeMarkerInternalsAccessor.isTemplateProcessingInterruptedException(e)) {
+ return new FreeMarkerServiceResponse.Builder().buildForFailure(new TimeoutException(
+ "Template processing was aborted for exceeding the " + getMaxTemplateExecutionTime()
+ + " ms time limit set for this online service. This is usually because you have "
+ + "a very long running #list (or other kind of loop) in your template."));
+ }
+ // Not expected
+ throw new FreeMarkerServiceException("Unexpected exception during template evaluation", e);
+ }
+
+ return new FreeMarkerServiceResponse.Builder().buildForSuccess(writer.toString(), resultTruncated);
+ } finally {
+ synchronized (this) {
+ taskEnded = true;
+ notifyAll();
+ }
+ }
+ }
+
+ private synchronized boolean isTemplateExecutionStarted() {
+ return templateExecutionStarted;
+ }
+
+ private synchronized boolean isTaskEnded() {
+ return taskEnded;
+ }
+
+ /**
+ * @return non-{@code null} after the task execution has actually started, but before it has finished.
+ */
+ private synchronized Thread getTemplateExecutorThread() {
+ return templateExecutorThread;
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/services/FreeMarkerServiceException.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/services/FreeMarkerServiceException.java b/src/main/java/com/kenshoo/freemarker/services/FreeMarkerServiceException.java
new file mode 100644
index 0000000..118528d
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/services/FreeMarkerServiceException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.services;
+
+/**
+ * When {@link FreeMarkerService} fails on an unexpected way (non-user error).
+ */
+public class FreeMarkerServiceException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public FreeMarkerServiceException(String message) {
+ super(message);
+ // TODO Auto-generated constructor stub
+ }
+
+ public FreeMarkerServiceException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/services/FreeMarkerServiceResponse.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/services/FreeMarkerServiceResponse.java b/src/main/java/com/kenshoo/freemarker/services/FreeMarkerServiceResponse.java
new file mode 100644
index 0000000..eab7ce3
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/services/FreeMarkerServiceResponse.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 Kenshoo.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kenshoo.freemarker.services;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: nir
+ * Date: 4/12/14
+ * Time: 11:28 AM
+ */
+public class FreeMarkerServiceResponse {
+
+ private final String templateOutput;
+ private final boolean templateOutputTruncated;
+ private final Throwable failureReason;
+
+ FreeMarkerServiceResponse(String templateOutput, boolean templateOutputTruncated) {
+ this.templateOutput = templateOutput;
+ this.templateOutputTruncated = templateOutputTruncated;
+ this.failureReason = null;
+ }
+
+ FreeMarkerServiceResponse(Throwable failureReason) {
+ this.templateOutput = null;
+ this.templateOutputTruncated = false;
+ this.failureReason = failureReason;
+ }
+
+ public String getTemplateOutput() {
+ return templateOutput;
+ }
+
+ public boolean isTemplateOutputTruncated() {
+ return templateOutputTruncated;
+ }
+
+ public boolean isSuccesful() {
+ return failureReason == null;
+ }
+
+ public Throwable getFailureReason() {
+ return failureReason;
+ }
+
+ public static class Builder {
+
+ public FreeMarkerServiceResponse buildForSuccess(String result, boolean resultTruncated){
+ return new FreeMarkerServiceResponse(result, resultTruncated);
+ }
+
+ public FreeMarkerServiceResponse buildForFailure(Throwable failureReason){
+ return new FreeMarkerServiceResponse(failureReason);
+ }
+
+ }
+
+}