view gnv-artifacts/src/main/java/de/intevation/gnv/state/layer/LayerOutputState.java @ 772:cd64ab8a46a7

Put time to live of an artifact into meta document which is created while wms publishing (issue197). gnv-artifacts/trunk@829 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Fri, 26 Mar 2010 09:21:31 +0000
parents 1614b27dcbfa
children 9a828e5a2390
line wrap: on
line source
/**
 *
 */
package de.intevation.gnv.state.layer;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

import de.intevation.artifactdatabase.Config;
import de.intevation.artifactdatabase.XMLUtils;
import de.intevation.artifacts.ArtifactNamespaceContext;
import de.intevation.artifacts.CallContext;
import de.intevation.gnv.artifacts.context.GNVArtifactContext;
import de.intevation.gnv.geobackend.base.Result;
import de.intevation.gnv.geobackend.base.query.QueryExecutor;
import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
import de.intevation.gnv.geobackend.base.query.exception.QueryException;
import de.intevation.gnv.state.InputData;
import de.intevation.gnv.state.OutputStateBase;
import de.intevation.gnv.state.exception.StateException;
import de.intevation.gnv.utils.ArtifactXMLUtilities;
import de.intevation.gnv.utils.FileUtils;
import de.intevation.gnv.utils.MapfileGenerator;
import de.intevation.gnv.utils.MetaWriter;
import de.intevation.gnv.utils.ShapeFileWriter;

/**
 * @author Tim Englich <tim.englich@intevation.de>
 *
 */
public class LayerOutputState extends OutputStateBase {

    /**
     * the logger, used to log exceptions and additonaly information
     */
    private static Logger log = Logger.getLogger(LayerOutputState.class);
    
    /**
     * The UID of this Class.
     */
    private static final long serialVersionUID = 9180957321704424049L;
    
    // TODO: Replace
    public static final String LAYER_MODEL    = "layer";

    /**
     * The ID for the Query fetching the Layer from the DB
     */
    private String dataQueryID = null; 
    
    /**
     * The ID for the Query fetching the Geometry from the DB
     * which should be used to Clip the Layerdata
     */
    private String geometryQueryID = null;
    
    private String columnQueryID = null;
    
    /**
     * The ID for the Value which will hold the Geometrie-Value
     */
    private String geometryID = null;
    
    private Boolean shapeFileLock = new Boolean(true);
    
    private String shapeFilePath;
    
    private String geometryType = null;
    
    private String templateID = null;
    
    public static final String SHAPEFILE_NAME   = "data.shp";
    
    /**
     * Constructor
     */
    public LayerOutputState() {
        super();
    }

    /**
     * @see de.intevation.gnv.state.OutputState#out(org.w3c.dom.Document, 
     *      java.util.Collection, java.io.OutputStream, 
     *      java.lang.String, de.intevation.artifacts.CallContext)
     */
    public void out(Document format, Collection<InputData> inputData,
                    OutputStream outputStream, String uuid,
                    CallContext callContext) throws StateException {
        
        log.debug("LayerOutputState.out");
        String outputMode = XMLUtils.xpathString(
                format, XPATH_OUTPUT_MODE, ArtifactNamespaceContext.INSTANCE);
        if (outputMode.equalsIgnoreCase("wms")) {
            Collection<Result> data = this.fetchData();
            if (data != null && !data.isEmpty()){
                XMLUtils.toStream(this.getWMS(uuid, callContext, data),
                                  outputStream);
            }else{
                this.writeExceptionReport2Stream(outputStream);
            }
        }else if (outputMode.equalsIgnoreCase("zip")){
            Collection<Result> data = this.fetchData();
            if (data != null && !data.isEmpty()){
                this.writeZip(uuid, callContext, outputStream, data);
            }else{
                this.writeExceptionReport2Stream(outputStream);
            }
            
        }
    }

