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/26 00:12:17 UTC

svn commit: r758461 [13/47] - in /incubator/pivot/branches: ./ 1.1/ 1.1/charts-test/ 1.1/charts-test/src/ 1.1/charts-test/src/pivot/ 1.1/charts-test/src/pivot/charts/ 1.1/charts-test/src/pivot/charts/test/ 1.1/charts/ 1.1/charts/lib/ 1.1/charts/src/ 1....

Added: incubator/pivot/branches/1.1/web/src/pivot/web/BasicAuthentication.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/BasicAuthentication.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/BasicAuthentication.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/BasicAuthentication.java Wed Mar 25 23:08:38 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/branches/1.1/web/src/pivot/web/DeleteQuery.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/DeleteQuery.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/DeleteQuery.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/DeleteQuery.java Wed Mar 25 23:08:38 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/branches/1.1/web/src/pivot/web/GetQuery.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/GetQuery.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/GetQuery.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/GetQuery.java Wed Mar 25 23:08:38 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/branches/1.1/web/src/pivot/web/PostQuery.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/PostQuery.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/PostQuery.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/PostQuery.java Wed Mar 25 23:08:38 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/branches/1.1/web/src/pivot/web/PutQuery.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/PutQuery.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/PutQuery.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/PutQuery.java Wed Mar 25 23:08:38 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/branches/1.1/web/src/pivot/web/Query.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/Query.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/Query.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/Query.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,500 @@
+/*
+ * 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.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 javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+
+import pivot.collections.Dictionary;
+import pivot.collections.HashMap;
+import pivot.io.IOTask;
+import pivot.serialization.JSONSerializer;
+import pivot.serialization.Serializer;
+import pivot.util.ListenerList;
+import pivot.util.concurrent.Dispatcher;
+import pivot.util.concurrent.SynchronizedListenerList;
+
+/**
+ * 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 IOTask<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();
+        }
+    }
+
+    /**
+     * 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 HostnameVerifier hostnameVerifier = 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 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 HostnameVerifier getHostnameVerifier() {
+        return hostnameVerifier;
+    }
+
+    public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
+        this.hostnameVerifier = hostnameVerifier;
+    }
+
+    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
+     * The serializer (must be non-null).
+     */
+    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;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected Object execute(Method method, Object value)
+        throws QueryException {
+        URL location = getLocation();
+        HttpURLConnection connection = null;
+
+        Serializer<Object> serializer = (Serializer<Object>)this.serializer;
+
+        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);
+
+            if (connection instanceof HttpsURLConnection
+                && hostnameVerifier != null) {
+                HttpsURLConnection httpsConnection = (HttpsURLConnection)connection;
+                httpsConnection.setHostnameVerifier(hostnameVerifier);
+            }
+
+            // Set the request headers
+            connection.setRequestProperty("Content-Type", serializer.getMIMEType(value));
+            for (String key : requestProperties) {
+                connection.setRequestProperty(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;
+    }
+
+    /**
+     * Returns the query listener list.
+     */
+    public ListenerList<QueryListener<V>> getQueryListeners() {
+        return queryListeners;
+    }
+}

Added: incubator/pivot/branches/1.1/web/src/pivot/web/QueryException.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/QueryException.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/QueryException.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/QueryException.java Wed Mar 25 23:08:38 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 {
+    private 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/branches/1.1/web/src/pivot/web/QueryListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/QueryListener.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/QueryListener.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/QueryListener.java Wed Mar 25 23:08:38 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/branches/1.1/web/src/pivot/web/package.html
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/package.html?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/package.html (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/package.html Wed Mar 25 23:08:38 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/branches/1.1/web/src/pivot/web/server/ProxyServlet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/server/ProxyServlet.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/server/ProxyServlet.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/server/ProxyServlet.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,260 @@
+/*
+ * 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;
+
+import pivot.collections.HashSet;
+
+/**
+ * HTTP proxy that allows an unsigned applet to issue web queries to services
+ * outside of its origin server.
+ *
+ * @author gbrown
+ * @author tvolkert
+ */
+public class ProxyServlet extends HttpServlet {
+    /**
+     * A set of HTTP response header names, stored in a case-insensitive manner.
+     *
+     * @author tvolkert
+     */
+    private static class ResponseHeaderSet extends HashSet<String> {
+        private static final long serialVersionUID = 3055851700567335445L;
+
+        @Override
+        public void add(String element) {
+            super.add(element.toLowerCase());
+        }
+
+        @Override
+        public void remove(String element) {
+            super.remove(element.toLowerCase());
+        }
+
+        @Override
+        public boolean contains(String element) {
+            return super.contains(element.toLowerCase());
+        }
+    }
+
+    private String hostname = null;
+    private int port = -1;
+    private String path = null;
+
+    private static ResponseHeaderSet ignoreResponseHeaders = new ResponseHeaderSet();
+
+    private static final long serialVersionUID = -1794977331184160392L;
+
+    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;
+
+    static {
+        ignoreResponseHeaders.add("Transfer-Encoding");
+    }
+
+    @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 && !ignoreResponseHeaders.contains(key)) {
+                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();
+                }
+
+                if (inputStream != null) {
+                    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();
+                }
+            }
+        }
+    }
+}

