Index: /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeJSON.java
===================================================================
--- /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeJSON.java	(revision 32105)
+++ /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeJSON.java	(revision 32106)
@@ -25,24 +25,8 @@
 
 //public class PerVolumeJSON implements VoidFunction<String> 
-public class PerVolumeJSON implements Function<Text,Integer> 
+public class PerVolumeJSON implements Function<Text,Integer>
 {
 	private static final long serialVersionUID = 1L;
-	protected String _input_dir;
-	protected String _whitelist_filename;
-	protected String _langmap_directory;
-	
-	protected final ArrayList<String> _solr_endpoints;
-	protected final int _solr_endpoints_len;
-	
-	//protected String _solr_url;
-	protected String _output_dir;
-	
-	protected int    _verbosity;
-	
-	protected WhitelistBloomFilter _whitelist_bloomfilter;
-	protected UniversalPOSLangMap _universal_langmap;
-
-	boolean _icu_tokenize;
-	boolean _strict_file_io;
+	protected PerVolumeUtil _per_vol_util;
 
 	public PerVolumeJSON(String input_dir, String whitelist_filename, String langmap_directory,
@@ -50,22 +34,10 @@
 					     boolean icu_tokenize, boolean strict_file_io)
 	{
-		System.out.println("*** PerVolumeJSON Constructor, langmap_directory = " + langmap_directory);
 		
-		_input_dir  = input_dir;
-		_whitelist_filename = whitelist_filename;
-		_langmap_directory = langmap_directory;
-		
-		_solr_endpoints = solr_endpoints;
-		_solr_endpoints_len = solr_endpoints.size();
-		
-		//_solr_url   = solr_url;
-		_output_dir = output_dir;
-		_verbosity  = verbosity;
-		
-		_icu_tokenize   = icu_tokenize;
-		_strict_file_io = strict_file_io;
-		
-		_whitelist_bloomfilter = null;
-		_universal_langmap = null;
+		// Had issues with class not found in Spark when set up with inheritance
+		_per_vol_util = new PerVolumeUtil(input_dir, whitelist_filename, langmap_directory,
+				  solr_endpoints, output_dir, verbosity,
+				  icu_tokenize, strict_file_io);
+	
 	}
 	
@@ -74,254 +46,6 @@
 
 	{ 
-	        if (_whitelist_filename != null) {
-
-		    synchronized (_whitelist_filename) {
-			if (_whitelist_bloomfilter == null) {
-			    
-			    _whitelist_bloomfilter = new WhitelistBloomFilter(_whitelist_filename,true);
-			}
-		    }
-		}
-		
-		if (_langmap_directory != null) {
-
-		    synchronized (_langmap_directory) {
-			if (_universal_langmap == null) {
-			    _universal_langmap = new UniversalPOSLangMap(_langmap_directory);
-			}
-		    }
-		}
-
-		int ef_num_pages = 0;
-
-		String solr_url = null;
-		if (_solr_endpoints_len > 0) {
-			int random_choice = (int)(_solr_endpoints_len * Math.random());
-			solr_url = _solr_endpoints.get(random_choice);
-		}
-		
-		try {
-
-
-			JSONObject extracted_feature_record  = new JSONObject(json_text.toString());
-
-			if (extracted_feature_record != null) {
-				String volume_id = extracted_feature_record.getString("id");
-
-				JSONObject ef_metadata = extracted_feature_record.getJSONObject("metadata");
-				//String title= ef_metadata.getString("title");
-
-				//
-				// Top-level metadata Solr doc
-				//
-				JSONObject solr_add_metadata_doc_json = SolrDocJSON.generateToplevelMetadataSolrDocJSON(volume_id,ef_metadata);
-				if (solr_add_metadata_doc_json != null) {
-				
-					if ((_verbosity >=2)) {
-						System.out.println("==================");
-						System.out.println("Metadata JSON: " + solr_add_metadata_doc_json.toString());
-						System.out.println("==================");
-					}
-
-					if (solr_url != null) {
-
-						if ((_verbosity >=2) ) {
-							System.out.println("==================");
-							System.out.println("Posting to: " + solr_url);
-							System.out.println("==================");
-						}
-						SolrDocJSON.postSolrDoc(solr_url, solr_add_metadata_doc_json, volume_id, "top-level-metadata");
-					}
-				}
-				
-				//
-				// Now move on to POS extracted features per-page
-				//
-				boolean index_pages = true;
-				if (index_pages) {
-					
-					JSONObject ef_features = extracted_feature_record.getJSONObject("features");
-
-					int ef_page_count = ef_features.getInt("pageCount");
-
-					if (_verbosity >= 1) {
-						System.out.println("Processing: " + volume_id);
-						System.out.println("  pageCount = " + ef_page_count);
-					}
-
-					JSONArray ef_pages = ef_features.getJSONArray("pages");
-					ef_num_pages = ef_pages.length();
-
-
-					for (int i = 0; i < ef_page_count; i++) {
-						String formatted_i = String.format("page-%06d", i);
-						String page_id = volume_id + "." + formatted_i;
-
-						if (_verbosity >= 2) {
-							System.out.println("  Page: " + page_id);
-						}
-
-
-						JSONObject ef_page = ef_pages.getJSONObject(i);
-
-						if (ef_page != null) {
-							// Convert to Solr add form
-							JSONObject solr_add_doc_json 
-							= SolrDocJSON.generateSolrDocJSON(volume_id, page_id, 
-												ef_metadata, ef_page,
-												_whitelist_bloomfilter, _universal_langmap, _icu_tokenize);
-
-
-							if ((_verbosity >=2) && (i==20)) {
-								System.out.println("==================");
-								System.out.println("Sample output Solr add JSON [page 20]: " + solr_add_doc_json.toString());
-								System.out.println("==================");
-							}
-
-
-							if (solr_url != null) {
-								SolrDocJSON.postSolrDoc(solr_url, solr_add_doc_json,
-										volume_id, page_id);
-							}
-						}
-						else {
-							System.err.println("Skipping: " + page_id);
-						}
-
-					}
-				}
-				else {
-				    System.err.println("Skipping per-page POS text indexing");
-				}
-
-			}
-		}
-		catch (Exception e) {
-			if (_strict_file_io) {
-				throw e;
-			}
-			else {
-				e.printStackTrace();
-			}
-		}
-		
-		return ef_num_pages;
-
+		return _per_vol_util.call(json_text);
 	}
-		
-		/*
-	//public void call(String json_file_in) throws IOException
-	public Integer call(String json_file_in) throws IOException
-	
-	{ 
-		if ((_whitelist_filename != null) && (_whitelist_bloomfilter == null)) {
-			_whitelist_bloomfilter = new WhitelistBloomFilter(_whitelist_filename,true);
-		}
-
-		int ef_num_pages = 0;
-		
-		ArrayList<String> ids = new ArrayList<String>(); // want it to be non-null so can return valid iterator
-		
-		String full_json_file_in = _input_dir + "/" + json_file_in;
-		JSONObject extracted_feature_record = JSONClusterFileIO.readJSONFile(full_json_file_in);
-		
-		if (extracted_feature_record != null) {
-			String volume_id = extracted_feature_record.getString("id");
-
-			//JSONObject ef_metadata = extracted_feature_record.getJSONObject("metadata");
-			//String title= ef_metadata.getString("title");
-
-			JSONObject ef_features = extracted_feature_record.getJSONObject("features");
-
-			int ef_page_count = ef_features.getInt("pageCount");
-
-			if (_verbosity >= 1) {
-				System.out.println("Processing: " + json_file_in);
-				System.out.println("  pageCount = " + ef_page_count);
-			}
-
-			JSONArray ef_pages = ef_features.getJSONArray("pages");
-			ef_num_pages = ef_pages.length();
-
-			// Make directory for page-level JSON output
-			String json_dir = ClusterFileIO.removeSuffix(json_file_in,".json.bz2");
-			String page_json_dir = json_dir + "/pages";
-
-			if (_output_dir != null) {
-				ClusterFileIO.createDirectoryAll(_output_dir + "/" + page_json_dir);
-			}
-			
-			ids = new ArrayList<String>(ef_num_pages);
-			for (int i = 0; i < ef_page_count; i++) {
-				String formatted_i = String.format("page-%06d", i);
-				String page_id = volume_id + "." + formatted_i;
-
-				if (_verbosity >= 2) {
-					System.out.println("  Page: " + page_id);
-				}
-
-				String output_json_bz2 = page_json_dir +"/" + formatted_i + ".json.bz2";
-				ids.add(page_id); 
-
-				if (_verbosity >=2) {
-					if (i==0) {
-						System.out.println("Sample output JSON page file [i=0]: " + output_json_bz2);
-					}
-				}
-				JSONObject ef_page = ef_pages.getJSONObject(i);
-
-				if (ef_page != null) {
-					// Convert to Solr add form
-					JSONObject solr_add_doc_json 
-					= SolrDocJSON.generateSolrDocJSON(volume_id, page_id, ef_page, _whitelist_bloomfilter, _icu_tokenize);
-
-
-					if ((_verbosity >=2) && (i==20)) {
-						System.out.println("==================");
-						System.out.println("Sample output Solr add JSON [page 20]: " + solr_add_doc_json.toString());
-						System.out.println("==================");
-					}
-
-
-					if (_solr_url != null) {
-						if ((_verbosity >=2) && (i==20)) {
-							System.out.println("==================");
-							System.out.println("Posting to: " + _solr_url);
-							System.out.println("==================");
-						}
-						SolrDocJSON.postSolrDoc(_solr_url, solr_add_doc_json);
-					}
-
-					if (_output_dir != null) {
-						if ((_verbosity >=2) && (i==20)) {
-							System.out.println("==================");
-							System.out.println("Saving to: " + _output_dir);
-							System.out.println("==================");
-						}
-						SolrDocJSON.saveSolrDoc(solr_add_doc_json, _output_dir + "/" + output_json_bz2);
-					}
-				}
-				else {
-					System.err.println("Skipping: " + page_id);
-				}
-
-			}
-		}
-		else {
-			// File did not exist, or could not be parsed
-			String mess = "Failed to read in bzipped JSON file '" + full_json_file_in + "'";
-			if (_strict_file_io) {
-				throw new IOException(mess);
-			}
-			else {
-				System.err.println("Warning: " + mess);
-				System.out.println("Warning: " + mess);
-			}
-		}
-		
-		return ef_num_pages;
-				
-	}
-	*/
 }
 
