001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.request;
017:
018: import java.io.BufferedReader;
019: import java.io.CharArrayReader;
020: import java.io.CharArrayWriter;
021: import java.io.IOException;
022: import java.io.Reader;
023: import java.io.Writer;
024: import java.util.Map;
025: import java.util.logging.Logger;
026:
027: import javax.xml.transform.Transformer;
028: import javax.xml.transform.TransformerException;
029: import javax.xml.transform.stream.StreamResult;
030: import javax.xml.transform.stream.StreamSource;
031:
032: import org.apache.solr.core.SolrConfig;
033: import org.apache.solr.util.NamedList;
034: import org.apache.solr.util.xslt.TransformerProvider;
035:
036: /** QueryResponseWriter which captures the output of the XMLWriter
037: * (in memory for now, not optimal performancewise), and applies an XSLT transform
038: * to it.
039: */
040: public class XSLTResponseWriter implements QueryResponseWriter {
041:
042: public static final String DEFAULT_CONTENT_TYPE = "text/xml";
043: public static final String TRANSFORM_PARAM = "tr";
044: public static final String CONTEXT_TRANSFORMER_KEY = "xsltwriter.transformer";
045:
046: private Integer xsltCacheLifetimeSeconds = null;
047: public static final int XSLT_CACHE_DEFAULT = 60;
048: private static final String XSLT_CACHE_PARAM = "xsltCacheLifetimeSeconds";
049:
050: private static final Logger log = Logger
051: .getLogger(XSLTResponseWriter.class.getName());
052:
053: public void init(NamedList n) {
054: final SolrParams p = SolrParams.toSolrParams(n);
055: xsltCacheLifetimeSeconds = p.getInt(XSLT_CACHE_PARAM,
056: XSLT_CACHE_DEFAULT);
057: log
058: .info("xsltCacheLifetimeSeconds="
059: + xsltCacheLifetimeSeconds);
060: }
061:
062: public String getContentType(SolrQueryRequest request,
063: SolrQueryResponse response) {
064: Transformer t = null;
065: try {
066: t = getTransformer(request);
067: } catch (Exception e) {
068: // TODO should our parent interface throw (IO)Exception?
069: throw new RuntimeException(
070: "getTransformer fails in getContentType", e);
071: }
072:
073: final String mediaTypeFromXslt = t
074: .getOutputProperty("media-type");
075: if (mediaTypeFromXslt == null
076: || mediaTypeFromXslt.length() == 0) {
077: // This did not happen in my tests, mediaTypeFromXslt is set to "text/xml"
078: // if the XSLT transform does not contain an xsl:output element. Not sure
079: // if this is standard behavior or if it's just my JVM/libraries
080: return DEFAULT_CONTENT_TYPE;
081: }
082: return mediaTypeFromXslt;
083: }
084:
085: public void write(Writer writer, SolrQueryRequest request,
086: SolrQueryResponse response) throws IOException {
087: final Transformer t = getTransformer(request);
088:
089: // capture the output of the XMLWriter
090: final CharArrayWriter w = new CharArrayWriter();
091: XMLWriter.writeResponse(w, request, response);
092:
093: // and write transformed result to our writer
094: final Reader r = new BufferedReader(new CharArrayReader(w
095: .toCharArray()));
096: final StreamSource source = new StreamSource(r);
097: final StreamResult result = new StreamResult(writer);
098: try {
099: t.transform(source, result);
100: } catch (TransformerException te) {
101: final IOException ioe = new IOException(
102: "XSLT transformation error");
103: ioe.initCause(te);
104: throw ioe;
105: }
106: }
107:
108: /** Get Transformer from request context, or from TransformerProvider.
109: * This allows either getContentType(...) or write(...) to instantiate the Transformer,
110: * depending on which one is called first, then the other one reuses the same Transformer
111: */
112: protected Transformer getTransformer(SolrQueryRequest request)
113: throws IOException {
114: final String xslt = request.getParams().get(TRANSFORM_PARAM,
115: null);
116: if (xslt == null) {
117: throw new IOException(
118: "'"
119: + TRANSFORM_PARAM
120: + "' request parameter is required to use the XSLTResponseWriter");
121: }
122:
123: // no need to synchronize access to context, right?
124: // Nothing else happens with it at the same time
125: final Map<Object, Object> ctx = request.getContext();
126: Transformer result = (Transformer) ctx
127: .get(CONTEXT_TRANSFORMER_KEY);
128: if (result == null) {
129: result = TransformerProvider.instance.getTransformer(xslt,
130: xsltCacheLifetimeSeconds.intValue());
131: ctx.put(CONTEXT_TRANSFORMER_KEY, result);
132: }
133: return result;
134: }
135: }
|