org.fao.geonet.component.csw.GetCapabilities.java Source code

Java tutorial

Introduction

Here is the source code for org.fao.geonet.component.csw.GetCapabilities.java

Source

//=============================================================================
//===   Copyright (C) 2001-2007 Food and Agriculture Organization of the
//===   United Nations (FAO-UN), United Nations World Food Programme (WFP)
//===   and United Nations Environment Programme (UNEP)
//===
//===   This program is free software; you can redistribute it and/or modify
//===   it under the terms of the GNU General Public License as published by
//===   the Free Software Foundation; either version 2 of the License, or (at
//===   your option) any later version.
//===
//===   This program 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
//===   General Public License for more details.
//===
//===   You should have received a copy of the GNU General Public License
//===   along with this program; if not, write to the Free Software
//===   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
//===
//===   Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
//===   Rome - Italy. email: geonetwork@osgeo.org
//==============================================================================

package org.fao.geonet.component.csw;

import com.google.common.base.Function;
import com.google.common.collect.Lists;

import org.apache.commons.lang.StringUtils;
import org.fao.geonet.GeonetContext;
import org.fao.geonet.Util;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.csw.common.Csw;
import org.fao.geonet.csw.common.exceptions.CatalogException;
import org.fao.geonet.csw.common.exceptions.NoApplicableCodeEx;
import org.fao.geonet.csw.common.exceptions.VersionNegotiationFailedEx;
import org.fao.geonet.domain.Address;
import org.fao.geonet.domain.Language;
import org.fao.geonet.domain.User;
import org.fao.geonet.kernel.SchemaManager;
import org.fao.geonet.kernel.csw.CatalogConfiguration;
import org.fao.geonet.kernel.csw.CatalogService;
import org.fao.geonet.kernel.csw.services.AbstractOperation;
import org.fao.geonet.kernel.csw.services.getrecords.FieldMapper;
import org.fao.geonet.kernel.search.LuceneConfig;
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.kernel.setting.Settings;
import org.fao.geonet.lib.Lib;
import org.fao.geonet.repository.CswCapabilitiesInfo;
import org.fao.geonet.repository.CswCapabilitiesInfoFieldRepository;
import org.fao.geonet.repository.LanguageRepository;
import org.fao.geonet.repository.UserRepository;
import org.fao.geonet.utils.Log;
import org.fao.geonet.utils.Xml;
import org.jdom.Element;
import org.jdom.Namespace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Component;

import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.ServletContext;

import jeeves.server.context.ServiceContext;
import jeeves.server.overrides.ConfigurationOverrides;

import static org.fao.geonet.kernel.setting.SettingManager.isPortRequired;

/**
 * TODO javadoc.
 */
@Component(CatalogService.BEAN_PREFIX + GetCapabilities.NAME)
public class GetCapabilities extends AbstractOperation implements CatalogService {
    //---------------------------------------------------------------------------
    //---
    //--- Constructor
    //---
    //---------------------------------------------------------------------------

    static final String NAME = "GetCapabilities";
    @Autowired
    private LuceneConfig _luceneConfig;
    @Autowired
    private CatalogConfiguration _catalogConfig;
    @Autowired
    private FieldMapper _fieldMapper;
    @Autowired
    private SchemaManager _schemaManager;
    //---------------------------------------------------------------------------
    //---
    //--- API methods
    //---
    //---------------------------------------------------------------------------

    public String getName() {
        return NAME;
    }

