Package org.vfny.geoserver.wfs.responses

Source Code of org.vfny.geoserver.wfs.responses.TransactionResponse

/* Copyright (c) 2001, 2003 TOPP - www.openplans.org.  All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.vfny.geoserver.wfs.responses;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.geotools.data.DataSourceException;
import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultQuery;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureLocking;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.feature.AttributeType;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureType;
import org.geotools.feature.IllegalAttributeException;
import org.geotools.feature.SchemaException;
import org.geotools.filter.FidFilter;
import org.geotools.filter.Filter;
import org.geotools.filter.FilterFactory;
import org.geotools.validation.Validation;
import org.geotools.validation.ValidationProcessor;
import org.geotools.validation.ValidationResults;
import org.vfny.geoserver.Request;
import org.vfny.geoserver.Response;
import org.vfny.geoserver.ServiceException;
import org.vfny.geoserver.global.Data;
import org.vfny.geoserver.global.FeatureTypeInfo;
import org.vfny.geoserver.global.GeoServer;
import org.vfny.geoserver.global.Service;
import org.vfny.geoserver.global.dto.WFSDTO;
import org.vfny.geoserver.wfs.WfsException;
import org.vfny.geoserver.wfs.requests.DeleteRequest;
import org.vfny.geoserver.wfs.requests.InsertRequest;
import org.vfny.geoserver.wfs.requests.SubTransactionRequest;
import org.vfny.geoserver.wfs.requests.TransactionRequest;
import org.vfny.geoserver.wfs.requests.UpdateRequest;

import com.vividsolutions.jts.geom.Envelope;


/**
* Handles a Transaction request and creates a TransactionResponse string.
*
* @author Chris Holmes, TOPP
* @version $Id: TransactionResponse.java,v 1.32 2004/06/29 11:27:54 sploreg Exp $
*/
public class TransactionResponse implements Response {
    /** Standard logging instance for class */
    private static final Logger LOGGER = Logger.getLogger(
            "org.vfny.geoserver.responses");

    /** Response to be streamed during writeTo */
    private WfsTransResponse response;

    /** Request provided to Execute method */
    private TransactionRequest request;

    /** Geotools2 transaction used for this opperations */
    protected Transaction transaction;

    /**
     * Constructor
     */
    public TransactionResponse() {
        transaction = null;
    }

    public void execute(Request request) throws ServiceException, WfsException {
        if (!(request instanceof TransactionRequest)) {
            throw new WfsException(
                "bad request, expected TransactionRequest, but got " + request);
        }

        if ((request.getWFS().getServiceLevel() & WFSDTO.TRANSACTIONAL) == 0) {
            throw new ServiceException("Transaction support is not enabled");
        }

        //REVISIT: this should maybe integrate with the other exception
        //handlers better - but things that go wrong here should cause
        //transaction exceptions.
        //try {
        execute((TransactionRequest) request);

        //} catch (Throwable thrown) {
        //    throw new WfsTransactionException(thrown);
        //}
    }

