diff options
Diffstat (limited to 'petascope/src/petascope/wcps/server/core/DbMetadataSource.java')
-rw-r--r-- | petascope/src/petascope/wcps/server/core/DbMetadataSource.java | 1174 |
1 files changed, 1174 insertions, 0 deletions
diff --git a/petascope/src/petascope/wcps/server/core/DbMetadataSource.java b/petascope/src/petascope/wcps/server/core/DbMetadataSource.java new file mode 100644 index 0000000..90d6a77 --- /dev/null +++ b/petascope/src/petascope/wcps/server/core/DbMetadataSource.java @@ -0,0 +1,1174 @@ +/* + * This file is part of PetaScope. + * + * PetaScope is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * PetaScope is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with PetaScope. If not, see <http://www.gnu.org/licenses/>. + * + * For more information please see <http://www.PetaScope.org> + * or contact Peter Baumann via <baumann@rasdaman.com>. + * + * Copyright 2009 Jacobs University Bremen, Peter Baumann. + */ +package petascope.wcps.server.core; + +//~--- JDK imports ------------------------------------------------------------ +import petascope.wcps.server.exceptions.ResourceException; +import petascope.wcps.server.exceptions.InvalidWcpsRequestException; +import petascope.wcps.server.exceptions.InvalidMetadataException; +import java.math.BigInteger; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Savepoint; +import java.sql.Statement; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The DbMetadataSource is a IMetadataSource that uses a relational database. It + * keeps a global connection which is reused on future requests, as well as between + * threads. Before each read, the connection is verified to be valid, and + * recreated if necessary. This IMetadataSource is not particularly efficient, + * because it accesses the database at least once for every read. To increase + * efficiency, wrap a CachedMetadataSource around this one. + * + */ +public class DbMetadataSource implements IMetadataSource { + + private static Logger LOG = LoggerFactory.getLogger(DbMetadataSource.class); + + /* Status variables */ + private boolean initializing; + private boolean checkAtInit; + + /* Contents of static tables */ + private Map<Integer, String> axisTypes; + private Map<Integer, String> crss; + private Map<Integer, String> dataTypes; + private Map<Integer, String> interpolationTypes; + private Map<Integer, String> nullResistances; + private Map<String, String> supportedFormats; + + /* Contents of static tables (reversed, for easy access if you + know the something's name and want to find out its id) */ + private Map<String, Integer> revAxisTypes; + private Map<String, Integer> revCrss; + private Map<String, Integer> revDataTypes; + private Map<String, Integer> revInterpolationTypes; + private Map<String, Integer> revNullResistances; + private Map<String, String> revSupportedFormats; // Not used + + /* Database access info */ + private String driver; + private String pass; + private String url; + private String user; + + /* Global database key variables */ + private Connection conn; + private Savepoint savepoint; + private String query; + + public DbMetadataSource(String driver, String url, String user, String pass) throws ResourceException, InvalidMetadataException { + this(driver, url, user, pass, true); + } + + public DbMetadataSource(String driver, String url, String user, String pass, boolean checkAtInit) throws ResourceException, InvalidMetadataException { + try { + this.driver = driver; + Class.forName(driver).newInstance(); + } catch (ClassNotFoundException e) { + throw new ResourceException("Metadata database error: Could not find JDBC driver: " + driver, e); + } catch (InstantiationException e) { + throw new ResourceException("Metadata database error: Could not instantiate JDBC driver: " + driver, e); + } catch (IllegalAccessException e) { + throw new ResourceException("Metadata database error: Access denied to JDBC driver: " + driver, e); + } + + this.driver = driver; + this.url = url; + this.user = user; + this.pass = pass; + Statement s = null; + + try { + + /* Read contents of static metadata tables */ + ensureConnection(); + + axisTypes = new HashMap<Integer, String>(); + revAxisTypes = new HashMap<String, Integer>(); + s = conn.createStatement(); + ResultSet r = s.executeQuery("SELECT id, axisType FROM PS_AxisType"); + + while (r.next()) { + axisTypes.put(r.getInt("id"), r.getString("axisType")); + revAxisTypes.put(r.getString("axisType"), r.getInt("id")); + } + + dataTypes = new HashMap<Integer, String>(); + revDataTypes = new HashMap<String, Integer>(); + r = s.executeQuery("SELECT id, dataType FROM PS_Datatype"); + + while (r.next()) { + dataTypes.put(r.getInt("id"), r.getString("dataType")); + revDataTypes.put(r.getString("dataType"), r.getInt("id")); + } + + interpolationTypes = new HashMap<Integer, String>(); + revInterpolationTypes = new HashMap<String, Integer>(); + r = s.executeQuery("SELECT id, interpolationType FROM PS_InterpolationType"); + + while (r.next()) { + interpolationTypes.put(r.getInt("id"), r.getString("interpolationType")); + revInterpolationTypes.put(r.getString("interpolationType"), r.getInt("id")); + } + + nullResistances = new HashMap<Integer, String>(); + revNullResistances = new HashMap<String, Integer>(); + r = s.executeQuery("SELECT id, nullResistance FROM PS_NullResistance"); + + while (r.next()) { + nullResistances.put(r.getInt("id"), r.getString("nullResistance")); + revNullResistances.put(r.getString("nullResistance"), r.getInt("id")); + } + + crss = new HashMap<Integer, String>(); + revCrss = new HashMap<String, Integer>(); + r = s.executeQuery("SELECT id, name FROM PS_Crs"); + + while (r.next()) { + crss.put(r.getInt("id"), r.getString("name")); + revCrss.put(r.getString("name"), r.getInt("id")); + } + + supportedFormats = new HashMap<String, String>(); + revSupportedFormats = new HashMap<String, String>(); + r = s.executeQuery("SELECT name, mimetype FROM PS_Format"); + + while (r.next()) { + supportedFormats.put(r.getString("name"), r.getString("mimetype")); + revSupportedFormats.put(r.getString("mimetype"), r.getString("name")); + } + + s.close(); + + /* Debug checks for reverse static tables */ +// log(revAxisTypes.toString()); +// log(revCrss.toString()); +// log(revDataTypes.toString()); +// log(revInterpolationTypes.toString()); +// log(revNullResistances.toString()); +// log(revSupportedFormats.toString()); + + /* Check Metadata consistency at startup, if needed */ + this.checkAtInit = checkAtInit; + + if (checkAtInit) { + initializing = true; + boolean coveragesOk = false; + + while (!coveragesOk) { + try { + Iterator<String> coverages = coverages().iterator(); + + while (coverages.hasNext()) { + read(coverages.next()); + } + } catch (InvalidWcpsRequestException ire) { + throw (InvalidMetadataException) ire.getCause(); + } + + coveragesOk = true; + } + + initializing = false; + } + + } catch (SQLException sqle) { + if (s != null) { + try { + s.close(); + } catch (SQLException e) { + } + } + + close(); + + throw new ResourceException("Metadata database error", sqle); + } + + } + + public void close() { + if (conn != null) { + try { + conn.close(); + } catch (SQLException sqle) { + } + conn = null; + } + } + + /* List all available coverages */ + @Override + public Set<String> coverages() throws ResourceException { + Statement s = null; + Set<String> coverages; + + try { + ensureConnection(); + s = conn.createStatement(); + + ResultSet r = s.executeQuery("SELECT name FROM PS_Coverage"); + + coverages = new HashSet<String>(r.getFetchSize()); + + while (r.next()) { + coverages.add(r.getString("name")); + } + + s.close(); + + return coverages; + } catch (SQLException sqle) { + if (s != null) { + try { + s.close(); + } catch (SQLException f) { + } + } + + close(); + + throw new ResourceException("Metadata database error", sqle); + } + + } + + @Override + public String formatToMimetype(String format) { + return supportedFormats.get(format); + } + + @Override + public Metadata read(String coverageName) throws InvalidWcpsRequestException, ResourceException { + LOG.debug("Reading metadata for coverage '{}'", coverageName); + + if ((coverageName == null) || coverageName.equals("")) { + throw new InvalidWcpsRequestException("Cannot retrieve coverage with null or empty name"); + } + + Statement s = null; + + try { + ensureConnection(); + s = conn.createStatement(); + + ResultSet r = s.executeQuery("SELECT id, nullDefault, interpolationTypeDefault, nullResistanceDefault FROM PS_Coverage WHERE name = '" + coverageName + "'"); + + if (!r.next()) { + throw new InvalidWcpsRequestException("Coverage '" + coverageName + "' is not served by this server"); + } + + int coverage = r.getInt("id"); + String nullDefault = r.getString("nullDefault"); + String interpolationTypeDefault = interpolationTypes.get(r.getInt("interpolationTypeDefault")); + String nullResistanceDefault = nullResistances.get(r.getInt("nullResistanceDefault")); + + r = s.executeQuery("SELECT lo, hi FROM PS_CellDomain WHERE coverage = '" + coverage + "' ORDER BY i ASC"); + CellDomainElement X = null, Y = null, cell; + boolean twoDCoverage = true; + List<CellDomainElement> cellDomain = new ArrayList<CellDomainElement>(r.getFetchSize()); + + while (r.next()) { + cell = new CellDomainElement(BigInteger.valueOf(r.getInt("lo")), BigInteger.valueOf(r.getInt("hi"))); + cellDomain.add(cell); + if (X == null) { + X = cell; + } else if (X != null && Y == null) { + Y = cell; + } else { + twoDCoverage = false; + } + } + if (X == null || Y == null) { + twoDCoverage = false; + } + + r = s.executeQuery("SELECT name, type FROM PS_Range WHERE coverage = '" + coverage + "' ORDER BY i ASC"); + List<RangeElement> range = new ArrayList<RangeElement>(r.getFetchSize()); + + while (r.next()) { + range.add(new RangeElement(r.getString("name"), dataTypes.get(r.getInt("type")))); + } + + r = s.executeQuery("SELECT interpolationType, nullResistance FROM PS_InterpolationSet WHERE coverage = '" + coverage + "'"); + Set<InterpolationMethod> interpolationSet = new HashSet<InterpolationMethod>(r.getFetchSize()); + + while (r.next()) { + interpolationSet.add(new InterpolationMethod(interpolationTypes.get(r.getInt("interpolationType")), nullResistances.get(r.getInt("nullResistance")))); + } + + r = s.executeQuery("SELECT nullValue FROM PS_NullSet WHERE coverage = '" + coverage + "'"); + Set<String> nullSet = new HashSet<String>(r.getFetchSize()); + + while (r.next()) { + nullSet.add(r.getString("nullValue")); + } + + r = s.executeQuery("SELECT id, name, type, numLo, numHi, strLo, strHi FROM PS_Domain WHERE coverage = '" + coverage + "' ORDER BY i ASC"); + List<DomainElement> domain = new ArrayList<DomainElement>(r.getFetchSize()); + Statement ss = conn.createStatement(); + + while (r.next()) { + String strLo = r.getString("strLo"); + String strHi = r.getString("strHi"); + + Double numLo = r.getDouble("numLo"); + + if (r.wasNull()) { + numLo = null; + } + + Double numHi = r.getDouble("numHi"); + + if (r.wasNull()) { + numHi = null; + } + + ResultSet rr = ss.executeQuery("SELECT crs FROM PS_CrsSet WHERE axis = '" + r.getInt("id") + "'"); + Set<String> crsSet = new HashSet<String>(rr.getFetchSize()); + + while (rr.next()) { + crsSet.add(crss.get(rr.getInt("crs"))); + } + + DomainElement d = new DomainElement(r.getString("name"), axisTypes.get(r.getInt("type")), + numLo, numHi, strLo, strHi, crsSet, axisTypes.values()); + domain.add(d); + } + + String abstr = ""; + String title = ""; + String keywords = ""; + r = s.executeQuery("SELECT title, abstract, keywords FROM PS_Descriptions WHERE coverage = '" + coverage + "'"); + if (r.next()) { + abstr = r.getString("abstract"); + title = r.getString("title"); + keywords = r.getString("keywords"); + } + + /* WGS84 is the only CRS we understand (except IMAGE_CRS, of course) */ + Wgs84Crs crs = null; + Double l1 = 0.0, l2 = 0.0, h1 = 0.0, h2 = 0.0; + Double o1 = null, o2 = null; + r = s.executeQuery("SELECT * FROM PS_CrsDetails WHERE coverage = '" + coverage + "'"); + if (r.next()) { + /* Domain extent */ + int x0 = X.getLo().intValue(); + int y0 = Y.getLo().intValue(); + int x1 = X.getHi().intValue(); + int y1 = Y.getHi().intValue(); + /* CRS Bounding box */ + l1 = r.getDouble("low1"); + l2 = r.getDouble("low2"); + h1 = r.getDouble("high1"); + h2 = r.getDouble("high2"); + String off1 = r.getString("offset1"); + String off2 = r.getString("offset2"); + if (off1 != null) { + o1 = Double.parseDouble(off1); + } + if (off2 != null) { + o2 = Double.parseDouble(off2); + } + /* Compute axis offsets if not predefined */ + if (o1 == null && o2 == null) { + o1 = (h1 - l1) / (double) (x1 - x0); + o2 = (h2 - l2) / (double) (y1 - y0); + LOG.debug("Calculated CRS axis offsets. For X: {}, for Y: {}", o1, o2); + LOG.debug(X.toString()); + LOG.debug(Y.toString()); + } + /* Only store CRS information if coverage is 2-D */ + if (twoDCoverage == true) { + crs = new Wgs84Crs(l1, h1, l2, h2, o1, o2); + LOG.trace("Found CRS : ", crs.toString()); + } else { + LOG.warn("Found CRS '{}', but coverage is not 2-dimensional. " + + "Ignoring CRS information.", crs.toString()); + } + } else if (twoDCoverage) { + LOG.warn(" WGS84 bounding box missing for 2-D coverage '" + coverageName + "'..."); + } + + /* Done with SQL statements */ + s.close(); + + /* Build the complete metadata object */ + Metadata meta = new Metadata(cellDomain, range, nullSet, nullDefault, interpolationSet, + new InterpolationMethod(interpolationTypeDefault, nullResistanceDefault), + coverageName, domain, crs, title, abstr, keywords); + meta.setCoverageId(coverage); + return meta; + } catch (InvalidMetadataException ime) { + if (checkAtInit && !initializing) { + throw new ResourceException("Previously valid metadata is now invalid. The metadata for coverage '" + coverageName + "' has been modified incorrectly.", ime); + } else { + throw new InvalidWcpsRequestException("Coverage '" + coverageName + "' has invalid metadata", ime); + } + } catch (SQLException sqle) { + if (s != null) { + try { + s.close(); + } catch (SQLException f) { + } + } + + close(); + + throw new ResourceException("Metadata database error", sqle); + } + + } + + /** Write a coverage's metadata to the database. This function can be used + * for both inserting new coverages and updating coverage metadata. + * + * @param meta Metadata container for the information to be stored in the metadata database + * @param commit Boolean value, specifying if we want to commit immediately or not + */ + private void write(Metadata meta, boolean commit) throws ResourceException { + String coverageName = meta.getCoverageName(); + if (existsCoverageName(coverageName)) { + updateCoverageMetadata(meta, commit); + } else { + insertNewCoverageMetadata(meta, commit); + } + } + + public void delete(Metadata meta, boolean commit) throws ResourceException { + String coverageName = meta.getCoverageName(); + if (existsCoverageName(coverageName) == false) { + throw new ResourceException("Cannot delete inexistent coverage: " + coverageName); + } + + /* Delete main coverage entry from "PS_Coverage". Auxiliary metadata are + * automatically deleted by the DB (via CASCADING) on + * deletion of the main entry in ps_coverage */ + Statement s = null; + try { + s = conn.createStatement(); + setQuery("DELETE FROM PS_Coverage WHERE name = '" + coverageName + "'"); + int count = s.executeUpdate(query); + LOG.trace("Affected rows: " + count); + s.close(); + + if (commit) { + commitAndClose(); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + try { + s.close(); + } catch (Exception e) { + } + } + } + + /** + * Check if there is metadata available for a given coverage name + * @param name coverage name + * @return true is coverage already exists + */ + public boolean existsCoverageName(String name) { + boolean result = false; + Statement s = null; + try { + s = conn.createStatement(); + setQuery("SELECT * FROM PS_Coverage WHERE name = '" + name + "'"); + ResultSet r = s.executeQuery(query); + if (r.next()) { + result = true; + } else { + result = false; + } + s.close(); + } catch (SQLException e) { + e.printStackTrace(); + } finally { + try { + s.close(); + } catch (Exception e) { + } + } + return result; + } + + /** + * Check if there exists a coverage with a given ID in the metadata database. + * @param id coverage id + * @return true is coverage already exists + */ + private boolean existsCoverageId(int id) { + boolean result = false; + Statement s = null; + try { + s = conn.createStatement(); + ResultSet r = s.executeQuery( + "SELECT * FROM PS_Coverage WHERE id = '" + id + "'"); + if (r.next()) { + result = true; + } else { + result = false; + } + s.close(); + } catch (SQLException e) { + e.printStackTrace(); + } finally { + try { + s.close(); + } catch (Exception e) { + } + } + System.out.println("Coverage with ID " + id + (result == false ? " does not " : "") + " exists."); + return result; + } + + /** Update metadata for an existing coverage. All information may change (including + * name), but the ID of the tuple in PS_Coverage will stay the same. + * + * @param meta Metadata object, container of the new information. + * @param commit True if we want to commit immediately, false to delay commit indefinitely + */ + public void updateCoverageMetadata(Metadata meta, boolean commit) throws ResourceException { + Statement s = null; + int coverageId = meta.getCoverageId(); + int count = -1; + String coverageName = meta.getCoverageName(); + + try { + ensureConnection(); + s = conn.createStatement(); + + String name = coverageName; + String nulldefault = meta.getNullDefault(); + int interpolatiotypendefault = revInterpolationTypes.get(meta.getInterpolationDefault()); + int nullresistancedefault = revNullResistances.get(meta.getNullResistanceDefault()); + + // Table PS_Coverage + setQuery("UPDATE PS_Coverage SET " + + "(name, nullDefault, interpolationTypeDefault, nullResistanceDefault) " + + " = ('" + name + "', '" + nulldefault + "', '" + + interpolatiotypendefault + "', '" + nullresistancedefault + "')" + + " WHERE id='" + coverageId + "'"); + count = s.executeUpdate(query); + if (count <= 0) { + throw new SQLException("Could not update table PS_Coverage."); + } + + // Table PS_CellDomain + /* Delete old data */ + setQuery("DELETE FROM PS_CellDomain WHERE coverage='" + coverageId + "'"); + count = s.executeUpdate(query); + if (count <= 0) { + throw new SQLException("Could not delete old entries from table PS_CellDomain."); + } + /* Insert new data */ + Iterator<CellDomainElement> cellIt = meta.getCellDomainIterator(); + int i = 0; + while (cellIt.hasNext()) { + CellDomainElement cell = cellIt.next(); + setQuery("INSERT INTO PS_CellDomain (coverage, i, lo, hi) VALUES " + + " ('" + coverageId + "', '" + i + "', '" + cell.getLo() + "', '" + + cell.getHi() + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert axis " + i + " for coverage " + + coverageName + "( id " + coverageId + ") into table PS_CellDomain"); + } + i++; + } + + // Table PS_Range + /* Delete old data */ + setQuery("DELETE FROM PS_Range WHERE coverage='" + coverageId + "'"); + count = s.executeUpdate(query); + if (count <= 0) { + throw new SQLException("Could not delete old entries from table PS_Range."); + } + /* Insert new data */ + Iterator<RangeElement> rangeIt = meta.getRangeIterator(); + i = 0; + while (rangeIt.hasNext()) { + RangeElement range = rangeIt.next(); + System.out.println(range); + int dataType = revDataTypes.get(range.getType()); + setQuery("INSERT INTO PS_Range (coverage, i, name, type) VALUES " + + " ('" + coverageId + "', '" + i + "', '" + range.getName() + "', '" + + dataType + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert range " + range.getName() + + "( id " + i + " ) for coverage " + + coverageName + "( id " + coverageId + ") into table PS_Range"); + } + i++; + } + + // Table PS_InterpolationSet + /* Delete old data */ + setQuery("DELETE FROM PS_InterpolationSet WHERE coverage='" + coverageId + "'"); + count = s.executeUpdate(query); + if (count <= 0) { + throw new SQLException("Could not delete old entries from table PS_InterpolationSet."); + } + /* Insert new data */ + Iterator<InterpolationMethod> methodIt = meta.getInterpolationMethodIterator(); + i = 0; + while (methodIt.hasNext()) { + InterpolationMethod method = methodIt.next(); + int interp = revInterpolationTypes.get(method.getInterpolationType()); + int nullRes = revNullResistances.get(method.getNullResistance()); + setQuery("INSERT INTO PS_InterpolationSet (coverage, interpolationType, nullResistance) VALUES " + + " ('" + coverageId + "', '" + interp + "', '" + nullRes + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert interpolation method (" + method.getInterpolationType() + + ", " + method.getNullResistance() + " ) for coverage " + + coverageName + "( id " + coverageId + ") into table PS_InterpolationSet"); + } + i++; + } + + // Table PS_NullSet + /* Delete old data */ + setQuery("DELETE FROM PS_NullSet WHERE coverage='" + coverageId + "'"); + count = s.executeUpdate(query); + if (count <= 0) { + throw new SQLException("Could not delete old entries from table PS_NullSet."); + } + /* Insert new data */ + Iterator<String> nullIt = meta.getNullSetIterator(); + while (nullIt.hasNext()) { + String nullValue = nullIt.next(); + setQuery("INSERT INTO PS_NullSet (coverage, nullValue) VALUES " + + " ('" + coverageId + "', '" + nullValue + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert null value '" + nullValue + + "' for coverage " + coverageName + "( id " + coverageId + + ") into table PS_NullSet"); + } + } + + // Tables PS_Domain and PS_CrsSet + /* Delete old data */ + setQuery("SELECT * FROM PS_Domain WHERE coverage='" + coverageId + "'"); + ResultSet r1 = s.executeQuery(query); + Vector axes = new Vector(5); + while (r1.next()) { + int axisId = r1.getInt("id"); + axes.add((Integer) axisId); + } + System.out.println(axes); + for (i = 0; i < axes.size(); i++) { + int axisId = ((Integer) axes.elementAt(i)).intValue(); + setQuery("DELETE FROM PS_CrsSet WHERE axis='" + axisId + "'"); + count = s.executeUpdate(query); + /* Do not worry about inexisting entries in CRS Sets: this implementation + does not really use CRSs. */ +// if (count <= 0) +// throw new SQLException("Could not delete old entries from table PS_CrsSet."); + + } + + setQuery("DELETE FROM PS_Domain WHERE coverage='" + coverageId + "'"); + count = s.executeUpdate(query); + if (count <= 0) { + throw new SQLException("Could not delete old entries from table PS_Domain."); + } + /* Insert new data */ + Iterator<DomainElement> domIt = meta.getDomainIterator(); + i = 0; + while (domIt.hasNext()) { + // Table PS_Domain + DomainElement dom = domIt.next(); + int axisType = revAxisTypes.get(dom.getType()); + Double numLo = dom.getNumLo(); + Double numHi = dom.getNumHi(); + String sNumLo = numLo == null ? "null" : "'" + numLo.toString() + "'"; + String sNumHi = numHi == null ? "null" : "'" + numHi.toString() + "'"; + String strLo = dom.getStrLo(); + String strHi = dom.getStrHi(); + if (strLo != null && strLo.equals("null") == false) { + strLo = "'" + strLo + "'"; + } + if (strHi != null && strHi.equals("null") == false) { + strHi = "'" + strHi + "'"; + } + + String sqlQuery = "INSERT INTO PS_Domain " + + "(coverage, i, name, type, numLo, numHi, strLo, strHi) VALUES " + + "('" + coverageId + "', '" + i + "', '" + dom.getName() + "', '" + + axisType + "', " + sNumLo + ", " + sNumHi + + ", " + strLo + ", " + strHi + ")"; + /* Need to get ID of the newly inserted tuple. Postgres has a cool construct (RETURNING), + * but we can also fall-back to another generic JDBC driver */ + int axisId = -1; + if (driver.equals("org.postgresql.Driver")) { + /* RETURNING clause is not standard SQL, only PostgreSQL understands it*/ + setQuery(sqlQuery + " RETURNING id"); + + ResultSet r = s.executeQuery(query); + if (r.next() == false) { + throw new SQLException("Could not insert domain element (i = " + i + + ", name=" + dom.getName() + ", type=" + dom.getType() + + ", numLo=" + dom.getNumLo() + ", numHi=" + dom.getNumHi() + + ", strLo=" + dom.getStrLo() + ", strHi=" + dom.getStrHi() + + ") for coverage " + coverageName + "( id " + coverageId + + ") into table PS_Domain"); + } + /* Retrieve the ID of the newly inserted tuple (PS_Domain) */ + axisId = r.getInt("id"); + } else { + /* Fallback to specific driver support on returning autogenerated keys. */ + String[] keys = new String[1]; + keys[0] = "id"; + int c = s.executeUpdate(sqlQuery, keys); + if (c <= 0) { + throw new SQLException("Could not insert domain element (i = " + i + + ", name=" + dom.getName() + ", type=" + dom.getType() + + ", numLo=" + dom.getNumLo() + ", numHi=" + dom.getNumHi() + + ", strLo=" + dom.getStrLo() + ", strHi=" + dom.getStrHi() + + ") for coverage " + coverageName + "( id " + coverageId + + ") into table PS_Domain"); + } + /* Retrieve the ID of the newly inserted tuple (PS_Domain) */ + ResultSet rr = s.getGeneratedKeys(); + if (rr.next() == false) { + throw new SQLException("Could not retrieve ID of the newly insterted tuple in table PS_Domain."); + } + axisId = rr.getInt("id"); + } + /* check ID */ + if (axisId < 0) { + throw new SQLException("Generated ID (" + axisId + + ") for the tuple in PS_Domain is not valid !"); + } + + /* Increment axis number */ + i++; + + // Table PS_CrsSet + Set<String> crsSet = dom.getCrsSet(); + System.out.println(crsSet); + Iterator<String> crsIt = crsSet.iterator(); + while (crsIt.hasNext()) { + String crs = crsIt.next(); + int crsId = revCrss.get(crs); + setQuery("INSERT INTO PS_CrsSet (axis, crs) VALUES " + + "('" + axisId + "', '" + crsId + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert Crs " + crs + + "for axis =" + axisId + " in table PS_CrsSet"); + } + } + } + + // Table PS_Descriptions + /* Delete old data */ + setQuery("DELETE FROM PS_Descriptions WHERE coverage='" + coverageId + "'"); + count = s.executeUpdate(query); + /* Coverage descriptions are not essential, do not throw an error if missing */ +// if (count <= 0) +// throw new SQLException("Could not delete old data from table PS_Descriptions"); + /* Insert new data */ + String title = meta.getTitle(); + String abstr = meta.getAbstract(); + String keywords = meta.getKeywords(); + if (title != null) { + title = "'" + title + "'"; + } + if (abstr != null) { + abstr = "'" + abstr + "'"; + } + if (keywords != null) { + keywords = "'" + keywords + "'"; + } + setQuery("INSERT INTO PS_Descriptions (coverage, title, abstract, keywords)" + + " VALUES ('" + coverageId + "', " + title + ", " + abstr + ", " + keywords + ")"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert descriptive metadata in table " + + "PS_Descriptions for coverage " + coverageName + " (ID " + coverageId + ")"); + } + + + s.close(); + + if (commit) { + commitAndClose(); + } + } catch (SQLException sqle) { + /* Abort this transaction */ + try { + if (s != null) { + s.close(); + } + abortAndClose(); + } catch (SQLException f) { + } + + throw new ResourceException("Metadata database error", sqle); + } + } + + /** Insert metadata for a new coverage. + * + * @param meta Metadata object, container of information + * @param commit Boolean value, specifying if we want to commit immediately or not + */ + public void insertNewCoverageMetadata(Metadata meta, boolean commit) throws ResourceException { + Statement s = null; + String coverageName = meta.getCoverageName(); + int coverageId = -1; + + try { + ensureConnection(); + s = conn.createStatement(); + + String name = coverageName; + /* FIXME: Table PS_COVERAGE: nullValue and CRS fields not USED !!! */ +// String nullvalue = ""; +// String crs = ""; + String nulldefault = meta.getNullDefault(); + int interpolatiotypendefault = revInterpolationTypes.get(meta.getInterpolationDefault()); + int nullresistancedefault = revNullResistances.get(meta.getNullResistanceDefault()); + + // Table PS_Coverage + String sqlQuery = "INSERT INTO PS_Coverage " + + "(name, nullDefault, interpolationTypeDefault, nullResistanceDefault) " + + " VALUES ('" + name + "', '" + nulldefault + "', '" + + interpolatiotypendefault + "', '" + nullresistancedefault + "')"; + /* Need to get ID of the newly inserted tuple. Postgres has a cool construct (RETURNING), + * but we can also fall-back to another generic JDBC driver */ + if (driver.equals("org.postgresql.Driver")) { + /* RETURNING clause is not standard SQL, only PostgreSQL understands it*/ + setQuery(sqlQuery + " RETURNING id"); + + ResultSet r = s.executeQuery(query); + if (r.next() == false) { + throw new SQLException("Could not insert new coverage in table PS_Coverage."); + } + /* Retrieve the ID of the newly inserted tuple (PS_Coverage) */ + coverageId = r.getInt("id"); + } else { + /* Fallback to specific driver support on returning autogenerated keys. */ + String[] keys = new String[1]; + keys[0] = "id"; + int c = s.executeUpdate(sqlQuery, keys); + if (c <= 0) { + throw new SQLException("Could not insert new coverage in table PS_Coverage."); + } + /* Retrieve the ID of the newly inserted tuple (PS_Coverage) */ + ResultSet rr = s.getGeneratedKeys(); + if (rr.next() == false) { + throw new SQLException("Could not retrieve ID of the newly insterted tuple in table PS_Coverage."); + } + coverageId = rr.getInt("id"); + } + /* check ID */ + if (coverageId < 0) { + throw new SQLException("Generated ID (" + coverageId + + ") for the tuple in PS_Coverage is not valid !"); + } + + // Table PS_CellDomain + Iterator<CellDomainElement> cellIt = meta.getCellDomainIterator(); + int i = 0; + while (cellIt.hasNext()) { + CellDomainElement cell = cellIt.next(); + setQuery("INSERT INTO PS_CellDomain (coverage, i, lo, hi) VALUES " + + " ('" + coverageId + "', '" + i + "', '" + cell.getLo() + "', '" + + cell.getHi() + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert axis " + i + " for coverage " + + coverageName + "( id " + coverageId + ") into table PS_CellDomain"); + } + i++; + } + + // Table PS_Range + Iterator<RangeElement> rangeIt = meta.getRangeIterator(); + i = 0; + while (rangeIt.hasNext()) { + RangeElement range = rangeIt.next(); + System.out.println(range); + int dataType = revDataTypes.get(range.getType()); + setQuery("INSERT INTO PS_Range (coverage, i, name, type) VALUES " + + " ('" + coverageId + "', '" + i + "', '" + range.getName() + "', '" + + dataType + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert range " + range.getName() + + "( id " + i + " ) for coverage " + + coverageName + "( id " + coverageId + ") into table PS_Range"); + } + i++; + } + + // Table PS_InterpolationSet + Iterator<InterpolationMethod> methodIt = meta.getInterpolationMethodIterator(); + i = 0; + while (methodIt.hasNext()) { + InterpolationMethod method = methodIt.next(); + int interp = revInterpolationTypes.get(method.getInterpolationType()); + int nullRes = revNullResistances.get(method.getNullResistance()); + setQuery("INSERT INTO PS_InterpolationSet (coverage, interpolationType, nullResistance) VALUES " + + " ('" + coverageId + "', '" + interp + "', '" + nullRes + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert interpolation method (" + method.getInterpolationType() + + ", " + method.getNullResistance() + " ) for coverage " + + coverageName + "( id " + coverageId + ") into table PS_InterpolationSet"); + } + i++; + } + + // Table PS_NullSet + Iterator<String> nullIt = meta.getNullSetIterator(); + while (nullIt.hasNext()) { + String nullValue = nullIt.next(); + setQuery("INSERT INTO PS_NullSet (coverage, nullValue) VALUES " + + " ('" + coverageId + "', '" + nullValue + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert null value '" + nullValue + + "' for coverage " + coverageName + "( id " + coverageId + + ") into table PS_NullSet"); + } + } + + Iterator<DomainElement> domIt = meta.getDomainIterator(); + i = 0; + while (domIt.hasNext()) { + // Table PS_Domain + DomainElement dom = domIt.next(); + int axisType = revAxisTypes.get(dom.getType()); + Double numLo = dom.getNumLo(); + Double numHi = dom.getNumHi(); + String sNumLo = numLo == null ? "null" : "'" + numLo.toString() + "'"; + String sNumHi = numHi == null ? "null" : "'" + numHi.toString() + "'"; + String strLo = dom.getStrLo(); + String strHi = dom.getStrHi(); + if (strLo != null && strLo.equals("null") == false) { + strLo = "'" + strLo + "'"; + } + if (strHi != null && strHi.equals("null") == false) { + strHi = "'" + strHi + "'"; + } + + sqlQuery = "INSERT INTO PS_Domain " + + "(coverage, i, name, type, numLo, numHi, strLo, strHi) VALUES " + + "('" + coverageId + "', '" + i + "', '" + dom.getName() + "', '" + + axisType + "', " + sNumLo + ", " + sNumHi + + ", " + strLo + ", " + strHi + ")"; + /* Need to get ID of the newly inserted tuple. Postgres has a cool construct (RETURNING), + * but we can also fall-back to another generic JDBC driver */ + int axisId = -1; + if (driver.equals("org.postgresql.Driver")) { + /* RETURNING clause is not standard SQL, only PostgreSQL understands it*/ + setQuery(sqlQuery + " RETURNING id"); + + ResultSet r = s.executeQuery(query); + if (r.next() == false) { + throw new SQLException("Could not insert domain element (i = " + i + + ", name=" + dom.getName() + ", type=" + dom.getType() + + ", numLo=" + dom.getNumLo() + ", numHi=" + dom.getNumHi() + + ", strLo=" + dom.getStrLo() + ", strHi=" + dom.getStrHi() + + ") for coverage " + coverageName + "( id " + coverageId + + ") into table PS_Domain"); + } + /* Retrieve the ID of the newly inserted tuple (PS_Domain) */ + axisId = r.getInt("id"); + } else { + /* Fallback to specific driver support on returning autogenerated keys. */ + String[] keys = new String[1]; + keys[0] = "id"; + int c = s.executeUpdate(sqlQuery, keys); + if (c <= 0) { + throw new SQLException("Could not insert domain element (i = " + i + + ", name=" + dom.getName() + ", type=" + dom.getType() + + ", numLo=" + dom.getNumLo() + ", numHi=" + dom.getNumHi() + + ", strLo=" + dom.getStrLo() + ", strHi=" + dom.getStrHi() + + ") for coverage " + coverageName + "( id " + coverageId + + ") into table PS_Domain"); + } + /* Retrieve the ID of the newly inserted tuple (PS_Domain) */ + ResultSet rr = s.getGeneratedKeys(); + if (rr.next() == false) { + throw new SQLException("Could not retrieve ID of the newly insterted tuple in table PS_Domain."); + } + axisId = rr.getInt("id"); + } + /* check ID */ + if (axisId < 0) { + throw new SQLException("Generated ID (" + axisId + + ") for the tuple in PS_Domain is not valid !"); + } + + /* Increment axis number */ + i++; + + // Table PS_CrsSet + Set<String> crsSet = dom.getCrsSet(); + Iterator<String> crsIt = crsSet.iterator(); + while (crsIt.hasNext()) { + String crs = crsIt.next(); + int crsId = revCrss.get(crs); + setQuery("INSERT INTO PS_CrsSet (axis, crs) VALUES " + + "('" + axisId + "', '" + crsId + "')"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert Crs " + crs + + "for axis =" + axisId + " in table PS_CrsSet"); + } + } + } + + // Table PS_Descriptions + String title = meta.getTitle(); + String abstr = meta.getAbstract(); + String keywords = meta.getKeywords(); + if (title != null) { + title = "'" + title + "'"; + } + if (abstr != null) { + abstr = "'" + abstr + "'"; + } + if (keywords != null) { + keywords = "'" + keywords + "'"; + } + setQuery("INSERT INTO PS_Descriptions (coverage, title, abstract, keywords)" + + " VALUES ('" + coverageId + "', " + title + ", " + abstr + ", " + keywords + ")"); + if (s.executeUpdate(query) <= 0) { + throw new SQLException("Could not insert descriptive metadata in table " + + "PS_Descriptions for coverage " + coverageName + " (ID " + coverageId + ")"); + } + + s.close(); + + if (commit) { + commitAndClose(); + } + } catch (SQLException sqle) { + /* Abort this transaction */ + try { + if (s != null) { + s.close(); + } + abortAndClose(); + } catch (SQLException f) { + } + + throw new ResourceException("Metadata database error", sqle); + } + + } + + public void ensureConnection() throws SQLException { + synchronized (this) { + LOG.trace("Ensuring connection is valid..."); +// if( connection == null || !connection.isValid( CONNECTION_TIMEOUT ) ) { // Not implemented by PostgreSQL yet. + if ((conn == null) || conn.isClosed()) { + LOG.trace("*** Opening new DB connection !"); + close(); + openConnection(); + LOG.trace("*** ok."); +// try { throw new Exception(""); } catch (Exception e) {e.printStackTrace();} + } + } + + } + + public void openConnection() throws SQLException { + conn = DriverManager.getConnection(url, user, pass); + conn.setAutoCommit(false); + savepoint = conn.setSavepoint(); + } + + public void abortAndClose() throws SQLException { + if (conn != null) { + conn.rollback(savepoint); + conn.close(); + conn = null; + } + } + + public void commitAndClose() throws SQLException { + if (conn != null) { + conn.commit(); + conn.close(); + conn = null; + } + } + + @Override + public Collection<String> getAxisNames() { + return axisTypes.values(); + } + + /* Logging function for SQL queries. */ + private void setQuery(String q) { + this.query = q; + LOG.trace("SQL Query: {}", q); + } + + /* Returns the available formatToMimetype formats, as stored in the metadata database */ + public String[] getMimetypesList() { + return supportedFormats.values().toArray(new String[1]); + } + + /* Translate a mime-type to a format name, if known to rasdaman. */ + public String mimetypeToFormat(String mime) { + LOG.debug(revSupportedFormats.toString()); + return revSupportedFormats.get(mime); + } + + /** + * @return the dataTypes + */ + public Collection<String> getDataTypes() { + return dataTypes.values(); + } + + /** + * @return the interpolationTypes + */ + public Collection<String> getInterpolationTypes() { + return interpolationTypes.values(); + } + + /** + * @return the nullResistances + */ + public Collection<String> getNullResistances() { + return nullResistances.values(); + } +} |