    /**
     * TODO javadoc.
     */
    public Element execute(Element request, ServiceContext context) throws CatalogException {

        checkService(request);
        checkAcceptVersions(request);

        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        boolean inspireEnabled = gc.getBean(SettingManager.class).getValueAsBool(Settings.SYSTEM_INSPIRE_ENABLE,
                false);

        //--- return capabilities

        Path file;

        if (inspireEnabled) {
            file = context.getAppPath().resolve("xml").resolve("csw").resolve("capabilities_inspire.xml");
        } else {
            file = context.getAppPath().resolve("xml").resolve("csw").resolve("capabilities.xml");
        }

        try {
            Element capabilities = Xml.loadFile(file);
            ServletContext servletContext = null;
            if (context.getServlet() != null) {
                servletContext = context.getServlet().getServletContext();
            }
            ConfigurationOverrides.DEFAULT.updateWithOverrides(file.toString(), servletContext,
                    context.getAppPath(), capabilities);

            String cswServiceSpecificContraint = request.getChildText(Geonet.Elem.FILTER);
            setKeywords(capabilities, context, cswServiceSpecificContraint);
            setOperationsParameters(capabilities);

            String currentLanguage = "";

            // INSPIRE: Use language parameter if available, otherwise use default (using context.getLanguage())
            if (inspireEnabled) {
                String isoLangParamValue = request.getAttributeValue("language");

                final LanguageRepository languageRepository = context.getBean(LanguageRepository.class);
                List<Language> languageList = languageRepository.findAllByInspireFlag(true);

                List<String> langCodes = Lists.transform(languageList, new Function<Language, String>() {
                    @Nullable
                    @Override
                    public String apply(@Nonnull Language input) {
                        return input.getId();
                    }
                });

                if (isoLangParamValue != null) {
                    // Retrieve GN language id from Iso language id
                    if (langCodes.contains(isoLangParamValue)) {
                        currentLanguage = isoLangParamValue;
                    }
                }

                String defaultLanguageId = context.getLanguage();
                try {
                    Language defaultLanguage = languageRepository.findOneByDefaultLanguage();

                    if (StringUtils.isEmpty(currentLanguage)) {
                        currentLanguage = defaultLanguage.getId();
                        defaultLanguageId = defaultLanguage.getId();
                    }
                } catch (EmptyResultDataAccessException e) {
                    Log.error(Geonet.CSW,
                            "No default language set in database languages table. "
                                    + "You MUST set one default language (using isDefault column). "
                                    + "Using session language as default. Error is: " + e.getMessage());
                    currentLanguage = context.getLanguage();
                }

                setInspireLanguages(capabilities, langCodes, currentLanguage, defaultLanguageId);
            } else {
                currentLanguage = context.getLanguage();
            }

            final CswCapabilitiesInfoFieldRepository infoRepository = context
                    .getBean(CswCapabilitiesInfoFieldRepository.class);
            CswCapabilitiesInfo cswCapabilitiesInfo = infoRepository.findCswCapabilitiesInfo(currentLanguage);

            // Retrieve contact data from users table
            String contactId = gc.getBean(SettingManager.class).getValue(Settings.SYSTEM_CSW_CONTACT_ID);
            if ((contactId == null) || (contactId.equals(""))) {
                contactId = "-1";
            }
            User user = context.getBean(UserRepository.class).findOne(contactId);

            substitute(context, capabilities, cswCapabilitiesInfo, user, currentLanguage);

            handleSections(request, capabilities);

            //
            // in read-only mode, remove publication services from capabilities
            //
            if (gc.isReadOnly()) {
                capabilities = removePublicationServices(capabilities);
            }

            return capabilities;
        } catch (Exception e) {
            Log.error(Geonet.CSW, "Cannot load/process capabilities");
            Log.error(Geonet.CSW, " (C) StackTrace\n" + Util.getStackTrace(e));

            throw new NoApplicableCodeEx("Cannot load/process capabilities");
        }
    }

