2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
18
package org.apache.solr.response;
20
import java.io.BufferedReader;
21
import java.io.CharArrayReader;
22
import java.io.CharArrayWriter;
23
import java.io.IOException;
24
import java.io.Reader;
25
import java.io.Writer;
27
import org.slf4j.Logger;
28
import org.slf4j.LoggerFactory;
30
import javax.xml.transform.Transformer;
31
import javax.xml.transform.TransformerException;
32
import javax.xml.transform.stream.StreamResult;
33
import javax.xml.transform.stream.StreamSource;
35
import org.apache.solr.core.SolrConfig;
36
import org.apache.solr.common.params.SolrParams;
37
import org.apache.solr.common.util.NamedList;
38
import org.apache.solr.common.util.XMLErrorLogger;
39
import org.apache.solr.request.SolrQueryRequest;
40
import org.apache.solr.util.xslt.TransformerProvider;
42
/** QueryResponseWriter which captures the output of the XMLWriter
43
* (in memory for now, not optimal performancewise), and applies an XSLT transform
46
public class XSLTResponseWriter implements QueryResponseWriter {
48
public static final String DEFAULT_CONTENT_TYPE = "application/xml";
49
public static final String TRANSFORM_PARAM = "tr";
50
public static final String CONTEXT_TRANSFORMER_KEY = "xsltwriter.transformer";
52
private Integer xsltCacheLifetimeSeconds = null;
53
public static final int XSLT_CACHE_DEFAULT = 60;
54
private static final String XSLT_CACHE_PARAM = "xsltCacheLifetimeSeconds";
56
private static final Logger log = LoggerFactory.getLogger(XSLTResponseWriter.class);
57
private static final XMLErrorLogger xmllog = new XMLErrorLogger(log);
59
public void init(NamedList n) {
60
final SolrParams p = SolrParams.toSolrParams(n);
61
xsltCacheLifetimeSeconds = p.getInt(XSLT_CACHE_PARAM,XSLT_CACHE_DEFAULT);
62
log.info("xsltCacheLifetimeSeconds=" + xsltCacheLifetimeSeconds);
66
public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
69
t = getTransformer(request);
70
} catch(Exception e) {
71
// TODO should our parent interface throw (IO)Exception?
72
throw new RuntimeException("getTransformer fails in getContentType",e);
75
String mediaType = t.getOutputProperty("media-type");
76
if (mediaType == null || mediaType.length()==0) {
77
// This did not happen in my tests, mediaTypeFromXslt is set to "text/xml"
78
// if the XSLT transform does not contain an xsl:output element. Not sure
79
// if this is standard behavior or if it's just my JVM/libraries
80
mediaType = DEFAULT_CONTENT_TYPE;
83
if (!mediaType.contains("charset")) {
84
String encoding = t.getOutputProperty("encoding");
85
if (encoding == null || encoding.length()==0) {
88
mediaType = mediaType + "; charset=" + encoding;
94
public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
95
final Transformer t = getTransformer(request);
97
// capture the output of the XMLWriter
98
final CharArrayWriter w = new CharArrayWriter();
99
XMLWriter.writeResponse(w,request,response);
101
// and write transformed result to our writer
102
final Reader r = new BufferedReader(new CharArrayReader(w.toCharArray()));
103
final StreamSource source = new StreamSource(r);
104
final StreamResult result = new StreamResult(writer);
106
t.transform(source, result);
107
} catch(TransformerException te) {
108
final IOException ioe = new IOException("XSLT transformation error");
114
/** Get Transformer from request context, or from TransformerProvider.
115
* This allows either getContentType(...) or write(...) to instantiate the Transformer,
116
* depending on which one is called first, then the other one reuses the same Transformer
118
protected Transformer getTransformer(SolrQueryRequest request) throws IOException {
119
final String xslt = request.getParams().get(TRANSFORM_PARAM,null);
121
throw new IOException("'" + TRANSFORM_PARAM + "' request parameter is required to use the XSLTResponseWriter");
123
// not the cleanest way to achieve this
124
SolrConfig solrConfig = request.getCore().getSolrConfig();
125
// no need to synchronize access to context, right?
126
// Nothing else happens with it at the same time
127
final Map<Object,Object> ctx = request.getContext();
128
Transformer result = (Transformer)ctx.get(CONTEXT_TRANSFORMER_KEY);
130
result = TransformerProvider.instance.getTransformer(solrConfig, xslt,xsltCacheLifetimeSeconds.intValue());
131
result.setErrorListener(xmllog);
132
ctx.put(CONTEXT_TRANSFORMER_KEY,result);