Added: incubator/pivot/branches/1.1/web/src/pivot/web/server/QueryServlet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/server/QueryServlet.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/server/QueryServlet.java (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/server/QueryServlet.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,677 @@
+/*
+ * 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.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+import javax.security.auth.login.LoginException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import pivot.collections.Dictionary;
+import pivot.collections.HashMap;
+import pivot.serialization.JSONSerializer;
+import pivot.serialization.SerializationException;
+import pivot.serialization.Serializer;
+import pivot.util.Base64;
+
+/**
+ * Abstract base class for web query servlets. It is the server counterpart to
+ * {@link pivot.web.Query pivot.web.Query}.
+ *
+ * @author tvolkert
+ */
+public abstract class QueryServlet extends HttpServlet {
+    static final long serialVersionUID = -646654620936816287L;
+
+    /**
+     * The supported HTTP methods.
+     *
+     * @author tvolkert
+     */
+    protected enum Method {
+        GET,
+        POST,
+        PUT,
+        DELETE;
+
+        public static Method decode(String value) {
+            return valueOf(value.toUpperCase());
+        }
+    }
+
+    /**
+     * User credentials, which will be made availale if the servlet's
+     * <tt>authenticationRequired</tt> flag is set to <tt>true</tt>.
+     *
+     * @author tvolkert
+     */
+    public static final class Credentials {
+        private String username;
+        private String password;
+
+        private Credentials(String username, String password) {
+            this.username = username;
+            this.password = password;
+        }
+
+        public String getUsername() {
+            return username;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+    }
+
+    /**
+     * Arguments dictionary implementation.
+     *
+     * @author tvolkert
+     */
+    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) {
+            throw new UnsupportedOperationException();
+        }
+
+        public String remove(String key) {
+            throw new UnsupportedOperationException();
+        }
+
+        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.
+     *
+     * @author tvolkert
+     */
+    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) {
+            throw new UnsupportedOperationException();
+        }
+
+        public String remove(String key) {
+            throw new UnsupportedOperationException();
+        }
+
+        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.
+     *
+     * @author tvolkert
+     */
+    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) {
+            return responseProperties.put(key, value);
+        }
+
+        public String remove(String key) {
+            return responseProperties.remove(key);
+        }
+
+        public boolean containsKey(String key) {
+            return responseProperties.containsKey(key);
+        }
+
+        public boolean isEmpty() {
+            return responseProperties.isEmpty();
+        }
+
+        public Iterator<String> iterator() {
+            return responseProperties.iterator();
+        }
+    }
+
+    private boolean authenticationRequired = false;
+    private Credentials credentials = null;
+
+    private String hostname;
+    private String contextPath;
+    private String queryPath;
+    private int port;
+    private boolean secure;
+    private Method method;
+
+    private boolean determineContentLength = false;
+
+    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 static final String BASIC_AUTHENTICATION_TAG = "Basic";
+    private static final String HTTP_PROTOCOL = "http";
+    private static final String HTTPS_PROTOCOL = "https";
+    private static final String URL_ENCODING = "UTF-8";
+
+    /**
+     * Gets the host name that was requested.
+     */
+    public String getHostname() {
+        return hostname;
+    }
+
+    /**
+     * Returns the portion of the request URI that indicates the context of the
+     * request. The context path always comes first in a request URI. The path
+     * starts with a "/" character but does not end with a "/" character. For
+     * servlets in the default (root) context, this method returns "".
+     */
+    public String getContextPath() {
+        return contextPath;
+    }
+
+    /**
+     * Returns the portion of the request URI that occurs after the context
+     * path but preceding the query string. It will start with a "/" character.
+     * For servlets in the default (root) context, this method returns the full
+     * path.
+     */
+    public String getQueryPath() {
+        return queryPath;
+    }
+
+    /**
+     * Returns the Internet Protocol (IP) port number of the interface on which
+     * the request was received.
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * Tells whether the request has been ecrypted over HTTPS.
+     */
+    public boolean isSecure() {
+        return secure;
+    }
+
+    /**
+     * Returns the name of the HTTP protocol that the request is using.
+     */
+    public String getProtocol() {
+        return isSecure() ? HTTPS_PROTOCOL : HTTP_PROTOCOL;
+    }
+
+    /**
+     * Gets the HTTP method with which the current request was made.
+     */
+    public Method getMethod() {
+        return method;
+    }
+
+    /**
+     * Tells whether this servlet is configured to always determine the content
+     * length of outgoing responses and set the <tt>Content-Length</tt> HTTP
+     * response header accordingly. If this flag is <tt>false</tt>, it is up to
+     * the servlet's discretion as to when to set the <tt>Content-Length</tt>
+     * header (it will do so if it is trivially easy). If this is set to
+     * <tt>true</tt>, it will force the servlet to always set the header, but
+     * doing so will incur a performance penalty, as the servlet will be unable
+     * to stream the response directly to the HTTP output stream as it gets
+     * serialized.
+     */
+    public boolean isDetermineContentLength() {
+        return determineContentLength;
+    }
+
+    /**
+     * Sets the value of the <tt>determineContentLength</tt> flag.
+     *
+     * @see #isDetermineContentLength()
+     */
+    public void setDetermineContentLength(boolean determineContentLength) {
+        this.determineContentLength = determineContentLength;
+    }
+
+    /**
+     * Tells whether or not this servlet will require authentication data. If
+     * set to <tt>true</tt>, and un-authenticated requests are received, the
+     * servlet will automatically respond with a request for authentication.
+     */
+    public boolean isAuthenticationRequired() {
+        return authenticationRequired;
+    }
+
+    /**
+     * Sets whether or not this servlet will require authentication data. If
+     * set to <tt>true</tt>, and un-authenticated requests are received, the
+     * servlet will automatically respond with a request for authentication.
+     */
+    public void setAuthenticationRequired(boolean authenticationRequired) {
+        this.authenticationRequired = authenticationRequired;
+
+        if (!authenticationRequired) {
+            credentials = null;
+        }
+    }
+
+    /**
+     * Gets the authentication credentials that were extracted from the
+     * request. These are only available if the <tt>authenticationRequired</tt>
+     * flag is set to <tt>true</tt>.
+     */
+    public Credentials getCredentials() {
+        return credentials;
+    }
+
+    /**
+     * Returns the servlet's arguments dictionary, which holds the values
+     * passed in the HTTP request query string.
+     */
+    public ArgumentsDictionary getArguments() {
+        return argumentsDictionary;
+    }
+
+    /**
+     * Returns the servlet's request property dictionary, which holds the HTTP
+     * request headers.
+     */
+    public RequestPropertiesDictionary getRequestProperties() {
+        return requestPropertiesDictionary;
+    }
+
+    /**
+     * Returns the servlet's response property dictionary, which holds the HTTP
+     * response headers that will be sent back to the client.
+     */
+    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
+     * The serializer (must be non-null).
+     */
+    public void setSerializer(Serializer<?> serializer) {
+        if (serializer == null) {
+            throw new IllegalArgumentException("serializer is null.");
+        }
+
+        this.serializer = serializer;
+    }
+
+    /**
+     *
+     */
+    protected Object doGet() throws ServletException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     *
+     */
+    protected URL doPost(Object value) throws ServletException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     *
+     */
+    protected void doPut(Object value) throws ServletException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     *
+     */
+    protected void doDelete() throws ServletException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Authorizes the current request, and throws a <tt>LoginException</tt> if
+     * the request is not authorized. This method will only be called if the
+     * <tt>authenticationRequired</tt> flag is set to <tt>true</tt>. Subclasses
+     * wishing to authorize the authenticated user credentials may override
+     * this method to perform that authorization. On the other hand, the
+     * <tt>authorize</tt> method of <tt>QueryServlet</tt> does nothing, so
+     * subclasses that wish to authenticate the request but not authorization
+     * it may simply not override this method.
+     * <p>
+     * This method is guaranteed to be called after the arguments and request
+     * properties have been made available.
+     *
+     * @throws LoginException
+     * Thrown if the request is not authorized
+     */
+    protected void authorize() throws ServletException, LoginException {
+        // No-op
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected void service(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        boolean proceed = true;
+
+        if (authenticationRequired) {
+            String authorization = request.getHeader("Authorization");
+
+            if (authorization == null) {
+                proceed = false;
+                doUnauthorized(request, response);
+            } else {
+                String encodedCredentials = authorization.substring
+                    (BASIC_AUTHENTICATION_TAG.length() + 1);
+                String decodedCredentials = new String(Base64.decode(encodedCredentials));
+                String[] credentialsPair = decodedCredentials.split(":");
+
+                String username = credentialsPair.length > 0 ? credentialsPair[0] : "";
+                String password = credentialsPair.length > 1 ? credentialsPair[1] : "";
+
+                if (credentials == null) {
+                    credentials = new Credentials(username, password);
+                } else {
+                    credentials.username = username;
+                    credentials.password = password;
+                }
+            }
+        }
+
+        if (proceed) {
+            // Extract our location context
+            try {
+                URL url = new URL(request.getRequestURL().toString());
+
+                hostname = url.getHost();
+                contextPath = request.getContextPath();
+                queryPath = request.getRequestURI();
+                port = request.getLocalPort();
+                secure = url.getProtocol().equalsIgnoreCase(HTTPS_PROTOCOL);
+                method = Method.decode(request.getMethod());
+
+                if (queryPath.startsWith(contextPath)) {
+                    queryPath = queryPath.substring(contextPath.length());
+                }
+            } catch (MalformedURLException ex) {
+                throw new ServletException(ex);
+            }
+
+            // Clear out any remnants of the previous service
+            arguments.clear();
+            requestProperties.clear();
+            responseProperties.clear();
+
+            // Copy the query string into our arguments dictionary
+            String queryString = request.getQueryString();
+            if (queryString != null) {
+                String[] pairs = queryString.split("&");
+
+                for (int i = 0, n = pairs.length; i < n; i++) {
+                    String[] pair = pairs[i].split("=");
+
+                    String key = URLDecoder.decode(pair[0], URL_ENCODING);
+                    String value = URLDecoder.decode((pair.length > 1) ? pair[1] : "", URL_ENCODING);
+
+                    arguments.put(key, value);
+                }
+            }
+
+            // Copy the request headers into our request properties dictionary
+            Enumeration<String> headerNames = request.getHeaderNames();
+            while (headerNames.hasMoreElements()) {
+                String headerName = headerNames.nextElement();
+                String headerValue = request.getHeader(headerName);
+
+                requestProperties.put(headerName, headerValue);
+            }
+
+            if (authenticationRequired) {
+                try {
+                    authorize();
+                } catch (LoginException ex) {
+                    proceed = false;
+                    doForbidden(request, response);
+                }
+            }
+        }
+
+        if (proceed) {
+            super.service(request, response);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        Serializer<Object> serializer = (Serializer<Object>)this.serializer;
+
+        try {
+            Object result = doGet();
+
+            response.setStatus(200);
+            setResponseHeaders(response);
+            response.setContentType(serializer.getMIMEType(result));
+
+            OutputStream responseOutputStream = response.getOutputStream();
+
+            if (determineContentLength) {
+                File tempFile = File.createTempFile("pivot", null);
+
+                // Serialize our result to an intermediary file
+                FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
+                try {
+                    serializer.writeObject(result, fileOutputStream);
+                } finally {
+                    fileOutputStream.close();
+                }
+
+                // Our content length is the length of the file in bytes
+                response.setHeader("Content-Length", String.valueOf(tempFile.length()));
+
+                // Now write the contents of the file out to our response
+                FileInputStream fileInputStream = new FileInputStream(tempFile);
+                try {
+                    byte[] buffer = new byte[1024];
+                    int nBytes;
+                    do {
+                        nBytes = fileInputStream.read(buffer);
+                        if (nBytes > 0) {
+                            responseOutputStream.write(buffer, 0, nBytes);
+                        }
+                    } while (nBytes != -1);
+                } finally {
+                    fileInputStream.close();
+                }
+            } else {
+                serializer.writeObject(result, responseOutputStream);
+            }
+        } catch (UnsupportedOperationException ex) {
+            doMethodNotAllowed(response);
+        } catch (SerializationException ex) {
+            throw new ServletException(ex);
+        }
+    }
+
+    @Override
+    protected final void doPost(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        try {
+            Object value = serializer.readObject(request.getInputStream());
+
+            URL url = doPost(value);
+
+            response.setStatus(201);
+            setResponseHeaders(response);
+            response.setHeader("Location", url.toString());
+            response.setContentLength(0);
+            response.flushBuffer();
+        } catch (UnsupportedOperationException ex) {
+            doMethodNotAllowed(response);
+        } catch (SerializationException ex) {
+            throw new ServletException(ex);
+        }
+    }
+
+    @Override
+    protected final void doPut(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        try {
+            Object value = serializer.readObject(request.getInputStream());
+
+            doPut(value);
+
+            response.setStatus(204);
+            setResponseHeaders(response);
+            response.setContentLength(0);
+            response.flushBuffer();
+        } catch (UnsupportedOperationException ex) {
+            doMethodNotAllowed(response);
+        } catch (SerializationException ex) {
+            throw new ServletException(ex);
+        }
+    }
+
+    @Override
+    protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        try {
+            doDelete();
+
+            response.setStatus(204);
+            setResponseHeaders(response);
+            response.setContentLength(0);
+            response.flushBuffer();
+        } catch (UnsupportedOperationException ex) {
+            doMethodNotAllowed(response);
+        }
+    }
+
+    @Override
+    protected final void doHead(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        doMethodNotAllowed(response);
+    }
+
+    @Override
+    protected final void doOptions(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        doMethodNotAllowed(response);
+    }
+
+    @Override
+    protected final void doTrace(HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException {
+        doMethodNotAllowed(response);
+    }
+
+    /**
+     *
+     */
+    private void doUnauthorized(HttpServletRequest request, HttpServletResponse response)
+        throws IOException {
+        response.setHeader("WWW-Authenticate", "BASIC realm=\""
+            + request.getServletPath() +"\"");
+        response.setStatus(401);
+        response.setContentLength(0);
+        response.flushBuffer();
+    }
+
+    /**
+     *
+     */
+    private void doForbidden(HttpServletRequest request, HttpServletResponse response)
+        throws IOException {
+        response.setStatus(403);
+        response.setContentLength(0);
+        response.flushBuffer();
+    }
+
+    /**
+     *
+     */
+    private void doMethodNotAllowed(HttpServletResponse response) throws IOException {
+        response.setStatus(405);
+        response.setContentLength(0);
+        response.flushBuffer();
+    }
+
+    /**
+     *
+     */
+    private void setResponseHeaders(HttpServletResponse response) {
+        for (String key : responseProperties) {
+            response.setHeader(key, responseProperties.get(key));
+        }
+    }
+}

Added: incubator/pivot/branches/1.1/web/src/pivot/web/server/package.html
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/web/src/pivot/web/server/package.html?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/web/src/pivot/web/server/package.html (added)
+++ incubator/pivot/branches/1.1/web/src/pivot/web/server/package.html Wed Mar 25 23:08:38 2009
@@ -0,0 +1,6 @@
+<html>
+<head></head>
+<body>
+<p>Contains classes to facilitate access to and development of web query services.</p>
+</body>
+</html>

Added: incubator/pivot/branches/1.1/wtk-test/.classpath
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/.classpath?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk-test/.classpath (added)
+++ incubator/pivot/branches/1.1/wtk-test/.classpath Wed Mar 25 23:08:38 2009
@@ -0,0 +1,10 @@
+<?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="/wtk"/>
+	<classpathentry kind="lib" path="lib/js.jar"/>
+	<classpathentry kind="lib" path="lib/js-engine.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>

Added: incubator/pivot/branches/1.1/wtk-test/.project
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/.project?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk-test/.project (added)
+++ incubator/pivot/branches/1.1/wtk-test/.project Wed Mar 25 23:08:38 2009
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>wtk-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/branches/1.1/wtk-test/lib/js-engine.jar
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/lib/js-engine.jar?rev=758461&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/branches/1.1/wtk-test/lib/js-engine.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/branches/1.1/wtk-test/lib/js.jar
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/lib/js.jar?rev=758461&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/branches/1.1/wtk-test/lib/js.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/AccordionTest.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/AccordionTest.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/AccordionTest.java (added)
+++ incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/AccordionTest.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,29 @@
+package pivot.wtk.test;
+
+import pivot.collections.Dictionary;
+import pivot.wtk.Application;
+import pivot.wtk.Display;
+import pivot.wtk.Frame;
+import pivot.wtkx.WTKXSerializer;
+
+public class AccordionTest implements Application {
+    private Frame frame = null;
+
+    public void startup(Display display, Dictionary<String, String> properties)
+        throws Exception {
+        WTKXSerializer wtkxSerializer = new WTKXSerializer();
+        frame = (Frame)wtkxSerializer.readObject(getClass().getResource("accordion_test.wtkx"));
+        frame.open(display);
+    }
+
+    public boolean shutdown(boolean optional) {
+        frame.close();
+        return true;
+    }
+
+    public void suspend() {
+    }
+
+    public void resume() {
+    }
+}

Added: incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/BorderTest.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/BorderTest.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/BorderTest.java (added)
+++ incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/BorderTest.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,32 @@
+package pivot.wtk.test;
+
+import pivot.collections.Dictionary;
+import pivot.wtk.Application;
+import pivot.wtk.Component;
+import pivot.wtk.Display;
+import pivot.wtk.Frame;
+import pivot.wtkx.WTKXSerializer;
+
+public class BorderTest implements Application {
+    private Frame frame = null;
+
+    public void startup(Display display, Dictionary<String, String> properties)
+        throws Exception {
+        WTKXSerializer wtkxSerializer = new WTKXSerializer();
+        frame = new Frame((Component)wtkxSerializer.readObject(getClass().getResource("border_test.wtkx")));
+        frame.setTitle("Border Test");
+        frame.setPreferredSize(480, 360);
+        frame.open(display);
+    }
+
+    public boolean shutdown(boolean optional) {
+        frame.close();
+        return true;
+    }
+
+    public void suspend() {
+    }
+
+    public void resume() {
+    }
+}

Added: incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/CardPaneTest.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/CardPaneTest.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/CardPaneTest.java (added)
+++ incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/CardPaneTest.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,101 @@
+/*
+ * 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.wtk.test;
+
+import pivot.collections.Dictionary;
+import pivot.util.Vote;
+import pivot.wtk.Application;
+import pivot.wtk.Button;
+import pivot.wtk.CardPane;
+import pivot.wtk.CardPaneListener;
+import pivot.wtk.Component;
+import pivot.wtk.Display;
+import pivot.wtk.FlowPane;
+import pivot.wtk.Frame;
+import pivot.wtk.Orientation;
+import pivot.wtk.Sheet;
+import pivot.wtkx.WTKXSerializer;
+
+public class CardPaneTest implements Application {
+    private Frame frame = null;
+    private Sheet sheet = null;
+    private CardPane cardPane = null;
+
+    public void startup(Display display, Dictionary<String, String> properties)
+        throws Exception {
+    	Frame frame = new Frame(new FlowPane());
+    	frame.getStyles().put("padding", 0);
+    	frame.setTitle("Card Pane Test");
+    	frame.setPreferredSize(800, 600);
+    	frame.setLocation(20, 20);
+
+        WTKXSerializer wtkxSerializer = new WTKXSerializer();
+        sheet = new Sheet((Component)wtkxSerializer.readObject(getClass().getResource("card_pane_test.wtkx")));
+
+        cardPane = (CardPane)wtkxSerializer.getObjectByName("cardPane");
+
+        Button.Group sizeGroup = Button.getGroup("sizeGroup");
+        sizeGroup.getGroupListeners().add(new Button.GroupListener() {
+        	public void selectionChanged(Button.Group buttonGroup, Button previousSelection) {
+        		final Button selection = buttonGroup.getSelection();
+        		int selectedIndex = selection == null ? -1 : selection.getParent().indexOf(selection);
+
+        		cardPane.getCardPaneListeners().add(new CardPaneListener() {
+        		    public void orientationChanged(CardPane cardPane, Orientation previousOrientation) {
+
+        		    }
+
+        			public Vote previewSelectedIndexChange(CardPane cardPane, int selectedIndex) {
+        				if (selection != null) {
+        					selection.getParent().setEnabled(false);
+        				}
+
+        				return Vote.APPROVE;
+        			}
+
+        			public void selectedIndexChangeVetoed(CardPane cardPane, Vote reason) {
+        				if (selection != null
+    						&& reason == Vote.DENY) {
+        					selection.getParent().setEnabled(true);
+        				}
+        			}
+
+        			public void selectedIndexChanged(CardPane cardPane, int previousSelectedIndex) {
+        				if (selection != null) {
+        					selection.getParent().setEnabled(true);
+        				}
+        			}
+        		});
+
+        		cardPane.setSelectedIndex(selectedIndex);
+        	}
+        });
+
+        frame.open(display);
+        sheet.open(frame);
+    }
+
+    public boolean shutdown(boolean optional) {
+    	frame.close();
+        return true;
+    }
+
+    public void resume() {
+    }
+
+    public void suspend() {
+    }
+}

Added: incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/CheckedListViewTest.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/CheckedListViewTest.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/CheckedListViewTest.java (added)
+++ incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/CheckedListViewTest.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,53 @@
+/*
+ * 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.wtk.test;
+
+import pivot.collections.Dictionary;
+import pivot.serialization.JSONSerializer;
+import pivot.wtk.Application;
+import pivot.wtk.Display;
+import pivot.wtk.ListView;
+import pivot.wtk.Window;
+
+public class CheckedListViewTest implements Application {
+    private Window window = null;
+
+    public void startup(Display display, Dictionary<String, String> properties)
+        throws Exception {
+    	ListView listView = new ListView(JSONSerializer.parseList("['One', 'Two', 'Three', 'Four']"));
+    	listView.setSelectMode(ListView.SelectMode.MULTI);
+    	listView.setCheckmarksEnabled(true);
+    	listView.setItemChecked(0, true);
+    	listView.setItemChecked(2, true);
+
+    	window = new Window(listView);
+        window.setTitle("Checked List View Test");
+        window.setMaximized(true);
+        window.open(display);
+    }
+
+    public boolean shutdown(boolean optional) {
+        window.close();
+        return true;
+    }
+
+    public void resume() {
+    }
+
+
+    public void suspend() {
+    }
+}

Added: incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/ColorListButtonTest.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/ColorListButtonTest.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/ColorListButtonTest.java (added)
+++ incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/ColorListButtonTest.java Wed Mar 25 23:08:38 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.wtk.test;
+
+import pivot.collections.Dictionary;
+import pivot.wtk.Application;
+import pivot.wtk.Component;
+import pivot.wtk.Display;
+import pivot.wtk.Frame;
+import pivot.wtkx.WTKXSerializer;
+
+public class ColorListButtonTest implements Application {
+    private Frame frame = null;
+
+    public void startup(Display display, Dictionary<String, String> properties)
+        throws Exception {
+        WTKXSerializer wtkxSerializer = new WTKXSerializer();
+        frame = new Frame((Component)wtkxSerializer.readObject(getClass().getResource("color_list_button_test.wtkx")));
+        frame.setTitle("Color List Button Test");
+        frame.setPreferredSize(480, 360);
+        frame.open(display);
+    }
+
+    public boolean shutdown(boolean optional) {
+        frame.close();
+        return true;
+    }
+
+    public void resume() {
+    }
+
+
+    public void suspend() {
+    }
+}

Added: incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/DragDropTest.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/DragDropTest.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/DragDropTest.java (added)
+++ incubator/pivot/branches/1.1/wtk-test/src/pivot/wtk/test/DragDropTest.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,184 @@
+/*
+ * 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.wtk.test;
+
+import java.awt.Color;
+import java.io.IOException;
+
+import pivot.collections.Dictionary;
+import pivot.wtk.Application;
+import pivot.wtk.Component;
+import pivot.wtk.Display;
+import pivot.wtk.DragSource;
+import pivot.wtk.DropAction;
+import pivot.wtk.DropTarget;
+import pivot.wtk.Frame;
+import pivot.wtk.ImageView;
+import pivot.wtk.Point;
+import pivot.wtk.Visual;
+import pivot.wtk.LocalManifest;
+import pivot.wtk.Manifest;
+import pivot.wtk.media.Image;
+
+public class DragDropTest implements Application {
+    private Frame frame1 = new Frame();
+    private Frame frame2 = new Frame();
+
+    private static final Color IMAGE_VIEW_BACKGROUND_COLOR = new Color(0x99, 0x99, 0x99);
+    private static final Color IMAGE_VIEW_DROP_HIGHLIGHT_COLOR = new Color(0xf0, 0xe6, 0x8c);
+
+    public void startup(Display display, Dictionary<String, String> properties) throws Exception {
+        frame1.setTitle("Frame 1");
+        frame1.setPreferredSize(160, 120);
+        frame1.getStyles().put("resizable", false);
+
+        DragSource imageDragSource = new DragSource() {
+            private Image image = null;
+            private Point offset = null;
+            private LocalManifest content = null;
+
+            public boolean beginDrag(Component component, int x, int y) {
+                ImageView imageView = (ImageView)component;
+                image = imageView.getImage();
+
+                if (image != null) {
+                    imageView.setImage((Image)null);
+                    content = new LocalManifest();
+                    content.putImage(image);
+                    offset = new Point(x - (imageView.getWidth() - image.getWidth()) / 2,
+                        y - (imageView.getHeight() - image.getHeight()) / 2);
+                }
+
+                return (image != null);
+            }
+
+            public void endDrag(Component component, DropAction dropAction) {
+                if (dropAction == null) {
+                    ImageView imageView = (ImageView)component;
+                    imageView.setImage(image);
+                }
+
+                image = null;
+                offset = null;
+                content = null;
+            }
+
+            public boolean isNative() {
+                return false;
+            }
+
+            public LocalManifest getContent() {
+                return content;
+            }
+
+            public Visual getRepresentation() {
+                return image;
+            }
+
+            public Point getOffset() {
+                return offset;
+            }
+
+            public int getSupportedDropActions() {
+                return DropAction.MOVE.getMask();
+            }
+        };
+
+        DropTarget imageDropTarget = new DropTarget() {
+            public DropAction dragEnter(Component component, Manifest dragContent,
+                int supportedDropActions, DropAction userDropAction) {
+                DropAction dropAction = null;
+
+                ImageView imageView = (ImageView)component;
+                if (imageView.getImage() == null
+                    && dragContent.containsImage()
+                    && DropAction.MOVE.isSelected(supportedDropActions)) {
+                    dropAction = DropAction.MOVE;
+                    component.getStyles().put("backgroundColor", IMAGE_VIEW_DROP_HIGHLIGHT_COLOR);
+                }
+
+                return dropAction;
+            }
+
+            public void dragExit(Component component) {
+                component.getStyles().put("backgroundColor", IMAGE_VIEW_BACKGROUND_COLOR);
+            }
+
+            public DropAction dragMove(Component component, Manifest dragContent,
+                int supportedDropActions, int x, int y, DropAction userDropAction) {
+                return (dragContent.containsImage() ? DropAction.MOVE : null);
+            }
+
+            public DropAction userDropActionChange(Component component, Manifest dragContent,
+                int supportedDropActions, int x, int y, DropAction userDropAction) {
+                return (dragContent.containsImage() ? DropAction.MOVE : null);
+            }
+
+            public DropAction drop(Component component, Manifest dragContent,
+                int supportedDropActions, int x, int y, DropAction userDropAction) {
+                DropAction dropAction = null;
+
+                if (dragContent.containsImage()) {
+                    ImageView imageView = (ImageView)component;
+                    try {
+                        imageView.setImage(dragContent.getImage());
+                        dropAction = DropAction.MOVE;
+                    } catch(IOException exception) {
+                        System.err.println(exception);
+                    }
+                }
+
+                dragExit(component);
+
+                return dropAction;
+            }
+        };
+
+        ImageView imageView1 = new ImageView();
+        imageView1.setImage(Image.load(getClass().getResource("go-home.png")));
+        imageView1.getStyles().put("backgroundColor", IMAGE_VIEW_BACKGROUND_COLOR);
+        imageView1.setDragSource(imageDragSource);
+        imageView1.setDropTarget(imageDropTarget);
+
+        frame1.setContent(imageView1);
+        frame1.open(display);
+
+        frame2.setTitle("Frame 2");
+        frame2.setPreferredSize(160, 120);
+        frame2.setLocation(180, 0);
+
+        ImageView imageView2 = new ImageView();
+        imageView2.getStyles().put("backgroundColor", IMAGE_VIEW_BACKGROUND_COLOR);
+        imageView2.setDragSource(imageDragSource);
+        imageView2.setDropTarget(imageDropTarget);
+
+        frame2.setContent(imageView2);
+
+        frame2.open(display);
+    }
+
+    public boolean shutdown(boolean optional) {
+        frame1.close();
+        frame2.close();
+        return true;
+    }
+
+    public void suspend() {
+    }
+
+    public void resume() {
+    }
+}