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:16 UTC
[1/4] incubator-freemarker-online-tester git commit: initial commit
Repository: incubator-freemarker-online-tester
Updated Branches:
refs/heads/master 0fb645b28 -> fc99d2b4f
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineResourceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineResourceTest.java b/src/test/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineResourceTest.java
new file mode 100644
index 0000000..ea30902
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineResourceTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import com.kenshoo.freemarker.services.FreeMarkerService;
+import com.kenshoo.freemarker.view.FreeMarkerOnlineView;
+
+import freemarker.core.OutputFormat;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: nir
+ * Date: 4/12/14
+ * Time: 11:23 PM
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FreeMarkerOnlineResourceTest {
+
+ @InjectMocks
+ FreeMarkerOnlineResource freeMarkerOnlineResultResource;
+
+ @Mock
+ FreeMarkerService freeMarkerService;
+
+ @Test
+ public void testInitialForm() {
+ when(freeMarkerService.calculateTemplateOutput(
+ anyString(), anyMap(), any(OutputFormat.class), any(Locale.class), any(TimeZone.class)))
+ .thenThrow(new AssertionError());
+ FreeMarkerOnlineView view = freeMarkerOnlineResultResource.blankForm();
+ assertEquals(view.getTemplate(), "");
+ assertEquals(view.getDataModel(), "");
+ }
+
+ @Test
+ public void testPostedBlankForm() {
+ when(freeMarkerService.calculateTemplateOutput(
+ anyString(), anyMap(), any(OutputFormat.class), any(Locale.class), any(TimeZone.class)))
+ .thenThrow(new AssertionError());
+ FreeMarkerOnlineView view = freeMarkerOnlineResultResource.formResult(null, null, null, null, null);
+ assertEquals(view.getTemplate(), "");
+ assertEquals(view.getDataModel(), "");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/services/FreeMarkerServiceResponseBuilderTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/services/FreeMarkerServiceResponseBuilderTest.java b/src/test/java/com/kenshoo/freemarker/services/FreeMarkerServiceResponseBuilderTest.java
new file mode 100644
index 0000000..d30bdec
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/services/FreeMarkerServiceResponseBuilderTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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 static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: nir
+ * Date: 4/13/14
+ * Time: 7:30 AM
+ */
+public class FreeMarkerServiceResponseBuilderTest {
+
+ private static final String RESULT = "Result";
+
+ private final FreeMarkerServiceResponse.Builder builder = new FreeMarkerServiceResponse.Builder();
+
+ @Test
+ public void testSuccessResult() {
+ FreeMarkerServiceResponse result = builder.buildForSuccess(RESULT, false);
+ assertThat(result.getTemplateOutput(), equalTo(RESULT));
+ assertThat(result.isTemplateOutputTruncated(), is(false));
+ assertThat(result.isSuccesful(), is(true));
+ assertThat(result.getFailureReason(), nullValue());
+ }
+
+ @Test
+ public void testSuccessTruncatedResult() {
+ FreeMarkerServiceResponse result = builder.buildForSuccess(RESULT, true);
+ assertThat(result.getTemplateOutput(), equalTo(RESULT));
+ assertThat(result.isTemplateOutputTruncated(), is(true));
+ assertThat(result.isSuccesful(), is(true));
+ assertThat(result.getFailureReason(), nullValue());
+ }
+
+ @Test
+ public void testErrorResult() {
+ Throwable failureReason = new Exception();
+ FreeMarkerServiceResponse result = builder.buildForFailure(failureReason);
+ assertThat(result.getTemplateOutput(), nullValue());
+ assertThat(result.isTemplateOutputTruncated(), is(false));
+ assertThat(result.isSuccesful(), is(false));
+ assertThat(result.getFailureReason(), sameInstance(failureReason));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/services/FreeMarkerServiceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/services/FreeMarkerServiceTest.java b/src/test/java/com/kenshoo/freemarker/services/FreeMarkerServiceTest.java
new file mode 100644
index 0000000..5bd19dd
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/services/FreeMarkerServiceTest.java
@@ -0,0 +1,310 @@
+/*
+ * 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 static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import freemarker.core.Environment;
+import freemarker.core.HTMLOutputFormat;
+import freemarker.core.ParseException;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: nir
+ * Date: 4/12/14
+ * Time: 11:15 AM
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FreeMarkerServiceTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FreeMarkerServiceTest.class);
+
+ private static final int MAX_THREADS = 3;
+ private static final int MAX_QUEUE_LENGTH = 2;
+ private static final int MAX_TEMPLATE_EXECUTION_TIME = 1500;
+
+ private static final int BLOCKING_TEST_TIMEOUT = 5000;
+
+ private static final String TRUNCATION_TEST_TEMPLATE = "12345";
+
+ @InjectMocks
+ private FreeMarkerService freeMarkerService;
+
+ @Before
+ public void initializeSpringBeans() {
+ freeMarkerService.setMaxQueueLength(MAX_QUEUE_LENGTH);
+ freeMarkerService.setMaxThreads(MAX_THREADS);
+ freeMarkerService.postConstruct();
+ freeMarkerService.setMaxTemplateExecutionTime(MAX_TEMPLATE_EXECUTION_TIME);
+ }
+
+ @Test
+ public void testCalculationOfATemplateWithNoDataModel() {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ "test", Collections.<String, Object>emptyMap(), null, null, null);
+ assertThat(serviceResponse.isSuccesful(), is(true));
+ assertThat(serviceResponse.getTemplateOutput(), is("test"));
+ }
+
+ @Test
+ public void testSimpleTemplate() {
+ HashMap<String, Object> dataModel = new HashMap<>();
+ dataModel.put("var1", "val1");
+ String templateSourceCode = "${var1}";
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ templateSourceCode, dataModel, null, null, null);
+ assertThat(serviceResponse.getTemplateOutput(), equalTo("val1"));
+ }
+
+ @Test
+ public void testTemplateWithFewArgsAndOperators() {
+ HashMap<String, Object> dataModel = new HashMap<>();
+ dataModel.put("var1", "val1");
+ dataModel.put("var2", "val2");
+ String template = "${var1?capitalize} ${var2?cap_first}";
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ template, dataModel, null, null, null);
+ assertThat(serviceResponse.getTemplateOutput(), equalTo("Val1 Val2"));
+ }
+
+ @Test
+ public void testOutputFormatParamterMatters() {
+ String template = "${'&'}";
+ {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ template, null, null, null, null);
+ assertThat(serviceResponse.getTemplateOutput(), equalTo("&"));
+ }
+ {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ template, null, HTMLOutputFormat.INSTANCE, null, null);
+ assertThat(serviceResponse.getTemplateOutput(), equalTo("&"));
+ }
+ }
+
+ @Test
+ public void testLocaleParameterMatters() {
+ String template = "${.locale}";
+ {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ template, null, null, new Locale("en", "US"), null);
+ assertThat(serviceResponse.getTemplateOutput(), equalTo("en_US"));
+ }
+ {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ template, null, null, new Locale("ru", "RU"), null);
+ assertThat(serviceResponse.getTemplateOutput(), equalTo("ru_RU"));
+ }
+ }
+
+ @Test
+ public void testTimeZoneParameterMatters() {
+ String template = "${" + System.currentTimeMillis() + "?numberToDatetime}";
+
+ String gmt1Result;
+ {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ template, null, null, null, TimeZone.getTimeZone("GMT+01"));
+ gmt1Result = serviceResponse.getTemplateOutput();
+ }
+
+ String gmt2Result;
+ {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ template, null, null, new Locale("ru", "RU"), null);
+ gmt2Result = serviceResponse.getTemplateOutput();
+ }
+
+ assertThat(gmt1Result, not(equalTo(gmt2Result)));
+ }
+
+ @Test
+ public void testTemplateWithSyntaxError() {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ "test ${xx", Collections.<String, Object>emptyMap(), null, null, null);
+ assertThat(serviceResponse.isSuccesful(), is(false));
+ assertThat(serviceResponse.getFailureReason(), instanceOf(ParseException.class));
+ }
+
+ @Test
+ public void testTemplateWithEvaluationError() {
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ "test ${x}", Collections.<String, Object>emptyMap(), null, null, null);
+ assertThat(serviceResponse.isSuccesful(), is(false));
+ assertThat(serviceResponse.getFailureReason(), instanceOf(TemplateException.class));
+ }
+
+ @Test
+ public void testResultAlmostTruncation() {
+ freeMarkerService.setMaxOutputLength(5);
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ TRUNCATION_TEST_TEMPLATE, Collections.<String, Object>emptyMap(), null, null, null);
+ assertThat(serviceResponse.isSuccesful(), is(true));
+ assertThat(serviceResponse.isTemplateOutputTruncated(), is(false));
+ assertThat(serviceResponse.getTemplateOutput(), equalTo(TRUNCATION_TEST_TEMPLATE));
+ }
+
+ @Test
+ public void testResultTruncation() {
+ freeMarkerService.setMaxOutputLength(4);
+ FreeMarkerServiceResponse serviceResponse = freeMarkerService.calculateTemplateOutput(
+ TRUNCATION_TEST_TEMPLATE, Collections.<String, Object>emptyMap(), null, null, null);
+ assertThat(serviceResponse.isSuccesful(), is(true));
+ assertThat(serviceResponse.isTemplateOutputTruncated(), is(true));
+ assertThat(serviceResponse.getTemplateOutput(),
+ startsWith(TRUNCATION_TEST_TEMPLATE.substring(0, freeMarkerService.getMaxOutputLength())));
+ assertThat(serviceResponse.getTemplateOutput().charAt(freeMarkerService.getMaxOutputLength()),
+ not(equalTo(TRUNCATION_TEST_TEMPLATE.charAt(freeMarkerService.getMaxOutputLength()))));
+ }
+
+ @Test
+ public void testTemplateExecutionTimeout() throws InterruptedException, ExecutionException {
+ freeMarkerService.setMaxTemplateExecutionTime(200);
+
+ // To avoid blocking the CI server forever without giving error:
+ Future<FreeMarkerServiceResponse> future = Executors.newSingleThreadExecutor().submit(
+ new Callable<FreeMarkerServiceResponse>() {
+
+ @Override
+ public FreeMarkerServiceResponse call() throws Exception {
+ return freeMarkerService.calculateTemplateOutput(
+ "<#list 1.. as _></#list>", Collections.<String, Object>emptyMap(), null, null, null);
+ }
+
+ });
+ FreeMarkerServiceResponse serviceResponse;
+ try {
+ serviceResponse = future.get(BLOCKING_TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ throw new AssertionError("The template execution wasn't aborted (within the timeout).");
+ }
+ assertThat(serviceResponse.isSuccesful(), is(false));
+ assertThat(serviceResponse.getFailureReason(), instanceOf(TimeoutException.class));
+ }
+
+ @Test
+ public void testServiceOverburden() throws InterruptedException {
+ final BlockerDirective blocker = new BlockerDirective();
+ final Map<String, BlockerDirective> blockerDataModel = Collections.singletonMap("blocker", blocker);
+ try {
+ // Fill all available task "slots":
+ for (int i = 0; i < MAX_THREADS + MAX_QUEUE_LENGTH; i++) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ freeMarkerService.calculateTemplateOutput("<@blocker/>", blockerDataModel, null, null, null);
+ }
+ }).start();
+ }
+
+ // Wait until all template executions has started:
+ synchronized (blocker) {
+ final long startTime = System.currentTimeMillis();
+ while (blocker.getEntered() < MAX_THREADS) {
+ // To avoid blocking the CI server forever is something goes wrong:
+ if (System.currentTimeMillis() - startTime > BLOCKING_TEST_TIMEOUT) {
+ fail("JUnit test timed out");
+ }
+ blocker.wait(1000);
+ }
+ }
+ Thread.sleep(200);
+ // Because the others are waiting in the queue, and weren't started:
+ assertThat(blocker.getEntered(), not(greaterThan(MAX_THREADS)));
+
+ // Souldn't accept on more tasks:
+ try {
+ freeMarkerService.calculateTemplateOutput("<@blocker/>", blockerDataModel, null, null, null);
+ fail("Expected RejectedExecutionException, but nothing was thrown.");
+ } catch (RejectedExecutionException e) {
+ // Expected
+ }
+ } finally {
+ // Ensure that the started threads will end:
+ blocker.release();
+ }
+ }
+
+ private static final class BlockerDirective implements TemplateDirectiveModel {
+
+ private int entered;
+ private boolean released;
+
+ public synchronized void release() {
+ released = true;
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void execute(Environment env, @SuppressWarnings("rawtypes") Map params,
+ TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
+ entered++;
+ notifyAll();
+ final long startTime = System.currentTimeMillis();
+ while (!released) {
+ // To avoid blocking the CI server forever is something goes wrong:
+ if (System.currentTimeMillis() - startTime > BLOCKING_TEST_TIMEOUT) {
+ LOG.error("JUnit test timed out");
+ }
+ try {
+ wait(1000);
+ } catch (InterruptedException e) {
+ LOG.error("JUnit test was interrupted");
+ }
+ }
+ LOG.debug("Blocker released");
+ }
+
+ public synchronized int getEntered() {
+ return entered;
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/util/DataModelParserTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/util/DataModelParserTest.java b/src/test/java/com/kenshoo/freemarker/util/DataModelParserTest.java
new file mode 100644
index 0000000..08f389c
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/util/DataModelParserTest.java
@@ -0,0 +1,277 @@
+/*
+ * 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.util;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.math.BigDecimal;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.GregorianCalendar;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import freemarker.template.utility.DateUtil;
+
+public class DataModelParserTest {
+
+ @Test
+ public void testEmpty() throws DataModelParsingException {
+ assertTrue(DataModelParser.parse("", DateUtil.UTC).isEmpty());
+ assertTrue(DataModelParser.parse(" \n ", DateUtil.UTC).isEmpty());
+ }
+
+ @Test
+ public void testSingleAssignment() throws DataModelParsingException {
+ assertEquals(ImmutableMap.of("n", "v"), DataModelParser.parse("n=v", DateUtil.UTC));
+ assertEquals(ImmutableMap.of("n", "v"), DataModelParser.parse("\n\n\tn\t= v", DateUtil.UTC));
+ assertEquals(ImmutableMap.of("longerN", "longer v"), DataModelParser.parse("longerN=longer v", DateUtil.UTC));
+ assertEquals(ImmutableMap.of("a:b.c-d$@", "foo bar\nbaaz"), DataModelParser.parse("a:b.c-d$@ = foo bar\nbaaz", DateUtil.UTC));
+ }
+
+ @Test
+ public void testNotBlankButHasNoAssignment() {
+ try {
+ DataModelParser.parse("x", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("must start with an assignment"));
+ }
+ }
+
+ @Test
+ public void testNoLinebreakBeforeEquals() {
+ try {
+ DataModelParser.parse("x\n=y", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("must start with an assignment"));
+ }
+ }
+
+ @Test
+ public void testMultipleAssignments() throws DataModelParsingException {
+ assertEquals(ImmutableMap.of("n1", "v1", "n2", "v2", "n3", "v3"),
+ DataModelParser.parse("n1=v1\nn2=v2\nn3=v3", DateUtil.UTC));
+ assertEquals(ImmutableMap.of("n1", "v1", "n2", "v2", "n3", "v3"),
+ DataModelParser.parse(" n1 = v1 \r\n\r\n\tn2=v2\nn3 = v3\n\n", DateUtil.UTC));
+ assertEquals(ImmutableMap.of("n1", "=\n=v", "n2", "l1\nl2\n\nl3", "n3", "v3"),
+ DataModelParser.parse("n1==\n=v \n n2=l1\nl2\n\nl3\nn3=v3", DateUtil.UTC));
+ }
+
+ @Test
+ public void testStrings() throws DataModelParsingException {
+ assertEquals(
+ ImmutableMap.of(
+ "a", "C:\\x",
+ "b", "foo\nbar",
+ "c", "foo\t\"bar\"",
+ "d", "foo\t\"bar\"",
+ "e", "Foo's"
+ ),
+ DataModelParser.parse(
+ "a=C:\\x\n"
+ + "b=foo\nbar\n"
+ + "c=foo\t\"bar\"\n"
+ + "d=\"foo\\t\\\"bar\\\"\"\n"
+ + "e=\"Foo's\"",
+ DateUtil.UTC));
+ try {
+ DataModelParser.parse("a=\"foo", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("quoted"));
+ }
+ try {
+ DataModelParser.parse("a='foo'", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("quoted"));
+ }
+ try {
+ DataModelParser.parse("a=\"\\x\"", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("escape"));
+ }
+ }
+
+ @Test
+ public void testBasicNumbers() throws DataModelParsingException {
+ assertEquals(
+ ImmutableMap.of(
+ "a", BigDecimal.valueOf(1),
+ "b", BigDecimal.valueOf(1.5),
+ "c", BigDecimal.valueOf(-1.5),
+ "d", BigDecimal.valueOf(1.5),
+ "e", BigDecimal.valueOf(-0.125)
+ ),
+ DataModelParser.parse("a=1\nb=1.5\nc=-1.5\nd=+1.5\ne=-12.5e-2", DateUtil.UTC));
+ try {
+ DataModelParser.parse("a=1,5", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("Malformed number"));
+ assertThat(e.getMessage(), not(containsString("ISO")));
+ }
+ }
+
+ @Test
+ public void testSpecialNumbers() throws DataModelParsingException {
+ assertEquals(
+ ImmutableMap.of(
+ "a", Double.NaN,
+ "b", Double.POSITIVE_INFINITY,
+ "c", Double.NEGATIVE_INFINITY,
+ "d", Double.POSITIVE_INFINITY
+ ),
+ DataModelParser.parse("a=NaN\nb=Infinity\nc=-Infinity\nd=+Infinity", DateUtil.UTC));
+ }
+
+ @Test
+ public void testBooleans() throws DataModelParsingException {
+ assertEquals(ImmutableMap.of("a", true, "b", false), DataModelParser.parse("a=true\nb=false", DateUtil.UTC));
+ try {
+ DataModelParser.parse("a=True", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("true"));
+ }
+ }
+
+ @Test
+ public void testTemporals() throws DataModelParsingException {
+ final Map<String, Object> dm = DataModelParser.parse("a=2014-02-12T01:02:03Z\nb=2014-02-12\nc=01:02:03Z", DateUtil.UTC);
+
+ final GregorianCalendar cal = new GregorianCalendar(DateUtil.UTC);
+ cal.clear();
+
+ cal.set(2014, 1, 12, 1, 2, 3);
+ Timestamp a = new Timestamp(cal.getTimeInMillis());
+ assertThat(dm.get("a"), instanceOf(Timestamp.class));
+ assertEquals(dm.get("a"), a);
+
+ cal.set(2014, 1, 12, 0, 0, 0);
+ java.sql.Date b = new java.sql.Date(cal.getTimeInMillis());
+ assertThat(dm.get("b"), instanceOf(java.sql.Date.class));
+ assertEquals(dm.get("b"), b);
+
+ cal.set(1970, 0, 1, 1, 2, 3);
+ Time c = new Time(cal.getTimeInMillis());
+ assertThat(dm.get("c"), instanceOf(Time.class));
+ assertEquals(dm.get("c"), c);
+
+ try {
+ DataModelParser.parse("a=2012T123", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("ISO 8601 date-time"));
+ }
+ try {
+ DataModelParser.parse("a=2012-0102", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("ISO 8601 date"));
+ }
+ try {
+ DataModelParser.parse("a=25:00", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("ISO 8601 time"));
+ }
+ }
+
+ @Test
+ public void testMaps() throws DataModelParsingException {
+ final Object map = DataModelParser.parse(
+ "n = {\n"
+ + "\t\"a\": 1,\n"
+ + "\t\"b\": 2\n"
+ + "}",
+ DateUtil.UTC)
+ .get("n");
+ assertEquals(ImmutableMap.of("a", 1, "b", 2), map);
+ assertThat(map, instanceOf(LinkedHashMap.class));
+ try {
+ DataModelParser.parse("n={1:2}", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("JSON"));
+ }
+ }
+
+ @Test
+ public void testLists() throws DataModelParsingException {
+ final Object list = DataModelParser.parse("n=[1, 2]", DateUtil.UTC).get("n");
+ assertEquals(ImmutableList.of(1, 2), list);
+ assertThat(list, instanceOf(List.class));
+ try {
+ DataModelParser.parse("n=[", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("JSON"));
+ }
+ }
+
+ @Test
+ public void testXML() throws DataModelParsingException {
+ final Object doc = DataModelParser.parse("n=<e xmlns='foo:/bar' a='123'>text</e>", DateUtil.UTC).get("n");
+ assertThat(doc, instanceOf(Document.class));
+ final Node firstChild = ((Document) doc).getFirstChild();
+ assertEquals("e", firstChild.getNodeName());
+ assertEquals("foo:/bar", firstChild.getNamespaceURI());
+
+ try {
+ DataModelParser.parse("n=<ns:e />", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("XML"));
+ }
+ }
+
+ @Test
+ public void testNull() throws DataModelParsingException {
+ assertNull(DataModelParser.parse("n=null", DateUtil.UTC).get("n"));
+ try {
+ DataModelParser.parse("a=NULL", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("null"));
+ }
+ }
+
+ @Test
+ public void testEmptyValue() throws DataModelParsingException {
+ try {
+ DataModelParser.parse("n=", DateUtil.UTC);
+ fail();
+ } catch (DataModelParsingException e) {
+ assertThat(e.getMessage(), containsString("Empty"));
+ }
+
+ assertEquals("", DataModelParser.parse("n=\"\"", DateUtil.UTC).get("n"));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/util/LengthLimitedWriterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/util/LengthLimitedWriterTest.java b/src/test/java/com/kenshoo/freemarker/util/LengthLimitedWriterTest.java
new file mode 100644
index 0000000..169b39e
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/util/LengthLimitedWriterTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.junit.Test;
+
+public class LengthLimitedWriterTest {
+
+ private StringWriter wrappedW = new StringWriter();
+ private LengthLimitedWriter w = new LengthLimitedWriter(wrappedW, 5);
+
+ @Test
+ public void testLimitNotExceeded() throws IOException {
+ w.write("123");
+ w.write("45");
+ }
+
+ @Test
+ public void testLimitExceededWithString() throws IOException {
+ w.write("123");
+ try {
+ w.write("456");
+ fail();
+ } catch (LengthLimitExceededException e) {
+ assertEquals("12345", wrappedW.toString());
+ }
+ }
+
+ @Test
+ public void testLimitExceededWithCharArray() throws IOException {
+ w.write(new char[] { '1', '2', '3' });
+ try {
+ w.write(new char[] { '4', '5', '6' });
+ fail();
+ } catch (LengthLimitExceededException e) {
+ assertEquals("12345", wrappedW.toString());
+ }
+ }
+
+ @Test
+ public void testLimitExceededWithChar() throws IOException {
+ w.write('1');
+ w.write('2');
+ w.write('3');
+ w.write('4');
+ w.write('5');
+ try {
+ w.write('6');
+ fail();
+ } catch (LengthLimitExceededException e) {
+ assertEquals("12345", wrappedW.toString());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/view/FreeMarkerOnlineViewTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/view/FreeMarkerOnlineViewTest.java b/src/test/java/com/kenshoo/freemarker/view/FreeMarkerOnlineViewTest.java
new file mode 100644
index 0000000..b73410f
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/view/FreeMarkerOnlineViewTest.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.view;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.junit.Test;
+
+import com.kenshoo.freemarker.services.AllowedSettingValuesMaps;
+
+import freemarker.core.HTMLOutputFormat;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: nir
+ * Date: 4/12/14
+ * Time: 11:13 PM
+ */
+public class FreeMarkerOnlineViewTest {
+
+ private static final String TEMPLATE = "Template";
+ private static final String DATA_MODEL = "DataModel";
+
+ @Test
+ public void testVieEmptyConstrucotr() {
+ FreeMarkerOnlineView view = new FreeMarkerOnlineView();
+ assertEquals(view.getTemplate(), "");
+ assertEquals(view.getDataModel(), "");
+ assertEquals(view.getOutputFormat(), AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT_KEY);
+ assertEquals(view.getLocale(), AllowedSettingValuesMaps.DEFAULT_LOCALE_KEY);
+ assertEquals(view.getTimeZone(), AllowedSettingValuesMaps.DEFAULT_TIME_ZONE_KEY);
+ }
+
+ @Test
+ public void testViewWhenAllOK() {
+ FreeMarkerOnlineView view = new FreeMarkerOnlineView();
+
+ view.setTemplate(TEMPLATE);
+ view.setDataModel(DATA_MODEL);
+ String outputFormatStr = HTMLOutputFormat.INSTANCE.getName();
+ view.setOutputFormat(outputFormatStr);
+ String localeStr = Locale.GERMAN.toString();
+ view.setLocale(localeStr);
+ String timeZoneStr = TimeZone.getTimeZone("GMT+01").getID();
+ view.setTimeZone(timeZoneStr);
+
+ assertEquals(view.getTemplate(), TEMPLATE);
+ assertEquals(view.getDataModel(), DATA_MODEL);
+ assertEquals(view.getOutputFormat(), outputFormatStr);
+ assertEquals(view.getLocale(), localeStr);
+ assertEquals(view.getTimeZone(), timeZoneStr);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/resources/spring/test-context.xml
----------------------------------------------------------------------
diff --git a/src/test/resources/spring/test-context.xml b/src/test/resources/spring/test-context.xml
new file mode 100644
index 0000000..2ca10e6
--- /dev/null
+++ b/src/test/resources/spring/test-context.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
+
+</beans>
\ No newline at end of file
[3/4] incubator-freemarker-online-tester git commit: initial commit
Posted by dd...@apache.org.
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);
+ }
+
+ }
+
+}
[4/4] incubator-freemarker-online-tester git commit: Contribution
from Kenshoo
Posted by dd...@apache.org.
Contribution from Kenshoo
Merge commit 'refs/pull/2/head' of https://github.com/apache/incubator-freemarker-online-tester
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/fc99d2b4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/tree/fc99d2b4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/diff/fc99d2b4
Branch: refs/heads/master
Commit: fc99d2b4fa6a9a863428ef737febc6413fcaa4da
Parents: 0fb645b abb2629
Author: ddekany <dd...@apache.org>
Authored: Sat Apr 1 21:43:31 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Apr 1 21:46:52 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(+)
----------------------------------------------------------------------
[2/4] incubator-freemarker-online-tester git commit: initial commit
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/util/DataModelParser.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/util/DataModelParser.java b/src/main/java/com/kenshoo/freemarker/util/DataModelParser.java
new file mode 100644
index 0000000..28cf6ff
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/util/DataModelParser.java
@@ -0,0 +1,264 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.DocumentBuilder;
+
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Document;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import freemarker.ext.dom.NodeModel;
+import freemarker.template.utility.DateUtil;
+import freemarker.template.utility.DateUtil.CalendarFieldsToDateConverter;
+import freemarker.template.utility.DateUtil.DateParseException;
+import freemarker.template.utility.DateUtil.TrivialCalendarFieldsToDateConverter;
+
+/**
+ * Parses the text that the user enters into the data model input field.
+ */
+public final class DataModelParser {
+
+ private static final String KEYWORD_NEGATIVE_INFINITY = "-Infinity";
+
+ private static final String KEYWORD_POSITIVE_INFINITY = "+Infinity";
+
+ private static final String KEYWORD_INFINITY = "Infinity";
+
+ private static final String KEYWORD_TRUE = "true";
+
+ private static final String KEYWORD_FALSE = "false";
+
+ private static final String KEYWORD_NULL = "null";
+
+ private static final String KEYWORD_NAN = "NaN";
+
+ /** Matches a line starting like "someVariable=". */
+ private static final Pattern ASSIGNMENT_START = Pattern.compile(
+ "^\\s*"
+ + "(\\p{L}[\\p{L}\\p{N}\\.:\\-_$@]*)" // name
+ + "[ \t]*=\\s*",
+ Pattern.MULTILINE);
+
+ /** Matches a value that starts like a number, or probably meant to be number at least. */
+ private static final Pattern NUMBER_LIKE = Pattern.compile("[+-]?[\\.,]?[0-9].*", Pattern.DOTALL);
+
+ private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
+
+ private DataModelParser() {
+ // Not meant to be instantiated
+ }
+
+ public static Map<String, Object> parse(String src, TimeZone timeZone) throws DataModelParsingException {
+ if (!StringUtils.hasText(src)) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, Object> dataModel = new LinkedHashMap<>();
+
+ String lastName = null;
+ int lastAssignmentStartEnd = 0;
+ final Matcher assignmentStart = ASSIGNMENT_START.matcher(src);
+ findAssignments: while (true) {
+ boolean hasNextAssignment = assignmentStart.find(lastAssignmentStartEnd);
+
+ if (lastName != null) {
+ String value = src.substring(
+ lastAssignmentStartEnd, hasNextAssignment ? assignmentStart.start() : src.length())
+ .trim();
+ final Object parsedValue;
+ try {
+ parsedValue = parseValue(value, timeZone);
+ } catch (DataModelParsingException e) {
+ throw new DataModelParsingException(
+ "Failed to parse the value of \"" + lastName + "\":\n" + e.getMessage(), e.getCause());
+ }
+ dataModel.put(lastName, parsedValue);
+ }
+
+ if (lastName == null && (!hasNextAssignment || assignmentStart.start() != 0)) {
+ throw new DataModelParsingException(
+ "The data model specification must start with an assignment (name=value).");
+ }
+
+ if (!hasNextAssignment) {
+ break findAssignments;
+ }
+
+ lastName = assignmentStart.group(1).trim();
+ lastAssignmentStartEnd = assignmentStart.end();
+ }
+
+ return dataModel;
+ }
+
+ private static Object parseValue(String value, TimeZone timeZone) throws DataModelParsingException {
+ // Note: Because we fall back to interpret the input as a literal string value when it doesn't look like
+ // anything else (like a number, boolean, etc.), it's important to avoid misunderstandings, and throw exception
+ // in suspicious situations. The user can always quote the string value if we are "too smart". But he will
+ // be confused about the rules of FreeMarker if what he believes to be a non-string is misinterpreted by this
+ // parser as a string. Getting sometimes an error and then quoting the string is better than that.
+
+ if (value.endsWith(";")) { // Tolerate this habit of Java and JavaScript programmers
+ value = value.substring(value.length() - 1).trim();
+ }
+
+ if (NUMBER_LIKE.matcher(value).matches()) {
+ try {
+ return new BigDecimal(value);
+ } catch (NumberFormatException e) {
+ // Maybe it's a ISO 8601 Date/time/datetime
+ CalendarFieldsToDateConverter calToDateConverter = new TrivialCalendarFieldsToDateConverter();
+
+ DateParseException attemptedTemportalPExc = null;
+ String attemptedTemporalType = null;
+ final int dashIdx = value.indexOf('-');
+ final int colonIdx = value.indexOf(':');
+ if (value.indexOf('T') > 1 || (dashIdx > 1 && colonIdx > dashIdx)) {
+ try {
+ return new Timestamp(
+ DateUtil.parseISO8601DateTime(value, timeZone, calToDateConverter).getTime());
+ } catch (DateParseException pExc) {
+ attemptedTemporalType = "date-time";
+ attemptedTemportalPExc = pExc;
+ }
+ } else if (dashIdx > 1) {
+ try {
+ return new java.sql.Date(
+ DateUtil.parseISO8601Date(value, timeZone, calToDateConverter).getTime());
+ } catch (DateParseException pExc) {
+ attemptedTemporalType = "date";
+ attemptedTemportalPExc = pExc;
+ }
+ } else if (colonIdx > 1) {
+ try {
+ return new Time(
+ DateUtil.parseISO8601Time(value, timeZone, calToDateConverter).getTime());
+ } catch (DateParseException pExc) {
+ attemptedTemporalType = "time";
+ attemptedTemportalPExc = pExc;
+ }
+ }
+ if (attemptedTemportalPExc == null) {
+ throw new DataModelParsingException("Malformed number: " + value, e);
+ } else {
+ throw new DataModelParsingException(
+ "Malformed ISO 8601 " + attemptedTemporalType + " (or malformed number): " +
+ attemptedTemportalPExc.getMessage(), e.getCause());
+ }
+ }
+ } else if (value.startsWith("\"")) {
+ try {
+ return JSON_MAPPER.readValue(value, String.class);
+ } catch (IOException e) {
+ throw new DataModelParsingException(
+ "Malformed quoted string (using JSON syntax): " + getMessageWithoutLocation(e),
+ e);
+ }
+ } else if (value.startsWith("\'")) {
+ throw new DataModelParsingException(
+ "Malformed quoted string (using JSON syntax): Use \" character for quotation, not \' character.");
+ } else if (value.startsWith("[")) {
+ try {
+ return JSON_MAPPER.readValue(value, List.class);
+ } catch (IOException e) {
+ throw new DataModelParsingException(
+ "Malformed list (using JSON syntax): " + getMessageWithoutLocation(e),
+ e);
+ }
+ } else if (value.startsWith("{")) {
+ try {
+ return JSON_MAPPER.readValue(value, LinkedHashMap.class);
+ } catch (IOException e) {
+ throw new DataModelParsingException(
+ "Malformed list (using JSON syntax): " + getMessageWithoutLocation(e),
+ e);
+ }
+ } else if (value.startsWith("<")) {
+ try {
+ DocumentBuilder builder = NodeModel.getDocumentBuilderFactory().newDocumentBuilder();
+ ErrorHandler errorHandler = NodeModel.getErrorHandler();
+ if (errorHandler != null) builder.setErrorHandler(errorHandler);
+ final Document doc = builder.parse(new InputSource(new StringReader(value)));
+ NodeModel.simplify(doc);
+ return doc;
+ } catch (SAXException e) {
+ final String saxMsg = e.getMessage();
+ throw new DataModelParsingException("Malformed XML: " + (saxMsg != null ? saxMsg : e), e);
+ } catch (Exception e) {
+ throw new DataModelParsingException("XML parsing has failed with internal error: " + e, e);
+ }
+ } else if (value.equalsIgnoreCase(KEYWORD_TRUE)) {
+ checkKeywordCase(value, KEYWORD_TRUE);
+ return Boolean.TRUE;
+ } else if (value.equalsIgnoreCase(KEYWORD_FALSE)) {
+ checkKeywordCase(value, KEYWORD_FALSE);
+ return Boolean.FALSE;
+ } else if (value.equalsIgnoreCase(KEYWORD_NULL)) {
+ checkKeywordCase(value, KEYWORD_NULL);
+ return null;
+ } else if (value.equalsIgnoreCase(KEYWORD_NAN)) {
+ checkKeywordCase(value, KEYWORD_NAN);
+ return Double.NaN;
+ } else if (value.equalsIgnoreCase(KEYWORD_INFINITY)) {
+ checkKeywordCase(value, KEYWORD_INFINITY);
+ return Double.POSITIVE_INFINITY;
+ } else if (value.equalsIgnoreCase(KEYWORD_POSITIVE_INFINITY)) {
+ checkKeywordCase(value, KEYWORD_POSITIVE_INFINITY);
+ return Double.POSITIVE_INFINITY;
+ } else if (value.equalsIgnoreCase(KEYWORD_NEGATIVE_INFINITY)) {
+ checkKeywordCase(value, KEYWORD_NEGATIVE_INFINITY);
+ return Double.NEGATIVE_INFINITY;
+ } else if (value.length() == 0) {
+ throw new DataModelParsingException(
+ "Empty value. (If you indeed wanted a 0 length string, quote it, like \"\".)");
+ } else {
+ return value;
+ }
+ }
+
+ private static String getMessageWithoutLocation(IOException e) {
+ return e instanceof JsonProcessingException
+ ? ((JsonProcessingException) e).getOriginalMessage()
+ : e.getMessage();
+ }
+
+ private static void checkKeywordCase(String inputKeyword, String correctKeyword) throws DataModelParsingException {
+ if (!correctKeyword.equals(inputKeyword)) {
+ throw new DataModelParsingException("Keywords are case sensitive; the correct form is: "
+ + correctKeyword);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/util/DataModelParsingException.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/util/DataModelParsingException.java b/src/main/java/com/kenshoo/freemarker/util/DataModelParsingException.java
new file mode 100644
index 0000000..cd24407
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/util/DataModelParsingException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.util;
+
+import java.util.TimeZone;
+
+/**
+ * Thrown by {@link DataModelParser#parse(String, TimeZone)}.
+ */
+public class DataModelParsingException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public DataModelParsingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DataModelParsingException(String message) {
+ super(message);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/util/ExceptionUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/util/ExceptionUtils.java b/src/main/java/com/kenshoo/freemarker/util/ExceptionUtils.java
new file mode 100644
index 0000000..999c450
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/util/ExceptionUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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.util;
+
+import freemarker.core.ParseException;
+import freemarker.template.TemplateException;
+
+public final class ExceptionUtils {
+
+ private ExceptionUtils() {
+ // Not meant to be instantiated
+ }
+
+ /**
+ * The error message (and sometimes also the class), and then the same with the cause exception, and so on. Doesn't
+ * contain the stack trace or other location information.
+ */
+ public static String getMessageWithCauses(final Throwable exc) {
+ StringBuilder sb = new StringBuilder();
+
+ Throwable curExc = exc;
+ while (curExc != null) {
+ if (curExc != exc) {
+ sb.append("\n\nCaused by:\n");
+ }
+ String msg = curExc.getMessage();
+ if (msg == null || !(curExc instanceof TemplateException || curExc instanceof ParseException)) {
+ sb.append(curExc.getClass().getName()).append(": ");
+ }
+ sb.append(msg);
+ curExc = curExc.getCause();
+ }
+ return sb.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/util/LengthLimitExceededException.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/util/LengthLimitExceededException.java b/src/main/java/com/kenshoo/freemarker/util/LengthLimitExceededException.java
new file mode 100644
index 0000000..67effd0
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/util/LengthLimitExceededException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+
+/**
+ * Thrown by {@link LengthLimitedWriter}.
+ */
+public class LengthLimitExceededException extends IOException {
+
+ private static final long serialVersionUID = 1L;
+
+ public LengthLimitExceededException() {
+ super("The outout String length limit of the Writer was exceeded.");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/util/LengthLimitedWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/util/LengthLimitedWriter.java b/src/main/java/com/kenshoo/freemarker/util/LengthLimitedWriter.java
new file mode 100644
index 0000000..a4d1450
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/util/LengthLimitedWriter.java
@@ -0,0 +1,83 @@
+/*
+ * 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.util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * A {@link StringWriter} that limits its buffer size, and throws {@link LengthLimitExceededException} when that's
+ * exceeded.
+ */
+public class LengthLimitedWriter extends FilterWriter {
+
+ private int lengthLeft;
+
+ public LengthLimitedWriter(Writer writer, int lengthLimit) {
+ super(writer);
+ this.lengthLeft = lengthLimit;
+ }
+
+ @Override
+ public void write(int c) throws IOException {
+ if (lengthLeft < 1) {
+ throw new LengthLimitExceededException();
+ }
+
+ super.write(c);
+
+ lengthLeft--;
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ final boolean lengthExceeded;
+ if (lengthLeft < len) {
+ len = lengthLeft;
+ lengthExceeded = true;
+ } else {
+ lengthExceeded = false;
+ }
+
+ super.write(cbuf, off, len);
+ lengthLeft -= len;
+
+ if (lengthExceeded) {
+ throw new LengthLimitExceededException();
+ }
+ }
+
+ @Override
+ public void write(String str, int off, int len) throws IOException {
+ final boolean lengthExceeded;
+ if (lengthLeft < len) {
+ len = lengthLeft;
+ lengthExceeded = true;
+ } else {
+ lengthExceeded = false;
+ }
+
+ super.write(str, off, len);
+ lengthLeft -= len;
+
+ if (lengthExceeded) {
+ throw new LengthLimitExceededException();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/com/kenshoo/freemarker/view/FreeMarkerOnlineView.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/kenshoo/freemarker/view/FreeMarkerOnlineView.java b/src/main/java/com/kenshoo/freemarker/view/FreeMarkerOnlineView.java
new file mode 100644
index 0000000..684511f
--- /dev/null
+++ b/src/main/java/com/kenshoo/freemarker/view/FreeMarkerOnlineView.java
@@ -0,0 +1,156 @@
+/*
+ * 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.view;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.kenshoo.freemarker.model.SelectionOption;
+import com.kenshoo.freemarker.services.AllowedSettingValuesMaps;
+import com.yammer.dropwizard.views.View;
+
+import freemarker.template.Configuration;
+
+/**
+ * Created with IntelliJ IDEA. User: nir Date: 4/11/14 Time: 12:23 PM
+ */
+public class FreeMarkerOnlineView extends View {
+
+ private static final List<SelectionOption> LOCALE_SELECTION_OPTIONS = toLocaleSelectionOptions(AllowedSettingValuesMaps.LOCALE_MAP);
+ private static final List<SelectionOption> TIME_ZONE_SELECTION_OPTIONS = toSelectionOptions(AllowedSettingValuesMaps.TIME_ZONE_MAP);
+ private static final List<SelectionOption> OUTPUT_FORMAT_SELECTION_OPTIONS = toSelectionOptions(AllowedSettingValuesMaps.OUTPUT_FORMAT_MAP);
+
+ private String template = "";
+ private String dataModel = "";
+ private String outputFormat = AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT_KEY;
+ private String locale = AllowedSettingValuesMaps.DEFAULT_LOCALE_KEY;
+ private String timeZone = AllowedSettingValuesMaps.DEFAULT_TIME_ZONE_KEY;
+
+ private boolean execute;
+
+ private static List<SelectionOption> toSelectionOptions(Map<String, ?> settingValueMap) {
+ ArrayList<SelectionOption> selectionOptions = new ArrayList<SelectionOption>(settingValueMap.size());
+ for (String key : settingValueMap.keySet()) {
+ selectionOptions.add(new SelectionOption(key, truncate(key, 25)));
+ }
+ Collections.sort(selectionOptions);
+ return selectionOptions;
+ }
+
+ private static List<SelectionOption> toLocaleSelectionOptions(Map<String, Locale> localeMap) {
+ ArrayList<SelectionOption> selectionOptions = new ArrayList<SelectionOption>(localeMap.size());
+ for (Map.Entry<String, Locale> ent : localeMap.entrySet()) {
+ Locale locale = ent.getValue();
+ selectionOptions.add(
+ new SelectionOption(ent.getKey(),
+ truncate(locale.getDisplayName(Locale.US), 18) + "; " + locale.toString()));
+ }
+ Collections.sort(selectionOptions);
+ return selectionOptions;
+ }
+
+ private static String truncate(String s, int maxLength) {
+ if (s == null) {
+ return null;
+ }
+ return s.length() <= maxLength ? s : s.substring(0, Math.max(maxLength - 3, 0)) + "[...]";
+ }
+
+ /**
+ *
+ * @param template
+ * @param dataModel
+ * @param execute set to true if the execution should be triggered on page load.
+ */
+ public FreeMarkerOnlineView() {
+ super("/view/freemarker-online.ftl", Charset.forName("utf-8"));
+ }
+
+ public String getTemplate() {
+ return template;
+ }
+
+ public void setTemplate(String template) {
+ this.template = withDefault(template, "");
+ }
+
+ public String getDataModel() {
+ return dataModel;
+ }
+
+ public void setDataModel(String dataModel) {
+ this.dataModel = withDefault(dataModel, "");
+ }
+
+ public String getFreeMarkerVersion() {
+ return Configuration.getVersion().toString();
+ }
+
+ public List<SelectionOption> getOutputFormats() {
+ return OUTPUT_FORMAT_SELECTION_OPTIONS;
+ }
+
+ public List<SelectionOption> getLocales() {
+ return LOCALE_SELECTION_OPTIONS;
+ }
+
+ public List<SelectionOption> getTimeZones() {
+ return TIME_ZONE_SELECTION_OPTIONS;
+ }
+
+ public String getOutputFormat() {
+ return outputFormat;
+ }
+
+ public void setOutputFormat(String outputFormat) {
+ this.outputFormat = withDefault(outputFormat, AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT_KEY);
+ }
+
+ public String getLocale() {
+ return locale;
+ }
+
+ public void setLocale(String locale) {
+ this.locale = withDefault(locale, AllowedSettingValuesMaps.DEFAULT_LOCALE_KEY);
+ }
+
+ public String getTimeZone() {
+ return timeZone;
+ }
+
+ public void setTimeZone(String timeZone) {
+ this.timeZone = withDefault(timeZone, AllowedSettingValuesMaps.DEFAULT_TIME_ZONE_KEY);
+ }
+
+ public boolean isExecute() {
+ return execute;
+ }
+
+ public void setExecute(boolean executeImmediately) {
+ this.execute = executeImmediately;
+ }
+
+ private static String withDefault(String value, String defaultValue) {
+ return !StringUtils.isBlank(value) ? value : defaultValue;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/java/freemarker/core/FreeMarkerInternalsAccessor.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/FreeMarkerInternalsAccessor.java b/src/main/java/freemarker/core/FreeMarkerInternalsAccessor.java
new file mode 100644
index 0000000..1ffc7f7
--- /dev/null
+++ b/src/main/java/freemarker/core/FreeMarkerInternalsAccessor.java
@@ -0,0 +1,61 @@
+/*
+ * 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 freemarker.core;
+
+import freemarker.core.ThreadInterruptionSupportTemplatePostProcessor.TemplateProcessingThreadInterruptedException;
+import freemarker.template.Template;
+
+/**
+ * Functions that depend on unpublished FreeMarker functionality. Might need to be adjusted for new FreeMarker releases.
+ * The relevant parts of the FreeMarker source code contains comments about keeping this in sync with that, so,
+ * hopefully this won't be a problem.
+ */
+public final class FreeMarkerInternalsAccessor {
+
+ /**
+ * Ensures that the template will react to {@link #interruptTemplateProcessing(Thread)}.
+ */
+ public static void makeTemplateInterruptable(Template template) {
+ _CoreAPI.addThreadInterruptedChecks(template);
+ }
+
+ /**
+ * Checks if the template processing has thrown exception because of a {@link #interruptTemplateProcessing(Thread)}
+ * call.
+ */
+ public static boolean isTemplateProcessingInterruptedException(Throwable e) {
+ return e instanceof TemplateProcessingThreadInterruptedException;
+ }
+
+ /**
+ * Tells a template processing in another thread to abort; asynchronous.
+ */
+ public static void interruptTemplateProcessing(Thread t) {
+ t.interrupt();
+ }
+
+ /**
+ * Called from the thread where the interruptible template execution ran earlier, to clear any related thread state.
+ */
+ public static void clearAnyPendingTemplateProcessingInterruption() {
+ Thread.interrupted(); // To clears the interruption flag
+ }
+
+ private FreeMarkerInternalsAccessor() {
+ // Not meant to be instantiated
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/assets/css/main.css
----------------------------------------------------------------------
diff --git a/src/main/resources/assets/css/main.css b/src/main/resources/assets/css/main.css
new file mode 100644
index 0000000..44b8972
--- /dev/null
+++ b/src/main/resources/assets/css/main.css
@@ -0,0 +1,114 @@
+.clear {
+ clear: both;
+}
+
+#layout {
+ position: relative;
+ padding: 0;
+}
+
+.header {
+ margin: 0;
+ padding: 1em 0;
+ border-bottom: 1px solid #eee;
+ text-align: center;
+ color: #444;
+}
+
+.content {
+ margin: 0 auto;
+ padding: 1em 0;
+ width: 920px;
+}
+
+.footer {
+ margin: 0;
+ padding: 1em 2em 0;
+ border-top: 1px solid #eee;
+ text-align: center;
+ color: #444;
+}
+
+.errorMessage {
+ font-size: small;
+ color: red;
+ display: none;
+}
+
+.header h1 {
+ margin: 0.2em 0;
+ font-size: 1.75em;
+ font-weight: normal;
+}
+
+#result {
+ background-color: #FFF;
+ color: #000
+}
+
+#result.error {
+ background-color: #FFF0F0;
+ color: #A00;
+}
+
+.faint {
+ color: #bbb
+}
+
+textarea.source-code {
+ font-family: monospace !important;
+ /* We set a few more things to decrease the chance of jQuery Autosize plugin issues: */
+ font-size: 1em !important;
+ resize: none !important;
+ line-height: 1em !important;
+}
+
+.hiddenByDefault {
+ display: none;
+}
+
+#dataModelExamples {
+ background: #eee;
+ padding: 4px;
+ margin: 0;
+}
+
+#dataModelExamples .description {
+ font-style: italic;
+ margin-bottom: 1em;
+}
+
+#dataModelExamples pre {
+ font-family: monospace;
+ font-size: 1em;
+ padding: 0;
+ margin: 0;
+}
+
+#templateAndModelForm input,
+#templateAndModelForm textarea,
+#templateAndModelForm button {
+ margin-bottom: 1em;
+}
+
+#templateAndModelForm label {
+ margin-top: 1em;
+ margin-bottom: 0.25em;
+}
+
+.formPanel {
+
+}
+
+.formBottomButtonsContainer {
+ margin-top: 1.5em;
+}
+
+.resultContainer {
+ margin-top: 1em;
+}
+
+.horizontalBox {
+ display: inline-block;
+ padding-right: 1em;
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/assets/js/autosize.min.js
----------------------------------------------------------------------
diff --git a/src/main/resources/assets/js/autosize.min.js b/src/main/resources/assets/js/autosize.min.js
new file mode 100644
index 0000000..98138c9
--- /dev/null
+++ b/src/main/resources/assets/js/autosize.min.js
@@ -0,0 +1,6 @@
+/*!
+ Autosize 3.0.8
+ license: MIT
+ http://www.jacklmoore.com/autosize
+*/
+!function(e,t){if("function"==typeof define&&define.amd)define(["exports","module"],t);else if("undefined"!=typeof exports&&"undefined"!=typeof module)t(exports,module);else{var o={exports:{}};t(o.exports,o),e.autosize=o.exports}}(this,function(e,t){"use strict";function o(e){function t(){var t=window.getComputedStyle(e,null);"vertical"===t.resize?e.style.resize="none":"both"===t.resize&&(e.style.resize="horizontal"),u="content-box"===t.boxSizing?-(parseFloat(t.paddingTop)+parseFloat(t.paddingBottom)):parseFloat(t.borderTopWidth)+parseFloat(t.borderBottomWidth),i()}function o(t){var o=e.style.width;e.style.width="0px",e.offsetWidth,e.style.width=o,v=t,l&&(e.style.overflowY=t),n()}function n(){var t=window.pageYOffset,o=document.body.scrollTop,n=e.style.height;e.style.height="auto";var i=e.scrollHeight+u;return 0===e.scrollHeight?void(e.style.height=n):(e.style.height=i+"px",document.documentElement.scrollTop=t,void(document.body.scrollTop=o))}function i(){var t=e.style.height;n();va
r i=window.getComputedStyle(e,null);if(i.height!==e.style.height?"visible"!==v&&o("visible"):"hidden"!==v&&o("hidden"),t!==e.style.height){var r=document.createEvent("Event");r.initEvent("autosize:resized",!0,!1),e.dispatchEvent(r)}}var r=void 0===arguments[1]?{}:arguments[1],d=r.setOverflowX,s=void 0===d?!0:d,a=r.setOverflowY,l=void 0===a?!0:a;if(e&&e.nodeName&&"TEXTAREA"===e.nodeName&&!e.hasAttribute("data-autosize-on")){var u=null,v="hidden",f=function(t){window.removeEventListener("resize",i),e.removeEventListener("input",i),e.removeEventListener("keyup",i),e.removeAttribute("data-autosize-on"),e.removeEventListener("autosize:destroy",f),Object.keys(t).forEach(function(o){e.style[o]=t[o]})}.bind(e,{height:e.style.height,resize:e.style.resize,overflowY:e.style.overflowY,overflowX:e.style.overflowX,wordWrap:e.style.wordWrap});e.addEventListener("autosize:destroy",f),"onpropertychange"in e&&"oninput"in e&&e.addEventListener("keyup",i),window.addEventListener("resize",i),e.addEventL
istener("input",i),e.addEventListener("autosize:update",i),e.setAttribute("data-autosize-on",!0),l&&(e.style.overflowY="hidden"),s&&(e.style.overflowX="hidden",e.style.wordWrap="break-word"),t()}}function n(e){if(e&&e.nodeName&&"TEXTAREA"===e.nodeName){var t=document.createEvent("Event");t.initEvent("autosize:destroy",!0,!1),e.dispatchEvent(t)}}function i(e){if(e&&e.nodeName&&"TEXTAREA"===e.nodeName){var t=document.createEvent("Event");t.initEvent("autosize:update",!0,!1),e.dispatchEvent(t)}}var r=null;"undefined"==typeof window||"function"!=typeof window.getComputedStyle?(r=function(e){return e},r.destroy=function(e){return e},r.update=function(e){return e}):(r=function(e,t){return e&&Array.prototype.forEach.call(e.length?e:[e],function(e){return o(e,t)}),e},r.destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],n),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],i),e}),t.exports=r});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/assets/js/jquery.autosize.min.js
----------------------------------------------------------------------
diff --git a/src/main/resources/assets/js/jquery.autosize.min.js b/src/main/resources/assets/js/jquery.autosize.min.js
new file mode 100644
index 0000000..5c764dc
--- /dev/null
+++ b/src/main/resources/assets/js/jquery.autosize.min.js
@@ -0,0 +1,6 @@
+/*!
+ Autosize 1.18.17
+ license: MIT
+ http://www.jacklmoore.com/autosize
+*/
+!function(e){var t,o={className:"autosizejs",id:"autosizejs",append:"\n",callback:!1,resizeDelay:10,placeholder:!0},i='<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; padding: 0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; overflow:hidden; transition:none; -webkit-transition:none; -moz-transition:none;"/>',a=["fontFamily","fontSize","fontWeight","fontStyle","letterSpacing","textTransform","wordSpacing","textIndent","whiteSpace"],n=e(i).data("autosize",!0)[0];n.style.lineHeight="99px","99px"===e(n).css("lineHeight")&&a.push("lineHeight"),n.style.lineHeight="",e.fn.autosize=function(i){return this.length?(i=e.extend({},o,i||{}),n.parentNode!==document.body&&e(document.body).append(n),this.each(function(){function o(){var t,o=window.getComputedStyle?window.getComputedStyle(u,null):null;o?(t=parseFloat(o.width),("border-box
"===o.boxSizing||"border-box"===o.webkitBoxSizing||"border-box"===o.mozBoxSizing)&&e.each(["paddingLeft","paddingRight","borderLeftWidth","borderRightWidth"],function(e,i){t-=parseFloat(o[i])})):t=p.width(),n.style.width=Math.max(t,0)+"px"}function s(){var s={};if(t=u,n.className=i.className,n.id=i.id,d=parseFloat(p.css("maxHeight")),e.each(a,function(e,t){s[t]=p.css(t)}),e(n).css(s).attr("wrap",p.attr("wrap")),o(),window.chrome){var r=u.style.width;u.style.width="0px";{u.offsetWidth}u.style.width=r}}function r(){var e,a;t!==u?s():o(),n.value=!u.value&&i.placeholder?p.attr("placeholder")||"":u.value,n.value+=i.append||"",n.style.overflowY=u.style.overflowY,a=parseFloat(u.style.height)||0,n.scrollTop=0,n.scrollTop=9e4,e=n.scrollTop,d&&e>d?(u.style.overflowY="scroll",e=d):(u.style.overflowY="hidden",c>e&&(e=c)),e+=z,Math.abs(a-e)>.01&&(u.style.height=e+"px",n.className=n.className,w&&i.callback.call(u,u),p.trigger("autosize.resized"))}function l(){clearTimeout(h),h=setTimeout(function
(){var e=p.width();e!==b&&(b=e,r())},parseInt(i.resizeDelay,10))}var d,c,h,u=this,p=e(u),z=0,w=e.isFunction(i.callback),f={height:u.style.height,overflow:u.style.overflow,overflowY:u.style.overflowY,wordWrap:u.style.wordWrap,resize:u.style.resize},b=p.width(),g=p.css("resize");p.data("autosize")||(p.data("autosize",!0),("border-box"===p.css("box-sizing")||"border-box"===p.css("-moz-box-sizing")||"border-box"===p.css("-webkit-box-sizing"))&&(z=p.outerHeight()-p.height()),c=Math.max(parseFloat(p.css("minHeight"))-z||0,p.height()),p.css({overflow:"hidden",overflowY:"hidden",wordWrap:"break-word"}),"vertical"===g?p.css("resize","none"):"both"===g&&p.css("resize","horizontal"),"onpropertychange"in u?"oninput"in u?p.on("input.autosize keyup.autosize",r):p.on("propertychange.autosize",function(){"value"===event.propertyName&&r()}):p.on("input.autosize",r),i.resizeDelay!==!1&&e(window).on("resize.autosize",l),p.on("autosize.resize",r),p.on("autosize.resizeIncludeStyle",function(){t=null,r()
}),p.on("autosize.destroy",function(){t=null,clearTimeout(h),e(window).off("resize",l),p.off("autosize").off(".autosize").css(f).removeData("autosize")}),r())})):this}}(jQuery||$);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/assets/js/jquery.blockUI.js
----------------------------------------------------------------------
diff --git a/src/main/resources/assets/js/jquery.blockUI.js b/src/main/resources/assets/js/jquery.blockUI.js
new file mode 100644
index 0000000..90ce5d6
--- /dev/null
+++ b/src/main/resources/assets/js/jquery.blockUI.js
@@ -0,0 +1,620 @@
+/*!
+ * jQuery blockUI plugin
+ * Version 2.70.0-2014.11.23
+ * Requires jQuery v1.7 or later
+ *
+ * Examples at: http://malsup.com/jquery/block/
+ * Copyright (c) 2007-2013 M. Alsup
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Thanks to Amir-Hossein Sobhi for some excellent contributions!
+ */
+
+;(function() {
+/*jshint eqeqeq:false curly:false latedef:false */
+"use strict";
+
+ function setup($) {
+ $.fn._fadeIn = $.fn.fadeIn;
+
+ var noOp = $.noop || function() {};
+
+ // this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
+ // confusing userAgent strings on Vista)
+ var msie = /MSIE/.test(navigator.userAgent);
+ var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent);
+ var mode = document.documentMode || 0;
+ var setExpr = $.isFunction( document.createElement('div').style.setExpression );
+
+ // global $ methods for blocking/unblocking the entire page
+ $.blockUI = function(opts) { install(window, opts); };
+ $.unblockUI = function(opts) { remove(window, opts); };
+
+ // convenience method for quick growl-like notifications (http://www.google.com/search?q=growl)
+ $.growlUI = function(title, message, timeout, onClose) {
+ var $m = $('<div class="growlUI"></div>');
+ if (title) $m.append('<h1>'+title+'</h1>');
+ if (message) $m.append('<h2>'+message+'</h2>');
+ if (timeout === undefined) timeout = 3000;
+
+ // Added by konapun: Set timeout to 30 seconds if this growl is moused over, like normal toast notifications
+ var callBlock = function(opts) {
+ opts = opts || {};
+
+ $.blockUI({
+ message: $m,
+ fadeIn : typeof opts.fadeIn !== 'undefined' ? opts.fadeIn : 700,
+ fadeOut: typeof opts.fadeOut !== 'undefined' ? opts.fadeOut : 1000,
+ timeout: typeof opts.timeout !== 'undefined' ? opts.timeout : timeout,
+ centerY: false,
+ showOverlay: false,
+ onUnblock: onClose,
+ css: $.blockUI.defaults.growlCSS
+ });
+ };
+
+ callBlock();
+ var nonmousedOpacity = $m.css('opacity');
+ $m.mouseover(function() {
+ callBlock({
+ fadeIn: 0,
+ timeout: 30000
+ });
+
+ var displayBlock = $('.blockMsg');
+ displayBlock.stop(); // cancel fadeout if it has started
+ displayBlock.fadeTo(300, 1); // make it easier to read the message by removing transparency
+ }).mouseout(function() {
+ $('.blockMsg').fadeOut(1000);
+ });
+ // End konapun additions
+ };
+
+ // plugin method for blocking element content
+ $.fn.block = function(opts) {
+ if ( this[0] === window ) {
+ $.blockUI( opts );
+ return this;
+ }
+ var fullOpts = $.extend({}, $.blockUI.defaults, opts || {});
+ this.each(function() {
+ var $el = $(this);
+ if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked'))
+ return;
+ $el.unblock({ fadeOut: 0 });
+ });
+
+ return this.each(function() {
+ if ($.css(this,'position') == 'static') {
+ this.style.position = 'relative';
+ $(this).data('blockUI.static', true);
+ }
+ this.style.zoom = 1; // force 'hasLayout' in ie
+ install(this, opts);
+ });
+ };
+
+ // plugin method for unblocking element content
+ $.fn.unblock = function(opts) {
+ if ( this[0] === window ) {
+ $.unblockUI( opts );
+ return this;
+ }
+ return this.each(function() {
+ remove(this, opts);
+ });
+ };
+
+ $.blockUI.version = 2.70; // 2nd generation blocking at no extra cost!
+
+ // override these in your code to change the default behavior and style
+ $.blockUI.defaults = {
+ // message displayed when blocking (use null for no message)
+ message: '<h1>Please wait...</h1>',
+
+ title: null, // title string; only used when theme == true
+ draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded)
+
+ theme: false, // set to true to use with jQuery UI themes
+
+ // styles for the message when blocking; if you wish to disable
+ // these and use an external stylesheet then do this in your code:
+ // $.blockUI.defaults.css = {};
+ css: {
+ padding: 0,
+ margin: 0,
+ width: '30%',
+ top: '40%',
+ left: '35%',
+ textAlign: 'center',
+ color: '#000',
+ border: '3px solid #aaa',
+ backgroundColor:'#fff',
+ cursor: 'wait'
+ },
+
+ // minimal style set used when themes are used
+ themedCSS: {
+ width: '30%',
+ top: '40%',
+ left: '35%'
+ },
+
+ // styles for the overlay
+ overlayCSS: {
+ backgroundColor: '#000',
+ opacity: 0.6,
+ cursor: 'wait'
+ },
+
+ // style to replace wait cursor before unblocking to correct issue
+ // of lingering wait cursor
+ cursorReset: 'default',
+
+ // styles applied when using $.growlUI
+ growlCSS: {
+ width: '350px',
+ top: '10px',
+ left: '',
+ right: '10px',
+ border: 'none',
+ padding: '5px',
+ opacity: 0.6,
+ cursor: 'default',
+ color: '#fff',
+ backgroundColor: '#000',
+ '-webkit-border-radius':'10px',
+ '-moz-border-radius': '10px',
+ 'border-radius': '10px'
+ },
+
+ // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
+ // (hat tip to Jorge H. N. de Vasconcelos)
+ /*jshint scripturl:true */
+ iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',
+
+ // force usage of iframe in non-IE browsers (handy for blocking applets)
+ forceIframe: false,
+
+ // z-index for the blocking overlay
+ baseZ: 1000,
+
+ // set these to true to have the message automatically centered
+ centerX: true, // <-- only effects element blocking (page block controlled via css above)
+ centerY: true,
+
+ // allow body element to be stetched in ie6; this makes blocking look better
+ // on "short" pages. disable if you wish to prevent changes to the body height
+ allowBodyStretch: true,
+
+ // enable if you want key and mouse events to be disabled for content that is blocked
+ bindEvents: true,
+
+ // be default blockUI will supress tab navigation from leaving blocking content
+ // (if bindEvents is true)
+ constrainTabKey: true,
+
+ // fadeIn time in millis; set to 0 to disable fadeIn on block
+ fadeIn: 200,
+
+ // fadeOut time in millis; set to 0 to disable fadeOut on unblock
+ fadeOut: 400,
+
+ // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
+ timeout: 0,
+
+ // disable if you don't want to show the overlay
+ showOverlay: true,
+
+ // if true, focus will be placed in the first available input field when
+ // page blocking
+ focusInput: true,
+
+ // elements that can receive focus
+ focusableElements: ':input:enabled:visible',
+
+ // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
+ // no longer needed in 2012
+ // applyPlatformOpacityRules: true,
+
+ // callback method invoked when fadeIn has completed and blocking message is visible
+ onBlock: null,
+
+ // callback method invoked when unblocking has completed; the callback is
+ // passed the element that has been unblocked (which is the window object for page
+ // blocks) and the options that were passed to the unblock call:
+ // onUnblock(element, options)
+ onUnblock: null,
+
+ // callback method invoked when the overlay area is clicked.
+ // setting this will turn the cursor to a pointer, otherwise cursor defined in overlayCss will be used.
+ onOverlayClick: null,
+
+ // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
+ quirksmodeOffsetHack: 4,
+
+ // class name of the message block
+ blockMsgClass: 'blockMsg',
+
+ // if it is already blocked, then ignore it (don't unblock and reblock)
+ ignoreIfBlocked: false
+ };
+
+ // private data and functions follow...
+
+ var pageBlock = null;
+ var pageBlockEls = [];
+
+ function install(el, opts) {
+ var css, themedCSS;
+ var full = (el == window);
+ var msg = (opts && opts.message !== undefined ? opts.message : undefined);
+ opts = $.extend({}, $.blockUI.defaults, opts || {});
+
+ if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked'))
+ return;
+
+ opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
+ css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
+ if (opts.onOverlayClick)
+ opts.overlayCSS.cursor = 'pointer';
+
+ themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
+ msg = msg === undefined ? opts.message : msg;
+
+ // remove the current block (if there is one)
+ if (full && pageBlock)
+ remove(window, {fadeOut:0});
+
+ // if an existing element is being used as the blocking content then we capture
+ // its current place in the DOM (and current display style) so we can restore
+ // it when we unblock
+ if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
+ var node = msg.jquery ? msg[0] : msg;
+ var data = {};
+ $(el).data('blockUI.history', data);
+ data.el = node;
+ data.parent = node.parentNode;
+ data.display = node.style.display;
+ data.position = node.style.position;
+ if (data.parent)
+ data.parent.removeChild(node);
+ }
+
+ $(el).data('blockUI.onUnblock', opts.onUnblock);
+ var z = opts.baseZ;
+
+ // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
+ // layer1 is the iframe layer which is used to supress bleed through of underlying content
+ // layer2 is the overlay layer which has opacity and a wait cursor (by default)
+ // layer3 is the message content that is displayed while blocking
+ var lyr1, lyr2, lyr3, s;
+ if (msie || opts.forceIframe)
+ lyr1 = $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>');
+ else
+ lyr1 = $('<div class="blockUI" style="display:none"></div>');
+
+ if (opts.theme)
+ lyr2 = $('<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+ (z++) +';display:none"></div>');
+ else
+ lyr2 = $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
+
+ if (opts.theme && full) {
+ s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:fixed">';
+ if ( opts.title ) {
+ s += '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || ' ')+'</div>';
+ }
+ s += '<div class="ui-widget-content ui-dialog-content"></div>';
+ s += '</div>';
+ }
+ else if (opts.theme) {
+ s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:absolute">';
+ if ( opts.title ) {
+ s += '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || ' ')+'</div>';
+ }
+ s += '<div class="ui-widget-content ui-dialog-content"></div>';
+ s += '</div>';
+ }
+ else if (full) {
+ s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage" style="z-index:'+(z+10)+';display:none;position:fixed"></div>';
+ }
+ else {
+ s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement" style="z-index:'+(z+10)+';display:none;position:absolute"></div>';
+ }
+ lyr3 = $(s);
+
+ // if we have a message, style it
+ if (msg) {
+ if (opts.theme) {
+ lyr3.css(themedCSS);
+ lyr3.addClass('ui-widget-content');
+ }
+ else
+ lyr3.css(css);
+ }
+
+ // style the overlay
+ if (!opts.theme /*&& (!opts.applyPlatformOpacityRules)*/)
+ lyr2.css(opts.overlayCSS);
+ lyr2.css('position', full ? 'fixed' : 'absolute');
+
+ // make iframe layer transparent in IE
+ if (msie || opts.forceIframe)
+ lyr1.css('opacity',0.0);
+
+ //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
+ var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
+ $.each(layers, function() {
+ this.appendTo($par);
+ });
+
+ if (opts.theme && opts.draggable && $.fn.draggable) {
+ lyr3.draggable({
+ handle: '.ui-dialog-titlebar',
+ cancel: 'li'
+ });
+ }
+
+ // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
+ var expr = setExpr && (!$.support.boxModel || $('object,embed', full ? null : el).length > 0);
+ if (ie6 || expr) {
+ // give body 100% height
+ if (full && opts.allowBodyStretch && $.support.boxModel)
+ $('html,body').css('height','100%');
+
+ // fix ie6 issue when blocked element has a border width
+ if ((ie6 || !$.support.boxModel) && !full) {
+ var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
+ var fixT = t ? '(0 - '+t+')' : 0;
+ var fixL = l ? '(0 - '+l+')' : 0;
+ }
+
+ // simulate fixed position
+ $.each(layers, function(i,o) {
+ var s = o[0].style;
+ s.position = 'absolute';
+ if (i < 2) {
+ if (full)
+ s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"');
+ else
+ s.setExpression('height','this.parentNode.offsetHeight + "px"');
+ if (full)
+ s.setExpression('width','jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"');
+ else
+ s.setExpression('width','this.parentNode.offsetWidth + "px"');
+ if (fixL) s.setExpression('left', fixL);
+ if (fixT) s.setExpression('top', fixT);
+ }
+ else if (opts.centerY) {
+ if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
+ s.marginTop = 0;
+ }
+ else if (!opts.centerY && full) {
+ var top = (opts.css && opts.css.top) ? parseInt(opts.css.top, 10) : 0;
+ var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
+ s.setExpression('top',expression);
+ }
+ });
+ }
+
+ // show the message
+ if (msg) {
+ if (opts.theme)
+ lyr3.find('.ui-widget-content').append(msg);
+ else
+ lyr3.append(msg);
+ if (msg.jquery || msg.nodeType)
+ $(msg).show();
+ }
+
+ if ((msie || opts.forceIframe) && opts.showOverlay)
+ lyr1.show(); // opacity is zero
+ if (opts.fadeIn) {
+ var cb = opts.onBlock ? opts.onBlock : noOp;
+ var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
+ var cb2 = msg ? cb : noOp;
+ if (opts.showOverlay)
+ lyr2._fadeIn(opts.fadeIn, cb1);
+ if (msg)
+ lyr3._fadeIn(opts.fadeIn, cb2);
+ }
+ else {
+ if (opts.showOverlay)
+ lyr2.show();
+ if (msg)
+ lyr3.show();
+ if (opts.onBlock)
+ opts.onBlock.bind(lyr3)();
+ }
+
+ // bind key and mouse events
+ bind(1, el, opts);
+
+ if (full) {
+ pageBlock = lyr3[0];
+ pageBlockEls = $(opts.focusableElements,pageBlock);
+ if (opts.focusInput)
+ setTimeout(focus, 20);
+ }
+ else
+ center(lyr3[0], opts.centerX, opts.centerY);
+
+ if (opts.timeout) {
+ // auto-unblock
+ var to = setTimeout(function() {
+ if (full)
+ $.unblockUI(opts);
+ else
+ $(el).unblock(opts);
+ }, opts.timeout);
+ $(el).data('blockUI.timeout', to);
+ }
+ }
+
+ // remove the block
+ function remove(el, opts) {
+ var count;
+ var full = (el == window);
+ var $el = $(el);
+ var data = $el.data('blockUI.history');
+ var to = $el.data('blockUI.timeout');
+ if (to) {
+ clearTimeout(to);
+ $el.removeData('blockUI.timeout');
+ }
+ opts = $.extend({}, $.blockUI.defaults, opts || {});
+ bind(0, el, opts); // unbind events
+
+ if (opts.onUnblock === null) {
+ opts.onUnblock = $el.data('blockUI.onUnblock');
+ $el.removeData('blockUI.onUnblock');
+ }
+
+ var els;
+ if (full) // crazy selector to handle odd field errors in ie6/7
+ els = $('body').children().filter('.blockUI').add('body > .blockUI');
+ else
+ els = $el.find('>.blockUI');
+
+ // fix cursor issue
+ if ( opts.cursorReset ) {
+ if ( els.length > 1 )
+ els[1].style.cursor = opts.cursorReset;
+ if ( els.length > 2 )
+ els[2].style.cursor = opts.cursorReset;
+ }
+
+ if (full)
+ pageBlock = pageBlockEls = null;
+
+ if (opts.fadeOut) {
+ count = els.length;
+ els.stop().fadeOut(opts.fadeOut, function() {
+ if ( --count === 0)
+ reset(els,data,opts,el);
+ });
+ }
+ else
+ reset(els, data, opts, el);
+ }
+
+ // move blocking element back into the DOM where it started
+ function reset(els,data,opts,el) {
+ var $el = $(el);
+ if ( $el.data('blockUI.isBlocked') )
+ return;
+
+ els.each(function(i,o) {
+ // remove via DOM calls so we don't lose event handlers
+ if (this.parentNode)
+ this.parentNode.removeChild(this);
+ });
+
+ if (data && data.el) {
+ data.el.style.display = data.display;
+ data.el.style.position = data.position;
+ data.el.style.cursor = 'default'; // #59
+ if (data.parent)
+ data.parent.appendChild(data.el);
+ $el.removeData('blockUI.history');
+ }
+
+ if ($el.data('blockUI.static')) {
+ $el.css('position', 'static'); // #22
+ }
+
+ if (typeof opts.onUnblock == 'function')
+ opts.onUnblock(el,opts);
+
+ // fix issue in Safari 6 where block artifacts remain until reflow
+ var body = $(document.body), w = body.width(), cssW = body[0].style.width;
+ body.width(w-1).width(w);
+ body[0].style.width = cssW;
+ }
+
+ // bind/unbind the handler
+ function bind(b, el, opts) {
+ var full = el == window, $el = $(el);
+
+ // don't bother unbinding if there is nothing to unbind
+ if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
+ return;
+
+ $el.data('blockUI.isBlocked', b);
+
+ // don't bind events when overlay is not in use or if bindEvents is false
+ if (!full || !opts.bindEvents || (b && !opts.showOverlay))
+ return;
+
+ // bind anchors and inputs for mouse and key events
+ var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove';
+ if (b)
+ $(document).bind(events, opts, handler);
+ else
+ $(document).unbind(events, handler);
+
+ // former impl...
+ // var $e = $('a,:input');
+ // b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
+ }
+
+ // event handler to suppress keyboard/mouse events when blocking
+ function handler(e) {
+ // allow tab navigation (conditionally)
+ if (e.type === 'keydown' && e.keyCode && e.keyCode == 9) {
+ if (pageBlock && e.data.constrainTabKey) {
+ var els = pageBlockEls;
+ var fwd = !e.shiftKey && e.target === els[els.length-1];
+ var back = e.shiftKey && e.target === els[0];
+ if (fwd || back) {
+ setTimeout(function(){focus(back);},10);
+ return false;
+ }
+ }
+ }
+ var opts = e.data;
+ var target = $(e.target);
+ if (target.hasClass('blockOverlay') && opts.onOverlayClick)
+ opts.onOverlayClick(e);
+
+ // allow events within the message content
+ if (target.parents('div.' + opts.blockMsgClass).length > 0)
+ return true;
+
+ // allow events for content that is not being blocked
+ return target.parents().children().filter('div.blockUI').length === 0;
+ }
+
+ function focus(back) {
+ if (!pageBlockEls)
+ return;
+ var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
+ if (e)
+ e.focus();
+ }
+
+ function center(el, x, y) {
+ var p = el.parentNode, s = el.style;
+ var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
+ var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
+ if (x) s.left = l > 0 ? (l+'px') : '0';
+ if (y) s.top = t > 0 ? (t+'px') : '0';
+ }
+
+ function sz(el, p) {
+ return parseInt($.css(el,p),10)||0;
+ }
+
+ }
+
+
+ /*global define:true */
+ if (typeof define === 'function' && define.amd && define.amd.jQuery) {
+ define(['jquery'], setup);
+ } else {
+ setup(jQuery);
+ }
+
+})();
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/assets/js/script.js
----------------------------------------------------------------------
diff --git a/src/main/resources/assets/js/script.js b/src/main/resources/assets/js/script.js
new file mode 100644
index 0000000..7e88910
--- /dev/null
+++ b/src/main/resources/assets/js/script.js
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+/**
+ * Created by Pmuruge on 8/28/2015.
+ */
+$(document).ready(function() {
+ $("#eval-btn").click(function() {
+ execute();
+ });
+ $('#templateAndModelForm textarea, #templateAndModelForm select').keydown(function (e) {
+ if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
+ execute();
+ }
+ });
+ $.blockUI.defaults.fadeIn = 1000;
+ $.blockUI.defaults.fadeOut = 0;
+});
+
+var hasPendingExecuteAjaxCall = false;
+
+function execute() {
+ if (hasPendingExecuteAjaxCall || !checkFormSendable()) {
+ return;
+ }
+
+ var request = {
+ "template": $("#template").val(),
+ "dataModel": $("#dataModel").val(),
+ "outputFormat": $("#outputFormat").val(),
+ "locale": $("#locale").val(),
+ "timeZone": $("#timeZone").val()
+ }
+
+ $.ajax({
+ method: "POST",
+ url: "/api/execute",
+ data: JSON.stringify(request),
+ headers: { "Content-Type":"application/json" },
+ beforeSend: function (jqXHR, options) {
+ hasPendingExecuteAjaxCall = true;
+ $.blockUI({ message: null });
+ $("#error").hide();
+ return true;
+ }
+ })
+ .done(function (data) {
+ if (data.problems && data.problems.length != 0) {
+ showResult(data.problems[0].message, true);
+ } else {
+ showResult(data.result, false);
+ }
+ })
+ .fail(function (data) {
+ if (data.responseJSON) {
+ showResult(data.responseJSON.errorCode + ": " + data.responseJSON.errorDescription, true);
+ } else {
+ showResult("The service was unavailable or had returned an invalid response.", true);
+ }
+ })
+ .always(function (data) {
+ hasPendingExecuteAjaxCall = false;
+ $.unblockUI();
+ });
+}
+
+function checkFormSendable() {
+ if($.trim($("#template").val()) === "" ) {
+ showResult("Template was empty; nothing to do.", true);
+ return false;
+ }
+ return true;
+}
+
+function showResult(result, isError) {
+ if (isError) {
+ $("#result").addClass("error");
+ } else {
+ $("#result").removeClass("error");
+ }
+ $("#result").val(result);
+ $(".resultContainer").show();
+ autosize.update($("#result"));
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/banner.txt
----------------------------------------------------------------------
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
new file mode 100644
index 0000000..784711d
--- /dev/null
+++ b/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+
+ ______ _ ____ _ _
+ | ____| | | / __ \ | (_)
+ | |__ _ __ ___ ___ _ __ ___ __ _ _ __| | _____ _ __ | | | |_ __ | |_ _ __ ___
+ | __| '__/ _ \/ _ \ '_ ` _ \ / _` | '__| |/ / _ \ '__| | | | | '_ \| | | '_ \ / _ \
+ | | | | | __/ __/ | | | | | (_| | | | < __/ | | |__| | | | | | | | | | __/
+ |_| |_| \___|\___|_| |_| |_|\__,_|_| |_|\_\___|_| \____/|_| |_|_|_|_| |_|\___|
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/freemarker-online.yml
----------------------------------------------------------------------
diff --git a/src/main/resources/freemarker-online.yml b/src/main/resources/freemarker-online.yml
new file mode 100644
index 0000000..370dd91
--- /dev/null
+++ b/src/main/resources/freemarker-online.yml
@@ -0,0 +1,22 @@
+# Spring configuration
+# Application Contexts to Load.
+applicationContext: ['classpath*:/spring/*-context.xml']
+
+logging:
+ # Settings for logging to a file.
+ file:
+ # If true, write log statements to a file.
+ enabled: true
+ threshold: ALL
+ # The file to which current statements will be logged.
+ currentLogFilename: /var/log/freemarker-online/freemarker-online.log
+
+ # When the log file rotates, the archived log will be renamed to this and gzipped. The
+ # %d is replaced with the previous day (yyyy-MM-dd). Custom rolling windows can be created
+ # by passing a SimpleDateFormat-compatible format as an argument: "%d{yyyy-MM-dd-hh}".
+ archivedLogFilenamePattern: /var/log/freemarker-online/freemarker-online-%d.log.gz
+ # The number of archived files to keep.
+ archivedFileCount: 5
+
+ # The timezone used to format dates. HINT: USE THE DEFAULT, UTC.
+ timeZone: UTC
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/spring/bootstrap-context.xml
----------------------------------------------------------------------
diff --git a/src/main/resources/spring/bootstrap-context.xml b/src/main/resources/spring/bootstrap-context.xml
new file mode 100644
index 0000000..2ea0c33
--- /dev/null
+++ b/src/main/resources/spring/bootstrap-context.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd ">
+
+ <context:component-scan base-package="com.kenshoo"/>
+
+ <context:annotation-config/>
+
+ <!-- Use the system properties (initalized by DW) to configure spring context files -->
+ <bean id="propertyPlaceholderConfigurer"
+ class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+ <property name="ignoreUnresolvablePlaceholders" value="true"/>
+ <property name="ignoreResourceNotFound" value="true"/>
+ </bean>
+
+</beans>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/view/freemarker-online.ftl
----------------------------------------------------------------------
diff --git a/src/main/resources/view/freemarker-online.ftl b/src/main/resources/view/freemarker-online.ftl
new file mode 100644
index 0000000..57746ff
--- /dev/null
+++ b/src/main/resources/view/freemarker-online.ftl
@@ -0,0 +1,129 @@
+<#ftl outputFormat="HTML">
+<#import "utils.ftl" as u>
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" href="css/main.css">
+ <link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.5.0/pure-min.css">
+
+ <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
+ <script src="js/jquery.blockUI.js"></script>
+ <script src="js/autosize.min.js"></script>
+ <script src="js/script.js"></script>
+ <script>
+ $(function() {
+ // Auto-focus on first form input:
+ $('#templateAndModelForm *:input[type!=hidden]:first').focus();
+
+ // Submit form when Ctrl+Enter is hit in a textarea:
+
+
+ // Dynamically adapt text areas heights to their content:
+ //$('#templateAndModelForm textarea').autosize();
+ autosize($('textarea'));
+
+ // Show/hide data model examples:
+ $("#showHideDataModelExamples").click(function(e) {
+ $("#dataModelExamples").toggle();
+ $("#hideDataModelExamplesLabel").toggle();
+ $("#showDataModelExamplesLabel").toggle();
+
+ e.preventDefault();
+ return false;
+ })
+ <#if execute>
+ execute();
+ </#if>
+ });
+ </script>
+
+ <title>Online FreeMarker Template Tester</title>
+</head>
+<body>
+<div id="layout">
+ <div id="main">
+ <div class="header">
+ <h1>Online FreeMarker Template Tester</h1>
+ </div>
+
+ <div class="content">
+ <!--[if lte IE 8]>
+ <div style="background-color: #C00; color: #fff; padding: 12px 24px;">
+ You seem to use Internet Explorer 8 or older. This page might won't work properly with that.
+ </div>
+ <![endif]-->
+
+ <form id="templateAndModelForm" method="post" class="pure-form pure-form-stacked">
+ <label for="template">Template <span class="faint">(Apache FreeMarker ${freeMarkerVersion})</span></label>
+ <textarea id="template" name="template" class="pure-input-1 source-code"
+ placeholder="Enter template, like: Hello ${r'${user}'}!"
+ >${template}</textarea>
+
+ <label for="template">
+ Data model
+ (<a id="showHideDataModelExamples" href="#" tabindex="-1"><!--
+ --><span id="showDataModelExamplesLabel">show</span><!--
+ --><span id="hideDataModelExamplesLabel" class="hiddenByDefault">hide</span>
+ examples</a>)
+ </label>
+ <div id="dataModelExamples" class="hiddenByDefault">
+ <div class="description">
+ Note: This syntax is specific to this online service; normally, you just have Java objects as
+ data-model.
+ </div>
+ <pre>someString = Some value
+otherString = "JSON\nsyntax"
+someNumber = 3.14
+someBoolean = true
+someDate = 2014-02-28
+someTime = 20:50:30.5+02:00
+someDatetime = 2014-02-28T18:50Z
+someList = ["JSON", "syntax", 1, 2, 3 ]
+someMap = { "JSON syntax": true, "nestedList": [1, 2, 3] }
+someXML = <example x="1">text</example></pre></div>
+ <textarea id="dataModel" name="dataModel" class="pure-input-1 source-code"
+ placeholder='Enter one or more assignments (e.g., user = John Doe), starting each in its own line.'
+ >${dataModel}</textarea>
+ <div class="formPanel">
+ <div class="horizontalBox">
+ <@u.htmlSelect caption="Output format" name="outputFormat" selectionOptions=outputFormats />
+ </div>
+ <div class="horizontalBox">
+ <@u.htmlSelect caption="Locale" name="locale" selectionOptions=locales />
+ </div>
+ <div class="horizontalBox">
+ <@u.htmlSelect caption="Time zone" name="timeZone" selectionOptions=timeZones />
+ </div>
+ </div>
+ <div class="formBottomButtonsContainer">
+ <input id="eval-btn" type="button" value="Evaluate" class="pure-button pure-button-primary"/>
+ <span class="faint">Ctrl+Enter in input fields will submit this form too</span>
+ </div>
+ <div style="display:none" class="resultContainer">
+ <label for="result">Result</label>
+ <textarea id="result" class="pure-input-1 source-code" readonly></textarea>
+ </div>
+
+ </form>
+ </div><!-- content -->
+
+ <div class="footer">
+ FreeMarker documentation:
+ <a href="http://freemarker.org/docs/" target="_blank">Contents</a>
+ |
+ <a href="http://freemarker.org/docs/dgui_template_overallstructure.html" target="_blank">Overall syntax</a>
+ |
+ <a href="http://freemarker.org/docs/dgui_template_exp.html#exp_cheatsheet" target="_blank">Expression syntax</a>
+ |
+ <a href="http://freemarker.org/docs/ref_directive_alphaidx.html" target="_blank">List of <#<i>directives</i>></a>
+ |
+ <a href="http://freemarker.org/docs/ref_builtins_alphaidx.html" target="_blank">List of <tt>?<i>built_in</i></tt> functions</a>
+ </div><!-- footer -->
+ </div><!-- main -->
+
+ <!-- Fork me on GitHub: -->
+ <a href="https://github.com/kenshoo/freemarker-online" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png"></a>
+</div><!-- layout -->
+</body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/main/resources/view/utils.ftl
----------------------------------------------------------------------
diff --git a/src/main/resources/view/utils.ftl b/src/main/resources/view/utils.ftl
new file mode 100644
index 0000000..cfa5ee6
--- /dev/null
+++ b/src/main/resources/view/utils.ftl
@@ -0,0 +1,13 @@
+<#ftl outputFormat='HTML'>
+
+<#macro htmlSelect caption name selectionOptions>
+ <div>${caption}:</div>
+ <div>
+ <select name="${name}" id="${name}" class="pure-input-1">
+ <#list selectionOptions as selectionOption>
+ <#local value = selectionOption.value>
+ <option value="${value}"<#if value == .vars[name]!> selected</#if>>${selectionOption.label}</option>
+ </#list>
+ </select>
+ </div>
+</#macro>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/platform/DropWizardServiceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/platform/DropWizardServiceTest.java b/src/test/java/com/kenshoo/freemarker/platform/DropWizardServiceTest.java
new file mode 100644
index 0000000..38f7e88
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/platform/DropWizardServiceTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.platform;
+
+import com.google.common.io.Resources;
+import com.kenshoo.freemarker.dropwizard.ApplicationStartup;
+import com.yammer.dropwizard.testing.junit.DropwizardServiceRule;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: shlomis
+ * Date: 9/9/13
+ * Time: 10:43 AM
+ */
+public class DropWizardServiceTest {
+ @ClassRule
+ public static TestRule testRule = new DropwizardServiceRule<>(ApplicationStartup.class,
+ Resources.getResource("freemarker-online.yml").getPath());
+
+
+ @Test
+ public void testServerIsUp() throws Exception {
+ ((DropwizardServiceRule) testRule).getService();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/platform/YamlPropertiesPersister.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/platform/YamlPropertiesPersister.java b/src/test/java/com/kenshoo/freemarker/platform/YamlPropertiesPersister.java
new file mode 100644
index 0000000..e01a881
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/platform/YamlPropertiesPersister.java
@@ -0,0 +1,93 @@
+/*
+ * 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.platform;
+
+import com.fasterxml.jackson.dataformat.yaml.snakeyaml.Yaml;
+import org.springframework.util.PropertiesPersister;
+import org.springframework.util.StringUtils;
+
+import java.io.*;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: shlomis
+ * Date: 9/8/13
+ * Time: 10:50 PM
+ */
+public class YamlPropertiesPersister implements PropertiesPersister {
+ @Override
+ public void load(Properties props, InputStream is) throws IOException {
+ load(props, new InputStreamReader(is));
+ }
+
+ /**
+ * We want to traverse map representing Yaml object and each time we find String=String pair we want to
+ * save it as Property. As we are going deeper into map we generate compound key as path-like String
+ *
+ * @see org.springframework.util.PropertiesPersister#load(java.util.Properties, java.io.Reader)
+ */
+ @Override
+ public void load(Properties props, Reader reader) throws IOException {
+ Yaml yaml = new Yaml();
+ @SuppressWarnings("unchecked")
+ Map<String, Object> map = (Map<String, Object>) yaml.load(reader);
+ // now we can populate supplied props
+ assignProperties(props, map, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void assignProperties(Properties props, Map<String, Object> map, String path) {
+ for (Map.Entry<String, Object> entry : map.entrySet()) {
+ String key = entry.getKey();
+ if (!StringUtils.isEmpty(path))
+ key = path + "." + key;
+ Object val = entry.getValue();
+ if (val instanceof String) {
+ // see if we need to create a compound key
+ props.put(key, val);
+ } else if (val instanceof Map) {
+ assignProperties(props, (Map<String, Object>) val, key);
+ }
+ }
+ }
+
+ @Override
+ public void store(Properties props, OutputStream os, String header) throws IOException {
+ throw new IllegalStateException("Current implementation is a read-only");
+ }
+
+ @Override
+ public void store(Properties props, Writer writer, String header) throws IOException {
+ throw new IllegalStateException("Current implementation is a read-only");
+ }
+
+ @Override
+ public void loadFromXml(Properties props, InputStream is) throws IOException {
+ throw new IllegalStateException("Use DefaultPropertiesPersister if you want to read/write XML");
+ }
+
+ @Override
+ public void storeToXml(Properties props, OutputStream os, String header) throws IOException {
+ throw new IllegalStateException("Use DefaultPropertiesPersister if you want to load/store to XML");
+ }
+
+ @Override
+ public void storeToXml(Properties props, OutputStream os, String header, String encoding) throws IOException {
+ throw new IllegalStateException("Use DefaultPropertiesPersister if you want to read/write XML");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/abb26297/src/test/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineExecuteResourceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineExecuteResourceTest.java b/src/test/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineExecuteResourceTest.java
new file mode 100644
index 0000000..f4886c8
--- /dev/null
+++ b/src/test/java/com/kenshoo/freemarker/resources/FreeMarkerOnlineExecuteResourceTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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 static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.context.request.RequestContextListener;
+
+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.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+
+/**
+ * Created by Pmuruge on 8/29/2015.
+ */
+public class FreeMarkerOnlineExecuteResourceTest extends JerseyTest {
+ private static final String DATA_MODEL = "user=John";
+ private static final String TEMPLATE_WITH_VARIABLE = "Welcome ${user}";
+ private static final String TEMPLATE_PLAIN = "Welcome John";
+ private static final String MALFORMED_DATA_MODEL = "userJohn";
+ private static final String EXECUTE_API = "api/execute";
+ @Override
+ protected AppDescriptor configure() {
+ return new WebAppDescriptor.Builder("com.kenshoo.freemarker.resources")
+ .contextPath("/")
+ .contextListenerClass(ContextLoaderListener.class)
+ .contextParam("contextConfigLocation", "classpath:spring/bootstrap-context.xml")
+ .servletClass(SpringServlet.class)
+ .requestListenerClass(RequestContextListener.class)
+ .build();
+ }
+
+ @Test
+ public void testSuccessRequest() throws Exception {
+ ExecuteRequest req = new ExecuteRequest(TEMPLATE_WITH_VARIABLE, DATA_MODEL);
+ ClientResponse resp = client().resource(getBaseURI().toString() + EXECUTE_API)
+ .header("Content-Type", "application/json").entity(req).post(ClientResponse.class);
+ assertEquals(200, resp.getStatus());
+ ExecuteResponse response = resp.getEntity(ExecuteResponse.class);
+ assertNull(response.getProblems());
+ }
+
+ @Test
+ public void testMalformedDataModel() throws Exception {
+ ExecuteRequest req = new ExecuteRequest(TEMPLATE_PLAIN, MALFORMED_DATA_MODEL);
+ ClientResponse resp = client().resource(getBaseURI().toString() + EXECUTE_API)
+ .header("Content-Type", "application/json").entity(req).post(ClientResponse.class);
+ assertEquals(200, resp.getStatus());
+ ExecuteResponse response = resp.getEntity(ExecuteResponse.class);
+ assertNotNull(response.getProblems());
+ assertTrue(containsProblem(response, ExecuteResourceField.DATA_MODEL));
+ }
+
+ @Test
+ public void testLongDataModel() throws Exception {
+ ExecuteRequest req = new ExecuteRequest(TEMPLATE_PLAIN, create30KString());
+ ClientResponse resp = client().resource(getBaseURI().toString() + EXECUTE_API)
+ .header("Content-Type", "application/json").entity(req).post(ClientResponse.class);
+ assertEquals(200, resp.getStatus());
+ ExecuteResponse response = resp.getEntity(ExecuteResponse.class);
+ assertNotNull(response.getProblems());
+ assertTrue(containsProblem(response, ExecuteResourceField.DATA_MODEL));
+ String problemMessage = getProblemMessage(response, ExecuteResourceField.DATA_MODEL);
+ assertThat(problemMessage, containsString("data model"));
+ assertThat(problemMessage, containsString("limit"));
+ }
+
+ @Test
+ public void testLongTemplate() throws Exception {
+ ExecuteRequest req = new ExecuteRequest(create30KString(), DATA_MODEL);
+ ClientResponse resp = client().resource(getBaseURI().toString() + EXECUTE_API)
+ .header("Content-Type", "application/json").entity(req).post(ClientResponse.class);
+ assertEquals(200, resp.getStatus());
+ ExecuteResponse response = resp.getEntity(ExecuteResponse.class);
+ assertNotNull(response.getProblems());
+ assertTrue(containsProblem(response, ExecuteResourceField.TEMPLATE));
+ String problemMessage = getProblemMessage(response, ExecuteResourceField.TEMPLATE);
+ assertThat(problemMessage, containsString("template"));
+ assertThat(problemMessage, containsString("limit"));
+ }
+
+ @Test
+ public void testMultipleErrorsDataModel() throws Exception {
+ ExecuteRequest req = new ExecuteRequest(create30KString(), create30KString());
+ req.setOutputFormat("wrongOutputFormat");
+ req.setLocale("wrongLocale");
+ req.setTimeZone("wrongTimeZone");
+
+ ClientResponse resp = client().resource(getBaseURI() + EXECUTE_API)
+ .header("Content-Type", "application/json").entity(req).post(ClientResponse.class);
+
+ assertEquals(200, resp.getStatus());
+ ExecuteResponse response = resp.getEntity(ExecuteResponse.class);
+ assertNotNull(response.getProblems());
+ assertThat(getProblemMessage(response, ExecuteResourceField.TEMPLATE), containsString("limit"));
+ assertThat(getProblemMessage(response, ExecuteResourceField.DATA_MODEL), containsString("limit"));
+ assertThat(getProblemMessage(response, ExecuteResourceField.OUTPUT_FORMAT), containsString("wrongOutputFormat"));
+ assertThat(getProblemMessage(response, ExecuteResourceField.LOCALE), containsString("wrongLocale"));
+ assertThat(getProblemMessage(response, ExecuteResourceField.TIME_ZONE), containsString("wrongTimeZone"));
+ }
+
+ private String create30KString() {
+ final String veryLongString;
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 30000 / 10; i++) {
+ sb.append("0123456789");
+ }
+ veryLongString = sb.toString();
+ }
+ return veryLongString;
+ }
+
+ private boolean containsProblem(ExecuteResponse response, ExecuteResourceField field) {
+ for (ExecuteResourceProblem problem : response.getProblems()) {
+ if (problem.getField() == field) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String getProblemMessage(ExecuteResponse response, ExecuteResourceField field) {
+ for (ExecuteResourceProblem problem : response.getProblems()) {
+ if (problem.getField() == field) {
+ return problem.getMessage();
+ }
+ }
+ return null;
+ }
+
+}