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 <[email protected]> Authored: Mon Mar 20 18:37:10 2017 +0200 Committer: nirfeldman <[email protected]> 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); + } + + } + +}