    /**
     * Removes CSW Harvest and CSW Transaction operations from Capabilities.
     *
     * @param capabilities the capabilities document
     * @return capabilities stripped of Harvest and Transaction
     */
    private Element removePublicationServices(Element capabilities) {
        Element operationsMetadata = capabilities.getChild(Csw.SECTION_OM, Csw.NAMESPACE_OWS);
        Element harvest = null;
        Element transaction = null;
        if (operationsMetadata != null) {
            @SuppressWarnings("unchecked")
            List<Element> operations = operationsMetadata.getChildren(Csw.OPERATION, Csw.NAMESPACE_OWS);
            for (Element operation : operations) {
                if (operation.getAttributeValue(Csw.ConfigFile.Operation.Attr.NAME)
                        .equals(Csw.ConfigFile.Operation.Attr.Value.TRANSACTION)) {
                    transaction = operation;
                } else if (operation.getAttributeValue(Csw.ConfigFile.Operation.Attr.NAME)
                        .equals(Csw.ConfigFile.Operation.Attr.Value.HARVEST)) {
                    harvest = operation;
                }
            }
            if (harvest != null) {
                operationsMetadata.removeContent(harvest);
            }
            if (transaction != null) {
                operationsMetadata.removeContent(transaction);
            }
        }
        return capabilities;
    }

    /**
     * TODO javadoc.
     */
    public Element adaptGetRequest(Map<String, String> params) {
        String service = params.get("service");
        String sections = params.get("sections");
        String sequence = params.get("updatesequence");
        String acceptVers = params.get("acceptversions");
        String acceptForm = params.get("acceptformats");
        String language = params.get("language");

        Element request = new Element(getName(), Csw.NAMESPACE_CSW);

        setAttrib(request, "service", service);
        setAttrib(request, "updateSequence", sequence);
        setAttrib(request, "language", language);

        fill(request, "AcceptVersions", "Version", acceptVers, Csw.NAMESPACE_OWS);
        fill(request, "Sections", "Section", sections, Csw.NAMESPACE_OWS);
        fill(request, "AcceptFormats", "OutputFormat", acceptForm, Csw.NAMESPACE_OWS);

        return request;
    }

    //---------------------------------------------------------------------------

    public Element retrieveValues(String parameterName) throws CatalogException {
        // TODO
        return null;
    }

    //---------------------------------------------------------------------------
    //---
    //--- Private methods
    //---
    //---------------------------------------------------------------------------

    /**
     * TODO javadoc.
     */
    private void checkAcceptVersions(Element request) throws CatalogException {
        Element versions = request.getChild("AcceptVersions", Csw.NAMESPACE_OWS);

        if (versions == null)
            return;

        @SuppressWarnings("unchecked")
        Iterator<Element> i = versions.getChildren().iterator();

        StringBuffer sb = new StringBuffer();

        while (i.hasNext()) {
            Element version = i.next();

            if (version.getText().equals(Csw.CSW_VERSION))
                return;

            sb.append(version.getText());

            if (i.hasNext())
                sb.append(",");
        }

        throw new VersionNegotiationFailedEx(sb.toString());
    }

    /**
     * TODO javadoc.
     */
    private void handleSections(Element request, Element capabilities) {
        Element sections = request.getChild("Sections", Csw.NAMESPACE_OWS);

        if (sections == null)
            return;

        //--- handle 'section' parameters

        HashSet<String> hsSections = new HashSet<String>();

        for (Object o : sections.getChildren()) {
            Element section = (Element) o;
            String sectionName = section.getText();
            // Handle recognized section names only, others are ignored. Case Sensitive.
            if (sectionName.equals(Csw.SECTION_SI) || sectionName.equals(Csw.SECTION_SP)
                    || sectionName.equals(Csw.SECTION_OM)) {
                hsSections.add(sectionName);
            }
        }

        //--- remove not requested sections

        if (!hsSections.contains("ServiceIdentification"))
            capabilities.getChild("ServiceIdentification", Csw.NAMESPACE_OWS).detach();

        if (!hsSections.contains("ServiceProvider"))
            capabilities.getChild("ServiceProvider", Csw.NAMESPACE_OWS).detach();

        if (!hsSections.contains("OperationsMetadata"))
            capabilities.getChild("OperationsMetadata", Csw.NAMESPACE_OWS).detach();

    }

