diff --git a/.gitignore b/.gitignore
index 17cecc2..9d93787 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.idea
.classpath
.project
.settings/
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..3b00020
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,125 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
diff --git a/README.md b/README.md
index d863697..9ba29c5 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ $ git clone https://github.com/scratches/spring-boot-legacy
$ (cd spring-boot-legacy; mvn install)
$ git clone https://github.com/scratches/spring-boot-sample-gae
$ cd spring-boot-sample-gae
-$ mvn gae:deploy
+$ mvn appengine:update
```
-Also runs as a deployed WAR in WTP or regular Tomcat container. The `main()` app (normal Spring Boot launcher) should also work.
\ No newline at end of file
+Also runs as a deployed jar in a Jetty container. The `main()` app (normal Spring Boot launcher) should also work.
\ No newline at end of file
diff --git a/gae-demo.iml b/gae-demo.iml
new file mode 100644
index 0000000..26c36e3
--- /dev/null
+++ b/gae-demo.iml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index f3eaeec..b2ac86b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.demo
gae-demo
- 0.0.1-SNAPSHOT
+ 0.0.2-SNAPSHOT
war
gae
@@ -14,35 +14,36 @@
org.springframework.boot
spring-boot-starter-parent
- 1.1.0.BUILD-SNAPSHOT
+ 1.1.2.BUILD-SNAPSHOT
-
- org.springframework.boot
- spring-boot-starter-web
-
-
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jetty
+
+
+
org.springframework.boot
spring-boot-starter-actuator
-
- org.springframework.boot
- spring-boot-starter-tomcat
- provided
-
+
org.springframework.boot
spring-boot-legacy
- 1.1.0.BUILD-SNAPSHOT
-
-
- net.kindleit
- gae-runtime
- ${gae.version}
- pom
- provided
+ 1.1.2.BUILD-SNAPSHOT
+
org.springframework.boot
spring-boot-starter-test
@@ -79,7 +80,7 @@
UTF-8
1.7
/
- 1.8.8
+ 1.9.12
${settings.localRepository}/com/google/appengine/appengine-java-sdk/${gae.version}/appengine-java-sdk-${gae.version}
test
@@ -90,33 +91,29 @@
org.springframework.boot
spring-boot-maven-plugin
-
- net.kindleit
- maven-gae-plugin
- 0.9.6
-
-
- net.kindleit
- gae-runtime
- ${gae.version}
- pom
-
-
-
-
- maven-release-plugin
-
- gae:deploy
-
-
-
- org.apache.tomcat.maven
- tomcat6-maven-plugin
- 2.0
-
- /
-
-
+
+ com.google.appengine
+ appengine-maven-plugin
+ ${gae.version}
+
+ false
+
+ -Xdebug
+ -agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n
+
+
+
+
+
+ endpoints_get_discovery_doc
+
+
+ src/main/webapp/WEB-INF/web.xml
+
+
+
+
+
diff --git a/src/main/java/demo/Application.java b/src/main/java/demo/Application.java
index 76be452..09c2ba8 100644
--- a/src/main/java/demo/Application.java
+++ b/src/main/java/demo/Application.java
@@ -4,27 +4,17 @@
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
@Configuration
-@ComponentScan
@EnableAutoConfiguration
-@RestController
-public class Application {
+@ComponentScan
+public class Application
+{
+
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
-
- @RequestMapping("/")
- public String home() {
- return "Hello World";
- }
- @RequestMapping("/version")
- public String getVersion() {
- return "1.0";
- }
}
diff --git a/src/main/java/demo/Item.java b/src/main/java/demo/Item.java
new file mode 100644
index 0000000..47bdeb4
--- /dev/null
+++ b/src/main/java/demo/Item.java
@@ -0,0 +1,46 @@
+package demo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Item {
+ private static final Logger log = LoggerFactory.getLogger(Item.class);
+ private final String id = java.util.UUID.randomUUID().toString();
+ private final String value;
+ private long creationTimeMs = 0;
+
+ public Item(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void startClockTicking() {
+ if (creationTimeMs == 0) //protect against being called twice.
+ {
+ creationTimeMs = System.currentTimeMillis();
+ log.info("started clock ticking");
+ }
+ }
+
+ public long getCreationTimeMs() {
+ return creationTimeMs;
+ }
+
+
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ return "Item{" +
+ "id='" + id + '\'' +
+ ", value='" + value + '\'' +
+ ", creationTimeMs=" + creationTimeMs +
+ '}';
+ }
+}
+
diff --git a/src/main/java/demo/QueueItemListener.java b/src/main/java/demo/QueueItemListener.java
new file mode 100644
index 0000000..8b4e9af
--- /dev/null
+++ b/src/main/java/demo/QueueItemListener.java
@@ -0,0 +1,7 @@
+package demo;
+
+
+public interface QueueItemListener
+{
+ void onTrigger(Item item);
+}
diff --git a/src/main/java/demo/controller/CronController.java b/src/main/java/demo/controller/CronController.java
new file mode 100644
index 0000000..3a81b8a
--- /dev/null
+++ b/src/main/java/demo/controller/CronController.java
@@ -0,0 +1,55 @@
+package demo.controller;
+
+import demo.service.SimpleItemQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Created by will on 01/10/2014.
+ */
+
+@RestController
+public class CronController {
+
+ private static final Logger log = LoggerFactory.getLogger(CronController.class);
+
+ @Autowired
+ SimpleItemQueue simpleItemQueue;
+
+
+ @RequestMapping(value = "/cron/item",
+ method = RequestMethod.GET,
+ consumes = "*",
+ headers = {"content-type=application/X-AppEngine-Cron"})
+ @ResponseBody
+ public ResponseEntity purgeQueue(HttpServletRequest request)
+ {
+
+
+ log.info("== called ==");
+
+ log.debug("userAgent =" + request.getHeader("User-Agent"));
+ log.debug("host =" + request.getHeader("Host"));
+ log.debug("X-AppEngine-Cron =" + request.getHeader("X-AppEngine-Cron"));
+ log.debug("X-AppEngine-QueueName =" + request.getHeader("X-AppEngine-QueueName"));
+ log.debug("X-AppEngine-TaskName =" + request.getHeader("X-AppEngine-TaskName"));
+ log.debug("X-AppEngine-TaskRetryCount =" + request.getHeader("X-AppEngine-TaskRetryCount"));
+
+ boolean isCronTask = "true".equals(request.getHeader("X-AppEngine-Cron"));
+ //String queueName = request.getHeader("X-AppEngine-QueueName");
+
+ if(isCronTask)
+ simpleItemQueue.manuallySeekTimeouts();
+ else
+ log.warn("someone tried to invoke cron");
+
+ return new ResponseEntity(HttpStatus.OK);
+ }
+
+}
diff --git a/src/main/java/demo/controller/ItemController.java b/src/main/java/demo/controller/ItemController.java
new file mode 100644
index 0000000..b02ff53
--- /dev/null
+++ b/src/main/java/demo/controller/ItemController.java
@@ -0,0 +1,60 @@
+package demo.controller;
+
+import demo.Item;
+import demo.pojo.ItemDisplay;
+import demo.service.SimpleItemQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * Created by will on 01/10/2014.
+ */
+
+@RestController
+public class ItemController {
+
+ private static final Logger log = LoggerFactory.getLogger(ItemController.class);
+
+ @Autowired
+ SimpleItemQueue simpleItemQueue;
+
+ @RequestMapping("/")
+ public String home() {
+ return "Hello World";
+ }
+
+ @RequestMapping("/version")
+ public String getVersion() {
+ return "1.0";
+ }
+
+
+ @RequestMapping(value = "/item/add", method = RequestMethod.POST, consumes = "application/json",
+ produces = "application/json",
+ headers = {"content-type=application/json"})
+ @ResponseBody
+ public void submitRfq(@RequestBody ItemDisplay itemDisplay) {
+ log.info("adding " + itemDisplay.value + " to queue");
+
+ Item item = new Item(itemDisplay.value);
+ simpleItemQueue.add(item);
+ }
+
+ @RequestMapping(value = "/item/size",
+ method = RequestMethod.GET,
+ consumes = "*",
+ produces = "application/json",
+ headers = {"content-type=application/json"})
+ @ResponseBody
+ public String getAll() {
+
+ return String.valueOf(simpleItemQueue.size());
+
+ }
+
+
+}
diff --git a/src/main/java/demo/pojo/ItemDisplay.java b/src/main/java/demo/pojo/ItemDisplay.java
new file mode 100644
index 0000000..160a711
--- /dev/null
+++ b/src/main/java/demo/pojo/ItemDisplay.java
@@ -0,0 +1,8 @@
+package demo.pojo;
+
+/**
+ * Created by will on 01/10/2014.
+ */
+public class ItemDisplay {
+ public String value;
+}
diff --git a/src/main/java/demo/queue/IQueueService.java b/src/main/java/demo/queue/IQueueService.java
new file mode 100644
index 0000000..fb5ada3
--- /dev/null
+++ b/src/main/java/demo/queue/IQueueService.java
@@ -0,0 +1,18 @@
+package demo.queue;
+
+import demo.Item;
+import demo.QueueItemListener;
+
+
+public interface IQueueService
+{
+ void add(Item item);
+
+ int size();
+
+ Item find(String id);
+
+ void manuallySeekTimeouts();
+
+ void clear();
+}
diff --git a/src/main/java/demo/service/SimpleItemQueue.java b/src/main/java/demo/service/SimpleItemQueue.java
new file mode 100644
index 0000000..fc3c712
--- /dev/null
+++ b/src/main/java/demo/service/SimpleItemQueue.java
@@ -0,0 +1,104 @@
+package demo.service;
+
+
+import demo.Item;
+import demo.QueueItemListener;
+import demo.queue.IQueueService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * This is a queue that self purges every 30 seconds.
+ */
+
+@Service
+public class SimpleItemQueue implements IQueueService
+{
+ private static final Logger log = LoggerFactory.getLogger(SimpleItemQueue.class);
+
+ static String MAX_QUEUE_LIFETIME_SECONDS = "30";
+
+ private final Long timeout = Long.parseLong(MAX_QUEUE_LIFETIME_SECONDS) * 1000L;
+ private final ConcurrentLinkedQueue- fifoQueue = new ConcurrentLinkedQueue<>();
+ private QueueItemListener rfqListener = new SelfPurger();
+
+
+ public SimpleItemQueue(){
+ log.info("called");
+ }
+
+ @Override
+ public void add(Item rfq)
+ {
+ rfq.startClockTicking();
+ fifoQueue.add(rfq);
+ }
+
+ @Override
+ public int size()
+ {
+ return fifoQueue.size();
+ }
+
+
+ @Override
+ public Item find(String id)
+ {
+ Iterator
- iterator = fifoQueue.iterator();
+ Item found = null;
+ while(iterator.hasNext())
+ {
+ found = iterator.next();
+ if(found.getId().equals(id))
+ return found;
+ }
+
+ return null;
+ }
+
+ @Override
+ public void manuallySeekTimeouts()
+ {
+
+ log.info(" seeking timeout");
+ long now = System.currentTimeMillis();
+ long elapsedTimeMs;
+ Iterator
- iterator = fifoQueue.iterator();
+ while(iterator.hasNext())
+ {
+ Item item = iterator.next();
+ elapsedTimeMs = now - item.getCreationTimeMs();
+ if(elapsedTimeMs >= timeout)
+ {
+ log.info("found a timedout id = " + item.getId());
+
+ rfqListener.onTrigger(item);
+ }
+ else
+ {
+ log.info("elapsedTimeMs = " + elapsedTimeMs);
+ }
+ }
+
+ }
+
+ @Override
+ public void clear() {
+ fifoQueue.clear();
+ }
+
+
+ class SelfPurger implements QueueItemListener
+ {
+
+ @Override
+ public void onTrigger(Item rfq)
+ {
+ fifoQueue.remove(rfq);
+ }
+ }
+}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 266ac62..a8afa73 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -1,4 +1,8 @@
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/appengine-web.xml b/src/main/webapp/WEB-INF/appengine-web.xml
index 23972a2..8219f6e 100644
--- a/src/main/webapp/WEB-INF/appengine-web.xml
+++ b/src/main/webapp/WEB-INF/appengine-web.xml
@@ -1,6 +1,6 @@
- dsyerboot
- 4
+ your_unique_app_here
+ 3
true
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/cron.xml b/src/main/webapp/WEB-INF/cron.xml
new file mode 100644
index 0000000..0cc46b7
--- /dev/null
+++ b/src/main/webapp/WEB-INF/cron.xml
@@ -0,0 +1,8 @@
+
+
+
+ /cron/item/
+ polling item Queue
+ every 1 mins synchronized
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index 6e9d6f9..b35ed50 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -12,16 +12,6 @@
org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener
-
- metricFilter
- org.springframework.web.filter.DelegatingFilterProxy
-
-
-
- metricFilter
- /*
-
-
appServlet
org.springframework.web.servlet.DispatcherServlet
@@ -37,4 +27,10 @@
/
+
+ defaultHtmlEscape
+ true
+
+
+
diff --git a/src/test/java/demo/AllIntegrationTests.java b/src/test/java/demo/BasicIntegrationTest.java
similarity index 90%
rename from src/test/java/demo/AllIntegrationTests.java
rename to src/test/java/demo/BasicIntegrationTest.java
index 294467e..5a4e9c0 100644
--- a/src/test/java/demo/AllIntegrationTests.java
+++ b/src/test/java/demo/BasicIntegrationTest.java
@@ -25,9 +25,9 @@
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest("server.port=0")
-public class AllIntegrationTests {
+public class BasicIntegrationTest {
- private static final Logger log = Logger.getLogger(AllIntegrationTests.class);
+ private static final Logger log = Logger.getLogger(BasicIntegrationTest.class);
@Value("${local.server.port}")
private int port;
diff --git a/src/test/java/demo/mock/CronSimulator.java b/src/test/java/demo/mock/CronSimulator.java
new file mode 100644
index 0000000..2da83c8
--- /dev/null
+++ b/src/test/java/demo/mock/CronSimulator.java
@@ -0,0 +1,45 @@
+package demo.mock;
+/*
+ *
+ * This class has a thread inside it - to simulate cron
+ */
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import demo.queue.*;
+
+public class CronSimulator implements Runnable {
+ private static final Logger log = LoggerFactory.getLogger(CronSimulator.class);
+ private final ScheduledExecutorService cronSimulator = Executors.newScheduledThreadPool(1);
+ private ScheduledFuture> scannerHandle;
+
+ private final IQueueService listener;
+
+ public CronSimulator(IQueueService listener) {
+ this.listener = listener;
+ }
+
+
+ public void start() {
+ log.info("start");
+ scannerHandle = cronSimulator.scheduleAtFixedRate(this, 0, 3, TimeUnit.SECONDS);
+ }
+
+ public void stop() {
+ log.info("stop");
+ scannerHandle.cancel(true);
+ }
+
+ @Override
+ public void run() {
+ listener.manuallySeekTimeouts();
+ }
+
+
+}
diff --git a/src/test/java/demo/service/SimpleItemQueueTest.java b/src/test/java/demo/service/SimpleItemQueueTest.java
new file mode 100644
index 0000000..34b20a1
--- /dev/null
+++ b/src/test/java/demo/service/SimpleItemQueueTest.java
@@ -0,0 +1,75 @@
+package demo.service;
+
+
+import demo.Application;
+import demo.Item;
+import demo.mock.CronSimulator;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import javax.annotation.PostConstruct;
+
+@ActiveProfiles("test")
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = Application.class)
+@WebAppConfiguration
+@IntegrationTest
+public class SimpleItemQueueTest
+{
+ private static final Logger log = LoggerFactory.getLogger(SimpleItemQueueTest.class);
+
+ @BeforeClass
+ public static void init()
+ {
+ SimpleItemQueue.MAX_QUEUE_LIFETIME_SECONDS = "3";
+ }
+
+ @Autowired
+ private SimpleItemQueue simpleItemQueue;
+
+ private CronSimulator cronSimulator;
+
+ @PostConstruct
+ public void start()
+ {
+ cronSimulator = new CronSimulator(simpleItemQueue);
+ }
+
+ @After
+ public void end()
+ {
+ simpleItemQueue.clear();
+ cronSimulator.stop();
+ }
+
+ @Test
+ public void testCleanExpiredTokens() throws Exception
+ {
+ cronSimulator.start();
+
+ Item one = new Item("one");
+
+ simpleItemQueue.add(one);
+
+ Assert.assertEquals(1, simpleItemQueue.size());
+
+ log.info("sleeping");
+ Thread.sleep(6000);
+ log.info("waking");
+
+ Assert.assertEquals(0, simpleItemQueue.size());
+
+
+ }
+}
\ No newline at end of file