tim@616: package de.intevation.gnv.state.layer; tim@616: sascha@779: import com.vividsolutions.jts.geom.Geometry; tim@616: tim@649: import com.vividsolutions.jts.io.ParseException; tim@649: import com.vividsolutions.jts.io.WKTReader; tim@649: tim@616: import de.intevation.artifactdatabase.Config; tim@616: import de.intevation.artifactdatabase.XMLUtils; sascha@779: tim@616: import de.intevation.artifacts.ArtifactNamespaceContext; tim@616: import de.intevation.artifacts.CallContext; sascha@779: tim@649: import de.intevation.gnv.artifacts.context.GNVArtifactContext; sascha@779: tim@616: import de.intevation.gnv.geobackend.base.Result; sascha@779: tim@649: import de.intevation.gnv.geobackend.base.query.QueryExecutor; tim@649: import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory; sascha@779: tim@649: import de.intevation.gnv.geobackend.base.query.exception.QueryException; sascha@779: tim@616: import de.intevation.gnv.state.InputData; tim@616: import de.intevation.gnv.state.OutputStateBase; sascha@779: tim@616: import de.intevation.gnv.state.exception.StateException; sascha@779: tim@723: import de.intevation.gnv.utils.ArtifactXMLUtilities; 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: sascha@779: import java.io.File; sascha@779: import java.io.IOException; sascha@779: import java.io.OutputStream; sascha@779: sascha@779: import java.util.Collection; sascha@779: import java.util.Iterator; sascha@779: sascha@779: import org.apache.log4j.Logger; sascha@779: sascha@779: import org.w3c.dom.Document; sascha@779: import org.w3c.dom.Element; sascha@779: import org.w3c.dom.Node; sascha@779: tim@616: /** sascha@780: * @author Tim Englich 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@655: // TODO: Replace tim@655: public static final String LAYER_MODEL = "layer"; 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@728: private String columnQueryID = null; sascha@778: tim@649: /** tim@649: * The ID for the Value which will hold the Geometrie-Value tim@649: */ tim@649: private String geometryID = null; sascha@778: tim@649: private Boolean shapeFileLock = new Boolean(true); sascha@778: tim@649: private String shapeFilePath; sascha@778: tim@655: private String geometryType = null; sascha@778: tim@724: private String templateID = null; sascha@778: tim@649: public static final String SHAPEFILE_NAME = "data.shp"; sascha@778: tim@616: /** tim@616: * Constructor tim@616: */ tim@616: public LayerOutputState() { tim@616: super(); tim@616: } tim@616: tim@616: /** sascha@778: * @see de.intevation.gnv.state.OutputState#out(org.w3c.dom.Document, sascha@778: * java.util.Collection, java.io.OutputStream, tim@616: * java.lang.String, de.intevation.artifacts.CallContext) tim@616: */ tim@616: public void out(Document format, Collection inputData, tim@616: OutputStream outputStream, String uuid, tim@616: CallContext callContext) throws StateException { sascha@778: 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")) { tim@616: Collection data = this.fetchData(); tim@723: if (data != null && !data.isEmpty()){ tim@723: XMLUtils.toStream(this.getWMS(uuid, callContext, data), tim@723: outputStream); tim@723: }else{ tim@723: this.writeExceptionReport2Stream(outputStream); tim@723: } tim@616: }else if (outputMode.equalsIgnoreCase("zip")){ tim@616: Collection data = this.fetchData(); tim@723: if (data != null && !data.isEmpty()){ tim@723: this.writeZip(uuid, callContext, outputStream, data); tim@723: }else{ tim@723: this.writeExceptionReport2Stream(outputStream); tim@723: } sascha@778: tim@616: } tim@616: } tim@723: tim@723: /** tim@723: * @param outputStream tim@723: */ tim@723: private void writeExceptionReport2Stream(OutputStream outputStream) { tim@723: Document document = XMLUtils.newDocument(); tim@723: new ArtifactXMLUtilities(). tim@723: createExceptionReport("No Data to Export", document); tim@723: XMLUtils.toStream(document,outputStream); tim@723: } sascha@778: sascha@778: tim@649: /** tim@649: * Fetches the Data from the Databasebackend tim@649: * @return tim@649: */ tim@616: protected Collection fetchData(){ tim@616: log.debug("LayerOutputState.fetchData"); tim@616: // TODO PUT ALL in CACHE tim@616: Collection result = this.getData(this.queryID); tim@616: Collection data = null; tim@649: String geometryWKT = null; tim@616: if (result != null){ tim@649: QueryExecutor queryExecutor = QueryExecutorFactory.getInstance() tim@649: .getQueryExecutor(); tim@616: Iterator it = result.iterator(); tim@649: String[] queryValues = null; tim@616: if (it.hasNext()){ tim@616: Result resultValue = it.next(); tim@649: String table = resultValue.getString(0); tim@649: String where = resultValue.getString(1); tim@728: String columns = this.fetchColumns(table); sascha@778: tim@724: templateID = resultValue.getString(2); tim@649: if (this.geometryID != null){ sascha@778: InputData geometryInputData = tim@649: this.inputData.get(this.geometryID); tim@649: if (geometryInputData != null){ sascha@778: tim@649: try { tim@649: Collection geometryData = queryExecutor tim@649: .executeQuery(this.geometryQueryID, tim@649: new String[]{geometryInputData.getValue()}); tim@649: Iterator 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: // TODO: what should happen?? 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@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: } tim@616: } sascha@778: tim@649: try { tim@649: data = queryExecutor.executeQuery(dataQueryID, tim@649: queryValues); tim@649: if (data != null && geometryWKT != null){ tim@649: WKTReader wktReader = new WKTReader(); tim@649: Geometry border = wktReader.read(geometryWKT); sascha@778: tim@649: Iterator dataIt = data.iterator(); tim@649: while (dataIt.hasNext()){ tim@649: // Trim the Geometries using the tim@649: // Geometry if on is available. tim@649: Result current = dataIt.next(); tim@649: String currentWKT = current.getString(0); tim@649: Geometry currentGeometry = null; tim@649: try { tim@649: currentGeometry = wktReader.read(currentWKT); tim@649: } catch (Exception e) { tim@649: log.error("Error parsing Geometry "+ currentWKT); tim@649: log.error(e,e); tim@649: } sascha@778: tim@649: if (currentGeometry != null){ tim@649: Geometry newGeometry = currentGeometry.intersection(border); tim@649: current.addColumnValue(0, newGeometry.toText()); tim@649: } tim@649: } tim@649: } tim@649: } catch (QueryException e) { tim@649: log.error(e,e); tim@649: } catch (ParseException e){ tim@649: log.error(e,e); tim@649: } tim@616: } tim@616: return data; tim@616: } tim@616: 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@728: .getQueryExecutor(); sascha@778: tim@728: Collection 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 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: } sascha@778: tim@728: } catch (QueryException e) { tim@728: log.error(e,e); tim@728: } tim@728: return returnValue; tim@728: } 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@616: } sascha@778: tim@649: protected String writeToShapeFile( tim@649: String uuid, tim@649: Collection data, tim@649: CallContext callContext tim@649: ) { tim@649: File baseDir = shapefileDirectory(callContext); sascha@778: tim@649: File shapeDir = new File(baseDir, uuid); tim@649: boolean success = false; tim@649: boolean createdDir = false; tim@649: tim@649: try { tim@649: synchronized (shapeFileLock) { tim@649: int count = 0; tim@649: while (shapeDir.exists()) { tim@649: shapeDir = new File(baseDir, uuid + "-" + count); tim@649: ++count; tim@649: } tim@649: tim@649: if (!shapeDir.mkdirs()) { sascha@778: log.error("cannot create directory '" tim@649: + shapeDir.getAbsolutePath() + "'"); tim@649: return null; tim@649: } tim@649: createdDir = true; tim@649: } tim@649: tim@649: File shapeFile = new File(shapeDir, SHAPEFILE_NAME); tim@655: if ((geometryType = ShapeFileWriter.writeDataToFile(shapeFile, "data", data)) == null){ tim@649: log.error("writing data into shapefile failed"); tim@649: return null; tim@649: } sascha@778: tim@649: shapeFilePath = shapeDir.getAbsolutePath(); tim@649: success = true; tim@649: tim@649: callContext.afterCall(CallContext.STORE); tim@649: tim@649: return shapeFilePath; tim@649: } tim@649: finally { tim@649: if (!success && createdDir) { tim@649: FileUtils.deleteRecursive(shapeDir); tim@649: } tim@649: } tim@649: } sascha@778: tim@649: protected void writeZip( tim@649: String uuid, tim@649: CallContext callContext, tim@649: OutputStream output, tim@649: Collection data 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 { sascha@778: tim@649: if ((p = writeToShapeFile(uuid, data, callContext)) != 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: tim@649: public String getShapeFilePath() { tim@649: synchronized (shapeFileLock) { tim@649: return shapeFilePath; tim@649: } tim@649: } sascha@778: tim@649: private static File shapefileDirectory(CallContext callContext) { tim@649: // TODO: Refactoring nessessary it should be used only one Shapefilepath tim@649: // for alle Modes. 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: tim@649: /** tim@649: * @see de.intevation.gnv.state.StateBase#endOfLife(java.lang.Object) tim@649: */ tim@649: @Override tim@649: public void endOfLife(Object globalContext) { tim@649: super.endOfLife(globalContext); tim@649: tim@649: // do it in background tim@649: new Thread() { tim@649: public void run() { tim@649: // TODO: Do the un-publishing WMS stuff. tim@649: String path = resetShapeFilePath(); tim@649: tim@649: if (path == null) { tim@649: return; tim@649: } tim@649: tim@649: File dir = new File(path); tim@649: 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: 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@649: public String resetShapeFilePath() { tim@649: synchronized (shapeFileLock) { tim@649: String path = shapeFilePath; tim@649: shapeFilePath = null; tim@655: geometryType = null; tim@724: templateID = null; tim@649: return path; tim@649: } tim@649: } sascha@778: protected Document getWMS(String uuid, sascha@778: CallContext callContext, sascha@778: Collection data) tim@655: throws StateException tim@655: { tim@655: // TODO: Do the real WMS publishing here! tim@655: Document document = XMLUtils.newDocument(); tim@655: tim@655: Element pathElement = document.createElement("path"); tim@655: document.appendChild(pathElement); tim@655: tim@655: String path = getShapeFilePath(); tim@655: tim@655: if (path != null && new File(path).isDirectory()) { tim@655: pathElement.setTextContent(path); tim@655: } tim@655: else { sascha@778: tim@655: if (data != null && tim@655: (path = writeToShapeFile(uuid, data, callContext)) != null) { tim@655: tim@724: String paramType = LAYER_MODEL+"_"+templateID; sascha@778: tim@724: if (!MapfileGenerator.getInstance().templateExists(paramType)){ tim@724: // If the template doesn't exist the Defaulttemplates will be used. tim@724: paramType = LAYER_MODEL+"_"+this.geometryType.toLowerCase(); tim@724: } sascha@778: Document meta = MetaWriter.writeLayerMeta(callContext, uuid, sascha@778: path, paramType, tim@655: this.determineGeometryType()); tim@655: if (meta != null) { tim@655: MapfileGenerator.getInstance().update(); tim@655: return meta; tim@655: } tim@655: tim@655: pathElement.setTextContent(path); tim@655: } tim@655: } tim@655: tim@655: return document; tim@655: } sascha@778: tim@655: private String determineGeometryType(){ sascha@778: tim@655: String returnValue = this.geometryType.toLowerCase(); sascha@778: tim@655: if (returnValue.equalsIgnoreCase("linestring")){ tim@655: returnValue = "Line"; tim@655: } tim@655: return returnValue; tim@655: } tim@616: }