    /**
     * @param outputStream
     */
    private void writeExceptionReport2Stream(OutputStream outputStream) {
        Document document = XMLUtils.newDocument();
        new ArtifactXMLUtilities().
        createExceptionReport("No Data to Export", document);
        XMLUtils.toStream(document,outputStream);
    }
    
    
    /**
     * Fetches the Data from the Databasebackend
     * @return
     */
    protected Collection<Result> fetchData(){
        log.debug("LayerOutputState.fetchData");
        // TODO PUT ALL in CACHE
        Collection<Result> result = this.getData(this.queryID);
        Collection<Result> data = null;
        String geometryWKT = null;
        if (result != null){
            QueryExecutor queryExecutor = QueryExecutorFactory.getInstance()
                                                              .getQueryExecutor();
            Iterator<Result> it = result.iterator();
            String[] queryValues = null;
            if (it.hasNext()){
                Result resultValue = it.next();
                String table = resultValue.getString(0);
                String where = resultValue.getString(1);
                String columns = this.fetchColumns(table);
                
                templateID = resultValue.getString(2);
                if (this.geometryID != null){
                    InputData geometryInputData = 
                         this.inputData.get(this.geometryID);
                    if (geometryInputData != null){
                        
                        try {
                            Collection<Result> geometryData = queryExecutor
                                                      .executeQuery(this.geometryQueryID,
                                                                    new String[]{geometryInputData.getValue()});
                            Iterator<Result> git = geometryData.iterator();
                            if (git.hasNext()){
                                Result geometryValue = git.next();
                                geometryWKT = geometryValue.getString(0);
                            }
                        } catch (QueryException e) {
                            log.error(e,e);
                            // TODO: what should happen??
                        }
                       queryValues = new String[]{columns,
                                                  table,
                                                  where,
                                                  geometryWKT};
                    }else{
                        //Look into the presetting for an WKT
                        InputData geometryWKTData = this.preSettings != null ? 
                                                    this.preSettings.get("geometry") :
                                                    null ;
                        if (geometryWKTData != null){
                            queryValues = new String[]{columns,
                                                       table,
                                                       where,
                                                       geometryWKTData.getValue()};
                        }else{
                            queryValues = new String[]{columns,table,where};
                        }
                    }
                }else{
                    //Look into the presetting for an WKT
                    InputData geometryWKTData = this.preSettings != null ? 
                                                this.preSettings.get("geometry") :
                                                null ;
                    if (geometryWKTData != null){
                        queryValues = new String[]{columns,
                                                   table,
                                                   where,
                                                   geometryWKTData.getValue()};
                    }else{
                        queryValues = new String[]{columns,table,where};
                    }
                }
            }
            
            try {
                data  = queryExecutor.executeQuery(dataQueryID,
                                                   queryValues);
                if (data != null && geometryWKT != null){
                    WKTReader wktReader = new WKTReader();
                    Geometry border = wktReader.read(geometryWKT);
                    
                    Iterator<Result> dataIt = data.iterator();
                    while (dataIt.hasNext()){
                        // Trim the Geometries using the
                        // Geometry if on is available.
                        Result current = dataIt.next();
                        String currentWKT = current.getString(0);
                        Geometry currentGeometry = null;
                        try {
                            currentGeometry = wktReader.read(currentWKT);
                        } catch (Exception e) {
                            log.error("Error parsing Geometry "+ currentWKT);
                            log.error(e,e);
                        }
                        
                        if (currentGeometry != null){
                            Geometry newGeometry = currentGeometry.intersection(border);
                            current.addColumnValue(0, newGeometry.toText());
                        }
                    }
                }
            } catch (QueryException e) {
                log.error(e,e);
            } catch (ParseException e){
                log.error(e,e);
            }
        }
        return data;
    }

    private String fetchColumns(String tableName){
        String returnValue = null;
        try {
            String[] tables = tableName.toUpperCase().split(",");
            String[] filter = tables[0].split("\\.");
            // Only use the first Table the second one will be ignored.
            QueryExecutor queryExecutor = QueryExecutorFactory.getInstance()
            .getQueryExecutor();
            
            Collection<Result> columnData = queryExecutor.
                                                executeQuery(this.columnQueryID, 
                                                             filter);
            if (columnData != null && !columnData.isEmpty()){
                StringBuffer sb = new StringBuffer();
                synchronized (sb) {
                    Iterator<Result> it = columnData.iterator();
                    while(it.hasNext()){
                        Result current = it.next();
                        sb.append(current.getString(0));
                        if (it.hasNext()){
                            sb.append(" , ");
                        }
                    }
                }
                returnValue = sb.toString();
            }
            
        } catch (QueryException e) {
            log.error(e,e);
        }
        return returnValue;
    }
    @Override
    public void setup(Node configuration) {
        log.debug("LayerOutputState.setup");
        super.setup(configuration);
        this.dataQueryID = Config.getStringXPath(configuration,
                                                 "queryID-layerdata");
        this.geometryID = Config.getStringXPath(configuration,
                                                "inputvalue-geometry");
        this.geometryQueryID =  Config.getStringXPath(configuration,
                                                "queryID-geometry");
        
        this.columnQueryID =   "layer_colums"; //Config.getStringXPath(configuration,
                                               // "queryID-columns");
    }
    
