package nl.ipo.cds.deegree.metadata.provider;
import nl.ipo.cds.dao.metadata.MetadataDao;
import nl.ipo.cds.domain.metadata.ExtendedCapabilities;
import nl.ipo.cds.domain.metadata.Keyword;
import nl.ipo.cds.domain.metadata.SpatialDatasetinfo;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axiom.om.xpath.AXIOMXPath;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.deegree.commons.ows.metadata.DatasetMetadata;
import org.deegree.commons.ows.metadata.ServiceIdentification;
import org.deegree.commons.ows.metadata.ServiceProvider;
import org.deegree.commons.ows.metadata.party.Address;
import org.deegree.commons.ows.metadata.party.ContactInfo;
import org.deegree.commons.ows.metadata.party.ResponsibleParty;
import org.deegree.commons.ows.metadata.party.Telephone;
import org.deegree.commons.tom.ows.CodeType;
import org.deegree.commons.tom.ows.LanguageString;
import org.deegree.commons.tom.ows.Version;
import org.deegree.commons.utils.Pair;
import org.deegree.commons.utils.StringPair;
import org.deegree.services.metadata.OWSMetadataProvider;
import org.deegree.workspace.Resource;
import org.deegree.workspace.ResourceMetadata;
import org.jaxen.JaxenException;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
class DatabaseMetadataProvider implements OWSMetadataProvider {
protected static final String NS_INSPIRE_COMMON = "http://inspire.ec.europa.eu/schemas/common/1.0";
protected static final String NS_INSPIRE_DLS = "http://inspire.ec.europa.eu/schemas/inspire_dls/1.0";
private static final Log technicalLog = LogFactory.getLog(DatabaseMetadataProvider.class);
private final MetadataDao metadataDao;
private final Map<String, List<OMElement>> protocolVersionToExtendedCapabilitiesTemplates;
private final DatabaseMetadataProviderMetadata resourceMetadata;
private final String serviceId;
private final Map<String, String> externalMetadataAuthorities;
DatabaseMetadataProvider(final MetadataDao metadataDao, final Map<String, List<OMElement>> protocolVersionToExtendedCapabilitiesTemplates, final DatabaseMetadataProviderMetadata resourceMetadata) {
this.metadataDao = metadataDao;
this.resourceMetadata = resourceMetadata;
this.protocolVersionToExtendedCapabilitiesTemplates = protocolVersionToExtendedCapabilitiesTemplates;
Map<String, String> externalMetadataAuthorities = new HashMap<String, String>();
externalMetadataAuthorities.put("NL.IPO", "http://www.ipo.nl");
this.externalMetadataAuthorities = Collections.unmodifiableMap(externalMetadataAuthorities);
String mdId = resourceMetadata.getIdentifier().getId();
this.serviceId = mdId.substring(mdId.lastIndexOf("/") +1, mdId.lastIndexOf("_"));
technicalLog.info("DatabaseMetadataProvider: Init (serviceName: " + serviceId + ")");
}
@Override
public void destroy() {
}
@Override
public void init() {
}
/**
* Get the ServiceIdentification via the metadataDao. This result in a ServiceIdentification in
* the cds-inspire-domain. Convert this to the deegree-metadata-domain
*/
@Override
public ServiceIdentification getServiceIdentification() {
technicalLog.debug("DatabaseMetadataProvider: getServiceIdentification");
nl.ipo.cds.domain.metadata.ServiceIdentification serviceIdentification = this.metadataDao.findService(this.serviceId).getServiceIdentification();
List<LanguageString> titles = Arrays.asList(new LanguageString[]{new LanguageString(serviceIdentification.getTitle(), null)});
List<LanguageString> abstracts = Arrays.asList(new LanguageString[]{new LanguageString(serviceIdentification.getAbstract(), null)});
List<Pair<List<LanguageString>, CodeType>> keywords = new ArrayList<Pair<List<LanguageString>, CodeType>>();
for (Iterator<Keyword> iterator = serviceIdentification.getKeywords().iterator(); iterator.hasNext();) {
Keyword keyword = iterator.next();
Pair<List<LanguageString>, CodeType> deegreeKeword = new Pair<List<LanguageString>, CodeType>(Arrays.asList(new LanguageString[]{new LanguageString(keyword.getValue(), null)}), new CodeType(keyword.getValue(), keyword.getCodeSpace()));
keywords.add(deegreeKeword);
}
List<Version> serviceTypeVersions = null;//Arrays.asList(new Version[]{new Version(1, 2, 3)});
ServiceIdentification deegreeServiceIdentification = new ServiceIdentification(
serviceIdentification.getServiceType(),
titles,
abstracts,
keywords,
new CodeType(serviceIdentification.getServiceType()),
serviceTypeVersions,
new ArrayList<String>(),
serviceIdentification.getFees(),
serviceIdentification.getAccessContraints());
return deegreeServiceIdentification;
}
/**
* Extract the serviceName from the url to the metadata of this service
* The convention is that a metadata-service-name is like:
* http://url-to-deegree-workspace/<metadata-service-name>_metadata.xml
*
* @param url
* @return
*/
protected String retrieveServiceName(URL url) {
technicalLog.debug("DatabaseMetadataProvider: retrieveServiceName " + url);
String path = url.getPath();
return path.substring(path.lastIndexOf("/") +1, path.indexOf("_"));
}
/**
* Get the ServiceProvider via the metadataDao. This result in a ServiceProvider in
* the cds-inspire-domain. Convert this to the deegree-metadata-domain
*/
@Override
public ServiceProvider getServiceProvider() {
technicalLog.debug("DatabaseMetadataProvider: getServiceProvider ");
nl.ipo.cds.domain.metadata.ServiceProvider serviceProvider = this.metadataDao.findService(this.serviceId).getServiceProvider();
// Address
Address deegreeAddress = new Address();
deegreeAddress.setAdministrativeArea(serviceProvider.getAdministrativeArea());
deegreeAddress.setCity(serviceProvider.getCity());
deegreeAddress.setCountry(serviceProvider.getCountry());
deegreeAddress.setDeliveryPoint(serviceProvider.getDeliveryPoints());
deegreeAddress.setElectronicMailAddress(serviceProvider.getEmailAddresses());
deegreeAddress.setPostalCode(serviceProvider.getPostalCode());
// ContactInfo
ContactInfo deegreeContactInfo = new ContactInfo();
deegreeContactInfo.setAddress(deegreeAddress);
deegreeContactInfo.setContactInstructions(serviceProvider.getContactInstructions());
deegreeContactInfo.setHoursOfService(serviceProvider.getHoursOfService());
if(StringUtils.isNotBlank(serviceProvider.getOnlineResource()))
try {
deegreeContactInfo.setOnlineResource(new URL(serviceProvider.getOnlineResource()));
} catch (MalformedURLException mue) {
technicalLog.error("OnlineResource is not a valid URL", mue);
}
Telephone deegreePhone = new Telephone();
deegreePhone.setFacsimile(serviceProvider.getFaxNumbers());
deegreePhone.setVoice(serviceProvider.getPhoneNumbers());
deegreeContactInfo.setPhone(deegreePhone );
// ServiceContact
ResponsibleParty deegreeServiceContact = new ResponsibleParty();
deegreeServiceContact.setContactInfo(deegreeContactInfo);
deegreeServiceContact.setIndividualName(serviceProvider.getIndividualName());
deegreeServiceContact.setOrganizationName(serviceProvider.getOrganizationName());
deegreeServiceContact.setPositionName(serviceProvider.getPositionName());
if(StringUtils.isNotBlank(serviceProvider.getRole())){
CodeType deegreeRole = new CodeType(serviceProvider.getRole());
deegreeServiceContact.setRole(deegreeRole);
}
// ServiceProvider
ServiceProvider deegreeServiceProvider = new ServiceProvider(serviceProvider.getProviderName(), serviceProvider.getProviderSite(), deegreeServiceContact);
return deegreeServiceProvider;
}
private void replaceText(OMElement doc, String path, String replacementText) throws JaxenException {
if(replacementText != null) {
AXIOMXPath xpath = new AXIOMXPath(path);
xpath.addNamespace("inspire_common", NS_INSPIRE_COMMON);
xpath.addNamespace("inspire_dls", NS_INSPIRE_DLS);
OMElement element = (OMElement)xpath.selectSingleNode(doc);
if(element != null) {
element.setText(replacementText);
}
}
}
/**
* Converts an XML string into an OMElement
*
* @param xml String representation of the XML
* @return <code>OMElement</code> instance
*/
private OMElement toOM(String xml) {
try {
XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(
new StringReader(xml));
StAXOMBuilder builder = new StAXOMBuilder(reader);
return builder.getDocumentElement();
} catch (Exception e) {
String msg = "Error creating a OMElement from text : " + e.getMessage();
technicalLog.error(msg, e);
throw new RuntimeException(msg, e);
}
}
private void addChildNodeElement(OMElement doc, String path, String content) throws JaxenException {
if(content != null) {
AXIOMXPath xpath = new AXIOMXPath(path);
xpath.addNamespace("inspire_common", NS_INSPIRE_COMMON);
xpath.addNamespace("inspire_dls", NS_INSPIRE_DLS);
OMElement parent = (OMElement)xpath.selectSingleNode(doc);
if(parent != null) {
OMElement child = toOM(content);
parent.addChild(child);
}
}
}
/**
* Creating the ExtendedCapabilities is done, by retrieving the ExtendedCapabilities templates
* and replacing the values of elements <inspire_common:URL> and <inspire_dls:SpatialDataSetIdentifier>
* with the content from the database.
*/
@Override
public Map<String, List<OMElement>> getExtendedCapabilities() {
technicalLog.debug("DatabaseMetadataProvider: getExtendedCapabilities for " + serviceId);
ExtendedCapabilities databaseExtendedCapabilities = metadataDao.findService(this.serviceId).getExtendedCapabilities();
if(databaseExtendedCapabilities == null) {
technicalLog.warn("Database doesn't contain extended capabilities for " + serviceId);
}
Map<String, List<OMElement>> extendedCapabilities = new HashMap<String, List<OMElement>>();
for (Entry<String,List<OMElement>> versionToTemplate : protocolVersionToExtendedCapabilitiesTemplates.entrySet()) {
String version = versionToTemplate.getKey();
String spatialDataSetIdentifierContent;
List<OMElement> extendedCapabilitiesElements = new ArrayList<OMElement>();
extendedCapabilities.put(version, extendedCapabilitiesElements);
for (OMElement templateEl : versionToTemplate.getValue()) {
try {
OMElement extCapElement = templateEl.cloneOMElement();
if(databaseExtendedCapabilities != null) {
replaceText(extCapElement, "//inspire_common:MetadataUrl/inspire_common:URL", databaseExtendedCapabilities.getMetadataUrl());
List<SpatialDatasetinfo> spatialDatasetinfoList = metadataDao.findService(this.serviceId).getSpatialDatasetinfos();
//SpatialDataSetIdentifier spatialDataSetIdentifier = databaseExtendedCapabilities.getSpatialDataSetIdentifier();
for(SpatialDatasetinfo spatialDatasetinfo: spatialDatasetinfoList) {
spatialDataSetIdentifierContent = "";
// replaceText(extCapElement, "//inspire_dls:SpatialDataSetIdentifier/inspire_common:Code", spatialDatasetinfo.getCode());
// replaceText(extCapElement, "//inspire_dls:SpatialDataSetIdentifier/inspire_common:Namespace", spatialDatasetinfo.getNamespace());
spatialDataSetIdentifierContent += "<inspire_dls:SpatialDataSetIdentifier xmlns:inspire_dls=\"http://inspire.ec.europa.eu/schemas/inspire_dls/1.0\">";
spatialDataSetIdentifierContent += "<inspire_common:Code xmlns:inspire_common=\"http://inspire.ec.europa.eu/schemas/common/1.0\">" + spatialDatasetinfo.getCode() + "</inspire_common:Code>";
spatialDataSetIdentifierContent += "<inspire_common:Namespace xmlns:inspire_common=\"http://inspire.ec.europa.eu/schemas/common/1.0\">" + spatialDatasetinfo.getNamespace() + "</inspire_common:Namespace>";
spatialDataSetIdentifierContent += "</inspire_dls:SpatialDataSetIdentifier>";
addChildNodeElement(extCapElement, "//inspire_dls:ExtendedCapabilities", spatialDataSetIdentifierContent);
}
if(spatialDatasetinfoList.size() <= 0) {
technicalLog.warn("spatialDataSetIdentifier is missing");
}
}
extendedCapabilitiesElements.add(extCapElement);
} catch (Exception e) {
String msg = "Error creating ExtendedCapabilities from template: " + e.getMessage();
technicalLog.error(msg, e);
throw new RuntimeException(msg, e);
}
}
}
return extendedCapabilities;
}
@Override
public List<DatasetMetadata> getDatasetMetadata() {
technicalLog.debug("DatabaseMetadataProvider: getDatasetMetadata ");
// source
return new ArrayList<DatasetMetadata>(createDeegreeDatasetMetadatas().values());
}
/**
* Utility-method to build up the complete map with DatasetMetadata objects.
* DatasetMetadata-objects are used to print the MetadataURL/OnlineResource/@xlink:href xml-attribute in
* the GetCapabilities response
* @return
*/
private Map<QName, DatasetMetadata> createDeegreeDatasetMetadatas() {
technicalLog.debug("DatabaseMetadataProvider: createDeegreeDatasetMetadatas ");
List<nl.ipo.cds.domain.metadata.DatasetMetadata> datasetMetadatas = this.metadataDao.findService(this.serviceId).getDatasetMetadatas();
//target
Map<QName, DatasetMetadata> deegreeDatasetMetadatas = new HashMap<QName, DatasetMetadata>(datasetMetadatas.size());
List<StringPair> ipoId;
String url = "";
List<SpatialDatasetinfo> spatialDatasetinfoList = metadataDao.findService(this.serviceId).getSpatialDatasetinfos();
for (Iterator<nl.ipo.cds.domain.metadata.DatasetMetadata> iterator = datasetMetadatas.iterator(); iterator
.hasNext();) {
nl.ipo.cds.domain.metadata.DatasetMetadata datasetMetadata = iterator.next();
for (SpatialDatasetinfo spatialDatasetinfo : spatialDatasetinfoList) {
if (datasetMetadata.getName().equals(spatialDatasetinfo.getName())) {
ipoId = Arrays.asList(new StringPair("NL.IPO", spatialDatasetinfo.getCode()));
url = datasetMetadata.getUrl();
url = url.replaceAll("id=(.*?\\&)", "id=" + spatialDatasetinfo.getNamespace() + "&");
DatasetMetadata deegreeDatasetMetadata = null;
if (datasetMetadata.getName().contains(":")) {
deegreeDatasetMetadata = new DatasetMetadata(new QName(datasetMetadata.getNamespace(), datasetMetadata.getName().split(":")[1], datasetMetadata.getName().split(":")[0]), null, null, null, url, ipoId);
} else {
deegreeDatasetMetadata = new DatasetMetadata(new QName(datasetMetadata.getNamespace(), datasetMetadata.getName()), null, null, null, url, ipoId);
}
deegreeDatasetMetadatas.put(deegreeDatasetMetadata.getQName(), deegreeDatasetMetadata);
}
}
}
return deegreeDatasetMetadatas;
}
@Override
public DatasetMetadata getDatasetMetadata(QName qName) {
return this.createDeegreeDatasetMetadatas().get(qName);
}
@Override
public ResourceMetadata<? extends Resource> getMetadata() {
return resourceMetadata;
}
@Override
public Map<String, String> getExternalMetadataAuthorities() {
return externalMetadataAuthorities;
}
}