/*
* 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.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import org.odmg.ODMGException;
import wcps.server.core.Metadata;
import wcst.server.ConfigManager;
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 synchronous processing.
*
* @author Andrei Aiordachioaie
*/
public class executeTransaction
{
private static boolean printLog = true;
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 WCSTException
{
input = tr;
output = new TransactionResponseType();
finished = false;
metadataSettingsPath = metadataDbPath;
meta = new MetadataDb(metadataSettingsPath);
String server = ConfigManager.RASDAMAN_URL;
String db = ConfigManager.RASDAMAN_DATABASE;
rasUtils = new RasdamanUtils(server, db);
rasUtils.init();
// In case no-one 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 WCSTException
*/
public TransactionResponseType get() throws WCSTException
{
try
{
if ( finished == false )
{
process();
}
}
catch (WCSTException e)
{
e.printStackTrace();
throw e;
}
if ( finished == false )
{
throw new WCSTException("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 WCSTException
{
if ( ! input.getService().equalsIgnoreCase("WCS") )
{
throw new WCSTException("InvalidParameterValue", "Service", "Service must be \"WCS\" !");
}
if ( ! input.getVersion().equalsIgnoreCase("1.1") )
{
throw new WCSTException("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 WCSTException("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 WCSTException("NoApplicableCode", "Could not commit metadata changes !");
}
}
catch (WCSTException 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 WCSTException("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
rasUtils.insertGrayImageAsArray(identifier, img);
log("Inserted image into Rasdaman !");
}
catch (Exception e)
{
System.err.println("Error !");
e.printStackTrace();
}
}
/** Load a BufferedImage from a ReferenceType object.
*
* @param pixels Reference object
* @return available image
* @throws WCSTException
*/
private BufferedImage loadPixelsReference(ReferenceType pixels) throws WCSTException
{
URL url = null;
BufferedImage img = null;
try
{
url = new URL(pixels.getHref());
}
catch (MalformedURLException e)
{
log("URL " + url.toString() + " is not valid !");
throw new WCSTException("InvalidParameterValue", "Reference pixels");
}
try
{
img = ImageIO.read(url);
}
catch (IOException ex)
{
throw new WCSTException("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 WCSTException
*/
private CoverageDescriptionType loadDescriptionReference(String identifier, ReferenceType desc) throws WCSTException
{
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 WCSTException("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 WCSTException("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 WCSTException("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 WCSTException("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 WCSTException("NoApplicableCode", "Could not find a CoverageDescription for coverage: " + identifier);
return desc0;
}
private CoverageSummaryType loadCoverageSummary(ReferenceType pixels) throws WCSTException
{
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 WCSTException("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 WCSTException("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 WCSTException("NoApplicableCode", "Coverage " + "Summary metadata could not be loaded !");
}
catch (javax.xml.bind.JAXBException ex)
{
log("Could not unmarshall the CoverageSummaryXML document !");
throw new WCSTException("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 WCSTException
{
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 WCSTException("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(ConfigManager.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 WCSTException on error
*/
private void insertDefaultCoverageMetadata(String identifier, BufferedImage img) throws WCSTException
{
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 WCSTException("NoApplicableCode", "Could not find default" + " interpolation method: 'none'");
}
if ( defnullresist == -1 )
{
throw new WCSTException("NoApplicableCode", "Could not find default null resistance: 'none'");
}
if ( MetadataUtils.setAllCoverageMetadata(meta, identifier, null, null, "0", sinterp, snullresist) == false )
{
throw new WCSTException("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 WCSTException("NoApplicableCode", "Could not find axis X in axis-types !");
}
if ( axisY == -1 )
{
throw new WCSTException("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 WCSTException("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 WCSTException("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 WCSTException("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 WCSTException("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 WCSTException("NoApplicableCode", "Cannot find datatype 'unsigned char', needed for gray images !");
}
if ( MetadataUtils.setRange(meta, coverageNumId, 0, "gray-intensity", datatypeId) == false )
{
throw new WCSTException("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 WCSTException("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 WCSTException("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 WCSTException("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 WCSTException
{
if ( elem.getAction() == null )
{
throw new WCSTException("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 WCSTException("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 WCSTException("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 WCSTException
{
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 WCSTException
{
log("Updating metadata with values from CoverageDescription...");
if ( MetadataUtils.existsCoverage(meta, identifier) == false )
throw new WCSTException("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 WCSTException("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 WCSTException("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 WCSTException("NoApplicableCode", "Unknown datatype: " + datatype);
if ( MetadataUtils.setRange(meta, coverageId, fieldCount, name, typeId) == false )
throw new WCSTException("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 WCSTException("NoApplicableCode", "Unknown interpolation method: " + method);
int nullId = MetadataUtils.getIdByNameGeneral(meta, "ps_nullresistance", "nullresistance", resist);
if ( nullId == -1 )
throw new WCSTException("NoApplicableCode", "Unknown null resistance: " + resist);
if ( MetadataUtils.setDefaultCoverageInterpolation(meta, identifier, interpId, nullId) == false )
throw new WCSTException("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 WCSTException("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 WCSTException("InvalidParameter", "LowerCorner");
if ( upper.size() != 2 )
throw new WCSTException("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 WCSTException("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 WCSTException("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 WCSTException
{
log("Executing action UpdateDataPart ...");
// Error checking
if ( MetadataUtils.existsCoverage(meta, identifier) == false )
{
throw new WCSTException("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 WCSTException("MissingParameterValue", "Reference role='" + getUrnCode("pixels") + "'");
}
if (desc == null)
{
throw new WCSTException("MissingParameterValue", "Reference role='" + getUrnCode("description") + "'");
}
// (2) Do the actual processing
try
{
insertSomePixelsIntoRasdaman(identifier, pixels.getHref(), desc.getHref());
}
catch (Exception e)
{
e.printStackTrace();
throw new WCSTException(e.getMessage(), e.getStackTrace().toString());
}
}
/**
* Performs the action "UpdateMetadata", as part of the Transaction operation
*
* @param identifier
* @param references
* @throws wcs.server.core.WCSTException
*/
private void actionUpdateMetadata(String identifier, List references) throws WCSTException
{
log("Executing action Update Metadata...");
// Only change the metadata for an existing coverage
if ( MetadataUtils.existsCoverage(meta, identifier) == false )
{
throw new WCSTException("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 WCSTException("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 WCSTException("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.WCSTException
*/
private void actionAddCoverage(String identifier, List references) throws WCSTException
{
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 WCSTException("MissingParameterValue", "Reference role='" + getUrnCode("pixels") + "'");
}
if ( descRef == null )
{
throw new WCSTException("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 (gray-scale) images. */
if (img.getColorModel().getNumComponents() != 1)
throw new WCSTException("MultiBandImagesNotSupported", "This server currently only supports one-band images (grayscale).");
insertImageIntoRasdaman(identifier, img);
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 (WCSTException e)
{
throw e;
}
catch (Exception e)
{
e.printStackTrace();
throw new WCSTException("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.WCSTException
*/
private void actionDeleteCoverage(String identifier, List references) throws WCSTException
{
log("Executing action Delete Coverage ...");
// Error checking
if ( MetadataUtils.existsCoverage(meta, identifier) == false )
{
throw new WCSTException("InvalidParameterValue", "Identifier");
}
// (2) Do the actual processing
try
{
deleteCoverageFromRasdaman(identifier);
deleteCoverageMetadata(identifier);
}
catch (Exception e)
{
throw new WCSTException(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 WCSTException
{
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 WCSTException("NoApplicableCode", "Unknown URN key '" + key + "'");
}
/**
* Get the Pixels Reference
*
* @param references List of References
* @return the Pixels Reference
*/
private ReferenceType getPixelsRef(List references) throws WCSTException
{
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 WCSTException
{
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 WCSTException
{
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 WCSTException
{
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 WCSTException
{
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 WCSTException
{
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 WCSTException on error
*/
private void checkMetadataValidity(String identifier) throws WCSTException
{
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 (WCSTException wcse)
{
throw wcse;
}
catch (Exception e)
{
e.printStackTrace();
throw new WCSTException("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);
}
}
}
}