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);
+        }
+        
+    }
+    
+}