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.handler;
017:
018: import java.io.IOException;
019: import java.io.Reader;
020: import java.io.Writer;
021: import java.util.ArrayList;
022: import java.util.List;
023: import java.util.logging.Logger;
024:
025: import org.apache.commons.io.IOUtils;
026: import org.apache.solr.core.SolrCore;
027: import org.apache.solr.core.SolrException;
028: import org.apache.solr.util.ContentStream;
029: import org.apache.solr.request.SolrQueryRequest;
030: import org.apache.solr.request.SolrQueryResponse;
031: import org.apache.solr.schema.IndexSchema;
032: import org.apache.solr.schema.SchemaField;
033: import org.apache.solr.update.AddUpdateCommand;
034: import org.apache.solr.update.CommitUpdateCommand;
035: import org.apache.solr.update.DeleteUpdateCommand;
036: import org.apache.solr.update.DocumentBuilder;
037: import org.apache.solr.update.UpdateHandler;
038: import org.apache.solr.util.NamedList;
039: import org.apache.solr.util.StrUtils;
040: import org.apache.solr.util.XML;
041: import org.apache.solr.util.SimpleOrderedMap;
042: import org.xmlpull.v1.XmlPullParser;
043: import org.xmlpull.v1.XmlPullParserException;
044: import org.xmlpull.v1.XmlPullParserFactory;
045:
046: public class XmlUpdateRequestHandler extends RequestHandlerBase {
047: public static Logger log = Logger
048: .getLogger(XmlUpdateRequestHandler.class.getName());
049:
050: private XmlPullParserFactory factory;
051:
052: @Override
053: public void init(NamedList args) {
054: super .init(args);
055:
056: try {
057: factory = XmlPullParserFactory.newInstance();
058: factory.setNamespaceAware(false);
059: } catch (XmlPullParserException e) {
060: throw new RuntimeException(e);
061: }
062: }
063:
064: @Override
065: public void handleRequestBody(SolrQueryRequest req,
066: SolrQueryResponse rsp) throws Exception {
067: Iterable<ContentStream> streams = req.getContentStreams();
068: if (streams == null) {
069: if (!RequestHandlerUtils.handleCommit(req, rsp, false)) {
070: throw new SolrException(
071: SolrException.ErrorCode.BAD_REQUEST,
072: "missing content stream");
073: }
074: return;
075: }
076:
077: // Cycle through each stream
078: for (ContentStream stream : req.getContentStreams()) {
079: Reader reader = stream.getReader();
080: try {
081: NamedList out = this .update(reader);
082: // TODO -- return useful info.
083: // rsp.add( "update", out );
084: } finally {
085: IOUtils.closeQuietly(reader);
086: }
087: }
088:
089: // perhaps commit when we are done
090: RequestHandlerUtils.handleCommit(req, rsp, false);
091: }
092:
093: public NamedList update(Reader reader) throws Exception {
094: SolrCore core = SolrCore.getSolrCore();
095: IndexSchema schema = core.getSchema();
096: UpdateHandler updateHandler = core.getUpdateHandler();
097:
098: // TODO: What results should be returned?
099: SimpleOrderedMap res = new SimpleOrderedMap();
100:
101: XmlPullParser xpp = factory.newPullParser();
102: long startTime = System.currentTimeMillis();
103:
104: xpp.setInput(reader);
105: xpp.nextTag();
106:
107: String currTag = xpp.getName();
108: if ("add".equals(currTag)) {
109: log.finest("SolrCore.update(add)");
110: AddUpdateCommand cmd = new AddUpdateCommand();
111: cmd.allowDups = false; // the default
112:
113: int status = 0;
114: boolean pendingAttr = false, committedAttr = false;
115: int attrcount = xpp.getAttributeCount();
116: for (int i = 0; i < attrcount; i++) {
117: String attrName = xpp.getAttributeName(i);
118: String attrVal = xpp.getAttributeValue(i);
119: if ("allowDups".equals(attrName)) {
120: cmd.allowDups = StrUtils.parseBoolean(attrVal);
121: } else if ("overwritePending".equals(attrName)) {
122: cmd.overwritePending = StrUtils
123: .parseBoolean(attrVal);
124: pendingAttr = true;
125: } else if ("overwriteCommitted".equals(attrName)) {
126: cmd.overwriteCommitted = StrUtils
127: .parseBoolean(attrVal);
128: committedAttr = true;
129: } else {
130: log.warning("Unknown attribute id in add:"
131: + attrName);
132: }
133: }
134:
135: //set defaults for committed and pending based on allowDups value
136: if (!pendingAttr)
137: cmd.overwritePending = !cmd.allowDups;
138: if (!committedAttr)
139: cmd.overwriteCommitted = !cmd.allowDups;
140:
141: DocumentBuilder builder = new DocumentBuilder(schema);
142: SchemaField uniqueKeyField = schema.getUniqueKeyField();
143: int eventType = 0;
144: // accumulate responses
145: List<String> added = new ArrayList<String>(10);
146: while (true) {
147: // this may be our second time through the loop in the case
148: // that there are multiple docs in the add... so make sure that
149: // objects can handle that.
150:
151: cmd.indexedId = null; // reset the id for this add
152:
153: if (eventType != 0) {
154: eventType = xpp.getEventType();
155: if (eventType == XmlPullParser.END_DOCUMENT)
156: break;
157: }
158: // eventType = xpp.next();
159: eventType = xpp.nextTag();
160: if (eventType == XmlPullParser.END_TAG
161: || eventType == XmlPullParser.END_DOCUMENT)
162: break; // should match </add>
163:
164: readDoc(builder, xpp);
165: builder.endDoc();
166: cmd.doc = builder.getDoc();
167: log.finest("adding doc...");
168: updateHandler.addDoc(cmd);
169: String docId = null;
170: if (uniqueKeyField != null)
171: docId = schema.printableUniqueKey(cmd.doc);
172: added.add(docId);
173:
174: } // end while
175: // write log and result
176: StringBuilder out = new StringBuilder();
177: for (String docId : added)
178: if (docId != null)
179: out.append(docId + ",");
180: String outMsg = out.toString();
181: if (outMsg.length() > 0)
182: outMsg = outMsg.substring(0, outMsg.length() - 1);
183: log.info("added id={" + outMsg + "} in "
184: + (System.currentTimeMillis() - startTime) + "ms");
185:
186: // Add output
187: res.add("added", outMsg);
188: } // end add
189:
190: else if ("commit".equals(currTag) || "optimize".equals(currTag)) {
191: log.finest("parsing " + currTag);
192:
193: CommitUpdateCommand cmd = new CommitUpdateCommand(
194: "optimize".equals(currTag));
195:
196: boolean sawWaitSearcher = false, sawWaitFlush = false;
197: int attrcount = xpp.getAttributeCount();
198: for (int i = 0; i < attrcount; i++) {
199: String attrName = xpp.getAttributeName(i);
200: String attrVal = xpp.getAttributeValue(i);
201: if ("waitFlush".equals(attrName)) {
202: cmd.waitFlush = StrUtils.parseBoolean(attrVal);
203: sawWaitFlush = true;
204: } else if ("waitSearcher".equals(attrName)) {
205: cmd.waitSearcher = StrUtils.parseBoolean(attrVal);
206: sawWaitSearcher = true;
207: } else {
208: log.warning("unexpected attribute commit/@"
209: + attrName);
210: }
211: }
212:
213: // If waitFlush is specified and waitSearcher wasn't, then
214: // clear waitSearcher.
215: if (sawWaitFlush && !sawWaitSearcher) {
216: cmd.waitSearcher = false;
217: }
218:
219: updateHandler.commit(cmd);
220: if ("optimize".equals(currTag)) {
221: log.info("optimize 0 "
222: + (System.currentTimeMillis() - startTime));
223: } else {
224: log.info("commit 0 "
225: + (System.currentTimeMillis() - startTime));
226: }
227: while (true) {
228: int eventType = xpp.nextTag();
229: if (eventType == XmlPullParser.END_TAG)
230: break; // match </commit>
231: }
232:
233: // add debug output
234: res.add(cmd.optimize ? "optimize" : "commit", "");
235: } // end commit
236:
237: else if ("delete".equals(currTag)) {
238: log.finest("parsing delete");
239:
240: DeleteUpdateCommand cmd = new DeleteUpdateCommand();
241: cmd.fromPending = true;
242: cmd.fromCommitted = true;
243: int attrcount = xpp.getAttributeCount();
244: for (int i = 0; i < attrcount; i++) {
245: String attrName = xpp.getAttributeName(i);
246: String attrVal = xpp.getAttributeValue(i);
247: if ("fromPending".equals(attrName)) {
248: cmd.fromPending = StrUtils.parseBoolean(attrVal);
249: } else if ("fromCommitted".equals(attrName)) {
250: cmd.fromCommitted = StrUtils.parseBoolean(attrVal);
251: } else {
252: log.warning("unexpected attribute delete/@"
253: + attrName);
254: }
255: }
256:
257: int eventType = xpp.nextTag();
258: currTag = xpp.getName();
259: String val = xpp.nextText();
260:
261: if ("id".equals(currTag)) {
262: cmd.id = val;
263: updateHandler.delete(cmd);
264: log.info("delete(id " + val + ") 0 "
265: + (System.currentTimeMillis() - startTime));
266: } else if ("query".equals(currTag)) {
267: cmd.query = val;
268: updateHandler.deleteByQuery(cmd);
269: log.info("deleteByQuery(query " + val + ") 0 "
270: + (System.currentTimeMillis() - startTime));
271: } else {
272: log.warning("unexpected XML tag /delete/" + currTag);
273: throw new SolrException(
274: SolrException.ErrorCode.BAD_REQUEST,
275: "unexpected XML tag /delete/" + currTag);
276: }
277:
278: res.add("delete", "");
279:
280: while (xpp.nextTag() != XmlPullParser.END_TAG)
281: ;
282: } // end delete
283: return res;
284: }
285:
286: private void readDoc(DocumentBuilder builder, XmlPullParser xpp)
287: throws IOException, XmlPullParserException {
288: // xpp should be at <doc> at this point
289:
290: builder.startDoc();
291:
292: int attrcount = xpp.getAttributeCount();
293: float docBoost = 1.0f;
294:
295: for (int i = 0; i < attrcount; i++) {
296: String attrName = xpp.getAttributeName(i);
297: String attrVal = xpp.getAttributeValue(i);
298: if ("boost".equals(attrName)) {
299: docBoost = Float.parseFloat(attrVal);
300: builder.setBoost(docBoost);
301: } else {
302: log.warning("Unknown attribute doc/@" + attrName);
303: }
304: }
305: if (docBoost != 1.0f)
306: builder.setBoost(docBoost);
307:
308: // while (findNextTag(xpp,"field") != XmlPullParser.END_DOCUMENT) {
309:
310: while (true) {
311: int eventType = xpp.nextTag();
312: if (eventType == XmlPullParser.END_TAG)
313: break; // </doc>
314:
315: String tname = xpp.getName();
316: // System.out.println("FIELD READER AT TAG " + tname);
317:
318: if (!"field".equals(tname)) {
319: log.warning("unexpected XML tag doc/" + tname);
320: throw new SolrException(
321: SolrException.ErrorCode.BAD_REQUEST,
322: "unexpected XML tag doc/" + tname);
323: }
324:
325: //
326: // get field name and parse field attributes
327: //
328: attrcount = xpp.getAttributeCount();
329: String name = null;
330: float boost = 1.0f;
331: boolean isNull = false;
332:
333: for (int i = 0; i < attrcount; i++) {
334: String attrName = xpp.getAttributeName(i);
335: String attrVal = xpp.getAttributeValue(i);
336: if ("name".equals(attrName)) {
337: name = attrVal;
338: } else if ("boost".equals(attrName)) {
339: boost = Float.parseFloat(attrVal);
340: } else if ("null".equals(attrName)) {
341: isNull = StrUtils.parseBoolean(attrVal);
342: } else {
343: log.warning("Unknown attribute doc/field/@"
344: + attrName);
345: }
346: }
347:
348: // now get the field value
349: String val = xpp.nextText(); // todo... text event for <field></field>???
350: // need this line for isNull???
351: // Don't add fields marked as null (for now at least)
352: if (!isNull) {
353: if (boost != 1.0f) {
354: builder.addField(name, val, boost);
355: } else {
356: builder.addField(name, val);
357: }
358: }
359:
360: // do I have to do a nextTag here to read the end_tag?
361:
362: } // end field loop
363: }
364:
365: /**
366: * A Convenience method for getting back a simple XML string indicating
367: * successs or failure from an XML formated Update (from the Reader)
368: */
369: public void doLegacyUpdate(Reader input, Writer output) {
370:
371: try {
372: NamedList ignored = this .update(input);
373: output.write("<result status=\"0\"></result>");
374: } catch (Exception ex) {
375: try {
376: XML.writeXML(output, "result", SolrException.toStr(ex),
377: "status", "1");
378: } catch (Exception ee) {
379: log.severe("Error writing to output stream: " + ee);
380: }
381: }
382: }
383:
384: //////////////////////// SolrInfoMBeans methods //////////////////////
385:
386: @Override
387: public String getDescription() {
388: return "Add documents with XML";
389: }
390:
391: @Override
392: public String getVersion() {
393: return "$Revision: 542679 $";
394: }
395:
396: @Override
397: public String getSourceId() {
398: return "$Id: XmlUpdateRequestHandler.java 542679 2007-05-29 22:28:21Z ryan $";
399: }
400:
401: @Override
402: public String getSource() {
403: return "$URL: https://svn.apache.org/repos/asf/lucene/solr/branches/branch-1.2/src/java/org/apache/solr/handler/XmlUpdateRequestHandler.java $";
404: }
405: }
|