diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b8157a4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+cobertura.ser
+.classpath
+.project
+.settings
+target/
+*.swp
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1c08350
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,44 @@
+
+ 4.0.0
+ com.github.notifo4j
+ notifo-client
+ jar
+ 1.0-SNAPSHOT
+ notifo4j
+
+
+
+ maven-compiler-plugin
+
+
+ 1.5
+
+
+
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.0.1
+
+
+ com.google.code.gson
+ gson
+ 1.4
+
+
+ junit
+ junit
+ 4.8.1
+ test
+
+
+ org.jmock
+ jmock-junit4
+ 2.5.1
+ test
+
+
+
diff --git a/src/main/java/com/notifo/client/NotifoClient.java b/src/main/java/com/notifo/client/NotifoClient.java
new file mode 100644
index 0000000..b56498f
--- /dev/null
+++ b/src/main/java/com/notifo/client/NotifoClient.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2010 Sean P. Scanlon.
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+/**
+ * @author sscanlon
+ *
+ */
+public interface NotifoClient {
+
+ /**
+ *
+ * @param messageBody
+ * @return
+ * @throws NotifoException
+ */
+ NotifoResponse sendMessage(NotifoMessage message) throws NotifoException;
+
+ /**
+ * Send a message to a particular user
+ */
+ NotifoResponse sendMessage(String to, String messageBody) throws NotifoException;
+
+ /**
+ * Send a message to the default user (you)
+ */
+ NotifoResponse sendMessage(String messageBody) throws NotifoException;
+
+ /**
+ *
+ * @param userName
+ * @return
+ * @throws NotifoException
+ */
+ NotifoResponse subscribeUser(String userName) throws NotifoException;
+
+}
diff --git a/src/main/java/com/notifo/client/NotifoException.java b/src/main/java/com/notifo/client/NotifoException.java
new file mode 100644
index 0000000..05d1489
--- /dev/null
+++ b/src/main/java/com/notifo/client/NotifoException.java
@@ -0,0 +1,18 @@
+/**
+ * Copyright (C) 2010 Sean P. Scanlon.
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+/**
+ * @author sscanlon
+ *
+ */
+public class NotifoException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public NotifoException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/com/notifo/client/NotifoHttpClient.java b/src/main/java/com/notifo/client/NotifoHttpClient.java
new file mode 100644
index 0000000..fbaf956
--- /dev/null
+++ b/src/main/java/com/notifo/client/NotifoHttpClient.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright (C) 2010 Sean P. Scanlon
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author sscanlon
+ *
+ */
+public class NotifoHttpClient implements NotifoClient {
+
+ private static final String SEND_NOTIFICATION_URL = "https://api.notifo.com/v1/send_notification";
+
+ private static final String SUBSCRIBE_USER_URL = "https://api.notifo.com/v1/subscribe_user";
+
+ private static final AuthScheme AUTH_SCHEME = new BasicScheme();
+
+ private HttpClient httpClient = new DefaultHttpClient();
+
+ private final UsernamePasswordCredentials credentials;
+
+ private final String userName;
+
+ /**
+ * Construct a client with a username and apikey. "username" will be the default recipient of
+ * messages
+ *
+ * @param userName
+ * @param apiKey
+ */
+ public NotifoHttpClient(String userName, String apiKey) {
+ this.userName = userName;
+ this.credentials = new UsernamePasswordCredentials(userName, apiKey);
+ ((DefaultHttpClient) httpClient).addRequestInterceptor(new HttpRequestInterceptor() {
+ public void process(HttpRequest request, HttpContext context) throws HttpException,
+ IOException {
+ request.addHeader(AUTH_SCHEME.authenticate(credentials, request));
+ }
+ });
+ }
+
+ @Override
+ public NotifoResponse sendMessage(String messageBody) throws NotifoException {
+ return sendMessage(new NotifoMessage(this.userName, messageBody));
+ }
+
+ @Override
+ public NotifoResponse sendMessage(String to, String messageBody) throws NotifoException {
+ return sendMessage(new NotifoMessage(to, messageBody));
+ }
+
+ /**
+ * Send a message with the parameters in {@link NotifoMessage}
+ */
+ @Override
+ public NotifoResponse sendMessage(NotifoMessage message) throws NotifoException {
+
+ HttpPost post = new HttpPost(SEND_NOTIFICATION_URL);
+
+ List nvps = new ArrayList();
+
+ nvps.add(new BasicNameValuePair("to", message.getTo()));
+ nvps.add(new BasicNameValuePair("msg", message.getMessage()));
+
+ addPairIfNotNull(nvps, "title", message.getSubject());
+ addPairIfNotNull(nvps, "uri", message.getUrl());
+ addPairIfNotNull(nvps, "label", message.getLabel());
+
+ try {
+ post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
+ } catch (UnsupportedEncodingException e) {
+ throw new NotifoException(e.getMessage(), e.getCause());
+ }
+
+ try {
+ HttpResponse response = httpClient.execute(post);
+
+ return NotifoResponseFactory.parseResponse(response);
+
+ } catch (Exception e) {
+ throw new NotifoException(e.getMessage(), e.getCause());
+ }
+ }
+
+ @Override
+ public NotifoResponse subscribeUser(String userName) throws NotifoException {
+ HttpPost post = new HttpPost(SUBSCRIBE_USER_URL);
+ try {
+ post.setEntity(
+ new UrlEncodedFormEntity(
+ Arrays.asList(
+ new BasicNameValuePair("username", userName)), HTTP.UTF_8)
+ );
+ return NotifoResponseFactory.parseResponse(httpClient.execute(post));
+ } catch (Exception e) {
+ throw new NotifoException(e.getMessage(), e.getCause());
+ }
+
+ }
+
+ private void addPairIfNotNull(List nvps, String key, Object value) {
+ if (value != null) {
+ nvps.add(new BasicNameValuePair(key, value.toString()));
+ }
+
+ }
+
+ /**
+ * Override the default http client
+ *
+ * @param httpClient
+ */
+ public void setHttpClient(HttpClient httpClient) {
+ this.httpClient = httpClient;
+ }
+
+}
diff --git a/src/main/java/com/notifo/client/NotifoMessage.java b/src/main/java/com/notifo/client/NotifoMessage.java
new file mode 100644
index 0000000..3a30565
--- /dev/null
+++ b/src/main/java/com/notifo/client/NotifoMessage.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (C) 2010 Sean P. Scanlon.
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+/**
+ * @author sscanlon
+ *
+ */
+public class NotifoMessage {
+
+ private final String to;
+ private final String message;
+ private String subject;
+ private String label;
+ private String url;
+
+ public NotifoMessage(String to, String message) {
+ this.to = to;
+ this.message = message;
+ }
+
+ /**
+ * @return the subject
+ */
+ public String getSubject() {
+ return subject;
+ }
+
+ /**
+ * @param subject
+ * the subject to set
+ */
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ /**
+ * @return the label
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * @param label
+ * the label to set
+ */
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ /**
+ * @return the to
+ */
+ public String getTo() {
+ return to;
+ }
+
+ /**
+ * @return the message
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * @return the url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ * the url to set
+ */
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+}
diff --git a/src/main/java/com/notifo/client/NotifoResponse.java b/src/main/java/com/notifo/client/NotifoResponse.java
new file mode 100644
index 0000000..1950f52
--- /dev/null
+++ b/src/main/java/com/notifo/client/NotifoResponse.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2010 Sean P Scanlon.
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author sscanlon
+ *
+ */
+public class NotifoResponse {
+
+ private static final String SUCCESS = "success";
+
+ private String status;
+
+ @SerializedName("response_code")
+ private int responseCode;
+
+ @SerializedName("response_message")
+ private String responseMessage;
+
+ public boolean isOk() {
+ return SUCCESS.equalsIgnoreCase(status);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("status: ").append(status)
+ .append(", responseCode: ").append(responseCode)
+ .append(", responseMessage: ").append(responseMessage)
+ .toString();
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public void setResponseCode(int responseCode) {
+ this.responseCode = responseCode;
+ }
+
+ public String getResponseMessage() {
+ return responseMessage;
+ }
+
+ public void setResponseMessage(String responseMessage) {
+ this.responseMessage = responseMessage;
+ }
+
+}
diff --git a/src/main/java/com/notifo/client/NotifoResponseFactory.java b/src/main/java/com/notifo/client/NotifoResponseFactory.java
new file mode 100644
index 0000000..6371915
--- /dev/null
+++ b/src/main/java/com/notifo/client/NotifoResponseFactory.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2010 Sean P Scanlon.
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.util.EntityUtils;
+
+import com.google.gson.Gson;
+
+/**
+ * @author sscanlon
+ *
+ */
+public class NotifoResponseFactory {
+
+ public static final Gson GSON = new Gson();
+
+ public static NotifoResponse parseResponse(HttpResponse response) {
+ NotifoResponse toReturn = null;
+ try {
+ String body = EntityUtils.toString(response.getEntity());
+ toReturn = GSON.fromJson(body, NotifoResponse.class);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return toReturn;
+ }
+
+}
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 0000000..5fe42d8
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,9 @@
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=DEBUG, A1
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
diff --git a/src/test/java/com/notifo/client/NotifoHttpClientTest.java b/src/test/java/com/notifo/client/NotifoHttpClientTest.java
new file mode 100644
index 0000000..a9fc231
--- /dev/null
+++ b/src/test/java/com/notifo/client/NotifoHttpClientTest.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2010 Sean P. Scanlon.
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author sscanlon
+ *
+ */
+public class NotifoHttpClientTest {
+
+ private Mockery mockery;
+
+ private NotifoHttpClient client;
+ private HttpClient httpClient;
+ private HttpResponse mockResponse;
+
+ @Before
+ public void setUp() throws Exception {
+ mockery = new Mockery();
+ httpClient = mockery.mock(HttpClient.class);
+ mockResponse = mockery.mock(HttpResponse.class);
+ client = new NotifoHttpClient("username", "apikey");
+ client.setHttpClient(httpClient);
+ }
+
+ @Test(expected = NotifoException.class)
+ public void testSendMessageWithException() throws Exception {
+
+ mockery.checking(new Expectations() {
+ {
+ one(httpClient).execute(with(any(HttpPost.class)));
+ will(throwException(new IOException("oops")));
+ }
+ });
+
+ client.sendMessage("test");
+
+ fail("shouldn't get here");
+ }
+
+ @Test
+ public void testSendMessage() throws Exception {
+
+ mockery.checking(new Expectations() {
+ {
+ exactly(3).of(httpClient).execute(with(any(HttpPost.class)));
+ will(returnValue(mockResponse));
+ exactly(3).of(mockResponse).getEntity();
+ will(returnValue(
+ new StringEntity("{}", "UTF-8")));
+ }
+ });
+
+ client.sendMessage("test");
+ client.sendMessage("user", "test");
+
+ NotifoMessage m = new NotifoMessage("", "");
+ m.setLabel("test label");
+ m.setSubject(null);
+ m.setUrl(null);
+ client.sendMessage(m);
+
+ mockery.assertIsSatisfied();
+ }
+
+ @Test
+ public void testSubscribeUser() throws Exception {
+ mockery.checking(new Expectations() {
+ {
+ one(httpClient).execute(with(any(HttpPost.class)));
+ will(returnValue(mockResponse));
+ one(mockResponse).getEntity();
+ will(returnValue(new StringEntity("{}", "UTF-8")));
+ }
+ });
+ assertNotNull(client.subscribeUser("sps"));
+ mockery.assertIsSatisfied();
+ }
+}
diff --git a/src/test/java/com/notifo/client/NotifoResponseFactoryTest.java b/src/test/java/com/notifo/client/NotifoResponseFactoryTest.java
new file mode 100644
index 0000000..e519fbd
--- /dev/null
+++ b/src/test/java/com/notifo/client/NotifoResponseFactoryTest.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2010 Sean P Scanlon.
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.StringEntity;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author sscanlon
+ *
+ */
+public class NotifoResponseFactoryTest {
+
+ private HttpResponse mockResponse;
+ private Mockery mockery;
+
+ @Before
+ public void setUp() throws Exception {
+ mockery = new Mockery();
+ mockResponse = mockery.mock(HttpResponse.class);
+ new NotifoResponseFactory(); // for code coverage :-)
+ }
+
+ @Test
+ public void testParseResponse() throws Exception {
+ mockery.checking(new Expectations() {
+ {
+ one(mockResponse).getEntity();
+ will(returnValue(new StringEntity("{")));
+ }
+ });
+ assertNull(NotifoResponseFactory.parseResponse(mockResponse));
+ mockery.assertIsSatisfied();
+
+ mockery.checking(new Expectations() {
+ {
+ one(mockResponse).getEntity();
+ will(returnValue(new StringEntity("{}")));
+ }
+ });
+ assertNotNull(NotifoResponseFactory.parseResponse(mockResponse));
+ mockery.assertIsSatisfied();
+ }
+}
diff --git a/src/test/java/com/notifo/client/NotifoResponseTest.java b/src/test/java/com/notifo/client/NotifoResponseTest.java
new file mode 100644
index 0000000..d92930c
--- /dev/null
+++ b/src/test/java/com/notifo/client/NotifoResponseTest.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2010 Sean P Scanlon.
+ * All rights reserved. Unauthorized disclosure or distribution is prohibited.
+ */
+package com.notifo.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * @author sscanlon
+ *
+ */
+public class NotifoResponseTest {
+
+ @Test
+ public void testBasics() throws Exception {
+ NotifoResponse response = new NotifoResponse();
+ response.setResponseCode(200);
+ response.setResponseMessage("foo");
+ response.setStatus("success");
+ assertTrue(response.isOk());
+ assertEquals(response.getResponseCode(), 200);
+ assertEquals(response.getStatus(), "success");
+ assertEquals(response.getResponseMessage(), "foo");
+ assertNotNull(response.toString());
+ }
+
+}