You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/03/16 17:16:58 UTC

svn commit: r754926 [10/38] - in /incubator/pivot/tags/v1.0: ./ charts-test/ charts-test/src/ charts-test/src/pivot/ charts-test/src/pivot/charts/ charts-test/src/pivot/charts/test/ charts/ charts/lib/ charts/src/ charts/src/pivot/ charts/src/pivot/cha...

Added: incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/trees.wtkx
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/trees.wtkx?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/trees.wtkx (added)
+++ incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/trees.wtkx Mon Mar 16 16:16:40 2009
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (c) 2008 VMware, Inc.
+
+Licensed 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.
+-->
+
+<Border xmlns:wtkx="http://pivot.dev.java.net/wtkx/2008"
+    xmlns:content="pivot.wtk.content" xmlns="pivot.wtk">
+    <content>
+        <FlowPane styles="{padding:4, spacing:10}">
+            <FlowPane orientation="vertical">
+                <Label text="Simple" styles="{fontBold:true}"/>
+                <Border styles="{padding:0, color:10}">
+                    <content>
+                        <ScrollPane preferredWidth="160" preferredHeight="240"
+                            horizontalScrollBarPolicy="fillToCapacity"
+                            styles="{backgroundColor:null}">
+                            <view>
+                                <TreeView>
+                                    <treeData>
+                                        <content:TreeBranch>
+                                            <content:TreeBranch text="Numbers">
+                                                <content:TreeNode text="0"/>
+                                                <content:TreeNode text="1"/>
+                                                <content:TreeNode text="2"/>
+                                                <content:TreeNode text="3"/>
+                                                <content:TreeNode text="4"/>
+                                                <content:TreeNode text="5"/>
+                                                <content:TreeNode text="6"/>
+                                                <content:TreeNode text="7"/>
+                                                <content:TreeNode text="8"/>
+                                                <content:TreeNode text="9"/>
+                                            </content:TreeBranch>
+                                            <content:TreeBranch text="Letters">
+                                                <content:TreeNode text="A"/>
+                                                <content:TreeNode text="B"/>
+                                                <content:TreeNode text="C"/>
+                                                <content:TreeNode text="D"/>
+                                                <content:TreeNode text="E"/>
+                                                <content:TreeNode text="F"/>
+                                                <content:TreeNode text="G"/>
+                                                <content:TreeNode text="H"/>
+                                                <content:TreeNode text="I"/>
+                                                <content:TreeNode text="J"/>
+                                                <content:TreeNode text="K"/>
+                                                <content:TreeNode text="L"/>
+                                                <content:TreeNode text="M"/>
+                                                <content:TreeNode text="N"/>
+                                                <content:TreeNode text="O"/>
+                                                <content:TreeNode text="P"/>
+                                                <content:TreeNode text="Q"/>
+                                                <content:TreeNode text="R"/>
+                                                <content:TreeNode text="S"/>
+                                                <content:TreeNode text="T"/>
+                                                <content:TreeNode text="U"/>
+                                                <content:TreeNode text="V"/>
+                                                <content:TreeNode text="W"/>
+                                                <content:TreeNode text="X"/>
+                                                <content:TreeNode text="Y"/>
+                                                <content:TreeNode text="Z"/>
+                                            </content:TreeBranch>
+                                        </content:TreeBranch>
+                                    </treeData>
+                                    <nodeRenderer>
+                                        <content:TreeViewNodeRenderer showIcon="false"/>
+                                    </nodeRenderer>
+                                </TreeView>
+                            </view>
+                        </ScrollPane>
+                    </content>
+                </Border>
+            </FlowPane>
+
+            <FlowPane orientation="vertical">
+                <Label text="Editable" styles="{fontBold:true}"/>
+                <Border styles="{padding:0, color:10}">
+                    <content>
+                        <ScrollPane wtkx:id="editableTreeViewScrollPane" preferredWidth="160" preferredHeight="240"
+                            horizontalScrollBarPolicy="fillToCapacity"
+                            styles="{backgroundColor:null}">
+                            <view>
+                                <TreeView wtkx:id="editableTreeView">
+                                    <treeData>
+                                        <content:TreeBranch>
+                                            <content:TreeBranch text="Activity" icon="@folder.png">
+                                                <content:TreeBranch text="Games" icon="@folder.png">
+                                                    <content:TreeNode text="A very long game name that just serves to show how a wide tree node behaves" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Air Hockey" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Foosball" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Ping Pong" icon="@page_white.png"/>
+                                                </content:TreeBranch>
+                                                <content:TreeBranch text="Sports" icon="@folder.png">
+                                                    <content:TreeNode text="Baseball" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Basketball" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Football" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Ice Hockey" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Soccer" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Softball" icon="@page_white.png"/>
+                                                </content:TreeBranch>
+                                                <content:TreeNode text="Camping" icon="@page_white.png"/>
+                                                <content:TreeNode text="Skiing" icon="@page_white.png"/>
+                                            </content:TreeBranch>
+                                            <content:TreeBranch text="Occasion" icon="@folder.png">
+                                                <content:TreeBranch text="Holidays" icon="@folder.png">
+                                                    <content:TreeNode text="Christmas" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Independence Day" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Labor Day" icon="@page_white.png"/>
+                                                    <content:TreeNode text="New Year's Day" icon="@page_white.png"/>
+                                                    <content:TreeNode text="President's Day" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Thanksgiving" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Valentine's Day" icon="@page_white.png"/>
+                                                    <content:TreeNode text="Veteran's Day" icon="@page_white.png"/>
+                                                </content:TreeBranch>
+                                                <content:TreeNode text="Anniversary" icon="@page_white.png"/>
+                                                <content:TreeNode text="Birthday" icon="@page_white.png"/>
+                                                <content:TreeNode text="Wedding" icon="@page_white.png"/>
+                                            </content:TreeBranch>
+                                            <content:TreeBranch text="Location" icon="@folder.png">
+                                                <content:TreeNode text="Africa" icon="@folder.png"/>
+                                                <content:TreeNode text="Antarctica" icon="@folder.png"/>
+                                                <content:TreeNode text="Asia" icon="@folder.png"/>
+                                                <content:TreeNode text="Australia" icon="@folder.png"/>
+                                                <content:TreeNode text="Europe" icon="@folder.png"/>
+                                                <content:TreeNode text="North America" icon="@folder.png"/>
+                                                <content:TreeBranch text="South America" icon="@folder.png">
+                                                    <content:TreeNode text="Peru" icon="@page_white.png"/>
+                                                </content:TreeBranch>
+                                            </content:TreeBranch>
+                                        </content:TreeBranch>
+                                    </treeData>
+                                    <nodeRenderer>
+                                        <content:TreeViewNodeRenderer showIcon="true"/>
+                                    </nodeRenderer>
+                                </TreeView>
+                            </view>
+                        </ScrollPane>
+                    </content>
+                </Border>
+            </FlowPane>
+
+            <FlowPane orientation="vertical">
+                <Label text="Multi-Select" styles="{fontBold:true}"/>
+                <Border styles="{padding:0, color:10}">
+                    <content>
+                        <ScrollPane preferredWidth="160" preferredHeight="240"
+                            horizontalScrollBarPolicy="fillToCapacity"
+                            styles="{backgroundColor:null}">
+                            <view>
+                                <TreeView selectMode="multi">
+                                    <treeData>
+                                        <content:TreeBranch>
+                                            <content:TreeBranch text="Africa" icon="@folder.png"/>
+                                            <content:TreeBranch text="Antarctica" icon="@folder.png"/>
+                                            <content:TreeBranch text="Asia" icon="@folder.png"/>
+                                            <content:TreeBranch text="Australia" icon="@folder.png"/>
+                                            <content:TreeBranch text="Europe" icon="@folder.png">
+                                                <content:TreeNode text="Albania" icon="@page_white.png"/>
+                                                <content:TreeNode text="Andorra" icon="@page_white.png"/>
+                                                <content:TreeNode text="Armenia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Austria" icon="@page_white.png"/>
+                                                <content:TreeNode text="Azerbaijan" icon="@page_white.png"/>
+                                                <content:TreeNode text="Belarus" icon="@page_white.png"/>
+                                                <content:TreeNode text="Belgium" icon="@page_white.png"/>
+                                                <content:TreeNode text="Bosnia and Herzegovina" icon="@page_white.png"/>
+                                                <content:TreeNode text="Bulgaria" icon="@page_white.png"/>
+                                                <content:TreeNode text="Croatia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Cyprus" icon="@page_white.png"/>
+                                                <content:TreeNode text="Czech Republic" icon="@page_white.png"/>
+                                                <content:TreeNode text="Denmark" icon="@page_white.png"/>
+                                                <content:TreeNode text="Estonia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Finland" icon="@page_white.png"/>
+                                                <content:TreeNode text="Former Yugoslav Republic of Macedonia" icon="@page_white.png"/>
+                                                <content:TreeNode text="France" icon="@page_white.png"/>
+                                                <content:TreeNode text="Georgia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Germany" icon="@page_white.png"/>
+                                                <content:TreeNode text="Greece" icon="@page_white.png"/>
+                                                <content:TreeNode text="Hungary" icon="@page_white.png"/>
+                                                <content:TreeNode text="Iceland" icon="@page_white.png"/>
+                                                <content:TreeNode text="Ireland" icon="@page_white.png"/>
+                                                <content:TreeNode text="Italy" icon="@page_white.png"/>
+                                                <content:TreeNode text="Latvia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Liechtenstein" icon="@page_white.png"/>
+                                                <content:TreeNode text="Lithuania" icon="@page_white.png"/>
+                                                <content:TreeNode text="Luxembourg" icon="@page_white.png"/>
+                                                <content:TreeNode text="Malta" icon="@page_white.png"/>
+                                                <content:TreeNode text="Moldova" icon="@page_white.png"/>
+                                                <content:TreeNode text="Monaco" icon="@page_white.png"/>
+                                                <content:TreeNode text="Montenegro" icon="@page_white.png"/>
+                                                <content:TreeNode text="Netherlands" icon="@page_white.png"/>
+                                                <content:TreeNode text="Norway" icon="@page_white.png"/>
+                                                <content:TreeNode text="Poland" icon="@page_white.png"/>
+                                                <content:TreeNode text="Portugal" icon="@page_white.png"/>
+                                                <content:TreeNode text="Romania" icon="@page_white.png"/>
+                                                <content:TreeNode text="Russia" icon="@page_white.png"/>
+                                                <content:TreeNode text="San Marino" icon="@page_white.png"/>
+                                                <content:TreeNode text="Serbia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Slovakia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Slovenia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Spain" icon="@page_white.png"/>
+                                                <content:TreeNode text="Sweden" icon="@page_white.png"/>
+                                                <content:TreeNode text="Switzerland" icon="@page_white.png"/>
+                                                <content:TreeNode text="Turkey" icon="@page_white.png"/>
+                                                <content:TreeNode text="Ukraine" icon="@page_white.png"/>
+                                                <content:TreeNode text="United Kingdom" icon="@page_white.png"/>
+                                                <content:TreeNode text="Vatican City State" icon="@page_white.png"/>
+                                            </content:TreeBranch>
+                                            <content:TreeBranch text="North America" icon="@folder.png">
+                                                <content:TreeNode text="Alabama" icon="@page_white.png"/>
+                                                <content:TreeNode text="Alaska" icon="@page_white.png"/>
+                                                <content:TreeNode text="Arizona" icon="@page_white.png"/>
+                                                <content:TreeNode text="Arkansas" icon="@page_white.png"/>
+                                                <content:TreeNode text="California" icon="@page_white.png"/>
+                                                <content:TreeNode text="Colorado" icon="@page_white.png"/>
+                                                <content:TreeNode text="Connecticut" icon="@page_white.png"/>
+                                                <content:TreeNode text="Delaware" icon="@page_white.png"/>
+                                                <content:TreeNode text="District of Columbia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Florida" icon="@page_white.png"/>
+                                                <content:TreeNode text="Georgia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Hawaii" icon="@page_white.png"/>
+                                                <content:TreeNode text="Idaho" icon="@page_white.png"/>
+                                                <content:TreeNode text="Illinois" icon="@page_white.png"/>
+                                                <content:TreeNode text="Indiana" icon="@page_white.png"/>
+                                                <content:TreeNode text="Iowa" icon="@page_white.png"/>
+                                                <content:TreeNode text="Kansas" icon="@page_white.png"/>
+                                                <content:TreeNode text="Kentucky" icon="@page_white.png"/>
+                                                <content:TreeNode text="Louisiana" icon="@page_white.png"/>
+                                                <content:TreeNode text="Maine" icon="@page_white.png"/>
+                                                <content:TreeNode text="Maryland" icon="@page_white.png"/>
+                                                <content:TreeNode text="Massachusetts" icon="@page_white.png"/>
+                                                <content:TreeNode text="Michigan" icon="@page_white.png"/>
+                                                <content:TreeNode text="Minnesota" icon="@page_white.png"/>
+                                                <content:TreeNode text="Mississippi" icon="@page_white.png"/>
+                                                <content:TreeNode text="Missouri" icon="@page_white.png"/>
+                                                <content:TreeNode text="Montana" icon="@page_white.png"/>
+                                                <content:TreeNode text="Nebraska" icon="@page_white.png"/>
+                                                <content:TreeNode text="Nevada" icon="@page_white.png"/>
+                                                <content:TreeNode text="New Hampshire" icon="@page_white.png"/>
+                                                <content:TreeNode text="New Jersey" icon="@page_white.png"/>
+                                                <content:TreeNode text="New Mexico" icon="@page_white.png"/>
+                                                <content:TreeNode text="New York" icon="@page_white.png"/>
+                                                <content:TreeNode text="North Carolina" icon="@page_white.png"/>
+                                                <content:TreeNode text="North Dakota" icon="@page_white.png"/>
+                                                <content:TreeNode text="Ohio" icon="@page_white.png"/>
+                                                <content:TreeNode text="Oklahoma" icon="@page_white.png"/>
+                                                <content:TreeNode text="Oregon" icon="@page_white.png"/>
+                                                <content:TreeNode text="Pennsylvania" icon="@page_white.png"/>
+                                                <content:TreeNode text="Rhode Island" icon="@page_white.png"/>
+                                                <content:TreeNode text="South Carolina" icon="@page_white.png"/>
+                                                <content:TreeNode text="South Dakota" icon="@page_white.png"/>
+                                                <content:TreeNode text="Tennessee" icon="@page_white.png"/>
+                                                <content:TreeNode text="Texas" icon="@page_white.png"/>
+                                                <content:TreeNode text="Utah" icon="@page_white.png"/>
+                                                <content:TreeNode text="Vermont" icon="@page_white.png"/>
+                                                <content:TreeNode text="Virginia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Washington" icon="@page_white.png"/>
+                                                <content:TreeNode text="West Virginia" icon="@page_white.png"/>
+                                                <content:TreeNode text="Wisconsin" icon="@page_white.png"/>
+                                                <content:TreeNode text="Wyoming" icon="@page_white.png"/>
+                                            </content:TreeBranch>
+                                            <content:TreeBranch text="South America" icon="@folder.png"/>
+                                        </content:TreeBranch>
+                                    </treeData>
+                                </TreeView>
+                            </view>
+                        </ScrollPane>
+                    </content>
+                </Border>
+            </FlowPane>
+        </FlowPane>
+    </content>
+</Border>

