aboutsummaryrefslogtreecommitdiff
path: root/src/uk
diff options
context:
space:
mode:
Diffstat (limited to 'src/uk')
-rw-r--r--src/uk/org/ury/client/Client.java96
-rw-r--r--src/uk/org/ury/client/test/ClientTest.java84
-rw-r--r--src/uk/org/ury/frontend/FrontendApplet.java572
-rw-r--r--src/uk/org/ury/library/LibraryRequestHandler.java4
-rw-r--r--src/uk/org/ury/library/viewer/LibraryViewer.java275
-rw-r--r--src/uk/org/ury/library/viewer/LibraryViewer.properties2
-rw-r--r--src/uk/org/ury/library/viewer/LibraryViewerPanel.java408
-rw-r--r--src/uk/org/ury/protocol/ProtocolUtils.java147
-rw-r--r--src/uk/org/ury/server/ApiRequestHandler.java42
-rw-r--r--src/uk/org/ury/server/HttpHandler.java343
-rw-r--r--src/uk/org/ury/server/HttpListenerThread.java125
-rw-r--r--src/uk/org/ury/server/HttpWorkerThread.java104
-rw-r--r--src/uk/org/ury/server/Server.java611
-rw-r--r--src/uk/org/ury/server/ServerRequestHandler.java4
-rw-r--r--src/uk/org/ury/show/viewer/ShowViewer.java205
15 files changed, 1495 insertions, 1527 deletions
diff --git a/src/uk/org/ury/client/Client.java b/src/uk/org/ury/client/Client.java
index 1a3b53a..69a095a 100644
--- a/src/uk/org/ury/client/Client.java
+++ b/src/uk/org/ury/client/Client.java
@@ -13,58 +13,48 @@ import java.util.Map;
import uk.org.ury.protocol.ProtocolUtils;
import uk.org.ury.protocol.exceptions.DecodeFailureException;
-public class Client
-{
- /**
- * Get a raw response from the server.
- *
- * @param file The "file", including path and query string, to
- * fetch from the server.
- *
- * @return The response from the server, as a key-value map.
- * @throws DecodeFailureException if the decode failed.
- */
-
- public Map<?, ?>
- get (String file) throws DecodeFailureException
- {
- URL url = null;
- URLConnection uc = null;
- String result = "";
-
- try
- {
- url = new URL ("http://localhost:8000" + file);
- }
- catch (MalformedURLException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace ();
- }
-
- try
- {
- uc = url.openConnection ();
-
- BufferedReader in = new BufferedReader (new InputStreamReader
- (uc.getInputStream ()));
-
-
- String inputLine;
+public class Client {
+ /**
+ * Get a raw response from the server.
+ *
+ * @param file
+ * The "file", including path and query string, to fetch from the
+ * server.
+ *
+ * @return The response from the server, as a key-value map.
+ * @throws DecodeFailureException
+ * if the decode failed.
+ */
- for (inputLine = in.readLine();
- inputLine != null;
- inputLine = in.readLine())
- {
- result += inputLine;
- }
- }
- catch (IOException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace ();
- }
-
- return ProtocolUtils.decode (result);
- }
+ public Map<?, ?> get(String file) throws DecodeFailureException {
+ URL url = null;
+ URLConnection uc = null;
+ String result = "";
+
+ try {
+ url = new URL("http://localhost:8000" + file);
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ try {
+ uc = url.openConnection();
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(
+ uc.getInputStream()));
+
+ String inputLine;
+
+ for (inputLine = in.readLine(); inputLine != null; inputLine = in
+ .readLine()) {
+ result += inputLine;
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ 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 707a80f..3ee70ef 100644
--- a/src/uk/org/ury/client/test/ClientTest.java
+++ b/src/uk/org/ury/client/test/ClientTest.java
@@ -15,59 +15,43 @@ import uk.org.ury.client.Client;
import uk.org.ury.protocol.Directive;
import uk.org.ury.protocol.exceptions.DecodeFailureException;
-
/**
* JUnit test for the low-level client logic.
*
* @author Matt Windsor
*/
-
-public class ClientTest
-{
-
- /**
- * @throws java.lang.Exception
- */
-
- @Before
- public void
- setUp () throws Exception
- {
- }
-
-
- /**
- * @throws java.lang.Exception
- */
-
- @After
- public void
- tearDown () throws Exception
- {
- }
-
-
- /**
- * Test method for {@link uk.org.ury.client.Client#get(java.lang.String)}.
- */
-
- @Test
- public void
- testGet ()
- {
- Client client = new Client ();
-
- Map<?, ?> response = null;
-
- try
- {
- response = client.get ("/server/ServerRequestHandler?function=test");
- }
- catch (DecodeFailureException e)
- {
- e.printStackTrace ();
- }
-
- Assert.assertEquals ("Test succeeded.", response.get (Directive.INFO.toString ()));
- }
+public class ClientTest {
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ /**
+ * Test method for {@link uk.org.ury.client.Client#get(java.lang.String)}.
+ */
+ @Test
+ public void testGet() {
+ Client client = new Client();
+
+ Map<?, ?> response = null;
+
+ 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/frontend/FrontendApplet.java b/src/uk/org/ury/frontend/FrontendApplet.java
index 18f0c7a..8e0e229 100644
--- a/src/uk/org/ury/frontend/FrontendApplet.java
+++ b/src/uk/org/ury/frontend/FrontendApplet.java
@@ -14,330 +14,268 @@ import uk.org.ury.frontend.exceptions.LoadFailureException;
import uk.org.ury.testrig.Launcher;
/**
- * A frame that hosts a FrontendModulePanel, used for serving frontend
- * panels in a window (application mode).
+ * A frame that hosts a FrontendModulePanel, used for serving frontend panels in
+ * a window (application mode).
*
* @author Matt Windsor
- *
+ *
*/
-public class FrontendApplet extends JApplet implements FrontendMaster, Launcher
-{
- /**
- *
- */
-
- private static final long serialVersionUID = 740928181256928433L;
-
- private FrontendModulePanel child;
- private FrontendControlPanel cpanel;
-
-
- /**
- * Main method.
- *
- * @param args The command-line arguments to the program. These
- * will currently be ignored.
- */
-
- @Override
- public void
- init ()
- {
- try
- {
- javax.swing.SwingUtilities.invokeAndWait (new Runnable()
- {
- public void
- run ()
- {
- try
- {
- loadModule (DEFAULT_MODULE_NAME);
- }
- catch (LoadFailureException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace ();
- }
- setupUI ();
- }
- });
- }
- catch (Exception e)
- {
- e.printStackTrace ();
- System.err.println("createGUI didn't successfully complete");
- }
- }
-
-
- /**
- * Set up the user interface of the frame.
- */
-
- @Override
- public void
- setupUI ()
- {
- try
- {
- // Set System L&F
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName ());
- }
- catch (UnsupportedLookAndFeelException e)
- {
- // handle exception
- }
- catch (ClassNotFoundException e)
- {
- // handle exception
- }
- catch (InstantiationException e)
- {
- // handle exception
- }
- catch (IllegalAccessException e)
- {
- // handle exception
- }
-
-
- // Composition
-
- add (child, BorderLayout.CENTER);
-
- setPreferredSize (new Dimension (800, 600));
- setMinimumSize (new Dimension (800, 600));
-
- setVisible (true);
- }
-
-
- /**
- * Load a module into the frontend frame.
- *
- * Loading will fail with a fatal error if the class is not found,
- * or is not an implementor of FrontendModule.
+public class FrontendApplet extends JApplet implements FrontendMaster, Launcher {
+ /**
*
- * @param moduleName The fully qualified class-name of the module,
- * minus the leading "uk.org.ury." domain.
- *
- * @throws LoadFailureException if the class is
- * not found, or is not an implementor of
- * FrontendModule.
- */
-
- @Override
- public void
- loadModule (String moduleName)
- throws LoadFailureException
- {
- Class<?> moduleClass = null;
-
- try
- {
- moduleClass = Class.forName ("uk.org.ury." + moduleName);
- }
- catch (ClassNotFoundException e)
- {
- throw new LoadFailureException ("Could not load module: "
- + e.getMessage ());
- }
-
-
- if (FrontendModule.class.isAssignableFrom (moduleClass) == false)
- {
- throw new LoadFailureException ("Could not load module: Not a FrontendModule");
- }
- else
- {
- FrontendModulePanel temp = child;
-
- try
- {
- child = ((FrontendModule) moduleClass.newInstance ()).runFrontend (this);
- }
- catch (InstantiationException e)
- {
- throw new LoadFailureException ("Could not load module: "
- + e.getMessage ());
- }
- catch (IllegalAccessException e)
- {
- throw new LoadFailureException ("Could not load module: "
- + e.getMessage ());
- }
-
- if (temp != null)
- remove (temp);
-
- getContentPane ().add (child, BorderLayout.CENTER);
- child.setMaster (this);
-
- repaint ();
- }
- }
-
-
- /**
- * Load a module into the frontend frame, additionally installing
- * a control panel to communicate with the previous module.
- *
- * Loading will fail with a fatal error if the class is not found,
- * or is not an implementor of FrontendModule.
- *
- * @param moduleName The fully qualified class-name of the module,
- * minus the leading "uk.org.ury." domain.
- *
- * @param cPanelName The fully qualified class-name of the control
- * panel to install, minus the leading
- * "uk.org.ury." domain.
- *
- * @throws LoadFailureException if the class is
- * not found, or is not an implementor of
- * FrontendModule.
- */
-
- @Override
- public void
- loadModule (String moduleName, String cPanelName)
- throws LoadFailureException
- {
- FrontendModulePanel newParent = child;
- loadModule (moduleName);
- FrontendModulePanel newChild = child;
-
- loadControlPanel (cPanelName, newParent, newChild);
- }
-
-
- /**
- * Load and install a control panel class given the class name.
- *
- * @param cPanelName The fully qualified class-name of the control
- * panel to load, minus the leading
- * "uk.org.ury." domain.
- *
- * @param parent The parent panel in the relationship modelled
- * by the control panel interface.
- *
- * @param child The child panel in the relationship modelled
- * by the control panel interface.
- *
- * @throws LoadFailureException if the class is
- * not found, or is not an implementor of
- * FrontendControlPanel.
*/
- private void
- loadControlPanel (String cPanelName, FrontendModulePanel parent,
- FrontendModulePanel child)
- throws LoadFailureException
- {
- Class<?> cPanelClass = null;
-
- try
- {
- cPanelClass = Class.forName ("uk.org.ury." + cPanelName);
- }
- catch (ClassNotFoundException e)
- {
- throw new LoadFailureException ("Could not load control panel: "
- + e.getMessage ());
- }
-
-
- if (FrontendControlPanel.class.isAssignableFrom (cPanelClass))
- {
- FrontendControlPanel temp = cpanel;
-
- try
- {
- cpanel = ((FrontendControlPanel) cPanelClass.newInstance ());
- }
- catch (InstantiationException e)
- {
- throw new LoadFailureException ("Could not load control panel: "
- + e.getMessage ());
- }
- catch (IllegalAccessException e)
- {
- throw new LoadFailureException ("Could not load control panel: "
- + e.getMessage ());
- }
-
- if (temp != null)
- remove (temp);
-
- cpanel.setPanels (parent, child);
- cpanel.setMaster (this);
- cpanel.setPreviousCPanel (temp);
-
- add (cpanel, BorderLayout.SOUTH);
- repaint ();
- }
- }
-
-
- /**
- * Restore an existing module and control panel into the frontend
- * frame.
- *
- * @param mpanel The module panel to restore.
- *
- * @param cpanel The control panel to restore, if any. A null
- * value signifies a lack of control panel.
- *
- * @throws IllegalArgumentException if the mpanel is null.
- */
-
- @Override
- public void
- restoreModule (FrontendModulePanel mpanel,
- FrontendControlPanel cpanel)
- {
- if (mpanel == null)
- throw new IllegalArgumentException ("mpanel is null.");
-
- remove (child);
- remove (this.cpanel);
-
- child = mpanel;
- add (child);
-
- if (cpanel != null)
- add (cpanel, BorderLayout.SOUTH);
-
- this.cpanel = cpanel;
-
- repaint ();
- }
-
-
- /**
- * Report a fatal error,
- *
- * @param message The message, eg the exception message, to report
- * to the user.
- */
+ private static final long serialVersionUID = 740928181256928433L;
- @Override
- public void
- fatalError (String message)
- {
- FrontendError.reportFatal (message, this);
- }
+ private FrontendModulePanel child;
+ private FrontendControlPanel cpanel;
-
- /**
- * @return the resource directory.
- */
-
- @Override
- public String
- getResourceDirectory ()
- {
- return getCodeBase () + "res/";
- }
+ /**
+ * Main method.
+ */
+
+ @Override
+ public void init() {
+ try {
+ javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ try {
+ loadModule(DEFAULT_MODULE_NAME);
+ } catch (LoadFailureException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ setupUI();
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.err.println("createGUI didn't successfully complete");
+ }
+ }
+
+ /**
+ * Set up the user interface of the frame.
+ */
+
+ @Override
+ public void setupUI() {
+ try {
+ // Set System L&F
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (UnsupportedLookAndFeelException e) {
+ // handle exception
+ } catch (ClassNotFoundException e) {
+ // handle exception
+ } catch (InstantiationException e) {
+ // handle exception
+ } catch (IllegalAccessException e) {
+ // handle exception
+ }
+
+ // Composition
+
+ add(child, BorderLayout.CENTER);
+
+ setPreferredSize(new Dimension(800, 600));
+ setMinimumSize(new Dimension(800, 600));
+
+ setVisible(true);
+ }
+
+ /**
+ * Load a module into the frontend frame.
+ *
+ * Loading will fail with a fatal error if the class is not found, or is not
+ * an implementor of FrontendModule.
+ *
+ * @param moduleName
+ * The fully qualified class-name of the module, minus the
+ * leading "uk.org.ury." domain.
+ *
+ * @throws LoadFailureException
+ * if the class is not found, or is not an implementor of
+ * FrontendModule.
+ */
+
+ @Override
+ public void loadModule(String moduleName) throws LoadFailureException {
+ Class<?> moduleClass = null;
+
+ try {
+ moduleClass = Class.forName("uk.org.ury." + moduleName);
+ } catch (ClassNotFoundException e) {
+ throw new LoadFailureException("Could not load module: "
+ + e.getMessage());
+ }
+
+ if (FrontendModule.class.isAssignableFrom(moduleClass) == false) {
+ throw new LoadFailureException(
+ "Could not load module: Not a FrontendModule");
+ } else {
+ FrontendModulePanel temp = child;
+
+ try {
+ child = ((FrontendModule) moduleClass.newInstance())
+ .runFrontend(this);
+ } catch (InstantiationException e) {
+ throw new LoadFailureException("Could not load module: "
+ + e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new LoadFailureException("Could not load module: "
+ + e.getMessage());
+ }
+
+ if (temp != null)
+ remove(temp);
+
+ getContentPane().add(child, BorderLayout.CENTER);
+ child.setMaster(this);
+
+ repaint();
+ }
+ }
+
+ /**
+ * Load a module into the frontend frame, additionally installing a control
+ * panel to communicate with the previous module.
+ *
+ * Loading will fail with a fatal error if the class is not found, or is not
+ * an implementor of FrontendModule.
+ *
+ * @param moduleName
+ * The fully qualified class-name of the module, minus the
+ * leading "uk.org.ury." domain.
+ *
+ * @param cPanelName
+ * The fully qualified class-name of the control panel to
+ * install, minus the leading "uk.org.ury." domain.
+ *
+ * @throws LoadFailureException
+ * if the class is not found, or is not an implementor of
+ * FrontendModule.
+ */
+
+ @Override
+ public void loadModule(String moduleName, String cPanelName)
+ throws LoadFailureException {
+ FrontendModulePanel newParent = child;
+ loadModule(moduleName);
+ FrontendModulePanel newChild = child;
+
+ loadControlPanel(cPanelName, newParent, newChild);
+ }
+
+ /**
+ * Load and install a control panel class given the class name.
+ *
+ * @param cPanelName
+ * The fully qualified class-name of the control panel to load,
+ * minus the leading "uk.org.ury." domain.
+ *
+ * @param parent
+ * The parent panel in the relationship modelled by the control
+ * panel interface.
+ *
+ * @param child
+ * The child panel in the relationship modelled by the control
+ * panel interface.
+ *
+ * @throws LoadFailureException
+ * if the class is not found, or is not an implementor of
+ * FrontendControlPanel.
+ */
+
+ private void loadControlPanel(String cPanelName,
+ FrontendModulePanel parent, FrontendModulePanel child)
+ throws LoadFailureException {
+ Class<?> cPanelClass = null;
+
+ try {
+ cPanelClass = Class.forName("uk.org.ury." + cPanelName);
+ } catch (ClassNotFoundException e) {
+ throw new LoadFailureException("Could not load control panel: "
+ + e.getMessage());
+ }
+
+ if (FrontendControlPanel.class.isAssignableFrom(cPanelClass)) {
+ FrontendControlPanel temp = cpanel;
+
+ try {
+ cpanel = ((FrontendControlPanel) cPanelClass.newInstance());
+ } catch (InstantiationException e) {
+ throw new LoadFailureException("Could not load control panel: "
+ + e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new LoadFailureException("Could not load control panel: "
+ + e.getMessage());
+ }
+
+ if (temp != null)
+ remove(temp);
+
+ cpanel.setPanels(parent, child);
+ cpanel.setMaster(this);
+ cpanel.setPreviousCPanel(temp);
+
+ add(cpanel, BorderLayout.SOUTH);
+ repaint();
+ }
+ }
+
+ /**
+ * Restore an existing module and control panel into the frontend frame.
+ *
+ * @param mpanel
+ * The module panel to restore.
+ *
+ * @param cpanel
+ * The control panel to restore, if any. A null value signifies a
+ * lack of control panel.
+ *
+ * @throws IllegalArgumentException
+ * if the mpanel is null.
+ */
+
+ @Override
+ public void restoreModule(FrontendModulePanel mpanel,
+ FrontendControlPanel cpanel) {
+ if (mpanel == null)
+ throw new IllegalArgumentException("mpanel is null.");
+
+ remove(child);
+ remove(this.cpanel);
+
+ child = mpanel;
+ add(child);
+
+ if (cpanel != null)
+ add(cpanel, BorderLayout.SOUTH);
+
+ this.cpanel = cpanel;
+
+ repaint();
+ }
+
+ /**
+ * Report a fatal error,
+ *
+ * @param message
+ * The message, eg the exception message, to report to the user.
+ */
+
+ @Override
+ public void fatalError(String message) {
+ FrontendError.reportFatal(message, this);
+ }
+
+ /**
+ * @return the resource directory.
+ */
+
+ @Override
+ public String getResourceDirectory() {
+ return getCodeBase() + "res/";
+ }
} \ No newline at end of file
diff --git a/src/uk/org/ury/library/LibraryRequestHandler.java b/src/uk/org/ury/library/LibraryRequestHandler.java
index ad174b8..1c4ad96 100644
--- a/src/uk/org/ury/library/LibraryRequestHandler.java
+++ b/src/uk/org/ury/library/LibraryRequestHandler.java
@@ -17,7 +17,7 @@ import uk.org.ury.library.exceptions.EmptySearchException;
import uk.org.ury.library.item.LibraryItem;
import uk.org.ury.protocol.Directive;
import uk.org.ury.server.Server;
-import uk.org.ury.server.RequestHandler;
+import uk.org.ury.server.ApiRequestHandler;
import uk.org.ury.server.exceptions.HandleFailureException;
@@ -27,7 +27,7 @@ import uk.org.ury.server.exceptions.HandleFailureException;
* @author Matt Windsor
*/
-public class LibraryRequestHandler implements RequestHandler
+public class LibraryRequestHandler implements ApiRequestHandler
{
/**
* Handle a server GET request (that is, a request for data
diff --git a/src/uk/org/ury/library/viewer/LibraryViewer.java b/src/uk/org/ury/library/viewer/LibraryViewer.java
index 9e854b7..11496cb 100644
--- a/src/uk/org/ury/library/viewer/LibraryViewer.java
+++ b/src/uk/org/ury/library/viewer/LibraryViewer.java
@@ -1,3 +1,14 @@
+/*
+ * LibraryViewer.java
+ * ------------------
+ *
+ * Part of the URY Frontend Platform
+ *
+ * V0.00 2011/03/20
+ *
+ * (C) 2011 URY Computing
+ */
+
package uk.org.ury.library.viewer;
import java.util.ArrayList;
@@ -7,168 +18,134 @@ import java.util.Map;
import java.util.Set;
import uk.org.ury.client.Client;
-
import uk.org.ury.frontend.AbstractFrontendModule;
import uk.org.ury.frontend.FrontendMaster;
import uk.org.ury.frontend.FrontendModulePanel;
import uk.org.ury.frontend.exceptions.UICreationFailureException;
-
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.protocol.Directive;
import uk.org.ury.protocol.ProtocolUtils;
import uk.org.ury.protocol.exceptions.DecodeFailureException;
import uk.org.ury.protocol.exceptions.InvalidMessageException;
-
/**
* Module for investigating the track library.
- *
- * @author Matt Windsor
- *
+ *
+ * @author Matt Windsor
*/
-
-public class LibraryViewer extends AbstractFrontendModule
-{
- /**
- *
- */
-
- private static final long serialVersionUID = -2782366476480563739L;
- private List<LibraryItem> libraryList;
- private LibraryViewerPanel panel;
-
-
- /**
- * Construct a new LibraryViewer as a frontend object.
- */
-
- public
- LibraryViewer ()
- {
- libraryList = new ArrayList<LibraryItem> ();
- panel = null;
- }
-
-
- /**
- * Run the library viewer frontend.
- */
-
- public FrontendModulePanel
- runFrontend (FrontendMaster master)
- {
- try
- {
- panel = new LibraryViewerPanel (this, master);
- }
- catch (UICreationFailureException e)
- {
- master.fatalError (e.getMessage ());
- }
-
- return panel;
- }
-
-
- /**
- * Do a library search.
- *
- * This will update the library list to reflect the results of the
- * search.
- *
- * @param search The string fragment to use in searches.
- * Cannot be empty or null.
- *
- * @throws EmptySearchException if the search string is
- * empty or null (from LibraryUtils.search).
- *
- * @throws InvalidMessageException if the response from
- * the server is invalid.
- */
-
- public void
- doSearch (String search)
- throws EmptySearchException, InvalidMessageException
- {
- // TODO: fan out?
-
- if (search == null || search == "")
- throw new EmptySearchException ();
-
-
- Client cl = new Client ();
- Map<?, ?> response = null;
- libraryList.clear ();
-
- try
- {
- response = cl.get ("/library/LibraryRequestHandler?function=search&search=" + search);
- }
- catch (DecodeFailureException e)
- {
- throw new InvalidMessageException (e.getMessage ());
- }
-
- // Check to see if this is Map<String, ?> by looking for the status,
- // which should always be in a valid response.
-
-
- if (ProtocolUtils.responseIsOK (response) == false)
- throw new InvalidMessageException ((String)
- response.get (Directive.REASON.toString ()));
-
- // Should contain a list of items, even if there are no items.
- if (response.containsKey (Directive.ITEMS.toString ()) == false)
- throw new InvalidMessageException ("No item set returned.");
-
- if ((response.get (Directive.ITEMS.toString ()) instanceof List<?>) == false)
- throw new InvalidMessageException ("Malformed item list.");
-
-
- for (Object obj : (List<?>) response.get (Directive.ITEMS.toString ()))
- {
- Map<LibraryItemProperty, String> properties
- = new HashMap<LibraryItemProperty, String> ();
-
- if (obj instanceof Map<?, ?> == false)
- throw new InvalidMessageException ("Malformed item.");
-
- Set<?> keySet = ((Map<?, ?>) obj).keySet ();
-
- // Check to make sure this item has only String-String mappings.
- for (Object key : keySet)
- {
- if ((key instanceof String
- && ((Map<?, ?>) obj).get (key) instanceof String)
- == false)
- throw new InvalidMessageException ("Not a valid property.");
- else if (LibraryItemProperty.valueOf ((String) key) == null)
- throw new InvalidMessageException ("Property type "
- + key + " not recognised.");
- else
- properties.put (LibraryItemProperty.valueOf ((String) key),
- (String) ((Map<?, ?>) obj).get (key));
-
- }
-
- libraryList.add (new LibraryItem (properties));
- }
-
- //libraryList = LibraryUtils.search (dd, search);
- }
-
-
- /**
- * @return the current library list.
- */
-
- public List<LibraryItem>
- getLibraryList ()
- {
- return libraryList;
- }
+public class LibraryViewer extends AbstractFrontendModule {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -2782366476480563739L;
+ private List<LibraryItem> libraryList;
+ private LibraryViewerPanel panel;
+
+ /**
+ * Construct a new LibraryViewer as a frontend object.
+ */
+ public LibraryViewer() {
+ libraryList = new ArrayList<LibraryItem>();
+ panel = null;
+ }
+
+ /**
+ * Run the library viewer frontend.
+ */
+ @Override
+ public FrontendModulePanel runFrontend(FrontendMaster master) {
+ try {
+ panel = new LibraryViewerPanel(this, master);
+ } catch (UICreationFailureException e) {
+ master.fatalError(e.getMessage());
+ }
+
+ return panel;
+ }
+
+ /**
+ * Do a library search.
+ *
+ * This will update the library list to reflect the results of the search.
+ *
+ * @param search
+ * The string fragment to use in searches. Cannot be empty or
+ * null.
+ *
+ * @throws EmptySearchException
+ * if the search string is empty or null (from
+ * LibraryUtils.search).
+ *
+ * @throws InvalidMessageException
+ * if the response from the server is invalid.
+ */
+ public void doSearch(String search) throws EmptySearchException,
+ InvalidMessageException {
+ // TODO: fan out?
+
+ if (search == null || search == "")
+ throw new EmptySearchException();
+
+ Client cl = new Client();
+ Map<?, ?> response = null;
+ libraryList.clear();
+
+ try {
+ response = cl
+ .get("/library/LibraryRequestHandler?function=search&search="
+ + search);
+ } catch (DecodeFailureException e) {
+ throw new InvalidMessageException(e.getMessage());
+ }
+
+ /*
+ * Check to see if this is Map<String, ?> by looking for the status,
+ * which should always be in a valid response.
+ */
+
+ if (ProtocolUtils.responseIsOK(response) == false)
+ throw new InvalidMessageException(
+ (String) response.get(Directive.REASON.toString()));
+
+ // Should contain a list of items, even if there are no items.
+ if (response.containsKey(Directive.ITEMS.toString()) == false)
+ throw new InvalidMessageException("No item set returned.");
+
+ if ((response.get(Directive.ITEMS.toString()) instanceof List<?>) == false)
+ throw new InvalidMessageException("Malformed item list.");
+
+ for (Object obj : (List<?>) response.get(Directive.ITEMS.toString())) {
+ Map<LibraryItemProperty, String> properties = new HashMap<LibraryItemProperty, String>();
+
+ if (obj instanceof Map<?, ?> == false)
+ throw new InvalidMessageException("Malformed item.");
+
+ Set<?> keySet = ((Map<?, ?>) obj).keySet();
+
+ // Check to make sure this item has only String-String mappings.
+ for (Object key : keySet) {
+ if ((key instanceof String && ((Map<?, ?>) obj).get(key) instanceof String) == false)
+ throw new InvalidMessageException("Not a valid property.");
+ else if (LibraryItemProperty.valueOf((String) key) == null)
+ throw new InvalidMessageException("Property type " + key
+ + " not recognised.");
+ else
+ properties.put(LibraryItemProperty.valueOf((String) key),
+ (String) ((Map<?, ?>) obj).get(key));
+
+ }
+
+ libraryList.add(new LibraryItem(properties));
+ }
+ }
+
+ /**
+ * @return the current library list.
+ */
+
+ public List<LibraryItem> getLibraryList() {
+ return libraryList;
+ }
}
diff --git a/src/uk/org/ury/library/viewer/LibraryViewer.properties b/src/uk/org/ury/library/viewer/LibraryViewer.properties
index 677cbc5..bb88988 100644
--- a/src/uk/org/ury/library/viewer/LibraryViewer.properties
+++ b/src/uk/org/ury/library/viewer/LibraryViewer.properties
@@ -3,7 +3,7 @@ MODULE_NAME = Library Viewer Demo
// Search errors
// 1st parameter: search term, 2nd parameter: failure reason etc.
-ERR_UNKNOWN = Unknown error while searching for '%1$s'.
+ERR_UNKNOWN = Unknown error.
ERR_SEARCH_FAILED = Search for '%1$s' failed: %2$s
ERR_EMPTY_SEARCH = Please type in a search term.
ERR_NO_RESULTS = Sorry, but no results were found for '%1$s'.
diff --git a/src/uk/org/ury/library/viewer/LibraryViewerPanel.java b/src/uk/org/ury/library/viewer/LibraryViewerPanel.java
index b3ffdd3..c3ec351 100644
--- a/src/uk/org/ury/library/viewer/LibraryViewerPanel.java
+++ b/src/uk/org/ury/library/viewer/LibraryViewerPanel.java
@@ -1,8 +1,15 @@
-/**
+/*
+ * LibraryViewerPanel.java
+ * -----------------------
+ *
+ * Part of the URY Frontend Platform
+ *
+ * V0.00 2011/03/20
*
+ * (C) 2011 URY Computing
*/
-package uk.org.ury.library.viewer;
+package uk.org.ury.library.viewer;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutionException;
@@ -14,242 +21,197 @@ import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
-
import uk.org.ury.frontend.FrontendMaster;
import uk.org.ury.frontend.FrontendModulePanel;
import uk.org.ury.frontend.HintField;
import uk.org.ury.frontend.exceptions.UICreationFailureException;
-
import uk.org.ury.library.exceptions.EmptySearchException;
-
import uk.org.ury.protocol.exceptions.InvalidMessageException;
-
/**
* Frontend panel providing access to an underlying library viewer.
*
* @author Matt Windsor, Nathan Lasseter
*/
+public class LibraryViewerPanel extends FrontendModulePanel {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -2441616418398056712L;
-public class LibraryViewerPanel extends FrontendModulePanel
-{
- /**
- *
- */
- private static final long serialVersionUID = -2441616418398056712L;
-
-
- /* Panel widgets exposed by the SwiXML user interface. */
-
- private JTable resultsTable;
- private JScrollPane resultsPane;
-
- private JPanel messagePanel;
- private JLabel messageLabel;
-
- private JPanel searchingPanel;
- private JLabel searchingLabel;
-
- private JTextField searchField;
- private JButton searchButton;
- private JLabel searchForLabel;
-
- private HintField hint;
-
-
- /* This contains the last search failure message, for use in
- * letting the user know what happened.
- */
-
- private String searchFailureMessage;
-
-
- // Resource bundle.
-
- private ResourceBundle rb;
-
-
- /**
- * Construct a new LibraryViewerPanel.
- *
- * @param viewer The LibraryViewer controlling this LibraryViewerPanel.
- *
- * @param master The FrontendMaster driving the frontend.
- *
- * @throws UICreationFailureException if the UI creation fails.
- */
-
- public
- LibraryViewerPanel (LibraryViewer viewer, FrontendMaster master)
- throws UICreationFailureException
- {
- /* The UI implementation is contained in library_viewer_gui.xml.
- *
- * See this file for more details.
+ /* Panel widgets exposed by the SwiXML user interface. */
+
+ private JTable resultsTable;
+ private JScrollPane resultsPane;
+
+ private JPanel messagePanel;
+ private JLabel messageLabel;
+
+ private JPanel searchingPanel;
+ private JLabel searchingLabel;
+
+ private JTextField searchField;
+ private JButton searchButton;
+ private JLabel searchForLabel;
+
+ private HintField hint;
+
+ /*
+ * This contains the last search failure message, for use in letting the
+ * user know what happened.
*/
-
- super (viewer, "library_viewer_gui.xml", master);
-
-
- // Fill in locale-specific data.
-
- rb = ResourceBundle.getBundle ("uk.org.ury.library.viewer.LibraryViewer");
-
- searchFailureMessage = rb.getString ("ERR_UNKNOWN");
-
- searchingLabel.setText (rb.getString ("MSG_SEARCHING"));
- searchForLabel.setText (rb.getString ("LBL_SEARCHFOR"));
- searchButton.setText (rb.getString ("BTN_SEARCH"));
- hint.setText (rb.getString ("HINT"));
-
- // Fine-tune table
-
- resultsTable.setAutoCreateRowSorter (true);
- }
-
-
- /**
- * @return the name of the panel.
- *
- * @see uk.org.ury.frontend.FrontendModulePanel#getModuleName()
- */
-
- @Override
- public String
- getModuleName ()
- {
- return rb.getString ("MODULE_NAME");
- }
-
-
- /**
- * Action method for performing a search, bound by the UI XML
- * manifest to the search field and button.
- */
-
- public void
- search ()
- {
- /* We can't let the user search while another search is going on,
- * but it's not good to let the search "freeze" the UI.
+
+ private String searchFailureMessage;
+
+ // Resource bundle.
+
+ private ResourceBundle rb;
+
+ /**
+ * Construct a new LibraryViewerPanel.
+ *
+ * @param viewer
+ * The LibraryViewer controlling this LibraryViewerPanel.
*
- * Hence the search function disables all sensitive parts of the
- * interface and launches a search as a background process.
+ * @param master
+ * The FrontendMaster driving the frontend.
*
- * We also swap the results table or no-results panel out for a
- * panel that says "Searching...", in the interests of
- * user-friendliness.
+ * @throws UICreationFailureException
+ * if the UI creation fails.
*/
-
- searchField.setEnabled (false);
- searchButton.setEnabled (false);
- resultsPane.setVisible (false);
- messagePanel.setVisible (false);
- searchingPanel.setVisible (true);
- searchingLabel.setText (String.format (rb.getString ("MSG_SEARCHING"),
- searchField.getText ()));
-
- final LibraryViewer master = (LibraryViewer) getModule ();
-
-
- SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void> ()
- {
- private String searchTerm = "";
-
-
- /**
- * Perform a task in a separate thread from event-dispatch.
- *
- * In this case, perform a search.
- *
- * @return whether or not the search was successful.
- */
-
- @Override
- public Boolean
- doInBackground ()
- {
- searchTerm = searchField.getText ();
-
- try
- {
- master.doSearch (searchTerm);
- }
- catch (InvalidMessageException e)
- {
- searchFailureMessage = String.format (rb.getString ("ERR_SEARCH_FAILED"),
- searchTerm, e.getMessage ());
- return false;
- }
- catch (EmptySearchException e)
- {
- searchFailureMessage = rb.getString ("ERR_EMPTY_SEARCH");
- return false;
- }
-
- return true;
- }
-
-
- /**
- * Perform post-search cleanup and finalisation.
- */
-
- @Override
- public void
- done ()
- {
- // Figure out whether or not the search succeeded.
-
- boolean hasSucceeded = false;
-
- try
- {
- hasSucceeded = this.get ();
- }
- catch (InterruptedException e)
- {
- e.printStackTrace ();
- }
- catch (ExecutionException e)
- {
- searchFailureMessage = String.format (rb.getString ("ERR_SEARCH_FAILED"),
- searchTerm, e.getMessage ());
- }
-
-
- /* Re-enable widgets and swap panels according to whether
- * or not results were found.
- */
-
- searchField.setEnabled (true);
- searchButton.setEnabled (true);
- searchingPanel.setVisible (false);
-
- if (hasSucceeded == false)
- {
- messageLabel.setText (searchFailureMessage);
- messagePanel.setVisible (true);
- }
- else if (master.getLibraryList ().size () == 0)
- {
- messageLabel.setText (String.format (rb.getString ("ERR_NO_RESULTS"),
- searchTerm));
- messagePanel.setVisible (true);
- }
- else
- {
- // Force table update with new results.
-
- resultsTable.setModel (new LibraryTableModel (master.getLibraryList ()));
-
- messagePanel.setVisible (false);
- resultsPane.setVisible (true);
- }
- }
- };
-
-
- worker.execute ();
- }
+
+ public LibraryViewerPanel(LibraryViewer viewer, FrontendMaster master)
+ throws UICreationFailureException {
+ /*
+ * The UI implementation is contained in library_viewer_gui.xml.
+ *
+ * See this file for more details.
+ */
+ super(viewer, "library_viewer_gui.xml", master);
+
+ // Fill in locale-specific data.
+ rb = ResourceBundle
+ .getBundle("uk.org.ury.library.viewer.LibraryViewer");
+
+ searchFailureMessage = rb.getString("ERR_UNKNOWN");
+
+ searchingLabel.setText(rb.getString("MSG_SEARCHING"));
+ searchForLabel.setText(rb.getString("LBL_SEARCHFOR"));
+ searchButton.setText(rb.getString("BTN_SEARCH"));
+ hint.setText(rb.getString("HINT"));
+
+ // Fine-tune table
+ resultsTable.setAutoCreateRowSorter(true);
+ }
+
+ /**
+ * @return the name of the panel.
+ *
+ * @see uk.org.ury.frontend.FrontendModulePanel#getModuleName()
+ */
+ @Override
+ public String getModuleName() {
+ return rb.getString("MODULE_NAME");
+ }
+
+ /**
+ * Action method for performing a search, bound by the UI XML manifest to
+ * the search field and button.
+ */
+ public void search() {
+ /*
+ * We can't let the user search while another search is going on, but
+ * it's not good to let the search "freeze" the UI.
+ *
+ * Hence the search function disables all sensitive parts of the
+ * interface and launches a search as a background process.
+ *
+ * We also swap the results table or no-results panel out for a panel
+ * that says "Searching...", in the interests of user-friendliness.
+ */
+ searchField.setEnabled(false);
+ searchButton.setEnabled(false);
+ resultsPane.setVisible(false);
+ messagePanel.setVisible(false);
+ searchingPanel.setVisible(true);
+ searchingLabel.setText(String.format(rb.getString("MSG_SEARCHING"),
+ searchField.getText()));
+
+ final LibraryViewer master = (LibraryViewer) getModule();
+
+ SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
+ private String searchTerm = "";
+
+ /**
+ * Perform a task in a separate thread from event-dispatch.
+ *
+ * In this case, perform a search.
+ *
+ * @return whether or not the search was successful.
+ */
+ @Override
+ public Boolean doInBackground() {
+ searchTerm = searchField.getText();
+
+ try {
+ master.doSearch(searchTerm);
+ } catch (InvalidMessageException e) {
+ searchFailureMessage = String.format(
+ rb.getString("ERR_SEARCH_FAILED"), searchTerm,
+ e.getMessage());
+ return false;
+ } catch (EmptySearchException e) {
+ searchFailureMessage = rb.getString("ERR_EMPTY_SEARCH");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Perform post-search cleanup and finalisation.
+ */
+ @Override
+ public void done() {
+ // Figure out whether or not the search succeeded.
+ boolean hasSucceeded = false;
+
+ try {
+ hasSucceeded = this.get();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
+ searchFailureMessage = String.format(
+ rb.getString("ERR_SEARCH_FAILED"), searchTerm,
+ e.getMessage());
+ }
+
+ /*
+ * Re-enable widgets and swap panels according to whether or not
+ * results were found.
+ */
+ searchField.setEnabled(true);
+ searchButton.setEnabled(true);
+ searchingPanel.setVisible(false);
+
+ if (hasSucceeded == false) {
+ messageLabel.setText(searchFailureMessage);
+ messagePanel.setVisible(true);
+ } else if (master.getLibraryList().size() == 0) {
+ messageLabel.setText(String.format(
+ rb.getString("ERR_NO_RESULTS"), searchTerm));
+ messagePanel.setVisible(true);
+ } else {
+ // Force table update with new results.
+ resultsTable.setModel(new LibraryTableModel(master
+ .getLibraryList()));
+
+ messagePanel.setVisible(false);
+ resultsPane.setVisible(true);
+ }
+ }
+ };
+
+ worker.execute();
+ }
}
diff --git a/src/uk/org/ury/protocol/ProtocolUtils.java b/src/uk/org/ury/protocol/ProtocolUtils.java
index d38eeed..b2d3ed9 100644
--- a/src/uk/org/ury/protocol/ProtocolUtils.java
+++ b/src/uk/org/ury/protocol/ProtocolUtils.java
@@ -11,88 +11,75 @@ import org.json.simple.JSONValue;
import uk.org.ury.protocol.exceptions.DecodeFailureException;
import uk.org.ury.protocol.exceptions.InvalidMessageException;
-
/**
- * Utilities for converting between strings encoded in the response
- * protocol and collections of items, as well as validating and
- * unpicking protocol messages.
+ * Utilities for converting between strings encoded in the response protocol and
+ * collections of items, as well as validating and unpicking protocol messages.
+ *
+ * @author Matt Windsor
*
- * @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.");
- }
-
-
- /**
- * Check if a response is flagged as having OK status.
- *
- * @param response The response message, as a key-value map
- * (eg in decoded format).
- *
- * @return true if the response is flagged with OK status,
- * false if not (eg ERROR status).
- *
- * @throws InvalidMessageException if the response is
- * invalid (eg the status is missing).
- */
-
- public static boolean
- responseIsOK (Map<?, ?> response)
- throws InvalidMessageException
- {
- if (response.containsKey (Directive.STATUS.toString ()) == false)
- throw new InvalidMessageException ("No status line in response.");
-
- if ((response.get (Directive.STATUS.toString ()) instanceof String) == false)
- throw new InvalidMessageException ("Status is not a string.");
-
- return (((String) response.get (Directive.STATUS.toString ()))
- .equals (Status.OK.toString ()));
- }
+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.");
+ }
+
+ /**
+ * Check if a response is flagged as having OK status.
+ *
+ * @param response
+ * The response message, as a key-value map (eg in decoded
+ * format).
+ *
+ * @return true if the response is flagged with OK status, false if not (eg
+ * ERROR status).
+ *
+ * @throws InvalidMessageException
+ * if the response is invalid (eg the status is missing).
+ */
+ public static boolean responseIsOK(Map<?, ?> response)
+ throws InvalidMessageException {
+ if (response.containsKey(Directive.STATUS.toString()) == false)
+ throw new InvalidMessageException("No status line in response.");
+
+ if ((response.get(Directive.STATUS.toString()) instanceof String) == false)
+ throw new InvalidMessageException("Status is not a string.");
+
+ return (((String) response.get(Directive.STATUS.toString()))
+ .equals(Status.OK.toString()));
+ }
}
diff --git a/src/uk/org/ury/server/ApiRequestHandler.java b/src/uk/org/ury/server/ApiRequestHandler.java
new file mode 100644
index 0000000..a2290f8
--- /dev/null
+++ b/src/uk/org/ury/server/ApiRequestHandler.java
@@ -0,0 +1,42 @@
+package uk.org.ury.server;
+
+import java.util.Map;
+
+import uk.org.ury.server.exceptions.HandleFailureException;
+
+
+/**
+ * Interface for classes that can handle requests addressed to their
+ * class name from the main server.
+ *
+ * For an example of how to implement a RequestHandler, see
+ * ServerRequestHandler.
+ *
+ * @author Matt Windsor
+ */
+public interface ApiRequestHandler
+{
+ /**
+ * Handle a server GET request (that is, a request for data
+ * output).
+ *
+ * @param parameters A key-value map of parameters supplied with the
+ * server request. Typically, the "function"
+ * parameter will detail the function that the
+ * request handler is expected to perform.
+ *
+ * @param server The server from which the request originated.
+ * This will be able to provide the handler with
+ * pooled resources, for example the database.
+ *
+ * @return A series of key-value pairs to pass back to
+ * the client.
+ *
+ * @throws HandleFailureException if the handler cannot
+ * handle the request.
+ */
+
+ public Map<String, Object>
+ handleGetRequest (Map<String, String> parameters, Server server)
+ throws HandleFailureException;
+}
diff --git a/src/uk/org/ury/server/HttpHandler.java b/src/uk/org/ury/server/HttpHandler.java
new file mode 100644
index 0000000..41b4dc4
--- /dev/null
+++ b/src/uk/org/ury/server/HttpHandler.java
@@ -0,0 +1,343 @@
+/*
+ * HttpHandler.java
+ * ---------------------
+ *
+ * Part of the URY Server Platform
+ *
+ * V0.00 2011/03/20
+ *
+ * (C) 2011 URY Computing
+ *
+ * Based on the HttpCore example code, which is available under the
+ * Apache License, version 2.0; the copyright notice provided with
+ * said code follows.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package uk.org.ury.server;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.json.simple.JSONValue;
+
+import uk.org.ury.protocol.Directive;
+import uk.org.ury.protocol.Status;
+import uk.org.ury.server.exceptions.BadRequestException;
+import uk.org.ury.server.exceptions.HandleFailureException;
+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;
+
+/**
+ * @author Matt Windsor, Apache Software Foundation
+ */
+public class HttpHandler implements HttpRequestHandler {
+ private Server server;
+
+ /**
+ * Construct a new HttpHandler.
+ *
+ * @param server
+ * The server whose HTTP requests are to be handled by this
+ * handler.
+ */
+ public HttpHandler(Server server) {
+ this.server = server;
+ }
+
+ /**
+ * Handle a HTTP request.
+ *
+ * @param request
+ * The HTTP request.
+ *
+ * @param response
+ * The response that the handler will populate during the
+ * handling of the request.
+ *
+ * @param context
+ * The HTTP context.
+ */
+ @Override
+ public void handle(HttpRequest request, HttpResponse response,
+ HttpContext context) throws HttpException, IOException {
+ String method = request.getRequestLine().getMethod()
+ .toUpperCase(Locale.ENGLISH);
+
+ if (method.equals("GET")) {
+ try {
+ handleGet(request, response, context);
+ } catch (HandlerNotFoundException e) {
+ // TODO: log
+ serveError(request, response, HttpStatus.SC_NOT_FOUND,
+ e.getMessage());
+ } catch (BadRequestException e) {
+ // TODO: log
+ serveError(request, response, HttpStatus.SC_BAD_REQUEST,
+ e.getMessage());
+ } catch (HandlingException e) {
+ serveError(request, response,
+ HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ } else {
+ serveError(request, response, HttpStatus.SC_NOT_IMPLEMENTED,
+ "Method not implemented.");
+ }
+ }
+
+ /**
+ * Handle a HTTP GET request.
+ *
+ * @param request
+ * The HTTP request.
+ *
+ * @param response
+ * The response that the handler will populate during the
+ * handling of the request.
+ *
+ * @param context
+ * The HTTP context.
+ *
+ * @throws HandlerNotFoundException
+ * if the client requested a request handler that could not be
+ * found on the class path.
+ *
+ * @throws HandlerSetupFailureException
+ * if the handler was found but could not be set up (eg does not
+ * implement appropriate interface or cannot be instantiated).
+ *
+ * @throws HandleFailureException
+ * if an appropriate handler was contacted, but it failed to
+ * process the request.
+ *
+ * @throws BadRequestException
+ * if the request was malformed or invalid.
+ *
+ * @throws NotAHandlerException
+ * if the class requested to handle the request is not a
+ * handler.
+ */
+ public void handleGet(HttpRequest request, HttpResponse response,
+ HttpContext context) throws HandlerNotFoundException,
+ HandlerSetupFailureException, HandleFailureException,
+ BadRequestException, NotAHandlerException {
+ String path = request.getRequestLine().getUri();
+
+ if (path.equals("/index.html") || path.equals("/")) {
+ // Someone's trying to get the index page!
+ // Humour them.
+
+ response.setStatusLine(request.getProtocolVersion(),
+ HttpStatus.SC_OK, "OK");
+
+ StringEntity entity = null;
+
+ try {
+ entity = new StringEntity(Server.DOCTYPE + Server.INDEX_HTML);
+ } catch (UnsupportedEncodingException e) {
+ throw new HandlerSetupFailureException("(Index page)", e);
+ }
+
+ entity.setContentType("text/html");
+
+ response.setEntity(entity);
+ } else {
+ // Convert this into a URL and fan out the various parts of it.
+
+ URL pathURL = null;
+
+ try {
+ pathURL = new URL("http://localhost" + path);
+ } catch (MalformedURLException e) {
+ throw new BadRequestException(e);
+ }
+
+ String className = "uk.org.ury"
+ + pathURL.getPath().replace('/', '.');
+ System.out.println(className);
+ Class<?> newClass = null;
+
+ try {
+ newClass = Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ throw new HandlerNotFoundException(className, e);
+ }
+
+ if (ApiRequestHandler.class.isAssignableFrom(newClass) == false)
+ throw new NotAHandlerException(className);
+
+ String queryString = pathURL.getQuery();
+ Map<String, String> parameters;
+
+ try {
+ parameters = parseQueryString(queryString);
+ } catch (UnsupportedEncodingException e) {
+ throw new HandlerSetupFailureException(className, e);
+ }
+
+ Map<String, Object> content = null;
+
+ try {
+ ApiRequestHandler srh = ((ApiRequestHandler) newClass
+ .newInstance());
+ content = srh.handleGetRequest(parameters, server);
+ } catch (InstantiationException e) {
+ throw new HandlerSetupFailureException(className, e);
+ } catch (IllegalAccessException e) {
+ throw new HandlerSetupFailureException(className, e);
+ }
+
+ // Everything seems OK, so make the response.
+
+ response.setStatusLine(request.getProtocolVersion(),
+ HttpStatus.SC_OK, "OK");
+
+ content.put(Directive.STATUS.toString(), Status.OK.toString());
+
+ StringEntity entity = null;
+
+ try {
+ entity = new StringEntity(JSONValue.toJSONString(content));
+ } catch (UnsupportedEncodingException e) {
+ throw new HandlerSetupFailureException(className, e);
+ }
+
+ entity.setContentType(HTTP.PLAIN_TEXT_TYPE);
+ response.setEntity(entity);
+ }
+ }
+
+ /**
+ * Serve a HTTP plain-text error as the HTTP response for a request.
+ *
+ * @param request
+ * The request that is being responded to.
+ *
+ * @param response
+ * The response to populate with the error message.
+ *
+ * @param code
+ * HTTP status code to use.
+ *
+ * @param reason
+ * The reason to display to the client.
+ */
+ private void serveError(HttpRequest request, HttpResponse response,
+ int code, String reason) {
+ // Get the reason string to put in the error response.
+ String statusReason = "";
+
+ switch (code) {
+ case HttpStatus.SC_BAD_REQUEST:
+ statusReason = "Bad Request";
+ break;
+ case HttpStatus.SC_NOT_FOUND:
+ statusReason = "Not Found";
+ break;
+ default:
+ case HttpStatus.SC_INTERNAL_SERVER_ERROR:
+ statusReason = "Internal Server Error";
+ break;
+ }
+
+ response.setStatusLine(request.getProtocolVersion(), code, statusReason);
+ StringEntity entity = null;
+
+ try {
+ 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) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (entity != null) {
+ entity.setContentType(HTTP.PLAIN_TEXT_TYPE);
+ response.setEntity(entity);
+ }
+ }
+
+ /**
+ * Parse a query string, populating a key-value map of the URL-unescaped
+ * results.
+ *
+ * @param query
+ * The query string to parse.
+ *
+ * @return A map associating parameter keys and values.
+ *
+ * @throws UnsupportedEncodingException
+ * if the URL decoder fails.
+ */
+ public Map<String, String> parseQueryString(String query)
+ throws UnsupportedEncodingException {
+ Map<String, String> params = new HashMap<String, String>();
+
+ // At least one parameter
+ if (query != null && query.endsWith("&") == false) {
+ String[] qsplit = { query };
+
+ // More than one parameter - split the query.
+ if (query.contains("&"))
+ qsplit = query.split("&");
+
+ for (String param : qsplit) {
+ // Has a value
+ if (param.contains("=") && param.endsWith("=") == false) {
+ String[] paramsplit = param.split("=");
+ params.put(URLDecoder.decode(paramsplit[0], "UTF-8"),
+ URLDecoder.decode(paramsplit[1], "UTF-8"));
+ }
+ // Doesn't have a value
+ else if (param.contains("=") == false) {
+ params.put(URLDecoder.decode(param, "UTF-8"), null);
+ }
+ }
+ }
+
+ return params;
+ }
+}
diff --git a/src/uk/org/ury/server/HttpListenerThread.java b/src/uk/org/ury/server/HttpListenerThread.java
new file mode 100644
index 0000000..9396d0b
--- /dev/null
+++ b/src/uk/org/ury/server/HttpListenerThread.java
@@ -0,0 +1,125 @@
+/*
+ * HttpListenerThread.java
+ * -----------------------
+ *
+ * Part of the URY Server Platform
+ *
+ * V0.00 2011/03/20
+ *
+ * (C) 2011 URY Computing
+ *
+ * Based on the HttpCore example code, which is available under the
+ * Apache License, version 2.0; the copyright notice provided with
+ * said code follows.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package uk.org.ury.server;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.DefaultHttpResponseFactory;
+import org.apache.http.impl.DefaultHttpServerConnection;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.SyncBasicHttpParams;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestHandlerRegistry;
+import org.apache.http.protocol.HttpService;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.ResponseConnControl;
+import org.apache.http.protocol.ResponseContent;
+import org.apache.http.protocol.ResponseDate;
+import org.apache.http.protocol.ResponseServer;
+
+/**
+ * Listener thread for the URY server HTTP interface.
+ *
+ * @author Matt Windsor, Apache Software Foundation
+ */
+public class HttpListenerThread extends Thread {
+ private ServerSocket ssocket;
+ private HttpParams params;
+ private HttpProcessor httpproc;
+ private HttpRequestHandlerRegistry registry;
+ private HttpService service;
+
+ public HttpListenerThread(int port, Server server) throws IOException {
+ ssocket = new ServerSocket(port);
+ params = new SyncBasicHttpParams();
+
+ params
+ .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
+ .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
+ .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
+ .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
+ .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");
+
+ httpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[] {
+ new ResponseDate(),
+ new ResponseServer(),
+ new ResponseContent(),
+ new ResponseConnControl()
+ });
+
+ registry = new HttpRequestHandlerRegistry();
+ registry.register("*", new HttpHandler(server));
+
+ service = new HttpService(httpproc,
+ new DefaultConnectionReuseStrategy(),
+ new DefaultHttpResponseFactory(), registry, params);
+ }
+
+ /**
+ * Thread execution body.
+ */
+ @Override
+ public void run() {
+ while (Thread.interrupted() == false) {
+ Socket csocket = null;
+ DefaultHttpServerConnection conn
+ = new DefaultHttpServerConnection();
+ Thread thread = null;
+
+ try {
+ csocket = ssocket.accept();
+ conn.bind(csocket, params);
+ } catch (IOException e) {
+ e.printStackTrace();
+ break;
+ }
+
+ thread = new HttpWorkerThread(service, conn);
+ thread.setDaemon(true);
+ thread.start();
+ }
+ }
+}
diff --git a/src/uk/org/ury/server/HttpWorkerThread.java b/src/uk/org/ury/server/HttpWorkerThread.java
new file mode 100644
index 0000000..246038c
--- /dev/null
+++ b/src/uk/org/ury/server/HttpWorkerThread.java
@@ -0,0 +1,104 @@
+/*
+ * HttpWorkerThread.java
+ * ---------------------
+ *
+ * Part of the URY Server Platform
+ *
+ * V0.00 2011/03/20
+ *
+ * (C) 2011 URY Computing
+ *
+ * Based on the HttpCore example code, which is available under the
+ * Apache License, version 2.0; the copyright notice provided with
+ * said code follows.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package uk.org.ury.server;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.HttpException;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpService;
+
+/**
+ * A worker thread in the server HTTP interface.
+ *
+ * This thread handles requests from the connected client, passing them to the
+ * request handler(s).
+ *
+ * @author Matt Windsor, Apache Software Foundation
+ */
+public class HttpWorkerThread extends Thread {
+ private final HttpService service;
+ private final HttpServerConnection conn;
+
+ /**
+ * Construct a new HttpWorkerThread.
+ *
+ * @param service
+ * The HTTP service the thread is working for.
+ * @param conn
+ * The connection the thread is listening on.
+ */
+ public HttpWorkerThread(HttpService service, HttpServerConnection conn) {
+ super();
+ this.service = service;
+ this.conn = conn;
+ }
+
+ /**
+ * Thread execution body.
+ */
+ public void run() {
+ HttpContext context = new BasicHttpContext(null);
+
+ try {
+ while (Thread.interrupted() == false && conn.isOpen()) {
+ service.handleRequest(conn, context);
+ }
+ } catch (ConnectionClosedException e) {
+ System.out.println("Client closed connection.");
+ } catch (InterruptedIOException e) {
+ System.out.println("Interrupted IO: " + e.getMessage());
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (HttpException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ conn.shutdown();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+}
diff --git a/src/uk/org/ury/server/Server.java b/src/uk/org/ury/server/Server.java
index e75cb97..2419915 100644
--- a/src/uk/org/ury/server/Server.java
+++ b/src/uk/org/ury/server/Server.java
@@ -1,555 +1,98 @@
-/**
+/*
+ * Server.java
+ * -----------
+ *
+ * Part of the URY Server Platform
+ *
+ * V0.00 2011/03/20
*
+ * (C) 2011 URY Computing
*/
+
package uk.org.ury.server;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-
-import java.net.MalformedURLException;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.URL;
-import java.net.URLDecoder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.HttpVersion;
-import org.apache.http.ParseException;
-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;
import uk.org.ury.database.UserClass;
import uk.org.ury.database.exceptions.ConnectionFailureException;
import uk.org.ury.database.exceptions.MissingCredentialsException;
-import uk.org.ury.protocol.Directive;
-import uk.org.ury.protocol.Status;
-import uk.org.ury.server.exceptions.BadRequestException;
-import uk.org.ury.server.exceptions.HandleFailureException;
-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;
/**
* The unified URY server, accepting requests over HTTP.
*
- * @author Matt Windsor
+ * @author Matt Windsor
*/
-
-public class Server
-{
-
- private ServerSocket serverSocket;
-
- private static final String SERVER_VERSION = "SLUT 0.0";
- private static final String DOCTYPE =
- "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""
- + "\"http://www.w3.org/TR/html4/strict.dtd\">";
- private static final String INDEX_HTML =
- "\n<html>"
- + "\n <head>"
- + "\n <title>" + SERVER_VERSION + "</title>"
- + "\n </head>"
- + "\n <body>"
- + "\n <h1>Welcome to the " + SERVER_VERSION + " server</h1>"
- + "\n <p>This server exposes a class-based API for accessing"
- + "\n the internals of the " + SERVER_VERSION + " system.</p>"
- + "\n <p>See the documentation for details.</p>"
- + "\n </body>"
- + "\n</html>";
-
-
- /**
- * The main method, which serves to create a server.
- *
- * @param args The argument vector.
- */
-
- public static void
- main (String[] args)
- {
- Server srv = new Server ();
- srv.run ();
- }
-
-
- /**
- * Run the server.
- */
-
- private void
- run ()
- {
- try
- {
- serverSocket = new ServerSocket (8000);
- }
- catch (IOException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace ();
- }
-
- Socket clientSocket = null;
-
- while (true)
- {
- System.out.println ("Accepting connections... bring 'em on!");
-
- try
- {
- clientSocket = serverSocket.accept ();
- }
- catch (IOException e)
- {
- System.out.println ("SLUT: Accept failed on port 8000. I'm bailing.");
- System.exit (-1);
- }
-
- try
- {
- doConnection (clientSocket);
- }
- catch (IOException e)
- {
- e.printStackTrace ();
- }
- finally
- {
- try
- {
- clientSocket.close ();
- }
- catch (IOException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
+public class Server {
+ public static final String SERVER_VERSION = "SLUT 0.0";
+ public static final String DOCTYPE = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""
+ + "\"http://www.w3.org/TR/html4/strict.dtd\">";
+ public static final String INDEX_HTML = "\n<html>" + "\n <head>"
+ + "\n <title>" + SERVER_VERSION + "</title>" + "\n </head>"
+ + "\n <body>" + "\n <h1>Welcome to the " + SERVER_VERSION
+ + " server</h1>"
+ + "\n <p>This server exposes a class-based API for accessing"
+ + "\n the internals of the " + SERVER_VERSION + " system.</p>"
+ + "\n <p>See the documentation for details.</p>" + "\n </body>"
+ + "\n</html>";
+
+ /**
+ * The main method, which serves to create a server.
+ *
+ * @param args
+ * The argument vector.
+ */
+ public static void main(String[] args) {
+ Server srv = new Server();
+ srv.run();
}
- }
-
- public void
- doConnection (Socket clientSocket)
- throws IOException
- {
- PrintWriter out = new PrintWriter (clientSocket.getOutputStream(), true);
- BufferedReader in = new BufferedReader (new InputStreamReader (
- clientSocket.getInputStream()));
- String inputLine;
-
- //initiate conversation with client
-
- List<String> buffer = new ArrayList<String> ();
-
-
- for (inputLine = in.readLine (); inputLine != null; inputLine = in.readLine ())
- {
- if (inputLine.equals (""))
- break;
-
- buffer.add (inputLine);
-
- if (inputLine.startsWith ("Expect:")
- && inputLine.split (":")[1].startsWith ("100-continue"))
- out.print ("HTTP/1.1 100 Continue\n\r\n");
-
- out.flush ();
- }
-
- processBuffer (buffer, out);
-
- out.flush ();
- out.close ();
- in.close ();
-
- System.out.println ("Just finished with this one...");
- }
-
-
- public void
- processBuffer (List<String> buffer, PrintWriter out)
- {
- String requestStart = buffer.get (0);
-
- System.out.println (requestStart);
-
- HttpResponse response;
-
- if (requestStart.startsWith ("GET"))
- {
- System.out.println ("That was a GET...");
- try
- {
- response = handleGet (buffer);
- }
- catch (HandlerNotFoundException e)
- {
- // TODO: log
- response = serveError (HttpStatus.SC_NOT_FOUND,
- e.getMessage ());
- }
- catch (BadRequestException e)
- {
- // TODO: log
- response = serveError (HttpStatus.SC_BAD_REQUEST,
- e.getMessage ());
- }
- catch (HandlingException e)
- {
- response = serveError (HttpStatus.SC_INTERNAL_SERVER_ERROR,
- e.getMessage ());
- }
- }
- else
- {
- System.out.println ("Uh-oh! I don't know what to do!");
- response = serveError (HttpStatus.SC_NOT_IMPLEMENTED,
- "Feature not implemented yet.");
- }
-
-
- // Now send the response.
-
- for (Header h : response.getAllHeaders ())
- {
- out.println (h);
- }
-
- try
- {
- out.print (EntityUtils.toString (response.getEntity ()));
- }
- catch (ParseException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace ();
- }
- catch (IOException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace ();
- }
- }
-
-
- /**
- * Handle a HTTP GET request.
- *
- * @param buffer The HTTP request as a list of strings.
- *
- * @return The HTTP response.
- *
- * @throws HandlerNotFoundException if the client requested
- * a request handler that could not be found on the
- * class path.
- *
- * @throws HandlerSetupFailureException if the handler was
- * found but could not be set up (eg does not
- * implement appropriate interface or cannot be
- * instantiated).
- *
- * @throws HandleFailureException if an appropriate handler
- * was contacted, but it failed to process the
- * request.
- *
- * @throws BadRequestException if the request was malformed
- * or invalid.
- *
- * @throws NotAHandlerException if the class requested to
- * handle the request is not a handler.
- */
-
- public HttpResponse
- handleGet (List<String> buffer)
- throws HandlerNotFoundException, HandlerSetupFailureException,
- HandleFailureException, BadRequestException, NotAHandlerException
- {
- HttpResponse response = null;
-
- String[] getsplit = buffer.get (0).split (" ");
- String path = getsplit[1];
-
- if (path.equals ("/index.html")
- || path.equals ("/"))
- {
- // Someone's trying to get the index page!
- // Humour them.
-
- response = new BasicHttpResponse (HttpVersion.HTTP_1_1,
- HttpStatus.SC_OK,
- "OK");
-
- StringEntity entity = null;
-
- try
- {
- entity = new StringEntity (DOCTYPE + INDEX_HTML);
- }
- catch (UnsupportedEncodingException e)
- {
- throw new HandlerSetupFailureException ("(Index page)", e);
- }
-
- response.setEntity (entity);
- }
- else
- {
- // Convert this into a URL and fan out the various parts of it.
-
- URL pathURL = null;
-
- try
- {
- pathURL = new URL ("http://localhost" + path);
- }
- catch (MalformedURLException e)
- {
- throw new BadRequestException (e);
- }
-
- String className = "uk.org.ury" + pathURL.getPath ().replace ('/', '.');
- System.out.println (className);
- Class<?> newClass = null;
-
-
- try
- {
- newClass = Class.forName (className);
- }
- catch (ClassNotFoundException e)
- {
- throw new HandlerNotFoundException (className, e);
- }
-
-
- // Check for error (response set) here.
-
- if (response == null
- && RequestHandler.class.isAssignableFrom (newClass))
- {
- String queryString = pathURL.getQuery ();
- Map<String, String> parameters;
-
- try
- {
- parameters = parseQueryString (queryString);
- }
- catch (UnsupportedEncodingException e)
- {
- throw new HandlerSetupFailureException (className, e);
- }
-
- Map<String, Object> content = null;
-
- try
- {
- RequestHandler srh = ((RequestHandler) newClass.newInstance ());
- content = srh.handleGetRequest (parameters, this);
- }
- catch (InstantiationException e)
- {
- throw new HandlerSetupFailureException (className, e);
- }
- catch (IllegalAccessException e)
- {
- throw new HandlerSetupFailureException (className, e);
- }
-
-
- // Everything seems OK, so make the response.
-
- response = new BasicHttpResponse (HttpVersion.HTTP_1_1,
- HttpStatus.SC_OK,
- "OK");
-
- content.put (Directive.STATUS.toString (),
- Status.OK.toString ());
-
- StringEntity entity = null;
-
- try
- {
- entity = new StringEntity (JSONValue.toJSONString (content));
- }
- catch (UnsupportedEncodingException e)
- {
- throw new HandlerSetupFailureException (className, e);
- }
-
- entity.setContentType (HTTP.PLAIN_TEXT_TYPE);
- response.setEntity (entity);
- }
- else
- throw new NotAHandlerException (className);
- }
-
- return response;
- }
-
-
- /**
- * Serve a HTTP plain-text error as a HTTP response.
- *
- * @param code HTTP status code to use.
- * @param reason The reason to display to the client.
- *
- * @return the HTTP response for the error.
- */
-
- private HttpResponse
- serveError (int code, String reason)
- {
- // Get the reason string to put in the error response.
-
- String statusReason = "";
-
- switch (code)
- {
- case HttpStatus.SC_BAD_REQUEST:
- statusReason = "Bad Request";
- break;
- case HttpStatus.SC_NOT_FOUND:
- statusReason = "Not Found";
- break;
- default:
- case HttpStatus.SC_INTERNAL_SERVER_ERROR:
- statusReason = "Internal Server Error";
- break;
- }
-
- HttpResponse response = new BasicHttpResponse (HttpVersion.HTTP_1_1,
- code, statusReason);
- StringEntity entity = null;
-
- try
- {
- 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)
- {
- // TODO Auto-generated catch block
- e.printStackTrace ();
- }
-
- if (entity != null)
- {
- entity.setContentType (HTTP.PLAIN_TEXT_TYPE);
- response.setEntity (entity);
- }
-
- return response;
- }
-
-
- /**
- * Parse a query string, populating a key-value map of the
- * URL-unescaped results.
- *
- * @param query The query string to parse.
- *
- * @return A map associating parameter keys and values.
- *
- * @throws UnsupportedEncodingException if the URL decoder
- * fails.
- */
-
- public Map<String, String>
- parseQueryString (String query)
- throws UnsupportedEncodingException
- {
- Map<String, String> params = new HashMap<String, String> ();
-
- // At least one parameter
- if (query != null
- && query.endsWith ("&") == false)
- {
- String[] qsplit = {query};
-
- // More than one parameter - split the query.
- if (query.contains ("&"))
- qsplit = query.split ("&");
-
-
- for (String param : qsplit)
- {
- // Has a value
- if (param.contains ("=")
- && param.endsWith ("=") == false)
- {
- String[] paramsplit = param.split ("=");
- params.put (URLDecoder.decode (paramsplit[0], "UTF-8"),
- URLDecoder.decode (paramsplit[1], "UTF-8"));
- }
- // Doesn't have a value
- else if (param.contains ("=") == false)
- {
- params.put (URLDecoder.decode (param, "UTF-8"), null);
- }
- }
- }
-
- return params;
- }
-
-
- /**
- * Get a database connection using the given user class.
- *
- * @param userClass The user class to get a connection for.
- *
- * @return a database connection, which may or may not
- * have been created on this call.
- *
- * @throw MissingCredentialsException if the credentials
- * for the given userclass are missing.
- *
- * @throw ConnectionFailureException if the connection
- * failed.
- */
-
- public DatabaseDriver
- getDatabaseConnection (UserClass userClass)
- throws MissingCredentialsException, ConnectionFailureException
- {
- // TODO: Singleton
-
- ConfigReader config = new ConfigReader ("res/conf.xml");
-
- return new DatabaseDriver (config, UserClass.READ_ONLY);
- }
+ /**
+ * Run the server.
+ */
+ private void run() {
+ Thread thread = null;
+
+ try {
+ thread = new HttpListenerThread(8000, this);
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+
+ thread.setDaemon(false);
+ thread.run();
+ }
+ /**
+ * Get a database connection using the given user class.
+ *
+ * @param userClass
+ * The user class to get a connection for.
+ *
+ * @return a database connection, which may or may not have been created on
+ * this call.
+ *
+ * @throws MissingCredentialsException
+ * if the credentials for the given userclass are missing.
+ *
+ * @throws ConnectionFailureException
+ * if the connection failed.
+ */
+ public DatabaseDriver getDatabaseConnection(UserClass userClass)
+ throws MissingCredentialsException, ConnectionFailureException {
+ // TODO: Singleton
+
+ ConfigReader config = new ConfigReader("res/conf.xml");
+
+ return new DatabaseDriver(config, UserClass.READ_ONLY);
+ }
- /**
- * @return the version string of the server.
- */
-
- public String
- getVersion ()
- {
- return SERVER_VERSION;
- }
+ /**
+ * @return the version string of the server.
+ */
+ public String getVersion() {
+ return SERVER_VERSION;
+ }
}
diff --git a/src/uk/org/ury/server/ServerRequestHandler.java b/src/uk/org/ury/server/ServerRequestHandler.java
index c8d4f39..df67faf 100644
--- a/src/uk/org/ury/server/ServerRequestHandler.java
+++ b/src/uk/org/ury/server/ServerRequestHandler.java
@@ -7,7 +7,7 @@ import java.util.HashMap;
import java.util.Map;
import uk.org.ury.server.Server;
-import uk.org.ury.server.RequestHandler;
+import uk.org.ury.server.ApiRequestHandler;
import uk.org.ury.server.exceptions.HandleFailureException;
@@ -17,7 +17,7 @@ import uk.org.ury.server.exceptions.HandleFailureException;
* @author Matt Windsor
*/
-public class ServerRequestHandler implements RequestHandler
+public class ServerRequestHandler implements ApiRequestHandler
{
/**
* Handle a server GET request (that is, a request for data
diff --git a/src/uk/org/ury/show/viewer/ShowViewer.java b/src/uk/org/ury/show/viewer/ShowViewer.java
index 24e696b..d11e8d5 100644
--- a/src/uk/org/ury/show/viewer/ShowViewer.java
+++ b/src/uk/org/ury/show/viewer/ShowViewer.java
@@ -1,3 +1,14 @@
+/*
+ * ShowViewer.java
+ * ---------------
+ *
+ * Part of the URY Frontend Platform
+ *
+ * V0.00 2011/03/20
+ *
+ * (C) 2011 URY Computing
+ */
+
package uk.org.ury.show.viewer;
import java.util.List;
@@ -11,135 +22,97 @@ import uk.org.ury.frontend.AbstractFrontendModule;
import uk.org.ury.frontend.FrontendMaster;
import uk.org.ury.frontend.FrontendModulePanel;
import uk.org.ury.frontend.exceptions.UICreationFailureException;
-
import uk.org.ury.show.ShowChannel;
import uk.org.ury.show.ShowUtils;
import uk.org.ury.show.item.ShowItem;
-
/**
* Frontend module for viewing show details.
*
- * This serves as the base for the show playout and editor classes,
- * but can be used stand-alone as an (admittedly rather pointless)
- * module.
+ * This serves as the base for the show playout and editor classes, but can be
+ * used stand-alone as an (admittedly rather pointless) module.
+ *
+ * @author Matt Windsor
*
- * @author Matt Windsor
- *
*/
+public class ShowViewer extends AbstractFrontendModule {
+ /**
+ *
+ */
-public class ShowViewer extends AbstractFrontendModule
-{
- /**
- *
- */
-
- private static final long serialVersionUID = -2782366476480563739L;
- private DatabaseDriver dd;
- private ShowChannel[] channels;
- private ShowViewerPanel panel;
- private ConfigReader config;
-
-
- /**
- * Construct a new ShowViewer as a frontend object.
- */
-
- public
- ShowViewer ()
- {
- channels = new ShowChannel[ShowUtils.NUM_CHANNELS];
- }
-
-
- /**
- * Run the library viewer frontend.
- */
-
- @Override
- public FrontendModulePanel
- runFrontend (FrontendMaster master)
- {
- dd = null;
- config = null;
-
- try
- {
- config = new ConfigReader (master.getResourceDirectory () + "conf.xml");
- }
- catch (MissingCredentialsException e)
- {
- System.out.println(e);
+ private static final long serialVersionUID = -2782366476480563739L;
+ private DatabaseDriver dd;
+ private ShowChannel[] channels;
+ private ShowViewerPanel panel;
+ private ConfigReader config;
+
+ /**
+ * Construct a new ShowViewer as a frontend object.
+ */
+ public ShowViewer() {
+ channels = new ShowChannel[ShowUtils.NUM_CHANNELS];
}
-
-
-
- try
- {
- dd = new DatabaseDriver (config, UserClass.READ_ONLY);
- }
- catch (MissingCredentialsException e)
- {
- // TODO: Privilege de-escalation
- master.fatalError (e.getMessage ());
- }
- catch (Exception f)
- {
- master.fatalError (f.getMessage ());
- }
-
- for (int i = 0; i < channels.length; i++)
- {
- channels[i] = new ShowChannel ();
-
- try
- {
- for (ShowItem item : ShowUtils.getChannelList (dd, 4696, i))
- {
- channels[i].add (item);
- }
- }
- catch (QueryFailureException e)
- {
- master.fatalError (e.getMessage ());
- }
- }
-
- try
- {
- panel = new ShowViewerPanel (this, master);
- }
- catch (UICreationFailureException e)
- {
- master.fatalError (e.getMessage ());
- }
-
- return panel;
- }
+ /**
+ * Run the library viewer frontend.
+ */
+ @Override
+ public FrontendModulePanel runFrontend(FrontendMaster master) {
+ dd = null;
+ config = null;
+
+ try {
+ config = new ConfigReader(master.getResourceDirectory()
+ + "conf.xml");
+ } catch (MissingCredentialsException e) {
+ System.out.println(e);
+ }
+
+ try {
+ dd = new DatabaseDriver(config, UserClass.READ_ONLY);
+ } catch (MissingCredentialsException e) {
+ // TODO: Privilege de-escalation
+ master.fatalError(e.getMessage());
+ } catch (Exception f) {
+ master.fatalError(f.getMessage());
+ }
- /**
- * @return the channel array.
- */
-
- public ShowChannel[]
- getChannels ()
- {
- // TODO Auto-generated method stub
- return channels;
- }
+ for (int i = 0; i < channels.length; i++) {
+ channels[i] = new ShowChannel();
-
- /**
- * @return the list of bin names.
- *
- * @throws QueryFailureException if the underlying database query
- * fails.
- */
+ try {
+ for (ShowItem item : ShowUtils.getChannelList(dd, 4696, i)) {
+ channels[i].add(item);
+ }
+ } catch (QueryFailureException e) {
+ master.fatalError(e.getMessage());
+ }
+ }
- public List<String>
- getBins () throws QueryFailureException
- {
- return ShowUtils.getPublicFolders (dd);
- }
+ try {
+ panel = new ShowViewerPanel(this, master);
+ } catch (UICreationFailureException e) {
+ master.fatalError(e.getMessage());
+ }
+
+ return panel;
+ }
+
+ /**
+ * @return the channel array.
+ */
+ public ShowChannel[] getChannels() {
+ // TODO Auto-generated method stub
+ return channels;
+ }
+
+ /**
+ * @return the list of bin names.
+ *
+ * @throws QueryFailureException
+ * if the underlying database query fails.
+ */
+ public List<String> getBins() throws QueryFailureException {
+ return ShowUtils.getPublicFolders(dd);
+ }
}