    /**
     * Execute Transaction request.
     *
     * <p>
     * The results of this opperation are stored for use by writeTo:
     *
     * <ul>
     * <li>
     * transaction: used by abort & writeTo to commit/rollback
     * </li>
     * <li>
     * request: used for users getHandle information to report errors
     * </li>
     * <li>
     * stores: FeatureStores required for Transaction
     * </li>
     * <li>
     * failures: List of failures produced
     * </li>
     * </ul>
     * </p>
     *
     * <p>
     * Because we are using geotools2 locking facilities our modification will
     * simply fail with IOException if we have not provided proper
     * authorization.
     * </p>
     *
     * <p>
     * The specification allows a WFS to implement PARTIAL sucess if it is
     * unable to rollback all the requested changes.  This implementation is
     * able to offer full Rollback support and will not require the use of
     * PARTIAL success.
     * </p>
     *
     * @param transactionRequest
     *
     * @throws ServiceException DOCUMENT ME!
     * @throws WfsException
     * @throws WfsTransactionException DOCUMENT ME!
     */
    protected void execute(TransactionRequest transactionRequest)
        throws ServiceException, WfsException {
        request = transactionRequest; // preserved toWrite() handle access
        transaction = new DefaultTransaction();
        LOGGER.fine("request is " + request);

        Data catalog = transactionRequest.getWFS().getData();

        WfsTransResponse build = new WfsTransResponse(WfsTransResponse.SUCCESS,
                transactionRequest.getGeoServer().isVerbose());

        //
        // We are going to preprocess our elements,
        // gathering all the FeatureSources we need
        //
        // Map of required FeatureStores by typeName
        Map stores = new HashMap();
       
        // Map of required FeatureStores by typeRef (dataStoreId:typeName)
        // (This will be added to the contents are harmed)
        Map stores2= new HashMap();

        // Gather FeatureStores required by Transaction Elements
        // and configure them with our transaction
        //
        // (I am using element rather than transaction sub request
        // to agree with the spec docs)
        for (int i = 0; i < request.getSubRequestSize(); i++) {
            SubTransactionRequest element = request.getSubRequest(i);


            String typeRef = null;
            String elementName = null;
            FeatureTypeInfo meta = null;
           
            if (element instanceof InsertRequest) {
                // Option 1: Guess FeatureStore based on insert request
                //
                Feature feature = ((InsertRequest) element).getFeatures()
                                   .features().next();
                if (feature != null) {
                    String name = feature.getFeatureType().getTypeName();
                    URI uri = feature.getFeatureType().getNamespace();
                    
                    LOGGER.fine("Locating FeatureSource uri:'"+uri+"' name:'"+name+"'");                                      
                    meta = catalog.getFeatureTypeInfo(name, uri==null?null:uri.toString())//change suggested by DZweirs
               
                    //HACK: The insert request does not get the correct typename,
                    //as we can no longer hack in the prefix since we are using the
                    //real featureType.  So this is the only good place to do the
                    //look-up for the internal typename to use.  We should probably
                    //rethink our use of prefixed internal typenames (cdf:bc_roads),
                    //and have our requests actually use a type uri and type name.
                    //Internally we can keep the prefixes, but if we do that then
                    //this will be less hacky and we'll also be able to read in xml
                    //for real, since the prefix should refer to the uri.
                    //
                    // JG:
                    // Transalation Insert does not have a clue about prefix - this provides the clue
                    element.setTypeName( meta.getNameSpace().getPrefix()+":"+meta.getTypeName() );
                }
                else {
                    LOGGER.finer("Insert was empty - does not need a FeatuerSoruce");
                  continue; // insert is actually empty
                }
            }
            else {
                // Option 2: lookup based on elmentName (assume prefix:typeName)
                typeRef = null; // unknown at this time
                elementName = element.getTypeName();
                if( stores.containsKey( elementName )) {
                    LOGGER.finer("FeatureSource '"+elementName+"' already loaded." );
                    continue;
                }
                LOGGER.fine("Locating FeatureSource '"+elementName+"'...");               
                meta = catalog.getFeatureTypeInfo(elementName);
                element.setTypeName( meta.getNameSpace().getPrefix()+":"+meta.getTypeName() );               
            }           
            typeRef = meta.getDataStoreInfo().getId()+":"+meta.getTypeName();
            elementName = meta.getNameSpace().getPrefix()+":"+meta.getTypeName();
            LOGGER.fine("located FeatureType w/ typeRef '"+typeRef+"' and elementName '"+elementName+"'" );                         
            if (stores.containsKey(elementName)) {
                // typeName already loaded
                continue;
            }
            try {
                FeatureSource source = meta.getFeatureSource();
                if (source instanceof FeatureStore) {
                    FeatureStore store = (FeatureStore) source;
                    store.setTransaction(transaction);
                    stores.put( elementName, source );
                    stores2.put( typeRef, source );
                } else {
                    throw new WfsTransactionException(elementName
                        + " is read-only", element.getHandle(),
                        request.getHandle());
                }
            } catch (IOException ioException) {
                throw new WfsTransactionException(elementName
                    + " is not available:" + ioException,
                    element.getHandle(), request.getHandle());
            }
        }

        // provide authorization for transaction
        //
        String authorizationID = request.getLockId();

        if (authorizationID != null) {
            if ((request.getWFS().getServiceLevel() & WFSDTO.SERVICE_LOCKING) == 0) {
                // could we catch this during the handler, rather than during execution?
                throw new ServiceException("Lock support is not enabled");
            }
            LOGGER.finer("got lockId: " + authorizationID);

            if (!catalog.lockExists(authorizationID)) {
                String mesg = "Attempting to use a lockID that does not exist"
                    + ", it has either expired or was entered wrong.";
                throw new WfsException(mesg);
            }

            try {
                transaction.addAuthorization(authorizationID);
            } catch (IOException ioException) {
                // This is a real failure - not associated with a element
                //
                throw new WfsException("Authorization ID '" + authorizationID
                    + "' not useable", ioException);
            }
        }

        // execute elements in order,
        // recording results as we go
        //
        // I will need to record the damaged area for
        // pre commit validation checks
        //
        Envelope envelope = new Envelope();

        for (int i = 0; i < request.getSubRequestSize(); i++) {
            SubTransactionRequest element = request.getSubRequest(i);
           
            // We expect element name to be of the format prefix:typeName
            // We take care to force the insert element to have this format above.
            //
            String elementName = element.getTypeName();
            String handle = element.getHandle();
            FeatureStore store = (FeatureStore) stores.get(elementName);
            if( store == null ){
              throw new ServiceException( "Could not locate FeatureStore for '"+elementName+"'" );                       
            }
            String typeName = store.getSchema().getTypeName();
           
            if (element instanceof DeleteRequest) {
                if ((request.getWFS().getServiceLevel() & WFSDTO.SERVICE_DELETE) == 0) {
                    // could we catch this during the handler, rather than during execution?
                    throw new ServiceException(
                        "Transaction Delete support is not enabled");
                }
               
                DeleteRequest delete = (DeleteRequest) element;
               
                //do a check for Filter.NONE, the spec specifically does not
                // allow this
                if (delete.getFilter() == Filter.NONE) {
                  throw new ServiceException(
                  "Filter must be supplied for Transaction Delete"
                  );
                }
               
                LOGGER.finer( "Transaction Delete:"+element );
                try {
                    Filter filter = delete.getFilter();

                    Envelope damaged = store.getBounds(new DefaultQuery(
                                delete.getTypeName(), filter));

                    if (damaged == null) {
                        damaged = store.getFeatures(filter).getBounds();
                    }

                    if ((request.getLockId() != null)
                            && store instanceof FeatureLocking
                            && (request.getReleaseAction() == TransactionRequest.SOME)) {
                        FeatureLocking locking = (FeatureLocking) store;

                        // TODO: Revisit Lock/Delete interaction in gt2
                        if (false) {
                            // REVISIT: This is bad - by releasing locks before
                            // we remove features we open ourselves up to the danger
                            // of someone else locking the features we are about to
                            // remove.
                            //
                            // We cannot do it the other way round, as the Features will
                            // not exist
                            //
                            // We cannot grab the fids offline using AUTO_COMMIT
                            // because we may have removed some of them earlier in the
                            // transaction
                            //
                            locking.unLockFeatures(filter);
                            store.removeFeatures(filter);
                        } else {
                            // This a bit better and what should be done, we will
                            // need to rework the gt2 locking api to work with
                            // fids or something
                            //
                            // The only other thing that would work would be
                            // to specify that FeatureLocking is required to
                            // remove locks when removing Features.
                            //
                            // While that sounds like a good idea, it would be
                            // extra work when doing release mode ALL.
                            //
                            DataStore data = store.getDataStore();
                            FilterFactory factory = FilterFactory
                                .createFilterFactory();
                            FeatureWriter writer;                           
                            writer = data.getFeatureWriter(typeName, filter,
                                    transaction);

                            try {
                                while (writer.hasNext()) {
                                    String fid = writer.next().getID();
                                    locking.unLockFeatures(factory
                                        .createFidFilter(fid));
                                    writer.remove();
                                }
                            } finally {
                                writer.close();
                            }

                            store.removeFeatures(filter);
                        }
                    } else {
                        // We don't have to worry about locking right now
                        //
                        store.removeFeatures(filter);
                    }

                    envelope.expandToInclude(damaged);
                } catch (IOException ioException) {
                    throw new WfsTransactionException(ioException.getMessage(),
                        element.getHandle(), request.getHandle());
                }
            }

            if (element instanceof InsertRequest) {
                if ((request.getWFS().getServiceLevel() & WFSDTO.SERVICE_INSERT) == 0) {
                    // could we catch this during the handler, rather than during execution?
                    throw new ServiceException(
                        "Transaction INSERT support is not enabled");
                }
                LOGGER.finer( "Transasction Insert:"+element );
                try {
                    InsertRequest insert = (InsertRequest) element;
                    FeatureCollection collection = insert.getFeatures();

                    FeatureReader reader = DataUtilities.reader(collection);
                    FeatureType schema = store.getSchema();

                    // Need to use the namespace here for the lookup, due to our weird
                    // prefixed internal typenames.  see
                    //   http://jira.codehaus.org/secure/ViewIssue.jspa?key=GEOS-143
                   
                    // Once we get our datastores making features with the correct namespaces
                    // we can do something like this:
                    // FeatureTypeInfo typeInfo = catalog.getFeatureTypeInfo(schema.getTypeName(), schema.getNamespace());
                    // until then (when geos-144 is resolved) we're stuck with:
                    FeatureTypeInfo typeInfo = catalog.getFeatureTypeInfo(element.getTypeName() );

                    // this is possible with the insert hack above.
                    LOGGER.finer("Use featureValidation to check contents of insert" );
                    featureValidation( typeInfo.getDataStoreInfo().getId(), schema, collection );

                    Set fids = store.addFeatures(reader);
                    build.addInsertResult(element.getHandle(), fids);

                    //
                    // Add to validation check envelope                               
                    envelope.expandToInclude(collection.getBounds());
                } catch (IOException ioException) {
                    throw new WfsTransactionException(ioException,
                        element.getHandle(), request.getHandle());
                }
            }

            if (element instanceof UpdateRequest) {
                if ((request.getWFS().getServiceLevel() & WFSDTO.SERVICE_UPDATE) == 0) {
                    // could we catch this during the handler, rather than during execution?
                    throw new ServiceException(
                        "Transaction Update support is not enabled");
                }
                LOGGER.finer( "Transaction Update:"+element);
                try {
                    UpdateRequest update = (UpdateRequest) element;
                    Filter filter = update.getFilter();

                    AttributeType[] types = update.getTypes(store.getSchema());
                    Object[] values = update.getValues();

                    DefaultQuery query = new DefaultQuery(update.getTypeName(),
                            filter);

                    // Pass through data to collect fids and damaged region
                    // for validation
                    //
                    Set fids = new HashSet();
                    LOGGER.finer("Preprocess to remember modification as a set of fids" );                   
                    FeatureReader preprocess = store.getFeatures( filter ).reader();
                    try {
                        while( preprocess.hasNext() ){
                            Feature feature = preprocess.next();
                            fids.add( feature.getID() );
                            envelope.expandToInclude( feature.getBounds() );
                        }
                    } catch (NoSuchElementException e) {
                        throw new ServiceException( "Could not aquire FeatureIDs", e );
                    } catch (IllegalAttributeException e) {
                        throw new ServiceException( "Could not aquire FeatureIDs", e );
                    }
                    finally {
                        preprocess.close();
                    }
                   
                    try{
                      if (types.length == 1) {
                          store.modifyFeatures(types[0], values[0], filter);
                      } else {
                          store.modifyFeatures(types, values, filter);
                      }
                }catch (IOException e//DJB: this is for cite tests.  We should probaby do this for all the exceptions here - throw a transaction FAIL instead of serice exception 
        {
                  //this failed - we want a FAILED not a service exception!
                  build = new WfsTransResponse(WfsTransResponse.FAILED,
                            transactionRequest.getGeoServer().isVerbose());
                   // add in exception details here??
                      build.setMessage(e.getLocalizedMessage());
                  response = build;
                  // DJB: it looks like the transaction is rolled back in writeTo()
                  return;
          }                   
                    if ((request.getLockId() != null)
                            && store instanceof FeatureLocking
                            && (request.getReleaseAction() == TransactionRequest.SOME)) {
                        FeatureLocking locking = (FeatureLocking) store;
                        locking.unLockFeatures(filter);
                    }
                   
                    // Post process - check features for changed boundary and
                    // pass them off to the ValidationProcessor
                    //
                    if( !fids.isEmpty() ) {
                        LOGGER.finer("Post process update for boundary update and featureValidation");
                        FidFilter modified = FilterFactory.createFilterFactory().createFidFilter();
                        modified.addAllFids( fids );
                   
                        FeatureCollection changed = store.getFeatures( modified ).collection();
                        envelope.expandToInclude( changed.getBounds() );
                   
                        FeatureTypeInfo typeInfo = catalog.getFeatureTypeInfo(element.getTypeName());
                        featureValidation(typeInfo.getDataStoreInfo().getId(),store.getSchema(), changed);                   
                    }
                } catch (IOException ioException) {
                    throw new WfsTransactionException(ioException,
                        element.getHandle(), request.getHandle());
                } catch (SchemaException typeException) {
                    throw new WfsTransactionException(typeName
                        + " inconsistent with update:"
                        + typeException.getMessage(), element.getHandle(),
                        request.getHandle());
                }
            }
        }

        // All opperations have worked thus far
        //
        // Time for some global Validation Checks against envelope
        //
        try {
            integrityValidation(stores2, envelope);
        } catch (Exception invalid) {
            throw new WfsTransactionException(invalid);
        }

        // we will commit in the writeTo method
        // after user has got the response
        response = build;
    }
   
    protected void featureValidation(String dsid, FeatureType type,
        FeatureCollection collection)
        throws IOException, WfsTransactionException {
       
        LOGGER.finer("FeatureValidation called on "+dsid+":"+type.getTypeName() );
       
        ValidationProcessor validation = request.getValidationProcessor();
    if (validation == null){
      //This is a bit hackish, as the validation processor should not
      //be null, but confDemo gives us a null processor right now, some
      //thing to do with no test element in the xml files in validation.
      //But I'm taking no validation process to mean that we can't do
      //any validation.  Hopefully this doesn't mess people up?
      //could mess up some validation stuff, but not everyone makes use
      //of that, and I don't want normal transaction stuff messed up. ch
            LOGGER.warning("ValidationProcessor unavailable");
      return;
    }
        final Map failed = new TreeMap();
        ValidationResults results = new ValidationResults() {
                String name;
                String description;
                public void setValidation(Validation validation) {
                    name = validation.getName();
                    description = validation.getDescription();
                }               
                public void error(Feature feature, String message) {
                    LOGGER.warning(name + ": " + message + " (" + description
                        + ")");
                    failed.put(feature.getID(),
                        name + ": " + message + " " + "(" + description + ")");
                }
                public void warning(Feature feature, String message) {
                    LOGGER.warning(name + ": " + message + " (" + description
                        + ")");
                }
            };

        try {
      // HACK: turned the collection into a feature reader for the validation processor
      FeatureReader fr = DataUtilities.reader(collection);
            validation.runFeatureTests(dsid, type, fr, results);
        } catch (Exception badIdea) {
            // ValidationResults should of handled stuff will redesign :-)
            throw new DataSourceException("Validation Failed", badIdea);
        }

        if (failed.isEmpty()) {
            return; // everything worked out
        }

        StringBuffer message = new StringBuffer();

        for (Iterator i = failed.entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            message.append(entry.getKey());
            message.append(" failed test ");
            message.append(entry.getValue());
            message.append("\n");
        }

        throw new WfsTransactionException(message.toString(), "validation");
    }

    protected void integrityValidation(Map stores, Envelope check)
        throws IOException, WfsTransactionException {
        Data catalog = request.getWFS().getData();
        ValidationProcessor validation = request.getValidationProcessor();
        if( validation == null ) {
            LOGGER.warning( "Validation Processor unavaialble" );
            return;
        }
        LOGGER.finer( "Required to validate "+stores.size()+" typeRefs" );
        LOGGER.finer( "within "+check );
        // go through each modified typeName
        // and ask what we need to check
        //
        Set typeRefs = new HashSet();               
        for (Iterator i = stores.keySet().iterator(); i.hasNext();) {
            String typeRef = (String) i.next();
            typeRefs.add( typeRef );
           
            Set dependencies = validation.getDependencies( typeRef );
            LOGGER.finer( "typeRef "+typeRef+" requires "+dependencies);           
            typeRefs.addAll( dependencies );
        }

        // Grab a source for each typeName we need to check
        // Grab from the provided stores - so we check against
        // the transaction
        //
        Map sources = new HashMap();

        for (Iterator i = typeRefs.iterator(); i.hasNext();) {
            String typeRef = (String) i.next();
            LOGGER.finer("Searching for required typeRef: " + typeRef );

            if (stores.containsKey( typeRef )) {
                LOGGER.finer(" found required typeRef: " + typeRef +" (it was already loaded)");               
                sources.put( typeRef, stores.get(typeRef));
            } else {
                // These will be using Transaction.AUTO_COMMIT
                // this is okay as they were not involved in our
                // Transaction...
                LOGGER.finer(" could not find typeRef: " + typeRef +" (we will now load it)");
                String split[] = typeRef.split(":");
                String dataStoreId = split[0];
                String typeName = split[1];
                LOGGER.finer(" going to look for dataStoreId:"+dataStoreId+" and typeName:"+typeName );               
               
                // FeatureTypeInfo meta = catalog.getFeatureTypeInfo(typeName);
                String uri = catalog.getDataStoreInfo( dataStoreId ).getNameSpace().getURI();
                LOGGER.finer(" sorry I mean uri: " + uri +" and typeName:"+typeName );
               
                FeatureTypeInfo meta = catalog.getFeatureTypeInfo( typeName, uri );
                if( meta == null ){
                  throw new IOException( "Could not find typeRef:"+typeRef +" for validation processor" );
                }
                LOGGER.finer(" loaded required typeRef: " + typeRef );               
                sources.put( typeRef, meta.getFeatureSource());                                               
            }
        }
        LOGGER.finer( "Total of "+sources.size()+" featureSource marshalled for testing" );
        final Map failed = new TreeMap();
        ValidationResults results = new ValidationResults() {
                String name;
                String description;

                public void setValidation(Validation validation) {
                    name = validation.getName();
                    description = validation.getDescription();
                }

                public void error(Feature feature, String message) {
                    LOGGER.warning(name + ": " + message + " (" + description
                        + ")");
                    if (feature == null) {
                        failed.put("ALL",
                                name + ": " + message + " " + "(" + description + ")");                       
                    } else {
                    failed.put(feature.getID(),
                        name + ": " + message + " " + "(" + description + ")");
                    }
                }

                public void warning(Feature feature, String message) {
                    LOGGER.warning(name + ": " + message + " (" + description
                        + ")");
                }
            };

        try {
          //should never be null, but confDemo is giving grief, and I
          //don't want transactions to mess up just because validation
          //stuff is messed up. ch
            LOGGER.finer("Runing integrity tests using validation processor ");
          validation.runIntegrityTests(stores.keySet(), sources, check, results);         
        } catch (Exception badIdea) {
            badIdea.printStackTrace();
            // ValidationResults should of handled stuff will redesign :-)
            throw new DataSourceException("Validation Failed", badIdea);
        }
        if (failed.isEmpty()) {
            LOGGER.finer( "All validation tests passed" );           
            return; // everything worked out
        }
        LOGGER.finer( "Validation fail - marshal result for transaction document" );
        StringBuffer message = new StringBuffer();
        for (Iterator i = failed.entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            message.append(entry.getKey());
            message.append(" failed test ");
            message.append(entry.getValue());
            message.append("\n");
        }       
        throw new WfsTransactionException(message.toString(), "validation");
    }

    /**
     * Responce MIME type as define by ServerConig.
     *
     * @param gs DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getContentType(GeoServer gs) {
        return gs.getMimeType();
    }

    public String getContentEncoding() {
        return null;
    }

    /**
     * Writes generated xmlResponse.
     *
     * <p>
     * I have delayed commiting the result until we have returned it to the
     * user, this gives us a chance to rollback if we are not able to provide
     * a response.
     * </p>
     * I could not quite figure out what to about releasing locks. It could be
     * we are supposed to release locks even if the transaction fails, or only
     * if it succeeds.
     *
     * @param out DOCUMENT ME!
     *
     * @throws ServiceException DOCUMENT ME!
     * @throws IOException DOCUMENT ME!
     */
    public void writeTo(OutputStream out) throws ServiceException, IOException {
        if ((transaction == null) || (response == null)) {
            throw new ServiceException("Transaction not executed");
        }

        if (response.status == WfsTransResponse.PARTIAL) {
            throw new ServiceException("Canceling PARTIAL response");
        }

        try {
            Writer writer;

            writer = new OutputStreamWriter(out);
            writer = new BufferedWriter(writer);

            response.writeXmlResponse(writer, request);
            writer.flush();

            switch (response.status) {
            case WfsTransResponse.SUCCESS:
                transaction.commit();

                break;

            case WfsTransResponse.FAILED:
                transaction.rollback();

                break;
            }
        } catch (IOException ioException) {
            transaction.rollback();
            throw ioException;
        } finally {
            transaction.close();
            transaction = null;
        }

        //
        // Lets deal with the locks
        //
        // Q: Why talk to Data you ask
        // A: Only class that knows all the DataStores
        //
        // We really need to ask all DataStores to release/refresh
        // because we may have locked Features with this Authorizations
        // on them, even though we did not refer to them in this transaction.
        //
        // Q: Why here, why now?
        // A: The opperation was a success, and we have completed the opperation
        //
        // We also need to do this if the opperation is not a success,
        // you can find this same code in the abort method
        //
        Data catalog = request.getWFS().getData();

        if (request.getLockId() != null) {
            if (request.getReleaseAction() == TransactionRequest.ALL) {
                catalog.lockRelease(request.getLockId());
            } else if (request.getReleaseAction() == TransactionRequest.SOME) {
                catalog.lockRefresh(request.getLockId());
            }
        }
    }

    /* (non-Javadoc)
     * @see org.vfny.geoserver.responses.Response#abort()
     */
    public void abort(Service gs) {
        if (transaction == null) {
            return; // no transaction to rollback
        }

        try {
            transaction.rollback();
            transaction.close();
        } catch (IOException ioException) {
            // nothing we can do here
            LOGGER.log(Level.SEVERE,
                "Failed trying to rollback a transaction:" + ioException);
        }

        if (request != null) {
            //
            // TODO: Do we need release/refresh during an abort?              
            if (request.getLockId() != null) {
                Data catalog = gs.getData();

                if (request.getReleaseAction() == TransactionRequest.ALL) {
                    catalog.lockRelease(request.getLockId());
                } else if (request.getReleaseAction() == TransactionRequest.SOME) {
                    catalog.lockRefresh(request.getLockId());
                }
            }
        }

        request = null;
        response = null;
    }
}
TOP

Related Classes of org.vfny.geoserver.wfs.responses.TransactionResponse

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.