ingo@1805: package de.intevation.flys.artifacts;
ingo@1805: 
ingo@3019: import java.io.File;
ingo@3019: 
ingo@1805: import java.util.ArrayList;
ingo@1805: import java.util.List;
ingo@1805: 
ingo@1854: import java.util.regex.Pattern;
ingo@1854: import java.util.regex.Matcher;
ingo@1854: 
ingo@1805: import org.w3c.dom.Document;
ingo@1805: 
ingo@1805: import org.apache.log4j.Logger;
ingo@1805: 
ingo@1854: import org.hibernate.impl.SessionFactoryImpl;
ingo@1854: 
ingo@1805: import com.vividsolutions.jts.geom.Envelope;
ingo@1805: 
ingo@1805: import de.intevation.artifacts.Artifact;
ingo@1805: import de.intevation.artifacts.ArtifactFactory;
ingo@1805: import de.intevation.artifacts.CallMeta;
ingo@1805: 
ingo@3019: import de.intevation.artifacts.common.utils.FileTools;
ingo@3019: 
ingo@1805: import de.intevation.artifactdatabase.data.DefaultStateData;
ingo@1805: import de.intevation.artifactdatabase.state.Facet;
ingo@1805: import de.intevation.artifactdatabase.state.State;
ingo@1805: 
ingo@1805: 
ingo@1854: import de.intevation.flys.backend.SessionFactoryProvider;
ingo@1854: 
ingo@2683: import de.intevation.flys.artifacts.resources.Resources;
ingo@1805: import de.intevation.flys.artifacts.states.DefaultState;
ingo@3302: import de.intevation.flys.artifacts.model.map.WMSDBLayerFacet;
ingo@1854: import de.intevation.flys.utils.FLYSUtils;
ingo@3019: import de.intevation.flys.utils.MapfileGenerator;
ingo@1805: 
ingo@1805: 
ingo@1805: public abstract class WMSDBArtifact extends StaticFLYSArtifact {
ingo@1805: 
ingo@1805:     private static final Logger logger = Logger.getLogger(WMSDBArtifact.class);
ingo@1805: 
ingo@1854:     public static final Pattern DB_URL_PATTERN =
ingo@1854:         Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([a-zA-Z]+)");
sascha@3923: 
ingo@3918:     public static final Pattern DB_PSQL_URL_PATTERN =
ingo@3918:         Pattern.compile("(.*)\\/\\/(.*):([0-9]+)\\/([a-zA-Z0-9]+)");
ingo@1854: 
ingo@1805: 
ingo@1805:     @Override
ingo@1805:     public void setup(
ingo@1805:         String          identifier,
ingo@1805:         ArtifactFactory factory,
ingo@1805:         Object          context,
ingo@1805:         CallMeta        callMeta,
ingo@1805:         Document        data)
ingo@1805:     {
ingo@1805:         logger.debug("WMSDBArtifact.setup");
ingo@1805: 
ingo@1805:         super.setup(identifier, factory, context, callMeta, data);
ingo@1805: 
felix@2741:         String ids = getDatacageIDValue(data);
ingo@1805: 
ingo@1805:         if (ids != null && ids.length() > 0) {
ingo@1805:             addData("ids", new DefaultStateData("ids", null, null, ids));
ingo@1805:         }
ingo@1805:         else {
ingo@1805:             throw new IllegalArgumentException("No attribute 'ids' found!");
ingo@1805:         }
ingo@1805: 
ingo@1805:         List<Facet> fs = new ArrayList<Facet>();
ingo@1805: 
ingo@1805:         WMSDBState state = (WMSDBState) getCurrentState(context);
ingo@1805:         state.computeInit(this, hash(), context, callMeta, fs);
ingo@1805: 
ingo@1805:         if (!fs.isEmpty()) {
ingo@1805:             facets.put(getCurrentStateId(), fs);
ingo@1805:         }
ingo@1805:     }
ingo@1805: 
ingo@1805: 
ingo@1805:     @Override
ingo@1805:     protected void initialize(
ingo@1805:         Artifact artifact,
ingo@1805:         Object   context,
ingo@1805:         CallMeta callMeta)
ingo@1805:     {
ingo@1805:         // do nothing
ingo@1805:     }
ingo@1805: 
ingo@1805: 
ingo@3019:     @Override
ingo@3019:     protected State getState(Object context, String stateID) {
ingo@3019:         return getCurrentState(context);
ingo@3019:     }
ingo@3019: 
ingo@3019: 
ingo@1805:     /**
ingo@1805:      * Get a list containing the one and only State.
ingo@1805:      * @param  context ignored.
ingo@1805:      * @return list with one and only state.
ingo@1805:      */
ingo@1805:     @Override
ingo@1805:     protected List<State> getStates(Object context) {
ingo@1805:         ArrayList<State> states = new ArrayList<State>();
ingo@1805:         states.add(getCurrentState(context));
ingo@1805: 
ingo@1805:         return states;
ingo@1805:     }
ingo@1805: 
ingo@1805: 
ingo@1805: 
ingo@1805:     public static abstract class WMSDBState extends DefaultState {
ingo@1805:         private static final Logger logger = Logger.getLogger(WMSDBState.class);
ingo@1805: 
raimund@2089:         protected FLYSArtifact artifact;
ingo@1805: 
ingo@2672:         protected String name;
ingo@2683:         protected int    riverId;
ingo@2672: 
ingo@2672: 
raimund@2082:         public WMSDBState() {}
raimund@2082: 
raimund@2089:         public WMSDBState(FLYSArtifact artifact) {
ingo@1805:             this.artifact = artifact;
ingo@2672:             this.name     = null;
ingo@2683:             this.riverId  = 0;
ingo@1805:         }
ingo@1805: 
ingo@1805:         @Override
ingo@1805:         public Object computeInit(
ingo@1805:             FLYSArtifact artifact,
ingo@1805:             String       hash,
ingo@1805:             Object       context,
ingo@1805:             CallMeta     meta,
ingo@1805:             List<Facet>  facets
ingo@1805:         ) {
ingo@1805:             logger.debug("WMSDBState.computeInit");
ingo@1805: 
ingo@1805:             String type = getFacetType();
ingo@1805: 
ingo@1805:             WMSDBLayerFacet facet = new WMSDBLayerFacet(
ingo@1805:                 0,
ingo@1805:                 type,
ingo@1805:                 getTitle(meta),
ingo@1805:                 ComputeType.INIT,
ingo@1805:                 getID(), hash,
ingo@1805:                 getUrl());
ingo@1805: 
ingo@1917:             String name = type + "-" + artifact.identifier();
ingo@1805: 
ingo@1805:             facet.addLayer(name);
ingo@1805:             facet.setExtent(getExtent());
ingo@3918:             facet.setOriginalExtent(getExtent(true));
ingo@1805:             facet.setSrid(getSrid());
ingo@1805:             facet.setData(getDataString());
ingo@1805:             facet.setFilter(getFilter());
ingo@1816:             facet.setGeometryType(getGeometryType());
ingo@1854:             facet.setConnection(getConnection());
ingo@1854:             facet.setConnectionType(getConnectionType());
ingo@1876:             facet.setLabelItem(getLabelItem());
ingo@1805: 
ingo@1805:             facets.add(facet);
ingo@1805: 
ingo@1805:             return null;
ingo@1805:         }
ingo@1805: 
ingo@1854:         /**
ingo@1854:          * This method returns a connection string for databases used by
ingo@1854:          * Mapserver's Mapfile.
ingo@1854:          *
ingo@1854:          * @return A connection string for Mapserver.
ingo@1854:          */
ingo@1854:         protected String getConnection() {
ingo@1854:             SessionFactoryImpl sf = (SessionFactoryImpl)
ingo@1854:                 SessionFactoryProvider.getSessionFactory();
ingo@1854: 
ingo@1854:             String user = SessionFactoryProvider.getUser(sf);
ingo@1854:             String pass = SessionFactoryProvider.getPass(sf);
ingo@1854:             String url  = SessionFactoryProvider.getURL(sf);
ingo@1854: 
ingo@1854:             logger.debug("Parse connection url: " + url);
ingo@1854: 
ingo@1854:             Matcher m = DB_URL_PATTERN.matcher(url);
ingo@1854:             if (!m.matches()) {
ingo@3918:                 logger.warn("Could not parse Connection string." +
ingo@3918:                 	"Try to parse PostgreSQL string.");
ingo@3918:                 // maybe this is a PostgreSQL connection...
ingo@3918:                 return getPostgreSQLConnection();
ingo@1854:             }
ingo@1854: 
ingo@1854:             logger.debug("Groups for connection string: " + m.groupCount());
ingo@1854:             int groups = m.groupCount();
ingo@1854: 
sascha@3087:             for (int i = 0; i <= groups; i++) {
ingo@1854:                 logger.debug("Group " + i + ": " + m.group(i));
ingo@1854:             }
ingo@1854: 
ingo@1854:             String connection = null;
ingo@1854: 
ingo@1854:             if (FLYSUtils.isUsingOracle()) {
ingo@1854:                 if (groups < 3) {
ingo@1854:                     logger.warn("Could only partially parse connection string.");
ingo@1854:                     return null;
ingo@1854:                 }
ingo@1854: 
ingo@1854:                 String host = m.group(2);
ingo@1854:                 String port = m.group(3);
ingo@1854: 
ingo@1854:                 connection = user + "/" + pass + "@" + host;
ingo@1854:             }
ingo@1854:             else {
ingo@1854:                 if (groups < 4) {
ingo@1854:                     logger.warn("Could only partially parse connection string.");
ingo@1854:                     return null;
ingo@1854:                 }
ingo@1854: 
ingo@1854:                 String host = m.group(2);
ingo@1854:                 String port = m.group(3);
ingo@1854:                 String db   = m.group(4);
ingo@1854: 
ingo@1854:                 StringBuilder sb = new StringBuilder();
ingo@1854:                 sb.append("dbname=" + db);
ingo@1854:                 sb.append("host='" + host + "'");
ingo@1854:                 sb.append("port=" + port);
ingo@1854:                 sb.append("password='" + pass + "'");
ingo@1854:                 sb.append("sslmode=disable");
ingo@1854: 
ingo@1854:                 connection = sb.toString();
ingo@1854:             }
ingo@1854: 
ingo@1854:             logger.debug("Created connection: '" + connection + "'");
ingo@1854: 
ingo@1854:             return connection;
ingo@1854:         }
sascha@3923: 
ingo@3918:         protected String getPostgreSQLConnection() {
ingo@3918:             SessionFactoryImpl sf = (SessionFactoryImpl)
ingo@3918:                 SessionFactoryProvider.getSessionFactory();
sascha@3923: 
ingo@3918:             String user = SessionFactoryProvider.getUser(sf);
ingo@3918:             String pass = SessionFactoryProvider.getPass(sf);
ingo@3918:             String url  = SessionFactoryProvider.getURL(sf);
sascha@3923: 
ingo@3918:             Matcher m = DB_PSQL_URL_PATTERN.matcher(url);
ingo@3918:             if (!m.matches()) {
ingo@3918:                 logger.warn("Could not parse PostgreSQL Connection string.");
ingo@3918:                 return null;
ingo@3918:             }
ingo@3918: 
ingo@3918:             int groups = m.groupCount();
ingo@3918:             logger.debug("Groups for PostgreSQL connection string: " + groups);
ingo@3918: 
ingo@3918:             if (logger.isDebugEnabled()) {
ingo@3918:                 for (int i = 0; i <= groups; i++) {
ingo@3918:                     logger.debug("Group " + i + ": " + m.group(i));
ingo@3918:                 }
ingo@3918:             }
ingo@3918: 
ingo@3918:             String connection = null;
ingo@3918: 
ingo@3918:             if (groups < 4) {
ingo@3918:                 logger.warn("Could only partially parse connection string.");
ingo@3918:                 return null;
ingo@3918:             }
ingo@3918: 
ingo@3918:             String host = m.group(2);
ingo@3918:             String port = m.group(3);
ingo@3918:             String db   = m.group(4);
ingo@3918: 
ingo@3918:             StringBuilder sb = new StringBuilder();
ingo@3918:             sb.append("dbname=" + db);
ingo@3918:             sb.append(" host='" + host + "'");
ingo@3918:             sb.append(" port=" + port);
ingo@3918:             sb.append(" user=" + user);
ingo@3918:             sb.append(" password='" + pass + "'");
ingo@3918:             sb.append(" sslmode=disable");
ingo@3918: 
ingo@3918:             connection = sb.toString();
ingo@3918: 
ingo@3918:             logger.debug("Created connection: '" + connection + "'");
ingo@3918: 
ingo@3918:             return connection;
ingo@3918:         }
ingo@1854: 
ingo@1854:         protected String getConnectionType() {
ingo@1854:             return FLYSUtils.isUsingOracle() ? "oraclespatial" : "postgis";
ingo@1854:         }
ingo@1854: 
ingo@1876:         protected String getLabelItem() {
ingo@1876:             return null;
ingo@1876:         }
ingo@1876: 
ingo@2683:         public int getRiverId() {
ingo@2683:             if (riverId == 0) {
ingo@2683:                 String   ids   = artifact.getDataAsString("ids");
ingo@2683:                 String[] parts = ids.split(";");
ingo@2683: 
ingo@2683:                 try {
sascha@3405:                     riverId = Integer.parseInt(parts[0]);
ingo@2683:                 }
ingo@2683:                 catch (NumberFormatException nfe) {
ingo@2683:                     logger.error("Cannot parse river id from '" + parts[0] + "'");
ingo@2683:                 }
ingo@2683:             }
ingo@2683: 
ingo@2683:             return riverId;
ingo@2683:         }
ingo@2683: 
ingo@2672:         /**
ingo@2672:          * Returns the name of the WMS layer. This method extracts the name
ingo@2672:          * from 'ids' data string. It is expected, that the 'ids' string is
ingo@2672:          * seperated by ';' and that the name is placed at index 1.
ingo@2672:          *
ingo@2672:          * @return the name of the WMS layer.
ingo@2672:          */
ingo@2672:         public String getName() {
ingo@2672:             if (name == null) {
ingo@2672:                 String ids = artifact.getDataAsString("ids");
ingo@2672: 
ingo@2683:                 String parts[] = ids != null ? ids.split(";") : null;
ingo@2683: 
ingo@2683:                 if (parts != null && parts.length >= 2) {
ingo@2683:                     name = parts[1];
ingo@2683:                 }
ingo@2672:             }
ingo@2672: 
ingo@2672:             return name;
ingo@2672:         }
ingo@2672: 
ingo@2683: 
ingo@2683:         /**
ingo@2683:          * Returns the name of the layer (returned by getName()) or the layer
ingo@2683:          * type if the name is empty. The layer type is created by an i18n
ingo@2683:          * string of getFacetType().
ingo@2683:          *
ingo@2683:          * @param meta A CallMeta used for i18n.
ingo@2683:          *
ingo@2683:          * @return the name of the layer or its type if name is empty.
ingo@2683:          */
ingo@2683:         protected String getTitle(CallMeta meta) {
ingo@2683:             String name = getName();
ingo@2683: 
ingo@2683:             return name != null && name.length() > 0
ingo@2683:                 ? name
ingo@2683:                 : Resources.getMsg(
ingo@2683:                     meta,
ingo@2683:                     getFacetType(),
ingo@2683:                     getFacetType());
ingo@2683:         }
ingo@2683: 
ingo@2683: 
ingo@3019:         @Override
ingo@3019:         public void endOfLife(Artifact owner, Object context) {
ingo@3019:             logger.info("Destroy WMSDBState: " + getID());
ingo@3019: 
ingo@3019:             String p = FLYSUtils.getXPathString(FLYSUtils.XPATH_SHAPEFILE_DIR);
ingo@3019:             File dir = new File(p, owner.identifier());
ingo@3019: 
ingo@3019:             if (dir != null && dir.exists()) {
ingo@3019:                 logger.debug("Try to delete directory '" + dir + "'");
ingo@3019: 
ingo@3019:                 FileTools.deleteRecursive(dir);
ingo@3019:                 MapfileGenerator.getInstance().update();
ingo@3019:             }
ingo@3019:         }
sascha@3923: 
ingo@3918:         /**
ingo@3918:          * This method returns the extent of a DB layer in the projection of the
ingo@3918:          * database.
sascha@3923:          *
ingo@3918:          * @return the extent of the DB layer in the projection of the database.
ingo@3918:          */
ingo@3918:         protected Envelope getExtent() {
ingo@3918:             return getExtent(false);
ingo@3918:         }
ingo@3019: 
ingo@3019: 
ingo@1805:         protected abstract String getFacetType();
ingo@1805: 
ingo@1805:         protected abstract String getUrl();
ingo@1805: 
ingo@1805:         protected abstract String getSrid();
ingo@1805: 
ingo@3918:         /**
ingo@3918:          * This method returns the extent of the DB layer. The projection of the
ingo@3918:          * extent depends on the <i>reproject</i> parameter. If reproject is set,
ingo@3918:          * the extent is reprojected into the original projection which is
ingo@3918:          * specified in the configuration. Otherwise, the projection of the
ingo@3918:          * database is used.
sascha@3923:          *
ingo@3918:          * @param reproject True, to reproject the extent into the projection
ingo@3918:          * specified in the configuration.
sascha@3923:          *
ingo@3918:          * @return the extent of the database layer.
ingo@3918:          */
ingo@3918:         protected abstract Envelope getExtent(boolean reproject);
ingo@1805: 
ingo@1805:         protected abstract String getFilter();
ingo@1805: 
ingo@1805:         protected abstract String getDataString();
ingo@1816: 
ingo@1816:         protected abstract String getGeometryType();
ingo@1805:     } // end of WMSDBState
ingo@1805: }
ingo@1805: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :