"Web queries" are Pivot's native means of communicating with remote data services. They are designed primarily to facilitate interaction with JSON-based REST services, but they are sufficiently generic to support communication with any type of HTTP-based service, using any data format.
A web query is represented by an instance of org.apache.pivot.web.Query, which extends the Task class discussed in the previous section. Query is an abstract class with four concrete subclasses, each of which corresponds to once of the four primary HTTP verbs:
GetQuery - executes an HTTP GET request
PostQuery - executes an HTTP POST request
PutQuery - executes an HTTP PUT request
DeleteQuery - executes an HTTP DELETE request
Query string parameters are specified via the query's parameter dictionary, and request headers can be specified via the request header dictionary. After the query has been executed, response headers are also available via the response header dictionary.
All query types have an associated serializer that is used to serialize or deserialize the request or response data. GetQuery returns an Object representing the data that was returned by the server, PostQuery and PutQuery define a value property that is sent to the server when the query is executed, and, by definition, DeleteQuery neither accepts nor returns a value.
Because they are instances of Task, web queries can be (and usually are) executed asynchronously, such that the UI doesn't get bogged down waiting for the server to respond. The following application demonstrates the use of a GetQuery to read data from a Yahoo! Pipe. The pipe in this example searches for "pizza" within 5 miles of Cambridge, Massachusetts (note that the applet is signed to allow it to communicate with the Yahoo web service):
The BXML for the application's user interface is as follows:
<Window title="Web Queries" maximized="true" xmlns:bxml="http://pivot.apache.org/bxml" xmlns:webqueries="org.apache.pivot.tutorials.webqueries" xmlns="org.apache.pivot.wtk"> <Border styles="{color:10}"> <StackPane> <ScrollPane horizontalScrollBarPolicy="fill"> <ListView bxml:id="listView"> <itemRenderer> <webqueries:ResultItemRenderer/> </itemRenderer> </ListView> </ScrollPane> <Label bxml:id="loadingLabel" text="Loading..." styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/> </StackPane> </Border> </Window>
Note that the ListView defines a custom item renderer; this renderer is used to present the name, address, and phone number of the matching items returned by the query.
The Java source for the application is as follows:
package org.apache.pivot.tutorials.webqueries; import org.apache.pivot.beans.BXMLSerializer; import org.apache.pivot.collections.List; import org.apache.pivot.collections.Map; import org.apache.pivot.json.JSON; import org.apache.pivot.util.concurrent.Task; import org.apache.pivot.util.concurrent.TaskListener; import org.apache.pivot.web.GetQuery; import org.apache.pivot.wtk.Application; import org.apache.pivot.wtk.DesktopApplicationContext; import org.apache.pivot.wtk.Display; import org.apache.pivot.wtk.Label; import org.apache.pivot.wtk.ListView; import org.apache.pivot.wtk.TaskAdapter; import org.apache.pivot.wtk.Window; public class WebQueries implements Application { private Window window = null; private ListView listView = null; private Label loadingLabel = null; @Override public void startup(Display display, Map<String, String> properties) throws Exception { BXMLSerializer bxmlSerializer = new BXMLSerializer(); window = (Window)bxmlSerializer.readObject(WebQueries.class, "web_queries.bxml"); listView = (ListView)bxmlSerializer.getNamespace().get("listView"); loadingLabel = (Label)bxmlSerializer.getNamespace().get("loadingLabel"); // Execute the query: // http://pipes.yahoo.com/pipes/pipe.run?_id=43115761f2da5af5341ae2e56a93d646&_render=json GetQuery getQuery = new GetQuery("pipes.yahoo.com", "/pipes/pipe.run"); getQuery.getParameters().put("_id", "43115761f2da5af5341ae2e56a93d646"); getQuery.getParameters().put("_render", "json"); getQuery.execute(new TaskAdapter<Object>(new TaskListener<Object>() { @Override public void taskExecuted(Task<Object> task) { List<?> items = (List<?>)JSON.get(task.getResult(), "value.items"); if (items.getLength() > 0) { listView.setListData(items); loadingLabel.setVisible(false); } else { loadingLabel.setText("No results."); } } @Override public void executeFailed(Task<Object> task) { loadingLabel.setText(task.getFault().getMessage()); } })); window.open(display); } @Override public boolean shutdown(boolean optional) { if (window != null) { window.close(); } return false; } @Override public void suspend() { } @Override public void resume() { } public static void main(String[] args) { DesktopApplicationContext.main(WebQueries.class, args); } }
In startup(), the application creates the query object and sets the appropriate query string parameters to identify the pipe and ensure that the pipe's output is returned as JSON data. It then executes the query, wrapping the task listener in a task adapter to ensure that the callback is executed on the UI thread. When the data is obtained, the "items" list is extracted from it and set as the data of the list view component.
The application uses a custom list item renderer to present the result data. The renderer is defined as follows (it extends BoxPane and uses labels to present the name, address, and phone numbers of the result items):
package org.apache.pivot.tutorials.webqueries; import java.awt.Color; import java.awt.Font; import org.apache.pivot.collections.Map; import org.apache.pivot.json.JSON; import org.apache.pivot.wtk.BoxPane; import org.apache.pivot.wtk.Insets; import org.apache.pivot.wtk.Label; import org.apache.pivot.wtk.ListView; import org.apache.pivot.wtk.Orientation; import org.apache.pivot.wtk.Style; public class ResultItemRenderer extends BoxPane implements ListView.ItemRenderer { private Label titleLabel = new Label(); private Label addressLabel = new Label(); private Label phoneLabel = new Label(); public ResultItemRenderer() { super(Orientation.VERTICAL); add(titleLabel); add(addressLabel); add(phoneLabel); getStyles().put(Style.padding, new Insets(3, 2, 3, 2)); getStyles().put(Style.spacing, 2); } @Override public void setSize(int width, int height) { super.setSize(width, height); validate(); } @Override public String toString(Object item) { return JSON.get(item, "title"); } @Override public void render(Object item, int index, ListView listView, boolean selected, boolean checked, boolean highlighted, boolean disabled) { if (item != null) { titleLabel.setText((String)JSON.get(item, "title")); phoneLabel.setText((String)JSON.get(item, "Phone")); Map<String, ?> location = JSON.get(item, "['y:location']"); if (location == null) { addressLabel.setText(""); } else { String street = JSON.get(location, "street"); String city = JSON.get(location, "city"); String state = JSON.get(location, "state"); addressLabel.setText(street + ", " + city + " " + state); } } Font font = listView.getStyles().getFont(Style.font); titleLabel.getStyles().put(Style.font, font.deriveFont(font.getStyle() | Font.BOLD)); phoneLabel.getStyles().put(Style.font, font); addressLabel.getStyles().put(Style.font, font); Color color; if (listView.isEnabled() && !disabled) { if (selected) { if (listView.isFocused()) { color = listView.getStyles().getColor(Style.selectionColor); } else { color = listView.getStyles().getColor(Style.inactiveSelectionColor); } } else { color = listView.getStyles().getColor(Style.color); } } else { color = listView.getStyles().getColor(Style.disabledColor); } titleLabel.getStyles().put(Style.color, color); phoneLabel.getStyles().put(Style.color, color); addressLabel.getStyles().put(Style.color, color); } }
Next: QueryServlet