diff --git a/solr/contrib/scripting/src/java/org/apache/solr/scripting/package-info.java b/solr/contrib/scripting/src/java/org/apache/solr/scripting/package-info.java new file mode 100644 index 00000000000..c6d67f2b7d4 --- /dev/null +++ b/solr/contrib/scripting/src/java/org/apache/solr/scripting/package-info.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Classes related to applying run time scripting changes to Solr. + */ +package org.apache.solr.scripting; diff --git a/solr/core/src/java/org/apache/solr/util/xslt/TransformerProvider.java b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/TransformerProvider.java similarity index 80% rename from solr/core/src/java/org/apache/solr/util/xslt/TransformerProvider.java rename to solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/TransformerProvider.java index db18e1204b8..d11173deb8a 100644 --- a/solr/core/src/java/org/apache/solr/util/xslt/TransformerProvider.java +++ b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/TransformerProvider.java @@ -14,38 +14,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.solr.util.xslt; +package org.apache.solr.scripting.xslt; + +import static org.apache.solr.scripting.xslt.XSLTConstants.CONTEXT_TRANSFORMER_KEY; import java.io.IOException; import java.lang.invoke.MethodHandles; +import java.util.Map; import java.util.concurrent.TimeUnit; - -import org.apache.solr.common.util.TimeSource; -import org.apache.solr.util.TimeOut; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.commons.io.IOUtils; - import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; - +import org.apache.commons.io.IOUtils; import org.apache.lucene.util.ResourceLoader; -import org.apache.solr.util.SystemIdResolver; +import org.apache.solr.common.util.TimeSource; import org.apache.solr.common.util.XMLErrorLogger; import org.apache.solr.core.SolrConfig; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.util.SystemIdResolver; +import org.apache.solr.util.TimeOut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -/** Singleton that creates a Transformer for the XSLTServletFilter. +/** + * Singleton that creates a Transformer for XSLT * For now, only caches the last created Transformer, but * could evolve to use an LRU cache of Transformers. * * See http://www.javaworld.com/javaworld/jw-05-2003/jw-0502-xsl_p.html for * one possible way of improving caching. */ - -public class TransformerProvider { +class TransformerProvider { private String lastFilename; private Templates lastTemplates = null; private TimeOut cacheExpiresTimeout; @@ -63,7 +64,28 @@ public class TransformerProvider { + "for high load scenarios, unless a single XSLT transform is used" + " and xsltCacheLifetimeSeconds is set to a sufficiently high value."); } - + + /** + * Get Transformer from request context, or from TransformerProvider. This allows either + * getContentType(...) or write(...) to instantiate the Transformer, depending on which one is + * called first, then the other one reuses the same Transformer + */ + static Transformer getTransformer( + SolrQueryRequest request, String xslt, int xsltCacheLifetimeSeconds) throws IOException { + // not the cleanest way to achieve this + // no need to synchronize access to context, right? + // Nothing else happens with it at the same time + final Map ctx = request.getContext(); + Transformer result = (Transformer) ctx.get(CONTEXT_TRANSFORMER_KEY); + if (result == null) { + SolrConfig solrConfig = request.getCore().getSolrConfig(); + result = instance.getTransformer(solrConfig, xslt, xsltCacheLifetimeSeconds); + result.setErrorListener(xmllog); + ctx.put(CONTEXT_TRANSFORMER_KEY, result); + } + return result; + } + /** Return a new Transformer, possibly created from our cached Templates object * @throws IOException If there is a low-level I/O error. */ diff --git a/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTConstants.java b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTConstants.java new file mode 100644 index 00000000000..36ca62eddec --- /dev/null +++ b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTConstants.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.scripting.xslt; + +class XSLTConstants { + /** Transformer param */ + static final String TR = "tr"; + static final String CONTEXT_TRANSFORMER_KEY = "xsltwriter.transformer"; + static final int XSLT_CACHE_DEFAULT = 60; + static final String XSLT_CACHE_PARAM = "xsltCacheLifetimeSeconds"; +} diff --git a/solr/core/src/java/org/apache/solr/response/XSLTResponseWriter.java b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTResponseWriter.java similarity index 60% rename from solr/core/src/java/org/apache/solr/response/XSLTResponseWriter.java rename to solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTResponseWriter.java index 8bc91523b03..cebdb4adb30 100644 --- a/solr/core/src/java/org/apache/solr/response/XSLTResponseWriter.java +++ b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTResponseWriter.java @@ -14,7 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.solr.response; +package org.apache.solr.scripting.xslt; + +import static org.apache.solr.scripting.xslt.XSLTConstants.TR; +import static org.apache.solr.scripting.xslt.XSLTConstants.XSLT_CACHE_DEFAULT; +import static org.apache.solr.scripting.xslt.XSLTConstants.XSLT_CACHE_PARAM; import java.io.BufferedReader; import java.io.CharArrayReader; @@ -22,48 +26,36 @@ import java.io.CharArrayWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; -import java.lang.invoke.MethodHandles; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; - -import org.apache.solr.core.SolrConfig; -import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.XMLErrorLogger; import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.util.xslt.TransformerProvider; +import org.apache.solr.response.QueryResponseWriter; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.response.XMLWriter; -/** QueryResponseWriter which captures the output of the XMLWriter - * (in memory for now, not optimal performancewise), and applies an XSLT transform +/** + * Customize the format of your search results via XSL stylesheet applied to the default + * XML response format. + * + * QueryResponseWriter captures the output of the XMLWriter + * (in memory for now, not optimal performance-wise), and applies an XSLT transform * to it. */ public class XSLTResponseWriter implements QueryResponseWriter { - public static final String DEFAULT_CONTENT_TYPE = "application/xml"; - public static final String CONTEXT_TRANSFORMER_KEY = "xsltwriter.transformer"; - - private Integer xsltCacheLifetimeSeconds = null; - public static final int XSLT_CACHE_DEFAULT = 60; - private static final String XSLT_CACHE_PARAM = "xsltCacheLifetimeSeconds"; - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private static final XMLErrorLogger xmllog = new XMLErrorLogger(log); - + private Integer xsltCacheLifetimeSeconds = null; + @Override public void init(@SuppressWarnings({"rawtypes"})NamedList n) { final SolrParams p = n.toSolrParams(); - xsltCacheLifetimeSeconds = p.getInt(XSLT_CACHE_PARAM,XSLT_CACHE_DEFAULT); - log.info("xsltCacheLifetimeSeconds={}", xsltCacheLifetimeSeconds); + xsltCacheLifetimeSeconds = p.getInt(XSLT_CACHE_PARAM, XSLT_CACHE_DEFAULT); } - @Override public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { Transformer t = null; @@ -73,7 +65,7 @@ public class XSLTResponseWriter implements QueryResponseWriter { // TODO should our parent interface throw (IO)Exception? throw new RuntimeException("getTransformer fails in getContentType",e); } - + String mediaType = t.getOutputProperty("media-type"); if (mediaType == null || mediaType.length()==0) { // This did not happen in my tests, mediaTypeFromXslt is set to "text/xml" @@ -81,7 +73,7 @@ public class XSLTResponseWriter implements QueryResponseWriter { // if this is standard behavior or if it's just my JVM/libraries mediaType = DEFAULT_CONTENT_TYPE; } - + if (!mediaType.contains("charset")) { String encoding = t.getOutputProperty("encoding"); if (encoding == null || encoding.length()==0) { @@ -89,18 +81,18 @@ public class XSLTResponseWriter implements QueryResponseWriter { } mediaType = mediaType + "; charset=" + encoding; } - + return mediaType; } @Override public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException { final Transformer t = getTransformer(request); - + // capture the output of the XMLWriter final CharArrayWriter w = new CharArrayWriter(); XMLWriter.writeResponse(w,request,response); - + // and write transformed result to our writer final Reader r = new BufferedReader(new CharArrayReader(w.toCharArray())); final StreamSource source = new StreamSource(r); @@ -111,27 +103,10 @@ public class XSLTResponseWriter implements QueryResponseWriter { throw new IOException("XSLT transformation error", te); } } - - /** Get Transformer from request context, or from TransformerProvider. - * This allows either getContentType(...) or write(...) to instantiate the Transformer, - * depending on which one is called first, then the other one reuses the same Transformer - */ + protected Transformer getTransformer(SolrQueryRequest request) throws IOException { - final String xslt = request.getParams().get(CommonParams.TR,null); - if(xslt==null) { - throw new IOException("'" + CommonParams.TR + "' request parameter is required to use the XSLTResponseWriter"); - } - // not the cleanest way to achieve this - SolrConfig solrConfig = request.getCore().getSolrConfig(); - // no need to synchronize access to context, right? - // Nothing else happens with it at the same time - final Map ctx = request.getContext(); - Transformer result = (Transformer)ctx.get(CONTEXT_TRANSFORMER_KEY); - if(result==null) { - result = TransformerProvider.instance.getTransformer(solrConfig, xslt,xsltCacheLifetimeSeconds.intValue()); - result.setErrorListener(xmllog); - ctx.put(CONTEXT_TRANSFORMER_KEY,result); - } - return result; + final String xslt = request.getParams().required().get(TR); + return TransformerProvider.getTransformer(request, xslt, xsltCacheLifetimeSeconds); } + } diff --git a/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandler.java b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandler.java new file mode 100644 index 00000000000..9ea91ba6c00 --- /dev/null +++ b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandler.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.scripting.xslt; + +import static org.apache.solr.scripting.xslt.XSLTConstants.*; + +import java.util.Map; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXSource; +import com.google.common.annotations.VisibleForTesting; +import org.apache.solr.common.EmptyEntityResolver; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.ContentStream; +import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.UpdateRequestHandler; +import org.apache.solr.handler.loader.XMLLoader; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.update.processor.UpdateRequestProcessor; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; + +/** + * Send XML formatted documents to Solr, transforming them from the original XML + * format to the Solr XML format using an XSLT stylesheet via the 'tr' parameter. + */ +public class XSLTUpdateRequestHandler extends UpdateRequestHandler { + + @Override + public void init(@SuppressWarnings({"rawtypes"})NamedList args) { + super.init(args); + setAssumeContentType("application/xml"); + + SolrParams p = null; + if (args != null) { + p = args.toSolrParams(); + } + final XsltXMLLoader loader = new XsltXMLLoader().init(p); + loaders = Map.of("application/xml", loader, "text/xml", loader); + } + + @VisibleForTesting + static class XsltXMLLoader extends XMLLoader { + + int xsltCacheLifetimeSeconds; + + @Override + public XsltXMLLoader init(SolrParams args) { + super.init(args); + + xsltCacheLifetimeSeconds = XSLT_CACHE_DEFAULT; + if (args != null) { + xsltCacheLifetimeSeconds = args.getInt(XSLT_CACHE_PARAM, XSLT_CACHE_DEFAULT); + } + return this; + } + + @Override + public void load( + SolrQueryRequest req, + SolrQueryResponse rsp, + ContentStream stream, + UpdateRequestProcessor processor) + throws Exception { + + String tr = req.getParams().get(TR, null); + if (tr == null) { + super.load(req, rsp, stream, processor); // no XSLT; do standard processing + return; + } + + if (req.getCore().getCoreDescriptor().isConfigSetTrusted() == false) { + throw new SolrException( + SolrException.ErrorCode.UNAUTHORIZED, + "The configset for this collection was uploaded without any authentication in place," + + " and this operation is not available for collections with untrusted configsets. To use this feature, re-upload the configset" + + " after enabling authentication and authorization."); + } + + final Transformer t = TransformerProvider.getTransformer(req, tr, xsltCacheLifetimeSeconds); + final DOMResult result = new DOMResult(); + + // first step: read XML and build DOM using Transformer (this is no overhead, as XSL always + // produces + // an internal result DOM tree, we just access it directly as input for StAX): + try (var is = stream.getStream()) { + final XMLReader xmlr = saxFactory.newSAXParser().getXMLReader(); + xmlr.setErrorHandler(xmllog); + xmlr.setEntityResolver(EmptyEntityResolver.SAX_INSTANCE); + final InputSource isrc = new InputSource(is); + isrc.setEncoding(ContentStreamBase.getCharsetFromContentType(stream.getContentType())); + final SAXSource source = new SAXSource(xmlr, isrc); + t.transform(source, result); + } catch (TransformerException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e.toString(), e); + } + + // second step: feed the intermediate DOM tree into StAX parser: + XMLStreamReader parser = null; // does not implement AutoCloseable! + try { + parser = inputFactory.createXMLStreamReader(new DOMSource(result.getNode())); + this.processUpdate(req, processor, parser); + } catch (XMLStreamException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e.toString(), e); + } finally { + if (parser != null) parser.close(); + } + } + + } + + //////////////////////// SolrInfoMBeans methods ////////////////////// + + @Override + public String getDescription() { + return "Add documents with XML, transforming with XSLT first"; + } +} diff --git a/solr/core/src/java/org/apache/solr/util/xslt/package-info.java b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/package-info.java similarity index 88% rename from solr/core/src/java/org/apache/solr/util/xslt/package-info.java rename to solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/package-info.java index 488386cfa75..e12c7650af6 100644 --- a/solr/core/src/java/org/apache/solr/util/xslt/package-info.java +++ b/solr/contrib/scripting/src/java/org/apache/solr/scripting/xslt/package-info.java @@ -16,8 +16,8 @@ */ /** - * XSLT related utilities (deprecated package, do not add new classes) + * XSLT related classes. */ -package org.apache.solr.util.xslt; +package org.apache.solr.scripting.xslt; diff --git a/solr/contrib/scripting/src/test-files/mailing_lists.pdf b/solr/contrib/scripting/src/test-files/mailing_lists.pdf new file mode 100644 index 00000000000..33b819f0649 --- /dev/null +++ b/solr/contrib/scripting/src/test-files/mailing_lists.pdf @@ -0,0 +1,382 @@ +%PDF-1.3 +%ª«¬­ +4 0 obj +<< /Type /Info +/Producer (FOP 0.20.5) >> +endobj +5 0 obj +<< /Length 425 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$BYuAO_'ZTnF'lQbNnGsdiUK'C#3dAWc3lI>k\P#:a@Qja<+itJa;R]7&ni\$9pOi?T._;3m?jT+q7>,P^70oB=!nr]%k%\U^KVqaF4*Z`$VJ7Gs`T5OO`(tY]Q1`-5*m;!--h%?*_0SbIU\BV=OFg<#%YcH_YI$(sDCIJts'M2*drjRrJE!OM7HP!^-&EW>B\:RYFnaY.m[$s5f"XG0>^fduHe6/++D0fY3@AWR@HYabmQ5jDQ.c0>I.uQX&(lA@VLm_s_9XnBh7%"*/%^]AO3eTI!BTo'pF?%''A*PDU*NW%d`2@p'@:D@U??4PP08m[K4N,8,(e`N+\7n+a>ac%q#,D8DRQ*3l]MS>'gn3lWNGmRAtQ7n]eDnLPrD!?DEdB/hNarb_7$B7U-H7!['nXLkV_no5AHq`>6~> +endstream +endobj +6 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 5 0 R +/Annots 7 0 R +>> +endobj +7 0 obj +[ +8 0 R +10 0 R +12 0 R +] +endobj +8 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 102.0 559.666 137.324 547.666 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 9 0 R +/H /I +>> +endobj +10 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 102.0 541.466 164.648 529.466 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 11 0 R +/H /I +>> +endobj +12 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 102.0 523.266 154.016 511.266 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 13 0 R +/H /I +>> +endobj +14 0 obj +<< /Length 2197 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb"/)9lo&I&A@C2n5a2!7YkueV^?,ABrC@*[F.^sK-J\u-^*\VZ9A3]?'#&sU^3,]d[;/F9HjMs^A"j:!rHNC?7rs!0)f1q`$?\lOaRt/g/f.>-Am[t'`RUrGL7Uk8K90.i-up;qeIYfjWZ2&ki:[3`TuXFj]`a&Hbo8r&P(RZ+M_>&eY.T4jXOI%UHbq1GnF>g$KgW%R24nBkc\[qA$(koU$isG(W7`PE,nMam;U4(ZC8,Ca!_P2VYf>\V0gK0g;-.E[Y(&s=+&g6ms""'Ip>0b/D!>a&PX9eo_tuueR:b=r@6Q5LM],XbK;&L$0WubNX9c"=FM$543G_>rAQ_%2/dW<)/"U1&]l:AZ&\Mif8sF`r5>b<$lqK"2t]maZ*oDb!^$Zn6OC'%XkI];&*rkLP1BMGI@$,0fK(=gC-3q7n7d4EQ4DepBc'^Q^A%e?19a(`S*FHTN*RNjP&P%2`6%jpOU\DBUN)cnMYa3PQ!sYETiGJi'q>>m*e;[,.1l\rZo3K;>$K"a1:s3pU>o+:'7fND!+6GV@2G;qf`\`=J#WkOjSke<1f>VfbcUtXM"1jGN:@Ptec8Mc-hmS5S>q/nAY%[4%7BCI![NA:We(41]ld_`pU80;+e`1DbG.RQ:'#GQJAL2![aIWY'A*Y_>mF7>2S0IWM%nLg3%%;7r5=;3!7]05r?Ft-6I]9n9C\fUUF6R\9bPEVSutd9LFTpaoaP7Iuus-S#S.3;sVu-*T/:&2Ld]&g0oHoo`TmR'b]ps6hq9s&f+6_5c(k"m96-f:YA!:)K:q+(Hl=t`:+"<lQm6B=K&/r/Ep6Y]EG.T/34(fT0=6_m5PA-7PVo:"r)W'.mX>1A8Yg9kfa?"Qp+ta7Hb$FM`*OP^>3Sg)P[?jIXd]i]"h)Tdjnm[6@=kmEBkP1/K[bg`"7U:BWk^=!+3\ANTnN75*Rh_<-UA*!&rr#KW/7EXkeJU9GF5RA,#kqJ5aC9Ra5,PsiI`uF23/B"nkPHe2Q;B@pBXGM-i;<'oOM,dc3'qL)Ne,OV2.*f^Bt;0P#roPn?h]@-63,-9lQSF!dic13Ag\_]m=7Llb\*&C+>\+o6)Y,C._?+X1Qok%j>f[#T!,CD2T4cL'.Nb_Vit&M]!j7j6LHB.g9AQre&be$gJhbAg68kDJf@XZ7'2791RD*qAP]u")(lEjX)\-#O$aK(E]jq*3XbL:3q:o&9gcZLl?:E-l'-dHf;;_hhH3m/Q3]9jJRn>Z8]1Gt6PAVJ[r2gsg=4$!6I$RQ@Y6;H(U>,LWdW>Z5iTYZ'tAcSfoN,U=/fIoA::l8X^fXIa4m3-]9$Zc\E0H^!pmfeMjW3#p1J)pbH^VZML"NZ$U,Yg;f[AVrZRhlRCC[)D*>K0IRWR98A=<>dPSd)@Ec)OXGjK01hM%!FhVR[I<5Va3V,I"YuQZb-,XEM!Gk_-r<9T0W#M!!;RX!]MtBdJ0ah'FCoNF1r"gmU>Rb4aE:Z'I)d-f_1:B0gfmnM?K9ljY>R%*Fc9oYiohHndi(!dK+]ElID:'g:PKq6fKKHdO>bmG-2]ZmVcqs+ef-EWR(1Da)F&CoL[['3)UZ^!fo+Ua2NSC7m5oIXlLoF)+cWUr/MaMP@shSN$gD*jB=:/ru]MF>3-m'j6_-'>(Uq'PN4Fl*XC8ABmg\b`kmI@<0Sh)bkNopK]E6S7,V*o!<)infW?).%mtC2S8!kqh$BpiWu=4)>.Wm+Mt.YPC"ZlO^Ge*Y5)8QlX2 +endstream +endobj +15 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 14 0 R +/Annots 16 0 R +>> +endobj +16 0 obj +[ +17 0 R +18 0 R +19 0 R +20 0 R +21 0 R +22 0 R +23 0 R +24 0 R +25 0 R +26 0 R +27 0 R +28 0 R +29 0 R +] +endobj +17 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 232.344 608.466 372.012 596.466 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:solr-user@lucene.apache.org) +/S /URI >> +/H /I +>> +endobj +18 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.0 591.266 189.336 579.266 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:solr-user-subscribe@lucene.apache.org) +/S /URI >> +/H /I +>> +endobj +19 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.0 578.066 215.988 566.066 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:solr-user-unsubscribe@lucene.apache.org) +/S /URI >> +/H /I +>> +endobj +20 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.0 564.866 197.316 552.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://mail-archives.apache.org/mod_mbox/lucene-solr-user/) +/S /URI >> +/H /I +>> +endobj +21 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 453.924 564.866 475.26 552.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://wiki.apache.org/solr/SolrResources) +/S /URI >> +/H /I +>> +endobj +22 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 259.668 441.722 396.672 429.722 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:solr-dev@lucene.apache.org) +/S /URI >> +/H /I +>> +endobj +23 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.0 424.522 189.336 412.522 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:solr-dev-subscribe@lucene.apache.org) +/S /URI >> +/H /I +>> +endobj +24 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.0 411.322 215.988 399.322 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:solr-dev-unsubscribe@lucene.apache.org) +/S /URI >> +/H /I +>> +endobj +25 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.0 398.122 197.316 386.122 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://mail-archives.apache.org/mod_mbox/lucene-solr-dev/) +/S /URI >> +/H /I +>> +endobj +26 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 453.924 398.122 475.26 386.122 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://wiki.apache.org/solr/SolrResources) +/S /URI >> +/H /I +>> +endobj +27 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 294.624 296.178 403.284 284.178 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (version_control.html) +/S /URI >> +/H /I +>> +endobj +28 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.0 265.778 189.336 253.778 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:solr-commits-subscribe@lucene.apache.org) +/S /URI >> +/H /I +>> +endobj +29 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.0 252.578 215.988 240.578 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:solr-commits-unsubscribe@lucene.apache.org) +/S /URI >> +/H /I +>> +endobj +31 0 obj +<< + /Title (\376\377\0\61\0\40\0\125\0\163\0\145\0\162\0\163) + /Parent 30 0 R + /Next 32 0 R + /A 9 0 R +>> endobj +32 0 obj +<< + /Title (\376\377\0\62\0\40\0\104\0\145\0\166\0\145\0\154\0\157\0\160\0\145\0\162\0\163) + /Parent 30 0 R + /Prev 31 0 R + /Next 33 0 R + /A 11 0 R +>> endobj +33 0 obj +<< + /Title (\376\377\0\63\0\40\0\103\0\157\0\155\0\155\0\151\0\164\0\163) + /Parent 30 0 R + /Prev 32 0 R + /A 13 0 R +>> endobj +34 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F3 +/BaseFont /Helvetica-Bold +/Encoding /WinAnsiEncoding >> +endobj +35 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F5 +/BaseFont /Times-Roman +/Encoding /WinAnsiEncoding >> +endobj +36 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F1 +/BaseFont /Helvetica +/Encoding /WinAnsiEncoding >> +endobj +37 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F2 +/BaseFont /Helvetica-Oblique +/Encoding /WinAnsiEncoding >> +endobj +38 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F7 +/BaseFont /Times-Bold +/Encoding /WinAnsiEncoding >> +endobj +1 0 obj +<< /Type /Pages +/Count 2 +/Kids [6 0 R 15 0 R ] >> +endobj +2 0 obj +<< /Type /Catalog +/Pages 1 0 R + /Outlines 30 0 R + /PageMode /UseOutlines + >> +endobj +3 0 obj +<< +/Font << /F3 34 0 R /F5 35 0 R /F1 36 0 R /F2 37 0 R /F7 38 0 R >> +/ProcSet [ /PDF /ImageC /Text ] >> +endobj +9 0 obj +<< +/S /GoTo +/D [15 0 R /XYZ 85.0 659.0 null] +>> +endobj +11 0 obj +<< +/S /GoTo +/D [15 0 R /XYZ 85.0 492.256 null] +>> +endobj +13 0 obj +<< +/S /GoTo +/D [15 0 R /XYZ 85.0 325.512 null] +>> +endobj +30 0 obj +<< + /First 31 0 R + /Last 33 0 R +>> endobj +xref +0 39 +0000000000 65535 f +0000007198 00000 n +0000007263 00000 n +0000007355 00000 n +0000000015 00000 n +0000000071 00000 n +0000000587 00000 n +0000000707 00000 n +0000000746 00000 n +0000007478 00000 n +0000000881 00000 n +0000007541 00000 n +0000001018 00000 n +0000007607 00000 n +0000001155 00000 n +0000003445 00000 n +0000003568 00000 n +0000003679 00000 n +0000003867 00000 n +0000004063 00000 n +0000004261 00000 n +0000004471 00000 n +0000004665 00000 n +0000004852 00000 n +0000005047 00000 n +0000005244 00000 n +0000005453 00000 n +0000005647 00000 n +0000005821 00000 n +0000006020 00000 n +0000007673 00000 n +0000006221 00000 n +0000006342 00000 n +0000006508 00000 n +0000006642 00000 n +0000006755 00000 n +0000006865 00000 n +0000006973 00000 n +0000007089 00000 n +trailer +<< +/Size 39 +/Root 2 0 R +/Info 4 0 R +>> +startxref +7724 +%%EOF diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/addfields.updateprocessor.js b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/addfields.updateprocessor.js similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/addfields.updateprocessor.js rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/addfields.updateprocessor.js diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/bad-solrconfig-bogus-scriptengine-name.xml b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/bad-solrconfig-bogus-scriptengine-name.xml similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/bad-solrconfig-bogus-scriptengine-name.xml rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/bad-solrconfig-bogus-scriptengine-name.xml diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/bad-solrconfig-invalid-scriptfile.xml b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/bad-solrconfig-invalid-scriptfile.xml similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/bad-solrconfig-invalid-scriptfile.xml rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/bad-solrconfig-invalid-scriptfile.xml diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/bad-solrconfig-missing-scriptfile.xml b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/bad-solrconfig-missing-scriptfile.xml similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/bad-solrconfig-missing-scriptfile.xml rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/bad-solrconfig-missing-scriptfile.xml diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/conditional.updateprocessor.js b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/conditional.updateprocessor.js similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/conditional.updateprocessor.js rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/conditional.updateprocessor.js diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/cross-compatible.js b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/cross-compatible.js similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/cross-compatible.js rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/cross-compatible.js diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/evil.js b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/evil.js similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/evil.js rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/evil.js diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/invalid.script.xml b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/invalid.script.xml similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/invalid.script.xml rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/invalid.script.xml diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/missing.functions.updateprocessor.js b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/missing.functions.updateprocessor.js similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/missing.functions.updateprocessor.js rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/missing.functions.updateprocessor.js diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/missleading.extension.updateprocessor.js.txt b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/missleading.extension.updateprocessor.js.txt similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/missleading.extension.updateprocessor.js.txt rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/missleading.extension.updateprocessor.js.txt diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/schema.xml b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/schema.xml similarity index 91% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/schema.xml rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/schema.xml index 140f12bd41a..4eb1e20c27f 100644 --- a/solr/contrib/scripting/src/test-files/solr/collection1/conf/schema.xml +++ b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/schema.xml @@ -29,6 +29,7 @@ + + + + + + + ${solr.data.dir:} + + + + + ${tests.luceneMatchVersion:LATEST} + + + + + + ${solr.commitwithin.softcommit:true} + + + + + + + + + + 5 + + + + + explicit + true + text + + + + + + + text + + + + diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/stateless-solrconfig-script-updateprocessor.xml b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/stateless-solrconfig-script-updateprocessor.xml similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/stateless-solrconfig-script-updateprocessor.xml rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/stateless-solrconfig-script-updateprocessor.xml diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/throw.error.on.add.updateprocessor.js b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/throw.error.on.add.updateprocessor.js similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/throw.error.on.add.updateprocessor.js rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/throw.error.on.add.updateprocessor.js diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/trivial.updateprocessor0.js b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/trivial.updateprocessor0.js similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/trivial.updateprocessor0.js rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/trivial.updateprocessor0.js diff --git a/solr/contrib/scripting/src/test-files/solr/collection1/conf/trivial.updateprocessor1.js b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/trivial.updateprocessor1.js similarity index 100% rename from solr/contrib/scripting/src/test-files/solr/collection1/conf/trivial.updateprocessor1.js rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/trivial.updateprocessor1.js diff --git a/solr/core/src/test-files/solr/collection1/conf/xslt/dummy-using-include.xsl b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/xslt/dummy-using-include.xsl similarity index 100% rename from solr/core/src/test-files/solr/collection1/conf/xslt/dummy-using-include.xsl rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/xslt/dummy-using-include.xsl diff --git a/solr/core/src/test-files/solr/collection1/conf/xslt/dummy.xsl b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/xslt/dummy.xsl similarity index 100% rename from solr/core/src/test-files/solr/collection1/conf/xslt/dummy.xsl rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/xslt/dummy.xsl diff --git a/solr/core/src/test-files/solr/collection1/conf/xslt/xsl-update-handler-test.xsl b/solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/xslt/xsl-update-handler-test.xsl similarity index 100% rename from solr/core/src/test-files/solr/collection1/conf/xslt/xsl-update-handler-test.xsl rename to solr/contrib/scripting/src/test-files/scripting/solr/collection1/conf/xslt/xsl-update-handler-test.xsl diff --git a/solr/contrib/scripting/src/test/org/apache/solr/scripting/update/ScriptUpdateProcessorFactoryTest.java b/solr/contrib/scripting/src/test/org/apache/solr/scripting/update/ScriptUpdateProcessorFactoryTest.java index 0a53db04393..6f2b3c15bb2 100644 --- a/solr/contrib/scripting/src/test/org/apache/solr/scripting/update/ScriptUpdateProcessorFactoryTest.java +++ b/solr/contrib/scripting/src/test/org/apache/solr/scripting/update/ScriptUpdateProcessorFactoryTest.java @@ -40,7 +40,7 @@ public class ScriptUpdateProcessorFactoryTest extends UpdateProcessorTestBase { @BeforeClass public static void beforeClass() throws Exception { Assume.assumeNotNull((new ScriptEngineManager()).getEngineByExtension("js")); - initCore("solrconfig-script-updateprocessor.xml", "schema.xml"); + initCore("solrconfig-script-updateprocessor.xml", "schema.xml", getFile("scripting/solr").getAbsolutePath()); } /** diff --git a/solr/contrib/scripting/src/test/org/apache/solr/scripting/update/TestBadScriptingUpdateProcessorConfig.java b/solr/contrib/scripting/src/test/org/apache/solr/scripting/update/TestBadScriptingUpdateProcessorConfig.java index 9d64e8d1422..34be46871a2 100644 --- a/solr/contrib/scripting/src/test/org/apache/solr/scripting/update/TestBadScriptingUpdateProcessorConfig.java +++ b/solr/contrib/scripting/src/test/org/apache/solr/scripting/update/TestBadScriptingUpdateProcessorConfig.java @@ -16,12 +16,16 @@ */ package org.apache.solr.scripting.update; +import java.util.Map; +import java.util.regex.Pattern; + import javax.script.ScriptEngineManager; -import org.apache.solr.core.AbstractBadConfigTestBase; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.core.CoreContainer; import org.junit.Assume; -public class TestBadScriptingUpdateProcessorConfig extends AbstractBadConfigTestBase { +public class TestBadScriptingUpdateProcessorConfig extends SolrTestCaseJ4 { public void testBogusScriptEngine() throws Exception { @@ -29,21 +33,67 @@ public class TestBadScriptingUpdateProcessorConfig extends AbstractBadConfigTest Assume.assumeTrue(null == (new ScriptEngineManager()).getEngineByName("giberish")); assertConfigs("bad-solrconfig-bogus-scriptengine-name.xml", - "schema.xml","giberish"); + "schema.xml",getFile("scripting/solr/collection1").getParent(),"giberish"); } public void testMissingScriptFile() throws Exception { // sanity check Assume.assumeNotNull((new ScriptEngineManager()).getEngineByExtension("js")); assertConfigs("bad-solrconfig-missing-scriptfile.xml", - "schema.xml","a-file-name-that-does-not-exist.js"); + "schema.xml",getFile("scripting/solr/collection1").getParent(),"a-file-name-that-does-not-exist.js"); } public void testInvalidScriptFile() throws Exception { // sanity check Assume.assumeNotNull((new ScriptEngineManager()).getEngineByName("javascript")); assertConfigs("bad-solrconfig-invalid-scriptfile.xml", - "schema.xml","invalid.script.xml"); + "schema.xml",getFile("scripting/solr/collection1").getParent(),"invalid.script.xml"); + } + + /** + * Given a solrconfig.xml file name, a schema file name, a solr home directory, + * and an expected errString, asserts that initializing a core with these + * files causes an error matching the specified errString ot be thrown. + */ + protected final void assertConfigs(final String solrconfigFile, + final String schemaFile, + final String solrHome, + final String errString) + throws Exception { + + ignoreException(Pattern.quote(errString)); + try { + + if (null == solrHome) { + initCore( solrconfigFile, schemaFile ); + } else { + initCore( solrconfigFile, schemaFile, solrHome ); + } + + CoreContainer cc = h.getCoreContainer(); + for (Map.Entry entry : cc.getCoreInitFailures().entrySet()) { + if (matches(entry.getValue().exception, errString)) + return; + } + } + catch (Exception e) { + if (matches(e, errString)) + return; + throw e; + } + finally { + deleteCore(); + resetExceptionIgnores(); + } + fail("Did not encounter any exception from: " + solrconfigFile + " using " + schemaFile); } + private static boolean matches(Exception e, String errString) { + for (Throwable t = e; t != null; t = t.getCause()) { + if (t.getMessage() != null && -1 != t.getMessage().indexOf(errString)) + return true; + } + return false; + } + } diff --git a/solr/contrib/scripting/src/test/org/apache/solr/scripting/xslt/XSLTOutputWriterTest.java b/solr/contrib/scripting/src/test/org/apache/solr/scripting/xslt/XSLTOutputWriterTest.java new file mode 100644 index 00000000000..37925642c74 --- /dev/null +++ b/solr/contrib/scripting/src/test/org/apache/solr/scripting/xslt/XSLTOutputWriterTest.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.scripting.xslt; + +import org.apache.solr.SolrTestCaseJ4; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Tests the ability to configure multiple query output writers, and select those at query time. This test + * is specific to the XSLT writer, which isn't part of the core. + * + * See the related unit test OutputWriterTest. + * + */ +public class XSLTOutputWriterTest extends SolrTestCaseJ4 { + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig.xml", "schema.xml", getFile("scripting/solr").getAbsolutePath()); + } + + @Test + public void testTrivialXsltWriter() throws Exception { + lrf.args.put("wt", "xslt"); + lrf.args.put("tr", "dummy.xsl"); + String out = h.query(req("*:*")); + assertTrue(out.contains("DUMMY")); + } + + @Test + public void testTrivialXsltWriterInclude() throws Exception { + lrf.args.put("wt", "xslt"); + lrf.args.put("tr", "dummy-using-include.xsl"); + String out = h.query(req("*:*")); + assertTrue(out.contains("DUMMY")); + } + +} diff --git a/solr/core/src/test/org/apache/solr/handler/XsltUpdateRequestHandlerTest.java b/solr/contrib/scripting/src/test/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandlerTest.java similarity index 82% rename from solr/core/src/test/org/apache/solr/handler/XsltUpdateRequestHandlerTest.java rename to solr/contrib/scripting/src/test/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandlerTest.java index 8e3f4199f85..78ea2eca135 100644 --- a/solr/core/src/test/org/apache/solr/handler/XsltUpdateRequestHandlerTest.java +++ b/solr/contrib/scripting/src/test/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandlerTest.java @@ -14,24 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.solr.handler; +package org.apache.solr.scripting.xslt; -import org.apache.solr.SolrTestCaseJ4; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; - -import org.apache.solr.common.params.CommonParams; +import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.params.MapSolrParams; import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrCore; -import org.apache.solr.handler.loader.XMLLoader; +import org.apache.solr.handler.loader.ContentStreamLoader; import org.apache.solr.request.LocalSolrQueryRequest; -import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.update.AddUpdateCommand; import org.apache.solr.update.processor.BufferingRequestProcessor; @@ -39,11 +37,17 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -public class XsltUpdateRequestHandlerTest extends SolrTestCaseJ4 { - +/** + *

+ * This tests the XSLTUpdateRequestHandler ability to work with XSLT stylesheet and xml content. + *

+*/ +public class XSLTUpdateRequestHandlerTest extends SolrTestCaseJ4 { + + @BeforeClass public static void beforeTests() throws Exception { - initCore("solrconfig.xml","schema.xml"); + initCore("solrconfig.xml", "schema.xml", getFile("scripting/solr").getAbsolutePath()); } @Override @@ -57,7 +61,7 @@ public class XsltUpdateRequestHandlerTest extends SolrTestCaseJ4 { @Test public void testUpdate() throws Exception { - String xml = + String xml = "" + " " + " " + @@ -69,15 +73,16 @@ public class XsltUpdateRequestHandlerTest extends SolrTestCaseJ4 { ""; Map args = new HashMap<>(); - args.put(CommonParams.TR, "xsl-update-handler-test.xsl"); - + args.put("tr", "xsl-update-handler-test.xsl"); + SolrCore core = h.getCore(); LocalSolrQueryRequest req = new LocalSolrQueryRequest( core, new MapSolrParams( args) ); ArrayList streams = new ArrayList<>(); streams.add(new ContentStreamBase.StringStream(xml)); req.setContentStreams(streams); SolrQueryResponse rsp = new SolrQueryResponse(); - try (UpdateRequestHandler handler = new UpdateRequestHandler()) { + //try (UpdateRequestHandler handler = new UpdateRequestHandler()) { + try (XSLTUpdateRequestHandler handler = new XSLTUpdateRequestHandler()) { handler.init(new NamedList()); handler.handleRequestBody(req, rsp); } @@ -94,15 +99,15 @@ public class XsltUpdateRequestHandlerTest extends SolrTestCaseJ4 { , "//str[@name='id'][.='12345']" ); } - + @Test public void testEntities() throws Exception { - // use a binary file, so when it's loaded fail with XML eror: + // use a binary file, so when it's loaded fail with XML error: String file = getFile("mailing_lists.pdf").toURI().toASCIIString(); - String xml = + String xml = "" + - ""+ // but named entities should be @@ -115,15 +120,15 @@ public class XsltUpdateRequestHandlerTest extends SolrTestCaseJ4 { " " + " " + ""; - SolrQueryRequest req = req(CommonParams.TR, "xsl-update-handler-test.xsl"); + SolrQueryRequest req = req("tr", "xsl-update-handler-test.xsl"); SolrQueryResponse rsp = new SolrQueryResponse(); BufferingRequestProcessor p = new BufferingRequestProcessor(null); - XMLLoader loader = new XMLLoader().init(null); + ContentStreamLoader loader = new XSLTUpdateRequestHandler.XsltXMLLoader().init(null); loader.load(req, rsp, new ContentStreamBase.StringStream(xml), p); AddUpdateCommand add = p.addCommands.get(0); assertEquals("12345", add.solrDoc.getField("id").getFirstValue()); assertEquals("zzz", add.solrDoc.getField("foo_s").getFirstValue()); req.close(); - } + } } diff --git a/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java index cd64b55333b..969ed0fd318 100644 --- a/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/UpdateRequestHandler.java @@ -64,7 +64,7 @@ public class UpdateRequestHandler extends ContentStreamHandlerBase implements Pe // NOTE: This constant is for use with the XML tag, not the HTTP param with same name public static final String COMMIT_WITHIN = "commitWithin"; - Map loaders = null; + protected Map loaders = null; ContentStreamLoader instance = new ContentStreamLoader() { @Override @@ -168,7 +168,7 @@ public class UpdateRequestHandler extends ContentStreamHandlerBase implements Pe @Override public String getDescription() { - return "Add documents using XML (with XSLT), CSV, JSON, or javabin"; + return "Add documents using XML, CSV, JSON, or javabin."; } @Override @@ -182,6 +182,3 @@ public class UpdateRequestHandler extends ContentStreamHandlerBase implements Pe public static final String BIN_PATH = "/update/bin"; } - - - diff --git a/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java b/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java index c8eac0b31aa..f63f86d5c37 100644 --- a/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java +++ b/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java @@ -16,17 +16,9 @@ */ package org.apache.solr.handler.loader; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.stream.FactoryConfigurationError; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.sax.SAXSource; +import static org.apache.solr.common.params.CommonParams.ID; +import static org.apache.solr.common.params.CommonParams.NAME; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -37,14 +29,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; - +import javax.xml.parsers.SAXParserFactory; +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; import org.apache.solr.common.EmptyEntityResolver; import org.apache.solr.common.SolrException; -import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.SolrInputDocument; -import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.ShardParams; import org.apache.solr.common.params.SolrParams; @@ -53,7 +48,6 @@ import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.StrUtils; import org.apache.solr.common.util.XMLErrorLogger; -import org.apache.solr.core.SolrConfig; import org.apache.solr.handler.RequestHandlerUtils; import org.apache.solr.handler.UpdateRequestHandler; import org.apache.solr.request.SolrQueryRequest; @@ -63,30 +57,16 @@ import org.apache.solr.update.CommitUpdateCommand; import org.apache.solr.update.DeleteUpdateCommand; import org.apache.solr.update.RollbackUpdateCommand; import org.apache.solr.update.processor.UpdateRequestProcessor; -import org.apache.solr.util.xslt.TransformerProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; - -import static org.apache.solr.common.params.CommonParams.ID; -import static org.apache.solr.common.params.CommonParams.NAME; - public class XMLLoader extends ContentStreamLoader { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private static final AtomicBoolean WARNED_ABOUT_INDEX_TIME_BOOSTS = new AtomicBoolean(); - static final XMLErrorLogger xmllog = new XMLErrorLogger(log); - - public static final String CONTEXT_TRANSFORMER_KEY = "xsltupdater.transformer"; + protected static final XMLErrorLogger xmllog = new XMLErrorLogger(log); - private static final String XSLT_CACHE_PARAM = "xsltCacheLifetimeSeconds"; - - public static final int XSLT_CACHE_DEFAULT = 60; - - int xsltCacheLifetimeSeconds; - XMLInputFactory inputFactory; - SAXParserFactory saxFactory; + protected XMLInputFactory inputFactory; + protected SAXParserFactory saxFactory; @Override public XMLLoader init(SolrParams args) { @@ -112,12 +92,7 @@ public class XMLLoader extends ContentStreamLoader { saxFactory = SAXParserFactory.newInstance(); saxFactory.setNamespaceAware(true); // XSL needs this! EmptyEntityResolver.configureSAXParserFactory(saxFactory); - - xsltCacheLifetimeSeconds = XSLT_CACHE_DEFAULT; - if(args != null) { - xsltCacheLifetimeSeconds = args.getInt(XSLT_CACHE_PARAM,XSLT_CACHE_DEFAULT); - log.debug("xsltCacheLifetimeSeconds={}", xsltCacheLifetimeSeconds); - } + return this; } @@ -133,95 +108,35 @@ public class XMLLoader extends ContentStreamLoader { InputStream is = null; XMLStreamReader parser = null; - String tr = req.getParams().get(CommonParams.TR,null); - if(tr!=null) { - if (req.getCore().getCoreDescriptor().isConfigSetTrusted() == false) { - throw new SolrException(ErrorCode.UNAUTHORIZED, "The configset for this collection was uploaded without any authentication in place," - + " and this operation is not available for collections with untrusted configsets. To use this feature, re-upload the configset" - + " after enabling authentication and authorization."); - } - - final Transformer t = getTransformer(tr,req); - final DOMResult result = new DOMResult(); - - // first step: read XML and build DOM using Transformer (this is no overhead, as XSL always produces - // an internal result DOM tree, we just access it directly as input for StAX): - try { - is = stream.getStream(); - final InputSource isrc = new InputSource(is); - isrc.setEncoding(charset); - final XMLReader xmlr = saxFactory.newSAXParser().getXMLReader(); - xmlr.setErrorHandler(xmllog); - xmlr.setEntityResolver(EmptyEntityResolver.SAX_INSTANCE); - final SAXSource source = new SAXSource(xmlr, isrc); - t.transform(source, result); - } catch(TransformerException te) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, te.getMessage(), te); - } finally { - IOUtils.closeQuietly(is); - } - // second step: feed the intermediate DOM tree into StAX parser: - try { - parser = inputFactory.createXMLStreamReader(new DOMSource(result.getNode())); - this.processUpdate(req, processor, parser); - } catch (XMLStreamException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e.getMessage(), e); - } finally { - if (parser != null) parser.close(); - } - } // Normal XML Loader - else { - try { - is = stream.getStream(); + try { + is = stream.getStream(); + if (log.isTraceEnabled()) { + final byte[] body = IOUtils.toByteArray(is); + // TODO: The charset may be wrong, as the real charset is later + // determined by the XML parser, the content-type is only used as a hint! if (log.isTraceEnabled()) { - final byte[] body = IOUtils.toByteArray(is); - // TODO: The charset may be wrong, as the real charset is later - // determined by the XML parser, the content-type is only used as a hint! - if (log.isTraceEnabled()) { - log.trace("body: {}", new String(body, (charset == null) ? - ContentStreamBase.DEFAULT_CHARSET : charset)); - } - IOUtils.closeQuietly(is); - is = new ByteArrayInputStream(body); + log.trace("body: {}", new String(body, (charset == null) ? + ContentStreamBase.DEFAULT_CHARSET : charset)); } - parser = (charset == null) ? - inputFactory.createXMLStreamReader(is) : inputFactory.createXMLStreamReader(is, charset); - this.processUpdate(req, processor, parser); - } catch (XMLStreamException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e.getMessage(), e); - } finally { - if (parser != null) parser.close(); IOUtils.closeQuietly(is); + is = new ByteArrayInputStream(body); } + parser = (charset == null) ? + inputFactory.createXMLStreamReader(is) : inputFactory.createXMLStreamReader(is, charset); + this.processUpdate(req, processor, parser); + } catch (XMLStreamException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e.getMessage(), e); + } finally { + if (parser != null) parser.close(); + IOUtils.closeQuietly(is); } } - - - /** Get Transformer from request context, or from TransformerProvider. - * This allows either getContentType(...) or write(...) to instantiate the Transformer, - * depending on which one is called first, then the other one reuses the same Transformer - */ - Transformer getTransformer(String xslt, SolrQueryRequest request) throws IOException { - // not the cleanest way to achieve this - // no need to synchronize access to context, right? - // Nothing else happens with it at the same time - final Map ctx = request.getContext(); - Transformer result = (Transformer)ctx.get(CONTEXT_TRANSFORMER_KEY); - if(result==null) { - SolrConfig solrConfig = request.getCore().getSolrConfig(); - result = TransformerProvider.instance.getTransformer(solrConfig, xslt, xsltCacheLifetimeSeconds); - result.setErrorListener(xmllog); - ctx.put(CONTEXT_TRANSFORMER_KEY,result); - } - return result; - } - /** * @since solr 1.2 */ - void processUpdate(SolrQueryRequest req, UpdateRequestProcessor processor, XMLStreamReader parser) + protected void processUpdate(SolrQueryRequest req, UpdateRequestProcessor processor, XMLStreamReader parser) throws XMLStreamException, IOException, FactoryConfigurationError { AddUpdateCommand addCmd = null; SolrParams params = req.getParams(); diff --git a/solr/core/src/test-files/solr/collection1/conf/foo/bar.txt b/solr/core/src/test-files/solr/collection1/conf/foo/bar.txt new file mode 100644 index 00000000000..a58b94e7fbf --- /dev/null +++ b/solr/core/src/test-files/solr/collection1/conf/foo/bar.txt @@ -0,0 +1 @@ +Placeholder file to test replication of subdirs. diff --git a/solr/core/src/test-files/solr/collection1/conf/solrconfig-leader.xml b/solr/core/src/test-files/solr/collection1/conf/solrconfig-leader.xml index 374c413e282..3893a944a96 100644 --- a/solr/core/src/test-files/solr/collection1/conf/solrconfig-leader.xml +++ b/solr/core/src/test-files/solr/collection1/conf/solrconfig-leader.xml @@ -18,7 +18,7 @@ --> - ${tests.luceneMatchVersion:LATEST} + ${tests.luceneMatchVersion:LATEST} ${solr.data.dir:} @@ -35,10 +35,10 @@ commit - - schema.xml,xslt/dummy.xsl + schema.xml,foo/bar.txt diff --git a/solr/core/src/test-files/solr/crazy-path-to-config.xml b/solr/core/src/test-files/solr/crazy-path-to-config.xml index 1c6f64fd7e4..0c956579e06 100644 --- a/solr/core/src/test-files/solr/crazy-path-to-config.xml +++ b/solr/core/src/test-files/solr/crazy-path-to-config.xml @@ -48,10 +48,9 @@ subject - + - solr diff --git a/solr/core/src/test/org/apache/solr/OutputWriterTest.java b/solr/core/src/test/org/apache/solr/OutputWriterTest.java index eb144e83fa9..888480b7cd1 100644 --- a/solr/core/src/test/org/apache/solr/OutputWriterTest.java +++ b/solr/core/src/test/org/apache/solr/OutputWriterTest.java @@ -33,17 +33,17 @@ import org.junit.Test; * */ public class OutputWriterTest extends SolrTestCaseJ4 { - + /** The XML string that's output for testing purposes. */ public static final String USELESS_OUTPUT = "useless output"; - + @BeforeClass public static void beforeClass() throws Exception { initCore("solr/crazy-path-to-config.xml","solr/crazy-path-to-schema.xml"); } - - - /** + + + /** * responseHeader has changed in SOLR-59, check old and new variants, * In SOLR-2413, we removed support for the deprecated versions */ @@ -55,39 +55,21 @@ public class OutputWriterTest extends SolrTestCaseJ4 { assertQ(req("foo"), "/response/lst[@name='responseHeader']/int[@name='status'][.='0']"); lrf.args.remove("wt"); assertQ(req("foo"), "/response/lst[@name='responseHeader']/int[@name='QTime']"); - - // and explicit 2.2 works as default + + // and explicit 2.2 works as default //lrf.args.put("version", "2.2"); lrf.args.put("wt", "standard"); assertQ(req("foo"), "/response/lst[@name='responseHeader']/int[@name='status'][.='0']"); lrf.args.remove("wt"); assertQ(req("foo"), "/response/lst[@name='responseHeader']/int[@name='QTime']"); } - + @Test public void testUselessWriter() throws Exception { lrf.args.put("wt", "useless"); String out = h.query(req("foo")); assertEquals(USELESS_OUTPUT, out); } - - @Test - public void testTrivialXsltWriter() throws Exception { - lrf.args.put("wt", "xslt"); - lrf.args.put("tr", "dummy.xsl"); - String out = h.query(req("foo")); - // System.out.println(out); - assertTrue(out.contains("DUMMY")); - } - - @Test - public void testTrivialXsltWriterInclude() throws Exception { - lrf.args.put("wt", "xslt"); - lrf.args.put("tr", "dummy-using-include.xsl"); - String out = h.query(req("foo")); - // System.out.println(out); - assertTrue(out.contains("DUMMY")); - } public void testLazy() { PluginBag.PluginHolder qrw = h.getCore().getResponseWriters().getRegistry().get("useless"); @@ -98,17 +80,17 @@ public class OutputWriterTest extends SolrTestCaseJ4 { assertTrue("Should not be a lazy class", qrw.getClass() == PluginBag.PluginHolder.class); } - + //////////////////////////////////////////////////////////////////////////// /** An output writer that doesn't do anything useful. */ - + public static class UselessOutputWriter implements QueryResponseWriter { - + public UselessOutputWriter() {} @Override public void init(@SuppressWarnings({"rawtypes"})NamedList n) {} - + @Override public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException { @@ -121,5 +103,5 @@ public class OutputWriterTest extends SolrTestCaseJ4 { } } - + } diff --git a/solr/core/src/test/org/apache/solr/handler/TestReplicationHandler.java b/solr/core/src/test/org/apache/solr/handler/TestReplicationHandler.java index 1a4da88955f..6e7a994aa5a 100644 --- a/solr/core/src/test/org/apache/solr/handler/TestReplicationHandler.java +++ b/solr/core/src/test/org/apache/solr/handler/TestReplicationHandler.java @@ -587,15 +587,15 @@ public class TestReplicationHandler extends SolrTestCaseJ4 { followerJetty.stop(); - // setup an xslt dir to force subdir file replication - File leaderXsltDir = new File(leader.getConfDir() + File.separator + "xslt"); - File leaderXsl = new File(leaderXsltDir, "dummy.xsl"); - assertTrue("could not make dir " + leaderXsltDir, leaderXsltDir.mkdirs()); - assertTrue(leaderXsl.createNewFile()); + // setup an sub directory /foo/ in order to force subdir file replication + File leaderFooDir = new File(leader.getConfDir() + File.separator + "foo"); + File leaderBarFile = new File(leaderFooDir, "bar.txt"); + assertTrue("could not make dir " + leaderFooDir, leaderFooDir.mkdirs()); + assertTrue(leaderBarFile.createNewFile()); - File followerXsltDir = new File(follower.getConfDir() + File.separator + "xslt"); - File followerXsl = new File(followerXsltDir, "dummy.xsl"); - assertFalse(followerXsltDir.exists()); + File followerFooDir = new File(follower.getConfDir() + File.separator + "foo"); + File followerBarFile = new File(followerFooDir, "bar.txt"); + assertFalse(followerFooDir.exists()); followerJetty = createAndStartJetty(follower); followerClient.close(); @@ -611,8 +611,8 @@ public class TestReplicationHandler extends SolrTestCaseJ4 { SolrDocument d = ((SolrDocumentList) followerQueryRsp.get("response")).get(0); assertEquals("newname = 2000", (String) d.getFieldValue("newname")); - assertTrue(followerXsltDir.isDirectory()); - assertTrue(followerXsl.exists()); + assertTrue(followerFooDir.isDirectory()); + assertTrue(followerBarFile.exists()); checkForSingleIndex(leaderJetty); checkForSingleIndex(followerJetty, true); diff --git a/solr/core/src/test/org/apache/solr/handler/XmlUpdateRequestHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/XmlUpdateRequestHandlerTest.java index c675868a146..4478379c94c 100644 --- a/solr/core/src/test/org/apache/solr/handler/XmlUpdateRequestHandlerTest.java +++ b/solr/core/src/test/org/apache/solr/handler/XmlUpdateRequestHandlerTest.java @@ -39,6 +39,9 @@ import java.util.LinkedList; import java.util.Objects; import java.util.Queue; +/** + * Tests the UpdateRequestHandler support for XML updates. + */ public class XmlUpdateRequestHandlerTest extends SolrTestCaseJ4 { private static XMLInputFactory inputFactory; protected static UpdateRequestHandler handler; diff --git a/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml b/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml index 3c51bbe8fd1..50a8c2c9d87 100644 --- a/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml +++ b/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml @@ -828,6 +828,17 @@ + + + 5 + + - + in Solr's conf/xslt directory. Changes to xslt files are checked + every xsltCacheLifetimeSeconds. This is part of the Scripting contrib module. + + See more about this response writer at https://lucene.apache.org/solr/guide/response-writers.html#xslt-response-writer + --> + 5 diff --git a/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl b/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl index a96e1d02448..a4a0512dd0a 100644 --- a/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl +++ b/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl @@ -1,4 +1,4 @@ - @@ -30,7 +34,7 @@ - + @@ -47,7 +51,7 @@ - + diff --git a/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc b/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc index 5350fe556ca..79e6dc1150b 100644 --- a/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc +++ b/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc @@ -144,8 +144,11 @@ _(raw; not yet edited)_ * SOLR-14972: The default port of prometheus exporter has changed from 9983 to 8989, so you may need to adjust your configuration after upgrade. -* SOLR-14067: StatelessScriptUpdateProcessorFactory moved to it's own /contrib/scripting/ package instead - of shipping as part of Solr due to security concerns. Renamed to ScriptUpdateProcessorFactory for simpler name. +* SOLR-14067: StatelessScriptUpdateProcessorFactory moved to /contrib/scripting/ package instead + of shipping as part of Solr, due to security concerns. Renamed to ScriptUpdateProcessorFactory for simpler name. + +* SOLR-15121: XSLTResponseWriter moved to /contrib/scripting/ package instead + of shipping as part of Solr, due to security concerns. === Upgrade Prerequisites in Solr 9 diff --git a/solr/solr-ref-guide/src/response-writers.adoc b/solr/solr-ref-guide/src/response-writers.adoc index f472e4ba94f..4c2323ec94b 100644 --- a/solr/solr-ref-guide/src/response-writers.adoc +++ b/solr/solr-ref-guide/src/response-writers.adoc @@ -162,6 +162,10 @@ The default behavior is not to indent. The XSLT Response Writer applies an XML stylesheet to output. It can be used for tasks such as formatting results for an RSS feed. +This response writer has been added as part of the Scripting contrib module. + +Learn more about adding the `dist/solr-scripting-*.jar` file into Solr's <>. + === tr Parameter The XSLT Response Writer accepts one parameter: the `tr` parameter, which identifies the XML transformation to use. The transformation must be found in the Solr `conf/xslt` directory. @@ -170,7 +174,7 @@ The Content-Type of the response is set according to the `` statemen === XSLT Configuration -The example below, from the `sample_techproducts_configs` <> in the Solr distribution, shows how the XSLT Response Writer is configured. +The example below, from the `sample_techproducts_configs` <> in the Solr distribution, shows how the XSLT Response Writer is configured. [source,xml] ---- @@ -179,13 +183,59 @@ The example below, from the `sample_techproducts_configs` < + class="solr.scripting.xslt.XSLTResponseWriter"> 5 ---- A value of 5 for `xsltCacheLifetimeSeconds` is good for development, to see XSLT changes quickly. For production you probably want a much higher value. +=== XSLT Writer Example + +`\http://localhost:8983/solr/techproducts/select?q=ipod&fl=id,cat,name,popularity,price,score&wt=xslt&tr=example_rss.xsl` transforms the results into a RSS feed: + +[source,xml] +---- + + + Example Solr RSS 2.0 Feed + http://localhost:8983/solr + + This has been formatted by the sample "example_rss.xsl" transform - use your own XSLT to get a nicer RSS feed. + + en-us + http://localhost:8983/solr + + iPod & iPod Mini USB 2.0 Cable + + http://localhost:8983/solr/select?q=id:IW-02 + + + + + http://localhost:8983/solr/select?q=id:IW-02 + + + +---- + +The `sample_techproducts_configs` also includes `example.xsl` which generates a simplistic HTML page +and `example_atom.xsl` that outputs in the Atom format. + +`updateXml.xsl` can be used to convert the standard Solr XML output into the Solr XML add docs format! Indeed you +could round trip your data via: + +[source,bash] +---- +curl -o docs_formatted_as_solr_add.xml "http://localhost:8983/solr/techproducts/select?q=ipod&fl=id,cat,name,popularity,price,score&wt=xslt&tr=updateXml.xsl" +curl -X POST -H "Content-Type: text/xml" -d @docs_formatted_as_solr_add.xml "http://localhost:8983/solr/techproducts/update?commitWithin=1000&overwrite=true" + +---- + +Lastly, the `luke.xsl` transformation demonstrates that you can apply very sophisticated transformations: `\http://localhost:8983/solr/techproducts/admin/luke?wt=xslt&tr=luke.xsl` + + + == Binary Response Writer This is a custom binary format used by Solr for inter-node communication as well as client-server communication. SolrJ uses this as the default for indexing as well as querying. See <> for more details. @@ -310,7 +360,7 @@ This response writer has been added as part of the extraction library, and will [source,bash] ---- cp contrib/extraction/lib/*.jar server/solr-webapp/webapp/WEB-INF/lib/ -cp dist/solr-cell-6.3.0.jar server/solr-webapp/webapp/WEB-INF/lib/ +cp dist/solr-extraction-*.jar server/solr-webapp/webapp/WEB-INF/lib/ ---- Once the libraries are in place, you can add `wt=xlsx` to your request, and results will be returned as an XLSX sheet. diff --git a/solr/solr-ref-guide/src/uploading-data-with-index-handlers.adoc b/solr/solr-ref-guide/src/uploading-data-with-index-handlers.adoc index 0d0ed7bd2d7..80e1209a69c 100644 --- a/solr/solr-ref-guide/src/uploading-data-with-index-handlers.adoc +++ b/solr/solr-ref-guide/src/uploading-data-with-index-handlers.adoc @@ -220,9 +220,36 @@ The status field will be non-zero in case of failure. === Using XSLT to Transform XML Index Updates -The UpdateRequestHandler allows you to index any arbitrary XML using the `` parameter to apply an https://en.wikipedia.org/wiki/XSLT[XSL transformation]. You must have an XSLT stylesheet in the `conf/xslt` directory of your <> that can transform the incoming data to the expected `` format, and use the `tr` parameter to specify the name of that stylesheet. +The Scripting contrib module provides a separate XSLT Update Request Handler that allows you to index any arbitrary XML by using the `` parameter to apply an https://en.wikipedia.org/wiki/XSLT[XSL transformation]. You must have an XSLT stylesheet in the `conf/xslt` directory of your <> that can transform the incoming data to the expected `` format, and use the `tr` parameter to specify the name of that stylesheet. -Here is an example XSLT stylesheet: +Learn more about adding the `dist/solr-scripting-*.jar` file into Solr's <>. + +=== tr Parameter + +The XSLT Update Request Handler accepts one parameter: the `tr` parameter, which identifies the XML transformation to use. The transformation must be found in the Solr `conf/xslt` directory. + + +=== XSLT Configuration + +The example below, from the `sample_techproducts_configs` <> in the Solr distribution, shows how the XSLT Update Request Handler is configured. + +[source,xml] +---- + + + 5 + +---- + +A value of 5 for `xsltCacheLifetimeSeconds` is good for development, to see XSLT changes quickly. For production you probably want a much higher value. + +=== XSLT Update Example + +Here is the `sample_techproducts_configs/conf/xslt/updateXml.xsl` XSL file for converting standard Solr XML output to the Solr expected `` format: [source,xml] ---- @@ -266,18 +293,15 @@ Here is an example XSLT stylesheet: This stylesheet transforms Solr's XML search result format into Solr's Update XML syntax. One example usage would be to copy a Solr 1.3 index (which does not have CSV response writer) into a format which can be indexed into another Solr file (provided that all fields are stored): -[source,plain] ----- -http://localhost:8983/solr/my_collection/select?q=*:*&wt=xslt&tr=updateXml.xsl&rows=1000 ----- - -You can also use the stylesheet in `XsltUpdateRequestHandler` to transform an index when updating: - [source,bash] ---- -curl "http://localhost:8983/solr/my_collection/update?commit=true&tr=updateXml.xsl" -H "Content-Type: text/xml" --data-binary @myexporteddata.xml +curl -o standard_solr_xml_format.xml "http://localhost:8983/solr/techproducts/select?q=ipod&fl=id,cat,name,popularity,price,score&wt=xml" +curl -X POST -H "Content-Type: text/xml" -d @standard_solr_xml_format.xml "http://localhost:8983/solr/techproducts/update/xslt?commit=true&tr=updateXml.xsl" + ---- +NOTE: You can see the opposite export/import cycle using the `tr` parameter in <>. + == JSON Formatted Index Updates Solr can accept JSON that conforms to a defined structure, or can accept arbitrary JSON-formatted documents. If sending arbitrarily formatted JSON, there are some additional parameters that need to be sent with the update request, described below in the section <>. diff --git a/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java b/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java index 144622cb901..ece61e7da1f 100644 --- a/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java +++ b/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java @@ -97,9 +97,6 @@ public interface CommonParams { /** default query field */ String DF = "df"; - /** Transformer param -- used with XSLT */ - String TR = "tr"; - /** whether to include debug data for all components pieces, including doing explains*/ String DEBUG_QUERY = "debugQuery";