    /**
     * TODO javadoc.
     */
    private void substitute(ServiceContext context, Element capab, CswCapabilitiesInfo cswCapabilitiesInfo,
            User contact, String langId) throws Exception {
        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        SettingManager sm = gc.getBean(SettingManager.class);

        HashMap<String, String> vars = new HashMap<String, String>();

        String protocol = sm.getValue(Settings.SYSTEM_SERVER_PROTOCOL);
        vars.put("$PROTOCOL", protocol);
        vars.put("$HOST", sm.getValue(Settings.SYSTEM_SERVER_HOST));

        String insecureport = sm.getValue(Settings.SYSTEM_SERVER_PORT);
        String secureport = sm.getValue(Settings.SYSTEM_SERVER_SECURE_PORT);

        String port = "https".equals(protocol) ? secureport : insecureport;
        vars.put("$PORT", isPortRequired(protocol, port) ? ":" + port : "");
        vars.put("$END-POINT", context.getService());
        vars.put("$NODE_ID", context.getNodeId());

        String providerName = sm.getValue(Settings.SYSTEM_SITE_ORGANIZATION);
        vars.put("$PROVIDER_NAME", StringUtils.isNotEmpty(providerName) ? providerName : "GeoNetwork opensource");

        vars.put("$SERVLET", context.getBaseUrl());

        // Set CSW contact information
        if (contact != null) {
            vars.put("$IND_NAME", contact.getName() + " " + contact.getSurname());
            vars.put("$ORG_NAME", contact.getOrganisation());
            vars.put("$POS_NAME", contact.getProfile().name());
            vars.put("$VOICE", "");
            vars.put("$FACSCIMILE", "");
            final Address address = contact.getPrimaryAddress();
            vars.put("$DEL_POINT", address.getAddress());
            vars.put("$CITY", address.getCity());
            vars.put("$ADMIN_AREA", address.getState());
            vars.put("$POSTAL_CODE", address.getZip());
            vars.put("$COUNTRY", address.getCountry());
            vars.put("$EMAIL", contact.getEmail());
            vars.put("$HOUROFSERVICE", "");
            vars.put("$CONTACT_INSTRUCTION", "");
        } else {
            vars.put("$IND_NAME", "");
            vars.put("$ORG_NAME", "");
            vars.put("$POS_NAME", "");
            vars.put("$VOICE", "");
            vars.put("$FACSCIMILE", "");
            vars.put("$DEL_POINT", "");
            vars.put("$CITY", "");
            vars.put("$ADMIN_AREA", "");
            vars.put("$POSTAL_CODE", "");
            vars.put("$COUNTRY", "");
            vars.put("$EMAIL", "");
            vars.put("$HOUROFSERVICE", "");
            vars.put("$CONTACT_INSTRUCTION", "");
        }

        vars.put("$TITLE", cswCapabilitiesInfo.getTitle());
        vars.put("$ABSTRACT", cswCapabilitiesInfo.getAbstract());
        vars.put("$FEES", cswCapabilitiesInfo.getFees());
        vars.put("$ACCESS_CONSTRAINTS", cswCapabilitiesInfo.getAccessConstraints());

        vars.put("$LOCALE", langId);

        Lib.element.substitute(capab, vars);
    }