Added: incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/weather-few-clouds.png
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/weather-few-clouds.png?rev=754926&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/weather-few-clouds.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/x-office-document.png
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/x-office-document.png?rev=754926&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0/tutorials/src/pivot/tutorials/x-office-document.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0/web-test/.classpath
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web-test/.classpath?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web-test/.classpath (added)
+++ incubator/pivot/tags/v1.0/web-test/.classpath Mon Mar 16 16:16:40 2009
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/core"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/web"/>
+	<classpathentry kind="lib" path="/web/lib/servlet-api.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>

Added: incubator/pivot/tags/v1.0/web-test/.project
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web-test/.project?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web-test/.project (added)
+++ incubator/pivot/tags/v1.0/web-test/.project Mon Mar 16 16:16:40 2009
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>web-test</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

Added: incubator/pivot/tags/v1.0/web-test/src/pivot/web/test/WebQueryTestClient.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web-test/src/pivot/web/test/WebQueryTestClient.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web-test/src/pivot/web/test/WebQueryTestClient.java (added)
+++ incubator/pivot/tags/v1.0/web-test/src/pivot/web/test/WebQueryTestClient.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web.test;
+
+import java.net.URL;
+
+import pivot.collections.Dictionary;
+import pivot.serialization.BinarySerializer;
+import pivot.serialization.JSONSerializer;
+import pivot.util.concurrent.Task;
+import pivot.util.concurrent.TaskListener;
+import pivot.web.BasicAuthentication;
+import pivot.web.GetQuery;
+import pivot.web.DeleteQuery;
+import pivot.web.PostQuery;
+import pivot.web.PutQuery;
+
+public class WebQueryTestClient {
+    public static void main(String[] args) {
+        final boolean useProxy = true;
+
+        final String HOSTNAME = "localhost";
+        final String PATH = (useProxy ? "/pivot_web_test/proxy" : "/pivot_web_test/webquery") + "/bar/quux";
+        final int PORT = 8080;
+        final boolean SECURE = false;
+
+        BasicAuthentication authentication = new BasicAuthentication("foo", "bar");
+
+        // GET
+        GetQuery getQuery = new GetQuery(HOSTNAME, PORT, PATH, SECURE);
+        getQuery.getArguments().put("a", "b");
+        getQuery.setSerializer(new BinarySerializer());
+        authentication.authenticate(getQuery);
+
+        getQuery.execute(new TaskListener<Object>() {
+            @SuppressWarnings("unchecked")
+            public void taskExecuted(Task<Object> task) {
+                Dictionary<String, Object> result = (Dictionary<String, Object>)task.getResult();
+
+                System.out.println("GET result: "
+                    + "username: " + result.get("username") + ", "
+                    + "pathInfo: " + result.get("pathInfo") + ", "
+                    + "queryString: " + result.get("queryString"));
+            }
+
+            public void executeFailed(Task<Object> task) {
+                System.out.println("GET fault: " + task.getFault());
+            }
+        });
+
+        // POST
+        PostQuery postQuery = new PostQuery(HOSTNAME, PORT, PATH, SECURE);
+        authentication.authenticate(postQuery);
+        postQuery.setValue(JSONSerializer.parseList("[1, 2, 3]"));
+
+        postQuery.execute(new TaskListener<URL>() {
+            public void taskExecuted(Task<URL> task) {
+                System.out.println("POST result: " + task.getResult());
+            }
+
+            public void executeFailed(Task<URL> task) {
+                System.out.println("POST fault: " + task.getFault());
+            }
+        });
+
+        // PUT
+        PutQuery putQuery = new PutQuery(HOSTNAME, PORT, PATH, SECURE);
+        authentication.authenticate(putQuery);
+        putQuery.setValue(JSONSerializer.parseMap("{a:100, b:200, c:300}"));
+
+        putQuery.execute(new TaskListener<Void>() {
+            public void taskExecuted(Task<Void> task) {
+                System.out.println("PUT result");
+            }
+
+            public void executeFailed(Task<Void> task) {
+                System.out.println("PUT fault: " + task.getFault());
+            }
+        });
+
+        // POST
+        DeleteQuery deleteQuery = new DeleteQuery(HOSTNAME, PORT, PATH, SECURE);
+        authentication.authenticate(deleteQuery);
+
+        deleteQuery.execute(new TaskListener<Void>() {
+            public void taskExecuted(Task<Void> task) {
+                System.out.println("DELETE result");
+            }
+
+            public void executeFailed(Task<Void> task) {
+                System.out.println("DELETE fault: " + task.getFault());
+            }
+        });
+
+        // Wait for the async. calls to complete
+        try {
+            Thread.sleep(10000);
+        } catch(InterruptedException exception) {
+        }
+    }
+}