    protected String writeToShapeFile(
                                      String             uuid,
                                      Collection<Result> data,
                                      CallContext        callContext
                                  ) {
        File baseDir = shapefileDirectory(callContext);
        
        File shapeDir = new File(baseDir, uuid);
        boolean success    = false;
        boolean createdDir = false;

        try {
            synchronized (shapeFileLock) {
                int count = 0;
                while (shapeDir.exists()) {
                    shapeDir = new File(baseDir, uuid + "-" + count);
                    ++count;
                }

                if (!shapeDir.mkdirs()) {
                    log.error("cannot create directory '" 
                        + shapeDir.getAbsolutePath() + "'");
                    return null;
                }
                createdDir = true;
            }

            File shapeFile = new File(shapeDir, SHAPEFILE_NAME);
            if ((geometryType = ShapeFileWriter.writeDataToFile(shapeFile, "data", data)) == null){
                log.error("writing data into shapefile failed");
                return null;
            }
            
            shapeFilePath = shapeDir.getAbsolutePath();
            success = true;

            callContext.afterCall(CallContext.STORE);

            return shapeFilePath;
        }
        finally {
            if (!success && createdDir) {
                FileUtils.deleteRecursive(shapeDir);
            }
        }
    }
    
    protected void writeZip(
            String       uuid,
            CallContext  callContext,
            OutputStream output,
            Collection<Result> data
        ) 
        throws StateException
        {
            try {
                String p = getShapeFilePath();
                if (p != null) {
                    File dir = new File(p);
                    if (dir.isDirectory()) {
                        FileUtils.createZipArchive(dir, output);
                    }
                }
                else {
                  
                    if ((p = writeToShapeFile(uuid, data, callContext)) != null) {
                        FileUtils.createZipArchive(new File(p), output);
                    }
                }
            }
            catch (IOException ioe) {
                log.error(ioe.getLocalizedMessage(), ioe);
            }
        }
    
    public String getShapeFilePath() {
        synchronized (shapeFileLock) {
            return shapeFilePath;
        }
    }
    
    private static File shapefileDirectory(CallContext callContext) {
        // TODO: Refactoring nessessary it should be used only one Shapefilepath
        //       for alle Modes. Code was taken from HorizontalCrossSectionMeshOutputState
        GNVArtifactContext context =
            (GNVArtifactContext)callContext.globalContext();
        File dir = (File)context.get(
            GNVArtifactContext.HORIZONTAL_CROSS_SECTION_RESULT_SHAPEFILE_PATH_KEY);
        return dir != null
            ? dir
            : GNVArtifactContext.DEFAULT_HORIZONTAL_CROSS_SECTION_PROFILE_SHAPEFILE_PATH;
    }
    
    /**
     * @see de.intevation.gnv.state.StateBase#endOfLife(java.lang.Object)
     */
    @Override
    public void endOfLife(Object globalContext) {
        super.endOfLife(globalContext);

        // do it in background
        new Thread() {
            public void run() {
                // TODO: Do the un-publishing WMS stuff.
                String path = resetShapeFilePath();

                if (path == null) {
                    return;
                }

                File dir = new File(path);

                for (int i = 0; i < 10; ++i) {
                    if (!dir.exists() || FileUtils.deleteRecursive(dir)) {
                        MapfileGenerator.getInstance().update();
                        return;
                    }

                    try {
                        Thread.sleep(10000L);
                    }
                    catch (InterruptedException ie) {
                    }
                }

                log.error("failed to remove directory '" + path + "'");
            } // run
        }.start();
    }
    
    public String resetShapeFilePath() {
        synchronized (shapeFileLock) {
            String path = shapeFilePath;
            shapeFilePath = null;
            geometryType = null;
            templateID = null;
            return path;
        }
    }
    protected Document getWMS(String uuid, 
                              CallContext callContext, 
                              Collection<Result> data) 
    throws StateException
    {
        // TODO: Do the real WMS publishing here!
        Document document = XMLUtils.newDocument();

        Element pathElement = document.createElement("path");
        document.appendChild(pathElement);

        String path = getShapeFilePath();

        if (path != null && new File(path).isDirectory()) {
            pathElement.setTextContent(path);
        }
        else {
            
            if (data != null &&
                (path = writeToShapeFile(uuid, data, callContext)) != null) {

                String paramType = LAYER_MODEL+"_"+templateID;
                
                if (!MapfileGenerator.getInstance().templateExists(paramType)){
                    // If the template doesn't exist the Defaulttemplates will be used.
                    paramType = LAYER_MODEL+"_"+this.geometryType.toLowerCase();
                }
                Document meta = MetaWriter.writeLayerMeta(callContext, uuid, 
                                                          path, paramType, 
                                                          this.determineGeometryType());
                if (meta != null) {
                    MapfileGenerator.getInstance().update();
                    return meta;
                }

                pathElement.setTextContent(path);
            }
        }

        return document;
    }
    
    private String determineGeometryType(){
        
        String returnValue = this.geometryType.toLowerCase();
        
        if (returnValue.equalsIgnoreCase("linestring")){
            returnValue = "Line";
        }
        return returnValue;
    }
}

http://dive4elements.wald.intevation.org