ingo@1115: /*
ingo@1115:  * Copyright (c) 2010 by Intevation GmbH
ingo@1115:  *
ingo@1115:  * This program is free software under the LGPL (>=v2.1)
ingo@1115:  * Read the file LGPL.txt coming with the software for details
ingo@1115:  * or visit http://www.gnu.org/licenses/ if it does not exist.
ingo@1115:  */
ingo@1115: 
tim@616: package de.intevation.gnv.state.layer;
tim@616: 
tim@799: import java.io.File;
tim@799: import java.io.IOException;
tim@799: import java.io.OutputStream;
tim@859: import java.util.ArrayList;
tim@799: import java.util.Collection;
tim@799: import java.util.Iterator;
tim@799: 
tim@799: import org.apache.log4j.Logger;
tim@799: import org.w3c.dom.Document;
tim@799: import org.w3c.dom.Node;
tim@799: 
tim@1070: import com.vividsolutions.jts.geom.Envelope;
sascha@779: import com.vividsolutions.jts.geom.Geometry;
tim@649: import com.vividsolutions.jts.io.ParseException;
tim@649: import com.vividsolutions.jts.io.WKTReader;
tim@649: 
sascha@1117: import de.intevation.artifacts.common.utils.Config;
sascha@1117: import de.intevation.artifacts.common.utils.XMLUtils;
tim@616: import de.intevation.artifacts.ArtifactNamespaceContext;
tim@616: import de.intevation.artifacts.CallContext;
tim@649: import de.intevation.gnv.artifacts.context.GNVArtifactContext;
tim@616: import de.intevation.gnv.geobackend.base.Result;
tim@649: import de.intevation.gnv.geobackend.base.query.QueryExecutor;
tim@649: import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
tim@649: import de.intevation.gnv.geobackend.base.query.exception.QueryException;
tim@616: import de.intevation.gnv.state.InputData;
tim@616: import de.intevation.gnv.state.OutputStateBase;
tim@616: import de.intevation.gnv.state.exception.StateException;
tim@723: import de.intevation.gnv.utils.ArtifactXMLUtilities;
tim@828: import de.intevation.gnv.utils.ExclusiveExec;
tim@649: import de.intevation.gnv.utils.FileUtils;
tim@649: import de.intevation.gnv.utils.MapfileGenerator;
tim@655: import de.intevation.gnv.utils.MetaWriter;
tim@649: import de.intevation.gnv.utils.ShapeFileWriter;
tim@616: 
tim@616: /**
tim@822:  * This <code>OutputState</code> is used for Layer-Products.
sascha@780:  * @author <a href="mailto:tim.englich@intevation.de">Tim Englich</a>
tim@616:  *
tim@616:  */
tim@616: public class LayerOutputState extends OutputStateBase {
tim@616: 
tim@616:     /**
tim@616:      * the logger, used to log exceptions and additonaly information
tim@616:      */
tim@616:     private static Logger log = Logger.getLogger(LayerOutputState.class);
sascha@778: 
tim@616:     /**
tim@616:      * The UID of this Class.
tim@616:      */
tim@616:     private static final long serialVersionUID = 9180957321704424049L;
sascha@778: 
tim@822:     /**
tim@822:      * The Basename of the Templates for the WMS-Exports
tim@822:      */
tim@655:     public static final String LAYER_MODEL    = "layer";
sascha@835: 
tim@822:     /**
tim@822:      * The Name of the Shapefile which will be generated.
tim@822:      */
tim@859:     public static final String SHAPEFILE_NAME   = "data";
tim@616: 
tim@616:     /**
tim@616:      * The ID for the Query fetching the Layer from the DB
tim@616:      */
sascha@778:     private String dataQueryID = null;
sascha@778: 
tim@649:     /**
tim@649:      * The ID for the Query fetching the Geometry from the DB
tim@649:      * which should be used to Clip the Layerdata
tim@649:      */
tim@649:     private String geometryQueryID = null;
sascha@778: 
tim@822:     /**
sascha@835:      * The ID of the Query for fetching the Columnnames of the Table which
tim@822:      * should put into the Shapefile.
tim@822:      */
tim@728:     private String columnQueryID = null;
sascha@778: 
tim@822:     /**
sascha@835:      * The ID of the Query for fetching the Information which kind of Geometry
tim@822:      * should be put into the Shapefile.
tim@822:      */
tim@799:     private String geometryTypeQueryID = null;
tim@799: 
tim@649:     /**
tim@822:      * The ID for the Value which will hold the Geometry-Value
tim@649:      */
tim@649:     private String geometryID = null;
sascha@778: 
tim@822:     /**
tim@822:      * Flag for synchronized Access of the Shapefile.
tim@822:      */
tim@649:     private Boolean shapeFileLock = new Boolean(true);
sascha@778: 
tim@822:     /**
tim@822:      * The Path where the Shapefile is stored.
tim@822:      */
tim@649:     private String shapeFilePath;
sascha@778: 
tim@822:     /**
tim@616:      * Constructor
tim@616:      */
tim@616:     public LayerOutputState() {
tim@616:         super();
tim@616:     }
tim@616: 
tim@616:     public void out(Document format, Collection<InputData> inputData,
tim@616:                     OutputStream outputStream, String uuid,
tim@616:                     CallContext callContext) throws StateException {
tim@616:         log.debug("LayerOutputState.out");
tim@616:         String outputMode = XMLUtils.xpathString(
tim@616:                 format, XPATH_OUTPUT_MODE, ArtifactNamespaceContext.INSTANCE);
tim@616:         if (outputMode.equalsIgnoreCase("wms")) {
ingo@870: 
ingo@870:             Collection<LayerMetaData> layerMetaData =
tim@859:                                           this.getRequestedLayerMetadata();
tim@859:             if (layerMetaData != null && !layerMetaData.isEmpty()){
tim@799:                 XMLUtils.toStream(this.getWMS(uuid, callContext,
tim@859:                                               layerMetaData,inputData),
tim@723:                                   outputStream);
tim@723:             }else{
tim@723:                 this.writeExceptionReport2Stream(outputStream);
tim@723:             }
tim@616:         }else if (outputMode.equalsIgnoreCase("zip")){
ingo@870:             Collection<LayerMetaData> layerMetaData =
tim@859:                 this.getRequestedLayerMetadata();
tim@859: 
tim@859:             if (layerMetaData != null && !layerMetaData.isEmpty()){
ingo@870:                 this.writeZip(uuid, callContext,
tim@859:                               outputStream, layerMetaData);
tim@723:             }else{
tim@723:                 this.writeExceptionReport2Stream(outputStream);
tim@723:             }
sascha@778: 
tim@616:         }
tim@616:     }
tim@723: 
tim@723:     /**
ingo@813:      * Writes an exception to an output stream.
ingo@813:      *
ingo@813:      * @param outputStream The output stream used to write the exception to.
tim@723:      */
tim@723:     private void writeExceptionReport2Stream(OutputStream outputStream) {
tim@723:         Document document = XMLUtils.newDocument();
ingo@813:         ArtifactXMLUtilities.
tim@723:         createExceptionReport("No Data to Export", document);
tim@723:         XMLUtils.toStream(document,outputStream);
tim@723:     }
sascha@778: 
tim@649:     /**
ingo@870:      * Returns the Metadata for the requested Layers for fetching the data
tim@859:      * of the Layer and generating the Layer and WMS.
tim@859:      * @return the Metadata for the requested Layers
tim@649:      */
tim@859:     private Collection<LayerMetaData> getRequestedLayerMetadata(){
tim@859:         log.debug("LayerOutputState.getRequestedLayerMetadata");
tim@616:         Collection<Result> result = this.getData(this.queryID);
tim@859:         Collection<LayerMetaData> returnValue = null;
tim@616:         if (result != null){
tim@649:             QueryExecutor queryExecutor = QueryExecutorFactory.getInstance()
tim@649:                                                               .getQueryExecutor();
tim@616:             Iterator<Result> it = result.iterator();
tim@859:             returnValue = new ArrayList<LayerMetaData>(result.size());
tim@859:             while (it.hasNext()){
tim@616:                 Result resultValue = it.next();
tim@649:                 String table = resultValue.getString(0);
tim@859:                 String geometryType = this.getGeometryType(table, queryExecutor);
tim@649:                 String where = resultValue.getString(1);
tim@728:                 String columns = this.fetchColumns(table);
tim@859:                 String templateID = resultValue.getString(2);
ingo@1057:                 String layername = resultValue.getString(3);
tim@859:                 String[] queryValues = null;
tim@859:                 String geometryWKT = null;
tim@649:                 if (this.geometryID != null){
sascha@778:                     InputData geometryInputData =
tim@649:                          this.inputData.get(this.geometryID);
tim@649:                     if (geometryInputData != null){
tim@649:                         try {
ingo@870: 
tim@649:                             Collection<Result> geometryData = queryExecutor
tim@649:                                                       .executeQuery(this.geometryQueryID,
tim@649:                                                                     new String[]{geometryInputData.getValue()});
tim@649:                             Iterator<Result> git = geometryData.iterator();
tim@649:                             if (git.hasNext()){
tim@649:                                 Result geometryValue = git.next();
tim@649:                                 geometryWKT = geometryValue.getString(0);
tim@649:                             }
tim@649:                         } catch (QueryException e) {
tim@649:                             log.error(e,e);
tim@649:                         }
tim@728:                        queryValues = new String[]{columns,
tim@728:                                                   table,
tim@649:                                                   where,
tim@649:                                                   geometryWKT};
tim@649:                     }else{
tim@729:                         //Look into the presetting for an WKT
sascha@778:                         InputData geometryWKTData = this.preSettings != null ?
tim@742:                                                     this.preSettings.get("geometry") :
tim@742:                                                     null ;
tim@729:                         if (geometryWKTData != null){
tim@729:                             queryValues = new String[]{columns,
tim@729:                                                        table,
tim@729:                                                        where,
tim@729:                                                        geometryWKTData.getValue()};
tim@1103:                             geometryWKT = geometryWKTData.getValue();
tim@729:                         }else{
tim@729:                             queryValues = new String[]{columns,table,where};
tim@729:                         }
tim@729:                     }
tim@729:                 }else{
tim@729:                     //Look into the presetting for an WKT
sascha@778:                     InputData geometryWKTData = this.preSettings != null ?
tim@742:                                                 this.preSettings.get("geometry") :
tim@742:                                                 null ;
tim@729:                     if (geometryWKTData != null){
tim@729:                         queryValues = new String[]{columns,
tim@729:                                                    table,
tim@729:                                                    where,
tim@729:                                                    geometryWKTData.getValue()};
tim@729:                     }else{
tim@728:                         queryValues = new String[]{columns,table,where};
tim@649:                     }
tim@649:                 }
ingo@1057:                 returnValue.add(new LayerMetaData(
ingo@1057:                     table, geometryType, where, columns,
ingo@1057:                     templateID, queryValues, geometryWKT, layername));
tim@616:             }
tim@859:         }
tim@859:         return returnValue;
tim@859:     }
sascha@778: 
tim@859:     /**
tim@859:      * Fetches the Data from the Databasebackend.
tim@859:      *
tim@859:      * @return the resultdata.
tim@859:      */
tim@1070:     protected Collection<Result> fetchData(LayerMetaData layerMetaData, Envelope mbr){
tim@859:         log.debug("LayerOutputState.fetchData");
tim@859:         Collection<Result> data = null;
tim@859:         QueryExecutor queryExecutor = QueryExecutorFactory.getInstance()
tim@859:                                                            .getQueryExecutor();
tim@859:         try {
tim@859:             data  = queryExecutor.executeQuery(dataQueryID,
tim@859:                                                layerMetaData.getQueryValues());
tim@1070:             if (data != null){
tim@859:                 WKTReader wktReader = new WKTReader();
tim@1070:                 Geometry border = null;
tim@1070:                 if (layerMetaData.getGeometryWKT() != null){
tim@1070:                     border = wktReader.read(layerMetaData.getGeometryWKT());
tim@1070:                 }
tim@859:                 Iterator<Result> dataIt = data.iterator();
tim@859:                 while (dataIt.hasNext()){
tim@859:                     // Trim the Geometries using the
tim@859:                     // Geometry if one is available.
tim@859:                     Result current = dataIt.next();
tim@859:                     String currentWKT = current.getString(0);
tim@859:                     Geometry currentGeometry = null;
tim@859:                     try {
tim@859:                         currentGeometry = wktReader.read(currentWKT);
tim@859:                     } catch (Exception e) {
tim@859:                         log.error("Error parsing Geometry "+ currentWKT);
tim@859:                         log.error(e,e);
tim@859:                     }
tim@859:                     if (currentGeometry != null){
tim@1070:                         if (border != null){
tim@1070:                             currentGeometry = currentGeometry.intersection(border);
tim@1070:                             current.addColumnValue(0, currentGeometry.toText());
tim@1070:                         }
tim@1070:                         if (mbr.isNull()){
tim@1070:                             mbr.init(currentGeometry.getEnvelopeInternal());
tim@1070:                         }else{
tim@1070:                             mbr.expandToInclude(currentGeometry.getEnvelopeInternal());
tim@1070:                         }
tim@649:                     }
tim@649:                 }
tim@649:             }
tim@859:         } catch (QueryException e) {
tim@859:             log.error(e,e);
tim@859:         } catch (ParseException e){
tim@859:             log.error(e,e);
tim@616:         }
tim@616:         return data;
tim@616:     }
tim@616: 
ingo@813: 
ingo@813:     /**
ingo@813:      * This method determines the geometry type on basis of a table name.
ingo@813:      *
ingo@813:      * @param tableName Name of the table in the database.
ingo@813:      * @param queryExecutor The QueryExecutor.
ingo@813:      * @return the geometry type as string (e.g. MultiPolygon, Polygon, etc).
ingo@813:      */
tim@799:     private String getGeometryType(String tableName,
tim@799:                                    QueryExecutor queryExecutor){
tim@799:         String returnValue = null;
tim@799:         String[] tables = tableName.toUpperCase().split(",");
tim@799:         String[] filter = tables[0].split("\\.");
tim@799:         try {
sascha@803:             Collection<Result> result =
tim@799:                 queryExecutor.executeQuery(this.geometryTypeQueryID, filter);
tim@799:             if (result != null && !result.isEmpty())
tim@799:             {
tim@799:                 int geometryCode = result.iterator().next().getInteger(0);
sascha@803:                 if (geometryCode == 11 ||
tim@799:                     geometryCode == 10){
tim@799:                     returnValue = "MultiPolygon";
sascha@803:                 }else if (geometryCode == 9 ||
tim@799:                           geometryCode == 8){
tim@799:                     returnValue = "MultiLineString";
tim@799:                 }else if (geometryCode == 7){
tim@799:                     returnValue = "MultiPoint";
tim@799:                 }else if (geometryCode == 6){
tim@799:                     returnValue = "GeometryCollection";
sascha@803:                 }else if (geometryCode == 5 ||
tim@799:                           geometryCode == 4){
tim@799:                     returnValue = "Polygon";
sascha@803:                 }else if (geometryCode == 3 ||
tim@799:                           geometryCode == 2){
tim@799:                     returnValue = "LineString";
tim@799:                 }else if (geometryCode == 1){
tim@799:                     returnValue = "Point";
tim@799:                 }else if (geometryCode == 0){
tim@799:                     returnValue = "Geometry";
tim@799:                 }
tim@799:             }
tim@799:         } catch (QueryException e) {
tim@799:             log.error(e,e);
tim@799:         }
tim@799:         return returnValue;
tim@799:     }
ingo@813: 
ingo@813: 
ingo@813:     /**
ingo@813:      * Fetch the columns of a specific table.
ingo@813:      *
ingo@813:      * @param tableName The name of the table.
ingo@813:      * @return the columns as string.
ingo@813:      */
tim@728:     private String fetchColumns(String tableName){
tim@728:         String returnValue = null;
tim@728:         try {
tim@756:             String[] tables = tableName.toUpperCase().split(",");
tim@756:             String[] filter = tables[0].split("\\.");
tim@756:             // Only use the first Table the second one will be ignored.
tim@728:             QueryExecutor queryExecutor = QueryExecutorFactory.getInstance()
tim@859:                                                             .getQueryExecutor();
tim@728:             Collection<Result> columnData = queryExecutor.
sascha@778:                                                 executeQuery(this.columnQueryID,
tim@728:                                                              filter);
tim@728:             if (columnData != null && !columnData.isEmpty()){
tim@728:                 StringBuffer sb = new StringBuffer();
tim@728:                 synchronized (sb) {
tim@728:                     Iterator<Result> it = columnData.iterator();
tim@728:                     while(it.hasNext()){
tim@728:                         Result current = it.next();
tim@728:                         sb.append(current.getString(0));
tim@728:                         if (it.hasNext()){
tim@728:                             sb.append(" , ");
tim@728:                         }
tim@728:                     }
tim@728:                 }
tim@728:                 returnValue = sb.toString();
tim@728:             }
tim@728:         } catch (QueryException e) {
tim@728:             log.error(e,e);
tim@728:         }
tim@728:         return returnValue;
tim@728:     }
ingo@813: 
ingo@813: 
tim@616:     @Override
tim@616:     public void setup(Node configuration) {
tim@616:         log.debug("LayerOutputState.setup");
tim@616:         super.setup(configuration);
tim@649:         this.dataQueryID = Config.getStringXPath(configuration,
tim@649:                                                  "queryID-layerdata");
tim@649:         this.geometryID = Config.getStringXPath(configuration,
tim@649:                                                 "inputvalue-geometry");
tim@649:         this.geometryQueryID =  Config.getStringXPath(configuration,
tim@649:                                                 "queryID-geometry");
sascha@778: 
tim@728:         this.columnQueryID =   "layer_colums"; //Config.getStringXPath(configuration,
tim@728:                                                // "queryID-columns");
tim@799:         this.geometryTypeQueryID = "geometry_type";
tim@616:     }
sascha@778: 
ingo@813: 
ingo@813:     /**
tim@822:      * Write the resultdata to shapefiles.
ingo@813:      *
ingo@813:      * @param uuid The UUID of the current artifact.
ingo@813:      * @param data The finalized data used for shapefile creation.
ingo@813:      * @param callContext The CallContext object.
ingo@813:      * @param geometryType The geometry type.
ingo@813:      * @return the shapefile path.
ingo@813:      */
tim@649:     protected String writeToShapeFile(
tim@649:                                       String             uuid,
tim@649:                                       Collection<Result> data,
tim@799:                                       CallContext        callContext,
tim@859:                                       String geometryType,
tim@859:                                       int layerNumber
tim@649:                                   ) {
tim@649:         boolean success    = false;
tim@859:         if (data != null && !data.isEmpty()){
tim@859:             File shapeDir = new File(shapeFilePath);
tim@859:             try {
tim@859:                 File shapeFile = new File(shapeDir, createShapeFileName(layerNumber));
tim@859:                 if (!ShapeFileWriter.writeDataToFile(shapeFile, "data", data,geometryType)){
tim@859:                     log.error("writing data into shapefile failed");
tim@649:                     return null;
tim@649:                 }
tim@859:                 success = true;
tim@859:                 callContext.afterCall(CallContext.STORE);
tim@859:                 return shapeFilePath;
tim@649:             }
tim@859:             finally {
tim@859:                 if (!success) {
tim@859:                     FileUtils.deleteRecursive(shapeDir);
tim@859:                 }
tim@649:             }
tim@859:         }else{
tim@859:             return null;
tim@649:         }
tim@649:     }
sascha@778: 
tim@859:     /**
tim@859:      * Check if the ShapeDir exists and if it exists delete all Contents
tim@859:      * in it. If it not exists the Director will be created.
tim@859:      * @param baseDir the BaseDirectory for all ShapeDirs
tim@859:      * @param uuid the UUID  which is used to create the Directory
ingo@870:      * @return true if the directory exists or could be created.
tim@859:      *         false if the directory could not be created.
tim@859:      */
tim@859:     private boolean createShapeDir(File baseDir, String uuid){
tim@859:         File shapeDir = new File(baseDir, uuid);
tim@859:         boolean createdDir = false;
tim@859:         synchronized (shapeFileLock) {
tim@859:             if (shapeDir.exists()) {
tim@859:                 FileUtils.deleteContent(shapeDir); // TODO Place on getZip and getWMS
tim@859:             }
tim@859:             else if (!shapeDir.mkdirs()) {
tim@859:                 log.error("cannot create directory '"
tim@859:                     + shapeDir.getAbsolutePath() + "'");
tim@859:                 return false;
tim@859:             }
tim@859:             createdDir = true;
tim@859:         }
tim@859:         shapeFilePath = shapeDir.getAbsolutePath();
tim@859:         return createdDir;
tim@859:     }
ingo@813: 
ingo@813:     /**
ingo@813:      * Create a zip archive with the shapefiles of the given shapefiles path and
ingo@813:      * write it to <code>output</code>.
ingo@813:      *
ingo@813:      * @param uuid The UUID of the current artifact.
ingo@813:      * @param callContext The CallContext object.
ingo@813:      * @param output The output stream.
ingo@813:      * @param data The data to be written to shapefile.
ingo@813:      * @param geometryType The geometry type.
ingo@813:      * @throws StateException if an error occured while zipfile creation.
ingo@813:      */
tim@649:     protected void writeZip(
tim@649:             String       uuid,
tim@649:             CallContext  callContext,
tim@649:             OutputStream output,
tim@859:             Collection<LayerMetaData> layerMetaData
sascha@778:         )
tim@649:         throws StateException
tim@649:         {
tim@649:             try {
tim@649:                 String p = getShapeFilePath();
tim@649:                 if (p != null) {
tim@649:                     File dir = new File(p);
tim@649:                     if (dir.isDirectory()) {
tim@649:                         FileUtils.createZipArchive(dir, output);
tim@649:                     }
tim@649:                 }
tim@649:                 else {
tim@859:                     File baseDir = shapefileDirectory(callContext);
tim@859:                     if (!this.createShapeDir(baseDir, uuid)){
tim@859:                         return;
tim@859:                     }
tim@859:                     Iterator<LayerMetaData> it = layerMetaData.iterator();
tim@859:                     int i = 1;
tim@1070:                     Envelope mbr = new Envelope();
tim@859:                     while(it.hasNext()){
tim@859:                         LayerMetaData lmd = it.next();
tim@1070:                         Collection<Result> data = this.fetchData(lmd, mbr);
tim@859:                         p = writeToShapeFile(uuid, data, callContext,lmd.getGeometryType(),i++);
tim@859:                     }
tim@859:                     if (p != null) {
tim@649:                         FileUtils.createZipArchive(new File(p), output);
tim@649:                     }
tim@649:                 }
tim@649:             }
tim@649:             catch (IOException ioe) {
tim@649:                 log.error(ioe.getLocalizedMessage(), ioe);
tim@649:             }
tim@649:         }
sascha@778: 
ingo@813:     /**
ingo@813:      * Returns the shapefile path.
ingo@813:      *
ingo@813:      * @return the shapefile path.
ingo@813:      */
tim@649:     public String getShapeFilePath() {
tim@649:         synchronized (shapeFileLock) {
tim@649:             return shapeFilePath;
tim@649:         }
tim@649:     }
sascha@778: 
ingo@1044: 
ingo@1044:     /**
ingo@1044:      * Set the shapefile path.
ingo@1044:      */
ingo@1044:     public void setShapeFilePath(String shapeFilePath) {
ingo@1044:         synchronized (shapeFileLock) {
ingo@1044:             this.shapeFilePath = shapeFilePath;
ingo@1044:         }
ingo@1044:     }
ingo@1044: 
tim@859:     /**
tim@859:      * Returns the basic-directory where the Shapefiles should be placed in.
tim@859:      * @param callContext the Context of this Call
tim@859:      * @return the Directory where the Shapefiles could be placed in.
tim@859:      *         (Please create an own directory in this dir and not put the
tim@859:      *          Files directly in it)
tim@859:      */
tim@649:     private static File shapefileDirectory(CallContext callContext) {
tim@859:         // Code was taken from HorizontalCrossSectionMeshOutputState
tim@649:         GNVArtifactContext context =
tim@649:             (GNVArtifactContext)callContext.globalContext();
tim@649:         File dir = (File)context.get(
tim@649:             GNVArtifactContext.HORIZONTAL_CROSS_SECTION_RESULT_SHAPEFILE_PATH_KEY);
tim@649:         return dir != null
tim@649:             ? dir
tim@649:             : GNVArtifactContext.DEFAULT_HORIZONTAL_CROSS_SECTION_PROFILE_SHAPEFILE_PATH;
tim@649:     }
sascha@778: 
ingo@813: 
tim@649:     @Override
tim@649:     public void endOfLife(Object globalContext) {
tim@649:         super.endOfLife(globalContext);
tim@649:         // do it in background
tim@649:         new Thread() {
ingo@813:             @Override
tim@649:             public void run() {
tim@649:                 String path = resetShapeFilePath();
tim@649:                 if (path == null) {
tim@649:                     return;
tim@649:                 }
tim@649:                 File dir = new File(path);
tim@649:                 for (int i = 0; i < 10; ++i) {
tim@649:                     if (!dir.exists() || FileUtils.deleteRecursive(dir)) {
tim@649:                         MapfileGenerator.getInstance().update();
tim@649:                         return;
tim@649:                     }
tim@649:                     try {
tim@649:                         Thread.sleep(10000L);
tim@649:                     }
tim@649:                     catch (InterruptedException ie) {
tim@649:                     }
tim@649:                 }
tim@649: 
tim@649:                 log.error("failed to remove directory '" + path + "'");
tim@649:             } // run
tim@649:         }.start();
tim@649:     }
sascha@778: 
tim@822:     /**
tim@822:      * Resets the Settings e.g shapeFilePath and templateID
tim@822:      * @return
tim@822:      */
tim@822:     private String resetShapeFilePath() {
tim@649:         synchronized (shapeFileLock) {
tim@649:             String path = shapeFilePath;
tim@649:             shapeFilePath = null;
tim@649:             return path;
tim@649:         }
tim@649:     }
ingo@813: 
ingo@813: 
ingo@813:     /**
ingo@813:      * Write data to shapefiles and feed a map service with information about
ingo@813:      * these shapefiles. The map service can be queried for displaying
ingo@813:      * corresponding layers as WMS.
ingo@813:      *
ingo@813:      * @param uuid The UUID of the current artifact.
ingo@813:      * @param callContext The CallContext object.
tim@859:      * @param layerMetaData The Metadata which is required to create the
tim@859:      *                      different Layers.
tim@828:      * @param inputData the Parameters which are send by the out-Call.
ingo@813:      * @return a document with some meta information (shapefile path, geometry
ingo@813:      * type, time to live of the current artifact, etc).
ingo@813:      * @throws StateException if an error occured while shapefile writing.
ingo@813:      */
ingo@1044:     protected Document getWMS(
ingo@1044:         String                    uuid,
ingo@1044:         CallContext               callContext,
ingo@1044:         Collection<LayerMetaData> layerMetaData,
ingo@1044:         Collection<InputData>     inputData)
tim@655:     throws StateException
tim@655:     {
ingo@1044:         Document document = XMLUtils.newDocument();
ingo@1044: 
ingo@1044:         File baseDir  = shapefileDirectory(callContext);
ingo@1044:         File shapeDir = new File(baseDir, uuid);
ingo@1044: 
ingo@1044:         boolean success    = false;
ingo@1044:         boolean createdDir = false;
ingo@1044: 
ingo@1044:         try {
ingo@1044:             // create shapefile directory or delete content if it already exists
ingo@1044:             synchronized (shapeFileLock) {
ingo@1044:                 if (shapeDir.exists()) {
ingo@1044:                     FileUtils.deleteContent(shapeDir);
tim@859:                 }
ingo@1044:                 else if (!shapeDir.mkdirs()) {
ingo@1044:                     log.error("cannot create directory '"
ingo@1044:                         + shapeDir.getAbsolutePath() + "'");
ingo@1044:                     return null;
ingo@1044:                 }
ingo@1044:                 setShapeFilePath(shapeDir.getAbsolutePath());
ingo@1044:                 createdDir = true;
tim@859:             }
ingo@1044: 
ingo@1044:             // process data
tim@859:             Iterator<LayerMetaData> it = layerMetaData.iterator();
ingo@1057:             
ingo@1057:             // create meta file
ingo@1057:             Document meta   = MetaWriter.initMeta();
ingo@1057:             MetaWriter.insertAbstractMeta(callContext, meta);            
ingo@1057: 
ingo@1057:             String   path   = getShapeFilePath();
ingo@1057:             String   prefix = getLayerPrefix(inputData);
ingo@1057: 
ingo@1057:             if (prefix == null) {
ingo@1057:                 prefix = uuid;
ingo@1057:             }
ingo@1057: 
tim@859:             int layerNumber = 0;
tim@1070:             Envelope mbr = new Envelope();
tim@859:             while (it.hasNext()){
tim@859:                 LayerMetaData lmd = it.next();
tim@859:                 layerNumber ++;
ingo@1057: 
tim@859:                 String geometryType = lmd.getGeometryType();
ingo@1057:                 String templateId   = lmd.getTemplateID();
ingo@1057: 
tim@859:                 ExclusiveExec.UniqueKey key = ExclusiveExec.INSTANCE.acquire(uuid);
tim@859:                 try{
tim@1070:                     Collection<Result> results = this.fetchData(lmd,mbr);
ingo@1057:                     if (results != null && writeToShapeFile(uuid, results,
ingo@1057:                                                          callContext,
ingo@1057:                                                          geometryType,
ingo@1057:                                                          layerNumber) != null) {
ingo@1057:                         String name   = getLayerName(uuid, layerNumber);
ingo@1057:                         String base   = lmd.getLayertitle();
ingo@1057:                         String title  = getLayerTitle(prefix, base);
ingo@1057:                         String data   = getLayerData(
ingo@1057:                             uuid, createShapeFileName(layerNumber));
ingo@1057:                         String type   = determineGeometryType(geometryType);
ingo@1057:                         String status = "OFF";
ingo@1057:                         String model  = findParameterTitle(
ingo@1057:                             geometryType, templateId);
ingo@1057: 
ingo@1057:                         MetaWriter.insertLayer(
ingo@1057:                             callContext, meta, name, title,
ingo@1057:                             data, model, type, status);
tim@859:                     }
ingo@1044: 
ingo@1044:                     success = true;
ingo@1044: 
tim@859:                     if (meta != null && !it.hasNext()) {
tim@1070:                         MetaWriter.insertMbr(mbr, "EPSG:4326",meta);
ingo@1057:                         MetaWriter.writeMetaFile(path, meta);
tim@859:                         MapfileGenerator.getInstance().update();
ingo@1057:                         return meta;
tim@859:                     }
tim@859:                 }finally{
tim@859:                     ExclusiveExec.INSTANCE.release(key);
tim@859:                 }
tim@859:             }
tim@1070:           
tim@859:             return document;
tim@859:         }
ingo@1044:         finally {
ingo@1044:             if (!success && createdDir) {
ingo@1044:                 FileUtils.deleteRecursive(shapeDir);
ingo@1044:             }
ingo@1044:         }
tim@859:     }
tim@655: 
tim@859:     /**
tim@859:      * Creates the name of the Shapefile
tim@859:      * @param layerNumber the Number of the Layer
tim@859:      * @return the create name of the Shapefile.
tim@859:      */
tim@859:     private String createShapeFileName(int layerNumber) {
tim@859:         return SHAPEFILE_NAME+"_"+layerNumber+".shp";
tim@859:     }
tim@655: 
sascha@778: 
ingo@1057:     protected String getLayerName(String uuid, int idx) {
ingo@1111:         return "GNV_" + uuid + "_" + idx;
ingo@1057:     }
ingo@1057: 
ingo@1057: 
ingo@1057:     protected String getLayerTitle(String prefix, String base) {
ingo@1111:         return "GNV_" + prefix + "_" + base;
ingo@1057:     }
ingo@1057: 
ingo@1057: 
ingo@1057:     protected String getLayerData(String shapeDir, String filename) {
ingo@1057:         File path = new File(shapeDir, filename);
ingo@1057: 
ingo@1057:         return path.toString();
ingo@1057:     }
ingo@1057: 
ingo@1057: 
tim@828:     /**
tim@828:      * Returns the parameterType for the Layer.
tim@828:      * @param geometryType
tim@828:      * @return
tim@828:      */
tim@859:     private String findParameterTitle(String geometryType, String templateID) {
tim@828:         String paramType = LAYER_MODEL+"_"+templateID;
tim@828:         if (!MapfileGenerator.getInstance().templateExists(paramType)){
tim@828:             // If the template doesn't exist the Defaulttemplates will be used.
tim@828:             paramType = LAYER_MODEL+"_"+
tim@828:                         this.determineDefaultTemplateName(geometryType);
tim@828:         }
tim@828:         return paramType;
tim@828:     }
tim@828: 
tim@828:     /**
tim@828:      * Find the title for a wms layer specified by the user.
tim@828:      *
tim@828:      * @param inputData A collection with InputData objects.
tim@828:      * @return the title.
tim@828:      */
ingo@1057:     protected String getLayerPrefix(Collection<InputData> inputData) {
tim@828:         for (InputData data: inputData) {
tim@828:             String name = data.getName();
tim@828:             if (name != null && name.equals("title")) {
tim@828:                 return (String) data.getValue();
tim@828:             }
tim@828:         }
tim@828:         return null;
tim@828:     }
tim@828: 
ingo@813: 
ingo@813:     /**
ingo@813:      * Turns the geometry type into a form used for the templating mechanism
ingo@813:      * while mapfile generation.
sascha@835:      *
ingo@813:      * @param geometryType The original geometry type.
ingo@813:      * @return a valid geometry type fpr the template mechanism.
ingo@813:      */
tim@799:     private String determineGeometryType(String geometryType){
tim@799:         String returnValue = geometryType.toLowerCase();
tim@655:         if (returnValue.equalsIgnoreCase("linestring")){
tim@655:             returnValue = "Line";
tim@799:         }else if (returnValue.equalsIgnoreCase("multilinestring")){
tim@799:             returnValue ="Line";
tim@799:         }else if (returnValue.equalsIgnoreCase("multipolygon")){
tim@799:             returnValue = "Polygon";
tim@799:         }
tim@799:         return returnValue;
tim@799:     }
sascha@803: 
ingo@813: 
ingo@813:     /**
ingo@813:      * Determine the default template name if no special template is existing
ingo@813:      * for this layer.
ingo@813:      *
ingo@813:      * @param geometryType The geometry type.
ingo@813:      * @return a default geometry fitting to the original geometry type.
ingo@813:      */
tim@799:     private String determineDefaultTemplateName(String geometryType){
tim@799:         String returnValue = geometryType.toLowerCase();
tim@799:         if (returnValue.equalsIgnoreCase("multilinestring")){
tim@799:             returnValue ="linestring";
tim@799:         }else if (returnValue.equalsIgnoreCase("multipolygon")){
tim@799:             returnValue = "polygon";
tim@799:         }else if (returnValue.equalsIgnoreCase("multipoint")){
tim@799:             returnValue = "point";
tim@655:         }
tim@655:         return returnValue;
tim@655:     }
tim@616: }
sascha@836: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :