diff options
author | Matt Windsor <mattwindsor@btinternet.com> | 2011-03-19 14:12:44 +0000 |
---|---|---|
committer | Matt Windsor <mattwindsor@btinternet.com> | 2011-03-19 14:12:44 +0000 |
commit | 410219d08abdb859315c4d6d0e0375287d64a88b (patch) | |
tree | 3d4610f6cc671147a0aec7bca573a38d6c98b58e | |
parent | 2efb758e7c2bd85801c76305161d9d8fa6d2be4b (diff) |
Converted protocol to JSON. Now passes its first ever unit test\!
-rw-r--r-- | src/uk/org/ury/client/Client.java | 21 | ||||
-rw-r--r-- | src/uk/org/ury/client/test/ClientTest.java | 18 | ||||
-rw-r--r-- | src/uk/org/ury/database/DatabaseItem.java | 11 | ||||
-rw-r--r-- | src/uk/org/ury/library/LibraryRequestHandler.java | 23 | ||||
-rw-r--r-- | src/uk/org/ury/library/LibraryUtils.java | 9 | ||||
-rw-r--r-- | src/uk/org/ury/server/RequestHandler.java | 7 | ||||
-rw-r--r-- | src/uk/org/ury/server/Server.java | 27 | ||||
-rw-r--r-- | src/uk/org/ury/server/ServerRequestHandler.java | 19 | ||||
-rw-r--r-- | src/uk/org/ury/server/protocol/DecodeFailureException.java | 31 | ||||
-rw-r--r-- | src/uk/org/ury/server/protocol/Directive.java | 59 | ||||
-rw-r--r-- | src/uk/org/ury/server/protocol/ProtocolUtils.java | 67 | ||||
-rw-r--r-- | src/uk/org/ury/server/protocol/Status.java | 17 |
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 + } |