From d7613204739faceafee4b1df5be64cfadd3d136a Mon Sep 17 00:00:00 2001 From: Kai P Date: Wed, 13 May 2026 11:14:06 +0200 Subject: [PATCH 01/10] dc transformer from old xsearch --- .../resources/transformers/MARC21slim2DC.xsl | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 sru/src/main/resources/transformers/MARC21slim2DC.xsl diff --git a/sru/src/main/resources/transformers/MARC21slim2DC.xsl b/sru/src/main/resources/transformers/MARC21slim2DC.xsl new file mode 100644 index 0000000000..0849ebead0 --- /dev/null +++ b/sru/src/main/resources/transformers/MARC21slim2DC.xsl @@ -0,0 +1,270 @@ + + + + + + + + + + + + + + + + + <xsl:call-template name="subfieldSelect"> + <xsl:with-param name="codes">abfghk</xsl:with-param> + </xsl:call-template> + + + + + + + + + + + + + yes + + + + yes + + + + text + cartographic + notated music + sound recording + still image + moving image + three dimensional object + software, multimedia + mixed material + + + + + + + + + + + + + ab + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + abcdq + + + + + + + + abcdq + + + + + + + + abcdq + + + + + + + + abcdq + + + + + + + + abcdq + + + + + + + + abcdq + + + + + + + + abcd + + + + + + + + abcdu + + + + + + + + ot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .:,;/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From efd2659aca9e84480dc6d4e3b3c182eb1f4d181c Mon Sep 17 00:00:00 2001 From: Kai P Date: Wed, 13 May 2026 15:50:13 +0200 Subject: [PATCH 02/10] dc in xsearch --- .../whelk/sru/servlet/XSearchServlet.java | 7 +++++-- .../resources/transformers/MARC21slim2DC.xsl | 21 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java index 4c3ef59d11..54f4b151c2 100644 --- a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java @@ -100,7 +100,7 @@ public class XSearchServlet extends WhelkHttpServlet { "json", Format.JSON, "mods", Format.MODS, "ris", Format.UNSUPPORTED, - "dc", Format.UNSUPPORTED, + "dc", Format.DC, "rdfdc", Format.UNSUPPORTED, "bibtex", Format.UNSUPPORTED, "refworks", Format.UNSUPPORTED, @@ -120,6 +120,7 @@ private enum Format { MARC_XML, MODS, JSON, + DC, UNSUPPORTED, } @@ -156,7 +157,8 @@ protected void init(Whelk whelk) { try { transformers = Map.of( - Format.MODS, loadXslt("transformers/MARC21slim2MODS3.xsl") + Format.MODS, loadXslt("transformers/MARC21slim2MODS3.xsl"), + Format.DC, loadXslt("transformers/MARC21slim2DC.xsl") ); } catch (IOException | TransformerConfigurationException e) { throw new IllegalStateException(e); @@ -236,6 +238,7 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc case MARC_XML -> sendMarcXML(res, items, start, to, totalItems, includeHoldings, include9xx); case JSON -> sendJson(res, items, start, to, totalItems); case MODS -> sendTransformedMarc(res, Format.MODS, items, start, to, totalItems, includeHoldings, include9xx); + case DC -> sendTransformedMarc(res, Format.DC, items, start, to, totalItems, false, false); } } catch (InvalidQueryException e) { diff --git a/sru/src/main/resources/transformers/MARC21slim2DC.xsl b/sru/src/main/resources/transformers/MARC21slim2DC.xsl index 0849ebead0..b5fa387c91 100644 --- a/sru/src/main/resources/transformers/MARC21slim2DC.xsl +++ b/sru/src/main/resources/transformers/MARC21slim2DC.xsl @@ -1,7 +1,7 @@ @@ -10,12 +10,18 @@ + + + + + + - + <xsl:call-template name="subfieldSelect"> @@ -27,7 +33,7 @@ <xsl:for-each select="marc:datafield[@tag=100]|marc:datafield[@tag=110]|marc:datafield[@tag=111]|marc:datafield[@tag=700]|marc:datafield[@tag=710]|marc:datafield[@tag=711]|marc:datafield[@tag=720]"> <creator> - <xsl:value-of select="."/> + <xsl:value-of select="./marc:subfield[@code='a']"/> </creator> </xsl:for-each> @@ -55,7 +61,7 @@ <xsl:for-each select="marc:datafield[@tag=655]"> <type> - <xsl:value-of select="."/> + <xsl:value-of select="./marc:subfield[@code='a']"/> </type> </xsl:for-each> @@ -192,6 +198,7 @@ </xsl:for-each> </dc> </xsl:template> + <xsl:template name="datafield"> <xsl:param name="tag"/> <xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param> @@ -267,4 +274,10 @@ <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise> </xsl:choose> </xsl:template> + + <xsl:template match="*|@*|text()"> + <xsl:copy> + <xsl:apply-templates select="*|@*|text()"/> + </xsl:copy> + </xsl:template> </xsl:stylesheet> From 0e66be86a2fff9a6af7afcaef390d077d7c7e716 Mon Sep 17 00:00:00 2001 From: Kai P <kai.poykio@kb.se> Date: Tue, 2 Jun 2026 16:22:14 +0200 Subject: [PATCH 03/10] mods and dc for sru/xsearch --- sru/curl-sru.sh | 1 + sru/curl-xsearch.sh | 1 + sru/run-local.sh | 1 + .../java/whelk/sru/servlet/SruServlet.java | 59 +++++++++++++++++-- .../whelk/sru/servlet/XSearchServlet.java | 36 +++++++---- .../resources/transformers/MARC21slim2DC.xsl | 4 +- .../transformers/MARC21slim2MODS3.xsl | 4 +- 7 files changed, 84 insertions(+), 22 deletions(-) create mode 100644 sru/curl-sru.sh create mode 100644 sru/curl-xsearch.sh create mode 100644 sru/run-local.sh diff --git a/sru/curl-sru.sh b/sru/curl-sru.sh new file mode 100644 index 0000000000..d884de95f8 --- /dev/null +++ b/sru/curl-sru.sh @@ -0,0 +1 @@ +curl 'localhost:8180/sru?version=1.1&operation=searchRetrieve&query=dinosaur&startRecord=2&maximumRecords=3&recordSchema=dc' diff --git a/sru/curl-xsearch.sh b/sru/curl-xsearch.sh new file mode 100644 index 0000000000..f34503d05d --- /dev/null +++ b/sru/curl-xsearch.sh @@ -0,0 +1 @@ +curl 'localhost:8180/xsearch?query=kamel&format=dc&start=1' diff --git a/sru/run-local.sh b/sru/run-local.sh new file mode 100644 index 0000000000..c06963b79b --- /dev/null +++ b/sru/run-local.sh @@ -0,0 +1 @@ +java -Xmx2G -Dxl.secret.properties=/appl/import/etc/secret.properties.prod -jar build/libs/sru.jar diff --git a/sru/src/main/java/whelk/sru/servlet/SruServlet.java b/sru/src/main/java/whelk/sru/servlet/SruServlet.java index 2667e28696..1233583868 100644 --- a/sru/src/main/java/whelk/sru/servlet/SruServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/SruServlet.java @@ -12,16 +12,22 @@ import whelk.search2.*; import whelk.sru.cql.Translation; import whelk.util.http.WhelkHttpServlet; - +//import javax.xml.transform.stax.StAXResult; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.Transformer; import java.io.IOException; import java.io.OutputStream; import java.io.StringReader; +import java.io.InputStreamReader; import java.util.*; // Test locally like so: @@ -34,12 +40,14 @@ public class SruServlet extends WhelkHttpServlet { XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); ResourceLookup resourceLookup; ESSettings esSettings; + private Formats formats = null; @Override protected void init(Whelk whelk) { converter = new JsonLD2MarcXMLConverter(whelk.getMarcFrameConverter()); resourceLookup = ResourceLookup.load(whelk); esSettings = new ESSettings(whelk); + formats = new Formats(); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { @@ -63,8 +71,11 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce } Map<String, Object> results; + String format; + try { String CqlQueryString = parameters.get("query")[0]; + format = parameters.get("recordSchema")[0]; String XlQueryString = Translation.translateCqlToXlQuery(CqlQueryString); // This part is a little weird @@ -82,6 +93,22 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce return; } + Transformer transformer = null; + + try { + switch (Formats.FORMATS.getOrDefault(format, Formats.Format.MARC_XML)) { + case MARC_XML -> transformer = null; + case MODS -> transformer = formats.transformers.get(Formats.Format.MODS).newTransformer(); + case DC -> transformer = formats.transformers.get(Formats.Format.DC).newTransformer(); + case UNSUPPORTED -> transformer = null; + } + } + catch (TransformerException e){ + logger.info(e.getMessage()); + res.sendError(400); + return; + } + // Like the pre-existing implementation, supply only up to 10 hits per query. List items = (List) results.get("items"); if (items.size() > 10) @@ -105,6 +132,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce writer.writeEndElement(); // numberOfRecords writer.writeStartElement("records"); + //writer.flush(); for (Object o : items) { Map m = (Map) o; @@ -124,20 +152,41 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce writer.writeEndElement(); // recordPacking writer.writeStartElement("recordSchema"); + // change to format schema writer.writeCharacters("info:srw/schema/1/marcxml-v1.1"); writer.writeEndElement(); // recordSchema - writer.writeStartElement("recordData"); - StaxUtils.copy(xmlInputFactory.createXMLStreamReader(new StringReader(convertedText)), writer); - writer.writeEndElement(); // recordData + //writer.writeStartElement("recordData"); + writer.flush(); + + out.write("<recordData>".getBytes("UTF-8")); + + if ( transformer == null ) { + StaxUtils.copy(xmlInputFactory.createXMLStreamReader(new StringReader(convertedText)), writer); + } else { + try { + transformer.transform(new StreamSource(new StringReader(convertedText)), new StreamResult(out)); + } + catch (TransformerException e) { + logger.info(e.getMessage()); + res.sendError(400); + return; + } + } + out.write("</recordData>".getBytes("UTF-8")); + out.flush(); + + //writer.writeEndElement(); // recordData writer.writeEndElement(); // record + writer.flush(); } writer.writeEndElement(); // records writer.writeEndElement(); // searchRetrieveResponse writer.writeEndDocument(); + writer.flush(); writer.close(); out.flush(); out.close(); @@ -146,4 +195,4 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce } } -} \ No newline at end of file +} diff --git a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java index 54f4b151c2..028b0c1ca2 100644 --- a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java @@ -34,11 +34,11 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; -import javax.xml.transform.Templates; +//import javax.xml.transform.Templates; import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; +//import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; +//import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import java.io.ByteArrayInputStream; @@ -89,12 +89,14 @@ public class XSearchServlet extends WhelkHttpServlet { private final Logger logger = LogManager.getLogger(this.getClass()); private final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); - private final TransformerFactory transformerFactory = TransformerFactory.newInstance(); + //private final TransformerFactory transformerFactory = TransformerFactory.newInstance(); + private Formats formats = null; private static final int DEFAULT_N = 10; private static final int MAX_N = 200; private static final int DEFAULT_START = 1; +/* private static final Map<String, Format> FORMATS = Map.of( "marcxml", Format.MARC_XML, "json", Format.JSON, @@ -107,6 +109,7 @@ public class XSearchServlet extends WhelkHttpServlet { "harvard", Format.UNSUPPORTED, "oxford", Format.UNSUPPORTED ); +*/ private static final Map<String, String> ORDER = Map.of( // "rank" is default @@ -116,6 +119,7 @@ public class XSearchServlet extends WhelkHttpServlet { "-chronological", "publication.year" ); +/* private enum Format { MARC_XML, MODS, @@ -123,6 +127,7 @@ private enum Format { DC, UNSUPPORTED, } +*/ // https://libris.kb.se/help/xsearch_swe.jsp?open=tech private static class Params { @@ -147,14 +152,16 @@ public static class Errors { XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); ResourceLookup resourceLookup; ESSettings esSettings; - Map<Format, Templates> transformers; + //Map<Format, Templates> transformers; @Override protected void init(Whelk whelk) { converter = new JsonLD2MarcXMLConverter(whelk.getMarcFrameConverter()); resourceLookup = ResourceLookup.load(whelk); esSettings = new ESSettings(whelk); + formats = new Formats(); +/* try { transformers = Map.of( Format.MODS, loadXslt("transformers/MARC21slim2MODS3.xsl"), @@ -163,6 +170,7 @@ Format.DC, loadXslt("transformers/MARC21slim2DC.xsl") } catch (IOException | TransformerConfigurationException e) { throw new IllegalStateException(e); } +*/ } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { @@ -191,17 +199,17 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc .orElse(DEFAULT_N); var format = getOptionalSingleNonEmpty(Params.FORMAT, parameters) - .map(f -> FORMATS.getOrDefault(f, Format.MARC_XML)) - .orElse(Format.MARC_XML); + .map(f -> Formats.FORMATS.getOrDefault(f, Formats.Format.MARC_XML)) + .orElse(Formats.Format.MARC_XML); var includeHoldings = getOptionalSingleNonEmpty(Params.HOLDINGS, parameters) .map("true"::equals).orElse(false) - && (format == Format.MARC_XML || format == Format.MODS); + && (format == Formats.Format.MARC_XML || format == Formats.Format.MODS); var include9xx = getOptionalSingleNonEmpty(Params.FORMAT_LEVEL, parameters) .map("full"::equals).orElse(false); - if (format == Format.UNSUPPORTED) { + if (format == Formats.Format.UNSUPPORTED) { throw new InvalidQueryException("format unsupported"); // TODO } @@ -237,8 +245,8 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc switch (format) { case MARC_XML -> sendMarcXML(res, items, start, to, totalItems, includeHoldings, include9xx); case JSON -> sendJson(res, items, start, to, totalItems); - case MODS -> sendTransformedMarc(res, Format.MODS, items, start, to, totalItems, includeHoldings, include9xx); - case DC -> sendTransformedMarc(res, Format.DC, items, start, to, totalItems, false, false); + case MODS -> sendTransformedMarc(res, Formats.Format.MODS, items, start, to, totalItems, includeHoldings, include9xx); + case DC -> sendTransformedMarc(res, Formats.Format.DC, items, start, to, totalItems, false, false); } } catch (InvalidQueryException e) { @@ -356,7 +364,7 @@ private static void copyRecord(XMLStreamReader reader, XMLStreamWriter writer) t } private void sendTransformedMarc(HttpServletResponse res, - Format format, + Formats.Format format, List<Map<?,?>> items, int from, int to, @@ -371,7 +379,7 @@ private void sendTransformedMarc(HttpServletResponse res, writeMarcXml(o, items, from, to, totalItems, includeHoldings, include9xx); ByteArrayInputStream i = new ByteArrayInputStream(o.toByteArray()); - Transformer transformer = transformers.get(format).newTransformer(); + Transformer transformer = formats.transformers.get(format).newTransformer(); OutputStream out = res.getOutputStream(); transformer.transform(new StreamSource(i), new StreamResult(res.getOutputStream())); @@ -379,12 +387,14 @@ private void sendTransformedMarc(HttpServletResponse res, out.close(); } +/* private Templates loadXslt(String name) throws IOException, TransformerConfigurationException { var url = Thread.currentThread().getContextClassLoader().getResource(name); assert url != null; var xsltSource = new StreamSource(url.openStream(), url.toExternalForm()); return transformerFactory.newTemplates(xsltSource); } +*/ private String expandRecord(String bibXml, Document bib, boolean includeHoldings, boolean include9xx) { if (!includeHoldings && !include9xx) { diff --git a/sru/src/main/resources/transformers/MARC21slim2DC.xsl b/sru/src/main/resources/transformers/MARC21slim2DC.xsl index b5fa387c91..74020a0c81 100644 --- a/sru/src/main/resources/transformers/MARC21slim2DC.xsl +++ b/sru/src/main/resources/transformers/MARC21slim2DC.xsl @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="UTF-8"?> +<!-- <?xml version="1.0" encoding="UTF-8"?> --> <xsl:stylesheet version="1.0" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="marc"> - <xsl:output method="xml" indent="yes"/> + <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> <xsl:template match="/"> <xsl:apply-templates/> diff --git a/sru/src/main/resources/transformers/MARC21slim2MODS3.xsl b/sru/src/main/resources/transformers/MARC21slim2MODS3.xsl index c24375d2a1..98563e8585 100644 --- a/sru/src/main/resources/transformers/MARC21slim2MODS3.xsl +++ b/sru/src/main/resources/transformers/MARC21slim2MODS3.xsl @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<!-- <?xml version="1.0" encoding="UTF-8"?> --> <xsl:stylesheet version="1.0" xmlns="http://www.loc.gov/mods/v3" xmlns:xlink="http://www.w3.org/1999/xlink" @@ -6,7 +6,7 @@ xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="marc"> <!--xsl:include href="http://www.loc.gov/marcxml/xslt/MARC21slimUtils.xsl"/--> <xsl:include href="MARC21slimUtils.xsl"/> - <xsl:output method="xml" indent="yes" encoding="UTF-8"/> + <xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/> <!-- Revision 2003/03/25 08:29 jrad From 1049dfff7fbdd340287c51ce035d29ca161b2d09 Mon Sep 17 00:00:00 2001 From: Kai P <kai.poykio@kb.se> Date: Thu, 4 Jun 2026 12:37:31 +0200 Subject: [PATCH 04/10] sru recordsschema and Formats.java --- .../main/java/whelk/sru/servlet/Formats.java | 53 +++++++++++++++++++ .../java/whelk/sru/servlet/SruServlet.java | 13 +++-- 2 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 sru/src/main/java/whelk/sru/servlet/Formats.java diff --git a/sru/src/main/java/whelk/sru/servlet/Formats.java b/sru/src/main/java/whelk/sru/servlet/Formats.java new file mode 100644 index 0000000000..60f9c6044b --- /dev/null +++ b/sru/src/main/java/whelk/sru/servlet/Formats.java @@ -0,0 +1,53 @@ +package whelk.sru.servlet; + +import java.io.IOException; +import java.util.Map; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.Templates; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamSource; + +public class Formats { + + protected Map<Format, Templates> transformers = null; + private final TransformerFactory transformerFactory = TransformerFactory.newInstance(); + + protected enum Format { + MARC_XML, + MODS, + JSON, + DC, + UNSUPPORTED, + } + + protected static final Map<String, Format> FORMATS = Map.of( + "marcxml", Format.MARC_XML, + "json", Format.JSON, + "mods", Format.MODS, + "ris", Format.UNSUPPORTED, + "dc", Format.DC, + "rdfdc", Format.UNSUPPORTED, + "bibtex", Format.UNSUPPORTED, + "refworks", Format.UNSUPPORTED, + "harvard", Format.UNSUPPORTED, + "oxford", Format.UNSUPPORTED + ); + + private Templates loadXslt(String name) throws IOException, TransformerConfigurationException { + var url = Thread.currentThread().getContextClassLoader().getResource(name); + assert url != null; + var xsltSource = new StreamSource(url.openStream(), url.toExternalForm()); + return transformerFactory.newTemplates(xsltSource); + } + + public Formats() { + try { + transformers = Map.of( + Format.MODS, loadXslt("transformers/MARC21slim2MODS3.xsl"), + Format.DC, loadXslt("transformers/MARC21slim2DC.xsl") + ); + } catch (IOException | TransformerConfigurationException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/sru/src/main/java/whelk/sru/servlet/SruServlet.java b/sru/src/main/java/whelk/sru/servlet/SruServlet.java index 1233583868..e175bbf394 100644 --- a/sru/src/main/java/whelk/sru/servlet/SruServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/SruServlet.java @@ -94,13 +94,14 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce } Transformer transformer = null; + String recordsschema = ""; try { switch (Formats.FORMATS.getOrDefault(format, Formats.Format.MARC_XML)) { - case MARC_XML -> transformer = null; - case MODS -> transformer = formats.transformers.get(Formats.Format.MODS).newTransformer(); - case DC -> transformer = formats.transformers.get(Formats.Format.DC).newTransformer(); - case UNSUPPORTED -> transformer = null; + case MARC_XML -> { transformer = null; recordsschema = "marcxml-v1.1"; } + case MODS -> { transformer = formats.transformers.get(Formats.Format.MODS).newTransformer(); recordsschema = "mods-v3.0"; } + case DC -> { transformer = formats.transformers.get(Formats.Format.DC).newTransformer(); recordsschema = "dc-v1.1"; } + case UNSUPPORTED -> { transformer = null; recordsschema = "marcxml-v1.1"; } } } catch (TransformerException e){ @@ -152,8 +153,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce writer.writeEndElement(); // recordPacking writer.writeStartElement("recordSchema"); - // change to format schema - writer.writeCharacters("info:srw/schema/1/marcxml-v1.1"); + writer.writeCharacters("info:srw/schema/1/"+recordsschema); writer.writeEndElement(); // recordSchema //writer.writeStartElement("recordData"); @@ -194,5 +194,4 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce logger.error("Couldn't build SRU response.", e); } } - } From 91a2f2541f0ce20130e5bcf23692167a423213cf Mon Sep 17 00:00:00 2001 From: Kai P <kai.poykio@kb.se> Date: Thu, 23 Jul 2026 11:00:22 +0200 Subject: [PATCH 05/10] secret as parameter --- sru/run-local.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sru/run-local.sh b/sru/run-local.sh index c06963b79b..d1536ed3f5 100644 --- a/sru/run-local.sh +++ b/sru/run-local.sh @@ -1 +1,7 @@ -java -Xmx2G -Dxl.secret.properties=/appl/import/etc/secret.properties.prod -jar build/libs/sru.jar +SECRET=$1 + +if [ "$SECRET" != '' ]; then + java -Xmx2G -Dxl.secret.properties=$SECRET -jar build/libs/sru.jar +else + echo Nope. +fi From 1c025bbd89480fc0e1065414d9fcf336ad841440 Mon Sep 17 00:00:00 2001 From: Kai P <kai.poykio@kb.se> Date: Thu, 23 Jul 2026 11:17:11 +0200 Subject: [PATCH 06/10] cleanup --- .../java/whelk/sru/servlet/SruServlet.java | 51 +++++++++---------- .../whelk/sru/servlet/XSearchServlet.java | 50 ------------------ 2 files changed, 23 insertions(+), 78 deletions(-) diff --git a/sru/src/main/java/whelk/sru/servlet/SruServlet.java b/sru/src/main/java/whelk/sru/servlet/SruServlet.java index e175bbf394..ed2d4f0f74 100644 --- a/sru/src/main/java/whelk/sru/servlet/SruServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/SruServlet.java @@ -12,7 +12,6 @@ import whelk.search2.*; import whelk.sru.cql.Translation; import whelk.util.http.WhelkHttpServlet; -//import javax.xml.transform.stax.StAXResult; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.stream.XMLInputFactory; @@ -93,22 +92,22 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce return; } - Transformer transformer = null; - String recordsschema = ""; + Transformer transformer = null; + String recordsschema = ""; - try { - switch (Formats.FORMATS.getOrDefault(format, Formats.Format.MARC_XML)) { - case MARC_XML -> { transformer = null; recordsschema = "marcxml-v1.1"; } + try { + switch (Formats.FORMATS.getOrDefault(format, Formats.Format.MARC_XML)) { + case MARC_XML -> { transformer = null; recordsschema = "marcxml-v1.1"; } case MODS -> { transformer = formats.transformers.get(Formats.Format.MODS).newTransformer(); recordsschema = "mods-v3.0"; } case DC -> { transformer = formats.transformers.get(Formats.Format.DC).newTransformer(); recordsschema = "dc-v1.1"; } - case UNSUPPORTED -> { transformer = null; recordsschema = "marcxml-v1.1"; } + case UNSUPPORTED -> { transformer = null; recordsschema = "marcxml-v1.1"; } + } } - } - catch (TransformerException e){ + catch (TransformerException e){ logger.info(e.getMessage()); res.sendError(400); return; - } + } // Like the pre-existing implementation, supply only up to 10 hits per query. List items = (List) results.get("items"); @@ -133,7 +132,6 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce writer.writeEndElement(); // numberOfRecords writer.writeStartElement("records"); - //writer.flush(); for (Object o : items) { Map m = (Map) o; @@ -156,28 +154,25 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce writer.writeCharacters("info:srw/schema/1/"+recordsschema); writer.writeEndElement(); // recordSchema - //writer.writeStartElement("recordData"); writer.flush(); - out.write("<recordData>".getBytes("UTF-8")); + out.write("<recordData>".getBytes("UTF-8")); - if ( transformer == null ) { + if ( transformer == null ) { StaxUtils.copy(xmlInputFactory.createXMLStreamReader(new StringReader(convertedText)), writer); - } else { - try { - transformer.transform(new StreamSource(new StringReader(convertedText)), new StreamResult(out)); - } - catch (TransformerException e) { - logger.info(e.getMessage()); - res.sendError(400); - return; - } - } - out.write("</recordData>".getBytes("UTF-8")); + } else { + try { + transformer.transform(new StreamSource(new StringReader(convertedText)), new StreamResult(out)); + } + catch (TransformerException e) { + logger.info(e.getMessage()); + res.sendError(400); + return; + } + } + out.write("</recordData>".getBytes("UTF-8")); out.flush(); - //writer.writeEndElement(); // recordData - writer.writeEndElement(); // record writer.flush(); } @@ -186,7 +181,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce writer.writeEndElement(); // searchRetrieveResponse writer.writeEndDocument(); - writer.flush(); + writer.flush(); writer.close(); out.flush(); out.close(); diff --git a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java index 028b0c1ca2..342345f92d 100644 --- a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java @@ -34,11 +34,8 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; -//import javax.xml.transform.Templates; import javax.xml.transform.Transformer; -//import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; -//import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import java.io.ByteArrayInputStream; @@ -89,28 +86,12 @@ public class XSearchServlet extends WhelkHttpServlet { private final Logger logger = LogManager.getLogger(this.getClass()); private final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); - //private final TransformerFactory transformerFactory = TransformerFactory.newInstance(); private Formats formats = null; private static final int DEFAULT_N = 10; private static final int MAX_N = 200; private static final int DEFAULT_START = 1; -/* - private static final Map<String, Format> FORMATS = Map.of( - "marcxml", Format.MARC_XML, - "json", Format.JSON, - "mods", Format.MODS, - "ris", Format.UNSUPPORTED, - "dc", Format.DC, - "rdfdc", Format.UNSUPPORTED, - "bibtex", Format.UNSUPPORTED, - "refworks", Format.UNSUPPORTED, - "harvard", Format.UNSUPPORTED, - "oxford", Format.UNSUPPORTED - ); -*/ - private static final Map<String, String> ORDER = Map.of( // "rank" is default "alphabetical", SORT_KEY_BY_LANG + ".sv", @@ -119,16 +100,6 @@ public class XSearchServlet extends WhelkHttpServlet { "-chronological", "publication.year" ); -/* - private enum Format { - MARC_XML, - MODS, - JSON, - DC, - UNSUPPORTED, - } -*/ - // https://libris.kb.se/help/xsearch_swe.jsp?open=tech private static class Params { public static final String QUERY = "query"; @@ -152,7 +123,6 @@ public static class Errors { XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); ResourceLookup resourceLookup; ESSettings esSettings; - //Map<Format, Templates> transformers; @Override protected void init(Whelk whelk) { @@ -160,17 +130,6 @@ protected void init(Whelk whelk) { resourceLookup = ResourceLookup.load(whelk); esSettings = new ESSettings(whelk); formats = new Formats(); - -/* - try { - transformers = Map.of( - Format.MODS, loadXslt("transformers/MARC21slim2MODS3.xsl"), - Format.DC, loadXslt("transformers/MARC21slim2DC.xsl") - ); - } catch (IOException | TransformerConfigurationException e) { - throw new IllegalStateException(e); - } -*/ } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { @@ -387,15 +346,6 @@ private void sendTransformedMarc(HttpServletResponse res, out.close(); } -/* - private Templates loadXslt(String name) throws IOException, TransformerConfigurationException { - var url = Thread.currentThread().getContextClassLoader().getResource(name); - assert url != null; - var xsltSource = new StreamSource(url.openStream(), url.toExternalForm()); - return transformerFactory.newTemplates(xsltSource); - } -*/ - private String expandRecord(String bibXml, Document bib, boolean includeHoldings, boolean include9xx) { if (!includeHoldings && !include9xx) { return bibXml; From 0b57fd580c86b7c02ee8f1e72e8f242dc3f9b81a Mon Sep 17 00:00:00 2001 From: Kai P <kai.poykio@kb.se> Date: Fri, 24 Jul 2026 14:12:19 +0200 Subject: [PATCH 07/10] mrg wit dev --- .../main/java/whelk/sru/servlet/Formats.java | 20 ++-- .../java/whelk/sru/servlet/SruServlet.java | 4 +- .../whelk/sru/servlet/XSearchServlet.java | 91 +++++++++++++------ 3 files changed, 77 insertions(+), 38 deletions(-) diff --git a/sru/src/main/java/whelk/sru/servlet/Formats.java b/sru/src/main/java/whelk/sru/servlet/Formats.java index 60f9c6044b..54f6fbf4f9 100644 --- a/sru/src/main/java/whelk/sru/servlet/Formats.java +++ b/sru/src/main/java/whelk/sru/servlet/Formats.java @@ -9,7 +9,7 @@ public class Formats { - protected Map<Format, Templates> transformers = null; + protected Map<Format, Xslt> transformers = null; private final TransformerFactory transformerFactory = TransformerFactory.newInstance(); protected enum Format { @@ -17,7 +17,8 @@ protected enum Format { MODS, JSON, DC, - UNSUPPORTED, + REF_WORKS, + UNSUPPORTED } protected static final Map<String, Format> FORMATS = Map.of( @@ -28,23 +29,28 @@ protected enum Format { "dc", Format.DC, "rdfdc", Format.UNSUPPORTED, "bibtex", Format.UNSUPPORTED, - "refworks", Format.UNSUPPORTED, + "refworks", Format.REF_WORKS, "harvard", Format.UNSUPPORTED, "oxford", Format.UNSUPPORTED ); - private Templates loadXslt(String name) throws IOException, TransformerConfigurationException { + public record Xslt(Templates templates, String contentType) { + + } + + public Xslt loadXslt(String name, String contentType) throws IOException, TransformerConfigurationException { var url = Thread.currentThread().getContextClassLoader().getResource(name); assert url != null; var xsltSource = new StreamSource(url.openStream(), url.toExternalForm()); - return transformerFactory.newTemplates(xsltSource); + return new Xslt(transformerFactory.newTemplates(xsltSource), contentType); } public Formats() { try { transformers = Map.of( - Format.MODS, loadXslt("transformers/MARC21slim2MODS3.xsl"), - Format.DC, loadXslt("transformers/MARC21slim2DC.xsl") + Format.MODS, loadXslt("transformers/MARC21slim2MODS3.xsl", "text/xml"), + Format.DC, loadXslt("transformers/MARC21slim2DC.xsl", "text/xml"), + Format.REF_WORKS, loadXslt("transformers/refworks.xsl", "text/plain") ); } catch (IOException | TransformerConfigurationException e) { throw new IllegalStateException(e); diff --git a/sru/src/main/java/whelk/sru/servlet/SruServlet.java b/sru/src/main/java/whelk/sru/servlet/SruServlet.java index ed2d4f0f74..40cf2a08ea 100644 --- a/sru/src/main/java/whelk/sru/servlet/SruServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/SruServlet.java @@ -98,8 +98,8 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce try { switch (Formats.FORMATS.getOrDefault(format, Formats.Format.MARC_XML)) { case MARC_XML -> { transformer = null; recordsschema = "marcxml-v1.1"; } - case MODS -> { transformer = formats.transformers.get(Formats.Format.MODS).newTransformer(); recordsschema = "mods-v3.0"; } - case DC -> { transformer = formats.transformers.get(Formats.Format.DC).newTransformer(); recordsschema = "dc-v1.1"; } + case MODS -> { transformer = formats.transformers.get(Formats.Format.MODS).templates().newTransformer(); recordsschema = "mods-v3.0"; } + case DC -> { transformer = formats.transformers.get(Formats.Format.DC).templates().newTransformer(); recordsschema = "dc-v1.1"; } case UNSUPPORTED -> { transformer = null; recordsschema = "marcxml-v1.1"; } } } diff --git a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java index 342345f92d..8f34f3ee48 100644 --- a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java @@ -3,9 +3,12 @@ import groovy.lang.Tuple2; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import se.kb.libris.util.marc.MarcRecord; +import se.kb.libris.util.marc.Datafield; import se.kb.libris.util.marc.Field; import se.kb.libris.util.marc.MarcFieldComparator; import se.kb.libris.util.marc.MarcRecord; +import se.kb.libris.util.marc.Subfield; import se.kb.libris.util.marc.io.MarcXmlRecordReader; import se.kb.libris.util.marc.io.MarcXmlRecordWriter; import se.kb.libris.utils.isbn.ConvertException; @@ -44,6 +47,7 @@ import java.io.OutputStream; import java.io.StringReader; import java.nio.charset.StandardCharsets; +import java.util.Iterator; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -88,6 +92,8 @@ public class XSearchServlet extends WhelkHttpServlet { private final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); private Formats formats = null; + private static final String appId = "https://libris.kb.se/xsearch"; + private static final int DEFAULT_N = 10; private static final int MAX_N = 200; private static final int DEFAULT_START = 1; @@ -123,12 +129,14 @@ public static class Errors { XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); ResourceLookup resourceLookup; ESSettings esSettings; + AppParams appParams; @Override protected void init(Whelk whelk) { converter = new JsonLD2MarcXMLConverter(whelk.getMarcFrameConverter()); resourceLookup = ResourceLookup.load(whelk); esSettings = new ESSettings(whelk); + appParams = new AppParams(appId, whelk); formats = new Formats(); } @@ -165,7 +173,10 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc .map("true"::equals).orElse(false) && (format == Formats.Format.MARC_XML || format == Formats.Format.MODS); - var include9xx = getOptionalSingleNonEmpty(Params.FORMAT_LEVEL, parameters) + //var include9xx = getOptionalSingleNonEmpty(Params.FORMAT_LEVEL, parameters) + // .map("full"::equals).orElse(false); + + boolean formatLevelFull = getOptionalSingleNonEmpty(Params.FORMAT_LEVEL, parameters) .map("full"::equals).orElse(false); if (format == Formats.Format.UNSUPPORTED) { @@ -179,11 +190,12 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc try { // TODO handle onr (record ID) here or in search2? - String instanceOnlyQueryString = "(" + query + ") AND type=Instance"; + //String instanceOnlyQueryString = "(" + query + ") AND type=Instance"; // This part is a little weird HashMap<String, String[]> paramsAsIfSearch = new HashMap<>(); - String[] q = new String[]{ instanceOnlyQueryString }; + //String[] q = new String[]{ instanceOnlyQueryString }; + String[] q = new String[]{ query }; paramsAsIfSearch.put("_q", q); paramsAsIfSearch.put("_stats", new String[]{"false"}); // don't need facets paramsAsIfSearch.put("_offset", new String[]{"" + (start - 1)}); @@ -193,8 +205,8 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc } QueryParams qp = new QueryParams(paramsAsIfSearch); - AppParams ap = new AppParams(new HashMap<>(), whelk.getJsonld()); - var results = new Query(qp, ap, resourceLookup, esSettings, whelk).collectResults(); + //AppParams ap = new AppParams(new HashMap<>(), whelk.getJsonld()); + var results = new Query(qp, appParams, resourceLookup, esSettings, whelk).collectResults(); @SuppressWarnings("unchecked") List<Map<?,?>> items = (List<Map<?,?>>) results.get("items"); @@ -202,10 +214,11 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc int to = Math.min(start + n, totalItems); switch (format) { - case MARC_XML -> sendMarcXML(res, items, start, to, totalItems, includeHoldings, include9xx); + case MARC_XML -> sendMarcXML(res, items, start, to, totalItems, includeHoldings, formatLevelFull); case JSON -> sendJson(res, items, start, to, totalItems); - case MODS -> sendTransformedMarc(res, Formats.Format.MODS, items, start, to, totalItems, includeHoldings, include9xx); + case MODS -> sendTransformedMarc(res, Formats.Format.MODS, items, start, to, totalItems, includeHoldings, formatLevelFull); case DC -> sendTransformedMarc(res, Formats.Format.DC, items, start, to, totalItems, false, false); + case REF_WORKS -> sendTransformedMarc(res, Formats.Format.REF_WORKS, items, start, to, totalItems, includeHoldings, formatLevelFull); } } catch (InvalidQueryException e) { @@ -223,11 +236,11 @@ private void sendMarcXML(HttpServletResponse res, int to, int totalItems, boolean includeHoldings, - boolean include9xx) throws IOException, XMLStreamException { + boolean formatLevelFull) throws IOException, XMLStreamException { res.setCharacterEncoding("UTF-8"); res.setContentType("text/xml"); OutputStream out = res.getOutputStream(); - writeMarcXml(out, items, from, to, totalItems, includeHoldings, include9xx); + writeMarcXml(out, items, from, to, totalItems, includeHoldings, formatLevelFull); out.flush(); out.close(); } @@ -238,7 +251,7 @@ private void writeMarcXml(OutputStream o, int to, int totalItems, boolean includeHoldings, - boolean include9xx) throws XMLStreamException { + boolean formatLevelFull) throws XMLStreamException { XMLStreamWriter writer = xmlOutputFactory.createXMLStreamWriter(o); @@ -262,7 +275,7 @@ private void writeMarcXml(OutputStream o, var bibXml = (String) converter.convert(embellished.data, embellished.getShortId()) .get(JsonLd.NON_JSON_CONTENT_KEY); - bibXml = expandRecord(bibXml, embellished, includeHoldings, include9xx); + bibXml = expandRecord(bibXml, embellished, includeHoldings, formatLevelFull); return bibXml; }).forEachOrdered( convertedText -> { @@ -338,7 +351,7 @@ private void sendTransformedMarc(HttpServletResponse res, writeMarcXml(o, items, from, to, totalItems, includeHoldings, include9xx); ByteArrayInputStream i = new ByteArrayInputStream(o.toByteArray()); - Transformer transformer = formats.transformers.get(format).newTransformer(); + Transformer transformer = formats.transformers.get(format).templates().newTransformer(); OutputStream out = res.getOutputStream(); transformer.transform(new StreamSource(i), new StreamResult(res.getOutputStream())); @@ -346,33 +359,33 @@ private void sendTransformedMarc(HttpServletResponse res, out.close(); } - private String expandRecord(String bibXml, Document bib, boolean includeHoldings, boolean include9xx) { - if (!includeHoldings && !include9xx) { - return bibXml; - } - + private String expandRecord(String bibXml, Document bib, boolean includeHoldings, boolean formatLevelFull) { try { MarcRecord bibRecord = MarcXmlRecordReader.fromXml(bibXml); - ListIterator<Field> li = bibRecord.listIterator(); - while (li.hasNext()) { - if (Objects.equals((li.next()).getTag(), "003")) { - li.remove(); + if (formatLevelFull) { + ListIterator<Field> li = bibRecord.listIterator(); + while (li.hasNext()) { + if (Objects.equals((li.next()).getTag(), "003")) { + li.remove(); + } } + bibRecord.addField(bibRecord.createControlfield("003", "SE-LIBR"), MarcFieldComparator.strictSorted); } - bibRecord.addField(bibRecord.createControlfield("003", "SE-LIBR"), MarcFieldComparator.strictSorted); - if (includeHoldings) { - List<Document> holdingDocuments = whelk.getAttachedHoldings(bib.getThingIdentifiers()); - for (Document holding : holdingDocuments) { - var holdXml = (String) converter.convert(holding.data, holding.getShortId()) - .get(JsonLd.NON_JSON_CONTENT_KEY); - MarcRecord holdRecord = MarcXmlRecordReader.fromXml(holdXml); + for (Document holding : whelk.getAttachedHoldings(bib.getThingIdentifiers())) { + var holdXml = (String) converter.convert(holding.data, holding.getShortId()) + .get(JsonLd.NON_JSON_CONTENT_KEY); + MarcRecord holdRecord = MarcXmlRecordReader.fromXml(holdXml); + + if (includeHoldings) { mergeBibMfhd(bibRecord, holding.getHeldBySigel(), holdRecord); + } else { + copy856(bibRecord, holding.getHeldBySigel(), holdRecord); } } - if (include9xx) { + if (formatLevelFull) { // Only 976... addSabTitles(bibRecord); } @@ -387,6 +400,26 @@ private String expandRecord(String bibXml, Document bib, boolean includeHoldings } } + // associatedMedia links from holdings + private static void copy856(MarcRecord bibRecord, String sigel, MarcRecord holdRecord) { + for (var f : holdRecord.getFields("856")) { + if (f instanceof Datafield) { + Datafield df = bibRecord.createDatafield(f.getTag()); + df.addSubfield('5', sigel); + df.setIndicator(0, ((Datafield)f).getIndicator(0)); + df.setIndicator(1, ((Datafield)f).getIndicator(1)); + + Iterator<Subfield> i = ((Datafield)f).iterator(); + while (i.hasNext()) { + Subfield sf = i.next(); + df.addSubfield(sf.getCode(), sf.getData()); + } + + bibRecord.addField(df); + } + } + } + // Or convert from MARC-XML? // https://git.kb.se/libris/legacy/search/-/blob/master/src/main/webapp/transformers/MARC21slim2JSON.xsl?ref_type=heads private void sendJson(HttpServletResponse res, From cd77fd3b7773c903a6264818ef72293a65e055c6 Mon Sep 17 00:00:00 2001 From: Kai P <kai.poykio@kb.se> Date: Tue, 18 Aug 2026 11:09:23 +0200 Subject: [PATCH 08/10] mrg... --- .../java/whelk/sru/servlet/SruServlet.java | 51 ++++++++++++++++++- .../whelk/sru/servlet/XSearchServlet.java | 5 +- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/sru/src/main/java/whelk/sru/servlet/SruServlet.java b/sru/src/main/java/whelk/sru/servlet/SruServlet.java index 40cf2a08ea..ca03d2f453 100644 --- a/sru/src/main/java/whelk/sru/servlet/SruServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/SruServlet.java @@ -41,12 +41,25 @@ public class SruServlet extends WhelkHttpServlet { ESSettings esSettings; private Formats formats = null; + AppParams appParams; + + String explain = loadResource("explain.xml"); + ExportProfile marcExportProfile; + @Override protected void init(Whelk whelk) { converter = new JsonLD2MarcXMLConverter(whelk.getMarcFrameConverter()); resourceLookup = ResourceLookup.load(whelk); esSettings = new ESSettings(whelk); formats = new Formats(); + appParams = new AppParams(appId, whelk); + Properties marcProperties = new Properties(); + marcExportProfile = new ExportProfile(marcProperties); + try { + marcProperties.load(new StringReader(loadResource("websok.properties"))); + } catch (IOException e) { + logger.error("Could not read MARC profile from jar resources.", e); + } } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { @@ -73,8 +86,8 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce String format; try { - String CqlQueryString = parameters.get("query")[0]; - format = parameters.get("recordSchema")[0]; + String CqlQueryString = getParameter(parameters, "query"); + format = getParameter(parameters, "recordSchema"); String XlQueryString = Translation.translateCqlToXlQuery(CqlQueryString); // This part is a little weird @@ -189,4 +202,38 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce logger.error("Couldn't build SRU response.", e); } } + + private static void sendXml(HttpServletResponse res, int status, String xml, String version) throws IOException { + if (!"1.2".equals(version)) { + xml = xml.replace("<version>1.2</version>", "<version>" + version + "</version>"); + xml = xml.replace("<zs:version>1.2</zs:version>", "<zs:version>" + version + "</zs:version>"); + } + + res.setStatus(status); + var writer = new PrintWriter(new BufferedOutputStream(res.getOutputStream())); + writer.print(xml); + writer.flush(); + writer.close(); + } + + private static String getParameter(Map<String, String[]> parameters, String name) { + if (!parameters.containsKey(name)) { + return null; + } + var parameter = parameters.get(name); + if (parameter.length != 1) { + return null; + } + return parameter[0]; + } + + private static String loadResource(String name) { + var path = "sru/" + name; + try (InputStream scriptStream = SruServlet.class.getClassLoader().getResourceAsStream(path)) { + assert scriptStream != null; + return IOUtils.toString(new InputStreamReader(scriptStream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java index 8f34f3ee48..2dd346bbb2 100644 --- a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java @@ -1,9 +1,8 @@ package whelk.sru.servlet; import groovy.lang.Tuple2; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import se.kb.libris.util.marc.MarcRecord; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; import se.kb.libris.util.marc.Datafield; import se.kb.libris.util.marc.Field; import se.kb.libris.util.marc.MarcFieldComparator; From d0ae05a4b513406a3c7b217ce140bbf8c9160954 Mon Sep 17 00:00:00 2001 From: Kai P <kai.poykio@kb.se> Date: Tue, 18 Aug 2026 12:15:15 +0200 Subject: [PATCH 09/10] ... --- sru/src/main/java/whelk/sru/servlet/SruServlet.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sru/src/main/java/whelk/sru/servlet/SruServlet.java b/sru/src/main/java/whelk/sru/servlet/SruServlet.java index ca03d2f453..969a15ded1 100644 --- a/sru/src/main/java/whelk/sru/servlet/SruServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/SruServlet.java @@ -23,9 +23,7 @@ import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.Transformer; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringReader; +import java.io.*; import java.io.InputStreamReader; import java.util.*; From b71823b2448e17ad031aa55f4db91b9f8a095c4c Mon Sep 17 00:00:00 2001 From: Kai P <kai.poykio@kb.se> Date: Tue, 18 Aug 2026 13:52:38 +0200 Subject: [PATCH 10/10] final? --- sru/src/main/java/whelk/sru/servlet/SruServlet.java | 13 ++++++++++--- .../main/java/whelk/sru/servlet/XSearchServlet.java | 9 ++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/sru/src/main/java/whelk/sru/servlet/SruServlet.java b/sru/src/main/java/whelk/sru/servlet/SruServlet.java index 60176412f0..0475ee276f 100644 --- a/sru/src/main/java/whelk/sru/servlet/SruServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/SruServlet.java @@ -210,15 +210,22 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce writer.writeCharacters("info:srw/schema/1/"+recordsschema); writer.writeEndElement(); // recordSchema - writer.flush(); + writer.flush(); out.write("<recordData>".getBytes("UTF-8")); + Vector<MarcRecord> marcRecords = MarcExport.compileVirtualMarcRecord(marcExportProfile, embellished, whelk, converter); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + MarcXmlRecordWriter stringOutput = new MarcXmlRecordWriter(baos, "UTF-8", false); + for (MarcRecord mr : marcRecords) { + stringOutput.writeRecord(mr); + } + if ( transformer == null ) { - StaxUtils.copy(xmlInputFactory.createXMLStreamReader(new StringReader(convertedText)), writer); + StaxUtils.copy(xmlInputFactory.createXMLStreamReader(new StringReader(baos.toString())), writer); } else { try { - transformer.transform(new StreamSource(new StringReader(convertedText)), new StreamResult(out)); + transformer.transform(new StreamSource(new StringReader(baos.toString())), new StreamResult(out)); } catch (TransformerException e) { logger.info(e.getMessage()); diff --git a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java index 5e00965c2a..3627152074 100644 --- a/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java +++ b/sru/src/main/java/whelk/sru/servlet/XSearchServlet.java @@ -37,6 +37,7 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.stream.StreamResult; @@ -97,8 +98,6 @@ public class XSearchServlet extends WhelkHttpServlet { private static final String appId = "https://libris.kb.se/xsearch"; - private static final String appId = "https://libris.kb.se/xsearch"; - private static final int DEFAULT_N = 10; private static final int MAX_N = 200; private static final int DEFAULT_START = 1; @@ -203,7 +202,7 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc .map(ORDER::get) .orElse(null); - String callback = format == Format.JSON + String callback = format == Formats.Format.JSON ? getOptionalSingleNonEmpty(Params.CALLBACK, parameters).orElse(null) : null; if (callback != null @@ -235,7 +234,7 @@ public void doGet2(HttpServletRequest req, HttpServletResponse res) throws IOExc switch (format) { case MARC_XML -> sendMarcXML(res, items, start, to, totalItems, includeHoldings, formatLevelFull); - case JSON -> sendJson(res, items, start, to, totalItems); + case JSON -> sendJson(res, items, start, to, totalItems, callback); case MODS -> sendTransformedMarc(res, Formats.Format.MODS, items, start, to, totalItems, includeHoldings, formatLevelFull); case DC -> sendTransformedMarc(res, Formats.Format.DC, items, start, to, totalItems, false, false); case REF_WORKS -> sendTransformedMarc(res, Formats.Format.REF_WORKS, items, start, to, totalItems, includeHoldings, formatLevelFull); @@ -369,7 +368,7 @@ private void sendTransformedMarc(HttpServletResponse res, boolean formatLevelFull) throws IOException, XMLStreamException, TransformerException { res.setCharacterEncoding("UTF-8"); - res.setContentType(xslt.contentType); + res.setContentType(formats.transformers.get(format).contentType()); ByteArrayOutputStream o = new ByteArrayOutputStream(); writeMarcXml(o, items, from, to, totalItems, includeHoldings, formatLevelFull);