Added: incubator/pivot/tags/v1.0/web-test/src/pivot/web/test/server/WebQueryTestServlet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web-test/src/pivot/web/test/server/WebQueryTestServlet.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web-test/src/pivot/web/test/server/WebQueryTestServlet.java (added)
+++ incubator/pivot/tags/v1.0/web-test/src/pivot/web/test/server/WebQueryTestServlet.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web.test.server;
+
+import java.io.IOException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import pivot.collections.HashMap;
+import pivot.serialization.BinarySerializer;
+import pivot.serialization.JSONSerializer;
+import pivot.serialization.SerializationException;
+import pivot.serialization.Serializer;
+import pivot.util.Base64;
+
+public class WebQueryTestServlet extends HttpServlet {
+    public static final long serialVersionUID = 0;
+
+    private String username = null;
+    private static final String BASIC_AUTHENTICATION_TAG = "Basic";
+
+    @Override
+    public void init(ServletConfig config) {
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    @Override
+    protected void service(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        String authorization = request.getHeader("Authorization");
+
+        if (authorization == null) {
+            response.setHeader("WWW-Authenticate", "BASIC realm=\""
+                + request.getServletPath() +"\"");
+            response.setStatus(401);
+            response.setContentLength(0);
+            response.flushBuffer();
+        } else {
+            String encodedCredentials = authorization.substring(BASIC_AUTHENTICATION_TAG.length() + 1);
+
+            String decodedCredentials = new String(Base64.decode(encodedCredentials));
+            String[] credentials = decodedCredentials.split(":");
+            username = credentials[0];
+
+            // TODO Parse query string
+
+            super.service(request, response);
+        }
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        String pathInfo = request.getPathInfo();
+        String queryString = request.getQueryString();
+
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        map.put("username", username);
+        map.put("pathInfo", pathInfo);
+        map.put("queryString", queryString);
+
+        response.setStatus(200);
+
+        Serializer serializer = new BinarySerializer();
+        response.setContentType(serializer.getMIMEType());
+
+        try {
+            serializer.writeObject(map, response.getOutputStream());
+        } catch(SerializationException exception) {
+            throw new ServletException(exception);
+        }
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        ServletInputStream inputStream = request.getInputStream();
+        JSONSerializer jsonSerializer = new JSONSerializer();
+
+        try {
+            Object value = jsonSerializer.readObject(inputStream);
+            jsonSerializer.writeObject(value, System.out);
+            response.setStatus(201);
+            response.setHeader("Location", request.getPathInfo() +"#101");
+        } catch(SerializationException exception) {
+            throw new ServletException(exception);
+        }
+
+        response.setContentLength(0);
+        response.flushBuffer();
+    }
+
+    @Override
+    protected void doPut(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        ServletInputStream inputStream = request.getInputStream();
+        JSONSerializer jsonSerializer = new JSONSerializer();
+
+        try {
+            Object value = jsonSerializer.readObject(inputStream);
+            jsonSerializer.writeObject(value, System.out);
+            response.setStatus(200);
+        } catch(SerializationException exception) {
+            throw new ServletException(exception);
+        }
+
+        response.setContentLength(0);
+        response.flushBuffer();
+    }
+
+    @Override
+    protected void doDelete(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        response.setStatus(200);
+        response.setContentLength(0);
+        response.flushBuffer();
+    }
+}

Added: incubator/pivot/tags/v1.0/web-test/web.xml
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web-test/web.xml?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web-test/web.xml (added)
+++ incubator/pivot/tags/v1.0/web-test/web.xml Mon Mar 16 16:16:40 2009
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+    <description>Pivot Web Query Test</description>
+    <display-name>Pivot Web Query Test</display-name>
+
+    <servlet>
+        <servlet-name>WebQueryTestServlet</servlet-name>
+        <servlet-class>pivot.web.test.server.WebQueryTestServlet</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ProxyServlet</servlet-name>
+        <servlet-class>pivot.web.server.ProxyServlet</servlet-class>
+        <init-param>
+            <param-name>hostname</param-name>
+            <param-value>localhost</param-value>
+        </init-param>
+        <init-param>
+            <param-name>port</param-name>
+            <param-value>8080</param-value>
+        </init-param>
+        <init-param>
+            <param-name>path</param-name>
+            <param-value>/pivot_web_test/webquery</param-value>
+        </init-param>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>WebQueryTestServlet</servlet-name>
+        <url-pattern>/webquery/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ProxyServlet</servlet-name>
+        <url-pattern>/proxy/*</url-pattern>
+    </servlet-mapping>
+</web-app>
+

Added: incubator/pivot/tags/v1.0/web/.classpath
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/.classpath?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/.classpath (added)
+++ incubator/pivot/tags/v1.0/web/.classpath Mon Mar 16 16:16:40 2009
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/core"/>
+	<classpathentry kind="lib" path="lib/servlet-api.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>

Added: incubator/pivot/tags/v1.0/web/.project
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/.project?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/.project (added)
+++ incubator/pivot/tags/v1.0/web/.project Mon Mar 16 16:16:40 2009
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>web</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

Added: incubator/pivot/tags/v1.0/web/lib/servlet-api.jar
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/lib/servlet-api.jar?rev=754926&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0/web/lib/servlet-api.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/Authentication.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/Authentication.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/Authentication.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/Authentication.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+/**
+ * Interface for attaching authentication information to a web query.
+ *
+ * @author gbrown
+ */
+public interface Authentication {
+    public void authenticate(Query<?> query);
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/BasicAuthentication.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/BasicAuthentication.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/BasicAuthentication.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/BasicAuthentication.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+import pivot.util.Base64;
+
+/**
+ * Implementation of the {@link Authentication} interface supporting the
+ * HTTP <a href="http://tools.ietf.org/rfc/rfc2617.txt">Basic
+ * Authentication</a> scheme.
+ */
+public class BasicAuthentication implements Authentication {
+    private String username = null;
+    private String password = null;
+
+    public BasicAuthentication(String username, String password) {
+        this.username = (username == null) ? "" : username;
+        this.password = (password == null) ? "" : password;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void authenticate(Query<?> query) {
+        String credentials = username + ":" + password;
+        String encodedCredentials = Base64.encode(credentials.getBytes());
+
+        query.getRequestProperties().put("Authorization", "Basic " + encodedCredentials);
+    }
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/DeleteQuery.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/DeleteQuery.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/DeleteQuery.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/DeleteQuery.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+/**
+ * Executes an HTTP DELETE operation.
+ *
+ * @author gbrown
+ */
+public class DeleteQuery extends Query<Void> {
+    public DeleteQuery(String hostname, String path) {
+        this(hostname, DEFAULT_PORT, path, false);
+    }
+
+    public DeleteQuery(String hostname, int port, String path, boolean secure) {
+        super(hostname, port, path, secure);
+    }
+
+    /**
+     * Synchronously executes the DELETE operation.
+     */
+    @Override
+    public Void execute() throws QueryException {
+        execute(Method.DELETE, null);
+        return null;
+    }
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/GetQuery.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/GetQuery.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/GetQuery.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/GetQuery.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+/**
+ * Executes an HTTP GET operation.
+ *
+ * @author gbrown
+ */
+public class GetQuery extends Query<Object> {
+    public GetQuery(String hostname, String path) {
+        this(hostname, DEFAULT_PORT, path, false);
+    }
+
+    public GetQuery(String hostname, int port, String path, boolean secure) {
+        super(hostname, port, path, secure);
+    }
+
+    /**
+     * Synchronously executes the GET operation.
+     *
+     * @return
+     * The result of the operation, deserialized using the query's serializer.
+     */
+    @Override
+    public Object execute() throws QueryException {
+        return execute(Method.GET, null);
+    }
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/PostQuery.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/PostQuery.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/PostQuery.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/PostQuery.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Executes an HTTP POST operation.
+ *
+ * @author gbrown
+ */
+public class PostQuery extends Query<URL> {
+    private Object value = null;
+
+    public PostQuery(String hostname, String path) {
+        this(hostname, DEFAULT_PORT, path, false);
+    }
+
+    public PostQuery(String hostname, int port, String path, boolean secure) {
+        super(hostname, port, path, secure);
+    }
+
+    /**
+     * Returns the value that will be POSTed to the server when the query is
+     * executed.
+     */
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Sets the value that will be POSTed to the server when the query is
+     * executed.
+     *
+     * @param value
+     * The value to POST to the server.
+     */
+    public void setValue(Object value) {
+        this.value = value;
+    }
+
+    /**
+     * Synchronously executes the POST operation.
+     *
+     * @return
+     * A URL that uniquely identifies the location of the resource created
+     * on the server by the operation, or <tt>null</tt> if the server did
+     * not return a location.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public URL execute() throws QueryException {
+        URL valueLocation = null;
+
+        execute(Method.POST, value);
+
+        String location = getResponseProperties().get("Location");
+        if (location != null) {
+            try {
+                valueLocation = new URL(getLocation(), location);
+            } catch(MalformedURLException exception) {
+            }
+        }
+
+        return valueLocation;
+    }
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/PutQuery.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/PutQuery.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/PutQuery.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/PutQuery.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+/**
+ * Executes an HTTP PUT operation.
+ *
+ * @author gbrown
+ */
+public class PutQuery extends Query<Void> {
+    private Object value = null;
+
+    public PutQuery(String hostname, String path) {
+        this(hostname, DEFAULT_PORT, path, false);
+    }
+
+    public PutQuery(String hostname, int port, String path, boolean secure) {
+        super(hostname, port, path, secure);
+    }
+
+    /**
+     * Returns the value that will be PUT to the server when the query is
+     * executed.
+     */
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Sets the value that will be PUT to the server when the query is
+     * executed.
+     *
+     * @param value
+     * The value to PUT to the server.
+     */
+    public void setValue(Object value) {
+        this.value = value;
+    }
+
+    /**
+     * Synchronously executes the PUT operation.
+     */
+    @Override
+    public Void execute() throws QueryException {
+        execute(Method.PUT, value);
+        return null;
+    }
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/Query.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/Query.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/Query.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/Query.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Iterator;
+
+import pivot.collections.Dictionary;
+import pivot.collections.HashMap;
+import pivot.serialization.JSONSerializer;
+import pivot.serialization.Serializer;
+import pivot.util.ListenerList;
+import pivot.util.concurrent.AbortException;
+import pivot.util.concurrent.Dispatcher;
+import pivot.util.concurrent.SynchronizedListenerList;
+import pivot.util.concurrent.Task;
+
+/**
+ * Abstract base class for web queries. A web query is an asynchronous
+ * operation that executes one of the following HTTP methods:
+ *
+ * <ul>
+ * <li>GET</li>
+ * <li>POST</li>
+ * <li>PUT</li>
+ * <li>DELETE</li>
+ * </ul>
+ *
+ * @param <V>
+ * The type of the value retrieved or sent via the query. For GET operations,
+ * it is {@link Object}; for POST operations, the type is {@link URL}. For PUT
+ * and DELETE, it is {@link Void}.
+ *
+ * @author gbrown
+ * @author tvolkert
+ */
+public abstract class Query<V> extends Task<V> {
+    /**
+     * The supported HTTP methods.
+     *
+     * @author gbrown
+     */
+    protected enum Method {
+        GET,
+        POST,
+        PUT,
+        DELETE
+    }
+
+    /**
+     * Arguments dictionary implementation.
+     */
+    public final class ArgumentsDictionary
+        implements Dictionary<String, String>, Iterable<String> {
+        public String get(String key) {
+            return arguments.get(key);
+        }
+
+        public String put(String key, String value) {
+            return arguments.put(key, value);
+        }
+
+        public String remove(String key) {
+            return arguments.remove(key);
+        }
+
+        public boolean containsKey(String key) {
+            return arguments.containsKey(key);
+        }
+
+        public boolean isEmpty() {
+            return arguments.isEmpty();
+        }
+
+        public Iterator<String> iterator() {
+            return arguments.iterator();
+        }
+    }
+
+    /**
+     * Request properties dictionary implementation.
+     */
+    public final class RequestPropertiesDictionary
+        implements Dictionary<String, String>, Iterable<String> {
+        public String get(String key) {
+            return requestProperties.get(key);
+        }
+
+        public String put(String key, String value) {
+            return requestProperties.put(key, value);
+        }
+
+        public String remove(String key) {
+            return requestProperties.remove(key);
+        }
+
+        public boolean containsKey(String key) {
+            return requestProperties.containsKey(key);
+        }
+
+        public boolean isEmpty() {
+            return requestProperties.isEmpty();
+        }
+
+        public Iterator<String> iterator() {
+            return requestProperties.iterator();
+        }
+    }
+
+    /**
+     * Response properties dictionary implementation.
+     */
+    public final class ResponsePropertiesDictionary
+        implements Dictionary<String, String>, Iterable<String> {
+        public String get(String key) {
+            return responseProperties.get(key);
+        }
+
+        public String put(String key, String value) {
+            throw new UnsupportedOperationException();
+        }
+
+        public String remove(String key) {
+            throw new UnsupportedOperationException();
+        }
+
+        public boolean containsKey(String key) {
+            return responseProperties.containsKey(key);
+        }
+
+        public boolean isEmpty() {
+            return responseProperties.isEmpty();
+        }
+
+        public Iterator<String> iterator() {
+            return responseProperties.iterator();
+        }
+    }
+
+    /**
+     * Output stream that monitors the bytes that are written to it by
+     * incrementing the <tt>bytesSent</tt> member variable.
+     *
+     * @author tvolkert
+     */
+    private class MonitoredOutputStream extends OutputStream {
+        private OutputStream outputStream;
+
+        public MonitoredOutputStream(OutputStream outputStream) {
+            this.outputStream = outputStream;
+        }
+
+        public void close() throws IOException {
+            outputStream.close();
+        }
+
+        public void flush() throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            outputStream.flush();
+        }
+
+        public void write(byte[] b) throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            outputStream.write(b);
+            bytesSent += b.length;
+        }
+
+        public void write(byte[] b, int off, int len) throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            outputStream.write(b, off, len);
+            bytesSent += len;
+        }
+
+        public void write(int b) throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            outputStream.write(b);
+            bytesSent++;
+        }
+    }
+
+    /**
+     * Input stream that monitors the bytes that are read from it by
+     * incrementing the <tt>bytesReceived</tt> member variable.
+     *
+     * @author tvolkert
+     */
+    private class MonitoredInputStream extends InputStream {
+        private InputStream inputStream;
+
+        long mark = 0;
+
+        public MonitoredInputStream(InputStream inputStream) {
+            this.inputStream = inputStream;
+        }
+
+        public int read() throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            int result = inputStream.read();
+
+            if (result != -1) {
+                bytesReceived++;
+            }
+
+            return result;
+        }
+
+        public int read(byte b[]) throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            int count = inputStream.read(b);
+
+            if (count != -1) {
+                bytesReceived += count;
+            }
+
+            return count;
+        }
+
+        public int read(byte b[], int off, int len) throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            int count = inputStream.read(b, off, len);
+
+            if (count != -1) {
+                bytesReceived += count;
+            }
+
+            return count;
+        }
+
+        public long skip(long n) throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            long count = inputStream.skip(n);
+            bytesReceived += count;
+            return count;
+        }
+
+        public int available() throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            return inputStream.available();
+        }
+
+        public void close() throws IOException {
+            inputStream.close();
+        }
+
+        public void mark(int readLimit) {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            inputStream.mark(readLimit);
+            mark = bytesReceived;
+        }
+
+        public void reset() throws IOException {
+            if (abort) {
+                throw new AbortException();
+            }
+
+            inputStream.reset();
+            bytesReceived = mark;
+        }
+
+        public boolean markSupported() {
+            return inputStream.markSupported();
+        }
+    }
+
+    /**
+     * Query listener list.
+     *
+     * @author tvolkert
+     */
+    private static class QueryListenerList<V> extends SynchronizedListenerList<QueryListener<V>>
+        implements QueryListener<V> {
+        public synchronized void connected(Query<V> query) {
+            for (QueryListener<V> listener : this) {
+                listener.connected(query);
+            }
+        }
+
+        public synchronized void requestSent(Query<V> query) {
+            for (QueryListener<V> listener : this) {
+                listener.requestSent(query);
+            }
+        }
+
+        public synchronized void responseReceived(Query<V> query) {
+            for (QueryListener<V> listener : this) {
+                listener.responseReceived(query);
+            }
+        }
+
+        public synchronized void failed(Query<V> query) {
+            for (QueryListener<V> listener : this) {
+                listener.failed(query);
+            }
+        }
+    }
+
+    private URL locationContext = null;
+
+    private HashMap<String, String> arguments = new HashMap<String, String>();
+    private HashMap<String, String> requestProperties = new HashMap<String, String>();
+    private HashMap<String, String> responseProperties = new HashMap<String, String>();
+
+    private ArgumentsDictionary argumentsDictionary = new ArgumentsDictionary();
+    private RequestPropertiesDictionary requestPropertiesDictionary = new RequestPropertiesDictionary();
+    private ResponsePropertiesDictionary responsePropertiesDictionary = new ResponsePropertiesDictionary();
+
+    private Serializer serializer = new JSONSerializer();
+
+    private volatile long bytesSent = 0;
+    private volatile long bytesReceived = 0;
+    private volatile long bytesExpected = -1;
+
+    private QueryListenerList<V> queryListeners = new QueryListenerList<V>();
+
+    private static Dispatcher DEFAULT_DISPATCHER = new Dispatcher();
+
+    public static final int DEFAULT_PORT = -1;
+
+    private static final String HTTP_PROTOCOL = "http";
+    private static final String HTTPS_PROTOCOL = "https";
+    private static final String URL_ENCODING = "UTF-8";
+
+    /**
+     * Creates a new web query.
+     *
+     * @param hostname
+     * @param port
+     * @param path
+     * @param secure
+     */
+    public Query(String hostname, int port, String path, boolean secure) {
+        super(DEFAULT_DISPATCHER);
+
+        try {
+            locationContext = new URL(secure ? HTTPS_PROTOCOL : HTTP_PROTOCOL,
+                hostname, port, path);
+        } catch (MalformedURLException exception) {
+            throw new IllegalArgumentException("Unable to construct context URL.",
+                exception);
+        }
+    }
+
+    public String getHostname() {
+        return locationContext.getHost();
+    }
+
+    public String getPath() {
+        return locationContext.getFile();
+    }
+
+    public int getPort() {
+        return locationContext.getPort();
+    }
+
+    public boolean isSecure() {
+        String protocol = locationContext.getProtocol();
+        return protocol.equalsIgnoreCase(HTTPS_PROTOCOL);
+    }
+
+    public URL getLocation() {
+        StringBuilder queryStringBuilder = new StringBuilder();
+
+        for (String key : arguments) {
+            try {
+                if (queryStringBuilder.length() > 0) {
+                    queryStringBuilder.append("&");
+                }
+
+                queryStringBuilder.append(URLEncoder.encode(key, URL_ENCODING)
+                    + "=" + URLEncoder.encode(arguments.get(key), URL_ENCODING));
+            } catch (UnsupportedEncodingException exception) {
+                throw new IllegalStateException("Unable to construct query string.", exception);
+            }
+        }
+
+        URL location = null;
+        try {
+            String queryString = queryStringBuilder.length() > 0 ?
+                "?" + queryStringBuilder.toString() : "";
+
+            location = new URL(locationContext.getProtocol(),
+                locationContext.getHost(),
+                locationContext.getPort(),
+                locationContext.getPath() + queryString);
+        } catch (MalformedURLException exception) {
+            throw new IllegalStateException("Unable to construct query URL.", exception);
+        }
+
+        return location;
+    }
+
+    /**
+     * Returns the web query's arguments dictionary. Arguments are passed via
+     * the query string of the web query's URL.
+     */
+    public ArgumentsDictionary getArguments() {
+        return argumentsDictionary;
+    }
+
+    /**
+     * Returns the web query's request property dictionary. Request properties
+     * are passed via HTTP headers when the query is executed.
+     */
+    public RequestPropertiesDictionary getRequestProperties() {
+        return requestPropertiesDictionary;
+    }
+
+    /**
+     * Returns the web query's response property dictionary. Response properties
+     * are returned via HTTP headers when the query is executed.
+     */
+    public ResponsePropertiesDictionary getResponseProperties() {
+        return responsePropertiesDictionary;
+    }
+
+    /**
+     * Returns the serializer used to stream the value passed to or from the
+     * web query. By default, an instance of {@link JSONSerializer} is used.
+     */
+    public Serializer getSerializer() {
+        return serializer;
+    }
+
+    /**
+     * Sets the serializer used to stream the value passed to or from the
+     * web query.
+     *
+     * @param serializer
+     */
+    public void setSerializer(Serializer serializer) {
+        if (serializer == null) {
+            throw new IllegalArgumentException("serializer is null.");
+        }
+
+        this.serializer = serializer;
+    }
+
+    /**
+     * Gets the number of bytes that have been sent in the body of this
+     * query's HTTP request. This will only be non-zero for POST and PUT
+     * requests, as GET and DELETE requests send no content to the server.
+     * <p>
+     * For POST and PUT requests, this number will increment in between the
+     * {@link QueryListener#connected(Query) connected} and
+     * {@link QueryListener#requestSent(Query) requestSent} phases of the
+     * <tt>QueryListener</tt> lifecycle methods. Interested listeners can poll
+     * for this value during that phase.
+     */
+    public long getBytesSent() {
+        return bytesSent;
+    }
+
+    /**
+     * Gets the number of bytes that have been received from the server in the
+     * body of the server's HTTP response. This will generally only be non-zero
+     * for GET requests, as POST, PUT, and DELETE requests generally don't
+     * solicit response content from the server.
+     * <p>
+     * This number will increment in between the
+     * {@link QueryListener#requestSent(Query) requestSent} and
+     * {@link QueryListener#responseReceived(Query) responseReceived} phases of
+     * the <tt>QueryListener</tt> lifecycle methods. Interested listeners can
+     * poll for this value during that phase.
+     */
+    public long getBytesReceived() {
+        return bytesReceived;
+    }
+
+    /**
+     * Gets the number of bytes that are expected to be received from the
+     * server in the body of the server's HTTP response. This value reflects
+     * the <tt>Content-Length</tt> HTTP response header and is thus merely an
+     * expectation. The actual total number of bytes that will be received is
+     * not known for certain until the full response has been received.
+     * <p>
+     * If the server did not specify a <tt>Content-Length</tt> HTTP response
+     * header, a value of <tt>-1</tt> will be returned to indicate that this
+     * value is unknown.
+     */
+    public long getBytesExpected() {
+        return bytesExpected;
+    }
+
+    protected Object execute(Method method, Object value)
+        throws QueryException {
+        URL location = getLocation();
+        HttpURLConnection connection = null;
+
+        bytesSent = 0;
+        bytesReceived = 0;
+        bytesExpected = -1;
+
+        int status = -1;
+        String message = null;
+
+        try {
+            // Clear any properties from a previous response
+            responseProperties.clear();
+
+            // Open a connection
+            connection = (HttpURLConnection)location.openConnection();
+            connection.setRequestMethod(method.toString());
+            connection.setAllowUserInteraction(false);
+            connection.setInstanceFollowRedirects(false);
+            connection.setUseCaches(false);
+
+            // Set the request headers
+            for (String key : requestProperties) {
+                connection.addRequestProperty(key, requestProperties.get(key));
+            }
+
+            // Set the input/output state
+            connection.setDoInput(true);
+            connection.setDoOutput(method == Method.POST || method == Method.PUT);
+
+            // Connect to the server
+            connection.connect();
+            queryListeners.connected(this);
+
+            // Write the request body
+            if (method == Method.POST || method == Method.PUT) {
+                OutputStream outputStream = null;
+                try {
+                    outputStream = connection.getOutputStream();
+                    serializer.writeObject(value, new MonitoredOutputStream(outputStream));
+                } finally {
+                    if (outputStream != null) {
+                        outputStream.close();
+                    }
+                }
+            }
+
+            // Notify listeners that the request has been sent
+            queryListeners.requestSent(this);
+
+            // Set the response info
+            status = connection.getResponseCode();
+            message = connection.getResponseMessage();
+
+            // If the response was anything other than 2xx, throw an exception
+            int statusPrefix = status / 100;
+            if (statusPrefix != 2) {
+                throw new QueryException(status, message);
+            }
+
+            // Record the content length
+            bytesExpected = connection.getContentLength();
+
+            // NOTE Header indexes start at 1, not 0
+            int i = 1;
+            for (String key = connection.getHeaderFieldKey(i);
+                key != null;
+                key = connection.getHeaderFieldKey(++i)) {
+                responseProperties.put(key, connection.getHeaderField(i));
+            }
+
+            // Read the response body
+            if (method == Method.GET) {
+                InputStream inputStream = null;
+                try {
+                    inputStream = connection.getInputStream();
+                    value = serializer.readObject(new MonitoredInputStream(inputStream));
+                } finally {
+                    if (inputStream != null) {
+                        inputStream.close();
+                    }
+                }
+            }
+
+            // Notify listeners that the response has been received
+            queryListeners.responseReceived(this);
+        } catch (Exception exception) {
+            queryListeners.failed(this);
+            throw new QueryException(exception);
+        }
+
+        return value;
+    }
+
+    /**
+     * Gets the query's <tt>QueryListener</tt>s.
+     */
+    public ListenerList<QueryListener<V>> getQueryListeners() {
+        return queryListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/QueryException.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/QueryException.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/QueryException.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/QueryException.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+import pivot.util.concurrent.TaskExecutionException;
+
+/**
+ * Thrown when an error occurs while executing a web query.
+ */
+public class QueryException extends TaskExecutionException {
+    public static final long serialVersionUID = 0;
+
+    private int status = -1;
+
+    // TODO Define static constants for status codes
+
+    public QueryException(int status) {
+        this(status, null);
+    }
+
+    public QueryException(int status, String message) {
+        super(message);
+
+        this.status = status;
+    }
+
+    public QueryException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Returns the HTTP status code corresponding to the exception.
+     *
+     * @return
+     * An HTTP status code reflecting the nature of the exception.
+     */
+    public int getStatus() {
+        return status;
+    }
+
+    @Override
+    public String getLocalizedMessage() {
+        String message = super.getLocalizedMessage();
+        return (message != null ? (status + " " + message) : String.valueOf(status));
+    }
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/QueryListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/QueryListener.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/QueryListener.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/QueryListener.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web;
+
+/**
+ * Defines event listener methods that pertain to queries. Developers register
+ * for such events by adding themselves to a query's list of "query
+ * listeners" (see {@link Query#getQueryListeners()}).
+ * <p>
+ * Note that, like {@link pivot.util.concurrent.TaskListener task listeners},
+ * query listeners will be notified on the query's worker thread, not the thread
+ * that executed the query.
+ *
+ * @author tvolkert
+ */
+public interface QueryListener<V> {
+    /**
+     * Called when a query has connected to the server but the request has not
+     * yet been sent.
+     *
+     * @param query
+     */
+    public void connected(Query<V> query);
+
+    /**
+     * Called when the request has been sent to the server but the response has
+     * not yet been received.
+     *
+     * @param query
+     */
+    public void requestSent(Query<V> query);
+
+    /**
+     * Called when a response has been received from the server.
+     *
+     * @param query
+     */
+    public void responseReceived(Query<V> query);
+
+    /**
+     * Called when an error has occurred
+     *
+     * @param query
+     */
+    public void failed(Query<V> query);
+}

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/package.html
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/package.html?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/package.html (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/package.html Mon Mar 16 16:16:40 2009
@@ -0,0 +1,38 @@
+<html>
+<head></head>
+<body>
+<p>Provides classes for communicating with HTTP-based web services.</p>
+
+<p>Fundamentally, a web query is simply an <a href="http://www.ietf.org/rfc/rfc2616.txt" target="_blank">HTTP</a> request. However, the default data format used by a web query is not HTML, but JSON. This allows a caller to effectively invoke database-like operations over the web - the HTTP methods are used in a manner that is very similar to their corresponding SQL equivalents:<p>
+
+<table style="border-collapse:collapse; border:solid 1px #999999">
+    <thead>
+        <td style="font-weight:bold">HTTP Method</td>
+        <td style="font-weight:bold">SQL Query</td>
+        <td style="font-weight:bold">Behavior</td>
+    </thead>
+    <tr>
+        <td><tt>POST</tt></td>
+        <td><tt>INSERT</tt></td>
+        <td>Create</td>
+    </tr>
+    <tr>
+        <td><tt>GET</tt></td>
+        <td><tt>SELECT</tt></td>
+        <td>Read</td>
+    </tr>
+    <tr>
+        <td><tt>PUT</tt></td>
+        <td><tt>UPDATE</tt></td>
+        <td>Update</td>
+    </tr>
+    <tr>
+        <td><tt>DELETE</tt></td>
+        <td><tt>DELETE</tt></td>
+        <td>Delete</td>
+    </tr>
+</table>
+
+<p>These operations, sometimes referred to as "CRUD", form the basis of the <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer" target="_blank">Representational State Transfer</a> (REST) model of building web services. Pivot web queries are designed primarily to facilitate interaction with JSON-based REST services. However, they are sufficiently generic to support communication with any type of HTTP-based service, using any data format (for example, XML). This enables web queries to be used in a broad range of server communication scenarios.</p>
+</body>
+</html>

Added: incubator/pivot/tags/v1.0/web/src/pivot/web/server/ProxyServlet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/web/src/pivot/web/server/ProxyServlet.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/web/src/pivot/web/server/ProxyServlet.java (added)
+++ incubator/pivot/tags/v1.0/web/src/pivot/web/server/ProxyServlet.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.
+ */
+package pivot.web.server;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * HTTP proxy that allows an unsigned applet to issue web queries to services
+ * outside of its origin server.
+ *
+ * @author gbrown
+ */
+public class ProxyServlet extends HttpServlet {
+    private String hostname = null;
+    private int port = -1;
+    private String path = null;
+
+    public static final long serialVersionUID = 0;
+
+    public static final String METHOD_GET = "GET";
+    public static final String METHOD_POST = "POST";
+    public static final String METHOD_PUT = "PUT";
+    public static final String METHOD_DELETE = "DELETE";
+
+    public static final String HOSTNAME_PARAM = "hostname";
+    public static final String PORT_PARAM = "port";
+    public static final String PATH_PARAM = "path";
+
+    public static final int BUFFER_SIZE = 1024;
+
+    @Override
+    public void init(ServletConfig config)
+        throws ServletException {
+        super.init();
+
+        hostname = config.getInitParameter(HOSTNAME_PARAM);
+        if (hostname == null) {
+            throw new ServletException("Hostname is required.");
+        }
+
+        String portHeader = config.getInitParameter(PORT_PARAM);
+        port = (portHeader == null) ? -1 : Integer.parseInt(portHeader);
+
+        path = config.getInitParameter(PATH_PARAM);
+        if (path == null) {
+            throw new ServletException("Path is required.");
+        }
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected void service(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        // Construct the URL
+        String path = this.path;
+
+        String pathInfo = request.getPathInfo();
+        if (pathInfo != null) {
+            path += "/" + pathInfo;
+        }
+
+        String queryString = request.getQueryString();
+        if (queryString != null) {
+            path += "?" + queryString;
+        }
+
+        URL url = null;
+        try {
+            url = new URL(request.getScheme(), hostname, port, path);
+        } catch(MalformedURLException exception) {
+            throw new ServletException("Unable to construct URL.", exception);
+        }
+
+        String method = request.getMethod();
+
+        // Open a connection to the URL
+        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+        connection.setRequestMethod(method);
+        connection.setAllowUserInteraction(false);
+        connection.setInstanceFollowRedirects(false);
+        connection.setUseCaches(false);
+
+        // Write request headers to connection
+        System.out.println("[Request Headers]");
+        Enumeration<String> headerNames = (Enumeration<String>)request.getHeaderNames();
+
+        if (headerNames != null) {
+            while (headerNames.hasMoreElements()) {
+                String headerName = headerNames.nextElement();
+
+                Enumeration<String> headerValues =
+                    (Enumeration<String>)request.getHeaders(headerName);
+
+                while (headerValues.hasMoreElements()) {
+                    String headerValue = headerValues.nextElement();
+                    System.out.println(headerName + ": " + headerValue);
+
+                    if (connection.getRequestProperty(headerName) == null) {
+                        connection.setRequestProperty(headerName, headerValue);
+                    } else {
+                        connection.addRequestProperty(headerName, headerValue);
+                    }
+                }
+            }
+        }
+
+        // Set the input/output state
+        connection.setDoInput(true);
+        connection.setDoOutput(method.equalsIgnoreCase(METHOD_POST)
+            || method.equalsIgnoreCase(METHOD_PUT));
+
+        // Connect to the server
+        connection.connect();
+
+        // Write the request body
+        if (connection.getDoOutput()) {
+            OutputStream outputStream = null;
+
+            try {
+                InputStream inputStream = request.getInputStream();
+
+                outputStream = connection.getOutputStream();
+                outputStream = new BufferedOutputStream(outputStream, BUFFER_SIZE);
+                for (int data = inputStream.read(); data != -1; data = inputStream.read()) {
+                    outputStream.write((byte)data);
+                }
+            } finally {
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+            }
+        }
+
+        // Set the response status
+        int status = connection.getResponseCode();
+        System.out.println("[Status] " + status);
+
+        int statusPrefix = status / 100;
+
+        if (statusPrefix == 1
+            || statusPrefix == 3) {
+            throw new ServletException("Unexpected server response: " + status);
+        }
+
+        response.setStatus(status);
+
+        // Write response headers
+        // NOTE Header indexes start at 1, not 0
+        System.out.println("[Response Headers]");
+
+        int i = 1;
+        for (String key = connection.getHeaderFieldKey(i);
+            key != null;
+            key = connection.getHeaderFieldKey(++i)) {
+            if (key != null) {
+                String value = connection.getHeaderField(i);
+                System.out.println(key + ": " + value);
+
+                if (response.containsHeader(key)) {
+                    response.addHeader(key, value);
+                } else {
+                    response.setHeader(key, value);
+                }
+            }
+        }
+
+        // Read the response body
+        if (method.equalsIgnoreCase(METHOD_GET)) {
+            InputStream inputStream = null;
+
+            try {
+                try {
+                    // Response returned on input stream
+                    inputStream = connection.getInputStream();
+                } catch(Exception exception) {
+                    // Response returned on error stream
+                    inputStream = connection.getErrorStream();
+                }
+
+                inputStream = new BufferedInputStream(inputStream, BUFFER_SIZE);
+
+                OutputStream outputStream = response.getOutputStream();
+                for (int data = inputStream.read(); data != -1; data = inputStream.read()) {
+                    outputStream.write((byte)data);
+                }
+
+                response.flushBuffer();
+            } finally {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+            }
+        }
+    }
+}