001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/ogcwebservices/wfs/operation/LockFeature.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstr. 19
030: 53115 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042:
043: ---------------------------------------------------------------------------*/
044: package org.deegree.ogcwebservices.wfs.operation;
045:
046: import java.util.ArrayList;
047: import java.util.List;
048: import java.util.Map;
049:
050: import org.deegree.datatypes.QualifiedName;
051: import org.deegree.framework.log.ILogger;
052: import org.deegree.framework.log.LoggerFactory;
053: import org.deegree.i18n.Messages;
054: import org.deegree.model.filterencoding.Filter;
055: import org.deegree.ogcwebservices.InconsistentRequestException;
056: import org.deegree.ogcwebservices.InvalidParameterValueException;
057: import org.deegree.ogcwebservices.OGCWebServiceException;
058: import org.w3c.dom.Element;
059:
060: /**
061: * Represents a <code>LockFeature</code> request to a web feature service.
062: * <p>
063: * Web connections are inherently stateless. Unfortunately, this means that the semantics of
064: * serializable transactions are not preserved. To understand the issue consider an UPDATE
065: * operation.
066: * <p>
067: * The client fetches a feature instance. The feature is then modified on the client side, and
068: * submitted back to the database, via a Transaction request for update. Serializability is lost
069: * since there is nothing to guarantee that while the feature was being modified on the client side,
070: * another client did not come along and update that same feature in the database.
071: * <p>
072: * One way to ensure serializability is to require that access to data be done in a mutually
073: * exclusive manner; that is while one transaction accesses a data item, no other transaction can
074: * modify the same data item. This can be accomplished by using locks that control access to the
075: * data.
076: * <p>
077: * The purpose of the LockFeature interface is to expose a long term feature locking mechanism to
078: * ensure consistency. The lock is considered long term because network latency would make feature
079: * locks last relatively longer than native commercial database locks.
080: * <p>
081: * The LockFeature interface is optional and need only be implemented if the underlying datastore
082: * supports (or can be made to support) data locking. In addition, the implementation of locking is
083: * completely opaque to the client.
084: *
085: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
086: * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
087: * @author last edited by: $Author: apoth $
088: *
089: * @version $Revision: 9345 $
090: */
091: public class LockFeature extends AbstractWFSRequest {
092:
093: private static final ILogger LOG = LoggerFactory
094: .getLogger(LockFeature.class);
095:
096: private static final long serialVersionUID = 1407310243527517490L;
097:
098: /** Default value for expiry (in minutes). */
099: public static String DEFAULT_EXPIRY = "5";
100:
101: /** Duration until timeout (in milliseconds). */
102: private long expiry;
103:
104: private ALL_SOME_TYPE lockAction;
105:
106: private List<Lock> locks;
107:
108: /**
109: * Known lock actions.
110: */
111: public static enum ALL_SOME_TYPE {
112:
113: /**
114: * Acquire a lock on all requested feature instances. If some feature instances cannot be
115: * locked, the operation should fail, and no feature instances should remain locked.
116: */
117: ALL,
118:
119: /**
120: * Lock as many of the requested feature instances as possible.
121: */
122: SOME
123: }
124:
125: /**
126: * String value for {@link ALL_SOME_TYPE ALL_SOME_TYPE.ALL}.
127: */
128: public static String LOCK_ACTION_ALL = "ALL";
129:
130: /**
131: * String value for {@link ALL_SOME_TYPE ALL_SOME_TYPE.SOME}.
132: */
133: public static String LOCK_ACTION_SOME = "SOME";
134:
135: /**
136: * Creates a new <code>LockFeature</code> instance from the given parameters.
137: *
138: * @param version
139: * request version
140: * @param id
141: * id of the request
142: * @param handle
143: * handle of the request
144: * @param expiry
145: * the limit on how long the web feature service keeps the lock (in milliseconds)
146: * @param lockAction
147: * method for lock acquisition
148: * @param locks
149: * contained lock operations
150: */
151: LockFeature(String version, String id, String handle, long expiry,
152: ALL_SOME_TYPE lockAction, List<Lock> locks) {
153: super (version, id, handle, null);
154: this .expiry = expiry;
155: this .lockAction = lockAction;
156: this .locks = locks;
157: }
158:
159: /**
160: * Creates a new <code>LockFeature</code> instance from the given parameters.
161: *
162: * @param version
163: * request version
164: * @param id
165: * id of the request
166: * @param handle
167: * handle of the request
168: * @param expiry
169: * the limit on how long the web feature service holds the lock (in milliseconds)
170: * @param lockAction
171: * method for lock acquisition
172: * @param locks
173: * contained lock operations
174: * @return new <code>LockFeature</code> request
175: */
176: public static LockFeature create(String version, String id,
177: String handle, long expiry, ALL_SOME_TYPE lockAction,
178: List<Lock> locks) {
179: return new LockFeature(version, id, handle, expiry, lockAction,
180: locks);
181: }
182:
183: /**
184: * Creates a new <code>LockFeature</code> instance from a document that contains the DOM
185: * representation of the request.
186: *
187: * @param id
188: * of the request
189: * @param root
190: * element that contains the DOM representation of the request
191: * @return new <code>LockFeature</code> request
192: * @throws OGCWebServiceException
193: */
194: public static LockFeature create(String id, Element root)
195: throws OGCWebServiceException {
196: LockFeatureDocument doc = new LockFeatureDocument();
197: doc.setRootElement(root);
198: LockFeature request;
199: try {
200: request = doc.parse(id);
201: } catch (Exception e) {
202: LOG.logError(e.getMessage(), e);
203: throw new OGCWebServiceException("LockFeature", e
204: .getMessage());
205: }
206: return request;
207: }
208:
209: /**
210: * Creates a new <code>LockFeature</code> request from the given parameter map.
211: *
212: * @param kvp
213: * key-value pairs, keys have to be uppercase
214: * @return new <code>LockFeature</code> request
215: * @throws InconsistentRequestException
216: * @throws InvalidParameterValueException
217: */
218: public static LockFeature create(Map<String, String> kvp)
219: throws InconsistentRequestException,
220: InvalidParameterValueException {
221:
222: // SERVICE
223: checkServiceParameter(kvp);
224:
225: // ID (deegree specific)
226: String id = kvp.get("ID");
227:
228: // VERSION
229: String version = checkVersionParameter(kvp);
230:
231: // TYPENAME
232: QualifiedName[] typeNames = extractTypeNames(kvp);
233: if (typeNames == null) {
234: // no TYPENAME parameter -> FEATUREID must be present
235: String featureId = kvp.get("FEATUREID");
236: if (featureId != null) {
237: String msg = Messages
238: .getMessage("WFS_FEATUREID_PARAM_UNSUPPORTED");
239: throw new InvalidParameterValueException(msg);
240: }
241: String msg = Messages
242: .getMessage("WFS_TYPENAME+FID_PARAMS_MISSING");
243: throw new InvalidParameterValueException(msg);
244: }
245:
246: // EXPIRY
247: String expiryString = getParam("EXPIRY", kvp, DEFAULT_EXPIRY);
248: int expiry = 0;
249: try {
250: expiry = Integer.parseInt(expiryString);
251: if (expiry < 1) {
252: throw new NumberFormatException();
253: }
254: } catch (NumberFormatException e) {
255: String msg = Messages
256: .getMessage("WFS_PARAMETER_INVALID_INT",
257: expiryString, "EXPIRY");
258: throw new InvalidParameterValueException(msg);
259: }
260:
261: // LOCKACTION
262: String lockActionString = getParam("LOCKACTION", kvp, "ALL");
263: ALL_SOME_TYPE lockAction = validateLockAction(lockActionString);
264:
265: // BBOX
266: Filter bboxFilter = extractBBOXFilter(kvp);
267:
268: // FILTER (prequisite: TYPENAME)
269: Map<QualifiedName, Filter> filterMap = extractFilters(kvp,
270: typeNames);
271: if (bboxFilter != null && filterMap.size() > 0) {
272: String msg = Messages.getMessage("WFS_BBOX_FILTER_INVALID");
273: throw new InvalidParameterValueException(msg);
274: }
275:
276: // build a Lock instance for each requested feature type (later also for each featureid...)
277: List<Lock> locks = new ArrayList<Lock>(typeNames.length);
278: for (QualifiedName ftName : typeNames) {
279: Filter filter;
280: if (bboxFilter != null) {
281: filter = bboxFilter;
282: } else {
283: filter = filterMap.get(ftName);
284: }
285: locks.add(new Lock(null, ftName, filter));
286: }
287: return new LockFeature(version, id, null, expiry, lockAction,
288: locks);
289: }
290:
291: /**
292: * Returns the limit on how long the web feature service holds the lock in the event that a
293: * transaction is never issued that would release the lock. The expiry limit is specified in
294: * milliseconds.
295: *
296: * @return the limit on how long the web feature service holds the lock (in milliseconds)
297: */
298: public long getExpiry() {
299: return this .expiry;
300: }
301:
302: /**
303: * Returns the mode for lock acquisition.
304: *
305: * @see ALL_SOME_TYPE
306: *
307: * @return the mode for lock acquisition
308: */
309: public ALL_SOME_TYPE getLockAction() {
310: return this .lockAction;
311: }
312:
313: /**
314: * Returns whether this request requires that all features have to be lockable in order to be
315: * performed succesfully.
316: *
317: * @see ALL_SOME_TYPE
318: *
319: * @return true, if all features have to be lockable, false otherwise
320: */
321: public boolean lockAllFeatures() {
322: return this .lockAction == ALL_SOME_TYPE.ALL;
323: }
324:
325: /**
326: * Returns the contained lock operations.
327: *
328: * @return the contained lock operations
329: */
330: public List<Lock> getLocks() {
331: return this .locks;
332: }
333:
334: static ALL_SOME_TYPE validateLockAction(String lockActionString)
335: throws InvalidParameterValueException {
336: ALL_SOME_TYPE lockAction = ALL_SOME_TYPE.ALL;
337: if (LOCK_ACTION_ALL.equals(lockActionString)) {
338: // nothing to do
339: } else if (LOCK_ACTION_SOME.equals(lockActionString)) {
340: lockAction = ALL_SOME_TYPE.SOME;
341: } else {
342: String msg = Messages
343: .getMessage("WFS_LOCKACTION_INVALID",
344: lockActionString, LOCK_ACTION_ALL,
345: LOCK_ACTION_SOME);
346: throw new InvalidParameterValueException("LOCKACTION", msg);
347: }
348: return lockAction;
349: }
350: }
|