You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by bo...@apache.org on 2015/03/18 00:33:31 UTC

[09/17] incubator-ranger git commit: Support for Solr as Audit Destination.

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/util/ClientUtils.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/util/ClientUtils.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/util/ClientUtils.java
new file mode 100644
index 0000000..f2e4036
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/util/ClientUtils.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.client.solrj.util;
+
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.SolrInputField;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.Base64;
+import org.apache.solr.common.util.ContentStream;
+import org.apache.solr.common.util.ContentStreamBase;
+import org.apache.solr.common.util.DateUtil;
+import org.apache.solr.common.util.XML;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URLEncoder;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+
+/**
+ *
+ * @since solr 1.3
+ */
+public class ClientUtils 
+{
+  // Standard Content types
+  public static final String TEXT_XML = "application/xml; charset=UTF-8";  
+  
+  /**
+   * Take a string and make it an iterable ContentStream
+   */
+  public static Collection<ContentStream> toContentStreams( final String str, final String contentType )
+  {
+    if( str == null )
+      return null;
+
+    ArrayList<ContentStream> streams = new ArrayList<>( 1 );
+    ContentStreamBase ccc = new ContentStreamBase.StringStream( str );
+    ccc.setContentType( contentType );
+    streams.add( ccc );
+    return streams;
+  }
+
+  /**
+   * @param d SolrDocument to convert
+   * @return a SolrInputDocument with the same fields and values as the
+   *   SolrDocument.  All boosts are 1.0f
+   */
+  public static SolrInputDocument toSolrInputDocument( SolrDocument d )
+  {
+    SolrInputDocument doc = new SolrInputDocument();
+    for( String name : d.getFieldNames() ) {
+      doc.addField( name, d.getFieldValue(name), 1.0f );
+    }
+    return doc;
+  }
+
+  /**
+   * @param d SolrInputDocument to convert
+   * @return a SolrDocument with the same fields and values as the SolrInputDocument
+   */
+  public static SolrDocument toSolrDocument(SolrInputDocument d) {
+    SolrDocument doc = new SolrDocument();
+    for (SolrInputField field : d) {
+      doc.setField(field.getName(), field.getValue());
+    }
+    if (d.getChildDocuments() != null) {
+      for (SolrInputDocument in : d.getChildDocuments()) {
+        doc.addChildDocument(toSolrDocument(in));
+      }
+
+    }
+    return doc;
+  }
+
+  //------------------------------------------------------------------------
+  //------------------------------------------------------------------------
+
+  public static void writeXML( SolrInputDocument doc, Writer writer ) throws IOException
+  {
+    writer.write("<doc boost=\""+doc.getDocumentBoost()+"\">");
+
+    for( SolrInputField field : doc ) {
+      float boost = field.getBoost();
+      String name = field.getName();
+
+      for( Object v : field ) {
+        String update = null;
+
+        if (v instanceof Map) {
+          // currently only supports a single value
+          for (Entry<Object,Object> entry : ((Map<Object,Object>)v).entrySet()) {
+            update = entry.getKey().toString();
+            v = entry.getValue();
+            if (v instanceof Collection) {
+              Collection values = (Collection) v;
+              for (Object value : values) {
+                writeVal(writer, boost, name, value, update);
+                boost = 1.0f;
+              }
+            } else  {
+              writeVal(writer, boost, name, v, update);
+              boost = 1.0f;
+            }
+          }
+        } else  {
+          writeVal(writer, boost, name, v, update);
+          // only write the boost for the first multi-valued field
+          // otherwise, the used boost is the product of all the boost values
+          boost = 1.0f;
+        }
+      }
+    }
+
+    if (doc.hasChildDocuments()) {
+      for (SolrInputDocument childDocument : doc.getChildDocuments()) {
+        writeXML(childDocument, writer);
+      }
+    }
+    
+    writer.write("</doc>");
+  }
+
+  private static void writeVal(Writer writer, float boost, String name, Object v, String update) throws IOException {
+    if (v instanceof Date) {
+      v = DateUtil.getThreadLocalDateFormat().format( (Date)v );
+    } else if (v instanceof byte[]) {
+      byte[] bytes = (byte[]) v;
+      v = Base64.byteArrayToBase64(bytes, 0, bytes.length);
+    } else if (v instanceof ByteBuffer) {
+      ByteBuffer bytes = (ByteBuffer) v;
+      v = Base64.byteArrayToBase64(bytes.array(), bytes.position(),bytes.limit() - bytes.position());
+    }
+
+    if (update == null) {
+      if( boost != 1.0f ) {
+        XML.writeXML(writer, "field", v.toString(), "name", name, "boost", boost);
+      } else if (v != null) {
+        XML.writeXML(writer, "field", v.toString(), "name", name );
+      }
+    } else {
+      if( boost != 1.0f ) {
+        XML.writeXML(writer, "field", v.toString(), "name", name, "boost", boost, "update", update);
+      } else {
+        if (v == null)  {
+          XML.writeXML(writer, "field", null, "name", name, "update", update, "null", true);
+        } else  {
+          XML.writeXML(writer, "field", v.toString(), "name", name, "update", update);
+        }
+      }
+    }
+  }
+
+
+  public static String toXML( SolrInputDocument doc )
+  {
+    StringWriter str = new StringWriter();
+    try {
+      writeXML( doc, str );
+    }
+    catch( Exception ex ){}
+    return str.toString();
+  }
+
+  //---------------------------------------------------------------------------------------
+
+  /**
+   * See: <a href="https://www.google.com/?gws_rd=ssl#q=lucene+query+parser+syntax">Lucene query parser syntax</a>
+   * for more information on Escaping Special Characters
+   */
+  // NOTE: its broken to link to any lucene-queryparser.jar docs, not in classpath!!!!!
+  public static String escapeQueryChars(String s) {
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < s.length(); i++) {
+      char c = s.charAt(i);
+      // These characters are part of the query syntax and must be escaped
+      if (c == '\\' || c == '+' || c == '-' || c == '!'  || c == '(' || c == ')' || c == ':'
+        || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
+        || c == '*' || c == '?' || c == '|' || c == '&'  || c == ';' || c == '/'
+        || Character.isWhitespace(c)) {
+        sb.append('\\');
+      }
+      sb.append(c);
+    }
+    return sb.toString();
+  }
+
+  public static String toQueryString( SolrParams params, boolean xml ) {
+    StringBuilder sb = new StringBuilder(128);
+    try {
+      String amp = xml ? "&amp;" : "&";
+      boolean first=true;
+      Iterator<String> names = params.getParameterNamesIterator();
+      while( names.hasNext() ) {
+        String key = names.next();
+        String[] valarr = params.getParams( key );
+        if( valarr == null ) {
+          sb.append( first?"?":amp );
+          sb.append(key);
+          first=false;
+        }
+        else {
+          for (String val : valarr) {
+            sb.append( first? "?":amp );
+            sb.append(key);
+            if( val != null ) {
+              sb.append('=');
+              sb.append( URLEncoder.encode( val, "UTF-8" ) );
+            }
+            first=false;
+          }
+        }
+      }
+    }
+    catch (IOException e) {throw new RuntimeException(e);}  // can't happen
+    return sb.toString();
+  }
+
+  /** Constructs a slices map from a collection of slices and handles disambiguation if multiple collections are being queried simultaneously */
+  public static void addSlices(Map<String,Slice> target, String collectionName, Collection<Slice> slices, boolean multiCollection) {
+    for (Slice slice : slices) {
+      String key = slice.getName();
+      if (multiCollection) key = collectionName + "_" + key;
+      target.put(key, slice);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/util/package-info.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/util/package-info.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/util/package-info.java
new file mode 100644
index 0000000..9464970
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/util/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+/** 
+ * Utilities for Solr client applications.
+ */
+package org.apache.solr.client.solrj.util;
+
+

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/EnumFieldValue.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/EnumFieldValue.java b/ranger_solrj/src/main/java/org/apache/solr/common/EnumFieldValue.java
new file mode 100644
index 0000000..50d1fb0
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/EnumFieldValue.java
@@ -0,0 +1,116 @@
+package org.apache.solr.common;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.Serializable;
+
+/**
+ * Represents a Enum field value, which includes integer value (indicating the sort order) and string (displayed) value.
+ * Note: this class has a natural ordering that is inconsistent with equals
+ */
+
+public final class EnumFieldValue implements Serializable, Comparable<EnumFieldValue> {
+  private final Integer intValue;
+  private final String stringValue;
+
+  @Override
+  public int hashCode() {
+    int result = intValue != null ? intValue.hashCode() : 0;
+    result = 31 * result + (stringValue != null ? stringValue.hashCode() : 0);
+    return result;
+  }
+
+  public EnumFieldValue(Integer intValue, String stringValue) {
+    this.intValue = intValue;
+    this.stringValue = stringValue;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null)
+      return false;
+    if (!(obj instanceof EnumFieldValue))
+      return false;
+
+    EnumFieldValue otherEnumFieldValue = (EnumFieldValue) obj;
+    return equalsIntegers(intValue, otherEnumFieldValue.intValue) && equalStrings(stringValue, otherEnumFieldValue.stringValue);
+  }
+
+  /**
+   * @return string (displayed) value
+   */
+  @Override
+  public String toString() {
+    return stringValue;
+  }
+
+  /**
+   * @return integer value (indicating the sort order)
+   */
+  public Integer toInt() {
+    return intValue;
+  }
+
+  @Override
+  public int compareTo(EnumFieldValue o) {
+    if (o == null)
+      return 1;
+    return compareIntegers(intValue, o.intValue);
+  }
+
+  private boolean equalStrings(String str1, String str2) {
+    if ((str1 == null) && (str2 == null))
+      return true;
+
+    if (str1 == null)
+      return false;
+
+    if (str2 == null)
+      return false;
+
+    return str1.equals(str2);
+  }
+
+  private boolean equalsIntegers(Integer int1, Integer int2) {
+    if ((int1 == null) && (int2 == null))
+      return true;
+
+    if (int1 == null)
+      return false;
+
+    if (int2 == null)
+      return false;
+
+    return int1.equals(int2);
+  }
+
+  private int compareIntegers(Integer int1, Integer int2) {
+    if ((int1 == null) && (int2 == null))
+      return 0;
+
+    if (int1 == null)
+      return -1;
+
+    if (int2 == null)
+      return 1;
+
+    return int1.compareTo(int2);
+  }
+}
+
+

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/SolrDocument.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/SolrDocument.java b/ranger_solrj/src/main/java/org/apache/solr/common/SolrDocument.java
new file mode 100644
index 0000000..b7e4465
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/SolrDocument.java
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.common;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.solr.common.util.NamedList;
+
+
+/**
+ * A concrete representation of a document within a Solr index.  Unlike a lucene
+ * Document, a SolrDocument may have an Object value matching the type defined in
+ * schema.xml
+ * 
+ * For indexing documents, use the SolrInputDocument that contains extra information
+ * for document and field boosting.
+ * 
+ *
+ * @since solr 1.3
+ */
+public class SolrDocument implements Map<String,Object>, Iterable<Map.Entry<String, Object>>, Serializable
+{
+  private final Map<String,Object> _fields;
+  
+  private List<SolrDocument> _childDocuments;
+  
+  public SolrDocument()
+  {
+    _fields = new LinkedHashMap<>();
+  }
+
+  /**
+   * @return a list of field names defined in this document - this Collection is directly backed by this SolrDocument.
+   * @see #keySet
+   */
+  public Collection<String> getFieldNames() {
+    return this.keySet();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Add / Set / Remove Fields
+  ///////////////////////////////////////////////////////////////////
+
+  /**
+   * Remove all fields from the document
+   */
+  @Override
+  public void clear()
+  {
+    _fields.clear();
+
+    if(_childDocuments != null) {
+      _childDocuments.clear();
+    }
+  }
+  
+  /**
+   * Remove all fields with the name
+   */
+  public boolean removeFields(String name) 
+  {
+    return this.remove( name ) != null;
+  }
+
+  /**
+   * Set a field with the given object.  If the object is an Array, it will 
+   * set multiple fields with the included contents.  This will replace any existing 
+   * field with the given name
+   */
+  @SuppressWarnings("unchecked")
+  public void setField(String name, Object value) 
+  {
+    if( value instanceof Object[] ) {
+      value = new ArrayList(Arrays.asList( (Object[])value ));
+    }
+    else if( value instanceof Collection ) {
+      // nothing
+    }
+    else if( value instanceof NamedList ) {
+      // nothing
+    }
+    else if( value instanceof Iterable ) {
+      ArrayList<Object> lst = new ArrayList<>();
+      for( Object o : (Iterable)value ) {
+        lst.add( o );
+      }
+      value = lst;
+    }
+    _fields.put(name, value);
+  }
+  
+  /**
+   * This will add a field to the document.  If fields already exist with this
+   * name it will append value to the collection. If the value is Collection,
+   * each value will be added independently. 
+   * 
+   * The class type of value and the name parameter should match schema.xml. 
+   * schema.xml can be found in conf directory under the solr home by default.
+   * 
+   * @param name Name of the field, should match one of the field names defined under "fields" tag in schema.xml.
+   * @param value Value of the field, should be of same class type as defined by "type" attribute of the corresponding field in schema.xml. 
+   */
+  @SuppressWarnings("unchecked")
+  public void addField(String name, Object value) 
+  { 
+    Object existing = _fields.get(name);
+    if (existing == null) {
+      if( value instanceof Collection ) {
+        Collection<Object> c = new ArrayList<>( 3 );
+        for ( Object o : (Collection<Object>)value ) {
+          c.add(o);
+        }
+        this.setField( name, c );
+      } else {
+        this.setField( name, value );
+      }
+      return;
+    }
+    
+    Collection<Object> vals = null;
+    if( existing instanceof Collection ) {
+      vals = (Collection<Object>)existing;
+    }
+    else {
+      vals = new ArrayList<>( 3 );
+      vals.add( existing );
+    }
+    
+    // Add the values to the collection
+    if( value instanceof Iterable ) {
+      for( Object o : (Iterable<Object>)value ) {
+        vals.add( o );
+      }
+    }
+    else if( value instanceof Object[] ) {
+      for( Object o : (Object[])value ) {
+        vals.add( o );
+      }
+    }
+    else {
+      vals.add( value );
+    }
+    _fields.put( name, vals );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Get the field values
+  ///////////////////////////////////////////////////////////////////
+
+  /**
+   * returns the first value for a field
+   */
+  public Object getFirstValue(String name) {
+    Object v = _fields.get( name );
+    if (v == null || !(v instanceof Collection)) return v;
+    Collection c = (Collection)v;
+    if (c.size() > 0 ) {
+      return c.iterator().next();
+    }
+    return null;
+  }
+  
+  /**
+   * Get the value or collection of values for a given field.  
+   */
+  public Object getFieldValue(String name) {
+    return _fields.get( name );
+  }
+
+  /**
+   * Get a collection of values for a given field name
+   */
+  @SuppressWarnings("unchecked")
+  public Collection<Object> getFieldValues(String name) {
+    Object v = _fields.get( name );
+    if( v instanceof Collection ) {
+      return (Collection<Object>)v;
+    }
+    if( v != null ) {
+      ArrayList<Object> arr = new ArrayList<>(1);
+      arr.add( v );
+      return arr;
+    }
+    return null;
+  }
+    
+  @Override
+  public String toString()
+  {
+    return "SolrDocument"+_fields;
+  }
+
+  /**
+   * Iterate of String-&gt;Object keys
+   */
+  @Override
+  public Iterator<Entry<String, Object>> iterator() {
+    return _fields.entrySet().iterator();
+  }
+
+  //-----------------------------------------------------------------------------------------
+  // JSTL Helpers
+  //-----------------------------------------------------------------------------------------
+  
+  /**
+   * Expose a Map interface to the solr field value collection.
+   */
+  public Map<String,Collection<Object>> getFieldValuesMap()
+  {
+    return new Map<String,Collection<Object>>() {
+      /** Get the field Value */
+      @Override
+      public Collection<Object> get(Object key) { 
+        return getFieldValues( (String)key ); 
+      }
+      
+      // Easily Supported methods
+      @Override
+      public boolean containsKey(Object key) { return _fields.containsKey( key ); }
+      @Override
+      public Set<String>  keySet()           { return _fields.keySet();  }
+      @Override
+      public int          size()             { return _fields.size();    }
+      @Override
+      public boolean      isEmpty()          { return _fields.isEmpty(); }
+
+      // Unsupported operations.  These are not necessary for JSTL
+      @Override
+      public void clear() { throw new UnsupportedOperationException(); }
+      @Override
+      public boolean containsValue(Object value) {throw new UnsupportedOperationException();}
+      @Override
+      public Set<java.util.Map.Entry<String, Collection<Object>>> entrySet() {throw new UnsupportedOperationException();}
+      @Override
+      public void putAll(Map<? extends String, ? extends Collection<Object>> t) {throw new UnsupportedOperationException();}
+      @Override
+      public Collection<Collection<Object>> values() {throw new UnsupportedOperationException();}
+      @Override
+      public Collection<Object> put(String key, Collection<Object> value) {throw new UnsupportedOperationException();}
+      @Override
+      public Collection<Object> remove(Object key) {throw new UnsupportedOperationException();}
+      @Override
+      public String toString() {return _fields.toString();}
+    };
+  }
+
+  /**
+   * Expose a Map interface to the solr fields.  This function is useful for JSTL
+   */
+  public Map<String,Object> getFieldValueMap() {
+    return new Map<String,Object>() {
+      /** Get the field Value */
+      @Override
+      public Object get(Object key) { 
+        return getFirstValue( (String)key ); 
+      }
+      
+      // Easily Supported methods
+      @Override
+      public boolean containsKey(Object key) { return _fields.containsKey( key ); }
+      @Override
+      public Set<String>  keySet()           { return _fields.keySet();  }
+      @Override
+      public int          size()             { return _fields.size();    }
+      @Override
+      public boolean      isEmpty()          { return _fields.isEmpty(); }
+
+      // Unsupported operations.  These are not necessary for JSTL
+      @Override
+      public void clear() { throw new UnsupportedOperationException(); }
+      @Override
+      public boolean containsValue(Object value) {throw new UnsupportedOperationException();}
+      @Override
+      public Set<java.util.Map.Entry<String, Object>> entrySet() {throw new UnsupportedOperationException();}
+      @Override
+      public void putAll(Map<? extends String, ? extends Object> t) {throw new UnsupportedOperationException();}
+      @Override
+      public Collection<Object> values() {throw new UnsupportedOperationException();}
+      @Override
+      public Collection<Object> put(String key, Object value) {throw new UnsupportedOperationException();}
+      @Override
+      public Collection<Object> remove(Object key) {throw new UnsupportedOperationException();}      
+      @Override
+      public String toString() {return _fields.toString();}
+   };
+  }
+
+  //---------------------------------------------------
+  // MAP interface
+  //---------------------------------------------------
+
+  @Override
+  public boolean containsKey(Object key) {
+    return _fields.containsKey(key);
+  }
+
+  @Override
+  public boolean containsValue(Object value) {
+    return _fields.containsValue(value);
+  }
+
+  @Override
+  public Set<Entry<String, Object>> entrySet() {
+    return _fields.entrySet();
+  }
+  //TODO: Shouldn't the input parameter here be a String?  The _fields map requires a String.
+  @Override
+  public Object get(Object key) {
+    return _fields.get(key);
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return _fields.isEmpty();
+  }
+
+  @Override
+  public Set<String> keySet() {
+    return _fields.keySet();
+  }
+
+  @Override
+  public Object put(String key, Object value) {
+    return _fields.put(key, value);
+  }
+
+  @Override
+  public void putAll(Map<? extends String, ? extends Object> t) {
+    _fields.putAll( t );
+  }
+
+  @Override
+  public Object remove(Object key) {
+    return _fields.remove(key);
+  }
+
+  @Override
+  public int size() {
+    return _fields.size();
+  }
+
+  @Override
+  public Collection<Object> values() {
+    return _fields.values();
+  }
+  
+  public void addChildDocument(SolrDocument child) {
+    if (_childDocuments == null) {
+      _childDocuments = new ArrayList<>();
+    }
+     _childDocuments.add(child);
+   }
+   
+   public void addChildDocuments(Collection<SolrDocument> childs) {
+     for (SolrDocument child : childs) {
+       addChildDocument(child);
+     }
+   }
+
+   /** Returns the list of child documents, or null if none. */
+   public List<SolrDocument> getChildDocuments() {
+     return _childDocuments;
+   }
+   
+   public boolean hasChildDocuments() {
+     boolean isEmpty = (_childDocuments == null || _childDocuments.isEmpty());
+     return !isEmpty;
+   }
+
+  public int getChildDocumentCount() {
+    return _childDocuments.size();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/SolrDocumentList.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/SolrDocumentList.java b/ranger_solrj/src/main/java/org/apache/solr/common/SolrDocumentList.java
new file mode 100644
index 0000000..d803e7d
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/SolrDocumentList.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.common;
+
+import java.util.ArrayList;
+
+
+/**
+ * Represent a list of SolrDocuments returned from a search.  This includes
+ * position and offset information.
+ * 
+ *
+ * @since solr 1.3
+ */
+public class SolrDocumentList extends ArrayList<SolrDocument>
+{ 
+  private long numFound = 0;
+  private long start = 0;
+  private Float maxScore = null;
+  
+  public Float getMaxScore() {
+    return maxScore;
+  }
+  
+  public void setMaxScore(Float maxScore) {
+    this.maxScore = maxScore;
+  }
+  
+  public long getNumFound() {
+    return numFound;
+  }
+  
+  public void setNumFound(long numFound) {
+    this.numFound = numFound;
+  }
+  
+  public long getStart() {
+    return start;
+  }
+  
+  public void setStart(long start) {
+    this.start = start;
+  }
+
+  @Override
+  public String toString() {
+    return "{numFound="+numFound
+            +",start="+start
+            + (maxScore!=null ? ",maxScore="+maxScore : "")
+            +",docs="+super.toString()
+            +"}";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/SolrException.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/SolrException.java b/ranger_solrj/src/main/java/org/apache/solr/common/SolrException.java
new file mode 100644
index 0000000..02bbc04
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/SolrException.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.common;
+
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.solr.common.util.NamedList;
+import org.slf4j.Logger;
+
+/**
+ *
+ */
+public class SolrException extends RuntimeException {
+
+  /**
+   * This list of valid HTTP Status error codes that Solr may return in 
+   * the case of a "Server Side" error.
+   *
+   * @since solr 1.2
+   */
+  public enum ErrorCode {
+    BAD_REQUEST( 400 ),
+    UNAUTHORIZED( 401 ),
+    FORBIDDEN( 403 ),
+    NOT_FOUND( 404 ),
+    CONFLICT( 409 ),
+    UNSUPPORTED_MEDIA_TYPE( 415 ),
+    SERVER_ERROR( 500 ),
+    SERVICE_UNAVAILABLE( 503 ),
+    INVALID_STATE( 510 ),
+    UNKNOWN(0);
+    public final int code;
+    
+    private ErrorCode( int c )
+    {
+      code = c;
+    }
+    public static ErrorCode getErrorCode(int c){
+      for (ErrorCode err : values()) {
+        if(err.code == c) return err;
+      }
+      return UNKNOWN;
+    }
+  };
+
+  public SolrException(ErrorCode code, String msg) {
+    super(msg);
+    this.code = code.code;
+  }
+  public SolrException(ErrorCode code, String msg, Throwable th) {
+    super(msg, th);
+    this.code = code.code;
+  }
+
+  public SolrException(ErrorCode code, Throwable th) {
+    super(th);
+    this.code = code.code;
+  }
+
+  /**
+   * Constructor that can set arbitrary http status code. Not for 
+   * use in Solr, but may be used by clients in subclasses to capture 
+   * errors returned by the servlet container or other HTTP proxies.
+   */
+  protected SolrException(int code, String msg, Throwable th) {
+    super(msg, th);
+    this.code = code;
+  }
+  
+  int code=0;
+  protected NamedList<String> metadata;
+
+  /**
+   * The HTTP Status code associated with this Exception.  For SolrExceptions 
+   * thrown by Solr "Server Side", this should valid {@link ErrorCode}, 
+   * however client side exceptions may contain an arbitrary error code based 
+   * on the behavior of the Servlet Container hosting Solr, or any HTTP 
+   * Proxies that may exist between the client and the server.
+   *
+   * @return The HTTP Status code associated with this Exception
+   */
+  public int code() { return code; }
+
+  public void setMetadata(NamedList<String> metadata) {
+    this.metadata = metadata;
+  }
+
+  public NamedList<String> getMetadata() {
+    return metadata;
+  }
+
+  public String getMetadata(String key) {
+    return (metadata != null && key != null) ? metadata.get(key) : null;
+  }
+
+  public void setMetadata(String key, String value) {
+    if (key == null || value == null)
+      throw new IllegalArgumentException("Exception metadata cannot be null!");
+
+    if (metadata == null)
+      metadata = new NamedList<String>();
+    metadata.add(key, value);
+  }
+
+  public void log(Logger log) { log(log,this); }
+  public static void log(Logger log, Throwable e) {
+    String stackTrace = toStr(e);
+    String ignore = doIgnore(e, stackTrace);
+    if (ignore != null) {
+      log.info(ignore);
+      return;
+    }
+    log.error(stackTrace);
+
+  }
+
+  public static void log(Logger log, String msg, Throwable e) {
+    String stackTrace = msg + ':' + toStr(e);
+    String ignore = doIgnore(e, stackTrace);
+    if (ignore != null) {
+      log.info(ignore);
+      return;
+    }
+    log.error(stackTrace);
+  }
+  
+  public static void log(Logger log, String msg) {
+    String stackTrace = msg;
+    String ignore = doIgnore(null, stackTrace);
+    if (ignore != null) {
+      log.info(ignore);
+      return;
+    }
+    log.error(stackTrace);
+  }
+
+  // public String toString() { return toStr(this); }  // oops, inf loop
+  @Override
+  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();
+**/
+  }
+
+
+  /** For test code - do not log exceptions that match any of the regular expressions in ignorePatterns */
+  public static Set<String> ignorePatterns;
+
+  /** Returns null if this exception does not match any ignore patterns, or a message string to use if it does. */
+  public static String doIgnore(Throwable t, String m) {
+    if (ignorePatterns == null || m == null) return null;
+    if (t != null && t instanceof AssertionError) return null;
+
+    for (String regex : ignorePatterns) {
+      Pattern pattern = Pattern.compile(regex);
+      Matcher matcher = pattern.matcher(m);
+      
+      if (matcher.find()) return "Ignoring exception matching " + regex;
+    }
+
+    return null;
+  }
+  
+  public static Throwable getRootCause(Throwable t) {
+    while (true) {
+      Throwable cause = t.getCause();
+      if (cause!=null) {
+        t = cause;
+      } else {
+        break;
+      }
+    }
+    return t;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/SolrInputDocument.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/SolrInputDocument.java b/ranger_solrj/src/main/java/org/apache/solr/common/SolrInputDocument.java
new file mode 100644
index 0000000..a51efbf
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/SolrInputDocument.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.common;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represent the field and boost information needed to construct and index
+ * a Lucene Document.  Like the SolrDocument, the field values should
+ * match those specified in schema.xml 
+ *
+ *
+ * @since solr 1.3
+ */
+public class SolrInputDocument implements Map<String,SolrInputField>, Iterable<SolrInputField>, Serializable
+{
+  private final Map<String,SolrInputField> _fields;
+  private float _documentBoost = 1.0f;
+  private List<SolrInputDocument> _childDocuments;
+  
+  public SolrInputDocument() {
+    _fields = new LinkedHashMap<>();
+  }
+  
+  public SolrInputDocument(Map<String,SolrInputField> fields) {
+    _fields = fields;
+  }
+  
+  /**
+   * Remove all fields and boosts from the document
+   */
+  @Override
+  public void clear()
+  {
+    if( _fields != null ) {
+      _fields.clear();      
+    }
+    _childDocuments = null;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Add / Set fields
+  ///////////////////////////////////////////////////////////////////
+
+  /** 
+   * Add a field with implied null value for boost.
+   * 
+   * The class type of value and the name parameter should match schema.xml. 
+   * schema.xml can be found in conf directory under the solr home by default.
+   * 
+   * @param name Name of the field, should match one of the field names defined under "fields" tag in schema.xml.
+   * @param value Value of the field, should be of same class type as defined by "type" attribute of the corresponding field in schema.xml. 
+   * @see #addField(String, Object, float)
+   */
+  public void addField(String name, Object value) 
+  {
+    addField(name, value, 1.0f );
+  }
+  
+  /** Get the first value for a field.
+   * 
+   * @param name name of the field to fetch
+   * @return first value of the field or null if not present
+   */
+  public Object getFieldValue(String name) 
+  {
+    SolrInputField field = getField(name);
+    Object o = null;
+    if (field!=null) o = field.getFirstValue();
+    return o;
+  }
+  
+  /** Get all the values for a field.
+   * 
+   * @param name name of the field to fetch
+   * @return value of the field or null if not set
+   */
+  public Collection<Object> getFieldValues(String name) 
+  {
+    SolrInputField field = getField(name);
+    if (field!=null) {
+      return field.getValues();
+    }
+    return null;
+  } 
+  
+  /** Get all field names.
+   * 
+   * @return Set of all field names.
+   */
+  public Collection<String> getFieldNames() 
+  {
+    return _fields.keySet();
+  }
+  
+  /** Set a field with implied null value for boost.
+   * 
+   * @see #setField(String, Object, float)
+   * @param name name of the field to set
+   * @param value value of the field
+   */
+  public void setField(String name, Object value) 
+  {
+    setField(name, value, 1.0f );
+  }
+  
+  public void setField(String name, Object value, float boost ) 
+  {
+    SolrInputField field = new SolrInputField( name );
+    _fields.put( name, field );
+    field.setValue( value, boost );
+  }
+
+  /**
+   * Adds a field with the given name, value and boost.  If a field with the
+   * name already exists, then the given value is appended to the value of that
+   * field, with the new boost. If the value is a collection, then each of its
+   * values will be added to the field.
+   *
+   * The class type of value and the name parameter should match schema.xml. 
+   * schema.xml can be found in conf directory under the solr home by default.
+   * 
+   * @param name Name of the field, should match one of the field names defined under "fields" tag in schema.xml.
+   * @param value Value of the field, should be of same class type as defined by "type" attribute of the corresponding field in schema.xml. 
+   * @param boost Boost value for the field
+   */
+  public void addField(String name, Object value, float boost ) 
+  {
+    SolrInputField field = _fields.get( name );
+    if( field == null || field.value == null ) {
+      setField(name, value, boost);
+    }
+    else {
+      field.addValue( value, boost );
+    }
+  }
+
+  /**
+   * Remove a field from the document
+   * 
+   * @param name The field name whose field is to be removed from the document
+   * @return the previous field with <tt>name</tt>, or
+   *         <tt>null</tt> if there was no field for <tt>key</tt>.
+   */
+  public SolrInputField removeField(String name) {
+    return _fields.remove( name );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Get the field values
+  ///////////////////////////////////////////////////////////////////
+
+  public SolrInputField getField( String field )
+  {
+    return _fields.get( field );
+  }
+
+  @Override
+  public Iterator<SolrInputField> iterator() {
+    return _fields.values().iterator();
+  }
+  
+  public float getDocumentBoost() {
+    return _documentBoost;
+  }
+
+  public void setDocumentBoost(float documentBoost) {
+    _documentBoost = documentBoost;
+  }
+  
+  @Override
+  public String toString()
+  {
+    return "SolrInputDocument(fields: " + _fields.values()
+        + ( _childDocuments == null ? "" : (", children: " + _childDocuments) )
+        + ")";
+  }
+  
+  public SolrInputDocument deepCopy() {
+    SolrInputDocument clone = new SolrInputDocument();
+    Set<Entry<String,SolrInputField>> entries = _fields.entrySet();
+    for (Map.Entry<String,SolrInputField> fieldEntry : entries) {
+      clone._fields.put(fieldEntry.getKey(), fieldEntry.getValue().deepCopy());
+    }
+    clone._documentBoost = _documentBoost;
+
+    if (_childDocuments != null) {
+      clone._childDocuments = new ArrayList<>(_childDocuments.size());
+      for (SolrInputDocument child : _childDocuments) {
+        clone._childDocuments.add(child.deepCopy());
+      }
+    }
+    
+    return clone;
+  }
+
+  //---------------------------------------------------
+  // MAP interface
+  //---------------------------------------------------
+
+  @Override
+  public boolean containsKey(Object key) {
+    return _fields.containsKey(key);
+  }
+
+  @Override
+  public boolean containsValue(Object value) {
+    return _fields.containsValue(value);
+  }
+
+  @Override
+  public Set<Entry<String, SolrInputField>> entrySet() {
+    return _fields.entrySet();
+  }
+
+  @Override
+  public SolrInputField get(Object key) {
+    return _fields.get(key);
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return _fields.isEmpty();
+  }
+
+  @Override
+  public Set<String> keySet() {
+    return _fields.keySet();
+  }
+
+  @Override
+  public SolrInputField put(String key, SolrInputField value) {
+    return _fields.put(key, value);
+  }
+
+  @Override
+  public void putAll(Map<? extends String, ? extends SolrInputField> t) {
+    _fields.putAll( t );
+  }
+
+  @Override
+  public SolrInputField remove(Object key) {
+    return _fields.remove(key);
+  }
+
+  @Override
+  public int size() {
+    return _fields.size();
+  }
+
+  @Override
+  public Collection<SolrInputField> values() {
+    return _fields.values();
+  }
+
+  public void addChildDocument(SolrInputDocument child) {
+   if (_childDocuments == null) {
+     _childDocuments = new ArrayList<>();
+   }
+    _childDocuments.add(child);
+  }
+  
+  public void addChildDocuments(Collection<SolrInputDocument> childs) {
+    for (SolrInputDocument child : childs) {
+      addChildDocument(child);
+    }
+  }
+
+  /** Returns the list of child documents, or null if none. */
+  public List<SolrInputDocument> getChildDocuments() {
+    return _childDocuments;
+  }
+  
+  public boolean hasChildDocuments() {
+    boolean isEmpty = (_childDocuments == null || _childDocuments.isEmpty());
+    return !isEmpty;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/SolrInputField.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/SolrInputField.java b/ranger_solrj/src/main/java/org/apache/solr/common/SolrInputField.java
new file mode 100644
index 0000000..02b6856
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/SolrInputField.java
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.common;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ *
+ * @since solr 1.3
+ */
+public class SolrInputField implements Iterable<Object>, Serializable
+{
+  String name;
+  Object value = null; 
+  float boost = 1.0f;
+  
+  public SolrInputField( String n )
+  {
+    this.name = n;
+  }
+
+  //---------------------------------------------------------------
+  //---------------------------------------------------------------
+
+  /**
+   * Set the value for a field.  Arrays will be converted to a collection. If
+   * a collection is given, then that collection will be used as the backing
+   * collection for the values.
+   */
+  public void setValue(Object v, float b) {
+    boost = b;
+
+    if( v instanceof Object[] ) {
+      Object[] arr = (Object[])v;
+      Collection<Object> c = new ArrayList<>( arr.length );
+      for( Object o : arr ) {
+        c.add( o );
+      }
+      value = c;
+    }
+    else {
+      value = v;
+    }
+  }
+
+  /**
+   * Add values to a field.  If the added value is a collection, each value
+   * will be added individually.
+   */
+  @SuppressWarnings("unchecked")
+  public void addValue(Object v, float b) {
+    if( value == null ) {
+      if ( v instanceof Collection ) {
+        Collection<Object> c = new ArrayList<>( 3 );
+        for ( Object o : (Collection<Object>)v ) {
+          c.add( o );
+        }
+        setValue(c, b);
+      } else {
+        setValue(v, b);
+      }
+
+      return;
+    }
+    
+    // The lucene API and solr XML field specification make it possible to set boosts
+    // on multi-value fields even though lucene indexing does not support this.
+    // To keep behavior consistent with what happens in the lucene index, we accumulate
+    // the product of all boosts specified for this field.
+    boost *= b;
+    
+    Collection<Object> vals = null;
+    if( value instanceof Collection ) {
+      vals = (Collection<Object>)value;
+    }
+    else {
+      vals = new ArrayList<>( 3 );
+      vals.add( value );
+      value = vals;
+    }
+    
+    // Add the new values to a collection
+    if( v instanceof Iterable ) {
+      for( Object o : (Iterable<Object>)v ) {
+        vals.add( o );
+      }
+    }
+    else if( v instanceof Object[] ) {
+      for( Object o : (Object[])v ) {
+        vals.add( o );
+      }
+    }
+    else {
+      vals.add( v );
+    }
+  }
+
+  //---------------------------------------------------------------
+  //---------------------------------------------------------------
+  
+  @SuppressWarnings("unchecked")
+  public Object getFirstValue() {
+    if( value instanceof Collection ) {
+      Collection c = (Collection<Object>)value;
+      if( c.size() > 0 ) {
+        return c.iterator().next();
+      }
+      return null;
+    }
+    return value;
+  }
+
+  /**
+   * @return the value for this field.  If the field has multiple values, this
+   * will be a collection.
+   */
+  public Object getValue() {
+    return value;
+  }
+
+  /**
+   * @return the values for this field.  This will return a collection even
+   * if the field is not multi-valued
+   */
+  @SuppressWarnings("unchecked")
+  public Collection<Object> getValues() {
+    if( value instanceof Collection ) {
+      return (Collection<Object>)value;
+    }
+    if( value != null ) {
+      Collection<Object> vals = new ArrayList<>(1);
+      vals.add( value );
+      return vals;
+    }
+    return null;
+  }
+
+  /**
+   * @return the number of values for this field
+   */
+  public int getValueCount() {
+    if( value instanceof Collection ) {
+      return ((Collection)value).size();
+    }
+    return (value == null) ? 0 : 1;
+  }
+  
+  //---------------------------------------------------------------
+  //---------------------------------------------------------------
+  
+  public float getBoost() {
+    return boost;
+  }
+
+  public void setBoost(float boost) {
+    this.boost = boost;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public Iterator<Object> iterator() {
+    if( value instanceof Collection ) {
+      return ((Collection)value).iterator();
+    }
+    return new Iterator<Object>() {
+      boolean nxt = (value!=null);
+      
+      @Override
+      public boolean hasNext() {
+        return nxt;
+      }
+
+      @Override
+      public Object next() {
+        nxt = false;
+        return value;
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+
+  @Override
+  public String toString()
+  {
+    return name + ((boost == 1.0) ? "=" : ("("+boost+")=")) + value;
+  }
+
+  public SolrInputField deepCopy() {
+    SolrInputField clone = new SolrInputField(name);
+    clone.boost = boost;
+    // We can't clone here, so we rely on simple primitives
+    if (value instanceof Collection) {
+      Collection<Object> values = (Collection<Object>) value;
+      Collection<Object> cloneValues = new ArrayList<>(values.size());
+      cloneValues.addAll(values);
+      clone.value = cloneValues;
+    } else {
+      clone.value = value;
+    }
+    return clone;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/StringUtils.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/StringUtils.java b/ranger_solrj/src/main/java/org/apache/solr/common/StringUtils.java
new file mode 100644
index 0000000..00fef63
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/StringUtils.java
@@ -0,0 +1,26 @@
+package org.apache.solr.common;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class StringUtils {
+  
+  public static boolean isEmpty(String s) {
+    return (s == null) || s.isEmpty();
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/cloud/Aliases.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/cloud/Aliases.java b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/Aliases.java
new file mode 100644
index 0000000..1d18323
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/Aliases.java
@@ -0,0 +1,63 @@
+package org.apache.solr.common.cloud;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Aliases {
+
+  private Map<String,Map<String,String>> aliasMap;
+
+  public Aliases(Map<String,Map<String,String>> aliasMap) {
+    this.aliasMap = aliasMap;
+  }
+
+  public Aliases() {
+    this.aliasMap = new HashMap<>();
+  }
+  
+  public Map<String,String> getCollectionAliasMap() {
+    Map<String,String> cam = aliasMap.get("collection");
+    if (cam == null) return null;
+    return Collections.unmodifiableMap(cam);
+  }
+  
+  public Map<String,Map<String,String>> getAliasMap() {
+    return Collections.unmodifiableMap(aliasMap);
+  }
+
+  public int collectionAliasSize() {
+    Map<String,String> cam = aliasMap.get("collection");
+    if (cam == null) return 0;
+    return cam.size();
+  }
+  
+  @Override
+  public String toString() {
+    return "Aliases [aliasMap=" + aliasMap + "]";
+  }
+
+  public String getCollectionAlias(String collectionName) {
+    Map<String,String> cam = aliasMap.get("collection");
+    if (cam == null) return null;
+    return cam.get(collectionName);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/cloud/BeforeReconnect.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/cloud/BeforeReconnect.java b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/BeforeReconnect.java
new file mode 100644
index 0000000..44379a4
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/BeforeReconnect.java
@@ -0,0 +1,22 @@
+package org.apache.solr.common.cloud;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+public interface BeforeReconnect {
+  public void command();
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClosableThread.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClosableThread.java b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClosableThread.java
new file mode 100644
index 0000000..1a19cbd
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClosableThread.java
@@ -0,0 +1,27 @@
+package org.apache.solr.common.cloud;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @deprecated because this class is no longer used internally and will be removed
+ */
+@Deprecated
+public interface ClosableThread {
+  public void close();
+  public boolean isClosed();
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClusterState.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClusterState.java b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClusterState.java
new file mode 100644
index 0000000..1b14360
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClusterState.java
@@ -0,0 +1,397 @@
+package org.apache.solr.common.cloud;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.noggit.JSONWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Immutable state of the cloud. Normally you can get the state by using
+ * {@link ZkStateReader#getClusterState()}.
+ * @lucene.experimental
+ */
+public class ClusterState implements JSONWriter.Writable {
+  private static Logger log = LoggerFactory.getLogger(ClusterState.class);
+  
+  private final Integer znodeVersion;
+  
+  private final Map<String, CollectionRef> collectionStates;
+  private Set<String> liveNodes;
+
+  /**
+   * Use this constr when ClusterState is meant for consumption.
+   */
+  public ClusterState(Integer znodeVersion, Set<String> liveNodes,
+      Map<String, DocCollection> collectionStates) {
+    this(liveNodes, getRefMap(collectionStates),znodeVersion);
+  }
+
+  private static Map<String, CollectionRef> getRefMap(Map<String, DocCollection> collectionStates) {
+    Map<String, CollectionRef> collRefs =  new LinkedHashMap<>(collectionStates.size());
+    for (Entry<String, DocCollection> entry : collectionStates.entrySet()) {
+      final DocCollection c = entry.getValue();
+      collRefs.put(entry.getKey(), new CollectionRef(c));
+    }
+    return collRefs;
+  }
+
+  /**Use this if all the collection states are not readily available and some needs to be lazily loaded
+   */
+  public ClusterState(Set<String> liveNodes, Map<String, CollectionRef> collectionStates, Integer znodeVersion){
+    this.znodeVersion = znodeVersion;
+    this.liveNodes = new HashSet<>(liveNodes.size());
+    this.liveNodes.addAll(liveNodes);
+    this.collectionStates = new LinkedHashMap<>(collectionStates);
+  }
+
+
+  /**
+   * Returns a new cluster state object modified with the given collection.
+   *
+   * @param collectionName the name of the modified (or deleted) collection
+   * @param collection     the collection object. A null value deletes the collection from the state
+   * @return the updated cluster state which preserves the current live nodes and zk node version
+   */
+  public ClusterState copyWith(String collectionName, DocCollection collection) {
+    ClusterState result = new ClusterState(liveNodes, new LinkedHashMap<>(collectionStates), znodeVersion);
+    if (collection == null) {
+      result.collectionStates.remove(collectionName);
+    } else {
+      result.collectionStates.put(collectionName, new CollectionRef(collection));
+    }
+    return result;
+  }
+
+
+  /**
+   * Get the lead replica for specific collection, or null if one currently doesn't exist.
+   */
+  public Replica getLeader(String collection, String sliceName) {
+    DocCollection coll = getCollectionOrNull(collection);
+    if (coll == null) return null;
+    Slice slice = coll.getSlice(sliceName);
+    if (slice == null) return null;
+    return slice.getLeader();
+  }
+  private Replica getReplica(DocCollection coll, String replicaName) {
+    if (coll == null) return null;
+    for (Slice slice : coll.getSlices()) {
+      Replica replica = slice.getReplica(replicaName);
+      if (replica != null) return replica;
+    }
+    return null;
+  }
+
+  public boolean hasCollection(String coll) {
+    return  collectionStates.containsKey(coll) ;
+  }
+
+  /**
+   * Gets the replica by the core name (assuming the slice is unknown) or null if replica is not found.
+   * If the slice is known, do not use this method.
+   * coreNodeName is the same as replicaName
+   */
+  public Replica getReplica(final String collection, final String coreNodeName) {
+    return getReplica(getCollectionOrNull(collection), coreNodeName);
+  }
+
+  /**
+   * Get the named Slice for collection, or null if not found.
+   */
+  public Slice getSlice(String collection, String sliceName) {
+    DocCollection coll = getCollectionOrNull(collection);
+    if (coll == null) return null;
+    return coll.getSlice(sliceName);
+  }
+
+  public Map<String, Slice> getSlicesMap(String collection) {
+    DocCollection coll = getCollectionOrNull(collection);
+    if (coll == null) return null;
+    return coll.getSlicesMap();
+  }
+  
+  public Map<String, Slice> getActiveSlicesMap(String collection) {
+    DocCollection coll = getCollectionOrNull(collection);
+    if (coll == null) return null;
+    return coll.getActiveSlicesMap();
+  }
+
+  public Collection<Slice> getSlices(String collection) {
+    DocCollection coll = getCollectionOrNull(collection);
+    if (coll == null) return null;
+    return coll.getSlices();
+  }
+
+  public Collection<Slice> getActiveSlices(String collection) {
+    DocCollection coll = getCollectionOrNull(collection);
+    if (coll == null) return null;
+    return coll.getActiveSlices();
+  }
+
+
+  /**
+   * Get the named DocCollection object, or throw an exception if it doesn't exist.
+   */
+  public DocCollection getCollection(String collection) {
+    DocCollection coll = getCollectionOrNull(collection);
+    if (coll == null) throw new SolrException(ErrorCode.BAD_REQUEST, "Could not find collection : " + collection);
+    return coll;
+  }
+
+  public CollectionRef getCollectionRef(String coll) {
+    return  collectionStates.get(coll);
+  }
+
+  public DocCollection getCollectionOrNull(String coll) {
+    CollectionRef ref = collectionStates.get(coll);
+    return ref == null? null:ref.get();
+  }
+
+  /**
+   * Get collection names.
+   */
+  public Set<String> getCollections() {
+    return collectionStates.keySet();
+  }
+
+
+  /**
+   * Get names of the currently live nodes.
+   */
+  public Set<String> getLiveNodes() {
+    return Collections.unmodifiableSet(liveNodes);
+  }
+
+  public String getShardId(String nodeName, String coreName) {
+    return getShardId(null, nodeName, coreName);
+  }
+
+  public String getShardId(String collectionName, String nodeName, String coreName) {
+    Collection<CollectionRef> states = collectionStates.values();
+    if (collectionName != null) {
+      CollectionRef c = collectionStates.get(collectionName);
+      if (c != null) states = Collections.singletonList( c );
+    }
+
+    for (CollectionRef ref : states) {
+      DocCollection coll = ref.get();
+      if(coll == null) continue;// this collection go tremoved in between, skip
+      for (Slice slice : coll.getSlices()) {
+        for (Replica replica : slice.getReplicas()) {
+          // TODO: for really large clusters, we could 'index' on this
+          String rnodeName = replica.getStr(ZkStateReader.NODE_NAME_PROP);
+          String rcore = replica.getStr(ZkStateReader.CORE_NAME_PROP);
+          if (nodeName.equals(rnodeName) && coreName.equals(rcore)) {
+            return slice.getName();
+          }
+        }
+      }
+    }
+    return null;
+  }
+  
+  /**
+   * Check if node is alive. 
+   */
+  public boolean liveNodesContain(String name) {
+    return liveNodes.contains(name);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("live nodes:" + liveNodes);
+    sb.append(" collections:" + collectionStates);
+    return sb.toString();
+  }
+
+  public static ClusterState load(Integer version, byte[] bytes, Set<String> liveNodes) {
+    return load(version, bytes, liveNodes, ZkStateReader.CLUSTER_STATE);
+  }
+  /**
+   * Create ClusterState from json string that is typically stored in zookeeper.
+   * 
+   * @param version zk version of the clusterstate.json file (bytes)
+   * @param bytes clusterstate.json as a byte array
+   * @param liveNodes list of live nodes
+   * @return the ClusterState
+   */
+  public static ClusterState load(Integer version, byte[] bytes, Set<String> liveNodes, String znode) {
+    // System.out.println("######## ClusterState.load:" + (bytes==null ? null : new String(bytes)));
+    if (bytes == null || bytes.length == 0) {
+      return new ClusterState(version, liveNodes, Collections.<String, DocCollection>emptyMap());
+    }
+    Map<String, Object> stateMap = (Map<String, Object>) ZkStateReader.fromJSON(bytes);
+    Map<String,CollectionRef> collections = new LinkedHashMap<>(stateMap.size());
+    for (Entry<String, Object> entry : stateMap.entrySet()) {
+      String collectionName = entry.getKey();
+      DocCollection coll = collectionFromObjects(collectionName, (Map<String,Object>)entry.getValue(), version, znode);
+      collections.put(collectionName, new CollectionRef(coll));
+    }
+
+    return new ClusterState( liveNodes, collections,version);
+  }
+
+
+  public static Aliases load(byte[] bytes) {
+    if (bytes == null || bytes.length == 0) {
+      return new Aliases();
+    }
+    Map<String,Map<String,String>> aliasMap = (Map<String,Map<String,String>>) ZkStateReader.fromJSON(bytes);
+
+    return new Aliases(aliasMap);
+  }
+
+  private static DocCollection collectionFromObjects(String name, Map<String, Object> objs, Integer version, String znode) {
+    Map<String,Object> props;
+    Map<String,Slice> slices;
+
+    Map<String,Object> sliceObjs = (Map<String,Object>)objs.get(DocCollection.SHARDS);
+    if (sliceObjs == null) {
+      // legacy format from 4.0... there was no separate "shards" level to contain the collection shards.
+      slices = makeSlices(objs);
+      props = Collections.emptyMap();
+    } else {
+      slices = makeSlices(sliceObjs);
+      props = new HashMap<>(objs);
+      objs.remove(DocCollection.SHARDS);
+    }
+
+    Object routerObj = props.get(DocCollection.DOC_ROUTER);
+    DocRouter router;
+    if (routerObj == null) {
+      router = DocRouter.DEFAULT;
+    } else if (routerObj instanceof String) {
+      // back compat with Solr4.4
+      router = DocRouter.getDocRouter((String)routerObj);
+    } else {
+      Map routerProps = (Map)routerObj;
+      router = DocRouter.getDocRouter((String) routerProps.get("name"));
+    }
+
+    return new DocCollection(name, slices, props, router, version, znode);
+  }
+
+  private static Map<String,Slice> makeSlices(Map<String,Object> genericSlices) {
+    if (genericSlices == null) return Collections.emptyMap();
+    Map<String,Slice> result = new LinkedHashMap<>(genericSlices.size());
+    for (Map.Entry<String,Object> entry : genericSlices.entrySet()) {
+      String name = entry.getKey();
+      Object val = entry.getValue();
+      if (val instanceof Slice) {
+        result.put(name, (Slice)val);
+      } else if (val instanceof Map) {
+        result.put(name, new Slice(name, null, (Map<String,Object>)val));
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public void write(JSONWriter jsonWriter) {
+    LinkedHashMap<String , DocCollection> map = new LinkedHashMap<>();
+    for (Entry<String, CollectionRef> e : collectionStates.entrySet()) {
+      // using this class check to avoid fetching from ZK in case of lazily loaded collection
+      if (e.getValue().getClass() == CollectionRef.class) {
+        // check if it is a lazily loaded collection outside of clusterstate.json
+        DocCollection coll = e.getValue().get();
+        if (coll.getStateFormat() == 1) {
+          map.put(coll.getName(),coll);
+        }
+      }
+    }
+    jsonWriter.write(map);
+  }
+
+  /**
+   * The version of clusterstate.json in ZooKeeper.
+   * 
+   * @return null if ClusterState was created for publication, not consumption
+   */
+  public Integer getZkClusterStateVersion() {
+    return znodeVersion;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result
+        + ((znodeVersion == null) ? 0 : znodeVersion.hashCode());
+    result = prime * result + ((liveNodes == null) ? 0 : liveNodes.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    ClusterState other = (ClusterState) obj;
+    if (znodeVersion == null) {
+      if (other.znodeVersion != null) return false;
+    } else if (!znodeVersion.equals(other.znodeVersion)) return false;
+    if (liveNodes == null) {
+      if (other.liveNodes != null) return false;
+    } else if (!liveNodes.equals(other.liveNodes)) return false;
+    return true;
+  }
+
+
+
+  /**
+   * Internal API used only by ZkStateReader
+   */
+  void setLiveNodes(Set<String> liveNodes){
+    this.liveNodes = liveNodes;
+  }
+
+  /**For internal use only
+   */
+  Map<String, CollectionRef> getCollectionStates() {
+    return collectionStates;
+  }
+
+  public static class CollectionRef {
+    private final DocCollection coll;
+
+    public CollectionRef(DocCollection coll) {
+      this.coll = coll;
+    }
+
+    public DocCollection get(){
+      return coll;
+    }
+
+    public boolean isLazilyLoaded() { return false; }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClusterStateUtil.java
----------------------------------------------------------------------
diff --git a/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClusterStateUtil.java b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClusterStateUtil.java
new file mode 100644
index 0000000..fe74884
--- /dev/null
+++ b/ranger_solrj/src/main/java/org/apache/solr/common/cloud/ClusterStateUtil.java
@@ -0,0 +1,230 @@
+package org.apache.solr.common.cloud;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClusterStateUtil {
+  private static Logger log = LoggerFactory.getLogger(ClusterStateUtil.class);
+  
+  private static final int TIMEOUT_POLL_MS = 1000;
+  
+  /**
+   * Wait to see *all* cores live and active.
+   * 
+   * @param zkStateReader
+   *          to use for ClusterState
+   * @param timeoutInMs
+   *          how long to wait before giving up
+   * @return false if timed out
+   */
+  public static boolean waitForAllActiveAndLive(ZkStateReader zkStateReader, int timeoutInMs) {
+    return waitForAllActiveAndLive(zkStateReader, null, timeoutInMs);
+  }
+  
+  /**
+   * Wait to see *all* cores live and active.
+   * 
+   * @param zkStateReader
+   *          to use for ClusterState
+   * @param collection to look at
+   * @param timeoutInMs
+   *          how long to wait before giving up
+   * @return false if timed out
+   */
+  public static boolean waitForAllActiveAndLive(ZkStateReader zkStateReader, String collection,
+      int timeoutInMs) {
+    long timeout = System.nanoTime()
+        + TimeUnit.NANOSECONDS.convert(timeoutInMs, TimeUnit.MILLISECONDS);
+    boolean success = false;
+    while (System.nanoTime() < timeout) {
+      success = true;
+      ClusterState clusterState = zkStateReader.getClusterState();
+      if (clusterState != null) {
+        Set<String> collections;
+        if (collection != null) {
+          collections = Collections.singleton(collection);
+        } else {
+          collections = clusterState.getCollections();
+        }
+        for (String coll : collections) {
+          DocCollection docCollection = clusterState.getCollection(coll);
+          Collection<Slice> slices = docCollection.getSlices();
+          for (Slice slice : slices) {
+            // only look at active shards
+            if (slice.getState().equals(Slice.ACTIVE)) {
+              Collection<Replica> replicas = slice.getReplicas();
+              for (Replica replica : replicas) {
+                // on a live node?
+                boolean live = clusterState.liveNodesContain(replica
+                    .getNodeName());
+                String state = replica.getStr(ZkStateReader.STATE_PROP);
+                if (!live || !state.equals(ZkStateReader.ACTIVE)) {
+                  // fail
+                  success = false;
+                }
+              }
+            }
+          }
+        }
+        if (!success) {
+          try {
+            Thread.sleep(TIMEOUT_POLL_MS);
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new SolrException(ErrorCode.SERVER_ERROR, "Interrupted");
+          }
+        }
+      }
+    }
+    
+    return success;
+  }
+  
+  /**
+   * Wait to see an entry in the ClusterState with a specific coreNodeName and
+   * baseUrl.
+   * 
+   * @param zkStateReader
+   *          to use for ClusterState
+   * @param collection
+   *          to look in
+   * @param coreNodeName
+   *          to wait for
+   * @param baseUrl
+   *          to wait for
+   * @param timeoutInMs
+   *          how long to wait before giving up
+   * @return false if timed out
+   */
+  public static boolean waitToSeeLive(ZkStateReader zkStateReader,
+      String collection, String coreNodeName, String baseUrl,
+      int timeoutInMs) {
+    long timeout = System.nanoTime()
+        + TimeUnit.NANOSECONDS.convert(timeoutInMs, TimeUnit.MILLISECONDS);
+    
+    while (System.nanoTime() < timeout) {
+      log.debug("waiting to see replica just created live collection={} replica={} baseUrl={}",
+          collection, coreNodeName, baseUrl);
+      ClusterState clusterState = zkStateReader.getClusterState();
+      if (clusterState != null) {
+        DocCollection docCollection = clusterState.getCollection(collection);
+        Collection<Slice> slices = docCollection.getSlices();
+        for (Slice slice : slices) {
+          // only look at active shards
+          if (slice.getState().equals(Slice.ACTIVE)) {
+            Collection<Replica> replicas = slice.getReplicas();
+            for (Replica replica : replicas) {
+              // on a live node?
+              boolean live = clusterState.liveNodesContain(replica.getNodeName());
+              String rcoreNodeName = replica.getName();
+              String rbaseUrl = replica.getStr(ZkStateReader.BASE_URL_PROP);
+              if (live && coreNodeName.equals(rcoreNodeName)
+                  && baseUrl.equals(rbaseUrl)) {
+                // found it
+                return true;
+              }
+            }
+          }
+        }
+        try {
+          Thread.sleep(TIMEOUT_POLL_MS);
+        } catch (InterruptedException e) {
+          Thread.currentThread().interrupt();
+          throw new SolrException(ErrorCode.SERVER_ERROR, "Interrupted");
+        }
+      }
+    }
+    
+    log.error("Timed out waiting to see replica just created in cluster state. Continuing...");
+    return false;
+  }
+  
+  public static boolean waitForAllNotLive(ZkStateReader zkStateReader, int timeoutInMs) {
+    return waitForAllNotLive(zkStateReader, null, timeoutInMs);
+  }
+  
+
+  public static boolean waitForAllNotLive(ZkStateReader zkStateReader,
+      String collection, int timeoutInMs) {
+    long timeout = System.nanoTime()
+        + TimeUnit.NANOSECONDS.convert(timeoutInMs, TimeUnit.MILLISECONDS);
+    boolean success = false;
+    while (System.nanoTime() < timeout) {
+      success = true;
+      ClusterState clusterState = zkStateReader.getClusterState();
+      if (clusterState != null) {
+        Set<String> collections;
+        if (collection == null) {
+          collections = clusterState.getCollections();
+        } else {
+          collections = Collections.singleton(collection);
+        }
+        for (String coll : collections) {
+          DocCollection docCollection = clusterState.getCollection(coll);
+          Collection<Slice> slices = docCollection.getSlices();
+          for (Slice slice : slices) {
+            // only look at active shards
+            if (slice.getState().equals(Slice.ACTIVE)) {
+              Collection<Replica> replicas = slice.getReplicas();
+              for (Replica replica : replicas) {
+                // on a live node?
+                boolean live = clusterState.liveNodesContain(replica
+                    .getNodeName());
+                if (live) {
+                  // fail
+                  success = false;
+                }
+              }
+            }
+          }
+        }
+        if (!success) {
+          try {
+            Thread.sleep(TIMEOUT_POLL_MS);
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new SolrException(ErrorCode.SERVER_ERROR, "Interrupted");
+          }
+        }
+      }
+    }
+    
+    return success;
+  }
+  
+  public static boolean isAutoAddReplicas(ZkStateReader reader, String collection) {
+    ClusterState clusterState = reader.getClusterState();
+    if (clusterState != null) {
+      DocCollection docCollection = clusterState.getCollectionOrNull(collection);
+      if (docCollection != null) {
+        return docCollection.getAutoAddReplicas();
+      }
+    }
+    return false;
+  }
+  
+}