    /**
     * TODO javadoc.
     */
    private void setInspireLanguages(Element capabilities, List<String> languages, String currLang,
            String defaultLang) {
        Element inspireExtCapabilities = capabilities.getChild("OperationsMetadata", Csw.NAMESPACE_OWS)
                .getChild("ExtendedCapabilities", Csw.NAMESPACE_INSPIRE_DS);

        Element inspireLanguages = inspireExtCapabilities.getChild("SupportedLanguages", Csw.NAMESPACE_INSPIRE_COM);

        if (defaultLang == null)
            defaultLang = "eng";

        try {
            // Add DefaultLanguage
            for (String lang : languages) {
                Element defaultLanguage;
                Element language;

                if (lang.equalsIgnoreCase(defaultLang)) {
                    defaultLanguage = new Element("DefaultLanguage", Csw.NAMESPACE_INSPIRE_COM);
                    language = new Element("Language", Csw.NAMESPACE_INSPIRE_COM);

                    language.setText(lang);
                    @SuppressWarnings("unchecked")
                    List<Element> defaultLangChildren = defaultLanguage.getChildren();
                    defaultLangChildren.add(language);
                    @SuppressWarnings("unchecked")
                    List<Element> inspireLanguagesChildren = inspireLanguages.getChildren();
                    inspireLanguagesChildren.add(defaultLanguage);

                    break;
                }
            }

            // Add list of supported languages
            for (String lang : languages) {
                Element supportedLanguage;
                Element language;

                if (!(lang.equalsIgnoreCase(defaultLang))) {
                    supportedLanguage = new Element("SupportedLanguage", Csw.NAMESPACE_INSPIRE_COM);
                    language = new Element("Language", Csw.NAMESPACE_INSPIRE_COM);

                    language.setText(lang);
                    @SuppressWarnings("unchecked")
                    List<Element> supportedLanguageChildren = supportedLanguage.getChildren();
                    supportedLanguageChildren.add(language);
                    @SuppressWarnings("unchecked")
                    List<Element> inspireLanguagesChildren = inspireLanguages.getChildren();
                    inspireLanguagesChildren.add(supportedLanguage);
                }

            }

            // Current language
            HashMap<String, String> vars = new HashMap<String, String>();
            if (languages.contains(currLang)) {
                vars.put("$INSPIRE_LOCALE", currLang);

            } else {
                vars.put("$INSPIRE_LOCALE", defaultLang);
            }

            Lib.element.substitute(capabilities, vars);

        } catch (Exception ex) {
            // TODO: handle exception
            ex.printStackTrace();
        }

    }

    /**
     * Defines keyword section of the GetCapabilities document according to catalogue content.
     * Reading  Lucene index, most popular keywords are added to the document.
     */
    private void setKeywords(Element capabilities, ServiceContext context, String cswServiceSpecificContraint) {
        Element serviceIdentificationEl = capabilities.getChild("ServiceIdentification", Csw.NAMESPACE_OWS);
        @SuppressWarnings("unchecked")
        List<Element> keywords = serviceIdentificationEl.getChildren("Keywords", Csw.NAMESPACE_OWS);

        List<Element> values;
        String[] properties = { "keyword" };
        try {
            values = GetDomain.handlePropertyName(_catalogConfig, properties, context, true,
                    _catalogConfig.getMaxNumberOfRecordsForKeywords(), cswServiceSpecificContraint, _luceneConfig);
        } catch (Exception e) {
            Log.error(Geonet.CSW, "Error getting domain value for specified PropertyName : " + e);
            // If GetDomain operation failed, just add nothing to the capabilities document template.
            return;
        }

        for (Element k : keywords) {
            Element keyword;
            int cpt = 0;
            for (Element v : values) {
                keyword = new Element("Keyword", Csw.NAMESPACE_OWS);
                keyword.setText(v.getText());
                k.addContent(keyword);
                cpt++;
                if (cpt == _catalogConfig.getNumberOfKeywords()) {
                    break;
                }
            }
            // Add <ows:Type>theme</ows:Type>
            k.addContent(new Element("Type", Csw.NAMESPACE_OWS).setText("theme"));
            break; // only for first Keywords element in case of several.
        }
    }

