From 8e8ad756de21e3884549cce209c91d443ab7ac23 Mon Sep 17 00:00:00 2001 From: Andrei Aiordachioaie Date: Mon, 3 Aug 2009 11:54:21 +0200 Subject: WCS-T added to repository --- src/wcst/transaction/executeTransaction.java | 1320 ++++++++++++++++++++++++++ 1 file changed, 1320 insertions(+) create mode 100644 src/wcst/transaction/executeTransaction.java (limited to 'src/wcst/transaction/executeTransaction.java') diff --git a/src/wcst/transaction/executeTransaction.java b/src/wcst/transaction/executeTransaction.java new file mode 100644 index 0000000..9033f62 --- /dev/null +++ b/src/wcst/transaction/executeTransaction.java @@ -0,0 +1,1320 @@ +/* + * 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 . + * + * For more information please see + * or contact Peter Baumann via . + * + * Copyright 2009 Jacobs University Bremen, Peter Baumann. + */ + + +package wcst.transaction; + +//~--- non-JDK imports -------------------------------------------------------- + +import java.sql.SQLException; +import net.opengis.ows.v_1_0_0.BoundingBoxType; +import net.opengis.wcs.ows.v_1_1_0.InterpolationMethodType; +import net.opengis.wcs.v_1_1_0.CoverageDescriptionType; +import net.opengis.wcs.v_1_1_0.CoverageDescriptions; +import net.opengis.wcs.v_1_1_0.CoverageSummaryType; +import net.opengis.wcs.v_1_1_0.FieldType; +import net.opengis.wcs.v_1_1_0.RangeType; + +import org.apache.commons.io.IOUtils; + +import wcst.server.core.CodeType; +import wcst.server.core.CoverageType; +import wcst.server.core.KeywordsType; +import wcst.server.core.LanguageStringType; +import wcst.server.core.ManifestType; +import wcst.server.core.ReferenceType; +import wcst.server.core.TransactionResponseType; +import wcst.server.core.TransactionType; + +import wcst.transaction.tools.SDU; + +//~--- JDK imports ------------------------------------------------------------ + +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.image.BufferedImage; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.StringReader; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.UUID; + +import javax.imageio.ImageIO; + +import javax.swing.JFrame; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.Unmarshaller; +import org.odmg.ODMGException; +import wcps.server.core.Metadata; +import wcst.transaction.tools.RasdamanUtils; + +/** + * This class takes a WCS-T Transaction XML request and executes the request, + * building the corresponding XML respose. Use this class for synchroneous processing. + * + * @author Andrei Aiordachioaie + */ +public class executeTransaction +{ + private static boolean printLog = true; + public final String ACCEPTED_LANGUAGE = "en"; + public final static String DEFAULT_RASSERVER = "http://kahlua.eecs.jacobs-university.de:7001"; + public final static String DEFAULT_RASDATABASE = "RASBASE"; + private boolean finished; + private TransactionType input; + private MetadataDb meta; + protected TransactionResponseType output; + private String metadataSettingsPath; + private RasdamanUtils rasUtils; + private String requestId; + + /** + * Default constructor. Initialize internal variables. + * @param tr Transaction object, a WCS-T request + * @param metadataDbPath Path to the "dbparams.properties" file + */ + public executeTransaction(TransactionType tr, String metadataDbPath) throws WCSException + { + input = tr; + output = new TransactionResponseType(); + finished = false; + metadataSettingsPath = metadataDbPath; + meta = new MetadataDb(metadataSettingsPath); + + String server = DEFAULT_RASSERVER; + String db = DEFAULT_RASDATABASE; + + rasUtils = new RasdamanUtils(server, db); + try { rasUtils.init(); } + catch (Exception e) + { + e.printStackTrace(); + throw new WCSException("NoApplicableCode", "Could not initialize: " + e.getMessage()); + } + + // In case noone will ever call this method and we need it + generateRequestId(); + } + + /** + * Generate a new Request ID string, and return it. If the transaction request + * does not include a request ID, the last generated string will be used. + * @return String Request ID + */ + public String generateRequestId() + { + requestId = String.valueOf(UUID.randomUUID()); + return requestId; + } + + /** + * Log a message to System.out + * @param str Text of the message + */ + private static void log(String str) + { + if ( printLog ) + { + System.out.println(" " + str); + } + } + + /** + * Main method of this class: Computes the response to the TransactionResponse + * request given to the constructor. If needed, it also calls process() + * @return a TransactionResponse object. + * @throws WCSException + */ + public TransactionResponseType get() throws WCSException + { + try + { + if ( finished == false ) + { + process(); + } + } + catch (WCSException e) + { + e.printStackTrace(); + + throw e; + } + if ( finished == false ) + { + throw new WCSException("NoApplicableCode", "Could not execute the Transaction request! " + "Please see the other errors..."); + } + + return output; + } + + /** + * Computes the response to the Transaction request given to the constructor. + */ + public void process() throws WCSException + { + if ( ! input.getService().equalsIgnoreCase("WCS") ) + { + throw new WCSException("InvalidParameterValue", "Service", "Service must be \"WCS\" !"); + } + if ( ! input.getVersion().equalsIgnoreCase("1.1") ) + { + throw new WCSException("InvalidParameterValue", "Service", "Service Version must be \"1.1\" !"); + } + + // Set the output request ID + String reqID = input.getRequestId(); + if ( reqID == null ) + { + reqID = requestId; + } + output.setRequestId(reqID); + + // All actions succeed or fail as one group. + try + { + ManifestType covs = input.getInputCoverages(); + List l = covs.getReferenceGroup(); + + for (int i = 0; i < l.size(); i++) + { + // This object is the XML element "InputCoverages" + Object obj = ((JAXBElement) l.get(i)).getValue(); + CoverageType cov = (CoverageType) obj; + + // Each action adds something to the output XML document + processInputCoverageNode(cov); + } + + finished = true; + + /* Commit rasdaman changes */ + try + { + log("Commit rasdaman changes ..."); + rasUtils.commitAndClose(); + log("Rasdaman coverages saved successfully !"); + } + catch (ODMGException e) + { + log("Could not commit rasdaman changes: " + e.getMessage()); + throw new WCSException("NoApplicableCode", "Could not commit Rasdaman changes !"); + } + + /* Commit metadata changes */ + try + { + log("Commit metadata changes ..."); + meta.commitChangesAndClose(); + log("Metadata has been saved !"); + } + catch (SQLException ex) + { + log("Could not commit metadata changes: " + ex.getMessage()); + throw new WCSException("NoApplicableCode", "Could not commit metadata changes !"); + } + } + catch (WCSException e) + { + // One action failed, therefore all actions have failed + e.printStackTrace(); + + /* Abort metadata changes */ + finished = false; + try + { + log("Rolling back metadata database changes ..."); + meta.abortChangesAndClose(); + log("Metadata rollback completed!"); + } + catch (SQLException ex) + { + log("Could not rollback metadata changes: " + ex.getMessage()); + } + + /* Abort rasdaman changes */ + try + { + log("Aborting rasdaman changes ..."); + rasUtils.abortAndClose(); + log("Rasdaman changes aborted !"); + } + catch (ODMGException ex) + { + log("Could not abort rasdaman changes: " + ex.getMessage()); + } + + throw e; + } + catch (Exception e) + { + // Unknown error handling + e.printStackTrace(); + throw new WCSException("NoApplicableCode", "Runtime error: " + e.getMessage()); + } + } + + /** + * Delete a coverage from the Rasdaman server. + * @param identifier Name of coverage + * @throws Exception + */ + private void deleteCoverageFromRasdaman(String identifier) throws Exception + { + try + { + log("Deleting coverage from Rasdaman ..."); + rasUtils.deleteCollection(identifier); + log("Rasdaman Collection '" + identifier + "' is now deleted !"); + } + catch (ODMGException e) + { + e.printStackTrace(); + log("Failed to delete rasdaman collection " + identifier); + + throw new ODMGException("Failed to delete collection from Rasdaman !"); + } + } + + /** + * Delete all records of a coverage from the metadata DB + * + * @param identifier ID of the coverage + */ + private void deleteCoverageMetadata(String identifier) throws Exception + { + log("Deleting coverage " + identifier + " from the metadata DB"); + int id = MetadataUtils.getCoverageID(meta, identifier); + String strId = Integer.toString(id); + + // These auxiliary metadata are automatically deleted by the DB (via CASCADING) on + // deletion of the main entry in ps_coverage + /* + // Delete auxiliary declarations + MetadataUtils.deleteRowFromTable(meta, "ps_celldomain", "coverage", strId); + MetadataUtils.deleteRowFromTable(meta, "ps_domain", "coverage", strId); + MetadataUtils.deleteRowFromTable(meta, "ps_interpolationmethodlist", "coverage", strId); + MetadataUtils.deleteRowFromTable(meta, "ps_interpolationset", "coverage", strId); + MetadataUtils.deleteRowFromTable(meta, "ps_nullset", "coverage", strId); + MetadataUtils.deleteRowFromTable(meta, "ps_range", "coverage", strId); + */ + + // Delete main entry from "ps_coverage" + MetadataUtils.deleteRowFromTable(meta, "ps_coverage", "name", identifier); + + log("Coverage " + identifier + " is now deleted from the Metadata !"); + } + + /** + * Insert pixel data for a coverage into RasDaMan DB system + * + * @param identifier Identifier of the coverage + * @param href The location of the pixels for the new image + */ + private void insertImageIntoRasdaman(String identifier, BufferedImage img) + { + log("Inserting image into Rasdaman raster server..."); + try + { + // Display image for check + displayImage(identifier, img); + rasUtils.insertGrayImageAsArray(identifier, img); + log("Inserted image into Rasdaman !"); + } + catch (Exception e) + { + System.err.println("Error !"); + e.printStackTrace(); + } + } + + /** Display a image on the server computer's monitor, for debugging purposes. + * + * @param name Name of image + * @param img BufferedImage object + */ + public static void displayImage(String name, BufferedImage img) + { + JFrame f = new JFrame("Image " + name); +// f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new ShowImage(img)); + f.setSize(img.getWidth() + 50, img.getHeight() + 50); + f.setVisible(true); + } + + /** Load a BufferedImage from a ReferenceType object. + * + * @param pixels Reference object + * @return available image + * @throws WCSException + */ + private BufferedImage loadPixelsReference(ReferenceType pixels) throws WCSException + { + URL url = null; + BufferedImage img = null; + + try + { + url = new URL(pixels.getHref()); + } + catch (MalformedURLException e) + { + log("URL " + url.toString() + " is not valid !"); + throw new WCSException("InvalidParameterValue", "Reference pixels"); + } + + try + { + img = ImageIO.read(url); + } + catch (IOException ex) + { + throw new WCSException("NoApplicableCode", "The URL '" + url + "' does not contain a valid image."); + } + + return img; + } + + /** + * Load a Coverage Description XML object from a Reference + * + * @param identifier Name of coverage + * @param desc Reference to a CoverageDescriptions xml + * @return coverage description + * @throws WCSException + */ + private CoverageDescriptionType loadDescriptionReference(String identifier, ReferenceType desc) throws WCSException + { + URL url = null; + String xmlString = null; + CoverageDescriptions descs = null; + CoverageDescriptionType desc0 = null; + + // Load the URL + try + { + url = new URL(desc.getHref()); + } + catch (MalformedURLException e) + { + log("URL " + url.toString() + " is not valid !"); + + throw new WCSException("InvalidParameterValue", "Reference pixels"); + } + + // Read the contents of the URL + try + { + URLConnection conn = url.openConnection(); + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + + xmlString = IOUtils.toString(in); + } + catch (IOException ex) + { + throw new WCSException("NoApplicableCode", "Error loading the " + "coverage description from URL " + url.toString()); + } + + // Unmarshall the XML string into a Java Object + try + { + JAXBContext jaxbCtx = JAXBContext.newInstance(CoverageDescriptions.class.getPackage().getName()); + Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); + Object obj = unmarshaller.unmarshal(new StringReader(xmlString)); + + if ( obj instanceof JAXBElement ) + descs = (CoverageDescriptions) ((JAXBElement) obj).getValue(); + else if ( obj instanceof CoverageDescriptions ) + descs = (CoverageDescriptions) obj; + else + throw new WCSException("NoApplicableCode", "Coverage " + "Description metadata could not be loaded !"); + } + catch (javax.xml.bind.JAXBException ex) + { + log("Could not unmarshall the CoverageDescription XML document: " + ex.getMessage()); + throw new WCSException("NoApplicableCode", "Could not unmarshall " + "the CoverageDescription XML document: " + ex.getMessage()); + } + + // Filter by coverage name + desc0 = null; + Iterator i = descs.getCoverageDescription().iterator(); + + while (i.hasNext()) + { + CoverageDescriptionType d = i.next(); + + if ( d.getIdentifier().equals(identifier) ) + { + desc0 = d; + break; + } + } + + if (desc0 == null) + throw new WCSException("NoApplicableCode", "Could not find a CoverageDescription for coverage: " + identifier); + + return desc0; + } + + private CoverageSummaryType loadCoverageSummary(ReferenceType pixels) throws WCSException + { + URL url = null; + String xmlString = null; + CoverageSummaryType xml = null; + + try + { + url = new URL(pixels.getHref()); + } + catch (MalformedURLException e) + { + log("URL " + url.toString() + " is not valid !"); + + throw new WCSException("InvalidParameterValue", "Reference summary"); + } + + // Read the contents of the URL + try + { + URLConnection conn = url.openConnection(); + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + + xmlString = IOUtils.toString(in); + } + catch (IOException ex) + { + throw new WCSException("NoApplicableCode", "Error loading the " + "coverage summary from URL " + url.toString()); + } + + // Unmarshall the XML string into a Java Object + try + { + JAXBContext jaxbCtx = JAXBContext.newInstance(CoverageSummaryType.class.getPackage().getName()); + Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); + Object obj = unmarshaller.unmarshal(new StringReader(xmlString)); + + if ( obj instanceof JAXBElement ) + xml = (CoverageSummaryType) ((JAXBElement) obj).getValue(); + else if ( obj instanceof CoverageSummaryType ) + xml = (CoverageSummaryType) obj; + else + throw new WCSException("NoApplicableCode", "Coverage " + "Summary metadata could not be loaded !"); + } + catch (javax.xml.bind.JAXBException ex) + { + log("Could not unmarshall the CoverageSummaryXML document !"); + + throw new WCSException("NoApplicableCode", "Could not unmarshall " + "the CoverageSummary XML document !"); + } + + return xml; + } + + /** + * Updates the coverage metadata: textual descriptions. The title and the abstract + * are specified in multiple languages, but we only store english. + * + * @param identifier ID of the coverage + * @param title title to set + * @param covAbstract abstract to set + * @param keywords list of keywords + */ + private void updateCoverageMetadataFromSummary(String identifier, CoverageSummaryType summary) throws WCSException + { + log("Updating metadata with values from Coverage Summary..."); + + String title = null, abstr = null, keywords = null; + + title = summary.getTitle(); + abstr = summary.getAbstract(); + List keywordL = summary.getKeywords(); + List kList = new ArrayList(); + Iterator i = keywordL.iterator(); + + while (i.hasNext()) + { + KeywordsType keywordInMultipleLangs = (KeywordsType) i.next(); + String keyword = filterAcceptedLanguage(keywordInMultipleLangs.getKeyword()); + + kList.add(keyword); + } + keywords = SDU.string2str(kList); + + int covId = MetadataUtils.getCoverageID(meta, identifier); + + if ( MetadataUtils.updateDescriptionMetadata(meta, covId, title, abstr, keywords) == false ) + throw new WCSException("NoApplicableCode", "Could not update textual description metadata !"); + } + + /** + * Retrieve only the string in the accepted language from a multiple-language list + * + * @param list List of strings in several languages + * @return String in the accepted language, or null if none is found + */ + private String filterAcceptedLanguage(List list) + { + String result = null; + Iterator i = list.iterator(); + + while (i.hasNext()) + { + LanguageStringType a = (LanguageStringType) i.next(); + + if ( a.getLang().equals(ACCEPTED_LANGUAGE) ) + result = a.getValue(); + } + + return result; + } + + /** + * Partially updates a Rasdaman coverage with pixels from pixHref. Info about + * the bounding box is available from descHref. + * + * @param identifier ID of the coverage to be updates + * @param pixHref URL for the pixels data + * @param descHref URL for the metadata + */ + private void insertSomePixelsIntoRasdaman(String identifier, String pixHref, String descHref) + { + // TODO: Implement ! + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Inserts default metadata values for the given coverage. + * + * @param identifier ID of the coverage + * @param img The image, fetched from external reference + * @throws WCSException on error + */ + private void insertDefaultCoverageMetadata(String identifier, BufferedImage img) throws WCSException + { + log("Inserting default metadata values ..."); + + // (a) Table ps_coverage: Set default interpolation type + default null resistance + null default + log("Adding default metadata for coverage: " + identifier); + int definterp = MetadataUtils.getInterpolationCalledNone(meta); + int defnullresist = MetadataUtils.getNullResistanceCalledNone(meta); + String sinterp = Integer.toString(definterp); + String snullresist = Integer.toString(defnullresist); + + if ( definterp == -1 ) + { + throw new WCSException("NoApplicableCode", "Could not find default" + " interpolation method: 'none'"); + } + if ( defnullresist == -1 ) + { + throw new WCSException("NoApplicableCode", "Could not find default null resistance: 'none'"); + } + + if ( MetadataUtils.setAllCoverageMetadata(meta, identifier, null, null, "0", sinterp, snullresist) == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert default metadata for coverage: " + identifier); + } + + // (b) Table ps_domain: insert default values for the X and Y axes + log("Adding domain metadata (axis X) for coverage: " + identifier); + int coverageNumId = MetadataUtils.getCoverageID(meta, identifier); + int axisX = MetadataUtils.getIdByNameGeneral(meta, "ps_axistype", "axistype", "x"); + int axisY = MetadataUtils.getIdByNameGeneral(meta, "ps_axistype", "axistype", "y"); + if ( axisX == -1 ) + { + throw new WCSException("NoApplicableCode", "Could not find axis X in axis-types !"); + } + if ( axisY == -1 ) + { + throw new WCSException("NoApplicableCode", "Could not find axis Y in axis-types !"); + } + if ( MetadataUtils.setDomain(meta, coverageNumId, 0, "x", axisX, (Double) 0.0, (Double) 1.0, null, null) == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert Domain Metadata for axis X!"); + } + log("Adding domain metadata (axis Y) for coverage: " + identifier); + if ( MetadataUtils.setDomain(meta, coverageNumId, 1, "y", axisY, (Double) 0.0, (Double) 1.0, null, null) == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert Domain Metadata for axis Y!"); + } + + // (c) Table ps_celldomain: insert default values for the x and y axes + log("Adding cell domain metadata (axis X) for coverage: " + identifier); + if ( MetadataUtils.setCellDomain(meta, coverageNumId, 0, 0, img.getHeight() - 1) == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert Cell Domain Metadata for axis X!"); + } + log("Adding cell domain metadata (axis Y) for coverage: " + identifier); + if ( MetadataUtils.setCellDomain(meta, coverageNumId, 1, 0, img.getWidth() - 1) == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert Cell Domain Metadata for axis Y!"); + } + + // (d) Table ps_ranges: insert values for the only field of the image + // TODO: Update this to handle multiple fields + // Datatype: 0..255 = unsigned char + log("Adding Range metadata for gray-scale coverage: " + identifier); + int datatypeId = MetadataUtils.getIdByNameGeneral(meta, "ps_datatype", "datatype", "unsigned char"); + + if ( datatypeId == -1 ) + { + throw new WCSException("NoApplicableCode", "Cannot find datatype 'unsigned char', needed for gray images !"); + } + if ( MetadataUtils.setRange(meta, coverageNumId, 0, "gray-intensity", datatypeId) == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert Range Metadata!"); + } + + // (e) Table ps_interpolationsets: Insert default interpolation tuples + log("Adding default interpolation tuples for coverage: " + identifier); + if ( MetadataUtils.setInterpolationSets(meta, coverageNumId, definterp, defnullresist) == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert Interpolation Set metadata!"); + } + + // (f) Table ps_nullsets: Insert null value for coverage + log("Adding null values for coverage: " + identifier); + if ( MetadataUtils.setNullSets(meta, coverageNumId, "0") == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert Null Sets metadata!"); + } + + // (g) Table ps_descriptions: Insert default values for textual descriptions + log("Adding textual description for coverage: " + identifier); + if ( MetadataUtils.insertDescription(meta, coverageNumId, "Coverage " + identifier, "Available coverage", identifier) == false ) + { + throw new WCSException("NoApplicableCode", "Could not insert textual description metadata!"); + } + + log("Done inserting default metadata"); + } + + /** + * Processes one node from the input XML + * + * @param elem the JAXB node equivalent to the node + */ + private void processInputCoverageNode(CoverageType elem) throws WCSException + { + if ( elem.getAction() == null ) + { + throw new WCSException("InvalidParameterValue", "Action", "Every node must contain an child node !!!"); + } + + String action = elem.getAction().getValue(); + String identifier = null; + List references = null; + + if ( elem.getIdentifier() == null ) + { + throw new WCSException("InvalidParameter", "Identifier"); + } + + identifier = elem.getIdentifier().getValue(); + references = elem.getAbstractReferenceBase(); + + if ( action.equalsIgnoreCase("Add") ) + { + actionAddCoverage(identifier, references); + } + else if ( action.equalsIgnoreCase("UpdateMetadata") ) + { + actionUpdateMetadata(identifier, references); + } + else if ( action.equalsIgnoreCase("Delete") ) + { + actionDeleteCoverage(identifier, references); + } + else if ( action.equalsIgnoreCase("UpdateAll") ) + { + actionUpdateAll(identifier, references); + } + else if ( action.equalsIgnoreCase("UpdateDataPart") ) + { + throw new WCSException("ActionNotSupported", + "Action \"UpdateDataPart\" is not supported yet."); + /* TODO: UpdateDataPart is not yet functional. The Rasdaman server + * returns with an unexpected internal error (code: 10000) when + * a partial update query is sent. */ +// actionUpdateDataPart(identifier, references); + } + } + + /** + * Performs the action "UpdateAll", as part of the Transaction operation + * + * @param identifier Name of coverage to update + * @param references List of references with data for update + */ + private void actionUpdateAll(String identifier, List references) throws WCSException + { + log("Executing action Update All ..."); + // Updating everything is equivalent to deleting old coverage and adding the new one + actionDeleteCoverage(identifier, references); + actionAddCoverage(identifier, references); + + log("Finished action Update All!"); + } + + /** + * Updates the Metadata DB with the information contained in the CoverageDescriptions XML object + * + * @param identifier ID of the coverage + * @param desc object that contains the coverage description. + */ + private void updateCoverageMetadataFromDescription(String identifier, CoverageDescriptionType desc) throws WCSException + { + log("Updating metadata with values from CoverageDescription..."); + + if ( MetadataUtils.existsCoverage(meta, identifier) == false ) + throw new WCSException("NoApplicableCode", "Inexistent coverage: " + identifier); + int coverageId = MetadataUtils.getCoverageID(meta, identifier); + + log("Updating metadata for coverage name: " + identifier); + + /* (A) Table ps_coverage: Error check for Coverage Name */ + if ( MetadataUtils.existsCoverage(meta, identifier) == false) + throw new WCSException("NoApplicableCode", "Could not find metadata for coverage : " + identifier); + + /* (B) Table ps_descriptions: Update coverage title, abstract, keywords */ + String title = desc.getTitle(); + String abstr = desc.getAbstract(); + String keywords = desc.getKeywords().toString(); + + log("Using new keywords: " + keywords); + if ( MetadataUtils.updateDescriptions(meta, coverageId, title, abstr, keywords) == false ) + throw new WCSException("NoApplicableCode", "Could not update textual descriptions for coverage: " + identifier); + + /* (C) Table ps_range: Update field name and interpolation information */ + log("Updating field information..."); + // delete all previous information + MetadataUtils.deleteRowFromTable(meta, "ps_range", "coverage", String.valueOf(coverageId)); + // and insert new info from XML + RangeType range = desc.getRange(); + List fields = range.getField(); + Iterator i = fields.iterator(); + int fieldCount = 0; + + while (i.hasNext()) + { + FieldType field = (FieldType) i.next(); + + String name = field.getIdentifier(); + String datatype = field.getDefinition().getDataType().getValue(); + int typeId = MetadataUtils.getIdByNameGeneral(meta, "ps_datatype", "datatype", datatype); + + if ( typeId == -1 ) + throw new WCSException("NoApplicableCode", "Unknown datatype: " + datatype); + + if ( MetadataUtils.setRange(meta, coverageId, fieldCount, name, typeId) == false ) + throw new WCSException("NoApplicableCode", "Could not add information for field '" + name + "' !"); + fieldCount++; + } + + /* (D) Table ps_coverage: Update default interpolation method and null resistance */ + + /* + * We store interpolation methods at coverage level, not field level. + * So we only look at the interpolation method list of the first field, + * and use it on the whole coverage + */ + log("Updating default interpolation and null resistance..."); + InterpolationMethodType defInterp = fields.get(0).getInterpolationMethods().getDefaultMethod(); + String method = defInterp.getValue(); + String resist = defInterp.getNullResistance(); + int interpId = MetadataUtils.getIdByNameGeneral(meta, "ps_interpolationtype", "interpolationtype", method); + + if ( interpId == -1 ) + throw new WCSException("NoApplicableCode", "Unknown interpolation method: " + method); + int nullId = MetadataUtils.getIdByNameGeneral(meta, "ps_nullresistance", "nullresistance", resist); + + if ( nullId == -1 ) + throw new WCSException("NoApplicableCode", "Unknown null resistance: " + resist); + if ( MetadataUtils.setDefaultCoverageInterpolation(meta, identifier, interpId, nullId) == false ) + throw new WCSException("NoApplicableCode", "Could not update default interpolation for coverage: " + identifier); + + /* (E) Table ps_celldomain: Update cell domain of the coverage. */ + /* NOTE: Only works for 2-D coverages */ + log("Updating bounding box of coverage ..."); + List> list = desc.getDomain().getSpatialDomain().getBoundingBox(); + + if ( list.size() != 1 ) + throw new WCSException("NoApplicableCode", "Exactly 1 bounding box should be present for coverage: " + identifier); + BoundingBoxType bbox = list.get(0).getValue(); + List lower = bbox.getLowerCorner(); + List upper = bbox.getUpperCorner(); + + if ( lower.size() != 2 ) + throw new WCSException("InvalidParameter", "LowerCorner"); + if ( upper.size() != 2 ) + throw new WCSException("InvalidParameter", "UpperCorder"); + long loX = lower.get(0).longValue(); + long loY = lower.get(1).longValue(); + long hiX = upper.get(0).longValue(); + long hiY = upper.get(1).longValue(); + + int xAxisId = MetadataUtils.getAxisNumberByCoverageAndName(meta, coverageId, "x"); + + if ( MetadataUtils.updateCellDomain(meta, coverageId, xAxisId, loX, hiX) == false ) + throw new WCSException("NoApplicableCode", "Could not update X axis limits for coverage: " + identifier); + int yAxisId = MetadataUtils.getAxisNumberByCoverageAndName(meta, coverageId, "y"); + + if ( MetadataUtils.updateCellDomain(meta, coverageId, yAxisId, loY, hiY) == false ) + throw new WCSException("NoApplicableCode", "Could not update Y axis limits for coverage: " + identifier); + + /* (F) Table ps_crss: Update supported CRS */ + // FIXME later ... we don't support CRSs as of yet + } + + /** + * Performs the action "UpdateDataPart", as part of the Transaction operation + * + * @param identifier + * @param references + */ + private void actionUpdateDataPart(String identifier, List references) throws WCSException + { + log("Executing action UpdateDataPart ..."); + + // Error checking + if ( MetadataUtils.existsCoverage(meta, identifier) == false ) + { + throw new WCSException("InvalidParameter", "Identifier"); + } + + // Obtain the references + ReferenceType pixels, desc; + + pixels = getPixelsRef(references); + desc = getDescriptionRef(references); + + // References check. We are updating a coverage values, mandatory are: pixels, description + if ( pixels == null ) + { + throw new WCSException("MissingParameterValue", "Reference role='" + getUrnCode("pixels") + "'"); + } + if (desc == null) + { + throw new WCSException("MissingParameterValue", "Reference role='" + getUrnCode("description") + "'"); + } + + // (2) Do the actual processing + try + { + insertSomePixelsIntoRasdaman(identifier, pixels.getHref(), desc.getHref()); + } + catch (Exception e) + { + e.printStackTrace(); + throw new WCSException(e.getMessage(), e.getStackTrace().toString()); + } + } + + /** + * Performs the action "UpdateMetadata", as part of the Transaction operation + * + * @param identifier + * @param references + * @throws wcs.server.core.WCSException + */ + private void actionUpdateMetadata(String identifier, List references) throws WCSException + { + log("Executing action Update Metadata..."); + + // Only change the metadata for an existing coverage + if ( MetadataUtils.existsCoverage(meta, identifier) == false ) + { + throw new WCSException("InvalidParameter", "Identifier"); + } + + // Obtain the references + ReferenceType descRef, summRef; + + descRef = getDescriptionRef(references); + summRef = getSummaryRef(references); + + // References check. We are updating metadata, mandatory is only description + if ( descRef == null ) + { + throw new WCSException("MissingParameterValue", "Reference role='" + getUrnCode("description") + "'"); + } + + log("Loading reference: coverage description ..."); + CoverageDescriptionType desc = loadDescriptionReference(identifier, descRef); + + CoverageSummaryType summ = null; + + if ( summRef != null ) + { + log("Loading reference: coverage summary ..."); + summ = loadCoverageSummary(summRef); + } + + log("Done loading references !"); + + // (2) Do the actual processing + try + { + updateCoverageMetadataFromDescription(identifier, desc); + if (summ != null) + updateCoverageMetadataFromSummary(identifier, summ); + } + catch (Exception e) + { + e.printStackTrace(); + throw new WCSException("NoApplicableCode", e.getMessage()); + } + + log("Finished action Update Metadata !"); + } + + /** + * Performs the action "Add", as part of the Transaction operation + * + * @param identifier + * @param references + * @throws wcs.server.core.WCSException + */ + private void actionAddCoverage(String identifier, List references) throws WCSException + { + log("Executing action AddCoverage ..."); + + // Obtain the references + ReferenceType pixelsRef, descRef, summRef; + + pixelsRef = getPixelsRef(references); + descRef = getDescriptionRef(references); + summRef = getSummaryRef(references); + + // References check. We are adding a coverage, mandatory are: pixels, description + if ( pixelsRef == null ) + { + throw new WCSException("MissingParameterValue", "Reference role='" + getUrnCode("pixels") + "'"); + } + if ( descRef == null ) + { + throw new WCSException("MissingParameterValue", "Reference role='" + getUrnCode("description") + "'"); + } + + log("Loading reference: coverage pixels ..."); + BufferedImage img = loadPixelsReference(pixelsRef); + + log("Loading reference: coverage description ..."); + CoverageDescriptionType desc = loadDescriptionReference(identifier, descRef); + + CoverageSummaryType summ = null; + + if ( summRef != null ) + { + log("Loading reference: coverage summary ..."); + summ = loadCoverageSummary(summRef); + } + + log("Done loading references !"); + + /** + * (1) Insert metadata + */ + boolean changeId = false; + + if ( MetadataUtils.existsCoverage(meta, identifier) ) + { + changeId = true; + log("Changing coverage identifier since coverage '" + identifier + "' already exists !"); + } + + // Generate new coverage name ? + while (changeId) + { + identifier = "coverage_" + Integer.toString((new Random()).nextInt()); + changeId = MetadataUtils.existsCoverage(meta, identifier); + } + + /** + * (2) Do the actual processing. Stores the image in rasdaman and then the + * metadata in the db. + * + * Metadata is processed as follows: + * (a) default values are inserted + * (b) metadata is updated with values from CoverageDescription + * (c) metadata is updated with values from CoverageSummary + */ + try + { + /* Currently we only support one-band images. So convert to one-band (gray-scale)! */ + BufferedImage grayImg = rasUtils.convertImageToGray(img); + insertImageIntoRasdaman(identifier, grayImg); + + insertDefaultCoverageMetadata(identifier, img); + updateCoverageMetadataFromDescription(identifier, desc); + // Top level descriptions overwrite other metadata sources + if ( summ != null ) + updateCoverageMetadataFromSummary(identifier, summ); + // Check metadata validity + checkMetadataValidity(identifier); + } + catch (WCSException e) + { + throw e; + } + catch (Exception e) + { + e.printStackTrace(); + throw new WCSException("NoApplicableCode", e.getMessage()); + } + + /** + * (3) Indicate success: Add this ID to the output XML document + */ + CodeType id = new CodeType(); + id.setValue(identifier); + output.getIdentifier().add(id); + log("Finished action Add !"); + } + + /** + * Performs the action "Delete", as part of the Transaction operation + * + * @param identifier + * @param references + * @throws wcs.server.core.WCSException + */ + private void actionDeleteCoverage(String identifier, List references) throws WCSException + { + log("Executing action Delete Coverage ..."); + + // Error checking + if ( MetadataUtils.existsCoverage(meta, identifier) == false ) + { + throw new WCSException("InvalidParameterValue", "Identifier"); + } + + // (2) Do the actual processing + try + { + deleteCoverageFromRasdaman(identifier); + deleteCoverageMetadata(identifier); + } + catch (Exception e) + { + throw new WCSException(e.getMessage(), e.getStackTrace().toString()); + } + + // Indicate success: Add this ID to the output XML document + CodeType id = new CodeType(); + + id.setValue(identifier); + output.getIdentifier().add(id); + + log("Finished action Delete !"); + } + + /** + * Retrieve the full URN code of a string + * + * @param key Internal representation of a URN code + * @return the URN code + */ + private String getUrnCode(String key) throws WCSException + { + if ( key.equalsIgnoreCase("pixels") ) + { + return "urn:ogc:def:role:WCS:1.1:Pixels"; + } + if ( key.equalsIgnoreCase("description") ) + { + return "urn:ogc:def:role:WCS:1.1:CoverageDescription"; + } + if ( key.equalsIgnoreCase("summary") ) + { + return "urn:ogc:def:role:WCS:1.1:CoverageSummary"; + } + if ( key.equalsIgnoreCase("transform") ) + { + return "urn:ogc:def:role:WCS:1.1:GeoreferencingTransformation"; + } + if ( key.equalsIgnoreCase("other") ) + { + return "urn:ogc:def:role:WCS:1.1:OtherSource"; + } + + throw new WCSException("NoApplicableCode", "Unknown URN key '" + key + "'"); + } + + /** + * Get the Pixels Reference + * + * @param references List of References + * @return the Pixels Reference + */ + private ReferenceType getPixelsRef(List references) throws WCSException + { + ReferenceType result = searchReferenceList("pixels", references); + + return result; + } + + /** + * Get the Coverage Description Reference + * + * @param references List of References + * @return the Coverage Description Reference + */ + private ReferenceType getDescriptionRef(List references) throws WCSException + { + ReferenceType result = searchReferenceList("description", references); + + return result; + } + + /** + * Get the Coverage Summary Reference + * + * @param references List of References + * @return the Coverage Summary Reference + */ + private ReferenceType getSummaryRef(List references) throws WCSException + { + ReferenceType result = searchReferenceList("summary", references); + + return result; + } + + /** + * Get the Georeferencing Transform Reference + * + * @param references List of References + * @return the Georeferencing Transform Reference + */ + private ReferenceType getTransformRef(List references) throws WCSException + { + ReferenceType result = searchReferenceList("transform", references); + + return result; + } + + /** + * Get the "Other" Reference + * + * @param references List of References + * @return the "Other" Reference + */ + private ReferenceType getOtherRef(List references) throws WCSException + { + ReferenceType result = searchReferenceList("other", references); + + return result; + } + + /** + * Search a references list for a particular term + * @param key internal name of reference + * @param references List of references + * @return a Reference object + */ + private ReferenceType searchReferenceList(String key, List references) throws WCSException + { + String urn = getUrnCode(key); + Iterator i = references.iterator(); + + while (i.hasNext()) + { + Object obj = i.next(); + JAXBElement jelem = (JAXBElement) obj; + ReferenceType ref = (ReferenceType) jelem.getValue(); + String role = ref.getRole(); + + if ( role.equalsIgnoreCase(urn) ) + { + return ref; + } + } + + return null; + + } + + /** + * Check that the coverage metadata is consistent, and will not break the + * PetaScope implementation. Throws an exception if metadata is not valid. + * + * This function uses code from PetaScope + * implementation of WCPS. + * @param identifier Name of the coverage + * @throws WCSException on error + */ + private void checkMetadataValidity(String identifier) throws WCSException + { + try + { + // Try building a metadata object. + Metadata metadata = MetadataUtils.checkCoverageMetadata(meta, identifier); + // Now we have a valid Metadata object, so everything is ok :-) Chillax. + } + catch (WCSException wcse) + { + throw wcse; + } + catch (Exception e) + { + e.printStackTrace(); + throw new WCSException("NoApplicableCode", "Metadata is not valid " + + "for coverage " + identifier + ", because: " + e.getMessage()); + } + } + + /** + * Panel to display loaded image + * + * + * @version 09.Jul 2009 + * @author Andrei Aiordachioaie + */ + public static class ShowImage extends Panel + { + BufferedImage image; + + public ShowImage(BufferedImage img) + { + this.image = img; + } + + public void paint(Graphics g) + { + if ( image != null ) + { + g.drawImage(image, 0, 0, null); + } + } + } +} -- cgit