diff --git a/license.txt b/license.txt
new file mode 100644
index 0000000..28631e6
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,32 @@
+====
+ MOTECH PLATFORM OPENSOURCE LICENSE AGREEMENT
+
+ Copyright (c) 2011 Grameen Foundation USA. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ 3. Neither the name of Grameen Foundation USA, nor its respective contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY GRAMEEN FOUNDATION USA AND ITS CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL GRAMEEN FOUNDATION USA OR ITS CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ OF SUCH DAMAGE.
+====
+
diff --git a/motech-delivery-config/motech-delivery-config.iml b/motech-delivery-config/motech-delivery-config.iml
deleted file mode 100644
index ef582b1..0000000
--- a/motech-delivery-config/motech-delivery-config.iml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/motech-delivery-config/pom.xml b/motech-delivery-config/pom.xml
index 8b6bfc7..7238088 100644
--- a/motech-delivery-config/pom.xml
+++ b/motech-delivery-config/pom.xml
@@ -5,11 +5,9 @@
motech-delivery
motech-delivery
- 0.1
+ 0.3-SNAPSHOT
4.0.0
-
motech-delivery-config
-
-
+ Motech Delivery Config
\ No newline at end of file
diff --git a/motech-delivery-deploy/motech-delivery-deploy.iml b/motech-delivery-deploy/motech-delivery-deploy.iml
deleted file mode 100644
index ef582b1..0000000
--- a/motech-delivery-deploy/motech-delivery-deploy.iml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/motech-delivery-deploy/pom.xml b/motech-delivery-deploy/pom.xml
index 851d249..7f92314 100644
--- a/motech-delivery-deploy/pom.xml
+++ b/motech-delivery-deploy/pom.xml
@@ -5,11 +5,9 @@
motech-delivery
motech-delivery
- 0.1
+ 0.3-SNAPSHOT
4.0.0
-
motech-delivery-deploy
-
-
+ Motech Delivery Deploy
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-datetime-simulator/pom.xml b/motech-delivery-tools/motech-datetime-simulator/pom.xml
new file mode 100644
index 0000000..8b3533f
--- /dev/null
+++ b/motech-delivery-tools/motech-datetime-simulator/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+ motech-delivery-tools
+ motech-delivery
+ 0.3-SNAPSHOT
+
+ 4.0.0
+
+ motech-datetime-simulator
+ Motech DateTime Simulator
+ jar
+
+
+ 3.0.5.RELEASE
+ 1.6.1
+ UTF-8
+ 0.3-SNAPSHOT
+
+
+
+
+ org.springframework
+ spring-webmvc
+ ${spring.version}
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.motechproject
+ motech-platform-common
+ ${motech.version}
+
+
+ commons-lang
+ commons-lang
+ 2.6
+ provided
+
+
+ javax.servlet
+ servlet-api
+ 2.5
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 2.1.1
+
+
+
+
+
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-datetime-simulator/src/main/java/org/motechproject/deliverytools/datetimesimulator/domain/TimeMachine.java b/motech-delivery-tools/motech-datetime-simulator/src/main/java/org/motechproject/deliverytools/datetimesimulator/domain/TimeMachine.java
new file mode 100644
index 0000000..292c71f
--- /dev/null
+++ b/motech-delivery-tools/motech-datetime-simulator/src/main/java/org/motechproject/deliverytools/datetimesimulator/domain/TimeMachine.java
@@ -0,0 +1,48 @@
+package org.motechproject.deliverytools.datetimesimulator.domain;
+
+import org.apache.commons.lang.StringUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.motechproject.util.DateUtil;
+import org.motechproject.util.datetime.DateTimeSource;
+
+public class TimeMachine implements DateTimeSource {
+ private String today;
+ private String hour;
+ private String minute;
+ private DateTimeSource dateTimeSource;
+
+ public TimeMachine(DateTimeSource dateTimeSource) {
+ this.dateTimeSource = dateTimeSource;
+ }
+
+ @Override
+ public DateTimeZone timeZone() {
+ return dateTimeSource.timeZone();
+ }
+
+ @Override
+ public DateTime now() {
+ DateTime dateTime = new DateTime(timeZone());
+ LocalDate today = today();
+ dateTime = dateTime.withYear(today.getYear()).withMonthOfYear(today.getMonthOfYear()).withDayOfMonth(today.getDayOfMonth());
+
+ if (StringUtils.isEmpty(hour) || StringUtils.isEmpty(minute)) return dateTime;
+ return dateTime.withHourOfDay(Integer.parseInt(hour)).withMinuteOfHour(Integer.parseInt(minute));
+ }
+
+ @Override
+ public LocalDate today() {
+ if (StringUtils.isEmpty(today)) return new LocalDate(timeZone());
+
+ LocalDate configuredDate = LocalDate.parse(today);
+ return new LocalDate(timeZone()).withYear(configuredDate.getYear()).withMonthOfYear(configuredDate.getMonthOfYear()).withDayOfMonth(configuredDate.getDayOfMonth());
+ }
+
+ public void update(String date, String hour, String minute) {
+ this.today = StringUtils.isEmpty(date) ? this.today : date;
+ this.hour = StringUtils.isEmpty(hour) ? this.hour : hour;
+ this.minute = StringUtils.isEmpty(minute) ? this.minute : minute;
+ }
+}
diff --git a/motech-delivery-tools/motech-datetime-simulator/src/main/java/org/motechproject/deliverytools/datetimesimulator/web/DateTimeController.java b/motech-delivery-tools/motech-datetime-simulator/src/main/java/org/motechproject/deliverytools/datetimesimulator/web/DateTimeController.java
new file mode 100644
index 0000000..73d9fca
--- /dev/null
+++ b/motech-delivery-tools/motech-datetime-simulator/src/main/java/org/motechproject/deliverytools/datetimesimulator/web/DateTimeController.java
@@ -0,0 +1,44 @@
+package org.motechproject.deliverytools.datetimesimulator.web;
+
+import org.apache.log4j.Logger;
+import org.motechproject.deliverytools.datetimesimulator.domain.TimeMachine;
+import org.motechproject.util.DateTimeSourceUtil;
+import org.motechproject.util.DateUtil;
+import org.motechproject.util.datetime.DefaultDateTimeSource;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletResponse;
+
+@RequestMapping("/motech-delivery-tools/datetime")
+@Controller
+public class DateTimeController {
+ private Logger logger = Logger.getLogger(this.getClass().getName());
+
+ @RequestMapping(value = "update", method = RequestMethod.GET)
+ @ResponseBody
+ public String update(@RequestParam String date, @RequestParam String hour, @RequestParam String minute, HttpServletResponse response) {
+ try {
+ TimeMachine sourceInstance;
+ if (!(DateTimeSourceUtil.SourceInstance instanceof TimeMachine)) {
+ DateTimeSourceUtil.SourceInstance = new TimeMachine(DateTimeSourceUtil.SourceInstance);
+ }
+ sourceInstance = (TimeMachine) DateTimeSourceUtil.SourceInstance;
+ sourceInstance.update(date, hour, minute);
+ return String.format("Successfully set datetime to: %s", DateUtil.now());
+ } catch (Exception e) {
+ response.setStatus(500);
+ logger.error(String.format("Could not set the datetime from Date=%s, Hour=%s, Minute=%s. Did you use something like: 2011-10-17", date, hour, minute), e);
+ return e.toString();
+ }
+ }
+
+ @RequestMapping(value = "get", method = RequestMethod.GET)
+ @ResponseBody
+ public String get() {
+ return DateUtil.today().toString();
+ }
+}
diff --git a/motech-delivery-tools/motech-datetime-simulator/src/main/resources/applicationDateTimeSimulatorContext.xml b/motech-delivery-tools/motech-datetime-simulator/src/main/resources/applicationDateTimeSimulatorContext.xml
new file mode 100644
index 0000000..88a3d7a
--- /dev/null
+++ b/motech-delivery-tools/motech-datetime-simulator/src/main/resources/applicationDateTimeSimulatorContext.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-datetime-simulator/src/test/java/org/motechproject/deliverytools/datetimesimulator/domain/TimeMachineTest.java b/motech-delivery-tools/motech-datetime-simulator/src/test/java/org/motechproject/deliverytools/datetimesimulator/domain/TimeMachineTest.java
new file mode 100644
index 0000000..da0ed52
--- /dev/null
+++ b/motech-delivery-tools/motech-datetime-simulator/src/test/java/org/motechproject/deliverytools/datetimesimulator/domain/TimeMachineTest.java
@@ -0,0 +1,70 @@
+package org.motechproject.deliverytools.datetimesimulator.domain;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.junit.Before;
+import org.junit.Test;
+import org.motechproject.util.datetime.DateTimeSource;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TimeMachineTest {
+ private TimeMachine timeMachine;
+
+ @Before
+ public void setUp() {
+ DateTimeSource dateTimeSource = mock(DateTimeSource.class);
+ when(dateTimeSource.timeZone()).thenReturn(DateTimeZone.UTC);
+ timeMachine = new TimeMachine(dateTimeSource);
+ }
+
+ @Test
+ public void whenDateAndTimeIsNotSetShouldBeToday() {
+ assertNotNull(timeMachine.today());
+ assertNotNull(timeMachine.now());
+ }
+
+ @Test
+ public void whenOnlyDateIsSetup() {
+ timeMachine.update("2011-10-17", "", null);
+ assertDate();
+ }
+
+ private void assertDate() {
+ //Joda should have used interface for getYear/month etc methods
+ LocalDate today = timeMachine.today();
+ assertNotNull(today);
+ assertEquals(2011, today.getYear());
+ assertEquals(10, today.getMonthOfYear());
+ assertEquals(17, today.getDayOfMonth());
+
+ DateTime now = timeMachine.now();
+ assertNotNull(now);
+ assertEquals(2011, now.getYear());
+ assertEquals(10, now.getMonthOfYear());
+ assertEquals(17, now.getDayOfMonth());
+ }
+
+ @Test
+ public void whenBothDateAndTimeIsSet() {
+ timeMachine.update("2011-10-17", "14", "20");
+ assertDate();
+ DateTime now = timeMachine.now();
+ assertEquals(14, now.getHourOfDay());
+ assertEquals(20, now.getMinuteOfHour());
+ }
+
+ @Test
+ public void useTheLastSetValues() {
+ timeMachine.update("2011-10-17", "14", "20");
+ timeMachine.update(null, null, null);
+ assertDate();
+ DateTime now = timeMachine.now();
+ assertEquals(14, now.getHourOfDay());
+ assertEquals(20, now.getMinuteOfHour());
+ }
+}
diff --git a/motech-delivery-tools/motech-datetime-simulator/src/test/java/org/motechproject/deliverytools/datetimesimulator/web/DateTimeControllerTest.java b/motech-delivery-tools/motech-datetime-simulator/src/test/java/org/motechproject/deliverytools/datetimesimulator/web/DateTimeControllerTest.java
new file mode 100644
index 0000000..e724da7
--- /dev/null
+++ b/motech-delivery-tools/motech-datetime-simulator/src/test/java/org/motechproject/deliverytools/datetimesimulator/web/DateTimeControllerTest.java
@@ -0,0 +1,61 @@
+package org.motechproject.deliverytools.datetimesimulator.web;
+
+import org.joda.time.DateTimeZone;
+import org.junit.Before;
+import org.junit.Test;
+import org.motechproject.util.DateTimeSourceUtil;
+import org.motechproject.util.DateUtil;
+import org.motechproject.util.datetime.DateTimeConfiguration;
+import org.motechproject.util.datetime.ExternalDateTimeSource;
+
+import javax.servlet.http.HttpServletResponse;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.*;
+
+public class DateTimeControllerTest {
+ private DateTimeController controller;
+ private HttpServletResponse servletResponse;
+
+ @Before
+ public void setUp() {
+ controller = new DateTimeController();
+ servletResponse = mock(HttpServletResponse.class);
+ }
+
+ @Test
+ public void invalidInput() {
+ controller.update("afsdff", null, null, servletResponse);
+ verify(servletResponse).setStatus(500);
+ }
+
+ @Test
+ public void todayIsNotSetWhenExternalDateTimeSourceIsNotSet() {
+ controller.update("2011-10-17", null, null, servletResponse);
+ verifyZeroInteractions(servletResponse);
+ }
+
+ @Test
+ public void todayIsSet() {
+ DateTimeConfiguration dateTimeConfiguration = mock(DateTimeConfiguration.class);
+ when(dateTimeConfiguration.timeZone()).thenReturn(DateTimeZone.UTC);
+ DateTimeSourceUtil.SourceInstance = new ExternalDateTimeSource(dateTimeConfiguration);
+
+ controller.update("2011-10-17", "", "", servletResponse);
+ assertEquals(10, DateUtil.today().getMonthOfYear());
+ assertEquals(17, DateUtil.now().getDayOfMonth());
+ verifyZeroInteractions(servletResponse);
+ }
+
+ @Test
+ public void setTimeWithoutDate() {
+ DateTimeConfiguration dateTimeConfiguration = mock(DateTimeConfiguration.class);
+ when(dateTimeConfiguration.timeZone()).thenReturn(DateTimeZone.UTC);
+ DateTimeSourceUtil.SourceInstance = new ExternalDateTimeSource(dateTimeConfiguration);
+
+ controller.update("2011-10-17", "14", "30", servletResponse);
+ assertEquals(10, DateUtil.today().getMonthOfYear());
+ assertEquals(30, DateUtil.now().getMinuteOfHour());
+ verifyZeroInteractions(servletResponse);
+ }
+}
diff --git a/motech-delivery-tools/motech-deliverytools-common/pom.xml b/motech-delivery-tools/motech-deliverytools-common/pom.xml
new file mode 100644
index 0000000..5da27ac
--- /dev/null
+++ b/motech-delivery-tools/motech-deliverytools-common/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ motech-delivery
+ motech-delivery
+ 0.3-SNAPSHOT
+
+ 4.0.0
+
+ motech-deliverytools-common
+ Motech Delivery Common
+
+
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-deliverytools-common/src/main/java/org/motechproject/deliverytools/common/DeliveryToolsObject.java b/motech-delivery-tools/motech-deliverytools-common/src/main/java/org/motechproject/deliverytools/common/DeliveryToolsObject.java
new file mode 100644
index 0000000..56a3f26
--- /dev/null
+++ b/motech-delivery-tools/motech-deliverytools-common/src/main/java/org/motechproject/deliverytools/common/DeliveryToolsObject.java
@@ -0,0 +1,25 @@
+package org.motechproject.deliverytools.common;
+
+import org.apache.log4j.Logger;
+
+public class DeliveryToolsObject {
+ protected Logger logger = Logger.getLogger(this.getClass());
+
+ static {
+ doNotInheritRootLoggerFor("org.motechproject");
+ doNotInheritRootLoggerFor("org.motechproject.deliverytools");
+ }
+
+ protected static void doNotInheritRootLoggerFor(String name) {
+ Logger logger = Logger.getLogger(name);
+ logger.setAdditivity(false);
+ }
+
+ protected void logInfo(String message, String ... params) {
+ logger.info(String.format(message, params));
+ }
+
+ protected void logInfo(String message, Object ... params) {
+ logger.info(String.format(message, params));
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/logs/deliverytools.log b/motech-delivery-tools/motech-job-handler-invoker/logs/deliverytools.log
new file mode 100644
index 0000000..e69de29
diff --git a/motech-delivery-tools/motech-job-handler-invoker/pom.xml b/motech-delivery-tools/motech-job-handler-invoker/pom.xml
new file mode 100644
index 0000000..bedd6a3
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+ motech-delivery
+ motech-delivery
+ 0.3-SNAPSHOT
+
+ 4.0.0
+
+ motech-job-handler-invoker
+ Motech Job Handler Invoker
+
+
+ 3.0.5.RELEASE
+ 1.6.1
+ UTF-8
+ 0.3-SNAPSHOT
+
+
+
+
+ org.springframework
+ spring-webmvc
+ ${spring.version}
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.motechproject
+ motech-platform-common
+ ${motech.version}
+
+
+ org.motechproject
+ motech-platform-scheduler
+ ${motech.version}
+
+
+ commons-lang
+ commons-lang
+ 2.6
+ provided
+
+
+ javax.servlet
+ servlet-api
+ 2.5
+
+
+ org.quartz-scheduler
+ quartz
+ 1.8.4
+
+
+ org.motechproject.deliverytools
+ motech-deliverytools-common
+ 0.3-SNAPSHOT
+
+
+ org.springframework
+ spring-test
+ 3.0.5.RELEASE
+ test
+
+
+
+
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/AllScheduledJobs.java b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/AllScheduledJobs.java
new file mode 100644
index 0000000..0abf1a0
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/AllScheduledJobs.java
@@ -0,0 +1,37 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+import org.motechproject.deliverytools.jobhandlerinvoker.domain.ScheduledJob;
+import org.motechproject.scheduler.MotechSchedulerServiceImpl;
+import org.quartz.*;
+import org.quartz.impl.StdSchedulerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AllScheduledJobs {
+ private SchedulerFactoryBean schedulerFactory;
+ private QueriedJobs queriedJobs;
+
+ @Autowired
+ public AllScheduledJobs(SchedulerFactoryBean schedulerFactory, QueriedJobs queriedJobs) throws SchedulerException {
+ this.schedulerFactory = schedulerFactory;
+ this.queriedJobs = queriedJobs;
+ }
+
+ public ScheduledJob get(String name) {
+ try {
+ Scheduler scheduler = schedulerFactory.getScheduler();
+ JobDetail jobDetail = scheduler.getJobDetail(name, MotechSchedulerServiceImpl.JOB_GROUP_NAME);
+ if (jobDetail == null)
+ throw new IllegalArgumentException("No job named:" + name);
+ Trigger[] triggers = scheduler.getTriggersOfJob(name, MotechSchedulerServiceImpl.JOB_GROUP_NAME);
+ if (triggers.length == 0 || triggers.length > 1)
+ throw new AssertionError(String.format("There should be exactly one trigger for every job. Found %s triggers for %s", triggers.length, name));
+ queriedJobs.add(name);
+ return new ScheduledJob(jobDetail, triggers[0]);
+ } catch (SchedulerException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/JobHandlerInvokeRequest.java b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/JobHandlerInvokeRequest.java
new file mode 100644
index 0000000..dc12cf1
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/JobHandlerInvokeRequest.java
@@ -0,0 +1,52 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+public class JobHandlerInvokeRequest {
+ private String className;
+ private String methodName;
+ private String jobId;
+ private boolean isRepeating;
+
+ public String className() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ public String methodName() {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public void setJobId(String jobId) {
+ this.jobId = jobId;
+ }
+
+ public String jobId() {
+ return jobId;
+ }
+
+ public boolean isRepeating() {
+ return isRepeating;
+ }
+
+ public void setRepeating(boolean repeating) {
+ isRepeating = repeating;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("JobHandlerInvokeRequest");
+ sb.append("{className='").append(className).append('\'');
+ sb.append(", methodName='").append(methodName).append('\'');
+ sb.append(", jobId='").append(jobId).append('\'');
+ sb.append(", isRepeating=").append(isRepeating);
+ sb.append('}');
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/QueriedJobs.java b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/QueriedJobs.java
new file mode 100644
index 0000000..df81ed8
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/QueriedJobs.java
@@ -0,0 +1,25 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Component
+public class QueriedJobs {
+ private Set jobNames = new HashSet();
+
+ public void add(String name) {
+ jobNames.add(name);
+ }
+
+ public String[] list() {
+ return jobNames.toArray(new String[jobNames.size()]);
+ }
+
+ public void clear() {
+ jobNames.clear();
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/ScheduledJobController.java b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/ScheduledJobController.java
new file mode 100644
index 0000000..bc3743d
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/ScheduledJobController.java
@@ -0,0 +1,94 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.log4j.Logger;
+import org.motechproject.deliverytools.jobhandlerinvoker.domain.MotechEventInvocation;
+import org.motechproject.deliverytools.jobhandlerinvoker.domain.ScheduledJob;
+import org.motechproject.deliverytools.jobhandlerinvoker.domain.ScheduledJobName;
+import org.motechproject.model.MotechEvent;
+import org.motechproject.util.DateUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.List;
+
+@RequestMapping("/motech-delivery-tools/jobhandler")
+@Controller
+public class ScheduledJobController {
+ private ApplicationContext applicationContext;
+ private AllScheduledJobs allScheduledJobs;
+ private QueriedJobs queriedJobs;
+ private Logger logger = Logger.getLogger(this.getClass().getName());
+
+ @Autowired
+ public ScheduledJobController(ApplicationContext applicationContext, AllScheduledJobs allScheduledJobs, QueriedJobs queriedJobs) {
+ this.applicationContext = applicationContext;
+ this.allScheduledJobs = allScheduledJobs;
+ this.queriedJobs = queriedJobs;
+ }
+
+ @RequestMapping(value = "invoke", method = RequestMethod.GET)
+ @ResponseBody
+ public String invoke(JobHandlerInvokeRequest request, HttpServletResponse response) {
+ try {
+ String className = request.className();
+ String beanName = className.substring(0, 1).toLowerCase() + className.substring(1, className.length());
+ Object bean = applicationContext.getBean(beanName);
+ Method method = ReflectionUtils.findMethod(bean.getClass(), request.methodName(), MotechEvent.class);
+
+ ScheduledJobName scheduledJobName = new ScheduledJobName(request.jobId(), method);
+ ScheduledJob scheduledJob = allScheduledJobs.get(scheduledJobName.jobId(request.isRepeating()));
+ MotechEventInvocation invocation = new MotechEventInvocation(scheduledJob);
+ ReflectionUtils.invokeMethod(method, bean, invocation.event());
+ return "All Good";
+ } catch (Throwable e) {
+ response.setStatus(500);
+ logger.error(String.format("Could not handle the request: %s", request.toString()), e);
+ return ExceptionUtils.getStackTrace(e);
+ }
+ }
+
+ @RequestMapping(value = "list", method = RequestMethod.GET)
+ @ResponseBody
+ public String list(TriggerListRequest request) throws Exception {
+ try {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("NOW: ").append(DateUtil.now().toDate().toString()).append("
");
+ String[] jobNames = queriedJobs.list();
+ for (String jobName : jobNames) {
+ ScheduledJob scheduledJob = allScheduledJobs.get(jobName);
+ stringBuilder.append(String.format("%s
", jobName));
+
+ List triggerDateTimes = scheduledJob.triggerSummary(request.number());
+ for (Date date : triggerDateTimes) {
+ stringBuilder.append(String.format("%s
", date));
+ }
+ stringBuilder.append("
");
+ }
+ return stringBuilder.toString();
+ } catch (Throwable e) {
+ logger.error("Could not handle the request to list", e);
+ return ExceptionUtils.getStackTrace(e);
+ }
+ }
+
+ @RequestMapping(value = "clear", method = RequestMethod.GET)
+ @ResponseBody
+ public String clear() {
+ try {
+ queriedJobs.clear();
+ return "All Good";
+ } catch (Throwable e) {
+ logger.error("Could not handle the request to clear", e);
+ return ExceptionUtils.getStackTrace(e);
+ }
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/TriggerListRequest.java b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/TriggerListRequest.java
new file mode 100644
index 0000000..cbd14e2
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/TriggerListRequest.java
@@ -0,0 +1,13 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+public class TriggerListRequest {
+ private int number;
+
+ public void setNumber(int number) {
+ this.number = number;
+ }
+
+ public int number() {
+ return number == 0 ? 3 : number;
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/MotechEventInvocation.java b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/MotechEventInvocation.java
new file mode 100644
index 0000000..8e26231
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/MotechEventInvocation.java
@@ -0,0 +1,18 @@
+package org.motechproject.deliverytools.jobhandlerinvoker.domain;
+
+import org.motechproject.model.MotechEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MotechEventInvocation {
+ private HashMap map = new HashMap();
+
+ public MotechEventInvocation(ScheduledJob scheduledJob) {
+ map.putAll(scheduledJob.payload());
+ }
+
+ public MotechEvent event() {
+ return new MotechEvent("notrelevant", map);
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJob.java b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJob.java
new file mode 100644
index 0000000..5ff0663
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJob.java
@@ -0,0 +1,34 @@
+package org.motechproject.deliverytools.jobhandlerinvoker.domain;
+
+import org.motechproject.util.DateUtil;
+import org.quartz.JobDetail;
+import org.quartz.Trigger;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public class ScheduledJob {
+ private JobDetail jobDetail;
+ private Trigger trigger;
+
+ public ScheduledJob(JobDetail jobDetail, Trigger trigger) {
+ this.jobDetail = jobDetail;
+ this.trigger = trigger;
+ }
+
+ public List triggerSummary(int number) {
+ Date currentDateTime = DateUtil.now().toDate();
+ ArrayList followingDates = new ArrayList();
+ for (int i = 0; i < number; i++) {
+ currentDateTime = trigger.getFireTimeAfter(currentDateTime);
+ followingDates.add(currentDateTime);
+ }
+ return followingDates;
+ }
+
+ public Map payload() {
+ return jobDetail.getJobDataMap().getWrappedMap();
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJobName.java b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJobName.java
new file mode 100644
index 0000000..07f7b39
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJobName.java
@@ -0,0 +1,23 @@
+package org.motechproject.deliverytools.jobhandlerinvoker.domain;
+
+import org.motechproject.scheduler.MotechSchedulerServiceImpl;
+import org.motechproject.server.event.annotations.MotechListener;
+
+import java.lang.reflect.Method;
+
+public class ScheduledJobName {
+ private String uniqueId;
+ private Method method;
+
+ public ScheduledJobName(String uniqueId, Method handlingMethod) {
+ this.uniqueId = uniqueId;
+ this.method = handlingMethod;
+ }
+
+ public String jobId(boolean isRepeating) {
+ MotechListener annotation = method.getAnnotation(MotechListener.class);
+ String jobId = String.format("%s-%s", annotation.subjects()[0], uniqueId);
+ if (isRepeating) return jobId + MotechSchedulerServiceImpl.REPEAT_JOB_SUFFIX;
+ return jobId;
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/main/resources/applicationJobHandlerInvokerContext.xml b/motech-delivery-tools/motech-job-handler-invoker/src/main/resources/applicationJobHandlerInvokerContext.xml
new file mode 100644
index 0000000..7a0dfbd
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/main/resources/applicationJobHandlerInvokerContext.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/AllScheduledJobsIT.java b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/AllScheduledJobsIT.java
new file mode 100644
index 0000000..80d8886
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/AllScheduledJobsIT.java
@@ -0,0 +1,55 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.motechproject.deliverytools.jobhandlerinvoker.domain.ScheduledJob;
+import org.motechproject.scheduler.MotechSchedulerServiceImpl;
+import org.motechproject.util.DateUtil;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.SchedulerException;
+import org.quartz.SimpleTrigger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {"/applicationJobHandlerInvokerContextForTest.xml"})
+public class AllScheduledJobsIT {
+ @Autowired
+ private AllScheduledJobs allScheduledJobs;
+ @Autowired
+ private SchedulerFactoryBean schedulerFactory;
+
+ private final String jobName = "jobNameForAllScheduledJobsIT";
+
+ @Before
+ public void setUp() throws SchedulerException {
+ schedulerFactory.getScheduler().deleteJob(jobName, MotechSchedulerServiceImpl.JOB_GROUP_NAME);
+ }
+
+ @Test
+ public void shouldLoadJobFromQuartzDataStore() throws SchedulerException {
+ JobDetail jobDetail = new JobDetail();
+ jobDetail.setName(jobName);
+ jobDetail.setGroup(MotechSchedulerServiceImpl.JOB_GROUP_NAME);
+ jobDetail.setJobClass(JobForAllScheduledJobsIT.class);
+ JobDataMap jobDataMap = new JobDataMap();
+ jobDataMap.put("foo", "bar");
+ jobDetail.setJobDataMap(jobDataMap);
+
+ SimpleTrigger simpleTrigger = new SimpleTrigger();
+ simpleTrigger.setName("triggerName");
+ simpleTrigger.setStartTime(DateUtil.now().plusDays(1).toDate());
+ schedulerFactory.getScheduler().scheduleJob(jobDetail, simpleTrigger);
+ ScheduledJob scheduledJob = allScheduledJobs.get(jobDetail.getName());
+ assertNotNull(scheduledJob);
+ assertEquals("bar", scheduledJob.payload().get("foo"));
+ assertNotNull(scheduledJob.triggerSummary(1).get(0));
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/HandlerForJobHandlerInvokerTests.java b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/HandlerForJobHandlerInvokerTests.java
new file mode 100644
index 0000000..7d2b0e6
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/HandlerForJobHandlerInvokerTests.java
@@ -0,0 +1,18 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+import org.motechproject.model.MotechEvent;
+import org.motechproject.server.event.annotations.MotechListener;
+
+public class HandlerForJobHandlerInvokerTests {
+ private MotechEvent motechEvent;
+ public static final String SAMPLE_SUBJECT = "SampleSubject";
+
+ public MotechEvent motechEvent() {
+ return motechEvent;
+ }
+
+ @MotechListener(subjects = SAMPLE_SUBJECT)
+ public void handle(MotechEvent motechEvent) {
+ this.motechEvent = motechEvent;
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/JobForAllScheduledJobsIT.java b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/JobForAllScheduledJobsIT.java
new file mode 100644
index 0000000..6bf647a
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/JobForAllScheduledJobsIT.java
@@ -0,0 +1,11 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+public class JobForAllScheduledJobsIT implements Job {
+ @Override
+ public void execute(JobExecutionContext context) throws JobExecutionException {
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/ScheduledJobControllerTest.java b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/ScheduledJobControllerTest.java
new file mode 100644
index 0000000..871d11b
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/ScheduledJobControllerTest.java
@@ -0,0 +1,36 @@
+package org.motechproject.deliverytools.jobhandlerinvoker;
+
+import org.junit.Test;
+import org.motechproject.deliverytools.common.DeliveryToolsObject;
+import org.motechproject.deliverytools.jobhandlerinvoker.domain.ScheduledJob;
+import org.quartz.JobDetail;
+import org.springframework.context.ApplicationContext;
+
+import javax.servlet.http.HttpServletResponse;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ScheduledJobControllerTest extends DeliveryToolsObject {
+ @Test
+ public void shouldInvoke() {
+ JobHandlerInvokeRequest request = new JobHandlerInvokeRequest();
+ request.setClassName(HandlerForJobHandlerInvokerTests.class.getSimpleName());
+ request.setMethodName("handle");
+ request.setJobId("job1234");
+
+ ApplicationContext applicationContext = mock(ApplicationContext.class);
+ HandlerForJobHandlerInvokerTests jobHandler = new HandlerForJobHandlerInvokerTests();
+ when(applicationContext.getBean("handlerForJobHandlerInvokerTests")).thenReturn(jobHandler);
+
+ HttpServletResponse response = mock(HttpServletResponse.class);
+ AllScheduledJobs allScheduledJobs = mock(AllScheduledJobs.class);
+ when(allScheduledJobs.get(anyString())).thenReturn(new ScheduledJob(new JobDetail(), null));
+
+ ScheduledJobController controllerScheduled = new ScheduledJobController(applicationContext, allScheduledJobs, new QueriedJobs());
+ controllerScheduled.invoke(request, response);
+ assertNotNull(jobHandler.motechEvent());
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJobNameTest.java b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJobNameTest.java
new file mode 100644
index 0000000..244742d
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/test/java/org/motechproject/deliverytools/jobhandlerinvoker/domain/ScheduledJobNameTest.java
@@ -0,0 +1,21 @@
+package org.motechproject.deliverytools.jobhandlerinvoker.domain;
+
+import org.junit.Test;
+import org.motechproject.deliverytools.jobhandlerinvoker.HandlerForJobHandlerInvokerTests;
+import org.motechproject.model.MotechEvent;
+import org.motechproject.scheduler.MotechSchedulerServiceImpl;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Method;
+
+import static org.junit.Assert.assertEquals;
+
+public class ScheduledJobNameTest {
+ @Test
+ public void jobId() {
+ Method method = ReflectionUtils.findMethod(HandlerForJobHandlerInvokerTests.class, "handle", MotechEvent.class);
+ ScheduledJobName scheduledJobName = new ScheduledJobName("abcd", method);
+ assertEquals(String.format("%s-abcd", HandlerForJobHandlerInvokerTests.SAMPLE_SUBJECT), scheduledJobName.jobId(false));
+ assertEquals(String.format("%s-abcd%s", HandlerForJobHandlerInvokerTests.SAMPLE_SUBJECT, MotechSchedulerServiceImpl.REPEAT_JOB_SUFFIX), scheduledJobName.jobId(true));
+ }
+}
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/applicationJobHandlerInvokerContextForTest.xml b/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/applicationJobHandlerInvokerContextForTest.xml
new file mode 100644
index 0000000..3d18e8e
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/applicationJobHandlerInvokerContextForTest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ classpath:quartz.properties
+
+
+ applicationContext
+
+
+
+
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/log4j.xml b/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/log4j.xml
new file mode 100644
index 0000000..26969c3
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/log4j.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/quartz.properties b/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/quartz.properties
new file mode 100644
index 0000000..63037ad
--- /dev/null
+++ b/motech-delivery-tools/motech-job-handler-invoker/src/test/resources/quartz.properties
@@ -0,0 +1,6 @@
+org.quartz.scheduler.instanceName = MotechScheduler
+
+org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
+org.quartz.threadPool.threadCount = 3
+
+org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
diff --git a/motech-delivery-tools/motech-kookoo-automation/pom.xml b/motech-delivery-tools/motech-kookoo-automation/pom.xml
new file mode 100644
index 0000000..9ff02be
--- /dev/null
+++ b/motech-delivery-tools/motech-kookoo-automation/pom.xml
@@ -0,0 +1,53 @@
+
+
+
+ motech-delivery
+ motech-delivery
+ 0.3-SNAPSHOT
+
+ 4.0.0
+
+ motech-kookoo-automation
+ Motech KooKoo Automation
+
+
+ 3.0.5.RELEASE
+ 1.6.1
+ UTF-8
+ 0.3-SNAPSHOT
+
+
+
+
+ org.springframework
+ spring-webmvc
+ ${spring.version}
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.motechproject
+ motech-platform-common
+ ${motech.version}
+
+
+ commons-lang
+ commons-lang
+ 2.6
+ provided
+
+
+ org.motechproject
+ motech-ivr-kookoo
+ 0.3-SNAPSHOT
+ provided
+
+
+
+
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-kookoo-automation/src/main/java/org/motechproject/deliverytools/kookoo/IncomingCall.java b/motech-delivery-tools/motech-kookoo-automation/src/main/java/org/motechproject/deliverytools/kookoo/IncomingCall.java
new file mode 100644
index 0000000..669f874
--- /dev/null
+++ b/motech-delivery-tools/motech-kookoo-automation/src/main/java/org/motechproject/deliverytools/kookoo/IncomingCall.java
@@ -0,0 +1,49 @@
+package org.motechproject.deliverytools.kookoo;
+
+import com.google.gson.reflect.TypeToken;
+import org.motechproject.dao.MotechJsonReader;
+import org.motechproject.ivr.kookoo.KookooCallServiceImpl;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.HashMap;
+
+public class IncomingCall {
+ private String apiKey;
+ private String phoneNumber;
+ private HashMap customParams;
+
+ public IncomingCall(String queryString) throws IOException {
+ QueryParams queryParams = QueryParams.fromQueryString(queryString);
+ apiKey = queryParams.getString(KookooCallServiceImpl.API_KEY_KEY);
+ phoneNumber = queryParams.getString(KookooCallServiceImpl.PHONE_NUMBER_KEY);
+
+ MotechJsonReader jsonReader = new MotechJsonReader();
+ Type type = new TypeToken>() {
+ }.getType();
+
+ String urlString = URLDecoder.decode(URLDecoder.decode(queryParams.getString(KookooCallServiceImpl.URL_KEY), "UTF-8"), "UTF-8");
+ URL callbackURL = new URL(urlString);
+ String customQuery = callbackURL.getQuery();
+ QueryParams customQueryParams = QueryParams.fromQueryString(customQuery);
+ customParams = (HashMap) jsonReader.from(customQueryParams.getString(KookooCallServiceImpl.CUSTOM_DATA_KEY), type);
+ }
+
+ public String apiKey() {
+ return apiKey;
+ }
+
+ public String phoneNumber() {
+ return phoneNumber;
+ }
+
+ public String customData(String customDataKey) {
+ return customParams.get(customDataKey);
+ }
+
+ public HashMap customParams() {
+ return customParams;
+ }
+}
diff --git a/motech-delivery-tools/motech-kookoo-automation/src/main/java/org/motechproject/deliverytools/kookoo/QueryParams.java b/motech-delivery-tools/motech-kookoo-automation/src/main/java/org/motechproject/deliverytools/kookoo/QueryParams.java
new file mode 100644
index 0000000..775d786
--- /dev/null
+++ b/motech-delivery-tools/motech-kookoo-automation/src/main/java/org/motechproject/deliverytools/kookoo/QueryParams.java
@@ -0,0 +1,38 @@
+package org.motechproject.deliverytools.kookoo;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+public class QueryParams {
+ private Map params = new HashMap();
+
+ public static QueryParams fromQueryString(String string) {
+ QueryParams queryParams = new QueryParams();
+ String[] keyValuePairs = StringUtils.split(string, "&");
+ for (String keyValuePair : keyValuePairs) {
+ String[] keyAndValue = StringUtils.split(keyValuePair, "=");
+ queryParams.put(keyAndValue[0], keyAndValue[1]);
+ }
+ return queryParams;
+ }
+
+ public QueryParams put(String key, Object value) {
+ params.put(key, value);
+ return this;
+ }
+
+ public Map params() {
+ return params;
+ }
+
+ public Object get(String key) {
+ return params.get(key);
+ }
+
+ public String getString(String key) {
+ return (String) get(key);
+ }
+}
diff --git a/motech-delivery-tools/motech-kookoo-automation/src/test/java/org/motechproject/deliverytools/kookoo/IncomingCallTest.java b/motech-delivery-tools/motech-kookoo-automation/src/test/java/org/motechproject/deliverytools/kookoo/IncomingCallTest.java
new file mode 100644
index 0000000..040a892
--- /dev/null
+++ b/motech-delivery-tools/motech-kookoo-automation/src/test/java/org/motechproject/deliverytools/kookoo/IncomingCallTest.java
@@ -0,0 +1,38 @@
+package org.motechproject.deliverytools.kookoo;
+
+import org.json.JSONObject;
+import org.junit.Test;
+import org.motechproject.ivr.kookoo.KookooCallServiceImpl;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+
+import static junit.framework.Assert.assertEquals;
+
+public class IncomingCallTest {
+ @Test
+ public void shouldParseWebResponse() throws IOException {
+ HashMap customParams = new HashMap();
+ customParams.put(KookooCallServiceImpl.IS_OUTBOUND_CALL, "true");
+ JSONObject json = new JSONObject(customParams);
+ String callbackBaseURL = "http://fds";
+ String callbackURL = String.format("%s?%s=%s", callbackBaseURL, KookooCallServiceImpl.CUSTOM_DATA_KEY, json.toString());
+
+ IncomingCall incomingCall = new IncomingCall(String.format("%s=foo&%s=676&%s=%s",
+ KookooCallServiceImpl.API_KEY_KEY, KookooCallServiceImpl.PHONE_NUMBER_KEY,
+ KookooCallServiceImpl.URL_KEY, URLEncoder.encode(callbackURL, "UTF-8")));
+ assertEquals("foo", incomingCall.apiKey());
+ assertEquals("676", incomingCall.phoneNumber());
+ assertEquals("true", incomingCall.customData(KookooCallServiceImpl.IS_OUTBOUND_CALL));
+ }
+
+ @Test
+ public void doubleEncodedURL() throws IOException {
+ String callbackURL = "http%253A%252F%252Flocalhost%253A8080%252Ftama%252Fivr%252Freply%253FdataMap%253D%257B%2522is_outbound_call%2522%253A%2522true%2522%257D&phone_no=01705589013";
+ IncomingCall incomingCall = new IncomingCall(String.format("%s=foo&%s=676&%s=%s",
+ KookooCallServiceImpl.API_KEY_KEY, KookooCallServiceImpl.PHONE_NUMBER_KEY,
+ KookooCallServiceImpl.URL_KEY, callbackURL));
+ assertEquals("true", incomingCall.customData(KookooCallServiceImpl.IS_OUTBOUND_CALL));
+ }
+}
diff --git a/motech-delivery-tools/motech-kookoo-automation/src/test/java/org/motechproject/deliverytools/kookoo/QueryParamsTest.java b/motech-delivery-tools/motech-kookoo-automation/src/test/java/org/motechproject/deliverytools/kookoo/QueryParamsTest.java
new file mode 100644
index 0000000..f0f78b2
--- /dev/null
+++ b/motech-delivery-tools/motech-kookoo-automation/src/test/java/org/motechproject/deliverytools/kookoo/QueryParamsTest.java
@@ -0,0 +1,13 @@
+package org.motechproject.deliverytools.kookoo;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class QueryParamsTest {
+ @Test
+ public void fromQueryString() {
+ QueryParams queryParams = QueryParams.fromQueryString("foo=bar&baz=quack");
+ assertEquals("bar", queryParams.get("foo"));
+ }
+}
diff --git a/motech-delivery-tools/motech-outbound-endpoint/pom.xml b/motech-delivery-tools/motech-outbound-endpoint/pom.xml
new file mode 100644
index 0000000..f2eaaea
--- /dev/null
+++ b/motech-delivery-tools/motech-outbound-endpoint/pom.xml
@@ -0,0 +1,57 @@
+
+
+
+ motech-delivery
+ motech-delivery
+ 0.3-SNAPSHOT
+
+ 4.0.0
+
+ motech-outbound-endpoint
+ Motech Outbound Endpoint
+
+
+ 3.0.5.RELEASE
+ 1.6.1
+ UTF-8
+ 0.3-SNAPSHOT
+
+
+
+
+ org.springframework
+ spring-webmvc
+ ${spring.version}
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.motechproject
+ motech-platform-common
+ ${motech.version}
+
+
+ commons-lang
+ commons-lang
+ 2.6
+ provided
+
+
+ javax.servlet
+ servlet-api
+ 2.5
+
+
+ org.motechproject.deliverytools
+ motech-deliverytools-common
+ ${motech.version}
+
+
+
+
\ No newline at end of file
diff --git a/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/GetLastReceivedRequest.java b/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/GetLastReceivedRequest.java
new file mode 100644
index 0000000..aa68af8
--- /dev/null
+++ b/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/GetLastReceivedRequest.java
@@ -0,0 +1,13 @@
+package org.motechproject.deliverytools.outboundendpoint;
+
+public class GetLastReceivedRequest {
+ private int waitForSeconds;
+
+ public void setWaitForSeconds(int waitForSeconds) {
+ this.waitForSeconds = waitForSeconds;
+ }
+
+ public int waitForSeconds() {
+ return waitForSeconds;
+ }
+}
diff --git a/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/HttpEndpoint.java b/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/HttpEndpoint.java
new file mode 100644
index 0000000..b2c3a22
--- /dev/null
+++ b/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/HttpEndpoint.java
@@ -0,0 +1,50 @@
+package org.motechproject.deliverytools.outboundendpoint;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@RequestMapping("/motech-delivery-tools/outbound")
+@Controller
+public class HttpEndpoint {
+ private static Request lastRequest;
+ private Logger logger = Logger.getLogger(this.getClass().getName());
+
+ @RequestMapping(value = "receive", method = RequestMethod.GET)
+ public void receive(HttpServletRequest servletRequest) {
+ String queryString = servletRequest.getQueryString();
+ logger.info(String.format("Received request: %s", queryString));
+ lastRequest = new Request(queryString);
+ }
+
+ @RequestMapping(value = "lastreceived", method = RequestMethod.GET)
+ @ResponseBody
+ public String lastRequest(GetLastReceivedRequest getLastReceivedRequest, HttpServletResponse response) throws InterruptedException {
+ if (getLastReceivedRequest.waitForSeconds() > 0) {
+ for (int i = 0; i < getLastReceivedRequest.waitForSeconds(); i++) {
+ if (lastRequest != null) return lastRequestQuery(response);
+ Thread.sleep(1000);
+ }
+ }
+ return lastRequestQuery(response);
+ }
+
+ private String lastRequestQuery(HttpServletResponse response) {
+ if (lastRequest == null) {
+ return "";
+ }
+ return lastRequest.queryString();
+ }
+
+ @RequestMapping(value = "clear", method = RequestMethod.GET)
+ @ResponseBody
+ public String clear() {
+ lastRequest = null;
+ return "All Good";
+ }
+}
diff --git a/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/Request.java b/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/Request.java
new file mode 100644
index 0000000..b2d3a0b
--- /dev/null
+++ b/motech-delivery-tools/motech-outbound-endpoint/src/main/java/org/motechproject/deliverytools/outboundendpoint/Request.java
@@ -0,0 +1,18 @@
+package org.motechproject.deliverytools.outboundendpoint;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import java.util.Map;
+
+public class Request {
+ @JsonProperty
+ private String queryString;
+
+ public Request(String queryString) {
+ this.queryString = queryString;
+ }
+
+ public String queryString() {
+ return queryString;
+ }
+}
diff --git a/motech-delivery-tools/pom.xml b/motech-delivery-tools/pom.xml
new file mode 100644
index 0000000..db3d219
--- /dev/null
+++ b/motech-delivery-tools/pom.xml
@@ -0,0 +1,25 @@
+
+
+
+ motech-delivery
+ motech-delivery
+ 0.3-SNAPSHOT
+
+ 4.0.0
+ pom
+ Motech Delivery Tools
+ motech-delivery-tools
+
+
+
+ motech-datetime-simulator
+ motech-deliverytools-common
+ motech-job-handler-invoker
+ motech-kookoo-automation
+ motech-outbound-endpoint
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index d91a641..fcf7612 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,15 +3,253 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
-
motech-delivery
motech-delivery
pom
- 0.1
+ 0.3-SNAPSHOT
+ Motech Delivery
motech-delivery-config
motech-delivery-deploy
+ motech-delivery-tools
+
+ UTF-8
+ 3.0.5.RELEASE
+
+
+
+
+ www.motechproject.org
+ file:////var/www/site
+
+
+
+
+
+ motech-repo
+ Motech Maven Repository
+ http://nexus.motechproject.org/content/repositories/public
+
+
+
+
+
+ motech-repo
+ Motech Maven Repository
+ http://nexus.motechproject.org/content/repositories/public
+
+ true
+
+
+
+
+
+
+
+ junit
+ junit
+ 4.8.2
+ test
+
+
+ org.hamcrest
+ hamcrest-library
+ 1.1
+ test
+
+
+ org.mockito
+ mockito-core
+ 1.8.5
+ test
+
+
+ log4j
+ log4j
+ 1.2.16
+
+
+ joda-time
+ joda-time
+ 2.0
+
+
+ org.springframework
+ spring-core
+ ${spring.version}
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.3.2
+
+ 1.6
+ 1.6
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.9
+
+
+ **/**/*ForTest.java
+
+
+
+
+ org.apache.maven.plugins
+ maven-eclipse-plugin
+ 2.6
+
+ 2.0
+ true
+
+
+
+ com.mycila.maven-license-plugin
+ maven-license-plugin
+ 1.9.0
+
+
+ true
+
+ **/jquery-autocomplete/**
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 2.2
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ 2.9
+
+ UTF-8
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 2.7
+
+
+ org.apache.maven.plugins
+ maven-surefire-report-plugin
+ 2.9
+
+ ${project.reporting.outputDirectory}/../xref-test
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-jxr-plugin
+ 2.1
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.7
+
+ true
+
+
+
+ aggregate
+
+ aggregate
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-pmd-plugin
+ 2.5
+
+ ${project.reporting.outputDirectory}/../xref
+
+
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
+ 2.3.1
+
+
+
+ index
+ dependencies
+ mailing-list
+ cim
+ issue-tracking
+ license
+ scm
+ summary
+
+
+
+
+
+ org.codehaus.mojo
+ taglist-maven-plugin
+ 2.4
+
+
+
+
+
+
+
+ MOTECH PLATFORM OPENSOURCE LICENSE AGREEMENT
+ license.txt
+
+
+
+
+ hudson
+ http://ci.motechproject.org/
+
+
+
+
+ MOTECH Dev
+ motech-dev+subscribe@googlegroups.com
+ motech-dev+unsubscribe@googlegroups.com
+ motech-dev@googlegroups.com
+ http://groups.google.com/group/motech-dev
+
+
+
+
+ scm:git:http://github.com/motech/motech-delivery-tools.git
+ scm:git:git@github.com:motech/motech-delivery-tools.git
+ http://github.com/motech/motech-delivery-tools
+
\ No newline at end of file