Permalink
Browse files

Added Javadocs and updated README.

  • Loading branch information...
cimlabs committed Nov 7, 2011
1 parent 73bbc8f commit 8f619fc07a0848315a31ab28cf31dfd4970701bb
View
13 README
@@ -1 +1,12 @@
-Initial README for project skeleton.
+This library provides a foundation for building XHTML hypermedia API
+clients. The major notion of a hypermedia API is that clients start from
+a known entry-point URL and proceed to use the API by following links and
+submitting forms--exactly the way humans do when they use the web. This
+style of API provides a lot of server-side flexibility in terms of
+controlling its own URL space, since clients never construct URLs
+themselves. Instead, clients construct their HTTP requests by following the
+existing, standardized semantics for HTTP <a> and <form> elements, which
+means the mechanisms for those requests are under the server's control.
+
+See also the "Hypermedia APIs" talk given by Jon Moore at Øredev 2010:
+http://oredev.org/2010/sessions/hypermedia-apis
View
21 pom.xml
@@ -54,6 +54,27 @@
<target>1.6</target>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <links>
+ <link>http://download.oracle.com/javase/1.5.0/docs/api/</link>
+ <link>http://jdom.org/docs/apidocs/</link>
+ <link>http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/</link>
+ </links>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ </configuration>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <phase>package</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<dependencies>
@@ -15,6 +15,12 @@
*/
package com.comcast.cim.rest.client.xhtml;
+/**
+ * This exception is raised when the client is asked to follow a
+ * link with a certain link relation or to submit a form with a
+ * given class, but no such link or form can be found in the
+ * current application state.
+ */
public class RelationNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
@@ -35,9 +35,24 @@
import org.jdom.JDOMException;
import org.jdom.xpath.XPath;
-
+/**
+ * Used for constructing HTTP requests from hypermedia controls
+ * (links and forms) presented by the server.
+ */
public class RequestBuilder {
+ /**
+ * Constructs a GET request used to follow a link.
+ * @param a The &lt;a&gt; tag in a parsed response body that
+ * is the link we want to follow.
+ * @param context The URL used to retrieve the current
+ * application state (parsed response body), needed so we
+ * can properly interpret relative URLs.
+ * @return {@link HttpUriRequest}
+ * @throws MalformedURLException if the @href attribute of
+ * the tag cannot be combined with the current URL context
+ * to form a valid URL.
+ */
public HttpUriRequest followLink(Element a, URL context) throws MalformedURLException {
if (!"a".equalsIgnoreCase(a.getName())) {
throw new IllegalArgumentException("can only follow links on <a> tags");
@@ -50,6 +65,29 @@ public HttpUriRequest followLink(Element a, URL context) throws MalformedURLExce
return new HttpGet(derived.toString());
}
+ /**
+ * Constructs a GET or POST request used to submit a form.
+ * The HTTP method used is taken from the form's @method
+ * attribute.
+ * @param form The &lt;form&gt; element from the current
+ * application state which is the form that should be
+ * submitted.
+ * @param context The URL used to retrieve the current
+ * application state (parsed response body). This is
+ * used if the form has a relative URL as its @action.
+ * @param args A map of input names to values. For each
+ * &lt;input&gt; or &lt;select&gt; element in the form,
+ * if its @name is a key in {@code args} then the value
+ * in {@code args} is used as the value in the form
+ * submission. If an input's @name is not found in
+ * {@code args} then it will be submitted with whatever
+ * default value is present.
+ * @return A request that can be issued to submit the
+ * form.
+ * @throws JDOMException
+ * @throws ParseException
+ * @throws IOException
+ */
public HttpUriRequest submitForm(Element form, URL context, Map<String, String> args)
throws JDOMException, ParseException, IOException {
String formMethod = form.getAttributeValue("method");
@@ -17,6 +17,10 @@
import org.apache.http.HttpResponse;
+/**
+ * Exception that wraps a server response whose status code
+ * does not reflect success (e.g. a 500 error).
+ */
public class ServerErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;
@@ -30,6 +34,9 @@ public ServerErrorException(HttpResponse resp) {
super(resp.getStatusLine().toString());
this.resp = resp;
}
-
+
+ /**
+ * @return the server response indicating an error
+ */
public HttpResponse getResponse() { return resp; }
}
@@ -30,6 +30,13 @@
private HttpResponse httpResponse;
private Document document;
+ /**
+ * Constructs a new application state.
+ * @param context the URL used to retrieve the response; this is needed
+ * for understanding relative URLs present in hypermedia controls.
+ * @param resp response sent by the server
+ * @param doc parsed response body
+ */
public XhtmlApplicationState(URL context, HttpResponse resp, Document doc) {
this.context = context;
this.httpResponse = resp;
@@ -61,6 +68,9 @@ public XhtmlApplicationState(URL context, HttpResponse resp, Document doc) {
*/
public HttpResponse getHttpResponse() { return httpResponse; }
+ /**
+ * @return {@code true} iff the response had a 2XX status code
+ */
public boolean succeeded() {
int status = httpResponse.getStatusLine().getStatusCode();
return (status >= 200 && status <= 299);
@@ -22,7 +22,10 @@
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
-
+/**
+ * Convenience wrapper around {@link HttpClient} that asks for and parses
+ * XHTML responses from the server.
+ */
public class XhtmlHttpClient {
private static final String ACCEPT_HEADER = "application/xhtml+xml,*/*;q=0.9";
@@ -34,7 +37,15 @@ public XhtmlHttpClient(HttpClient hc, XhtmlResponseHandlerFactory xrhf) {
this.httpClient = hc;
this.xhtmlResponseHandlerFactory = xrhf;
}
-
+
+ /**
+ * Executes the given HTTP request and returns the next
+ * application state.
+ * @param req HTTP request to execute
+ * @return new application state
+ * @throws ClientProtocolException
+ * @throws IOException
+ */
public XhtmlApplicationState execute(HttpUriRequest req)
throws ClientProtocolException, IOException {
req.setHeader("Accept",ACCEPT_HEADER);
@@ -25,6 +25,9 @@
import org.jdom.Element;
import org.jdom.JDOMException;
+/** Convenience class for moving from one application state to another
+ * by following a link or submitting a form.
+ */
public class XhtmlNavigator {
private XhtmlParser parser;
@@ -37,19 +40,43 @@ public XhtmlNavigator(XhtmlParser xp, RequestBuilder rb, XhtmlHttpClient xhc) {
this.client = xhc;
}
+ /**
+ * Follow an &lt;a&gt; tag with the given link relation.
+ * @param state current application state
+ * @param rel link relation that must appear in the @rel
+ * attribute of a link
+ * @return next application state
+ * @throws JDOMException
+ * @throws ClientProtocolException
+ * @throws IOException
+ */
public XhtmlApplicationState followLink(XhtmlApplicationState state, String rel)
throws JDOMException, ClientProtocolException, IOException {
Element a = parser.getLinkWithRelation(state.getDocument(), rel);
return traverseAnchor(state, rel, a);
}
+ /**
+ * Follow an &lt;a&gt; tag with the given link relation, starting
+ * the search from a given context in a parsed document.
+ * @param state current application state
+ * @param root element within the application state at which the
+ * search for the link should begin
+ * @param rel link relation that must appear in the @rel
+ * attribute of a link
+ * @return next application state
+ * @throws JDOMException
+ * @throws ClientProtocolException
+ * @throws IOException
+ */
public XhtmlApplicationState followLink(XhtmlApplicationState state, Element root,
String rel) throws JDOMException, ClientProtocolException,
IOException {
Element a = parser.getLinkWithRelation(root, rel);
return traverseAnchor(state, rel, a);
}
+
private XhtmlApplicationState traverseAnchor(XhtmlApplicationState state,
String rel, Element a)
throws MalformedURLException, ClientProtocolException, IOException {
@@ -64,6 +91,17 @@ private XhtmlApplicationState traverseAnchor(XhtmlApplicationState state,
return result;
}
+ /**
+ * Submits a form with the given @name, using the provided arguments.
+ * @param state current application state
+ * @param formName name of the form to submit
+ * @param args a map of input names to values to provide for those
+ * inputs when submitting the form
+ * @return next application state
+ * @throws JDOMException
+ * @throws ParseException
+ * @throws IOException
+ */
public XhtmlApplicationState submitForm(XhtmlApplicationState state, String formName,
Map<String, String> args)
throws JDOMException, ParseException, IOException {
@@ -21,32 +21,83 @@
import org.jdom.Namespace;
import org.jdom.xpath.XPath;
+/**
+ * Used for locating specific links and forms within a parsed
+ * XHTML response body.
+ */
public class XhtmlParser {
public static final String XHTML_NS_URI = "http://www.w3.org/1999/xhtml";
public static final Namespace XHTML_NS = Namespace.getNamespace(XHTML_NS_URI);
-
+
+ /**
+ * Generate an XPath search against an XHTML document (using the proper
+ * XHTML namespace).
+ * @param xhtmlPrefix This is the prefix to use when describing elements
+ * in the XHTML XML namespace. For example, provide &quot;xhtml&quot;
+ * here if you are going to refer to &quot;xhtml:a&quot; in your
+ * XPath expression.
+ * @param xpathExpression the XPath search term to use
+ * @return executable XPath search
+ * @throws JDOMException
+ */
public static XPath getXPath(String xhtmlPrefix, String xpathExpression)
throws JDOMException {
XPath xpath = XPath.newInstance(xpathExpression);
xpath.addNamespace(Namespace.getNamespace(xhtmlPrefix, XhtmlParser.XHTML_NS_URI));
return xpath;
}
+ /**
+ * Find a descendant of the given element that is an &lt;a&gt;
+ * tag with the given link relation in its @rel attribute.
+ * @param elt root element of the search
+ * @param rel link relation to find
+ * @return desired &lt;a&gt; element, or {@code null} if no
+ * such element exists
+ * @throws JDOMException
+ */
public Element getLinkWithRelation(Element elt, String rel) throws JDOMException {
XPath xpath = getXPath("xhtml", String.format(".//xhtml:a[@rel='%s']", rel));
return (Element)xpath.selectSingleNode(elt);
}
-
+
+ /**
+ * Find in the given XML document an &lt;a&gt;
+ * tag with the given link relation in its @rel attribute.
+ * @param doc XML document to search
+ * @param rel link relation to find
+ * @return desired &lt;a&gt; element, or {@code null} if no
+ * such element exists
+ * @throws JDOMException
+ */
public Element getLinkWithRelation(Document doc, String rel) throws JDOMException {
return getLinkWithRelation(doc.getRootElement(), rel);
}
+ /**
+ * Find a descendant of the given element that is a &lt;form&gt;
+ * tag with the given @name attribute.
+ * @param elt root element of the search
+ * @param formName @name attribute value to look for
+ * @return desired &lt;form&gt; element, or {@code null} if no
+ * such element exists
+ * @throws JDOMException
+ */
public Element getFormWithName(Element elt, String formName) throws JDOMException {
XPath xpath = getXPath("xhtml", String.format(".//xhtml:form[@name='%s']", formName));
return (Element)xpath.selectSingleNode(elt);
}
-
+
+ /**
+ * Find in the given XML document a &lt;form&gt;
+ * tag with the given @name attribute.
+ * @param doc XML document to search
+ * @param formName @name attribute value to look for
+ * @return desired &lt;form&gt; element, or {@code null} if no
+ * such element exists
+ * @throws JDOMException
+ */
public Element getFormWithName(Document doc, String formName) throws JDOMException {
return getFormWithName(doc.getRootElement(), formName);
}

0 comments on commit 8f619fc

Please sign in to comment.