Skip to content

Commit 92399f6

Browse files
committed
load XSLT from JAR, if no local file is found; fixes #27598
1 parent 7bf3bc3 commit 92399f6

File tree

2 files changed

+115
-9
lines changed

2 files changed

+115
-9
lines changed

goobi-viewer-connector/src/main/java/io/goobi/viewer/connector/oai/model/formats/MARCXMLFormat.java

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,34 @@
1515
*/
1616
package io.goobi.viewer.connector.oai.model.formats;
1717

18-
import java.io.FileInputStream;
18+
import java.io.File;
1919
import java.io.FileNotFoundException;
2020
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.net.MalformedURLException;
23+
import java.net.URI;
24+
import java.net.URISyntaxException;
25+
import java.net.URL;
26+
import java.nio.file.Path;
27+
import java.nio.file.Paths;
2128
import java.util.Arrays;
2229
import java.util.List;
2330

31+
import javax.xml.XMLConstants;
32+
import javax.xml.transform.Transformer;
33+
import javax.xml.transform.TransformerException;
34+
import javax.xml.transform.TransformerFactory;
35+
import javax.xml.transform.URIResolver;
36+
import javax.xml.transform.stream.StreamSource;
37+
2438
import org.apache.logging.log4j.LogManager;
2539
import org.apache.logging.log4j.Logger;
2640
import org.apache.solr.client.solrj.SolrServerException;
41+
import org.jdom2.Document;
2742
import org.jdom2.Element;
2843
import org.jdom2.Namespace;
29-
import org.jdom2.transform.XSLTransformException;
30-
import org.jdom2.transform.XSLTransformer;
44+
import org.jdom2.transform.JDOMResult;
45+
import org.jdom2.transform.JDOMSource;
3146