Index: /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeJSONList.java
===================================================================
--- /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeJSONList.java	(revision 32106)
+++ /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeJSONList.java	(revision 32106)
@@ -0,0 +1,42 @@
+package org.hathitrust.extractedfeatures;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.hadoop.io.Text;
+import org.apache.spark.api.java.function.FlatMapFunction;
+import org.apache.spark.api.java.function.Function;
+import org.apache.spark.api.java.function.VoidFunction;
+import org.apache.spark.util.DoubleAccumulator;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+public class PerVolumeJSONList implements Function<String,Integer>
+{
+	private static final long serialVersionUID = 1L;
+	protected PerVolumeUtil _per_vol_util;
+
+	public PerVolumeJSONList(String input_dir, String whitelist_filename, String langmap_directory,
+				         ArrayList<String> solr_endpoints, String output_dir, int verbosity, 
+					     boolean icu_tokenize, boolean strict_file_io)
+	{
+		_per_vol_util = new PerVolumeUtil(input_dir, whitelist_filename, langmap_directory,
+										  solr_endpoints, output_dir, verbosity,
+										  icu_tokenize, strict_file_io);
+				
+	}
+	
+	public Integer call(String json_file_in) throws IOException
+	{
+		// Read in JSON file as Text
+		String full_json_file_in = _per_vol_util.getInputDir() + "/" + json_file_in;
+		String json_content_string = ClusterFileIO.readTextFile(full_json_file_in);
+		
+		Text json_content_text = new Text(json_content_string);
+	
+		return _per_vol_util.call(json_content_text);
+	}
+}
+
Index: /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeUtil.java
===================================================================
--- /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeUtil.java	(revision 32106)
+++ /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/PerVolumeUtil.java	(revision 32106)
@@ -0,0 +1,330 @@
+package org.hathitrust.extractedfeatures;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.hadoop.io.Text;
+import org.apache.spark.api.java.function.FlatMapFunction;
+import org.apache.spark.api.java.function.Function;
+import org.apache.spark.api.java.function.VoidFunction;
+import org.apache.spark.util.DoubleAccumulator;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/*
+class PagedJSON implements Function<String, Boolean> {
+
+	private static final long serialVersionUID = 1L;
+
+	public Boolean call(String s) { return s.contains("a"); }
+}
+ */
+
+
+//public class PerVolumeJSON implements VoidFunction<String> 
+public class PerVolumeUtil
+{
+	//private static final long serialVersionUID = 1L;
+	protected String _input_dir;
+	protected String _whitelist_filename;
+	protected String _langmap_directory;
+	
+	protected final ArrayList<String> _solr_endpoints;
+	protected final int _solr_endpoints_len;
+	
+	protected String _output_dir;
+	
+	protected int    _verbosity;
+	
+	protected WhitelistBloomFilter _whitelist_bloomfilter;
+	protected UniversalPOSLangMap _universal_langmap;
+
+	boolean _icu_tokenize;
+	boolean _strict_file_io;
+
+	public PerVolumeUtil(String input_dir, String whitelist_filename, String langmap_directory,
+				         ArrayList<String> solr_endpoints, String output_dir, int verbosity, 
+					     boolean icu_tokenize, boolean strict_file_io)
+	{
+		System.out.println("*** PerVolumeUtil Constructor, langmap_directory = " + langmap_directory);
+		
+		_input_dir  = input_dir;
+		_whitelist_filename = whitelist_filename;
+		_langmap_directory = langmap_directory;
+		
+		_solr_endpoints = solr_endpoints;
+		_solr_endpoints_len = solr_endpoints.size();
+		
+		//_solr_url   = solr_url;
+		_output_dir = output_dir;
+		_verbosity  = verbosity;
+		
+		_icu_tokenize   = icu_tokenize;
+		_strict_file_io = strict_file_io;
+		
+		_whitelist_bloomfilter = null;
+		_universal_langmap = null;
+	}
+	
+	public String getInputDir()
+	{
+		return _input_dir;
+	}
+	
+	public Integer call(Text json_text) throws IOException
+
+	{ 
+	        if (_whitelist_filename != null) {
+
+		    synchronized (_whitelist_filename) {
+			if (_whitelist_bloomfilter == null) {
+			    
+			    _whitelist_bloomfilter = new WhitelistBloomFilter(_whitelist_filename,true);
+			}
+		    }
+		}
+		
+		if (_langmap_directory != null) {
+
+		    synchronized (_langmap_directory) {
+			if (_universal_langmap == null) {
+			    _universal_langmap = new UniversalPOSLangMap(_langmap_directory);
+			}
+		    }
+		}
+
+		int ef_num_pages = 0;
+
+		String solr_url = null;
+		if (_solr_endpoints_len > 0) {
+			int random_choice = (int)(_solr_endpoints_len * Math.random());
+			solr_url = _solr_endpoints.get(random_choice);
+		}
+		
+		try {
+
+
+			JSONObject extracted_feature_record  = new JSONObject(json_text.toString());
+
+			if (extracted_feature_record != null) {
+				String volume_id = extracted_feature_record.getString("id");
+
+				JSONObject ef_metadata = extracted_feature_record.getJSONObject("metadata");
+				//String title= ef_metadata.getString("title");
+
+				//
+				// Top-level metadata Solr doc
+				//
+				JSONObject solr_add_metadata_doc_json = SolrDocJSON.generateToplevelMetadataSolrDocJSON(volume_id,ef_metadata);
+				if (solr_add_metadata_doc_json != null) {
+				
+					if ((_verbosity >=2)) {
+						System.out.println("==================");
+						System.out.println("Metadata JSON: " + solr_add_metadata_doc_json.toString());
+						System.out.println("==================");
+					}
+
+					if (solr_url != null) {
+
+						if ((_verbosity >=2) ) {
+							System.out.println("==================");
+							System.out.println("Posting to: " + solr_url);
+							System.out.println("==================");
+						}
+						SolrDocJSON.postSolrDoc(solr_url, solr_add_metadata_doc_json, volume_id, "top-level-metadata");
+					}
+				}
+				
+				//
+				// Now move on to POS extracted features per-page
+				//
+				boolean index_pages = true;
+				if (index_pages) {
+					
+					JSONObject ef_features = extracted_feature_record.getJSONObject("features");
+
+					int ef_page_count = ef_features.getInt("pageCount");
+
+					if (_verbosity >= 1) {
+						System.out.println("Processing: " + volume_id);
+						System.out.println("  pageCount = " + ef_page_count);
+					}
+
+					JSONArray ef_pages = ef_features.getJSONArray("pages");
+					ef_num_pages = ef_pages.length();
+
+
+					for (int i = 0; i < ef_page_count; i++) {
+						String formatted_i = String.format("page-%06d", i);
+						String page_id = volume_id + "." + formatted_i;
+
+						if (_verbosity >= 2) {
+							System.out.println("  Page: " + page_id);
+						}
+
+
+						JSONObject ef_page = ef_pages.getJSONObject(i);
+
+						if (ef_page != null) {
+							// Convert to Solr add form
+							JSONObject solr_add_doc_json 
+							= SolrDocJSON.generateSolrDocJSON(volume_id, page_id, 
+												ef_metadata, ef_page,
+												_whitelist_bloomfilter, _universal_langmap, _icu_tokenize);
+
+
+							if ((_verbosity >=2) && (i==20)) {
+								System.out.println("==================");
+								System.out.println("Sample output Solr add JSON [page 20]: " + solr_add_doc_json.toString());
+								System.out.println("==================");
+							}
+
+
+							if (solr_url != null) {
+								SolrDocJSON.postSolrDoc(solr_url, solr_add_doc_json,
+										volume_id, page_id);
+							}
+						}
+						else {
+							System.err.println("Skipping: " + page_id);
+						}
+
+					}
+				}
+				else {
+				    System.err.println("Skipping per-page POS text indexing");
+				}
+
+			}
+		}
+		catch (Exception e) {
+			if (_strict_file_io) {
+				throw e;
+			}
+			else {
+				e.printStackTrace();
+			}
+		}
+		
+		return ef_num_pages;
+
+	}
+		
+		/*
+	//public void call(String json_file_in) throws IOException
+	public Integer call(String json_file_in) throws IOException
+	
+	{ 
+		if ((_whitelist_filename != null) && (_whitelist_bloomfilter == null)) {
+			_whitelist_bloomfilter = new WhitelistBloomFilter(_whitelist_filename,true);
+		}
+
+		int ef_num_pages = 0;
+		
+		ArrayList<String> ids = new ArrayList<String>(); // want it to be non-null so can return valid iterator
+		
+		String full_json_file_in = _input_dir + "/" + json_file_in;
+		JSONObject extracted_feature_record = JSONClusterFileIO.readJSONFile(full_json_file_in);
+		
+		if (extracted_feature_record != null) {
+			String volume_id = extracted_feature_record.getString("id");
+
+			//JSONObject ef_metadata = extracted_feature_record.getJSONObject("metadata");
+			//String title= ef_metadata.getString("title");
+
+			JSONObject ef_features = extracted_feature_record.getJSONObject("features");
+
+			int ef_page_count = ef_features.getInt("pageCount");
+
+			if (_verbosity >= 1) {
+				System.out.println("Processing: " + json_file_in);
+				System.out.println("  pageCount = " + ef_page_count);
+			}
+
+			JSONArray ef_pages = ef_features.getJSONArray("pages");
+			ef_num_pages = ef_pages.length();
+
+			// Make directory for page-level JSON output
+			String json_dir = ClusterFileIO.removeSuffix(json_file_in,".json.bz2");
+			String page_json_dir = json_dir + "/pages";
+
+			if (_output_dir != null) {
+				ClusterFileIO.createDirectoryAll(_output_dir + "/" + page_json_dir);
+			}
+			
+			ids = new ArrayList<String>(ef_num_pages);
+			for (int i = 0; i < ef_page_count; i++) {
+				String formatted_i = String.format("page-%06d", i);
+				String page_id = volume_id + "." + formatted_i;
+
+				if (_verbosity >= 2) {
+					System.out.println("  Page: " + page_id);
+				}
+
+				String output_json_bz2 = page_json_dir +"/" + formatted_i + ".json.bz2";
+				ids.add(page_id); 
+
+				if (_verbosity >=2) {
+					if (i==0) {
+						System.out.println("Sample output JSON page file [i=0]: " + output_json_bz2);
+					}
+				}
+				JSONObject ef_page = ef_pages.getJSONObject(i);
+
+				if (ef_page != null) {
+					// Convert to Solr add form
+					JSONObject solr_add_doc_json 
+					= SolrDocJSON.generateSolrDocJSON(volume_id, page_id, ef_page, _whitelist_bloomfilter, _icu_tokenize);
+
+
+					if ((_verbosity >=2) && (i==20)) {
+						System.out.println("==================");
+						System.out.println("Sample output Solr add JSON [page 20]: " + solr_add_doc_json.toString());
+						System.out.println("==================");
+					}
+
+
+					if (_solr_url != null) {
+						if ((_verbosity >=2) && (i==20)) {
+							System.out.println("==================");
+							System.out.println("Posting to: " + _solr_url);
+							System.out.println("==================");
+						}
+						SolrDocJSON.postSolrDoc(_solr_url, solr_add_doc_json);
+					}
+
+					if (_output_dir != null) {
+						if ((_verbosity >=2) && (i==20)) {
+							System.out.println("==================");
+							System.out.println("Saving to: " + _output_dir);
+							System.out.println("==================");
+						}
+						SolrDocJSON.saveSolrDoc(solr_add_doc_json, _output_dir + "/" + output_json_bz2);
+					}
+				}
+				else {
+					System.err.println("Skipping: " + page_id);
+				}
+
+			}
+		}
+		else {
+			// File did not exist, or could not be parsed
+			String mess = "Failed to read in bzipped JSON file '" + full_json_file_in + "'";
+			if (_strict_file_io) {
+				throw new IOException(mess);
+			}
+			else {
+				System.err.println("Warning: " + mess);
+				System.out.println("Warning: " + mess);
+			}
+		}
+		
+		return ef_num_pages;
+				
+	}
+	*/
+}
+
Index: /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/ProcessForSolrIngest.java
===================================================================
--- /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/ProcessForSolrIngest.java	(revision 32105)
+++ /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/ProcessForSolrIngest.java	(revision 32106)
@@ -142,5 +142,5 @@
 	
 	/*
-	public void execPerVolume()
+	public void execPerVolumeJSONFileList()
 	{	
 		String spark_app_name = generateSparkAppName("Per Volume");		
@@ -168,7 +168,12 @@
 		boolean strict_file_io = Boolean.getBoolean("wcsa-ef-ingest.strict-file-io");
 		
-		PerVolumeJSON per_vol_json = new PerVolumeJSON(_input_dir,_whitelist_filename, 
-											           _solr_url,_output_dir,_verbosity, progress_accum,per_vol,
-											           icu_tokenize,strict_file_io);
+		ArrayList<String> solr_endpoints = extrapolateSolrEndpoints(_solr_collection);
+		
+		//PerVolumeJSON per_vol_json = new PerVolumeJSON(_input_dir,_whitelist_filename, 
+		//									           _solr_url,_output_dir,_verbosity, progress_accum,per_vol,
+		//									           icu_tokenize,strict_file_io);
+		PerVolumeJSON per_vol_json = new PerVolumeJSON(_input_dir,_whitelist_filename, _langmap_directory,
+														solr_endpoints,_output_dir,_verbosity,
+														icu_tokenize,strict_file_io);
 
 		//json_list_data_rp.foreach(per_vol_json);
Index: /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/ProcessForSolrIngestJSONFilelist.java
===================================================================
--- /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/ProcessForSolrIngestJSONFilelist.java	(revision 32106)
+++ /other-projects/hathitrust/wcsa/extracted-features-solr/trunk/solr-ingest/src/main/java/org/hathitrust/extractedfeatures/ProcessForSolrIngestJSONFilelist.java	(revision 32106)
@@ -0,0 +1,258 @@
+package org.hathitrust.extractedfeatures;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import org.apache.commons.cli.*;
+import org.apache.hadoop.io.Text;
+import org.apache.spark.api.java.*;
+import org.apache.spark.util.DoubleAccumulator;
+import org.apache.spark.util.LongAccumulator;
+import org.hathitrust.extractedfeatures.PerPageJSONFlatmap;
+import org.json.JSONObject;
+import org.apache.spark.SparkConf;
+
+public class ProcessForSolrIngestJSONFilelist implements Serializable
+{
+	private static final long serialVersionUID = 1L;
+
+	protected static final int DEFAULT_NUM_CORES = 10;
+	protected static final int MINIMUM_NUM_PARTITIONS = 10*DEFAULT_NUM_CORES; 
+	
+	protected static final int DEFAULT_FILES_PER_PARTITION = 3000;
+	
+	protected String _input_dir;
+	protected String _json_list_filename;
+	
+	protected String _solr_base_url;
+	protected String _solr_collection;
+	
+	protected String _whitelist_filename;
+	protected String _langmap_directory;
+	
+	//protected String _solr_url;
+	protected String _output_dir;
+	
+	protected int    _verbosity;
+
+	public ProcessForSolrIngestJSONFilelist(String input_dir, String json_list_filename,
+											String solr_collection, String solr_base_url, 
+											String output_dir, int verbosity)
+	{
+		_input_dir = input_dir;
+		_json_list_filename = json_list_filename;
+		
+		_solr_collection = solr_collection;
+		_solr_base_url   = solr_base_url;
+		
+		boolean use_whitelist = Boolean.getBoolean("wcsa-ef-ingest.use-whitelist");
+		_whitelist_filename = (use_whitelist) ?  System.getProperty("wcsa-ef-ingest.whitelist-filename") : null;
+		
+		boolean use_langmap = Boolean.getBoolean("wcsa-ef-ingest.use-langmap");
+		_langmap_directory = (use_langmap) ?  System.getProperty("wcsa-ef-ingest.langmap-directory") : null;
+		
+		_output_dir = output_dir;
+		_verbosity  = verbosity;
+	}
+
+	protected String generateSparkAppName(String exec_mode)
+	{
+		String spark_app_name = "[" + exec_mode + "] Extract Features: Process for Solr Ingest from JSON-filelist";
+		spark_app_name += " [" + _solr_collection + "]";
+
+		if (_solr_base_url != null) { 
+			spark_app_name += " solr_base_url=" + _solr_base_url;
+		}
+		
+		if (_output_dir != null) { 
+			spark_app_name += " output_dir=" + _output_dir;
+		}
+		
+		return spark_app_name;
+	}
+	
+	public ArrayList<String> extrapolateSolrEndpoints(String solr_collection)
+	{
+		ArrayList<String> solr_endpoints = new ArrayList<String>();
+		
+		if (_solr_base_url != null) {
+			String solr_url = _solr_base_url + "/" + solr_collection + "/update";
+			
+			String solr_cloud_nodes = System.getProperty("wcsa-ef-ingest.solr-cloud-nodes",null);
+			if (solr_cloud_nodes != null) {
+				String [] cloud_nodes = solr_cloud_nodes.split(",");
+				for (String cn : cloud_nodes) {
+					String solr_endpoint = solr_url.replaceFirst("//.*?:\\d+/", "//"+cn+"/");
+					solr_endpoints.add(solr_endpoint);
+				}
+			}
+			else {
+				solr_endpoints.add(solr_url);
+			}
+		}
+		
+		return solr_endpoints;
+	}
+	
+	public void execPerVolumeJSONFilelist()
+	{	
+		String spark_app_name = generateSparkAppName("Per Volume");		
+		
+		SparkConf spark_conf = new SparkConf().setAppName(spark_app_name);
+		JavaSparkContext jsc = new JavaSparkContext(spark_conf);
+		
+		//String filename_root = _json_list_filename.replaceAll(".*/","").replaceAll("\\..*$","");
+		
+		//int num_partitions = Integer.getInteger("wcsa-ef-ingest.num-partitions", DEFAULT_NUM_PARTITIONS);
+		int files_per_partition = Integer.getInteger("wcsa-ef-ingest.files-per-partition", DEFAULT_FILES_PER_PARTITION);
+		
+		JavaRDD<String> json_list_data = jsc.textFile(_json_list_filename).cache();
+		json_list_data.setName("JSON-file-list");
+		
+		long num_volumes = json_list_data.count();
+		double per_vol = 100.0/(double)num_volumes;
+		
+		int num_partitions = (int)(num_volumes/files_per_partition)+1;
+		if (num_partitions < MINIMUM_NUM_PARTITIONS) {
+			num_partitions = MINIMUM_NUM_PARTITIONS;
+		}
+		JavaRDD<String> json_list_data_rp = json_list_data.repartition(num_partitions);
+		json_list_data_rp.setName("JSON-file-list--repartitioned");
+		
+		DoubleAccumulator progress_accum = jsc.sc().doubleAccumulator("Per Volume Progress Percent");
+		
+		boolean icu_tokenize = Boolean.getBoolean("wcsa-ef-ingest.icu-tokenize");
+		boolean strict_file_io = Boolean.getBoolean("wcsa-ef-ingest.strict-file-io");
+		
+		ArrayList<String> solr_endpoints = extrapolateSolrEndpoints(_solr_collection);
+		
+		PerVolumeJSONList per_vol_json = new PerVolumeJSONList(_input_dir,_whitelist_filename, _langmap_directory,
+														solr_endpoints,_output_dir,_verbosity,
+														icu_tokenize,strict_file_io);
+
+		JavaRDD<Integer> per_volume_page_count = json_list_data_rp.map(per_vol_json);
+		per_volume_page_count.setName("volume-page-counts");
+		
+		//Integer total_page_count = volume_page_counts.reduce((a, b) -> a + b);
+		long num_vol_ids = per_volume_page_count.count();
+		
+		System.out.println("");
+		System.out.println("############");
+		System.out.println("# Number of volume ids: " + num_vol_ids);
+		System.out.println("############");
+		System.out.println("");
+
+		jsc.close();
+	}
+	
+	public static void print_usage(HelpFormatter formatter, Options options)
+	{
+		formatter.printHelp("RUN.bash [options] input-dir json-filelist.txt solr-collection", options);
+	}
+	
+	public static void main(String[] args) {
+		Options options = new Options();
+
+		Option verbosity_opt = new Option("v", "verbosity", true, 
+				"Set to control the level of debugging output [0=none, 1=some, 2=lots]");
+		verbosity_opt.setRequired(false);
+		options.addOption(verbosity_opt);
+		
+		Option properties_opt = new Option("p", "properties", true, 
+				"Read in the specified Java properties file");
+		properties_opt.setRequired(false);
+		options.addOption(properties_opt);
+		
+		Option output_dir_opt = new Option("o", "output-dir", true, 
+				"If specified, save BZipped Solr JSON files to this directory");
+		output_dir_opt.setRequired(false);
+		options.addOption(output_dir_opt);
+		
+		Option solr_base_url_opt = new Option("u", "solr-base-url", true, 
+				"If specified, the base URL to post the Solr JSON data to");
+		solr_base_url_opt.setRequired(false);
+		options.addOption(solr_base_url_opt);
+		
+		Option read_only_opt = new Option("r", "read-only", false, 
+				"Used to initiate a run where the files are all read in, but nothing is ingested/saved");
+		read_only_opt.setRequired(false);
+		options.addOption(read_only_opt);
+		
+		// Need to work with CLI v1.2 as this is the JAR that is bundled with Hadoop/Spark 
+		CommandLineParser parser = new GnuParser(); 
+		//CommandLineParser parser = new DefaultParser(); // if working with CLI v1.3 and above
+		
+		HelpFormatter formatter = new HelpFormatter();
+		CommandLine cmd = null;
+
+		try {
+			cmd = parser.parse(options, args);
+		}
+		catch (ParseException e) {
+			System.err.println(e.getMessage());
+			print_usage(formatter,options);
+			System.exit(1);
+		}
+
+		
+		String verbosity_str = cmd.getOptionValue("verbosity","1");
+		int verbosity = Integer.parseInt(verbosity_str);
+
+		String property_filename = cmd.getOptionValue("properties",null);
+	
+		String output_dir = cmd.getOptionValue("output-dir",null);
+		String solr_base_url   = cmd.getOptionValue("solr-base-url",null);
+		boolean read_only   = cmd.hasOption("read-only");
+		
+		String[] filtered_args = cmd.getArgs();
+
+		if (filtered_args.length != 3) {
+			print_usage(formatter,options);
+			System.exit(1);
+		}
+	
+		if (property_filename != null) {
+			try {
+				FileInputStream fis = new FileInputStream(property_filename);
+				BufferedInputStream bis = new BufferedInputStream(fis);
+				
+				System.getProperties().load(bis);
+			} 
+			catch (FileNotFoundException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+				System.err.println("File not found: '" + property_filename + "'. Skipping property file read");
+			}
+			catch (IOException e) {
+				System.err.println("IO Exception for: '" + property_filename + "'. Malformed syntax? Skipping property file read");
+			}
+		}
+		
+		if (!read_only && ((output_dir == null) && (solr_base_url==null))) {
+			System.err.println("Need to specify either --solr-base-url or --output-dir otherwise generated files are not ingested/saved");
+			print_usage(formatter,options);
+			System.exit(1);
+		}
+		if (read_only) {
+			// For this case, need to ensure solr-url and output-dir are null
+			output_dir = null;
+			solr_base_url = null;
+		}
+		
+		String input_dir  = filtered_args[0];
+		String json_file_list = filtered_args[1];
+		String solr_collection = filtered_args[2];
+		
+		ProcessForSolrIngestJSONFilelist prep_for_ingest 
+			= new ProcessForSolrIngestJSONFilelist(input_dir,json_file_list, 
+												   solr_collection,solr_base_url, 
+												   output_dir,verbosity);
+			
+		prep_for_ingest.execPerVolumeJSONFilelist();
+		
+	}
+}
