001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wfs/LockFeatureHandler.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     53177 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    package org.deegree.ogcwebservices.wfs;
044    
045    import java.util.ArrayList;
046    import java.util.HashMap;
047    import java.util.List;
048    import java.util.Map;
049    import java.util.Set;
050    
051    import org.deegree.datatypes.QualifiedName;
052    import org.deegree.framework.log.ILogger;
053    import org.deegree.framework.log.LoggerFactory;
054    import org.deegree.i18n.Messages;
055    import org.deegree.io.datastore.Datastore;
056    import org.deegree.io.datastore.DatastoreException;
057    import org.deegree.io.datastore.FeatureId;
058    import org.deegree.io.datastore.LockManager;
059    import org.deegree.io.datastore.schema.MappedFeatureType;
060    import org.deegree.model.feature.Feature;
061    import org.deegree.model.feature.FeatureCollection;
062    import org.deegree.model.feature.schema.FeatureType;
063    import org.deegree.ogcwebservices.OGCWebServiceException;
064    import org.deegree.ogcwebservices.wfs.operation.FeatureResult;
065    import org.deegree.ogcwebservices.wfs.operation.GetFeature;
066    import org.deegree.ogcwebservices.wfs.operation.GetFeatureWithLock;
067    import org.deegree.ogcwebservices.wfs.operation.Lock;
068    import org.deegree.ogcwebservices.wfs.operation.LockFeature;
069    import org.deegree.ogcwebservices.wfs.operation.LockFeatureResponse;
070    import org.deegree.ogcwebservices.wfs.operation.Query;
071    
072    /**
073     * Handles {@link LockFeature} requests to the {@link WFService}.
074     * 
075     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
076     * @author last edited by: $Author: apoth $
077     * 
078     * @version $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
079     */
080    class LockFeatureHandler {
081    
082        private static final ILogger LOG = LoggerFactory.getLogger( LockFeatureHandler.class );
083    
084        // shared instance to handle GetFeature requests (for GetFeatureWithLock)
085        private GetFeatureHandler getFeatureHandler;
086    
087        private WFService wfs;
088    
089        /**
090         * Creates a new instance of <code>LockFeatureHandler</code>. Only called by the associated
091         * {@link WFService} (once).
092         * 
093         * @param wfs
094         *            associated WFService
095         */
096        LockFeatureHandler( WFService wfs ) {
097            this.wfs = wfs;
098            this.getFeatureHandler = new GetFeatureHandler( wfs );
099        }
100    
101        /**
102         * Handles a {@link LockFeature} request.
103         * 
104         * @param request
105         *            <code>LockFeature</code> request to perform
106         * @return response to the request
107         * @throws OGCWebServiceException
108         */
109        LockFeatureResponse handleRequest( LockFeature request )
110                                throws OGCWebServiceException {
111    
112            LockFeatureResponse response = null;
113    
114            try {
115                List<FeatureId> fidsToLock = new ArrayList<FeatureId>();
116                Map<Datastore, List<Lock>> dsToLocksMap = buildDsToLocksMap( request );
117                for ( Datastore ds : dsToLocksMap.keySet() ) {
118                    fidsToLock.addAll( ds.determineFidsToLock( dsToLocksMap.get( ds ) ) );
119                }
120                org.deegree.io.datastore.Lock lock = LockManager.getInstance().acquireLock( request, fidsToLock );
121                Set<String> lockedFeatures = lock.getLockedFids();
122                String[] lockedFids = new String[lockedFeatures.size()];
123                String[] notLockedFids = new String[fidsToLock.size() - lockedFeatures.size()];
124                int lockedIdx = 0, notLockedIdx = 0;
125                for ( FeatureId fid : fidsToLock ) {
126                    String fidAsString = fid.getAsString();
127                    if ( lockedFeatures.contains( fidAsString ) ) {
128                        lockedFids[lockedIdx++] = fidAsString;
129                    } else {
130                        notLockedFids[notLockedIdx++] = fidAsString;
131                    }
132                }
133                response = new LockFeatureResponse( request, lock.getId(), lockedFids, notLockedFids );
134            } catch ( DatastoreException e ) {
135                LOG.logDebug( e.getMessage(), e );
136                throw new OGCWebServiceException( this.getClass().getName(), e.getMessage() );
137            }
138    
139            return response;
140        }
141    
142        /**
143         * Handles a {@link GetFeatureWithLock} request.
144         * <p>
145         * This is performed using the following strategy:
146         * <ul>
147         * <li>Perform the request as a standard {@link GetFeature} request.</li>
148         * <li>Create a corresponding {@link LockFeature} request from the {@link GetFeatureWithLock}
149         * request.</li>
150         * <li>Set the "lockId" attribute in the result feature collection.</li>
151         * <li>Remove all features from the feature collection that could not be locked.</li>
152         * </ul>
153         * 
154         * @param request
155         *            <code>GetFeatureWithLock</code> request to perform
156         * @return response to the request
157         * @throws OGCWebServiceException
158         */
159        FeatureResult handleRequest( GetFeatureWithLock request )
160                                throws OGCWebServiceException {
161    
162            FeatureResult response = this.getFeatureHandler.handleRequest( request );
163    
164            List<Lock> locks = new ArrayList<Lock>( request.getQuery().length );
165            for ( Query query : request.getQuery() ) {
166                Lock lock = new Lock( null, query.getTypeNames()[0], query.getFilter() );
167                locks.add( lock );
168            }
169            LockFeature lockRequest = LockFeature.create( WFService.VERSION, null, null, request.getExpiry(),
170                                                          request.getLockAction(), locks );
171            LockFeatureResponse lockResponse = handleRequest( lockRequest );
172    
173            // set "lockId" parameter in result feature collection
174            FeatureCollection fc = (FeatureCollection) response.getResponse();
175            fc.setAttribute( "lockId", lockResponse.getLockId() );
176    
177            // remove all features from the result feature collection that could not be locked (and
178            // count removed features)
179            int removed = 0;
180            for ( String notLockedFid : lockResponse.getFeaturesNotLocked() ) {
181                Feature feature = fc.getFeature( notLockedFid );
182                if ( feature != null ) {
183                    fc.remove( feature );
184                    removed++;
185                }
186            }
187    
188            // correct "numberOfFeatures" attribute (and make it work even for resultType=HITS)
189            int before = Integer.parseInt( fc.getAttribute( "numberOfFeatures" ) );
190            fc.setAttribute( "numberOfFeatures", "" + ( before - removed ) );
191    
192            return response;
193        }
194    
195        /**
196         * Groups all {@link Lock}s contained in the given {@link LockFeature} request by the
197         * responsible {@link Datastore}, i.e. that serves the {@link FeatureType} to be locked.
198         * 
199         * @param request
200         * @return keys: responsible <code>Datastore</code>, values: List of <code>Lock</code>s
201         * @throws OGCWebServiceException
202         */
203        private Map<Datastore, List<Lock>> buildDsToLocksMap( LockFeature request )
204                                throws OGCWebServiceException {
205    
206            Map<Datastore, List<Lock>> dsToLocksMap = new HashMap<Datastore, List<Lock>>();
207            List<Lock> locks = request.getLocks();
208    
209            for ( Lock lock : locks ) {
210                QualifiedName ftName = lock.getTypeName();
211                MappedFeatureType ft = this.wfs.getMappedFeatureType( ftName );
212    
213                if ( ft == null ) {
214                    String msg = Messages.getMessage( "WFS_FEATURE_TYPE_UNKNOWN", ftName );
215                    throw new OGCWebServiceException( this.getClass().getName(), msg );
216                }
217                if ( ft.isAbstract() ) {
218                    String msg = Messages.getMessage( "WFS_FEATURE_TYPE_ABSTRACT", ftName );
219                    throw new OGCWebServiceException( this.getClass().getName(), msg );
220                }
221                if ( !ft.isVisible() ) {
222                    String msg = Messages.getMessage( "WFS_FEATURE_TYPE_INVISIBLE", ftName );
223                    throw new OGCWebServiceException( this.getClass().getName(), msg );
224                }
225    
226                Datastore ds = ft.getGMLSchema().getDatastore();
227                List<Lock> dsLocks = dsToLocksMap.get( ds );
228                if ( dsLocks == null ) {
229                    dsLocks = new ArrayList<Lock>();
230                    dsToLocksMap.put( ds, dsLocks );
231                }
232                dsLocks.add( lock );
233            }
234            return dsToLocksMap;
235        }
236    }