You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by yo...@apache.org on 2006/01/26 06:40:05 UTC

svn commit: r372455 [4/11] - in /incubator/solr/trunk: ./ src/ src/apps/ src/apps/SolarTest/ src/apps/SolarTest/src/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/solr/ src/java/org/apache/solr/analysis/ src/java/org/apache/solr/core...

Added: incubator/solr/trunk/src/java/org/apache/solr/core/SolrCore.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/core/SolrCore.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/core/SolrCore.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/core/SolrCore.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,970 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.core;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.solr.request.SolrRequestHandler;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.request.SolrQueryResponse;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.update.*;
+import org.apache.solr.util.DOMUtil;
+import org.apache.solr.util.RefCounted;
+import org.apache.solr.util.StrUtils;
+import org.apache.solr.util.XML;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import javax.xml.xpath.XPathConstants;
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+
+/**
+ * @author yonik
+ * @version $Id: SolrCore.java,v 1.47 2006/01/10 05:04:44 yonik Exp $
+ */
+
+public final class SolrCore {
+  public static final String cvsId="$Id: SolrCore.java,v 1.47 2006/01/10 05:04:44 yonik Exp $";
+  public static final String cvsSource="$Source: /cvs/main/searching/solr/solarcore/src/solr/SolrCore.java,v $";
+  public static final String cvsTag="$Name:  $";
+  public static final String version="1.0";  
+
+  public static Logger log = Logger.getLogger(SolrCore.class.getName());
+
+  private final IndexSchema schema;
+  private final String index_path;
+  private final UpdateHandler updateHandler;
+
+  public static SolrIndexConfig mainIndexConfig = new SolrIndexConfig("mainIndex");
+
+  static {
+    BooleanQuery.setMaxClauseCount(SolrConfig.config.getInt("query/maxBooleanClauses",BooleanQuery.getMaxClauseCount()));
+  }
+
+
+  public static List<SolrEventListener> parseListener(String path) {
+    List<SolrEventListener> lst = new ArrayList<SolrEventListener>();
+    log.info("Searching for listeners: " +path);
+    NodeList nodes = (NodeList)SolrConfig.config.evaluate(path, XPathConstants.NODESET);
+    if (nodes!=null) {
+      for (int i=0; i<nodes.getLength(); i++) {
+        Node node = nodes.item(i);
+          String className = DOMUtil.getAttr(node,"class");
+          SolrEventListener listener = (SolrEventListener)Config.newInstance(className);
+          listener.init(DOMUtil.childNodesToNamedList(node));
+          lst.add(listener);
+          log.info("added SolrEventListener: " + listener);
+      }
+    }
+    return lst;
+  }
+
+  List<SolrEventListener> firstSearcherListeners;
+  List<SolrEventListener> newSearcherListeners;
+  private void parseListeners() {
+    firstSearcherListeners = parseListener("//listener[@event=\"firstSearcher\"]");
+    newSearcherListeners = parseListener("//listener[@event=\"newSearcher\"]");
+  }
+
+
+  public IndexSchema getSchema() { return schema; }
+  public String getDir() { return index_path; }
+
+
+  private final RequestHandlers reqHandlers = new RequestHandlers(SolrConfig.config);
+
+  public SolrRequestHandler getRequestHandler(String handlerName) {
+    return reqHandlers.get(handlerName);
+  }
+
+
+  // TODO - what about a master that not might have a searcher normally open?
+  @Deprecated
+  public int maxDoc() {
+    RefCounted<SolrIndexSearcher> holder=null;
+    int num=0;
+    try {
+      holder = getSearcher();
+      SolrIndexSearcher searcher = holder.get();
+      num = searcher.maxDoc();
+    } catch (IOException e) {
+      log(e);
+    } finally {
+      if (holder != null) holder.decref();
+    }
+     return num;
+  }
+
+
+  // gets a non-caching searcher
+  public SolrIndexSearcher newSearcher(String name) throws IOException {
+    return new SolrIndexSearcher(schema, name,getDir(),false);
+  }
+
+
+  void initIndex() {
+    try {
+      File dirFile = new File(getDir());
+      boolean indexExists = dirFile.canRead();
+
+
+      boolean removeLocks = SolrConfig.config.getBool("mainIndex/unlockOnStartup", false);
+      if (removeLocks) {
+        // to remove locks, the directory must already exist... so we create it
+        // if it didn't exist already...
+        Directory dir = FSDirectory.getDirectory(dirFile, !indexExists);
+        if (IndexReader.isLocked(dir)) {
+          log.warning("WARNING: Solr index directory '" + getDir() + "' is locked.  Unlocking...");
+          IndexReader.unlock(dir);
+        }
+      }
+
+      // Create the index if it doesn't exist. Note that indexExists was tested *before*
+      // lock removal, since that will result in the creation of the directory.
+      if(!indexExists) {
+        log.warning("Solr index directory '" + dirFile + "' doesn't exist."
+                + " Creating new index...");
+
+        SolrIndexWriter writer = new SolrIndexWriter("SolrCore.initIndex",getDir(), true, schema, mainIndexConfig);
+        writer.close();
+
+      }
+
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+
+  private UpdateHandler createUpdateHandler(String className) {
+    try {
+      Class handlerClass = Config.findClass(className);
+      java.lang.reflect.Constructor cons = handlerClass.getConstructor(new Class[]{SolrCore.class});
+      return (UpdateHandler)cons.newInstance(new Object[]{this});
+    } catch (SolrException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new SolrException(500,"Error Instantiating Update Handler "+className, e);
+    }
+  }
+
+
+  // Singleton for now...
+  private static SolrCore core;
+
+  public static SolrCore getSolrCore() {
+    synchronized (SolrCore.class) {
+      if (core==null) core = new SolrCore(null,null);
+      return core;
+    }
+  }
+
+
+  public SolrCore(String index_path, IndexSchema schema) {
+    synchronized (SolrCore.class) {
+      // this is for backward compatibility (and also the reason
+      // the sync block is needed)
+      core = this;   // set singleton
+       try {
+      if (index_path==null) {
+        index_path=SolrConfig.config.get("indexDir","index");
+      }
+
+      log.info("Opening new SolrCore at " + index_path);
+
+      if (schema==null) {
+        schema = new IndexSchema("schema.xml");
+      }
+
+      this.schema = schema;
+      this.index_path = index_path;
+
+      parseListeners();
+
+      initIndex();
+
+      try {
+        // Open the searcher *before* the handler so we don't end up opening
+        // one in the middle.
+        getSearcher(false,false,null);
+
+        updateHandler = createUpdateHandler(
+                SolrConfig.config.get("updateHandler/@class", DirectUpdateHandler.class.getName())
+        );
+
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+     } finally {
+
+       }
+
+
+    }
+  }
+
+
+  public void close() {
+    log.info("CLOSING SolrCore!");
+    try {
+      closeSearcher();
+    } catch (Exception e) {
+      SolrException.log(log,e);
+    }
+    try {
+      searcherExecutor.shutdown();
+    } catch (Exception e) {
+      SolrException.log(log,e);
+    }
+    try {
+      updateHandler.close();
+    } catch (Exception e) {
+      SolrException.log(log,e);
+    }
+  }
+
+
+  void finalizer() { close(); }
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Searcher Control
+  ////////////////////////////////////////////////////////////////////////////////
+
+  // The current searcher used to service queries.
+  // Don't access this directly!!!! use getSearcher() to
+  // get it (and it will increment the ref count at the same time)
+  private RefCounted<SolrIndexSearcher> _searcher;
+
+  final ExecutorService searcherExecutor = Executors.newSingleThreadExecutor();
+  private int onDeckSearchers;  // number of searchers preparing
+  private Object searcherLock = new Object();  // the sync object for the searcher
+
+
+  public RefCounted<SolrIndexSearcher> getSearcher() {
+    try {
+      return getSearcher(false,true,null);
+    } catch (IOException e) {
+      SolrException.log(log,null,e);
+      return null;
+    }
+  }
+
+  /**
+   * Get a {@link SolrIndexSearcher} or start the process of creating a new one.
+   * <p>
+   * The registered searcher is the default searcher used to service queries.
+   * A searcher will normally be registered after all of the warming
+   * and event handlers (newSearcher or firstSearcher events) have run.
+   * In the case where there is no registered searcher, the newly created searcher will
+   * be registered before running the event handlers (a slow searcher is better than no searcher).
+   *
+   * <p>
+   * If <tt>forceNew==true</tt> then
+   *  A new searcher will be opened and registered irregardless if there is already
+   *    a registered searcher or other searchers in the process of being created.
+   * <p>
+   * If <tt>forceNew==false</tt> then:<ul>
+   *   <li>If a searcher is already registered, that searcher will be returned</li>
+   *   <li>If no searcher is currently registered, but at least one is in the process of being created, then
+   * this call will block until the first searcher is registered</li>
+   *   <li>If no searcher is currently registered, and no searchers in the process of being registered, a new
+   * searcher will be created.</li>
+   * </ul>
+   * <p>
+   * If <tt>returnSearcher==true</tt> then a {@link RefCounted}&lt{@link SolrIndexSearcher}&gt will be returned with
+   * the reference count incremented.  It <b>must</b> be decremented when no longer needed.
+   * <p>
+   * If <tt>waitSearcher!=null</tt> and a new {@link SolrIndexSearcher} was created,
+   * then it is filled in with a Future that will return after the searcher is registered.  The Future may be set to
+   * <tt>null</tt> in which case the SolrIndexSearcher created has already been registered at the time
+   * this method returned.
+   * <p>
+   * @param forceNew           if true, force the open of a new index searcher regardless if there is already one open.
+   * @param returnSearcher     if true, returns a {@link &ltSolrIndexSearcher&gt} holder with the refcount already incremented.
+   * @param waitSearcher       if non-null, will be filled in with a {@link Future} that will return after the new searcher is registered.
+   * @return
+   * @throws IOException
+   */
+  public RefCounted<SolrIndexSearcher> getSearcher(boolean forceNew, boolean returnSearcher, final Future[] waitSearcher) throws IOException {
+    // it may take some time to open an index.... we may need to make
+    // sure that two threads aren't trying to open one at the same time
+    // if it isn't necessary.
+
+    synchronized (searcherLock) {
+      // see if we can return the current searcher
+      if (_searcher!=null && !forceNew) {
+        if (returnSearcher) {
+          _searcher.incref();
+          return _searcher;
+        } else {
+          return null;
+        }
+      }
+
+      // check to see if we can wait for someone elses searcher to be set
+      if (onDeckSearchers>0 && !forceNew && _searcher==null) {
+        try {
+          searcherLock.wait();
+        } catch (InterruptedException e) {
+          log.info(SolrException.toStr(e));
+        }
+      }
+
+      // check again: see if we can return right now
+      if (_searcher!=null && !forceNew) {
+        if (returnSearcher) {
+          _searcher.incref();
+          return _searcher;
+        } else {
+          return null;
+        }
+      }
+
+      // At this point, we know we need to open a new searcher...
+      // first: increment count to signal other threads that we are
+      //        opening a new searcher.
+      onDeckSearchers++;
+    }
+
+    // open the index synchronously
+    // if this fails, we need to decrement onDeckSearchers again.
+    SolrIndexSearcher tmp;
+    try {
+      if (onDeckSearchers < 1) {
+        // should never happen... just a sanity check
+        log.severe("ERROR!!! onDeckSearchers is " + onDeckSearchers);
+        // reset to 1 (don't bother synchronizing)
+        onDeckSearchers=1;
+      } else if (onDeckSearchers > 1) {
+        log.info("PERFORMANCE WARNING: Overlapping onDeckSearchers=" + onDeckSearchers);
+      }
+      tmp = new SolrIndexSearcher(schema, "main", index_path, true);
+    } catch (Throwable th) {
+      synchronized(searcherLock) {
+        onDeckSearchers--;
+        // notify another waiter to continue... it may succeed
+        // and wake any others.
+        searcherLock.notify();
+      }
+      // need to close the searcher here??? we shouldn't have to.
+      throw new RuntimeException(th);
+    }
+
+    final SolrIndexSearcher newSearcher=tmp;
+
+    RefCounted<SolrIndexSearcher> currSearcherHolder=null;
+    final RefCounted<SolrIndexSearcher> newSearchHolder=newHolder(newSearcher);
+    if (returnSearcher) newSearchHolder.incref();
+
+    // a signal to decrement onDeckSearchers if something goes wrong.
+    final boolean[] decrementOnDeckCount=new boolean[1];
+    decrementOnDeckCount[0]=true;
+
+    try {
+
+      synchronized (searcherLock) {
+        if (_searcher == null) {
+          // if there isn't a current searcher then register this one
+          // before warming is complete instead of waiting.
+          registerSearcher(newSearchHolder);
+          decrementOnDeckCount[0]=false;
+        } else {
+          // get a reference to the current searcher for purposes of autowarming.
+          currSearcherHolder=_searcher;
+          currSearcherHolder.incref();
+        }
+      }
+
+
+      final SolrIndexSearcher currSearcher = currSearcherHolder==null ? null : currSearcherHolder.get();
+
+      //
+      // Note! if we registered the new searcher (but didn't increment it's
+      // reference count because returnSearcher==false, it's possible for
+      // someone else to register another searcher, and thus cause newSearcher
+      // to close while we are warming.
+      //
+      // Should we protect against that by incrementing the reference count?
+      // Maybe we should just let it fail?   After all, if returnSearcher==false
+      // and newSearcher has been de-registered, what's the point of continuing?
+      //
+
+      Future future=null;
+
+      // warm the new searcher based on the current searcher.
+      // should this go before the other event handlers or after?
+      if (currSearcher != null) {
+        future = searcherExecutor.submit(
+                new Callable() {
+                  public Object call() throws Exception {
+                    try {
+                      newSearcher.warm(currSearcher);
+                    } catch (Throwable e) {
+                      SolrException.logOnce(log,null,e);
+                    }
+                    return null;
+                  }
+                }
+        );
+      }
+
+      if (currSearcher==null && firstSearcherListeners.size() > 0) {
+        future = searcherExecutor.submit(
+                new Callable() {
+                  public Object call() throws Exception {
+                    try {
+                      for (SolrEventListener listener : firstSearcherListeners) {
+                        listener.newSearcher(newSearcher,null);
+                      }
+                    } catch (Throwable e) {
+                      SolrException.logOnce(log,null,e);
+                    }
+                    return null;
+                  }
+                }
+        );
+      }
+
+      if (currSearcher!=null && newSearcherListeners.size() > 0) {
+        future = searcherExecutor.submit(
+                new Callable() {
+                  public Object call() throws Exception {
+                    try {
+                      for (SolrEventListener listener : newSearcherListeners) {
+                        listener.newSearcher(newSearcher,null);
+                      }
+                    } catch (Throwable e) {
+                      SolrException.logOnce(log,null,e);
+                    }
+                    return null;
+                  }
+                }
+        );
+      }
+
+      // WARNING: this code assumes a single threaded executor (that all tasks
+      // queued will finish first).
+      final RefCounted<SolrIndexSearcher> currSearcherHolderF = currSearcherHolder;
+      Future finalFuture=null;
+      if (currSearcherHolder != null) {
+        finalFuture = searcherExecutor.submit(
+                new Callable() {
+                  public Object call() throws Exception {
+                    try {
+                      // signal that we no longer need to decrement
+                      // the count *before* registering the searcher since
+                      // registertSearcher will decrement even if it errors.
+                      decrementOnDeckCount[0]=false;
+                      registerSearcher(newSearchHolder);
+                    } catch (Throwable e) {
+                      SolrException.logOnce(log,null,e);
+                    } finally {
+                      // we are all done with the old searcher we used
+                      // for warming...
+                      currSearcherHolderF.decref();
+                    }
+                    return null;
+                  }
+                }
+        );
+      }
+
+      if (waitSearcher != null) {
+        waitSearcher[0] = finalFuture;
+      }
+
+      // Return the searcher as the warming tasks run in parallel
+      // callers may wait on the waitSearcher future returned.
+      return returnSearcher ? newSearchHolder : null;
+
+    }
+    catch (Exception e) {
+      SolrException.logOnce(log,null,e);
+      if (currSearcherHolder != null) currSearcherHolder.decref();
+
+      synchronized (searcherLock) {
+        if (decrementOnDeckCount[0]) {
+          onDeckSearchers--;
+        }
+        if (onDeckSearchers < 0) {
+          // sanity check... should never happen
+          log.severe("ERROR!!! onDeckSearchers after decrement=" + onDeckSearchers);
+          onDeckSearchers=0; // try and recover
+        }
+        // if we failed, we need to wake up at least one waiter to continue the process
+        searcherLock.notify();
+      }
+
+      // since the indexreader was already opened, assume we can continue on
+      // even though we got an exception.
+      return returnSearcher ? newSearchHolder : null;
+    }
+
+  }
+
+
+  private RefCounted<SolrIndexSearcher> newHolder(SolrIndexSearcher newSearcher) {
+    RefCounted<SolrIndexSearcher> holder = new RefCounted<SolrIndexSearcher>(newSearcher)
+    {
+      public void close() {
+        try {
+          resource.close();
+        } catch (IOException e) {
+          log.severe("Error closing searcher:" + SolrException.toStr(e));
+        }
+      }
+    };
+    holder.incref();  // set ref count to 1 to account for this._searcher
+    return holder;
+  }
+
+
+  // Take control of newSearcherHolder (which should have a reference count of at
+  // least 1 already.  If the caller wishes to use the newSearcherHolder directly
+  // after registering it, then they should increment the reference count *before*
+  // calling this method.
+  //
+  // onDeckSearchers will also be decremented (it should have been incremented
+  // as a result of opening a new searcher).
+  private void registerSearcher(RefCounted<SolrIndexSearcher> newSearcherHolder) throws IOException {
+    synchronized (searcherLock) {
+      try {
+        if (_searcher != null) {
+          _searcher.decref();   // dec refcount for this._searcher
+          _searcher=null;
+        }
+
+        _searcher = newSearcherHolder;
+        SolrIndexSearcher newSearcher = newSearcherHolder.get();
+
+        SolrInfoRegistry.getRegistry().put("currentSearcher", newSearcher);
+        newSearcher.register(); // register subitems (caches)
+        log.info("Registered new searcher " + newSearcher);
+
+      } catch (Throwable e) {
+        log(e);
+      } finally {
+        // wake up anyone waiting for a searcher
+        // even in the face of errors.
+        onDeckSearchers--;
+        searcherLock.notifyAll();
+      }
+    }
+  }
+
+
+
+  public void closeSearcher() {
+    log.info("Closing main searcher on request.");
+    synchronized (searcherLock) {
+      if (_searcher != null) {
+        _searcher.decref();   // dec refcount for this._searcher
+        _searcher=null;
+        SolrInfoRegistry.getRegistry().remove("currentSearcher");
+      }
+    }
+  }
+
+
+
+  public void execute(SolrQueryRequest req, SolrQueryResponse rsp) {
+    SolrRequestHandler handler = getRequestHandler(req.getQueryType());
+    if (handler==null) {
+      log.warning("Unknown Request Handler '" + req.getQueryType() +"' :" + req);
+      throw new SolrException(400,"Unknown Request Handler '" + req.getQueryType() + "'", true);
+    }
+    handler.handleRequest(req,rsp);
+    log.info(req.getParamString()+ " 0 "+
+	     (int)(rsp.getEndTime() - req.getStartTime()));
+  }
+
+
+
+
+
+  XmlPullParserFactory factory;
+  {
+    try {
+      factory = XmlPullParserFactory.newInstance();
+    } catch (XmlPullParserException e) {
+      throw new RuntimeException(e);
+    }
+    factory.setNamespaceAware(false);
+  }
+
+
+  private int findNextTag(XmlPullParser xpp, String tag) throws XmlPullParserException, IOException {
+    int eventType;
+    while((eventType=xpp.next()) != XmlPullParser.END_DOCUMENT) {
+      if(eventType == XmlPullParser.START_TAG) {
+        if (tag.equals(xpp.getName())) break;
+      }
+    }
+    return eventType;
+  }
+
+
+  public void update(Reader reader, Writer writer) {
+
+    // TODO: add param to specify maximum time to commit?
+
+    // todo - might be nice to separate command parsing w/ a factory
+    // then new commands could be added w/o risk to old ones
+
+
+    XmlPullParser xpp = null;
+    try {
+      xpp = factory.newPullParser();
+    } catch (XmlPullParserException e) {
+      throw new RuntimeException(e);
+    }
+
+    long startTime=System.currentTimeMillis();
+
+    try {
+      xpp.setInput(reader);
+      xpp.nextTag();
+
+      String currTag = xpp.getName();
+      if ("add".equals(currTag)) {
+        log.finest("SolrCore.update(add)");
+        AddUpdateCommand cmd = new AddUpdateCommand();
+        cmd.allowDups=false;  // the default
+
+        int status=0;
+        boolean pendingAttr=false, committedAttr=false;
+        int attrcount = xpp.getAttributeCount();
+        for (int i=0; i<attrcount; i++) {
+          String attrName = xpp.getAttributeName(i);
+          String attrVal = xpp.getAttributeValue(i);
+          if ("allowDups".equals(attrName)) {
+            cmd.allowDups = StrUtils.parseBoolean(attrVal);
+          } else if ("overwritePending".equals(attrName)) {
+            cmd.overwritePending = StrUtils.parseBoolean(attrVal);
+            pendingAttr=true;
+          } else if ("overwriteCommitted".equals(attrName)) {
+            cmd.overwriteCommitted = StrUtils.parseBoolean(attrVal);
+            committedAttr=true;
+          } else {
+            log.warning("Unknown attribute id in add:" + attrName);
+          }
+        }
+
+        //set defaults for committed and pending based on allowDups value
+        if (!pendingAttr) cmd.overwritePending=!cmd.allowDups;
+        if (!committedAttr) cmd.overwriteCommitted=!cmd.allowDups;
+
+        DocumentBuilder builder = new DocumentBuilder(schema);
+        int eventType=0;
+        while(true) {
+          // this may be our second time through the loop in the case
+          // that there are multiple docs in the add... so make sure that
+          // objects can handle that.
+
+          cmd.id = null;  // reset the id for this add     
+
+          if (eventType !=0) {
+            eventType=xpp.getEventType();
+            if (eventType==XmlPullParser.END_DOCUMENT) break;
+          }
+          // eventType = xpp.next();
+          eventType = xpp.nextTag();
+          if (eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) break;  // should match </add>
+
+          try {
+            readDoc(builder,xpp);
+            builder.endDoc();
+            cmd.doc = builder.getDoc();
+            log.finest("adding doc...");
+            updateHandler.addDoc(cmd);
+	          log.info("add "+status+" "+(System.currentTimeMillis()-startTime));
+            writer.write("<result status=\"" + status + "\"></result>");
+          } catch (SolrException e) {
+            log(e);
+	          log.info("add "+e.code+" "+(System.currentTimeMillis()-startTime));
+            writeResult(writer,e);
+            // we may not have finised reading the XML for this cmd,
+            // so eat any unused input up till "</add>"
+            eventType = xpp.getEventType();
+            while (true)  {
+              if ( eventType == XmlPullParser.END_DOCUMENT
+                      || (eventType == XmlPullParser.END_TAG && "add".equals(xpp.getName())))
+              {
+                break;
+              }
+              eventType = xpp.next();
+            }
+          }
+        }
+
+      /***
+      while (findNextTag(xpp,"doc") != XmlPullParser.END_DOCUMENT) {
+        readDoc(builder,xpp);
+        Document doc = builder.endDoc();
+        indexWriter.addDocument(doc);
+        docsAdded++;
+      }
+      ***/
+
+    } // end add
+
+      else if ("commit".equals(currTag) || "optimize".equals(currTag)) {
+        log.finest("parsing "+currTag);
+        try {
+          CommitUpdateCommand cmd = new CommitUpdateCommand("optimize".equals(currTag));
+
+          boolean sawWaitSearcher=false, sawWaitFlush=false;
+          int attrcount = xpp.getAttributeCount();
+          for (int i=0; i<attrcount; i++) {
+            String attrName = xpp.getAttributeName(i);
+            String attrVal = xpp.getAttributeValue(i);
+            if ("waitFlush".equals(attrName)) {
+              cmd.waitFlush = StrUtils.parseBoolean(attrVal);
+              sawWaitFlush=true;
+            } else if ("waitSearcher".equals(attrName)) {
+              cmd.waitSearcher = StrUtils.parseBoolean(attrVal);
+              sawWaitSearcher=true;
+            } else {
+              log.warning("unexpected attribute commit/@" + attrName);
+            }
+          }
+
+          // If waitFlush is specified and waitSearcher wasn't, then
+          // clear waitSearcher.
+          if (sawWaitFlush && !sawWaitSearcher) {
+            cmd.waitSearcher=false;
+          }
+
+          updateHandler.commit(cmd);
+          if ("optimize".equals(currTag)) {
+            log.info("optimize 0 "+(System.currentTimeMillis()-startTime));
+          }
+          else {
+            log.info("commit 0 "+(System.currentTimeMillis()-startTime));
+          }
+          while (true) {
+            int eventType = xpp.nextTag();
+            if (eventType == XmlPullParser.END_TAG) break; // match </commit>
+          }
+          writer.write("<result status=\"0\"></result>");
+        } catch (SolrException e) {
+          log(e);
+          if ("optimize".equals(currTag)) {
+            log.info("optimize "+e.code+" "+
+                    (System.currentTimeMillis()-startTime));
+          }
+          else {
+            log.info("commit "+e.code+" "+
+                    (System.currentTimeMillis()-startTime));
+          }
+          writeResult(writer,e);
+        } catch (Exception e) {
+          SolrException.log(log, "Exception during commit/optimize",e);
+          writeResult(writer,e);
+        }
+      }  // end commit
+
+    else if ("delete".equals(currTag)) {
+      log.finest("parsing delete");
+      try {
+        DeleteUpdateCommand cmd = new DeleteUpdateCommand();
+        cmd.fromPending=true;
+        cmd.fromCommitted=true;
+        int attrcount = xpp.getAttributeCount();
+        for (int i=0; i<attrcount; i++) {
+          String attrName = xpp.getAttributeName(i);
+          String attrVal = xpp.getAttributeValue(i);
+          if ("fromPending".equals(attrName)) {
+            cmd.fromPending = StrUtils.parseBoolean(attrVal);
+          } else if ("fromCommitted".equals(attrName)) {
+            cmd.fromCommitted = StrUtils.parseBoolean(attrVal);
+          } else {
+            log.warning("unexpected attribute delete/@" + attrName);
+          }
+        }
+
+        int eventType = xpp.nextTag();
+        currTag = xpp.getName();
+        String val = xpp.nextText();
+
+        if ("id".equals(currTag)) {
+          cmd.id =  val;
+          updateHandler.delete(cmd);
+          log.info("delete(id " + val + ") 0 " +
+                   (System.currentTimeMillis()-startTime));
+        } else if ("query".equals(currTag)) {
+          cmd.query =  val;
+          updateHandler.deleteByQuery(cmd);
+          log.info("deleteByQuery(query " + val + ") 0 " +
+                   (System.currentTimeMillis()-startTime));
+        } else {
+          log.warning("unexpected XML tag /delete/"+currTag);
+          throw new SolrException(400,"unexpected XML tag /delete/"+currTag);
+        }
+
+        writer.write("<result status=\"0\"></result>");
+
+        while (xpp.nextTag() != XmlPullParser.END_TAG);
+
+      } catch (SolrException e) {
+        log(e);
+        log.info("delete "+e.code+" "+(System.currentTimeMillis()-startTime));
+        writeResult(writer,e);
+      } catch (Exception e) {
+        log(e);
+        writeResult(writer,e);
+      }
+    } // end delete
+
+
+    } catch (XmlPullParserException e) {
+      log(e);
+      writeResult(writer,e);
+    } catch (IOException e) {
+      log(e);
+      writeResult(writer,e);
+    } catch (SolrException e) {
+      log(e);
+      log.info("update "+e.code+" "+(System.currentTimeMillis()-startTime));
+      writeResult(writer,e);
+    } catch (Throwable e) {
+      log(e);
+      writeResult(writer,e);
+    }
+
+  }
+
+  private void readDoc(DocumentBuilder builder, XmlPullParser xpp) throws IOException, XmlPullParserException {
+    // xpp should be at <doc> at this point
+
+    builder.startDoc();
+
+    int attrcount = xpp.getAttributeCount();
+    float docBoost = 1.0f;
+
+    for (int i=0; i<attrcount; i++) {
+      String attrName = xpp.getAttributeName(i);
+      String attrVal = xpp.getAttributeValue(i);
+      if ("boost".equals(attrName)) {
+        docBoost = Float.parseFloat(attrVal);
+      } else {
+        log.warning("Unknown attribute doc/@" + attrName);
+      }
+    }
+    if (docBoost != 1.0f) builder.setBoost(docBoost);
+
+    // while (findNextTag(xpp,"field") != XmlPullParser.END_DOCUMENT) {
+
+    while(true) {
+      int eventType = xpp.nextTag();
+      if (eventType == XmlPullParser.END_TAG) break;  // </doc>
+
+      String tname=xpp.getName();
+      // System.out.println("FIELD READER AT TAG " + tname);
+
+      if (!"field".equals(tname)) {
+        log.warning("unexpected XML tag doc/"+tname);
+        throw new SolrException(400,"unexpected XML tag doc/"+tname);
+      }
+
+      //
+      // get field name and parse field attributes
+      //
+      attrcount = xpp.getAttributeCount();
+      String name=null;
+      float boost=1.0f;
+      boolean isNull=false;
+
+      for (int i=0; i<attrcount; i++) {
+        String attrName = xpp.getAttributeName(i);
+        String attrVal = xpp.getAttributeValue(i);
+        if ("name".equals(attrName)) {
+          name=attrVal;
+        } else if ("boost".equals(attrName)) {
+          boost=Float.parseFloat(attrVal);
+        } else if ("null".equals(attrName)) {
+          isNull=StrUtils.parseBoolean(attrVal);
+        } else {
+          log.warning("Unknown attribute doc/field/@" + attrName);
+        }
+      }
+
+      // now get the field value
+      String val = xpp.nextText();      // todo... text event for <field></field>???
+                                        // need this line for isNull???
+      // Don't add fields marked as null (for now at least)
+      if (!isNull) {
+        if (docBoost != 1.0f) {
+          builder.addField(name,val,docBoost);
+        } else {
+          builder.addField(name,val);
+        }
+      }
+
+      // do I have to do a nextTag here to read the end_tag?
+
+    } // end field loop
+
+  }
+
+
+  final public static void log(Throwable e) {
+    SolrException.logOnce(log,null,e);
+  }
+
+
+  final static void writeResult(Writer out, SolrException e) {
+    try {
+      XML.writeXML(out,"result",e.getMessage(),"status",e.code());
+    } catch (Exception ee) {
+      log.severe("Error writing to putput stream: "+ee);
+    }
+  }
+
+  final static void writeResult(Writer out, Throwable e) {
+    try {
+      XML.writeXML(out,"result",SolrException.toStr(e),"status","1");
+    } catch (Exception ee) {
+      log.severe("Error writing to putput stream: "+ee);
+    }
+  }
+
+
+}
+
+
+
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/core/SolrCore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/core/SolrEventListener.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/core/SolrEventListener.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/core/SolrEventListener.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/core/SolrEventListener.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.core;
+
+import org.apache.solr.util.NamedList;
+import org.apache.solr.search.SolrIndexSearcher;
+
+import java.util.logging.Logger;
+
+/**
+ * @author yonik
+ * @version $Id: SolrEventListener.java,v 1.4 2005/05/25 04:26:47 yonik Exp $
+ */
+public interface SolrEventListener {
+  static final Logger log = Logger.getLogger(SolrCore.class.getName());
+
+  public void init(NamedList args);
+
+  public void postCommit();
+
+  public void newSearcher(SolrIndexSearcher newSearcher, SolrIndexSearcher currentSearcher);
+
+}
\ No newline at end of file

Propchange: incubator/solr/trunk/src/java/org/apache/solr/core/SolrEventListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/core/SolrException.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/core/SolrException.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/core/SolrException.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/core/SolrException.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,108 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.core;
+
+import java.util.logging.Logger;
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+
+/**
+ * @author yonik
+ * @version $Id: SolrException.java,v 1.6 2005/06/14 20:42:26 yonik Exp $
+ */
+
+
+public class SolrException extends RuntimeException {
+  public boolean logged=false;
+
+  public SolrException(int code, String msg) {
+    super(msg);
+    this.code=code;
+  }
+  public SolrException(int code, String msg, boolean alreadyLogged) {
+    super(msg);
+    this.code=code;
+    this.logged=alreadyLogged;
+  }
+
+  public SolrException(int code, String msg, Throwable th, boolean alreadyLogged) {
+    super(msg,th);
+    this.code=code;
+    logged=alreadyLogged;
+  }
+
+  public SolrException(int code, String msg, Throwable th) {
+    this(code,msg,th,true);
+  }
+
+  public SolrException(int code, Throwable th) {
+    super(th);
+    this.code=code;
+    logged=true;
+  }
+
+  int code=0;
+  public int code() { return code; }
+
+
+
+
+  public void log(Logger log) { log(log,this); }
+  public static void log(Logger log, Throwable e) {
+    log.severe(toStr(e));
+    if (e instanceof SolrException) {
+      ((SolrException)e).logged = true;
+    }
+  }
+
+  public static void log(Logger log, String msg, Throwable e) {
+    log.severe(msg + ':' + toStr(e));
+    if (e instanceof SolrException) {
+      ((SolrException)e).logged = true;
+    }
+  }
+
+  public static void logOnce(Logger log, String msg, Throwable e) {
+    if (e instanceof SolrException) {
+      if(((SolrException)e).logged) return;
+    }
+    if (msg!=null) log(log,msg,e);
+    else log(log,e);
+  }
+
+
+  // public String toString() { return toStr(this); }  // oops, inf loop
+  public String toString() { return super.toString(); }
+
+  public static String toStr(Throwable e) {
+    CharArrayWriter cw = new CharArrayWriter();
+    PrintWriter pw = new PrintWriter(cw);
+    e.printStackTrace(pw);
+    pw.flush();
+    return cw.toString();
+
+/** This doesn't work for some reason!!!!!
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    e.printStackTrace(pw);
+    pw.flush();
+    System.out.println("The STRING:" + sw.toString());
+    return sw.toString();
+**/
+  }
+
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/core/SolrException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfo.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfo.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfo.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfo.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.core;
+
+import java.net.URL;
+import org.apache.solr.util.*;
+
+/**
+ * @author ronp
+ * @version $Id: SolrInfo.java,v 1.3 2005/05/02 19:04:59 ronp Exp $
+ */
+
+// MBean pattern for holding various ui friendly strings and URLs
+// for use by objects which are 'plugable' to make administering
+// production use easier
+  // name        - simple common usage name, e.g. BasicQueryHandler
+  // version     - simple common usage version, e.g. 2.0
+  // description - simple one or two line description
+  // cvsId       - yes, really the CVS Id      (type 'man co')
+  // cvsName     - yes, really the CVS Name    (type 'man co')
+  // cvsSource   - yes, really the CVS Source  (type 'man co')
+  // docs        - URL list: TWIKI, Faq, Design doc, something! :)
+
+abstract class SolrInfo implements SolrInfoMBean {
+  public static String _cvsId="$Id: SolrInfo.java,v 1.3 2005/05/02 19:04:59 ronp Exp $";
+  public static String _cvsSource="$Source: /cvs/main/searching/solr/solarcore/src/solr/SolrInfo.java,v $";
+  public static String _cvsName="$Name:  $";
+
+
+  public String getName()          { return this.name;        }
+  public String getVersion()       { return this.version;     }
+  public String getDescription()   { return this.description; }
+  public Category getCategory()    { return SolrInfoMBean.Category.QUERYHANDLER; }
+  public String getCvsId()         { return this.cvsId;       }
+  public String getCvsName()       { return this.cvsName;     }
+  public String getCvsSource()     { return this.cvsSource;   }
+  public URL[] getDocs()           { return this.docs;        }
+  public NamedList getStatistics() { return null;        }
+
+
+  public void setName(String name )          { this.name        = name;      }
+  public void setVersion(String vers)        { this.version     = vers;      }
+  public void setDescription(String desc)    { this.description = desc;      }
+  public void setCvsId(String cvsId)         { this.cvsId       = cvsId;     }
+  public void setCvsName(String cvsName)     { this.cvsName     = cvsName;   }
+  public void setCvsSource(String cvsSource) { this.cvsSource   = cvsSource; }
+  public void setDocs(URL[] docs)            { this.docs        = docs;      }
+
+  public void addDoc(URL doc)
+  {
+    if (doc == null) {
+      // should throw runtime exception
+      return;
+    }
+    if (docs != null) {
+      URL[] newDocs = new URL[docs.length+1];
+      int i;
+      for (i = 0; i < docs.length; i++) {
+        newDocs[i] = docs[i];
+      }
+      newDocs[i] = doc;
+      docs = newDocs;
+    } else {
+      docs = new URL[1];
+      docs[0] = doc;
+    }
+  }
+
+  public void addDoc(String doc)
+  {
+    if (doc == null) {
+      // should throw runtime exception
+      return;
+    }
+    try {
+      URL docURL = new URL(doc);
+      addDoc(docURL);
+    } catch (Exception e) {
+      // ignore for now
+    }
+  }
+
+  private String name;
+  private String version;
+  private String description;
+  public String cvsId;
+  public String cvsSource;
+  public String cvsName;
+  private URL[] docs;
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoMBean.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoMBean.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoMBean.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoMBean.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.core;
+
+import java.net.URL;
+import org.apache.solr.util.*;
+
+/**
+ * @author ronp
+ * @version $Id: SolrInfoMBean.java,v 1.3 2005/05/04 19:15:23 ronp Exp $
+ */
+
+// MBean interface for getting various ui friendly strings and URLs
+// for use by objects which are 'plugable' to make administering
+// production use easier
+  // name        - simple common usage name, e.g. BasicQueryHandler
+  // version     - simple common usage version, e.g. 2.0
+  // description - simple one or two line description
+  // cvsId       - yes, really the CVS Id      (type 'man co')
+  // cvsName     - yes, really the CVS Name    (type 'man co')
+  // cvsSource   - yes, really the CVS Source  (type 'man co')
+  // docs        - URL list: TWIKI, Faq, Design doc, something! :)
+
+public interface SolrInfoMBean {
+
+  public enum Category { CORE, QUERYHANDLER, UPDATEHANDLER, CACHE, OTHER };
+
+  public String getName();
+  public String getVersion();
+  public String getDescription();
+  public Category getCategory();
+  public String getCvsId();
+  public String getCvsName();
+  public String getCvsSource();
+  public URL[] getDocs();
+  public NamedList getStatistics();
+
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoMBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoRegistry.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoRegistry.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoRegistry.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoRegistry.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.core;
+
+import org.apache.solr.core.SolrInfoMBean;
+
+import java.util.*;
+
+/**
+ * @author ronp
+ * @version $Id: SolrInfoRegistry.java,v 1.5 2005/05/14 03:34:39 yonik Exp $
+ */
+
+// A Registry to hold a collection of SolrInfo objects
+
+public class SolrInfoRegistry {
+  public static final String cvsId="$Id: SolrInfoRegistry.java,v 1.5 2005/05/14 03:34:39 yonik Exp $";
+  public static final String cvsSource="$Source: /cvs/main/searching/solr/solarcore/src/solr/SolrInfoRegistry.java,v $";
+  public static final String cvsName="$Name:  $";
+
+  private static final Map<String,SolrInfoMBean> inst = Collections.synchronizedMap(new LinkedHashMap<String,SolrInfoMBean>());
+
+  public static Map<String, SolrInfoMBean> getRegistry()
+  {
+    return inst;
+  }
+
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/core/SolrInfoRegistry.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/request/LocalSolrQueryRequest.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/LocalSolrQueryRequest.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/LocalSolrQueryRequest.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/LocalSolrQueryRequest.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,198 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.request;
+
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.util.RefCounted;
+import org.apache.solr.util.StrUtils;
+import org.apache.solr.util.NamedList;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.core.SolrCore;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author yonik
+ * @version $Id: LocalSolrQueryRequest.java,v 1.6 2005/06/02 22:03:38 yonik Exp $
+ */
+public class LocalSolrQueryRequest extends SolrQueryRequestBase {
+  private final NamedList args;
+  private final String query;
+  private final String qtype;
+  private final int start;
+  private final int limit;
+
+  public final static Map emptyArgs = new HashMap(0,1);
+
+  public LocalSolrQueryRequest(SolrCore core, String query, String qtype, int start, int limit, Map args) {
+    super(core);
+    this.query=query;
+    this.qtype=qtype;
+    this.start=start;
+    this.limit=limit;
+
+    this.args = new NamedList();
+    if (query!=null) this.args.add(SolrQueryRequestBase.QUERY_NAME, query);
+    if (qtype!=null) this.args.add(SolrQueryRequestBase.QUERYTYPE_NAME, qtype);
+    this.args.add(SolrQueryRequestBase.START_NAME, Integer.toString(start));
+    this.args.add(SolrQueryRequestBase.ROWS_NAME, Integer.toString(limit));
+
+    if (args!=null) this.args.addAll(args);
+  }
+
+
+  public LocalSolrQueryRequest(SolrCore core, NamedList args) {
+    super(core);
+    this.args=args;
+    this.query=getStrParam(QUERY_NAME,null);
+    this.qtype=getStrParam(QUERYTYPE_NAME,null);;
+    this.start=getIntParam(START_NAME,0);
+    this.limit=getIntParam(ROWS_NAME,10);
+  }
+
+
+  public String getParam(String name) {
+    return (String)args.get(name);
+  }
+
+  public String getQueryString() {
+    return query;
+  }
+
+  // signifies the syntax and the handler that should be used
+  // to execute this query.
+  public String getQueryType() {
+    return qtype;
+  }
+
+
+  // starting position in matches to return to client
+  public int getStart() {
+    return start;
+  }
+
+  // number of matching documents to return
+  public int getLimit() {
+    return limit;
+  }
+
+  final long startTime=System.currentTimeMillis();
+  // Get the start time of this request in milliseconds
+  public long getStartTime() {
+    return startTime;
+  }
+
+  // The index searcher associated with this request
+  RefCounted<SolrIndexSearcher> searcherHolder;
+  public SolrIndexSearcher getSearcher() {
+    // should this reach out and get a searcher from the core singleton, or
+    // should the core populate one in a factory method to create requests?
+    // or there could be a setSearcher() method that Solr calls
+
+    if (searcherHolder==null) {
+      searcherHolder = core.getSearcher();
+    }
+
+    return searcherHolder.get();
+  }
+
+  // The solr core (coordinator, etc) associated with this request
+  public SolrCore getCore() {
+    return core;
+  }
+
+  // The index schema associated with this request
+  public IndexSchema getSchema() {
+    return core.getSchema();
+  }
+
+  public String getParamString() {
+    StringBuilder sb = new StringBuilder(128);
+    try {
+
+      boolean first=true;
+      if (query!=null) {
+        if (!first) {
+          sb.append('&');
+        }
+        first=false;
+        sb.append("q=");
+        StrUtils.partialURLEncodeVal(sb,query);
+      }
+
+      // null, "", and "standard" are all the default query handler.
+      if (qtype!=null && !(qtype.equals("") || qtype.equals("standard"))) {
+        if (!first) {
+          sb.append('&');
+        }
+        first=false;
+        sb.append("qt=");
+        sb.append(qtype);
+      }
+
+      if (start!=0) {
+        if (!first) {
+          sb.append('&');
+        }
+        first=false;
+        sb.append("start=");
+        sb.append(start);
+      }
+
+      if (!first) {
+        sb.append('&');
+      }
+      first=false;
+      sb.append("rows=");
+      sb.append(limit);
+
+      if (args != null && args.size() > 0) {
+        for (int i=0; i<args.size(); i++) {
+          if (!first) {
+            sb.append('&');
+          }
+          first=false;
+
+          sb.append(args.getName(i));
+          sb.append('=');
+          StrUtils.partialURLEncodeVal(sb,args.getVal(i).toString());
+        }
+      }
+
+    } catch (Exception e) {
+      // should never happen... we only needed this because
+      // partialURLEncodeVal can throw an IOException, but it
+      // never will when adding to a StringBuilder.
+      throw new RuntimeException(e);
+    }
+
+    return sb.toString();
+  }
+
+
+  public void close() {
+    if (searcherHolder!=null) {
+      searcherHolder.decref();
+    }
+  }
+
+
+
+}
+
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/request/LocalSolrQueryRequest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/request/QueryResponseWriter.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/QueryResponseWriter.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/QueryResponseWriter.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/QueryResponseWriter.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.request;
+
+import java.io.Writer;
+import java.io.IOException;
+
+/**
+ * @author yonik
+ * @version $Id: QueryResponseWriter.java,v 1.2 2005/04/24 02:53:35 yonik Exp $
+ */
+public interface QueryResponseWriter {
+  public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException;
+}
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/request/QueryResponseWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.request;
+
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.core.SolrCore;
+
+/**
+ * @author yonik
+ * @version $Id: SolrQueryRequest.java,v 1.3 2005/05/10 19:40:12 yonik Exp $
+ */
+public interface SolrQueryRequest {
+  public String getParam(String name);
+
+  public String getQueryString();
+
+  // signifies the syntax and the handler that should be used
+  // to execute this query.
+  public String getQueryType();
+
+  // starting position in matches to return to client
+  public int getStart();
+
+  // number of matching documents to return
+  public int getLimit();
+
+  // Get the start time of this request in milliseconds
+  public long getStartTime();
+
+  // The index searcher associated with this request
+  public SolrIndexSearcher getSearcher();
+
+  // The solr core (coordinator, etc) associated with this request
+  public SolrCore getCore();
+
+  // The index schema associated with this request
+  public IndexSchema getSchema();
+
+  /**
+   * Returns a string representing all the important parameters.
+   * Suitable for logging.
+   */
+  public String getParamString();
+
+  /******
+  // Get the current elapsed time in milliseconds
+  public long getElapsedTime();
+  ******/
+}
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequestBase.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequestBase.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequestBase.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequestBase.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,130 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.request;
+
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.util.RefCounted;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrException;
+
+/**
+* @author yonik
+* @version $Id: SolrQueryRequestBase.java,v 1.6 2005/06/12 02:36:09 yonik Exp $
+*/
+public abstract class SolrQueryRequestBase implements SolrQueryRequest {
+ // some standard query argument names
+ public static final String QUERY_NAME="q";
+ public static final String START_NAME="start";
+ public static final String ROWS_NAME="rows";
+ public static final String XSL_NAME="xsl";
+ public static final String QUERYTYPE_NAME="qt";
+
+
+ protected final SolrCore core;
+
+ public SolrQueryRequestBase(SolrCore core) {
+   this.core=core;
+ }
+
+ public int getIntParam(String name) {
+   String s = getParam(name);
+   if (s==null) {
+     throw new SolrException(500,"Missing required parameter '"+name+"' from " + this);
+   }
+   return Integer.parseInt(s);
+ }
+
+ public int getIntParam(String name, int defval) {
+   String s = getParam(name);
+   return s==null ? defval : Integer.parseInt(s);
+ }
+
+ public String getStrParam(String name) {
+   String s = getParam(name);
+   if (s==null) {
+     throw new SolrException(500,"Missing required parameter '"+name+"' from " + this);
+   }
+   return s;
+ }
+
+ public String getStrParam(String name, String defval) {
+   String s = getParam(name);
+   return s==null ? defval : s;
+ }
+
+ public String getQueryString() {
+   return getParam(QUERY_NAME);
+ }
+
+ public String getQueryType() {
+   return getParam(QUERYTYPE_NAME);
+ }
+
+ // starting position in matches to return to client
+ public int getStart() {
+   return getIntParam(START_NAME, 0);
+ }
+
+ // number of matching documents to return
+ public int getLimit() {
+   return getIntParam(ROWS_NAME, 10);
+ }
+
+
+ protected final long startTime=System.currentTimeMillis();
+ // Get the start time of this request in milliseconds
+ public long getStartTime() {
+   return startTime;
+ }
+
+ // The index searcher associated with this request
+ protected RefCounted<SolrIndexSearcher> searcherHolder;
+ public SolrIndexSearcher getSearcher() {
+   // should this reach out and get a searcher from the core singleton, or
+   // should the core populate one in a factory method to create requests?
+   // or there could be a setSearcher() method that Solr calls
+
+   if (searcherHolder==null) {
+     searcherHolder = core.getSearcher();
+   }
+
+   return searcherHolder.get();
+ }
+
+ // The solr core (coordinator, etc) associated with this request
+ public SolrCore getCore() {
+   return core;
+ }
+
+ // The index schema associated with this request
+ public IndexSchema getSchema() {
+   return core.getSchema();
+ }
+
+
+ public void close() {
+   if (searcherHolder!=null) {
+     searcherHolder.decref();
+   }
+ }
+
+ public String toString() {
+   return this.getClass().getSimpleName() + '{' + getParamString() + '}';
+ }
+
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequestBase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,125 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.request;
+
+import org.apache.solr.util.NamedList;
+
+import java.util.*;
+
+/**
+ * <code>SolrQueryResponse</code> is used by a query handler to return
+ * the response to a query.
+ * @author yonik
+ * @version $Id: SolrQueryResponse.java,v 1.5 2005/08/10 04:27:04 yonik Exp $
+ * @since solr 0.9
+ */
+
+public class SolrQueryResponse {
+
+  protected  NamedList values = new NamedList();
+  // current holder for user defined values
+
+  protected Set<String> defaultReturnFields;
+
+  // error if this is set...
+  protected Exception err;
+
+  /***
+   // another way of returning an error
+  int errCode;
+  String errMsg;
+  ***/
+
+  public NamedList getValues() { return values; }
+
+  /**
+   *  Sets a list of all the named values to return.
+   */
+  public void setAllValues(NamedList nameValuePairs) {
+    values=nameValuePairs;
+  }
+
+  /**
+   * Sets the document field names of fields to return by default.
+   */
+  public void setReturnFields(Set<String> fields) {
+    defaultReturnFields=fields;
+  }
+  // TODO: should this be represented as a String[] such
+  // that order can be maintained if needed?
+
+  /**
+   * The document field names to return by default.
+   */
+  public Set<String> getReturnFields() {
+    return defaultReturnFields;
+  }
+
+
+  /**
+   * Appends a named value to the list of named values to be returned.
+   * @param name  the name of the value - may be null if unnamed
+   * @param val   the value to add - also may be null since null is a legal value
+   */
+  public void add(String name, Object val) {
+    values.add(name,val);
+  }
+
+  /**
+   * Causes an error to be returned instead of the results.
+   */
+  public void setException(Exception e) {
+    err=e;
+  }
+
+  /**
+   * Returns an Exception if there was a fatal error in processing the request.
+   * Returns null if the request succeeded.
+   */
+  public Exception getException() {
+    return err;
+  }
+
+  // Get and Set the endtime in milliseconds... used
+  // to calculate query time.
+  protected long endtime;
+
+  /** Time in milliseconds when the response officially finished. 
+   */
+  public long getEndTime() {
+    if (endtime==0) {
+      setEndTime();
+    }
+    return endtime;
+  }
+
+  /**
+   * Stop the timer for how long this query took.
+   */
+  public long setEndTime() {
+    return setEndTime(System.currentTimeMillis());
+  }
+
+  public long setEndTime(long endtime) {
+    if (endtime!=0) {
+      this.endtime=endtime;
+    }
+    return this.endtime;
+  }
+
+
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/request/SolrRequestHandler.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/SolrRequestHandler.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/SolrRequestHandler.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/SolrRequestHandler.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.request;
+
+import org.apache.solr.util.NamedList;
+import org.apache.solr.core.SolrInfoMBean;
+
+/**
+ * Implementations of <code>SolrRequestHandler</code> are called to handle query requests.
+ *
+ * Different <code>SolrRequestHandler</code>s are registered with the <code>SolrCore</code>.
+ * One way to register a SolrRequestHandler with the core is thorugh the <code>solarconfig.xml</code> file.
+ * <p>
+ * Example <code>solarconfig.xml</code> entry to register a <code>SolrRequestHandler</code> implementation to
+ * handle all queries with a query type of "test":
+ * <p>
+ * <code>
+ *    &lt;requestHandler name="test" class="solr.tst.TestRequestHandler" /&gt;
+ * </code>
+ * <p>
+ * A single instance of any registered SolrRequestHandler is created
+ * via the default constructor and is reused for all relevant queries.
+ *
+ * @author yonik
+ * @version $Id: SolrRequestHandler.java,v 1.7 2005/12/02 04:31:06 yonik Exp $
+ */
+public interface SolrRequestHandler extends SolrInfoMBean {
+
+  /** <code>init</code> will be called just once, immediately after creation.
+   * <p>The args are user-level initialization parameters that
+   * may be specified when declaring a request handler in
+   * solarconfig.xml
+   */
+  public void init(NamedList args);
+
+
+  /**
+   * Handles a query request.  This method must be thread safe.
+   * <p>
+   * Information about the request may be obtained from <code>req</code> and
+   * response information may be set using <code>rsp</code>.
+   * <p>
+   * There are no mandatory actions that handleRequest must perform.
+   * An empty handleRequest implementation would fulfill
+   * all interface obligations.
+   */
+  public void handleRequest(SolrQueryRequest req, SolrQueryResponse rsp);
+}
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/request/SolrRequestHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,225 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.request;
+
+import org.apache.lucene.search.*;
+import org.apache.lucene.document.Document;
+
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.logging.Level;
+import java.util.regex.Pattern;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.solr.util.StrUtils;
+import org.apache.solr.util.NamedList;
+import org.apache.solr.search.*;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrInfoMBean;
+import org.apache.solr.core.SolrException;
+
+/**
+ * @author yonik
+ * @version $Id: StandardRequestHandler.java,v 1.17 2005/12/02 04:31:06 yonik Exp $
+ */
+public class StandardRequestHandler implements SolrRequestHandler, SolrInfoMBean {
+
+  // statistics
+  // TODO: should we bother synchronizing these, or is an off-by-one error
+  // acceptable every million requests or so?
+  long numRequests;
+  long numErrors;
+
+
+  public void init(NamedList args) {
+    SolrCore.log.log(Level.INFO, "Unused request handler arguments:" + args);
+  }
+
+
+  private final Pattern splitList=Pattern.compile(",| ");
+
+  public void handleRequest(SolrQueryRequest req, SolrQueryResponse rsp) {
+    numRequests++;
+
+
+    // TODO: test if lucene will accept an escaped ';', otherwise
+    // we need to un-escape them before we pass to QueryParser
+    try {
+      String sreq = req.getQueryString();
+      String debug = req.getParam("debugQuery");
+
+      // find fieldnames to return (fieldlist)
+      String fl = req.getParam("fl");
+      int flags=0;
+      if (fl != null) {
+        // TODO - this could become more efficient if widely used.
+        // TODO - should field order be maintained?
+        String[] flst = splitList.split(fl,0);
+        if (flst.length > 0 && !(flst.length==1 && flst[0].length()==0)) {
+          Set<String> set = new HashSet<String>();
+          for (String fname : flst) {
+            if ("score".equals(fname)) flags |= SolrIndexSearcher.GET_SCORES;
+            set.add(fname);
+          }
+          rsp.setReturnFields(set);
+        }
+      }
+
+      if (sreq==null) throw new SolrException(400,"Missing queryString");
+      List<String> commands = StrUtils.splitSmart(sreq,';');
+
+      String qs = commands.size() >= 1 ? commands.get(0) : "";
+      Query query = QueryParsing.parseQuery(qs, req.getSchema());
+
+      // If the first non-query, non-filter command is a simple sort on an indexed field, then
+      // we can use the Lucene sort ability.
+      Sort sort = null;
+      if (commands.size() >= 2) {
+        QueryParsing.SortSpec sortSpec = QueryParsing.parseSort(commands.get(1), req.getSchema());
+        if (sortSpec != null) {
+          sort = sortSpec.getSort();
+          // ignore the count for now... it's currently only controlled by start & limit on req
+          // count = sortSpec.getCount();
+        }
+      }
+
+      DocList results = req.getSearcher().getDocList(query, null, sort, req.getStart(), req.getLimit(), flags);
+      rsp.add(null,results);
+
+      if (debug!=null) {
+        NamedList dbg = new NamedList();
+        try {
+          dbg.add("querystring",qs);
+          dbg.add("parsedquery",QueryParsing.toString(query,req.getSchema()));
+          dbg.add("explain", getExplainList(query, results, req.getSearcher(), req.getSchema()));
+          String otherQueryS = req.getParam("explainOther");
+          if (otherQueryS != null && otherQueryS.length() > 0) {
+            DocList otherResults = doQuery(otherQueryS,req.getSearcher(), req.getSchema(),0,10);
+            dbg.add("otherQuery",otherQueryS);
+            dbg.add("explainOther", getExplainList(query, otherResults, req.getSearcher(), req.getSchema()));
+          }
+        } catch (Exception e) {
+          SolrException.logOnce(SolrCore.log,"Exception during debug:",e);
+          dbg.add("exception_during_debug", SolrException.toStr(e));
+        }
+        rsp.add("debug",dbg);
+      }
+
+    } catch (SolrException e) {
+      rsp.setException(e);
+      numErrors++;
+      return;
+    } catch (Exception e) {
+      SolrException.log(SolrCore.log,e);
+      rsp.setException(e);
+      numErrors++;
+      return;
+    }
+  }
+
+  private NamedList getExplainList(Query query, DocList results, SolrIndexSearcher searcher, IndexSchema schema) throws IOException {
+    NamedList explainList = new NamedList();
+    DocIterator iterator = results.iterator();
+    for (int i=0; i<results.size(); i++) {
+      int id = iterator.nextDoc();
+
+      Explanation explain = searcher.explain(query, id);
+      //explainList.add(Integer.toString(id), explain.toString().split("\n"));
+
+      Document doc = searcher.doc(id);
+      String strid = schema.printableUniqueKey(doc);
+      String docname = "";
+      if (strid != null) docname="id="+strid+",";
+      docname = docname + "internal_docid="+id;
+
+      explainList.add(docname, "\n" +explain.toString());
+    }
+    return explainList;
+  }
+
+
+  private DocList doQuery(String sreq, SolrIndexSearcher searcher, IndexSchema schema, int start, int limit) throws IOException {
+    List<String> commands = StrUtils.splitSmart(sreq,';');
+
+    String qs = commands.size() >= 1 ? commands.get(0) : "";
+    Query query = QueryParsing.parseQuery(qs, schema);
+
+    // If the first non-query, non-filter command is a simple sort on an indexed field, then
+    // we can use the Lucene sort ability.
+    Sort sort = null;
+    if (commands.size() >= 2) {
+      QueryParsing.SortSpec sortSpec = QueryParsing.parseSort(commands.get(1), schema);
+      if (sortSpec != null) {
+        sort = sortSpec.getSort();
+        if (sortSpec.getCount() >= 0) {
+          limit = sortSpec.getCount();
+        }
+      }
+    }
+
+    DocList results = searcher.getDocList(query,(DocSet)null, sort, start, limit);
+    return results;
+  }
+
+
+
+  //////////////////////// SolrInfoMBeans methods //////////////////////
+
+
+  public String getName() {
+    return StandardRequestHandler.class.getName();
+  }
+
+  public String getVersion() {
+    return SolrCore.version;
+  }
+
+  public String getDescription() {
+    return "The standard Solr request handler";
+  }
+
+  public Category getCategory() {
+    return Category.QUERYHANDLER;
+  }
+
+  public String getCvsId() {
+    return "$Id: StandardRequestHandler.java,v 1.17 2005/12/02 04:31:06 yonik Exp $";
+  }
+
+  public String getCvsName() {
+    return "$Name:  $";
+  }
+
+  public String getCvsSource() {
+    return "$Source: /cvs/main/searching/solr/solarcore/src/solr/StandardRequestHandler.java,v $";
+  }
+
+  public URL[] getDocs() {
+    return null;
+  }
+
+  public NamedList getStatistics() {
+    NamedList lst = new NamedList();
+    lst.add("requests", numRequests);
+    lst.add("errors", numErrors);
+    return lst;
+  }
+}
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/request/XMLResponseWriter.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/XMLResponseWriter.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/XMLResponseWriter.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/XMLResponseWriter.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.request;
+
+import java.io.Writer;
+import java.io.IOException;
+
+/**
+ * @author yonik
+ * @version $Id: XMLResponseWriter.java,v 1.6 2005/04/24 02:53:35 yonik Exp $
+ */
+
+public class XMLResponseWriter implements QueryResponseWriter {
+  public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
+    XMLWriter.writeResponse(writer,req,rsp);
+  }
+}
+
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/request/XMLResponseWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native