From 6277216053dcbcad12857f5f0f56ae86406031da Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 3 Sep 2008 19:10:47 +0000 Subject: [PATCH] Adding HTTP protocol. Give a vector layer the HTTP protocol to create, read, update, and delete features via HTTP. This can be subclassed by protocols that extend HTTP. Thanks for the collaboration (and great tests) on this elemoine - good working with you guys. r=elemoine,me (closes #1652) git-svn-id: http://svn.openlayers.org/trunk/openlayers@7940 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/behavior-fixed-http-gml.html | 49 ++ lib/OpenLayers.js | 1 + lib/OpenLayers/Protocol/HTTP.js | 454 +++++++++++++++++ tests/Protocol/HTTP.html | 669 ++++++++++++++++++++++++++ tests/list-tests.html | 1 + 5 files changed, 1174 insertions(+) create mode 100644 examples/behavior-fixed-http-gml.html create mode 100644 lib/OpenLayers/Protocol/HTTP.js create mode 100644 tests/Protocol/HTTP.html diff --git a/examples/behavior-fixed-http-gml.html b/examples/behavior-fixed-http-gml.html new file mode 100644 index 0000000000..d5fb898923 --- /dev/null +++ b/examples/behavior-fixed-http-gml.html @@ -0,0 +1,49 @@ + + + OpenLayers Vector Behavior Example + + + + + + +

Vector Behavior Example (Fixed/HTTP/GML)

+

+ Vector layer with a Fixed strategy, HTTP protocol, and GML format. +

+
+
+ The vector layer shown uses the Fixed strategy, the HTTP protocol, + and the GML format. + The Fixed strategy is a simple strategy that fetches features once + and never re-requests new data. + The HTTP protocol makes requests using HTTP verbs. It should be + constructed with a url that corresponds to a collection of features + (a resource on some server). + The GML format is used to serialize features. +
+ + diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 5b890ab7f0..70e91bc64d 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -184,6 +184,7 @@ "OpenLayers/Strategy.js", "OpenLayers/Strategy/Fixed.js", "OpenLayers/Protocol.js", + "OpenLayers/Protocol/HTTP.js", "OpenLayers/Layer/PointTrack.js", "OpenLayers/Layer/GML.js", "OpenLayers/Style.js", diff --git a/lib/OpenLayers/Protocol/HTTP.js b/lib/OpenLayers/Protocol/HTTP.js new file mode 100644 index 0000000000..76efb06486 --- /dev/null +++ b/lib/OpenLayers/Protocol/HTTP.js @@ -0,0 +1,454 @@ +/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Protocol.js + * @requires OpenLayers/Feature/Vector.js + */ + +/** + * Class: OpenLayers.Protocol.HTTP + * A basic HTTP protocol for vector layers. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { + + /** + * Property: url + * {String} - Service URL, read-only, set through the options + * passed to constructor. + */ + url: null, + + /** + * Property: headers + * {Object} - HTTP request headers, read-only, set through the options + * passed to the constructor, + * Example: {'Content-Type': 'plain/text'} + */ + headers: null, + + /** + * Property: params + * {Object} - Parameters of GET requests, read-only, set through the options + * passed to the constructor, + * Example: {'bbox': '5,5,5,5'} + */ + params: null, + + /** + * Property: format + * {} Parser for reading and writing features. + */ + format: null, + + /** + * Property: callback + * {Object} - Function to be called when the , , + * , or operation completes, read-only, + * set through the options passed to the constructor. + */ + callback: null, + + /** + * Property: scope + * {Object} - Callback execution scope, read-only, set through the + * options passed to the constructor. + */ + scope: null, + + /** + * Constructor: OpenLayers.Protocol.HTTP + * A class for giving layers generic HTTP protocol. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + * + * Valid options include: + * url - {String} + * headers - {Object} + * params - {Object} + * format - {} + * callback - {Function} + * scope - {Object} + */ + initialize: function(options) { + this.params = {}; + this.headers = {}; + OpenLayers.Protocol.prototype.initialize.apply(this, arguments); + }, + + /** + * APIMethod: destroy + * Clean up the protocol. + */ + destroy: function() { + this.params = null; + this.headers = null; + OpenLayers.Protocol.prototype.destroy.apply(this); + }, + + /** + * Method: createCallback + * Returns a function that applies the given public method with resp and + * options arguments. + * + * Parameters: + * method - {Function} The method to be applied by the callback. + * response - {} The protocol response object. + * options - {Object} Options sent to the protocol method (read, create, + * update, or delete). + */ + createCallback: function(method, response, options) { + return OpenLayers.Function.bind(function() { + method.apply(this, [response, options]); + }, this); + }, + + /** + * Method: read + * Construct a request for reading new features. + * + * Parameters: + * options - {Object} Optional object for configuring the request. + * This object is modified and should not be reused. + * + * Returns: + * {} A response object, whose "priv" property + * references the HTTP request, this object is also passed to the + * callback function when the request completes, its "features" property + * is then populated with the the features received from the server. + */ + read: function(options) { + options = OpenLayers.Util.applyDefaults(options, this.options); + var resp = new OpenLayers.Protocol.Response({requestType: "read"}); + + resp.priv = OpenLayers.Request.GET({ + url: options.url, + callback: this.createCallback(this.handleRead, resp, options), + params: options.params, + headers: options.headers + }); + + return resp; + }, + + /** + * Method: handleRead + * Individual callbacks are created for read, create and update, should + * a subclass need to override each one separately. + * + * Parameters: + * resp - {} The response object to pass to + * the user callback. + * options - {Object} The user options passed to the read call. + */ + handleRead: function(resp, options) { + this.handleResponse(resp, options); + }, + + /** + * Method: create + * Construct a request for writing newly created features. + * + * Parameters: + * features - {Array({})} or + * {} + * options - {Object} Optional object for configuring the request. + * This object is modified and should not be reused. + * + * Returns: + * {} An + * object, whose "priv" property references the HTTP request, this + * object is also passed to the callback function when the request + * completes, its "features" property is then populated with the + * the features received from the server. + */ + create: function(features, options) { + options = OpenLayers.Util.applyDefaults(options, this.options); + + var resp = new OpenLayers.Protocol.Response({ + reqFeatures: features, + requestType: "create" + }); + + resp.priv = OpenLayers.Request.POST({ + url: options.url, + callback: this.createCallback(this.handleCreate, resp, options), + headers: options.headers, + data: this.format.write(features) + }); + + return resp; + }, + + /** + * Method: handleCreate + * Called the the request issued by is complete. May be overridden + * by subclasses. + * + * Parameters: + * resp - {} The response object to pass to + * any user callback. + * options - {Object} The user options passed to the create call. + */ + handleCreate: function(resp, options) { + this.handleResponse(resp, options); + }, + + /** + * Method: update + * Construct a request updating modified feature. + * + * Parameters: + * feature - {} + * options - {Object} Optional object for configuring the request. + * This object is modified and should not be reused. + * + * Returns: + * {} An + * object, whose "priv" property references the HTTP request, this + * object is also passed to the callback function when the request + * completes, its "features" property is then populated with the + * the feature received from the server. + */ + update: function(feature, options) { + var url = options.url || feature.url || this.options.url; + options = OpenLayers.Util.applyDefaults(options, this.options); + + var resp = new OpenLayers.Protocol.Response({ + reqFeatures: feature, + requestType: "update" + }); + + resp.priv = OpenLayers.Request.PUT({ + url: url, + callback: this.createCallback(this.handleUpdate, resp, options), + headers: options.headers, + data: this.format.write(feature) + }); + + return resp; + }, + + /** + * Method: handleUpdate + * Called the the request issued by is complete. May be overridden + * by subclasses. + * + * Parameters: + * resp - {} The response object to pass to + * any user callback. + * options - {Object} The user options passed to the update call. + */ + handleUpdate: function(resp, options) { + this.handleResponse(resp, options); + }, + + /** + * Method: delete + * Construct a request deleting a removed feature. + * + * Parameters: + * feature - {} + * options - {Object} Optional object for configuring the request. + * This object is modified and should not be reused. + * + * Returns: + * {} An + * object, whose "priv" property references the HTTP request, this + * object is also passed to the callback function when the request + * completes. + */ + "delete": function(feature, options) { + var url = options.url || feature.url || this.options.url; + options = OpenLayers.Util.applyDefaults(options, this.options); + + var resp = new OpenLayers.Protocol.Response({ + reqFeatures: feature, + requestType: "delete" + }); + + resp.priv = OpenLayers.Request.DELETE({ + url: url, + callback: this.createCallback(this.handleDelete, resp, options), + headers: options.headers + }); + + return resp; + }, + + /** + * Method: handleDelete + * Called the the request issued by is complete. May be overridden + * by subclasses. + * + * Parameters: + * resp - {} The response object to pass to + * any user callback. + * options - {Object} The user options passed to the delete call. + */ + handleDelete: function(resp, options) { + this.handleResponse(resp, options); + }, + + /** + * Method: handleResponse + * Called by CRUD specific handlers. + * + * Parameters: + * resp - {} The response object to pass to + * any user callback. + * options - {Object} The user options passed to the create, read, update, + * or delete call. + */ + handleResponse: function(resp, options) { + var request = resp.priv; + if(options.callback) { + if(request.status >= 200 && request.status < 300) { + // success + if(resp.requestType != "delete") { + resp.features = this.parseFeatures(request); + } + resp.code = OpenLayers.Protocol.Response.SUCCESS; + } else { + // failure + resp.code = OpenLayers.Protocol.Response.FAILURE; + } + options.callback.call(options.scope, resp); + } + }, + + /** + * Method: parseFeatures + * Read HTTP response body and return features. + * + * Parameters: + * request - {XMLHttpRequest} The request object + * + * Returns: + * {Array({})} or + * {} Array of features or a single feature. + */ + parseFeatures: function(request) { + var doc = request.responseXML; + if (!doc || !doc.documentElement) { + doc = request.responseText; + } + if (!doc || doc.length <= 0) { + return null; + } + return this.format.read(doc); + }, + + /** + * Method: commit + * Iterate over each feature and take action based on the feature state. + * Possible actions are create, update and delete. + * + * Parameters: + * features - {Array({})} + * options - {Object} Optional object for setting up intermediate commit + * callbacks. + * + * Valid options: + * create - {Object} Optional object to be passed to the method. + * update - {Object} Optional object to be passed to the method. + * delete - {Object} Optional object to be passed to the method. + * callback - {Function} Optional function to be called when the commit + * is complete. + * scope - {Object} Optional object to be set as the scope of the callback. + * + * Returns: + * {Array()} An array of response objects, + * one per request made to the server, each object's "priv" property + * references the corresponding HTTP request. + */ + commit: function(features, options) { + options = OpenLayers.Util.applyDefaults(options, this.options); + var resp = [], nResponses = 0; + + // Divide up features before issuing any requests. This properly + // counts requests in the event that any responses come in before + // all requests have been issued. + var types = {}; + types[OpenLayers.State.INSERT] = []; + types[OpenLayers.State.UPDATE] = []; + types[OpenLayers.State.DELETE] = []; + var feature, list; + for(var i=0, len=features.length; i 0 ? 1 : 0) + + types[OpenLayers.State.UPDATE].length + + types[OpenLayers.State.DELETE].length; + + function callback(response) { + nResponses++; + response.last = (nResponses >= nRequests); + this.callUserCallback(response, options); + } + + // start issuing requests + var queue = types[OpenLayers.State.INSERT]; + if(queue.length > 0) { + resp.push(this.create( + queue, OpenLayers.Util.applyDefaults( + {callback: callback, scope: this}, + options.create || {} // remove || when #1716 is resolved + ) + )); + } + queue = types[OpenLayers.State.UPDATE]; + for(var i=queue.length-1; i>=0; --i) { + resp.push(this.update( + queue[i], OpenLayers.Util.applyDefaults( + {callback: callback, scope: this}, + options.update || {} // remove || when #1716 is resolved + )) + ); + } + queue = types[OpenLayers.State.DELETE]; + for(var i=queue.length-1; i>=0; --i) { + resp.push(this["delete"]( + queue[i], OpenLayers.Util.applyDefaults( + {callback: callback, scope: this}, + options["delete"] || {} // remove || when #1716 is resolved + )) + ); + } + return resp; + }, + + /** + * Method: callUserCallback + * This method is used from within the commit method each time an + * an HTTP response is received from the server, it is responsible + * for calling the user-supplied callbacks. + * + * Parameters: + * resp - {} + * options - {Object} The map of options passed to the commit call. + */ + callUserCallback: function(resp, options) { + var opt = options[resp.requestType]; + if(opt && opt.callback) { + opt.callback.call(opt.scope, resp); + } + if(resp.last && options.callback) { + options.callback.call(options.scope); + } + }, + + CLASS_NAME: "OpenLayers.Protocol.HTTP" +}); diff --git a/tests/Protocol/HTTP.html b/tests/Protocol/HTTP.html new file mode 100644 index 0000000000..56a27b4476 --- /dev/null +++ b/tests/Protocol/HTTP.html @@ -0,0 +1,669 @@ + + + + + + + + diff --git a/tests/list-tests.html b/tests/list-tests.html index 9c68c3dbe0..adf2aaf8e8 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -113,6 +113,7 @@
  • Popup/FramedCloud.html
  • Projection.html
  • Protocol.html
  • +
  • Protocol/HTTP.html
  • Renderer.html
  • Renderer/Canvas.html
  • Renderer/Elements.html