3247
import io.goobi.viewer.connector.DataManager;
3348
import io.goobi.viewer.connector.oai.RequestHandler;
@@ -47,6 +62,8 @@ public class MARCXMLFormat extends METSFormat {
4762
static final Namespace NAMESPACE_MARC = Namespace.getNamespace("marc", "http://www.loc.gov/MARC21/slim");
4863
static final Namespace NAMESPACE_MODS = Namespace.getNamespace("mods", "http://www.loc.gov/mods/v3");
4964

65+
private static final String FILENAME_XSLT = "MODS2MARC21slim.xsl";
66+
5067
/** {@inheritDoc} */
5168
@Override
5269
public Element createListRecords(RequestHandler handler, int firstVirtualRow, int firstRawRow, int numRows, String versionDiscriminatorField,
@@ -178,10 +195,42 @@ static Element convertModsToMarc(Element mods, Element header) {
178195
org.jdom2.Document marcDoc = new org.jdom2.Document();
179196
marcDoc.setRootElement(newmods);
180197

181-
String filename = DataManager.getInstance().getConfiguration().getMods2MarcXsl();
182-
try (FileInputStream fis = new FileInputStream(filename)) {
183-
XSLTransformer transformer = new XSLTransformer(fis);
184-
org.jdom2.Document docTrans = transformer.transform(marcDoc);
198+
ClassLoader cl = MARCXMLFormat.class.getClassLoader();
199+
URL xsltUrl = null;
200+
File localXsltFile = new File(DataManager.getInstance().getConfiguration().getMods2MarcXsl());
201+
if (localXsltFile.exists()) {
202+
// Check for local XSLT file
203+
try {
204+
xsltUrl = localXsltFile.toURI().toURL();
205+
logger.debug("Using local XSLT: {}", localXsltFile.getAbsolutePath());
206+
} catch (MalformedURLException e) {
207+
throw new IllegalStateException("Invalid path for local override XSLT", e);
208+
}
209+
}
210+
if (xsltUrl == null) {
211+
// In-JAR XSLT fallback
212+
xsltUrl = cl.getResource(FILENAME_XSLT);
213+
if (xsltUrl == null) {
214+
throw new IllegalStateException("Default XSLT not found: " + FILENAME_XSLT);
215+
}
216+
}
217+
218+
try (InputStream input = xsltUrl.openStream()) {
219+
StreamSource xsltSource = new StreamSource(input);
220+
xsltSource.setSystemId(xsltUrl.toExternalForm());
221+
222+
TransformerFactory factory = TransformerFactory.newInstance();
223+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
224+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
225+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
226+
factory.setURIResolver(createXsltUriResolver());
227+
228+
Transformer transformer = factory.newTransformer(xsltSource);
229+
230+
JDOMSource source = new JDOMSource(marcDoc);
231+
JDOMResult result = new JDOMResult();
232+
transformer.transform(source, result);
233+
Document docTrans = result.getDocument();
185234
Element root = docTrans.getRootElement();
186235

187236
Element eleRecord = new Element(XmlConstants.ELE_NAME_RECORD, NAMESPACE_XML);
@@ -201,9 +250,66 @@ static Element convertModsToMarc(Element mods, Element header) {
201250
} catch (FileNotFoundException e) {
202251
logger.error(e.getMessage());
203252
return new ErrorCode().getCannotDisseminateFormat();
204-
} catch (IOException | XSLTransformException e) {
253+
} catch (IOException | TransformerException e) {
205254
logger.error(e.getMessage(), e);
206255
return new ErrorCode().getCannotDisseminateFormat();
207256
}
208257
}
258+
259+
private static URIResolver createXsltUriResolver() {
260+
return (href, base) -> {
261+
// If base is null, just try classpath
262+
if (base == null) {
263+
InputStream is = MARCXMLFormat.class.getClassLoader().getResourceAsStream(href);
264+
if (is == null) {
265+
throw new TransformerException("Classpath include not found: " + href);
266+
}
267+
StreamSource src = new StreamSource(is);
268+
src.setSystemId(MARCXMLFormat.class.getClassLoader()
269+
.getResource(href)
270+
.toExternalForm());
271+
return src;
272+
}
273+
274+
try {
275+
URI baseUri = new URI(base);
276+
if ("file".equals(baseUri.getScheme())) {
277+
// filesystem include
278+
Path basePath = Paths.get(baseUri).getParent();
279+
Path resolved = basePath.resolve(href).normalize();
280+
281+
Path allowedRoot = Paths.get(DataManager.getInstance()
282+
.getConfiguration()
283+
.getOaiFolder())
284+
.toAbsolutePath()
285+
.normalize();
286+
if (!resolved.startsWith(allowedRoot)) {
287+
throw new TransformerException("Access denied: " + href);
288+
}
289+
return new StreamSource(resolved.toFile());
290+
291+
}
292+
// classpath include: resolve relative to base systemId
293+
String basePathStr = baseUri.getPath();
294+
if (basePathStr == null)
295+
basePathStr = "";
296+
int lastSlash = basePathStr.lastIndexOf('/');
297+
String parentDir = (lastSlash >= 0) ? basePathStr.substring(0, lastSlash + 1) : "";
298+
String includedPath = parentDir + href;
299+
300+
InputStream is = MARCXMLFormat.class.getClassLoader().getResourceAsStream(includedPath);
301+
if (is == null) {
302+
throw new TransformerException("Classpath include not found: " + includedPath);
303+
}
304+
StreamSource src = new StreamSource(is);
305+
src.setSystemId(MARCXMLFormat.class.getClassLoader()
306+
.getResource(includedPath)
307+
.toExternalForm());
308+
return src;
309+
310+
} catch (URISyntaxException e) {
311+
throw new TransformerException("Invalid base URI: " + base, e);
312+
}
313+
};
314+
}
209315
}

goobi-viewer-connector/src/main/resources/MODS2MARC21slim.xsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<!-- MODS v3 to MARC21Slim transformation ntra 2/20/04 -->
99

1010
<!--xsl:include href="http://www.loc.gov/marcxml/xslt/MARC21slimUtils.xsl"/-->
11-
<xsl:include href="file:///opt/digiverso/viewer/oai/MARC21slimUtils.xsl"/>
11+
<xsl:include href="MARC21slimUtils.xsl"/>
1212

1313
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
1414

0 commit comments

Comments
 (0)