aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Windsor <mattwindsor@btinternet.com>2011-03-19 14:12:44 +0000
committerMatt Windsor <mattwindsor@btinternet.com>2011-03-19 14:12:44 +0000
commit410219d08abdb859315c4d6d0e0375287d64a88b (patch)
tree3d4610f6cc671147a0aec7bca573a38d6c98b58e
parent2efb758e7c2bd85801c76305161d9d8fa6d2be4b (diff)
Converted protocol to JSON. Now passes its first ever unit test\!
-rw-r--r--src/uk/org/ury/client/Client.java21
-rw-r--r--src/uk/org/ury/client/test/ClientTest.java18
-rw-r--r--src/uk/org/ury/database/DatabaseItem.java11
-rw-r--r--src/uk/org/ury/library/LibraryRequestHandler.java23
-rw-r--r--src/uk/org/ury/library/LibraryUtils.java9
-rw-r--r--src/uk/org/ury/server/RequestHandler.java7
-rw-r--r--src/uk/org/ury/server/Server.java27
-rw-r--r--src/uk/org/ury/server/ServerRequestHandler.java19
-rw-r--r--src/uk/org/ury/server/protocol/DecodeFailureException.java31
-rw-r--r--src/uk/org/ury/server/protocol/Directive.java59
-rw-r--r--src/uk/org/ury/server/protocol/ProtocolUtils.java67
-rw-r--r--src/uk/org/ury/server/protocol/Status.java17
12 files changed, 195 insertions, 114 deletions
diff --git a/src/uk/org/ury/client/Client.java b/src/uk/org/ury/client/Client.java
index f0b2679..3d810c5 100644
--- a/src/uk/org/ury/client/Client.java
+++ b/src/uk/org/ury/client/Client.java
@@ -3,11 +3,15 @@ package uk.org.ury.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
+
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.List;
+
+import java.util.Map;
+
+import uk.org.ury.server.protocol.DecodeFailureException;
+import uk.org.ury.server.protocol.ProtocolUtils;
public class Client
{
@@ -17,15 +21,16 @@ public class Client
* @param file The "file", including path and query string, to
* fetch from the server.
*
- * @return The response from the server, as a List of lines.
+ * @return The response from the server, as a key-value map.
+ * @throws DecodeFailureException if the decode failed.
*/
- public List<String>
- get (String file)
+ public Map<?, ?>
+ get (String file) throws DecodeFailureException
{
URL url = null;
URLConnection uc = null;
- List<String> result = new ArrayList<String> ();
+ String result = "";
try
{
@@ -51,7 +56,7 @@ public class Client
inputLine != null;
inputLine = in.readLine())
{
- result.add (inputLine);
+ result += inputLine;
}
}
catch (IOException e)
@@ -60,6 +65,6 @@ public class Client
e.printStackTrace ();
}
- return result;
+ return ProtocolUtils.decode (result);
}
}
diff --git a/src/uk/org/ury/client/test/ClientTest.java b/src/uk/org/ury/client/test/ClientTest.java
index e83c28c..48669aa 100644
--- a/src/uk/org/ury/client/test/ClientTest.java
+++ b/src/uk/org/ury/client/test/ClientTest.java
@@ -3,7 +3,7 @@
*/
package uk.org.ury.client.test;
-import java.util.List;
+import java.util.Map;
import junit.framework.Assert;
@@ -12,6 +12,8 @@ import org.junit.Before;
import org.junit.Test;
import uk.org.ury.client.Client;
+import uk.org.ury.server.protocol.DecodeFailureException;
+import uk.org.ury.server.protocol.Directive;
/**
@@ -55,9 +57,17 @@ public class ClientTest
{
Client client = new Client ();
- List<String> response = client.get ("/server/ServerRequestHandler?function=test");
+ Map<?, ?> response = null;
- Assert.assertEquals ("INFO: Test succeeded.", response.get (2));
+ try
+ {
+ response = client.get ("/server/ServerRequestHandler?function=test");
+ }
+ catch (DecodeFailureException e)
+ {
+ e.printStackTrace ();
+ }
+
+ Assert.assertEquals ("Test succeeded.", response.get (Directive.INFO.toString ()));
}
-
}
diff --git a/src/uk/org/ury/database/DatabaseItem.java b/src/uk/org/ury/database/DatabaseItem.java
index b1482ce..a6d929f 100644
--- a/src/uk/org/ury/database/DatabaseItem.java
+++ b/src/uk/org/ury/database/DatabaseItem.java
@@ -1,7 +1,6 @@
package uk.org.ury.database;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashMap;
import java.util.Map;
import uk.org.ury.database.exceptions.MissingPropertyException;
@@ -80,18 +79,18 @@ public abstract class DatabaseItem<E, T>
* @return a list of lines representing the response.
*/
- public List<String>
+ public Map<String, String>
asResponse ()
{
// TODO: Fan out implementation details into separate class
- List<String> response = new ArrayList<String> ();
+ Map<String, String> response = new HashMap<String, String> ();
for (E property : properties.keySet ())
{
if (properties.get (property) != null)
- response.add (property.toString () + ": "
- + properties.get (property).toString ());
+ response.put (property.toString (),
+ properties.get (property).toString ());
}
return response;
diff --git a/src/uk/org/ury/library/LibraryRequestHandler.java b/src/uk/org/ury/library/LibraryRequestHandler.java
index 30bb7fc..835e50e 100644
--- a/src/uk/org/ury/library/LibraryRequestHandler.java
+++ b/src/uk/org/ury/library/LibraryRequestHandler.java
@@ -4,6 +4,7 @@
package uk.org.ury.library;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -19,6 +20,7 @@ import uk.org.ury.server.RequestHandler;
import uk.org.ury.server.exceptions.HandleFailureException;
import uk.org.ury.server.protocol.Directive;
+
/**
* A request handler for library queries.
*
@@ -48,11 +50,11 @@ public class LibraryRequestHandler implements RequestHandler
*/
@Override
- public List<String>
+ public Map<String, Object>
handleGetRequest (Map<String, String> parameters, Server server)
throws HandleFailureException
{
- List<String> response = new ArrayList<String> ();
+ Map<String, Object> response = new HashMap<String, Object>();
if (parameters.containsKey ("function"))
{
@@ -64,8 +66,9 @@ public class LibraryRequestHandler implements RequestHandler
}
else if (function.equals ("help"))
{
- response.add ("INFO: Available functions:");
- response.add ("INFO: search (string) - search library for string.");
+ response.put (Directive.INFO.toString (),
+ "Available functions:");
+ response.put (Directive.INFO.toString (), "search (string) - search library for string.");
}
else
throw new HandleFailureException ("Unknown function: "
@@ -97,7 +100,7 @@ public class LibraryRequestHandler implements RequestHandler
private void
doSearch (Map<String, String> parameters,
- List<String> response, Server server)
+ Map<String, Object> response, Server server)
throws HandleFailureException
{
if (parameters.containsKey ("search") == false)
@@ -123,14 +126,14 @@ public class LibraryRequestHandler implements RequestHandler
try
{
+ List<Map<String, String>> itemArray = new ArrayList<Map<String, String>> ();
+
for (LibraryItem li : LibraryUtils.search (dd, search))
{
- response.add (Directive.ITEM_START.toString ());
-
- response.addAll (li.asResponse ());
-
- response.add (Directive.ITEM_END.toString ());
+ itemArray.add (li.asResponse ());
}
+
+ response.put ("items", itemArray);
}
catch (QueryFailureException e)
{
diff --git a/src/uk/org/ury/library/LibraryUtils.java b/src/uk/org/ury/library/LibraryUtils.java
index f1e41c8..474c58a 100644
--- a/src/uk/org/ury/library/LibraryUtils.java
+++ b/src/uk/org/ury/library/LibraryUtils.java
@@ -16,7 +16,6 @@ import uk.org.ury.database.exceptions.QueryFailureException;
import uk.org.ury.library.exceptions.EmptySearchException;
import uk.org.ury.library.item.LibraryItem;
import uk.org.ury.library.item.LibraryItemProperty;
-import uk.org.ury.server.protocol.Directive;
/**
@@ -153,7 +152,7 @@ public class LibraryUtils
* stanzas in the response.
*/
- public static List<LibraryItem>
+/* public static List<LibraryItem>
extractItemsFromResponse (List<String> response)
{
List<LibraryItem> result = new ArrayList<LibraryItem> ();
@@ -185,7 +184,7 @@ public class LibraryUtils
}
return result;
- }
+ }*/
/**
@@ -205,7 +204,7 @@ public class LibraryUtils
* DatabaseItem does not use String as its data type.
*/
- public static LibraryItem
+ /*public static LibraryItem
createItemFromResponse (List<String> response)
{
// TODO: More appropriate exceptions.
@@ -239,5 +238,5 @@ public class LibraryUtils
}
else
throw new IllegalArgumentException ("Malformed response.");
- }
+ }*/
}
diff --git a/src/uk/org/ury/server/RequestHandler.java b/src/uk/org/ury/server/RequestHandler.java
index a8f434d..d4880d6 100644
--- a/src/uk/org/ury/server/RequestHandler.java
+++ b/src/uk/org/ury/server/RequestHandler.java
@@ -1,6 +1,5 @@
package uk.org.ury.server;
-import java.util.List;
import java.util.Map;
import uk.org.ury.server.exceptions.HandleFailureException;
@@ -31,14 +30,14 @@ public interface RequestHandler
* This will be able to provide the handler with
* pooled resources, for example the database.
*
- * @return A list of lines to return in the body of the
- * server's response to the client.
+ * @return A series of key-value pairs to pass back to
+ * the client.
*
* @throws HandleFailureException if the handler cannot
* handle the request.
*/
- public List<String>
+ public Map<String, Object>
handleGetRequest (Map<String, String> parameters, Server server)
throws HandleFailureException;
}
diff --git a/src/uk/org/ury/server/Server.java b/src/uk/org/ury/server/Server.java
index 5273362..75b5bd9 100644
--- a/src/uk/org/ury/server/Server.java
+++ b/src/uk/org/ury/server/Server.java
@@ -29,6 +29,7 @@ import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
+import org.json.simple.JSONValue;
import uk.org.ury.config.ConfigReader;
import uk.org.ury.database.DatabaseDriver;
@@ -41,6 +42,8 @@ import uk.org.ury.server.exceptions.HandlerNotFoundException;
import uk.org.ury.server.exceptions.HandlerSetupFailureException;
import uk.org.ury.server.exceptions.HandlingException;
import uk.org.ury.server.exceptions.NotAHandlerException;
+import uk.org.ury.server.protocol.Directive;
+import uk.org.ury.server.protocol.Status;
/**
* The unified URY server, accepting requests over HTTP.
@@ -353,7 +356,7 @@ public class Server
throw new HandlerSetupFailureException (className, e);
}
- List<String> content;
+ Map<String, Object> content = null;
try
{
@@ -376,21 +379,14 @@ public class Server
HttpStatus.SC_OK,
"OK");
- String entityString = "";
-
- entityString += "START" + "\r\n";
-
- for (String line : content)
- entityString += line + "\r\n";
+ content.put (Directive.STATUS.toString (),
+ Status.OK.toString ());
- entityString += "END";
-
-
StringEntity entity = null;
try
{
- entity = new StringEntity (entityString);
+ entity = new StringEntity (JSONValue.toJSONString (content));
}
catch (UnsupportedEncodingException e)
{
@@ -445,7 +441,14 @@ public class Server
try
{
- entity = new StringEntity ("ERROR: " + reason);
+ Map<String, Object> content = new HashMap<String, Object> ();
+
+ content.put (Directive.STATUS.toString (),
+ Status.ERROR.toString ());
+ content.put (Directive.REASON.toString (),
+ reason);
+
+ entity = new StringEntity (JSONValue.toJSONString (content));
}
catch (UnsupportedEncodingException e)
{
diff --git a/src/uk/org/ury/server/ServerRequestHandler.java b/src/uk/org/ury/server/ServerRequestHandler.java
index f67f927..c8d4f39 100644
--- a/src/uk/org/ury/server/ServerRequestHandler.java
+++ b/src/uk/org/ury/server/ServerRequestHandler.java
@@ -3,8 +3,7 @@
*/
package uk.org.ury.server;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashMap;
import java.util.Map;
import uk.org.ury.server.Server;
@@ -41,11 +40,11 @@ public class ServerRequestHandler implements RequestHandler
*/
@Override
- public List<String>
+ public Map<String, Object>
handleGetRequest (Map<String, String> parameters, Server server)
throws HandleFailureException
{
- List<String> response = new ArrayList<String> ();
+ Map<String, Object> response = new HashMap<String, Object> ();
if (parameters.containsKey ("function"))
{
@@ -57,11 +56,11 @@ public class ServerRequestHandler implements RequestHandler
}
else if (function.equals ("help"))
{
- response.add ("INFO: Available functions:");
- response.add ("INFO: info - Get server information.");
+ response.put ("INFO", "Available functions:");
+ response.put ("INFO", "info - Get server information.");
}
else if (function.equals ("test"))
- response.add ("INFO: Test succeeded.");
+ response.put ("INFO", "Test succeeded.");
else
throw new HandleFailureException ("Unknown function: "
+ function + ". (Try 'function=help'.)");
@@ -86,11 +85,11 @@ public class ServerRequestHandler implements RequestHandler
*/
private void
- getInfo (List<String> response, Server server)
+ getInfo (Map<String, Object> response, Server server)
throws HandleFailureException
{
- response.add ("INFO: University Radio York BAPS Replacement");
- response.add ("INFO: Server version is " + server.getVersion ());
+ response.put ("INFO", "University Radio York BAPS Replacement");
+ response.put ("INFO", "Server version is " + server.getVersion ());
}
}
diff --git a/src/uk/org/ury/server/protocol/DecodeFailureException.java b/src/uk/org/ury/server/protocol/DecodeFailureException.java
new file mode 100644
index 0000000..faa2b1f
--- /dev/null
+++ b/src/uk/org/ury/server/protocol/DecodeFailureException.java
@@ -0,0 +1,31 @@
+/**
+ *
+ */
+package uk.org.ury.server.protocol;
+
+/**
+ * Exception thrown when the protocol decoder fails.
+ *
+ * @author Matt Windsor
+ */
+
+public class DecodeFailureException extends Exception
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3972492943653273528L;
+
+
+ /**
+ * Construct a new DecodeFailureException with a reason.
+ *
+ * @param reason The reason for throwing the exception.
+ */
+
+ public
+ DecodeFailureException (String reason)
+ {
+ super (reason);
+ }
+}
diff --git a/src/uk/org/ury/server/protocol/Directive.java b/src/uk/org/ury/server/protocol/Directive.java
index 0d7a162..ce1b8b9 100644
--- a/src/uk/org/ury/server/protocol/Directive.java
+++ b/src/uk/org/ury/server/protocol/Directive.java
@@ -11,59 +11,8 @@ package uk.org.ury.server.protocol;
public enum Directive
{
- // ID String representation Singleton?
-
- /** Directive marking the start of an item block. */
- ITEM_START ("ITEM-START" , false),
-
- /** Directive marking a property inside an item block. */
- ITEM_PROPERTY ("PROP" , false),
-
- /** Directive marking the end of an item block. */
- ITEM_END ("ITEM-END" , false);
-
-
-
- private String strRep; // String representation
- private boolean isSingleton; // Is a singleton?
-
-
- /**
- * Construct a new Directive.
- *
- * @param strRep The string representation of the Directive.
- *
- * @param isSingleton If true, then the Directive accepts no
- * properties. If false, then the Directive
- * must be provided with at least one.
- */
-
- private
- Directive (String strRep, boolean isSingleton)
- {
- this.strRep = strRep;
- this.isSingleton = isSingleton;
- }
-
-
- /**
- * @return the string representation.
- */
-
- public String
- toString ()
- {
- return strRep;
- }
-
-
- /**
- * @return true if the directive has no properties.
- */
-
- public Boolean
- isSingleton ()
- {
- return isSingleton;
- }
+ INFO, // Information string (can usually be ignored)
+ ITEMS, // Item
+ STATUS, // Status code (from the enum Status)
+ REASON; // Error reason
}
diff --git a/src/uk/org/ury/server/protocol/ProtocolUtils.java b/src/uk/org/ury/server/protocol/ProtocolUtils.java
new file mode 100644
index 0000000..e45bb22
--- /dev/null
+++ b/src/uk/org/ury/server/protocol/ProtocolUtils.java
@@ -0,0 +1,67 @@
+/**
+ *
+ */
+package uk.org.ury.server.protocol;
+
+import java.util.Map;
+
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+
+/**
+ * Utilities for converting between strings encoded in the response
+ * protocol and collections of items.
+ *
+ * @author Matt Windsor
+ *
+ */
+
+public class ProtocolUtils
+{
+ /**
+ * Encode a key-value map into a protocol string.
+ *
+ * The map can contain strings, lists and other maps. Other
+ * types may be accepted by the underlying encoding engine,
+ * but the above types are the only ones explicitly accepted.
+ *
+ * @param items The key-value map of items, which should contain
+ * strings, lists and maps. The keys of any map
+ * should be protocol directives.
+ *
+ * @return A string containing the encoded representation of
+ * the map.
+ */
+
+ public static String
+ encode (Map<String, Object> items)
+ {
+ return JSONValue.toJSONString (items);
+ }
+
+
+ /**
+ * Decode a protocol string into a key-value map.
+ *
+ * @param string The string to decode.
+ *
+ * @return A key-value map mapping directives to strings,
+ * lists and maps.
+ *
+ * @throws DecodeFailureException if the decoding engine
+ * returns something other than a map.
+ */
+
+ public static Map<?, ?>
+ decode (String string) throws DecodeFailureException
+ {
+ Object result = JSONValue.parse (string);
+
+ if (result instanceof JSONObject)
+ return (JSONObject) result;
+ else
+ throw new DecodeFailureException ("Result not a map.");
+ }
+
+}
diff --git a/src/uk/org/ury/server/protocol/Status.java b/src/uk/org/ury/server/protocol/Status.java
new file mode 100644
index 0000000..c9d848d
--- /dev/null
+++ b/src/uk/org/ury/server/protocol/Status.java
@@ -0,0 +1,17 @@
+/**
+ *
+ */
+package uk.org.ury.server.protocol;
+
+
+/**
+ * Statuses that can follow the STATUS directory.
+ *
+ * @author Matt Windsor
+ */
+
+public enum Status
+ {
+ OK, // The request was processed OK; response should be valid
+ ERROR // An error occurred; message provided as REASON directive
+ }