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() {
+ }
+}