    /**
     * TODO javadoc.
     */
    private void setOperationsParameters(Element capabilities) {

        @SuppressWarnings("unchecked")
        List<Element> operations = capabilities.getChild("OperationsMetadata", Csw.NAMESPACE_OWS)
                .getChildren("Operation", Csw.NAMESPACE_OWS);

        for (Element op : operations) {
            if (op.getAttributeValue(Csw.ConfigFile.Operation.Attr.NAME)
                    .equals(Csw.ConfigFile.Operation.Attr.Value.GET_RECORDS)) {
                fillGetRecordsParams(op);
                continue;
            }
            if (op.getAttributeValue(Csw.ConfigFile.Operation.Attr.NAME)
                    .equals(Csw.ConfigFile.Operation.Attr.Value.GET_RECORD_BY_ID)) {
                populateTypeNameAndOutputSchema(op);
                continue;
            }
            if (op.getAttributeValue(Csw.ConfigFile.Operation.Attr.NAME)
                    .equals(Csw.ConfigFile.Operation.Attr.Value.DESCRIBE_RECORD)) {
                populateTypeNameAndOutputSchema(op);
            }
        }
    }

    /**
     * Based on loaded schema plugins populate the list of typeNames and outputSchema available in
     * that catalog.
     *
     * <pre>
     *   <ows:Parameter xmlns:gfc="http://www.isotc211.org/2005/gfc" name="outputSchema">
     *      <!-- Set depending on schema plugins -->
     *      <ows:Value>http://www.opengis.net/cat/csw/2.0.2</ows:Value>
     *      <ows:Value>http://www.isotc211.org/2005/gfc</ows:Value>
     *      <ows:Value>http://www.isotc211.org/2005/gmd</ows:Value>
     *    </ows:Parameter>
     *    <ows:Parameter xmlns:gfc="http://www.isotc211.org/2005/gfc" name="typeNames">
     *      <!-- Set depending on schema plugins -->
     *      <ows:Value>csw:Record</ows:Value>
     *      <ows:Value>gfc:FC_FeatureCatalogue</ows:Value>
     *      <ows:Value>gmd:MD_Metadata</ows:Value>
     *    </ows:Parameter>
     * </pre>
     */
    private void populateTypeNameAndOutputSchema(Element op) {
        Map<String, Namespace> typenames = _schemaManager.getHmSchemasTypenames();
        List<Element> operations = op.getChildren("Parameter", Csw.NAMESPACE_OWS);
        for (Element operation : operations) {
            if ("typeNames".equals(operation.getAttributeValue("name"))) {
                for (Map.Entry<String, Namespace> entry : typenames.entrySet()) {
                    String typeName = entry.getKey();
                    Namespace ns = entry.getValue();
                    String typename = typeName;
                    operation.addNamespaceDeclaration(ns);
                    operation.addContent(new Element("Value", Csw.NAMESPACE_OWS).setText(typename));
                }
            } else if ("outputSchema".equals(operation.getAttributeValue("name"))) {
                for (Map.Entry<String, Namespace> entry : typenames.entrySet()) {
                    Namespace ns = entry.getValue();
                    operation.addNamespaceDeclaration(ns);
                    operation.addContent(new Element("Value", Csw.NAMESPACE_OWS).setText(ns.getURI()));
                }
            }
        }
    }

    /**
     * TODO javadoc.
     */
    private void fillGetRecordsParams(Element op) {
        Set<String> isoQueryableMap = _fieldMapper.getPropertiesByType(Csw.ISO_QUERYABLES);
        Element isoConstraint = new Element("Constraint", Csw.NAMESPACE_OWS).setAttribute("name",
                Csw.ISO_QUERYABLES);

        for (String params : isoQueryableMap) {
            isoConstraint.addContent(new Element("Value", Csw.NAMESPACE_OWS).setText(params));
        }

        Set<String> additionalQueryableMap = _fieldMapper.getPropertiesByType(Csw.ADDITIONAL_QUERYABLES);
        Element additionalConstraint = new Element("Constraint", Csw.NAMESPACE_OWS).setAttribute("name",
                Csw.ADDITIONAL_QUERYABLES);

        for (String params : additionalQueryableMap) {
            additionalConstraint.addContent(new Element("Value", Csw.NAMESPACE_OWS).setText(params));
        }
        op.addContent(isoConstraint);
        op.addContent(additionalConstraint);

        populateTypeNameAndOutputSchema(op);
    }

}