From 16b562d68602ca3eb8abba5f165dacaabc3d3aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Thu, 28 Dec 2023 11:34:13 +0100 Subject: [PATCH 1/8] Migrate OAIPMH server to Elasticsearch --- .../org/fao/geonet/kernel/oaipmh/Lib.java | 127 +++++-- .../org/fao/geonet/kernel/oaipmh/LibTest.java | 316 ++++++++++++++++++ 2 files changed, 408 insertions(+), 35 deletions(-) create mode 100644 core/src/test/java/org/fao/geonet/kernel/oaipmh/LibTest.java diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/Lib.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/Lib.java index 7d5c74221ef..67d4e1e1457 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/Lib.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/Lib.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -23,29 +23,41 @@ package org.fao.geonet.kernel.oaipmh; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import jeeves.constants.Jeeves; -import jeeves.server.ServiceConfig; import jeeves.server.context.ServiceContext; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.search.SearchHit; +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.kernel.search.EsSearchManager; +import org.fao.geonet.schema.iso19139.ISO19139SchemaPlugin; import org.fao.geonet.utils.Xml; import org.fao.oaipmh.exceptions.OaiPmhException; import org.jdom.Element; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; +import static org.fao.geonet.kernel.search.EsSearchManager.FIELDLIST_CORE; + //============================================================================= public class Lib { public static final String SESSION_OBJECT = "oai-list-records-result"; + private Lib() { + + } //--------------------------------------------------------------------------- //--- //--- API methods //--- //--------------------------------------------------------------------------- - private static ServiceConfig dummyConfig = new ServiceConfig(); //-------------------------------------------------------------------------- @@ -91,38 +103,20 @@ public static Element transform(Path schemaDir, Element env, Element md, String //--------------------------------------------------------------------------- public static List search(ServiceContext context, Element params) throws Exception { - throw new UnsupportedOperationException("ES search does not support OAI yet"); - -// GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME); -// SearchManager sm = gc.getBean(SearchManager.class); -// -// try (MetaSearcher searcher = sm.newSearcher(SearcherType.LUCENE, Geonet.File.SEARCH_LUCENE)) { -// -// if (context.isDebugEnabled()) -// context.debug("Searching with params:\n" + Xml.getString(params)); -// -// searcher.search(context, params, dummyConfig); -// -// params.addContent(new Element("fast").setText("true")); -// params.addContent(new Element("from").setText("1")); -// params.addContent(new Element("to").setText(searcher.getSize() + "")); -// -// context.info("Records found : " + searcher.getSize()); -// -// Element records = searcher.present(context, params, dummyConfig); -// -// records.getChild("summary").detach(); -// -// List result = new ArrayList(); -// -// for (Object o : records.getChildren()) { -// Element rec = (Element) o; -// Element info = rec.getChild("info", Edit.NAMESPACE); -// -// result.add(Integer.parseInt(info.getChildText("id"))); -// } -// return result; -// } + EsSearchManager searchMan = context.getBean(EsSearchManager.class); + + JsonNode esJsonQuery = createSearchQuery(params); + final SearchResponse queryResult = searchMan.query( + esJsonQuery, + FIELDLIST_CORE, + 0, 1000); + + List result = new ArrayList<>(); + + for (SearchHit hit : queryResult.getHits()) { + result.add(Integer.parseInt(hit.getSourceAsMap().get(Geonet.IndexFieldNames.ID).toString())); + } + return result; } //--------------------------------------------------------------------------- @@ -152,6 +146,69 @@ public static Element toJeevesException(OaiPmhException e) { return error; } + + static JsonNode createSearchQuery(Element params) throws JsonProcessingException { + final String PARAM_SET = "category"; + final String PARAM_FROM = "extFrom"; + final String PARAM_UNTIL = "extTo"; + final String PARAM_METADATAPREFIX = "_schema"; + + String jsonQuery = "{" + + " \"bool\": {" + + " \"must\": [" + + " {" + + " \"terms\": {" + + " \"isTemplate\": [\"n\"]" + + " }" + + " }, " + + " {" + + " \"term\": {" + + " \"documentStandard\": {" + + " \"value\": \"%s\"" + + " }" + + " }" + + " }%s%s" + + " ]" + + " }" + + "}"; + + String categoryQuery = ""; + if (params.getChild(PARAM_SET) != null) { + categoryQuery = String.format(" ,{" + + " \"term\": {" + + " \"cat\": {" + + " \"value\": \"%s\"" + + " }" + + " }" + + " }", params.getChild(PARAM_SET).getValue()); + } + + String temporalExtentQuery = ""; + if ((params.getChild(PARAM_FROM) != null) || + (params.getChild("extTo") != null)) { + + String fromQuery = ""; + if (params.getChild(PARAM_FROM) != null) { + fromQuery = String.format(" \"gte\": \"%s\",", params.getChild(PARAM_FROM).getValue()); + } + + String untilQuery = ""; + if (params.getChild(PARAM_UNTIL) != null) { + untilQuery = String.format(" \"lte\": \"%s\",", params.getChild(PARAM_UNTIL).getValue()) ; + } + + temporalExtentQuery = String.format(" ,{\"range\": {\"resourceTemporalDateRange\": {" + + " %s" + + " %s" + + " \"relation\": \"intersects\"" + + " }}}", fromQuery, untilQuery); + } + + ObjectMapper objectMapper = new ObjectMapper(); + + String schema = (params.getChild(PARAM_METADATAPREFIX) != null) ? params.getChild(PARAM_METADATAPREFIX).getValue() : ISO19139SchemaPlugin.IDENTIFIER; + return objectMapper.readTree(String.format(jsonQuery, schema, categoryQuery, temporalExtentQuery)); + } } //============================================================================= diff --git a/core/src/test/java/org/fao/geonet/kernel/oaipmh/LibTest.java b/core/src/test/java/org/fao/geonet/kernel/oaipmh/LibTest.java new file mode 100644 index 00000000000..f2b1ecf7b1b --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/oaipmh/LibTest.java @@ -0,0 +1,316 @@ +//============================================================================= +//=== Copyright (C) 2001-2023 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.kernel.oaipmh; + + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.fao.geonet.schema.iso19139.ISO19139SchemaPlugin; +import org.jdom.Element; +import org.jsoup.select.Elements; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class LibTest { + + @Test + public void testCreateSearchQueryMetadataPrefix() { + Element params = new Element("params"); + params.addContent(new Element("_schema").setText(ISO19139SchemaPlugin.IDENTIFIER)); + + ObjectMapper objectMapper = new ObjectMapper(); + + try { + String queryExpected = "{" + + " \"bool\": {" + + " \"must\": [" + + " {" + + " \"terms\": {" + + " \"isTemplate\": [\"n\"]" + + " }" + + " }, " + + " {" + + " \"term\": {" + + " \"documentStandard\": {" + + " \"value\": \"iso19139\"" + + " }" + + " }" + + " }" + + " ]" + + " }" + + "}"; + + JsonNode searchQueryExpected = objectMapper.readTree(queryExpected); + JsonNode searchQuery = Lib.createSearchQuery(params); + assertEquals(searchQueryExpected, searchQuery); + } catch (Exception ex) { + fail("Error creating OAIMPH search query"); + } + } + + @Test + public void testCreateSearchQueryMetadataPrefixAndSet() { + Element params = new Element("params"); + params.addContent(new Element("_schema").setText(ISO19139SchemaPlugin.IDENTIFIER)); + params.addContent(new Element("category").setText("maps")); + + ObjectMapper objectMapper = new ObjectMapper(); + + try { + String queryExpected = "{" + + " \"bool\": {" + + " \"must\": [" + + " {" + + " \"terms\": {" + + " \"isTemplate\": [\"n\"]" + + " }" + + " }, " + + " {" + + " \"term\": {" + + " \"documentStandard\": {" + + " \"value\": \"iso19139\"" + + " }" + + " }" + + " }, {" + + " \"term\": {" + + " \"cat\": {" + + " \"value\": \"maps\"" + + " }" + + " }" + + " }" + + " ]" + + " }" + + "}"; + + JsonNode searchQueryExpected = objectMapper.readTree(queryExpected); + JsonNode searchQuery = Lib.createSearchQuery(params); + assertEquals(searchQueryExpected, searchQuery); + } catch (Exception ex) { + fail("Error creating OAIMPH search query"); + } + } + + @Test + public void testCreateSearchQueryMetadataPrefixAndTemporalExtentFrom() { + Element params = new Element("params"); + params.addContent(new Element("_schema").setText(ISO19139SchemaPlugin.IDENTIFIER)); + params.addContent(new Element("extFrom").setText("2023-09-01")); + + ObjectMapper objectMapper = new ObjectMapper(); + + try { + String queryExpected = "{" + + " \"bool\": {" + + " \"must\": [" + + " {" + + " \"terms\": {" + + " \"isTemplate\": [\"n\"]" + + " }" + + " }, " + + " {" + + " \"term\": {" + + " \"documentStandard\": {" + + " \"value\": \"iso19139\"" + + " }" + + " }" + + " },{\"range\": {\"resourceTemporalDateRange\": {" + + " \"gte\": \"2023-09-01\"," + + " \"relation\": \"intersects\"" + + " }}}" + + " ]" + + " }" + + "}"; + + JsonNode searchQueryExpected = objectMapper.readTree(queryExpected); + JsonNode searchQuery = Lib.createSearchQuery(params); + assertEquals(searchQueryExpected, searchQuery); + } catch (Exception ex) { + fail("Error creating OAIMPH search query"); + } + } + + @Test + public void testCreateSearchQueryMetadataPrefixAndTemporalExtentTo() { + Element params = new Element("params"); + params.addContent(new Element("_schema").setText(ISO19139SchemaPlugin.IDENTIFIER)); + params.addContent(new Element("extTo").setText("2023-12-29")); + + ObjectMapper objectMapper = new ObjectMapper(); + + try { + String queryExpected = "{" + + " \"bool\": {" + + " \"must\": [" + + " {" + + " \"terms\": {" + + " \"isTemplate\": [\"n\"]" + + " }" + + " }, " + + " {" + + " \"term\": {" + + " \"documentStandard\": {" + + " \"value\": \"iso19139\"" + + " }" + + " }" + + " },{\"range\": {\"resourceTemporalDateRange\": {" + + " \"lte\": \"2023-12-29\"," + + " \"relation\": \"intersects\"" + + " }}}" + + " ]" + + " }" + + "}"; + + JsonNode searchQueryExpected = objectMapper.readTree(queryExpected); + JsonNode searchQuery = Lib.createSearchQuery(params); + assertEquals(searchQueryExpected, searchQuery); + } catch (Exception ex) { + fail("Error creating OAIMPH search query"); + } + } + + @Test + public void testCreateSearchQueryMetadataPrefixAndTemporalExtent() { + Element params = new Element("params"); + params.addContent(new Element("_schema").setText(ISO19139SchemaPlugin.IDENTIFIER)); + params.addContent(new Element("extFrom").setText("2023-09-01")); + params.addContent(new Element("extTo").setText("2023-12-29")); + + ObjectMapper objectMapper = new ObjectMapper(); + + try { + String queryExpected = "{" + + " \"bool\": {" + + " \"must\": [" + + " {" + + " \"terms\": {" + + " \"isTemplate\": [\"n\"]" + + " }" + + " }, " + + " {" + + " \"term\": {" + + " \"documentStandard\": {" + + " \"value\": \"iso19139\"" + + " }" + + " }" + + " },{\"range\": {\"resourceTemporalDateRange\": {" + + " \"gte\": \"2023-09-01\"," + + " \"lte\": \"2023-12-29\"," + + " \"relation\": \"intersects\"" + + " }}}" + + " ]" + + " }" + + "}"; + + JsonNode searchQueryExpected = objectMapper.readTree(queryExpected); + JsonNode searchQuery = Lib.createSearchQuery(params); + assertEquals(searchQueryExpected, searchQuery); + } catch (Exception ex) { + fail("Error creating OAIMPH search query"); + } + } + + @Test + public void testCreateSearchQueryAllParams() { + Element params = new Element("params"); + params.addContent(new Element("_schema").setText(ISO19139SchemaPlugin.IDENTIFIER)); + params.addContent(new Element("category").setText("maps")); + params.addContent(new Element("extFrom").setText("2023-09-01")); + params.addContent(new Element("extTo").setText("2023-12-29")); + + ObjectMapper objectMapper = new ObjectMapper(); + + try { + String queryExpected = "{" + + " \"bool\": {" + + " \"must\": [" + + " {" + + " \"terms\": {" + + " \"isTemplate\": [\"n\"]" + + " }" + + " }, " + + " {" + + " \"term\": {" + + " \"documentStandard\": {" + + " \"value\": \"iso19139\"" + + " }" + + " }" + + " }, {" + + " \"term\": {" + + " \"cat\": {" + + " \"value\": \"maps\"" + + " }" + + " }" + + " },{\"range\": {\"resourceTemporalDateRange\": {" + + " \"gte\": \"2023-09-01\"," + + " \"lte\": \"2023-12-29\"," + + " \"relation\": \"intersects\"" + + " }}}" + + " ]" + + " }" + + "}"; + + JsonNode searchQueryExpected = objectMapper.readTree(queryExpected); + JsonNode searchQuery = Lib.createSearchQuery(params); + assertEquals(searchQueryExpected, searchQuery); + } catch (Exception ex) { + fail("Error creating OAIMPH search query"); + } + } + + @Test + public void testCreateSearchQueryEmptyParams() { + Element params = new Element("params"); + + ObjectMapper objectMapper = new ObjectMapper(); + + try { + String queryExpected = "{" + + " \"bool\": {" + + " \"must\": [" + + " {" + + " \"terms\": {" + + " \"isTemplate\": [\"n\"]" + + " }" + + " }, " + + " {" + + " \"term\": {" + + " \"documentStandard\": {" + + " \"value\": \"iso19139\"" + + " }" + + " }" + + " }" + + " ]" + + " }" + + "}"; + + JsonNode searchQueryExpected = objectMapper.readTree(queryExpected); + JsonNode searchQuery = Lib.createSearchQuery(params); + assertEquals(searchQueryExpected, searchQuery); + } catch (Exception ex) { + fail("Error creating OAIMPH search query"); + } + } +} From c0849bdf92d1c739b57bbd5ce1301f3d3089f865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Thu, 28 Dec 2023 11:38:18 +0100 Subject: [PATCH 2/8] Migrate OAIPMH server to Elasticsearch / Update documentation --- docs/manual/docs/api/oai-pmh.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/manual/docs/api/oai-pmh.md b/docs/manual/docs/api/oai-pmh.md index b18419bb7c0..d4aabf96807 100644 --- a/docs/manual/docs/api/oai-pmh.md +++ b/docs/manual/docs/api/oai-pmh.md @@ -1,10 +1,5 @@ # Open Archive Initiative (OAI) {#oai-pmh} -!!! warning - - Not yet available in version 4. - - The OAI-PMH end point exposes the metadata records in your catalog in XML format using the version 2.0 of the OAI-PMH protocol. ## Configuration From 65a738487e9eb8e9523bd8a4f8a0deda5cb4a230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Thu, 28 Dec 2023 12:23:46 +0100 Subject: [PATCH 3/8] Migrate OAIPMH server API to Spring MVC --- docs/manual/docs/api/oai-pmh.md | 2 +- .../org/fao/geonet/api/oaipmh/OaiPmhApi.java | 108 ++++++++++++++++++ .../services/main/OaiPmhDispatcher.java | 61 ---------- .../config-security-mapping.xml | 3 +- web/src/main/webapp/WEB-INF/config.xml | 1 - .../WEB-INF/config/config-service-oai.xml | 32 ------ 6 files changed, 110 insertions(+), 97 deletions(-) create mode 100644 services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java delete mode 100644 services/src/main/java/org/fao/geonet/services/main/OaiPmhDispatcher.java delete mode 100644 web/src/main/webapp/WEB-INF/config/config-service-oai.xml diff --git a/docs/manual/docs/api/oai-pmh.md b/docs/manual/docs/api/oai-pmh.md index d4aabf96807..099f82c7077 100644 --- a/docs/manual/docs/api/oai-pmh.md +++ b/docs/manual/docs/api/oai-pmh.md @@ -4,7 +4,7 @@ The OAI-PMH end point exposes the metadata records in your catalog in XML format ## Configuration -The following URL is the standard end point for the catalog (substitute your GeoNetwork URL): ? +The following URL is the standard end point for the catalog (substitute your GeoNetwork URL): ? ## Requests diff --git a/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java b/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java new file mode 100644 index 00000000000..19f2ab244ea --- /dev/null +++ b/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2001-2023 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.api.oaipmh; + +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jeeves.server.context.ServiceContext; +import org.apache.commons.lang.StringUtils; +import org.fao.geonet.api.ApiUtils; +import org.fao.geonet.api.tools.i18n.LanguageUtils; +import org.fao.geonet.kernel.oaipmh.OaiPmhDispatcher; +import org.jdom.Element; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.Locale; + +@RequestMapping(value = { + "/{portal}/api/oaipmh" +}) +@Tag(name = "oaipmh", + description = "OAIPMH server operations") +@Controller("oaipmh") +public class OaiPmhApi { + + @Autowired + private LanguageUtils languageUtils; + + @Autowired + private OaiPmhDispatcher oaiPmhDispatcher; + + @io.swagger.v3.oas.annotations.Operation( + summary = "Oaiphm server", + description = "") + @GetMapping( + produces = MediaType.APPLICATION_XML_VALUE) + @ResponseStatus(HttpStatus.OK) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Oaiphm server response.") + }) + @ResponseBody + public Element dispatch( + @RequestParam(required = false) final String verb, + @RequestParam(required = false) final String metadataPrefix, + @RequestParam(required = false) final String set, + @RequestParam(required = false) final String from, + @RequestParam(required = false) final String until, + @RequestParam(required = false) final String resumptionToken, + final HttpServletRequest request + ) { + Locale locale = languageUtils.parseAcceptLanguage(request.getLocales()); + ServiceContext serviceContext = ApiUtils.createServiceContext(request, locale.getISO3Country()); + + Element params = new Element("params"); + if (StringUtils.isNotEmpty(verb)) { + params.addContent(new Element("verb").setText(verb)); + } + + if (StringUtils.isNotEmpty(metadataPrefix)) { + params.addContent(new Element("metadataPrefix").setText(metadataPrefix)); + } + + if (StringUtils.isNotEmpty(set)) { + params.addContent(new Element("set").setText(set)); + } + + if (StringUtils.isNotEmpty(from)) { + params.addContent(new Element("from").setText(from)); + } + + if (StringUtils.isNotEmpty(until)) { + params.addContent(new Element("until").setText(until)); + } + + if (StringUtils.isNotEmpty(resumptionToken)) { + params.addContent(new Element("resumptionToken").setText(resumptionToken)); + } + + return oaiPmhDispatcher.dispatch(params, serviceContext); + } + +} diff --git a/services/src/main/java/org/fao/geonet/services/main/OaiPmhDispatcher.java b/services/src/main/java/org/fao/geonet/services/main/OaiPmhDispatcher.java deleted file mode 100644 index 4a3fddef375..00000000000 --- a/services/src/main/java/org/fao/geonet/services/main/OaiPmhDispatcher.java +++ /dev/null @@ -1,61 +0,0 @@ -//============================================================================= -//=== 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.services.main; - -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; -import org.fao.geonet.GeonetContext; -import org.fao.geonet.constants.Geonet; -import org.jdom.Element; - -import java.nio.file.Path; - -//============================================================================= - -public class OaiPmhDispatcher implements Service { - //-------------------------------------------------------------------------- - //--- - //--- Init - //--- - //-------------------------------------------------------------------------- - - public void init(Path appPath, ServiceConfig config) throws Exception { - } - - //-------------------------------------------------------------------------- - //--- - //--- Service - //--- - //-------------------------------------------------------------------------- - - public Element exec(Element params, ServiceContext context) throws Exception { - GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME); - - return gc.getBean(org.fao.geonet.kernel.oaipmh.OaiPmhDispatcher.class).dispatch(params, context); - } -} - -//============================================================================= - diff --git a/web/src/main/webapp/WEB-INF/config-security/config-security-mapping.xml b/web/src/main/webapp/WEB-INF/config-security/config-security-mapping.xml index 1da3e0c5fac..64516502640 100644 --- a/web/src/main/webapp/WEB-INF/config-security/config-security-mapping.xml +++ b/web/src/main/webapp/WEB-INF/config-security/config-security-mapping.xml @@ -78,7 +78,6 @@ - @@ -236,7 +235,7 @@ - + diff --git a/web/src/main/webapp/WEB-INF/config.xml b/web/src/main/webapp/WEB-INF/config.xml index a6971c431fd..e4d0cfde308 100644 --- a/web/src/main/webapp/WEB-INF/config.xml +++ b/web/src/main/webapp/WEB-INF/config.xml @@ -149,7 +149,6 @@ config/config-service-search.xml config/config-service-sru.xml - config/config-service-oai.xml config/config-service-rss.xml config/config-service-dcat-rdf.xml config/config-service-thesaurus.xml diff --git a/web/src/main/webapp/WEB-INF/config/config-service-oai.xml b/web/src/main/webapp/WEB-INF/config/config-service-oai.xml deleted file mode 100644 index fb0868ab871..00000000000 --- a/web/src/main/webapp/WEB-INF/config/config-service-oai.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - Open Archive Initiative, Protocol for Metadata Harvesting - - - - From 07cd23038deb2662e807ca57d1144098512e5b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Thu, 28 Dec 2023 14:39:41 +0100 Subject: [PATCH 4/8] Migrate OAIPMH server to Elasticsearch / fix total calculation --- .../main/java/org/fao/geonet/kernel/oaipmh/Lib.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/Lib.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/Lib.java index 67d4e1e1457..52a00d44920 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/Lib.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/Lib.java @@ -106,10 +106,19 @@ public static List search(ServiceContext context, Element params) throw EsSearchManager searchMan = context.getBean(EsSearchManager.class); JsonNode esJsonQuery = createSearchQuery(params); - final SearchResponse queryResult = searchMan.query( + + // Get results count + SearchResponse queryResult = searchMan.query( + esJsonQuery, + FIELDLIST_CORE, + 0, 1); + + long total = queryResult.getHits().getTotalHits().value; + + queryResult = searchMan.query( esJsonQuery, FIELDLIST_CORE, - 0, 1000); + 0, (int) total); List result = new ArrayList<>(); From 4c4943ea82b8016873499716fccebc2081059c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Thu, 28 Dec 2023 15:14:42 +0100 Subject: [PATCH 5/8] OAIPMH server / Sonarlint improvements --- .../geonet/kernel/oaipmh/OaiPmhDispatcher.java | 4 ++-- .../fao/geonet/kernel/oaipmh/OaiPmhService.java | 6 +++--- .../kernel/oaipmh/ResumptionTokenCache.java | 17 ++++++++--------- .../oaipmh/services/AbstractTokenLister.java | 16 ++++++---------- .../kernel/oaipmh/services/GetRecord.java | 17 ++++++++--------- .../geonet/kernel/oaipmh/services/Identify.java | 4 ++-- .../kernel/oaipmh/services/ListIdentifiers.java | 10 ++++------ .../oaipmh/services/ListMetadataFormats.java | 6 +++--- .../kernel/oaipmh/services/ListRecords.java | 6 ++---- .../geonet/kernel/oaipmh/services/ListSets.java | 4 +--- 10 files changed, 39 insertions(+), 51 deletions(-) diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java index f7703be7f59..2b4cd1a0a8b 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -65,7 +65,7 @@ public class OaiPmhDispatcher { //--- Constructor //--- //--------------------------------------------------------------------------- - private HashMap hmServices = new HashMap(); + private HashMap hmServices = new HashMap<>(); //--------------------------------------------------------------------------- diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhService.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhService.java index 2a55e9090c6..9cec8fe414f 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhService.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhService.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -31,12 +31,12 @@ //============================================================================= public interface OaiPmhService { - public String getVerb(); + String getVerb(); /** * Executes the service on given input request */ - public AbstractResponse execute(AbstractRequest request, ServiceContext context) + AbstractResponse execute(AbstractRequest request, ServiceContext context) throws Exception; } diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/ResumptionTokenCache.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/ResumptionTokenCache.java index c41de0d9234..575c917587c 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/ResumptionTokenCache.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/ResumptionTokenCache.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2023 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -36,9 +36,7 @@ import java.util.TimeZone; public class ResumptionTokenCache extends Thread { - - - public final static int CACHE_EXPUNGE_DELAY = 10 * 1000; // 10 seconds + public static final int CACHE_EXPUNGE_DELAY = 10 * 1000; // 10 seconds private Map map; private static Object stopper = new Object(); @@ -93,6 +91,7 @@ public int getCachemaxsize() { return settingMan.getValueAsInt("system/oai/cachesize"); } + @Override public void run() { synchronized (stopper) { while (running && !isInterrupted()) { @@ -113,8 +112,8 @@ private synchronized void expunge() { Date now = getUTCTime(); - for (Map.Entry entry : map.entrySet()) { - if (((GeonetworkResumptionToken) entry.getValue()).getExpirDate().toDate().getTime() / 1000 < (now.getTime() / 1000)) { + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().getExpirDate().toDate().getTime() / 1000 < (now.getTime() / 1000)) { map.remove(entry.getKey()); if (Log.isDebugEnabled(Geonet.OAI_HARVESTER)) Log.debug(Geonet.OAI_HARVESTER, "OAI cache ::expunge removing:" + entry.getKey()); @@ -131,11 +130,11 @@ private void removeLast() { long oldest = Long.MAX_VALUE; Object oldkey = ""; - for (Map.Entry entry : map.entrySet()) { + for (Map.Entry entry : map.entrySet()) { - if (((GeonetworkResumptionToken) entry.getValue()).getExpirDate().getSeconds() < oldest) { + if (entry.getValue().getExpirDate().getSeconds() < oldest) { oldkey = entry.getKey(); - oldest = ((GeonetworkResumptionToken) entry.getValue()).getExpirDate().getSeconds(); + oldest = entry.getValue().getExpirDate().getSeconds(); } } diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/AbstractTokenLister.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/AbstractTokenLister.java index 9e5e4e6e220..5ed0b838b46 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/AbstractTokenLister.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/AbstractTokenLister.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2023 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -57,7 +57,7 @@ public abstract class AbstractTokenLister implements OaiPmhService { private SettingManager settingMan; private SchemaManager schemaMan; - public AbstractTokenLister(ResumptionTokenCache cache, SettingManager sm, SchemaManager scm) { + protected AbstractTokenLister(ResumptionTokenCache cache, SettingManager sm, SchemaManager scm) { this.cache = cache; this.settingMan = sm; this.schemaMan = scm; @@ -114,10 +114,8 @@ public AbstractResponse execute(AbstractRequest request, TokenListRequest req = (TokenListRequest) request; - //UserSession session = context.getUserSession(); SearchResult result; - //String token = req.getResumptionToken(); String strToken = req.getResumptionToken(); GeonetworkResumptionToken token = null; @@ -165,11 +163,12 @@ public AbstractResponse execute(AbstractRequest request, params.addContent(new Element("_schema").setText(schema)); result.addIds(Lib.search(context, (Element) params.clone())); } - if (schemas.size() == 0) result.setIds(new ArrayList()); + if (schemas.isEmpty()) result.setIds(new ArrayList<>()); } - if (result.getIds().size() == 0) + if (result.getIds().isEmpty()) { throw new NoRecordsMatchException("No results"); + } // we only need a new token if the result set is big enough if (result.getIds().size() > getMaxRecords()) { @@ -178,7 +177,6 @@ public AbstractResponse execute(AbstractRequest request, } } else { - //result = (SearchResult) session.getProperty(Lib.SESSION_OBJECT); token = cache.getResumptionToken(GeonetworkResumptionToken.buildKey(req)); if (Log.isDebugEnabled(Geonet.OAI_HARVESTER)) Log.debug(Geonet.OAI_HARVESTER, "OAI ListRecords : using ResumptionToken :" + GeonetworkResumptionToken.buildKey(req)); @@ -188,7 +186,6 @@ public AbstractResponse execute(AbstractRequest request, result = token.getRes(); - //pos = result.parseToken(token); pos = GeonetworkResumptionToken.getPos(req); } @@ -198,7 +195,6 @@ public AbstractResponse execute(AbstractRequest request, if (token == null && res.getSize() == 0) throw new NoRecordsMatchException("No results"); - //result.setupToken(res, pos); if (token != null) token.setupToken(pos); res.setResumptionToken(token); @@ -214,7 +210,7 @@ public AbstractResponse execute(AbstractRequest request, */ private List getSchemasThatCanConvertTo(String prefix) { - List result = new ArrayList(); + List result = new ArrayList<>(); for (String schema : schemaMan.getSchemas()) { if (Lib.existsConverter(schemaMan.getSchemaDir(schema), prefix)) { result.add(schema); diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/GetRecord.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/GetRecord.java index 4d3a0c41b13..bc723d1076c 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/GetRecord.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/GetRecord.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -79,14 +79,13 @@ public static Record buildRecordStat(ServiceContext context, Specification getConvertFormats(ServiceContext context) throws IO @SuppressWarnings("unchecked") List defaultSchemas = elem.getChildren(); - List defMdfs = new ArrayList(); + List defMdfs = new ArrayList<>(); for (Element schema : defaultSchemas) { defMdfs.add(new MetadataFormat(schema.getAttributeValue("prefix"), schema.getAttributeValue("schemaLocation"), schema.getAttributeValue("nsUrl"))); } diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListRecords.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListRecords.java index 81da0d91bb0..d90b41572e7 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListRecords.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListRecords.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -98,9 +98,7 @@ private Record buildRecord(ServiceContext context, int id, String prefix) throws // and we do not want to stop because of one error try { return GetRecord.buildRecordStat(context, (Specification)hasMetadataId(id), prefix); - } catch (IdDoesNotExistException e) { - return null; - } catch (CannotDisseminateFormatException e2) { + } catch (IdDoesNotExistException | CannotDisseminateFormatException e) { return null; } catch (Exception e3) { throw e3; diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListSets.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListSets.java index b4b82482cce..c667d9c032e 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListSets.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListSets.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -25,7 +25,6 @@ import jeeves.server.context.ServiceContext; -import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.MetadataCategory; import org.fao.geonet.kernel.oaipmh.OaiPmhService; import org.fao.geonet.repository.MetadataCategoryRepository; @@ -35,7 +34,6 @@ import org.fao.oaipmh.responses.AbstractResponse; import org.fao.oaipmh.responses.ListSetsResponse; import org.fao.oaipmh.responses.SetInfo; -import org.jdom.Element; import java.util.List; From 17a700d50bed6e9068894d981613b614c23a6787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Tue, 9 Jan 2024 20:19:28 +0100 Subject: [PATCH 6/8] OAIPMH server / Fix base URL --- .../java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java | 2 +- .../org/fao/geonet/kernel/oaipmh/services/Identify.java | 2 +- .../src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java index 2b4cd1a0a8b..289eb87a2f6 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java @@ -113,7 +113,7 @@ private Element dispatchI(Element request, ServiceContext context) { SettingInfo si = context.getBean(SettingInfo.class); try { - url = si.getSiteUrl() + context.getBaseUrl() + "/" + Jeeves.Prefix.SERVICE + "/en/" + context.getService(); + url = si.getSiteUrl() + context.getBaseUrl() + "/" + Jeeves.Prefix.SERVICE + "/" + context.getService(); params = OaiPmhFactory.extractParams(request); AbstractRequest req = OaiPmhFactory.parse(context.getApplicationContext(), params); diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/Identify.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/Identify.java index d559a21b3ac..a4d1d102c03 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/Identify.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/Identify.java @@ -55,7 +55,7 @@ public AbstractResponse execute(AbstractRequest request, ServiceContext context) IdentifyResponse res = new IdentifyResponse(); SettingInfo si = context.getBean(SettingInfo.class); - String baseUrl = si.getSiteUrl() + context.getBaseUrl() + "/" + Jeeves.Prefix.SERVICE + "/en/" + context.getService(); + String baseUrl = si.getSiteUrl() + context.getBaseUrl() + "/" + Jeeves.Prefix.SERVICE + "/" + context.getService(); res.setRepositoryName(si.getSiteName()); res.setBaseUrl(baseUrl); diff --git a/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java b/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java index 19f2ab244ea..3cc19e910eb 100644 --- a/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java +++ b/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java @@ -39,7 +39,6 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; -import java.util.Locale; @RequestMapping(value = { "/{portal}/api/oaipmh" @@ -74,8 +73,9 @@ public Element dispatch( @RequestParam(required = false) final String resumptionToken, final HttpServletRequest request ) { - Locale locale = languageUtils.parseAcceptLanguage(request.getLocales()); - ServiceContext serviceContext = ApiUtils.createServiceContext(request, locale.getISO3Country()); + ServiceContext serviceContext = ApiUtils.createServiceContext(request); + // Set the service name, used in OaiPmhDispatcher to build the oaiphm endpoint URL + serviceContext.setService("api/oaipmh"); Element params = new Element("params"); if (StringUtils.isNotEmpty(verb)) { From 726ba001b74229c8a02c5093e4fed89a57ca7e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Tue, 9 Jan 2024 20:58:50 +0100 Subject: [PATCH 7/8] OAIPMH server / Model API parameters and Sonarlint improvements --- .../kernel/oaipmh/OaiPmhDispatcher.java | 8 +- .../geonet/kernel/oaipmh/OaiPmhParams.java | 130 ++++++++++++++++++ .../src/main/java/org/fao/oaipmh/OaiPmh.java | 34 ++++- .../org/fao/oaipmh/server/OaiPmhFactory.java | 69 ++++------ .../org/fao/geonet/api/oaipmh/OaiPmhApi.java | 39 +----- 5 files changed, 198 insertions(+), 82 deletions(-) create mode 100644 core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java index 289eb87a2f6..f79d6dc5c22 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhDispatcher.java @@ -96,8 +96,8 @@ private void register(OaiPmhService s) { //--- //--------------------------------------------------------------------------- - public Element dispatch(Element request, ServiceContext context) { - Element response = dispatchI(request, context); + public Element dispatch(OaiPmhParams oaiPmhParams, ServiceContext context) { + Element response = dispatchI(oaiPmhParams, context); validateResponse(context, response); return response; @@ -105,7 +105,7 @@ public Element dispatch(Element request, ServiceContext context) { //--------------------------------------------------------------------------- - private Element dispatchI(Element request, ServiceContext context) { + private Element dispatchI(OaiPmhParams oaiPmhParams, ServiceContext context) { String url = null; Map params = null; @@ -114,7 +114,7 @@ private Element dispatchI(Element request, ServiceContext context) { try { url = si.getSiteUrl() + context.getBaseUrl() + "/" + Jeeves.Prefix.SERVICE + "/" + context.getService(); - params = OaiPmhFactory.extractParams(request); + params = oaiPmhParams.asMap(); AbstractRequest req = OaiPmhFactory.parse(context.getApplicationContext(), params); OaiPmhService srv = hmServices.get(req.getVerb()); diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java new file mode 100644 index 00000000000..dfad20fc901 --- /dev/null +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2001-2023 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.kernel.oaipmh; + +import org.apache.commons.lang.StringUtils; +import org.fao.oaipmh.OaiPmh; + +import java.util.HashMap; +import java.util.Map; + +public class OaiPmhParams { + private String verb; + private String metadataPrefix; + private String identifier; + private String set; + private String from; + private String until; + private String resumptionToken; + + public String getVerb() { + return verb; + } + + public void setVerb(String verb) { + this.verb = verb; + } + + public String getMetadataPrefix() { + return metadataPrefix; + } + + public void setMetadataPrefix(String metadataPrefix) { + this.metadataPrefix = metadataPrefix; + } + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getSet() { + return set; + } + + public void setSet(String set) { + this.set = set; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getUntil() { + return until; + } + + public void setUntil(String until) { + this.until = until; + } + + public String getResumptionToken() { + return resumptionToken; + } + + public void setResumptionToken(String resumptionToken) { + this.resumptionToken = resumptionToken; + } + + public Map asMap() { + Map params = new HashMap<>(); + + if (StringUtils.isNotEmpty(verb)) { + params.put(OaiPmh.ParamNames.VERB, verb); + } + + if (StringUtils.isNotEmpty(metadataPrefix)) { + params.put(OaiPmh.ParamNames.METADATA_PREFIX, metadataPrefix); + } + + if (StringUtils.isNotEmpty(identifier)) { + params.put(OaiPmh.ParamNames.IDENTIFIER, identifier); + } + + if (StringUtils.isNotEmpty(set)) { + params.put(OaiPmh.ParamNames.SET, set); + } + + if (StringUtils.isNotEmpty(from)) { + params.put(OaiPmh.ParamNames.FROM, from); + } + + if (StringUtils.isNotEmpty(until)) { + params.put(OaiPmh.ParamNames.UNTIL, until); + } + + if (StringUtils.isNotEmpty(resumptionToken)) { + params.put(OaiPmh.ParamNames.RESUMPTION_TOKEN, resumptionToken); + } + + return params; + } +} diff --git a/oaipmh/src/main/java/org/fao/oaipmh/OaiPmh.java b/oaipmh/src/main/java/org/fao/oaipmh/OaiPmh.java index 8dc2f896148..fe51c5891f1 100644 --- a/oaipmh/src/main/java/org/fao/oaipmh/OaiPmh.java +++ b/oaipmh/src/main/java/org/fao/oaipmh/OaiPmh.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -28,6 +28,13 @@ //============================================================================= public class OaiPmh { + /** + * Private constructor to avoid instantiate the class. + */ + private OaiPmh() { + + } + //--------------------------------------------------------------------------- //--- //--- Constants @@ -40,10 +47,35 @@ public class OaiPmh { //--------------------------------------------------------------------------- public static class Namespaces { + /** + * Private constructor to avoid instantiate the class. + */ + private Namespaces() { + + } public static final Namespace OAI_PMH = Namespace.getNamespace("http://www.openarchives.org/OAI/2.0/"); public static final Namespace OAI_DC = Namespace.getNamespace("oai_dc", "http://www.openarchives.org/OAI/2.0/oai_dc/"); public static final Namespace XSI = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); } + + public static class ParamNames { + /** + * Private constructor to avoid instantiate the class. + */ + private ParamNames() { + + } + + public static final String VERB = "verb"; + public static final String METADATA_PREFIX = "metadataPrefix"; + public static final String IDENTIFIER = "identifier"; + public static final String SET = "set"; + + public static final String FROM = "from"; + public static final String UNTIL = "until"; + + public static final String RESUMPTION_TOKEN = "resumptionToken"; + } } //============================================================================= diff --git a/oaipmh/src/main/java/org/fao/oaipmh/server/OaiPmhFactory.java b/oaipmh/src/main/java/org/fao/oaipmh/server/OaiPmhFactory.java index 523cb4ab12f..8c40440163f 100644 --- a/oaipmh/src/main/java/org/fao/oaipmh/server/OaiPmhFactory.java +++ b/oaipmh/src/main/java/org/fao/oaipmh/server/OaiPmhFactory.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -28,46 +28,33 @@ import org.fao.geonet.domain.ISODate; import org.fao.geonet.utils.GeonetHttpRequestFactory; -import org.fao.geonet.utils.XmlRequest; +import org.fao.oaipmh.OaiPmh; import org.fao.oaipmh.exceptions.BadArgumentException; import org.fao.oaipmh.exceptions.BadVerbException; import org.fao.oaipmh.requests.*; -import org.jdom.Element; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; //============================================================================= public class OaiPmhFactory { + /** + * Private constructor to avoid instantiate the class. + */ + private OaiPmhFactory() { + + } + //--------------------------------------------------------------------------- //--- //--- API methods //--- //--------------------------------------------------------------------------- - public static Map extractParams(Element request) throws BadArgumentException { - Map params = new HashMap(); - - for (Object o : request.getChildren()) { - Element elem = (Element) o; - String name = elem.getName(); - String value = elem.getText(); - - if (params.containsKey(name)) - throw new BadArgumentException("Parameter repeated : " + name); - - params.put(name, value); - } - - return params; - } - - //--------------------------------------------------------------------------- - public static AbstractRequest parse(ConfigurableApplicationContext applicationContext, Map params) throws BadVerbException, BadArgumentException { //--- duplicate parameters because the below procedure will consume them - Map params2 = new HashMap(); + Map params2 = new HashMap<>(); for (Map.Entry param : params.entrySet()) { @@ -161,7 +148,7 @@ private static ISODate consumeDate(Map params, String name) thro //--------------------------------------------------------------------------- private static void checkConsumption(Map params) throws BadArgumentException { - if (params.keySet().size() == 0) + if (params.keySet().isEmpty()) return; String extraParam = params.keySet().iterator().next(); @@ -188,8 +175,8 @@ private static GetRecordRequest handleGetRecord(ApplicationContext applicationCo throws BadArgumentException { GetRecordRequest req = new GetRecordRequest(applicationContext.getBean(GeonetHttpRequestFactory.class)); - req.setIdentifier(consumeMan(params, "identifier")); - req.setMetadataPrefix(consumeMan(params, "metadataPrefix")); + req.setIdentifier(consumeMan(params, OaiPmh.ParamNames.IDENTIFIER)); + req.setMetadataPrefix(consumeMan(params, OaiPmh.ParamNames.METADATA_PREFIX)); checkConsumption(params); @@ -203,14 +190,14 @@ private static ListIdentifiersRequest handleListIdentifiers(ApplicationContext a ListIdentifiersRequest req = new ListIdentifiersRequest(applicationContext.getBean(GeonetHttpRequestFactory.class)); - if (params.containsKey("resumptionToken")) - req.setResumptionToken(consumeMan(params, "resumptionToken")); + if (params.containsKey(OaiPmh.ParamNames.RESUMPTION_TOKEN)) + req.setResumptionToken(consumeMan(params, OaiPmh.ParamNames.RESUMPTION_TOKEN)); else { - req.setMetadataPrefix(consumeMan(params, "metadataPrefix")); - req.setSet(consumeOpt(params, "set")); + req.setMetadataPrefix(consumeMan(params, OaiPmh.ParamNames.METADATA_PREFIX)); + req.setSet(consumeOpt(params, OaiPmh.ParamNames.SET)); - req.setFrom(consumeDate(params, "from")); - req.setUntil(consumeDate(params, "until")); + req.setFrom(consumeDate(params, OaiPmh.ParamNames.FROM)); + req.setUntil(consumeDate(params, OaiPmh.ParamNames.UNTIL)); } checkConsumption(params); @@ -225,7 +212,7 @@ private static ListMetadataFormatsRequest handleListMdFormats(ApplicationContext ListMetadataFormatsRequest req = new ListMetadataFormatsRequest(applicationContext.getBean(GeonetHttpRequestFactory.class)); - req.setIdentifier(consumeOpt(params, "identifier")); + req.setIdentifier(consumeOpt(params, OaiPmh.ParamNames.IDENTIFIER)); checkConsumption(params); return req; @@ -238,14 +225,14 @@ private static ListRecordsRequest handleListRecords(ApplicationContext applicati ListRecordsRequest req = new ListRecordsRequest(applicationContext.getBean(GeonetHttpRequestFactory.class)); - if (params.containsKey("resumptionToken")) - req.setResumptionToken(consumeMan(params, "resumptionToken")); + if (params.containsKey(OaiPmh.ParamNames.RESUMPTION_TOKEN)) + req.setResumptionToken(consumeMan(params, OaiPmh.ParamNames.RESUMPTION_TOKEN)); else { - req.setMetadataPrefix(consumeMan(params, "metadataPrefix")); - req.setSet(consumeOpt(params, "set")); + req.setMetadataPrefix(consumeMan(params, OaiPmh.ParamNames.METADATA_PREFIX)); + req.setSet(consumeOpt(params, OaiPmh.ParamNames.SET)); - req.setFrom(consumeDate(params, "from")); - req.setUntil(consumeDate(params, "until")); + req.setFrom(consumeDate(params, OaiPmh.ParamNames.FROM)); + req.setUntil(consumeDate(params, OaiPmh.ParamNames.UNTIL)); } checkConsumption(params); @@ -260,8 +247,8 @@ private static ListSetsRequest handleListSets(ApplicationContext applicationCont ListSetsRequest req = new ListSetsRequest(applicationContext.getBean(GeonetHttpRequestFactory.class)); - if (params.containsKey("resumptionToken")) - req.setResumptionToken(consumeMan(params, "resumptionToken")); + if (params.containsKey(OaiPmh.ParamNames.RESUMPTION_TOKEN)) + req.setResumptionToken(consumeMan(params, OaiPmh.ParamNames.RESUMPTION_TOKEN)); checkConsumption(params); diff --git a/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java b/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java index 3cc19e910eb..fe920b5fb10 100644 --- a/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java +++ b/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java @@ -27,10 +27,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jeeves.server.context.ServiceContext; -import org.apache.commons.lang.StringUtils; import org.fao.geonet.api.ApiUtils; -import org.fao.geonet.api.tools.i18n.LanguageUtils; import org.fao.geonet.kernel.oaipmh.OaiPmhDispatcher; +import org.fao.geonet.kernel.oaipmh.OaiPmhParams; import org.jdom.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -48,9 +47,6 @@ @Controller("oaipmh") public class OaiPmhApi { - @Autowired - private LanguageUtils languageUtils; - @Autowired private OaiPmhDispatcher oaiPmhDispatcher; @@ -65,44 +61,15 @@ public class OaiPmhApi { }) @ResponseBody public Element dispatch( - @RequestParam(required = false) final String verb, - @RequestParam(required = false) final String metadataPrefix, - @RequestParam(required = false) final String set, - @RequestParam(required = false) final String from, - @RequestParam(required = false) final String until, - @RequestParam(required = false) final String resumptionToken, + final OaiPmhParams oaiPmhParams, final HttpServletRequest request ) { ServiceContext serviceContext = ApiUtils.createServiceContext(request); // Set the service name, used in OaiPmhDispatcher to build the oaiphm endpoint URL serviceContext.setService("api/oaipmh"); - Element params = new Element("params"); - if (StringUtils.isNotEmpty(verb)) { - params.addContent(new Element("verb").setText(verb)); - } - - if (StringUtils.isNotEmpty(metadataPrefix)) { - params.addContent(new Element("metadataPrefix").setText(metadataPrefix)); - } - - if (StringUtils.isNotEmpty(set)) { - params.addContent(new Element("set").setText(set)); - } - - if (StringUtils.isNotEmpty(from)) { - params.addContent(new Element("from").setText(from)); - } - - if (StringUtils.isNotEmpty(until)) { - params.addContent(new Element("until").setText(until)); - } - - if (StringUtils.isNotEmpty(resumptionToken)) { - params.addContent(new Element("resumptionToken").setText(resumptionToken)); - } - return oaiPmhDispatcher.dispatch(params, serviceContext); + return oaiPmhDispatcher.dispatch(oaiPmhParams, serviceContext); } } From 727e3280e107332d646a885e0eec44d88d036fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Wed, 10 Jan 2024 19:37:40 +0100 Subject: [PATCH 8/8] OAIPMH server / Remove non valid chars from the value to match the xslt filename to apply --- .../main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java | 4 +++- .../src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java index dfad20fc901..beabad44b6d 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/OaiPmhParams.java @@ -102,7 +102,9 @@ public Map asMap() { } if (StringUtils.isNotEmpty(metadataPrefix)) { - params.put(OaiPmh.ParamNames.METADATA_PREFIX, metadataPrefix); + //--- remove any non letter, number, dot or dash character + String processedMetadataPrefix = metadataPrefix.replaceAll("[^a-zA-Z0-9.-]", ""); + params.put(OaiPmh.ParamNames.METADATA_PREFIX, processedMetadataPrefix); } if (StringUtils.isNotEmpty(identifier)) { diff --git a/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java b/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java index fe920b5fb10..d14dc806d47 100644 --- a/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java +++ b/services/src/main/java/org/fao/geonet/api/oaipmh/OaiPmhApi.java @@ -68,7 +68,6 @@ public Element dispatch( // Set the service name, used in OaiPmhDispatcher to build the oaiphm endpoint URL serviceContext.setService("api/oaipmh"); - return oaiPmhDispatcher.dispatch(oaiPmhParams, serviceContext); }