Skip to content

Commit 05a4b5c

Browse files
authored
Merge pull request DSpace#11897 from the-library-code/oai-pmh-html
Serve OAI-PMH app as pre-transformed HTML according to Accept header
2 parents b19617a + d9359c6 commit 05a4b5c

2 files changed

Lines changed: 80 additions & 2 deletions

File tree

dspace-oai/src/main/java/org/dspace/xoai/controller/DSpaceOAIDataProvider.java

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,36 @@
1111
import static java.util.Arrays.asList;
1212
import static org.apache.logging.log4j.LogManager.getLogger;
1313

14+
import java.io.ByteArrayInputStream;
15+
import java.io.ByteArrayOutputStream;
1416
import java.io.IOException;
1517
import java.io.OutputStream;
1618
import java.util.Enumeration;
1719
import java.util.HashMap;
1820
import java.util.List;
1921
import java.util.Map;
2022
import javax.xml.stream.XMLStreamException;
23+
import javax.xml.transform.Result;
24+
import javax.xml.transform.Source;
25+
import javax.xml.transform.Transformer;
26+
import javax.xml.transform.TransformerException;
27+
import javax.xml.transform.TransformerFactory;
28+
import javax.xml.transform.stream.StreamResult;
29+
import javax.xml.transform.stream.StreamSource;
2130

2231
import com.lyncode.xoai.dataprovider.OAIDataProvider;
2332
import com.lyncode.xoai.dataprovider.OAIRequestParameters;
2433
import com.lyncode.xoai.dataprovider.core.XOAIManager;
2534
import com.lyncode.xoai.dataprovider.exceptions.InvalidContextException;
2635
import com.lyncode.xoai.dataprovider.exceptions.OAIException;
2736
import com.lyncode.xoai.dataprovider.exceptions.WritingXmlException;
37+
import jakarta.annotation.PostConstruct;
2838
import jakarta.servlet.ServletException;
2939
import jakarta.servlet.http.HttpServletRequest;
3040
import jakarta.servlet.http.HttpServletResponse;
3141
import org.apache.logging.log4j.Logger;
3242
import org.dspace.core.Context;
43+
import org.dspace.services.ConfigurationService;
3344
import org.dspace.xoai.services.api.cache.XOAICacheService;
3445
import org.dspace.xoai.services.api.config.XOAIManagerResolver;
3546
import org.dspace.xoai.services.api.config.XOAIManagerResolverException;
@@ -41,6 +52,10 @@
4152
import org.dspace.xoai.services.impl.xoai.DSpaceResumptionTokenFormatter;
4253
import org.springframework.beans.factory.annotation.Autowired;
4354
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
55+
import org.springframework.core.io.DefaultResourceLoader;
56+
import org.springframework.core.io.Resource;
57+
import org.springframework.core.io.ResourceLoader;
58+
import org.springframework.http.MediaType;
4459
import org.springframework.stereotype.Controller;
4560
import org.springframework.ui.Model;
4661
import org.springframework.web.bind.annotation.PathVariable;
@@ -57,6 +72,9 @@
5772
public class DSpaceOAIDataProvider {
5873
private static final Logger log = getLogger(DSpaceOAIDataProvider.class);
5974

75+
private TransformerFactory htmlTransformerFactory = null;
76+
private byte[] htmlTransformerSource = null;
77+
6078
@Autowired
6179
XOAICacheService cacheService;
6280
@Autowired
@@ -69,9 +87,32 @@ public class DSpaceOAIDataProvider {
6987
IdentifyResolver identifyResolver;
7088
@Autowired
7189
SetRepositoryResolver setRepositoryResolver;
90+
@Autowired
91+
ConfigurationService configurationService;
7292

7393
private DSpaceResumptionTokenFormatter resumptionTokenFormat = new DSpaceResumptionTokenFormatter();
7494

95+
@PostConstruct
96+
public void setUpHTMLTransformerFactory() {
97+
try {
98+
XOAIManager manager = xoaiManagerResolver.getManager();
99+
if (configurationService.getBooleanProperty("oai.html", true) && manager.hasStyleSheet()) {
100+
ResourceLoader resourceLoader = new DefaultResourceLoader();
101+
String styleSheetPath = manager.getStyleSheet();
102+
Resource styleSheetResource = resourceLoader.getResource("classpath:" + styleSheetPath);
103+
htmlTransformerSource = styleSheetResource.getContentAsByteArray();
104+
htmlTransformerFactory = TransformerFactory.newInstance();
105+
// run for potential exceptions only as a sanity check on the XSLT:
106+
htmlTransformerFactory.newTransformer(
107+
new StreamSource(new ByteArrayInputStream(htmlTransformerSource)));
108+
}
109+
} catch (Exception e) {
110+
htmlTransformerFactory = null;
111+
htmlTransformerSource = null;
112+
log.warn("Could not set up HTML transformer for OAI-PMH app: " + e.toString());
113+
}
114+
}
115+
75116
@RequestMapping("")
76117
public void index(HttpServletResponse response, HttpServletRequest request) throws IOException {
77118
response.sendRedirect(request.getRequestURI() + "/");
@@ -92,7 +133,8 @@ public String indexAction(HttpServletResponse response, Model model) throws Serv
92133

93134
@RequestMapping("/{context}")
94135
public String contextAction(Model model, HttpServletRequest request, HttpServletResponse response,
95-
@PathVariable("context") String xoaiContext) throws IOException, ServletException {
136+
@PathVariable("context") String xoaiContext)
137+
throws IOException, ServletException, TransformerException {
96138
Context context = null;
97139
try {
98140
request.setCharacterEncoding("UTF-8");
@@ -109,7 +151,26 @@ public String contextAction(Model model, HttpServletRequest request, HttpServlet
109151
OutputStream out = response.getOutputStream();
110152
OAIRequestParameters parameters = new OAIRequestParameters(buildParametersMap(request));
111153

112-
response.setContentType("text/xml");
154+
boolean shouldServeAsHTML = false;
155+
List<MediaType> acceptMediaTypes = MediaType.parseMediaTypes(request.getHeader("Accept"));
156+
if (htmlTransformerFactory != null) {
157+
response.addHeader("Vary", "Accept");
158+
for (MediaType acceptMediaType : acceptMediaTypes) {
159+
if (acceptMediaType.includes(MediaType.TEXT_XML) ||
160+
acceptMediaType.includes(MediaType.APPLICATION_XML)) {
161+
break;
162+
} else if (acceptMediaType.includes(MediaType.TEXT_HTML)) {
163+
shouldServeAsHTML = true;
164+
}
165+
}
166+
}
167+
168+
if (shouldServeAsHTML) {
169+
response.setContentType("text/html");
170+
out = new ByteArrayOutputStream();
171+
} else {
172+
response.setContentType("text/xml");
173+
}
113174
response.setCharacterEncoding("UTF-8");
114175

115176
String identification = xoaiContext + parameters.requestID();
@@ -124,6 +185,15 @@ public String contextAction(Model model, HttpServletRequest request, HttpServlet
124185
dataProvider.handle(parameters, out);
125186
}
126187

188+
if (shouldServeAsHTML) {
189+
OutputStream responseOut = response.getOutputStream();
190+
Source source = new StreamSource(new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray()));
191+
Result result = new StreamResult(responseOut);
192+
Transformer htmlTransformer = htmlTransformerFactory.newTransformer(
193+
new StreamSource(new ByteArrayInputStream(htmlTransformerSource)));
194+
htmlTransformer.transform(source, result);
195+
out = responseOut;
196+
}
127197

128198
out.flush();
129199
out.close();

dspace/config/modules/oai.cfg

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ oai.enabled = true
1616
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
1717
oai.path = oai
1818

19+
# Whether or not to enable the OAI HTML interface, useful for
20+
# debugging and learning about how OAI-PMH works
21+
# For the HTML interface to work, the stylesheet must also be
22+
# configured in config/crosswalks/oai/xoai.xml
23+
# (by default dspace-oai/src/main/resources/static/style.xsl)
24+
# Support for the OAI HTML interface is turned on by default.
25+
#oai.html = true
26+
1927
# Storage: solr | database (solr is recommended)
2028
oai.storage=solr
2129

0 commit comments

Comments
 (0)