com.nridge.ds.solr.SolrConfigJSON.java Source code

Java tutorial

Introduction

Here is the source code for com.nridge.ds.solr.SolrConfigJSON.java

Source

/*
 * NorthRidge Software, LLC - Copyright (c) 2019.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.nridge.ds.solr;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.nridge.core.app.mgr.AppMgr;
import com.nridge.core.base.doc.Document;
import com.nridge.core.base.ds.DSException;
import com.nridge.core.base.field.Field;
import com.nridge.core.base.field.data.DataBag;
import com.nridge.core.base.field.data.DataField;
import com.nridge.core.base.field.data.DataTextField;
import com.nridge.core.base.std.StrUtl;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;

import java.io.*;
import java.util.ArrayList;

/**
 * The SolrSchemaJSON provides a collection of methods that can load
 * a JSON representation of a Solr schema.  This class supports the
 * latest Solr 7.5 API service calls.
 *
 * @see <a href="http://lucene.apache.org/solr/guide/7_6/config-api.html">Solr Config API</a>
 * @see <a href="http://lucene.apache.org/solr/guide/7_6/requesthandlers-and-searchcomponents-in-solrconfig.html#search-components">Solr Search Components</a>
 * @see <a href="https://lucene.apache.org/solr/guide/7_6/implicit-requesthandlers.html">Solr Request Handler ParamSets</a>
 *
 * @author Al Cole
 * @since 1.0
 */
@SuppressWarnings("FieldCanBeLocal")
public class SolrConfigJSON {
    private final String OBJECT_RESPONSE_HEADER = "responseHeader";
    private final String OBJECT_RESPONSE_CONFIG = "config";
    private final String OBJECT_RESPONSE_CONFIG_QUERY = "query";
    private final String OBJECT_RESPONSE_CONFIG_QUERY_FC = "filterCache";
    private final String OBJECT_RESPONSE_CONFIG_QUERY_RC = "queryResultCache";
    private final String OBJECT_RESPONSE_CONFIG_QUERY_DC = "documentCache";
    private final String OBJECT_RESPONSE_CONFIG_QUERY_FVC = "fieldValueCache";
    private final String OBJECT_RESPONSE_CONFIG_UPDATE_HANDLER = "updateHandler";
    private final String OBJECT_RESPONSE_CONFIG_UH_IW = "indexWriter";
    private final String OBJECT_RESPONSE_CONFIG_UH_CW = "commitWithin";
    private final String OBJECT_RESPONSE_CONFIG_UH_AC = "autoCommit";
    private final String OBJECT_RESPONSE_CONFIG_UH_ASC = "autoSoftCommit";
    private final String OBJECT_RESPONSE_CONFIG_REQUEST_HANDLER = "requestHandler";
    private final String OBJECT_RESPONSE_CONFIG_SN_DEFAULTS = "defaults";
    private final String OBJECT_RESPONSE_CONFIG_SN_LC = "last-components";
    private final String OBJECT_RESPONSE_CONFIG_SN_IN = "invariants";
    private final String OBJECT_RESPONSE_CONFIG_SN_CO = "components";
    private final String OBJECT_RESPONSE_CONFIG_SN_UPDATE = "update";
    private final String OBJECT_RESPONSE_CONFIG_RESPONSE_WRITER = "queryResponseWriter";
    private final String OBJECT_RESPONSE_CONFIG_SEARCH_COMPONENT = "searchComponent";
    private final String OBJECT_RESPONSE_CONFIG_SC_SPELLCHECK = "spellcheck";
    private final String OBJECT_RESPONSE_CONFIG_SC_SC_SC = "spellchecker";
    private final String OBJECT_RESPONSE_CONFIG_SC_SUGGEST = "suggest";
    private final String OBJECT_RESPONSE_CONFIG_SC_SUGGESTER = "suggester";
    private final String OBJECT_RESPONSE_CONFIG_SC_TERM_VECTOR = "tvComponent";
    private final String OBJECT_RESPONSE_CONFIG_SC_TERMS = "terms";
    private final String OBJECT_RESPONSE_CONFIG_SC_ELEVATOR = "elevator";
    private final String OBJECT_RESPONSE_CONFIG_SC_HIGHLIGHT = "highlight";
    private final String OBJECT_RESPONSE_CONFIG_INIT_PARAMS = "initParams";
    private final String OBJECT_RESPONSE_CONFIG_LISTENER = "listener";
    private final String OBJECT_RESPONSE_CONFIG_DIRECTORY_FACTORY = "directoryFactory";
    private final String OBJECT_RESPONSE_CONFIG_CODEC_FACTORY = "codecFactory";
    private final String OBJECT_RESPONSE_CONFIG_UPDATE_HU_LOG = "updateHandlerupdateLog";
    private final String OBJECT_RESPONSE_CONFIG_REQUEST_DISPATCHER = "requestDispatcher";
    private final String OBJECT_RESPONSE_CONFIG_INDEX_CONFIG = "indexConfig";
    private final String OBJECT_RESPONSE_CONFIG_PEER_SYNC = "peerSync";

    private final AppMgr mAppMgr;

    public SolrConfigJSON(final AppMgr anAppMgr) {
        mAppMgr = anAppMgr;
    }

    public SolrConfigJSON(final AppMgr anAppMgr, Document aDocument) {
        mAppMgr = anAppMgr;
    }

    private boolean isNextTokenAnObject(JsonReader aReader) throws IOException {
        JsonToken jsonToken = aReader.peek();

        return (jsonToken == JsonToken.BEGIN_OBJECT);
    }

    private boolean isNextTokenAnArray(JsonReader aReader) throws IOException {
        JsonToken jsonToken = aReader.peek();

        return (jsonToken == JsonToken.BEGIN_ARRAY);
    }

    private String nextValueAsString(JsonReader aReader) throws IOException {
        String valueString = StringUtils.EMPTY;

        JsonToken jsonToken = aReader.peek();
        switch (jsonToken) {
        case BOOLEAN:
            valueString = StrUtl.booleanToString(aReader.nextBoolean());
            break;
        case NUMBER:
        case STRING:
            valueString = Solr.cleanContent(aReader.nextString());
            break;
        default:
            aReader.skipValue();
            break;
        }

        return valueString;
    }

    private void addBagTextField(DataBag aBag, String aName, String aValue) {
        if ((aBag != null) && (StringUtils.isNotEmpty(aName))) {
            DataTextField dataTextField = new DataTextField(aName, Field.nameToTitle(aName), aValue);
            dataTextField.addFeature(Field.FEATURE_IS_STORED, StrUtl.STRING_TRUE);
            aBag.add(dataTextField);
        }
    }

    private void populateCfgUpdateHandler(JsonReader aReader, Document aCfgDocument, String aDocType)
            throws IOException {
        DataBag dataBag;
        String jsonName, jsonValue;
        Document iwDocument, cwDocument, acDocument, ascDocument;

        Document uhDocument = new Document(aDocType);
        DataBag uhBag = uhDocument.getBag();
        aReader.beginObject();
        while (aReader.hasNext()) {
            jsonName = aReader.nextName();
            if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_UH_IW)) {
                iwDocument = new Document(Solr.RESPONSE_CONFIG_UH_INDEX_WRITER);
                iwDocument.setName(jsonName);
                dataBag = iwDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                uhDocument.addRelationship(Solr.RESPONSE_CONFIG_UH_INDEX_WRITER, iwDocument);
            } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_UH_CW)) {
                cwDocument = new Document(Solr.RESPONSE_CONFIG_UH_COMMIT_WITHIN);
                cwDocument.setName(jsonName);
                dataBag = cwDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                uhDocument.addRelationship(Solr.RESPONSE_CONFIG_UH_COMMIT_WITHIN, cwDocument);
            } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_UH_AC)) {
                acDocument = new Document(Solr.RESPONSE_CONFIG_UH_AUTO_COMMIT);
                acDocument.setName(jsonName);
                dataBag = acDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                uhDocument.addRelationship(Solr.RESPONSE_CONFIG_UH_AUTO_COMMIT, acDocument);
            } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_UH_ASC)) {
                ascDocument = new Document(Solr.RESPONSE_CONFIG_UH_AUTO_SOFT_COMMIT);
                ascDocument.setName(jsonName);
                dataBag = ascDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                uhDocument.addRelationship(Solr.RESPONSE_CONFIG_UH_AUTO_SOFT_COMMIT, ascDocument);
            } else {
                jsonValue = nextValueAsString(aReader);
                addBagTextField(uhBag, jsonName, jsonValue);
            }
        }
        aReader.endObject();
        aCfgDocument.addRelationship(Solr.RESPONSE_CONFIG_UPDATE_HANDLER, uhDocument);
    }

    private void populateCfgQuery(JsonReader aReader, Document aCfgDocument, String aDocType) throws IOException {
        DataBag dataBag;
        String jsonName, jsonValue;
        Document fcDocument, rcDocument, dcDocument, fvcDocument;

        Document queryDocument = new Document(aDocType);
        DataBag queryBag = queryDocument.getBag();
        aReader.beginObject();
        while (aReader.hasNext()) {
            jsonName = aReader.nextName();
            if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_QUERY_FC)) {
                fcDocument = new Document(Solr.RESPONSE_CONFIG_QUERY_FC);
                fcDocument.setName(jsonName);
                dataBag = fcDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                queryDocument.addRelationship(Solr.RESPONSE_CONFIG_QUERY_FC, fcDocument);
            } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_QUERY_RC)) {
                rcDocument = new Document(Solr.RESPONSE_CONFIG_QUERY_RC);
                rcDocument.setName(jsonName);
                dataBag = rcDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                queryDocument.addRelationship(Solr.RESPONSE_CONFIG_QUERY_RC, rcDocument);
            } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_QUERY_DC)) {
                dcDocument = new Document(Solr.RESPONSE_CONFIG_QUERY_DC);
                dcDocument.setName(jsonName);
                dataBag = dcDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                queryDocument.addRelationship(Solr.RESPONSE_CONFIG_QUERY_DC, dcDocument);
            } else if ((StringUtils.isEmpty(jsonName)) || // This handles a Solr 7.5 JSON output bug (empty string)
                    (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_QUERY_FVC))) {
                fvcDocument = new Document(Solr.RESPONSE_CONFIG_QUERY_FVC);
                fvcDocument.setName(jsonName);
                dataBag = fvcDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                queryDocument.addRelationship(Solr.RESPONSE_CONFIG_QUERY_FVC, fvcDocument);
            } else {
                jsonValue = nextValueAsString(aReader);
                addBagTextField(queryBag, jsonName, jsonValue);
            }
        }
        aReader.endObject();
        aCfgDocument.addRelationship(Solr.RESPONSE_CONFIG_QUERY, queryDocument);
    }

    private void addFieldToDocument(JsonReader aReader, Document aDocument, String aName) throws IOException {
        DataBag childBag;
        JsonToken jsonToken;
        boolean isArrayArray;
        Document childDocument;
        String jsonName, jsonValue;
        DataTextField dataTextField;

        DataBag dataBag = aDocument.getBag();
        if (isNextTokenAnArray(aReader)) {
            aReader.beginArray();
            dataTextField = new DataTextField(aName, Field.nameToTitle(aName));
            dataTextField.addFeature(Field.FEATURE_IS_STORED, StrUtl.STRING_TRUE);
            jsonToken = aReader.peek();
            if (jsonToken == JsonToken.BEGIN_ARRAY) {
                isArrayArray = true;
                aReader.beginArray();
                jsonToken = aReader.peek();
            } else
                isArrayArray = false;
            while (jsonToken != JsonToken.END_ARRAY) {
                jsonValue = nextValueAsString(aReader);
                dataTextField.addValue(jsonValue);
                jsonToken = aReader.peek();
            }
            aReader.endArray();
            if (isArrayArray)
                aReader.endArray();
            dataBag.add(dataTextField);
        } else if (isNextTokenAnObject(aReader)) {
            childDocument = new Document(aName);
            childDocument.setName(aName);
            childBag = childDocument.getBag();

            aReader.beginObject();
            jsonToken = aReader.peek();
            while (jsonToken != JsonToken.END_OBJECT) {
                jsonName = aReader.nextName();
                jsonValue = nextValueAsString(aReader);
                addBagTextField(childBag, jsonName, jsonValue);
                jsonToken = aReader.peek();
            }
            aReader.endObject();
            aDocument.addRelationship(aName, childDocument);
        } else {
            jsonValue = nextValueAsString(aReader);
            addBagTextField(dataBag, aName, jsonValue);
        }
    }

    private void addFieldToDocument(JsonReader aReader, Document aDocument) throws IOException {
        String jsonName;

        jsonName = aReader.nextName();
        addFieldToDocument(aReader, aDocument, jsonName);
    }

    private void populateCfgRequestHandler(JsonReader aReader, Document aCfgDocument, String aDocType)
            throws IOException {
        DataBag rhBag, snBag;
        String jsonName, jsonValue;
        Document snDocument, defDocument, coDocument, lcDocument, inDocument, upDocument;

        Document rhDocument = new Document(aDocType);
        rhBag = rhDocument.getBag();
        aReader.beginObject();
        while (aReader.hasNext()) {
            jsonName = aReader.nextName();
            if (StringUtils.startsWith(jsonName, "/")) {
                snDocument = new Document(Solr.RESPONSE_CONFIG_RH_SN);
                snDocument.setName(jsonName);
                snBag = snDocument.getBag();
                DataTextField operationField = new DataTextField(Solr.CONFIG_OPERATION_FIELD_NAME,
                        Field.nameToTitle(Solr.CONFIG_OPERATION_FIELD_NAME));
                operationField.addFeature(Field.FEATURE_IS_STORED, StrUtl.STRING_FALSE);
                snBag.add(operationField);
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_SN_DEFAULTS)) {
                        defDocument = new Document(Solr.RESPONSE_CONFIG_RH_SN_DEFAULTS);
                        defDocument.setName(jsonName);
                        aReader.beginObject();
                        while (aReader.hasNext())
                            addFieldToDocument(aReader, defDocument);
                        aReader.endObject();
                        snDocument.addRelationship(Solr.RESPONSE_CONFIG_RH_SN_DEFAULTS, defDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_SN_CO)) {
                        coDocument = new Document(Solr.RESPONSE_CONFIG_RH_SN_COMPONENTS);
                        coDocument.setName(jsonName);
                        addFieldToDocument(aReader, coDocument, jsonName);
                        snDocument.addRelationship(Solr.RESPONSE_CONFIG_RH_SN_COMPONENTS, coDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_SN_LC)) {
                        lcDocument = new Document(Solr.RESPONSE_CONFIG_RH_SN_LAST_COMPONENTS);
                        lcDocument.setName(jsonName);
                        addFieldToDocument(aReader, lcDocument, jsonName);
                        snDocument.addRelationship(Solr.RESPONSE_CONFIG_RH_SN_LAST_COMPONENTS, lcDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_SN_IN)) {
                        inDocument = new Document(Solr.RESPONSE_CONFIG_RH_SN_INVARIANTS);
                        inDocument.setName(jsonName);
                        aReader.beginObject();
                        while (aReader.hasNext())
                            addFieldToDocument(aReader, inDocument);
                        aReader.endObject();
                        snDocument.addRelationship(Solr.RESPONSE_CONFIG_RH_SN_INVARIANTS, inDocument);
                    } else {
                        jsonValue = nextValueAsString(aReader);
                        addBagTextField(snBag, jsonName, jsonValue);
                        snBag.add(new DataTextField(jsonName, Field.nameToTitle(jsonName), jsonValue));
                    }
                }
                aReader.endObject();
                rhDocument.addRelationship(Solr.RESPONSE_CONFIG_RH_SN, snDocument);
            } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_SN_UPDATE)) {
                upDocument = new Document(Solr.RESPONSE_CONFIG_RH_SN_UPDATE);
                upDocument.setName(jsonName);
                aReader.beginObject();
                while (aReader.hasNext())
                    addFieldToDocument(aReader, upDocument);
                aReader.endObject();
                rhDocument.addRelationship(Solr.RESPONSE_CONFIG_RH_SN_UPDATE, upDocument);
            } else {
                jsonValue = nextValueAsString(aReader);
                addBagTextField(rhBag, jsonName, jsonValue);
            }
        }
        aReader.endObject();
        aCfgDocument.addRelationship(Solr.RESPONSE_CONFIG_REQUEST_HANDLER, rhDocument);
    }

    private void populateQueryResponseWriterHandler(JsonReader aReader, Document aCfgDocument, String aDocType)
            throws IOException {
        DataBag dataBag;
        Document rwDocument;
        String jsonName, jsonValue;

        Document qrwDocument = new Document(aDocType);
        DataBag qewBag = qrwDocument.getBag();
        aReader.beginObject();
        while (aReader.hasNext()) {
            jsonName = aReader.nextName();
            if (isNextTokenAnObject(aReader)) {
                rwDocument = new Document(Solr.RESPONSE_CONFIG_QRW_RESPONSE_WRITER);
                rwDocument.setName(jsonName);
                dataBag = rwDocument.getBag();
                aReader.beginObject();
                while (aReader.hasNext()) {
                    jsonName = aReader.nextName();
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(dataBag, jsonName, jsonValue);
                }
                aReader.endObject();
                qrwDocument.addRelationship(Solr.RESPONSE_CONFIG_QRW_RESPONSE_WRITER, rwDocument);
            } else {
                jsonValue = nextValueAsString(aReader);
                addBagTextField(qewBag, jsonName, jsonValue);
            }
        }
        aReader.endObject();
        aCfgDocument.addRelationship(Solr.RESPONSE_CONFIG_QUERY_RESPONSE_WRITER, qrwDocument);
    }

    private void addObjectToDocument(JsonReader aReader, Document aParentDocument, String aType, String aName)
            throws IOException {
        String jsonValue;
        JsonToken jsonToken;
        boolean isArrayArray;
        Document childDocument;

        DataBag dataBag = aParentDocument.getBag();
        if (isNextTokenAnArray(aReader)) {
            aReader.beginArray();
            jsonToken = aReader.peek();
            if (jsonToken == JsonToken.BEGIN_ARRAY) {
                isArrayArray = true;
                aReader.beginArray();
                jsonToken = aReader.peek();
            } else
                isArrayArray = false;
            while (jsonToken != JsonToken.END_ARRAY) {
                childDocument = new Document(aType);
                childDocument.setName(aName);
                aReader.beginObject();
                while (aReader.hasNext())
                    addFieldToDocument(aReader, childDocument);
                aReader.endObject();
                aParentDocument.addRelationship(aType, childDocument);
                jsonToken = aReader.peek();
            }
            aReader.endArray();
            if (isArrayArray)
                aReader.endArray();
        } else if (isNextTokenAnObject(aReader)) {
            childDocument = new Document(aType);
            childDocument.setName(aName);
            aReader.beginObject();
            while (aReader.hasNext())
                addFieldToDocument(aReader, childDocument);
            aReader.endObject();
            aParentDocument.addRelationship(aType, childDocument);
        } else {
            jsonValue = nextValueAsString(aReader);
            addBagTextField(dataBag, aName, jsonValue);
        }
    }

    private void assignSearchComponentType(Document aDocument) {
        DataBag dataBag = aDocument.getBag();
        String className = dataBag.getValueAsString("class");

        if (StringUtils.contains(className, "SpellCheck"))
            aDocument.setType(Solr.RESPONSE_CONFIG_SEARCH_COMPONENT + " - " + Solr.RESPONSE_CONFIG_SC_SPELLCHECK);
        else if (StringUtils.contains(className, "Suggest"))
            aDocument.setType(Solr.RESPONSE_CONFIG_SEARCH_COMPONENT + " - " + Solr.RESPONSE_CONFIG_SC_SUGGEST);
        else if (StringUtils.contains(className, "TermVector"))
            aDocument.setType(Solr.RESPONSE_CONFIG_SEARCH_COMPONENT + " - " + Solr.RESPONSE_CONFIG_SC_TERM_VECTOR);
        else if (StringUtils.contains(className, "TermsComponent"))
            aDocument.setType(Solr.RESPONSE_CONFIG_SEARCH_COMPONENT + " - " + Solr.RESPONSE_CONFIG_SC_TERMS);
        else if (StringUtils.contains(className, "QueryElevation"))
            aDocument.setType(Solr.RESPONSE_CONFIG_SEARCH_COMPONENT + " - " + Solr.RESPONSE_CONFIG_SC_ELEVATOR);
        else if (StringUtils.contains(className, "HighlightComponent"))
            aDocument.setType(Solr.RESPONSE_CONFIG_SEARCH_COMPONENT + " - " + Solr.RESPONSE_CONFIG_SC_HIGHLIGHT);
    }

    private void populateSearchComponent(JsonReader aReader, Document aCfgDocument, String aDocType)
            throws IOException {
        DataBag scBag;
        Document scDocument;
        JsonToken jsonToken;
        boolean isArrayArray;
        Document childDocument;
        String jsonName, jsonValue;

        Document searchComponentDocument = new Document(aDocType);
        aReader.beginObject();
        while (aReader.hasNext()) {
            jsonName = aReader.nextName();
            scDocument = new Document(Solr.RESPONSE_CONFIG_SEARCH_COMPONENT);
            scDocument.setName(jsonName);
            scBag = scDocument.getBag();
            DataTextField operationField = new DataTextField(Solr.CONFIG_OPERATION_FIELD_NAME,
                    Field.nameToTitle(Solr.CONFIG_OPERATION_FIELD_NAME));
            operationField.addFeature(Field.FEATURE_IS_STORED, StrUtl.STRING_FALSE);
            scBag.add(operationField);

            aReader.beginObject();
            while (aReader.hasNext()) {
                jsonName = aReader.nextName();
                if (isNextTokenAnArray(aReader)) {
                    aReader.beginArray();
                    jsonToken = aReader.peek();
                    if (jsonToken == JsonToken.BEGIN_ARRAY) {
                        isArrayArray = true;
                        aReader.beginArray();
                        jsonToken = aReader.peek();
                    } else
                        isArrayArray = false;
                    while (jsonToken != JsonToken.END_ARRAY) {
                        childDocument = new Document(Field.nameToTitle(jsonName));
                        childDocument.setName(jsonName);
                        aReader.beginObject();
                        while (aReader.hasNext())
                            addFieldToDocument(aReader, childDocument);
                        aReader.endObject();
                        scDocument.addRelationship(Field.nameToTitle(jsonName), childDocument);
                        jsonToken = aReader.peek();
                    }
                    aReader.endArray();
                    if (isArrayArray)
                        aReader.endArray();
                } else if (isNextTokenAnObject(aReader)) {
                    childDocument = new Document(Field.nameToTitle(jsonName));
                    childDocument.setName(jsonName);
                    aReader.beginObject();
                    while (aReader.hasNext())
                        addFieldToDocument(aReader, childDocument);
                    aReader.endObject();
                    scDocument.addRelationship(Field.nameToTitle(jsonName), childDocument);
                } else {
                    jsonValue = nextValueAsString(aReader);
                    addBagTextField(scBag, jsonName, jsonValue);
                }
            }
            aReader.endObject();
            assignSearchComponentType(scDocument);
            searchComponentDocument.addRelationship(scDocument.getType(), scDocument);
        }

        aCfgDocument.addRelationship(aDocType, searchComponentDocument);
    }

    /**
     * Parses an JSON string representing the Solr schema and loads it into a document
     * and returns an instance to it.
     *
     * @param aConfigString JSON string representation of the Solr schema.
     *
     * @return Document instance containing the parsed data.
     *
     * @throws IOException I/O related exception.
     */
    public Document loadDocument(String aConfigString) throws IOException {
        String jsonName, jsonValue;
        Document componentDocument;

        Document configDocument = new Document(Solr.DOCUMENT_SCHEMA_TYPE);
        DataBag configBag = configDocument.getBag();

        StringReader stringReader = new StringReader(aConfigString);
        JsonReader jsonReader = new JsonReader(stringReader);

        jsonReader.beginObject();
        while (jsonReader.hasNext()) {
            jsonName = jsonReader.nextName();
            if (StringUtils.equals(jsonName, OBJECT_RESPONSE_HEADER)) {
                Document headerDocument = new Document(Solr.RESPONSE_SCHEMA_HEADER);
                DataBag headerBag = headerDocument.getBag();
                jsonReader.beginObject();
                while (jsonReader.hasNext()) {
                    jsonName = jsonReader.nextName();
                    jsonValue = nextValueAsString(jsonReader);
                    headerBag.add(new DataTextField(jsonName, Field.nameToTitle(jsonName), jsonValue));
                }
                jsonReader.endObject();
                configDocument.addRelationship(Solr.RESPONSE_SCHEMA_HEADER, headerDocument);
            } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG)) {
                jsonReader.beginObject();
                while (jsonReader.hasNext()) {
                    jsonName = jsonReader.nextName();
                    if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_UPDATE_HANDLER))
                        populateCfgUpdateHandler(jsonReader, configDocument, Solr.RESPONSE_CONFIG_UPDATE_HANDLER);
                    else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_QUERY))
                        populateCfgQuery(jsonReader, configDocument, Solr.RESPONSE_CONFIG_QUERY);
                    else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_REQUEST_HANDLER))
                        populateCfgRequestHandler(jsonReader, configDocument, Solr.RESPONSE_CONFIG_REQUEST_HANDLER);
                    else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_RESPONSE_WRITER))
                        populateQueryResponseWriterHandler(jsonReader, configDocument,
                                Solr.RESPONSE_CONFIG_QUERY_RESPONSE_WRITER);
                    else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_SEARCH_COMPONENT))
                        populateSearchComponent(jsonReader, configDocument, Solr.RESPONSE_CONFIG_SEARCH_COMPONENT);
                    else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_INIT_PARAMS)) {
                        componentDocument = new Document(Solr.RESPONSE_CONFIG_INIT_PARAMS);
                        addObjectToDocument(jsonReader, componentDocument, Solr.RESPONSE_CONFIG_INIT_PARAMETERS,
                                jsonName);
                        configDocument.addRelationship(Solr.RESPONSE_CONFIG_INIT_PARAMS, componentDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_LISTENER)) {
                        componentDocument = new Document(Solr.RESPONSE_CONFIG_LISTENER);
                        jsonReader.beginArray();
                        while (jsonReader.hasNext())
                            addObjectToDocument(jsonReader, componentDocument, Solr.RESPONSE_CONFIG_LISTENER_EVENTS,
                                    jsonName);
                        jsonReader.endArray();
                        configDocument.addRelationship(Solr.RESPONSE_CONFIG_LISTENER, componentDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_DIRECTORY_FACTORY)) {
                        componentDocument = new Document(Solr.RESPONSE_CONFIG_DIRECTORY_FACTORY);
                        addObjectToDocument(jsonReader, componentDocument, Solr.RESPONSE_CONFIG_DIRECTORY_ENTRIES,
                                jsonName);
                        configDocument.addRelationship(Solr.RESPONSE_CONFIG_DIRECTORY_FACTORY, componentDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_CODEC_FACTORY)) {
                        componentDocument = new Document(Solr.RESPONSE_CONFIG_CODEC_FACTORY);
                        addObjectToDocument(jsonReader, componentDocument, Solr.RESPONSE_CONFIG_CODEC_ENTRIES,
                                jsonName);
                        configDocument.addRelationship(Solr.RESPONSE_CONFIG_CODEC_FACTORY, componentDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_UPDATE_HU_LOG)) {
                        componentDocument = new Document(Solr.RESPONSE_CONFIG_UPDATE_HANDLER_ULOG);
                        addObjectToDocument(jsonReader, componentDocument, Solr.RESPONSE_CONFIG_UHUL_ENTRIES,
                                jsonName);
                        configDocument.addRelationship(Solr.RESPONSE_CONFIG_UPDATE_HANDLER_ULOG, componentDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_REQUEST_DISPATCHER)) {
                        componentDocument = new Document(Solr.RESPONSE_CONFIG_REQUEST_DISPATCHER);
                        addObjectToDocument(jsonReader, componentDocument, Solr.RESPONSE_CONFIG_RD_ENTRIES,
                                jsonName);
                        configDocument.addRelationship(Solr.RESPONSE_CONFIG_REQUEST_DISPATCHER, componentDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_INDEX_CONFIG)) {
                        componentDocument = new Document(Solr.RESPONSE_CONFIG_REQUEST_DISPATCHER);
                        addObjectToDocument(jsonReader, componentDocument, Solr.RESPONSE_CONFIG_INDEX_CONFIG,
                                jsonName);
                        configDocument.addRelationship(Solr.RESPONSE_CONFIG_IC_ENTRIES, componentDocument);
                    } else if (StringUtils.equals(jsonName, OBJECT_RESPONSE_CONFIG_PEER_SYNC)) {
                        componentDocument = new Document(Solr.RESPONSE_CONFIG_PEER_SYNC);
                        addObjectToDocument(jsonReader, componentDocument, Solr.RESPONSE_CONFIG_PS_ENTRIES,
                                jsonName);
                        configDocument.addRelationship(Solr.RESPONSE_CONFIG_PEER_SYNC, componentDocument);
                    } else {
                        jsonValue = nextValueAsString(jsonReader);
                        addBagTextField(configBag, jsonName, jsonValue);
                    }
                }
                jsonReader.endObject();
            } else
                jsonReader.skipValue();
        }
        jsonReader.endObject();
        jsonReader.close();

        return configDocument;
    }

    /**
     * Parses an input stream and loads it into an internally managed
     * document instance.
     *
     * @param anIS Input stream instance.
     *
     * @return Document capturing the Solr config.
     *
     * @throws IOException I/O related exception.
     */
    public Document load(InputStream anIS) throws IOException {
        String schemaString = IOUtils.toString(anIS, StrUtl.CHARSET_UTF_8);
        return loadDocument(schemaString);
    }

    /**
     * Parses a JSON formatted path/file name and loads it into an internally managed
     * document instance.
     *
     * @param aPathFileName Input path file name.
     *
     * @return Document capturing the Solr config.
     *
     * @throws IOException I/O related exception.
     */
    public Document load(String aPathFileName) throws IOException {
        File jsonFile = new File(aPathFileName);
        if (!jsonFile.exists())
            throw new IOException(aPathFileName + ": Does not exist.");

        Document solrConfigDocument = null;
        try (FileInputStream fileInputStream = new FileInputStream(jsonFile)) {
            solrConfigDocument = load(fileInputStream);
        }

        return solrConfigDocument;
    }

    private void appendFieldValueJSON(StringBuilder aStringBuilder, DataField aField) {
        if (aField.isValueNotEmpty()) {
            String fieldName = aField.getName();
            aStringBuilder.append(String.format("  \"%s\":", fieldName));
            if (aField.isMultiValue()) {
                int appendCount = 0;
                aStringBuilder.append(String.format(" [%n"));
                for (String fieldValue : aField.getValues()) {
                    if (appendCount > 0)
                        aStringBuilder.append(String.format(",%n"));
                    if ((Field.isNumber(aField.getType()))
                            || ((StringUtils.contains(fieldName, ".facet.") && (NumberUtils.isNumber(fieldValue)))))
                        aStringBuilder.append(String.format("%s", fieldValue));
                    else if (Field.isBoolean(aField.getType()))
                        aStringBuilder.append(String.format("%s", StrUtl.stringToBoolean(fieldValue)));
                    else
                        aStringBuilder.append(String.format("\"%s\"", fieldValue));
                    appendCount++;
                }
                aStringBuilder.append(" ]");
            } else {
                if ((Field.isNumber(aField.getType())) || (NumberUtils.isNumber(aField.getValue())))
                    aStringBuilder.append(String.format("%s", aField.getValue()));
                else if (Field.isBoolean(aField.getType()))
                    aStringBuilder.append(String.format("%s", aField.isValueTrue()));
                else
                    aStringBuilder.append(String.format("\"%s\"", aField.getValue()));
            }
        }
    }

    // http://localhost:8983/solr/techproducts/config
    private void appendServiceJSON(StringBuilder aStringBuilder, Document aServiceDocument) {
        DataField dataField;
        int appendCount, fieldCount;

        DataBag serviceBag = aServiceDocument.getBag();
        String serviceType = aServiceDocument.getType();
        switch (serviceType) {
        case Solr.RESPONSE_CONFIG_RH_SN_UPDATE:
        case Solr.RESPONSE_CONFIG_RH_SN_DEFAULTS:
        case Solr.RESPONSE_CONFIG_RH_SN_INVARIANTS:
            aStringBuilder.append(String.format("  \"%s\": {", serviceType.toLowerCase()));
            appendCount = 0;
            fieldCount = serviceBag.count();
            for (int i = 0; i < fieldCount; i++) {
                dataField = serviceBag.getByOffset(i);
                if ((dataField.isValueNotEmpty()) && (dataField.isFeatureTrue(Field.FEATURE_IS_STORED))) {
                    if (appendCount > 0)
                        aStringBuilder.append(String.format(",%n"));
                    appendFieldValueJSON(aStringBuilder, dataField);
                    appendCount++;
                }
            }
            aStringBuilder.append(String.format(" }%n"));
            break;
        case Solr.RESPONSE_CONFIG_RH_SN_COMPONENTS:
        case Solr.RESPONSE_CONFIG_RH_SN_LAST_COMPONENTS:
            fieldCount = serviceBag.count();
            if (fieldCount > 0) {
                dataField = serviceBag.getByOffset(0);
                appendFieldValueJSON(aStringBuilder, dataField);
            }
            break;
        default:
            break;
        }
    }

    // http://localhost:8983/solr/techproducts/config
    private void appendComponentJSON(StringBuilder aStringBuilder, Document aComponentDocument) {
        DataField dataField;
        int appendCount, fieldCount;

        DataBag componentBag = aComponentDocument.getBag();
        aStringBuilder.append(" {");
        appendCount = 0;
        fieldCount = componentBag.count();
        for (int i = 0; i < fieldCount; i++) {
            dataField = componentBag.getByOffset(i);
            if ((dataField.isValueNotEmpty()) && (dataField.isFeatureTrue(Field.FEATURE_IS_STORED))) {
                if (appendCount > 0)
                    aStringBuilder.append(String.format(",%n"));
                appendFieldValueJSON(aStringBuilder, dataField);
                appendCount++;
            }
        }
        aStringBuilder.append(String.format(" }%n"));
    }

    private boolean isOpearationValid(String anOperation) {
        switch (anOperation) {
        case Solr.CONFIG_OPERATION_ADD_REQ_HANDLER:
        case Solr.CONFIG_OPERATION_UPD_REQ_HANDLER:
        case Solr.CONFIG_OPERATION_DEL_REQ_HANDLER:
        case Solr.CONFIG_OPERATION_ADD_SC_HANDLER:
        case Solr.CONFIG_OPERATION_UPD_SC_HANDLER:
        case Solr.CONFIG_OPERATION_DEL_SC_HANDLER:
            return true;
        default:
            return false;
        }
    }

    public void convert(StringBuilder aStringBuilder, Document aDocument) throws DSException {
        DataField dataField;
        int fieldCount, appendCount;
        Logger appLogger = mAppMgr.getLogger(this, "convert");

        appLogger.trace(mAppMgr.LOGMSG_TRACE_ENTER);

        DataBag solrConfigurationBag = aDocument.getBag();
        String configOperation = solrConfigurationBag.getValueAsString(Solr.CONFIG_OPERATION_FIELD_NAME);
        if (StringUtils.isEmpty(configOperation))
            throw new DSException("Undefined config field operation - cannot convert to JSON.");
        else if (!isOpearationValid(configOperation))
            throw new DSException(
                    String.format("Config operation '%s' is invalid - cannot convert to JSON.", configOperation));
        else if (!solrConfigurationBag.isValid())
            throw new DSException("Data bag is not valid - cannot convert to JSON.");

        switch (configOperation) {
        case Solr.CONFIG_OPERATION_ADD_REQ_HANDLER:
        case Solr.CONFIG_OPERATION_UPD_REQ_HANDLER:
            aStringBuilder.append(String.format(" \"%s\": {%n", configOperation));
            appendCount = 0;
            fieldCount = solrConfigurationBag.count();
            for (int i = 0; i < fieldCount; i++) {
                dataField = solrConfigurationBag.getByOffset(i);
                if ((dataField.isValueNotEmpty()) && (dataField.isFeatureTrue(Field.FEATURE_IS_STORED))) {
                    if (appendCount > 0)
                        aStringBuilder.append(String.format(",%n"));
                    appendFieldValueJSON(aStringBuilder, dataField);
                    appendCount++;
                }
            }
            ArrayList<Document> serviceDocuments = aDocument.getRelatedDocuments();
            if ((serviceDocuments != null) && (serviceDocuments.size() > 0)) {
                for (Document serviceDocument : serviceDocuments) {
                    if (appendCount > 0)
                        aStringBuilder.append(String.format(",%n"));
                    appendServiceJSON(aStringBuilder, serviceDocument);
                    appendCount++;
                }
            }
            aStringBuilder.append("}");
            break;
        case Solr.CONFIG_OPERATION_DEL_REQ_HANDLER:
            dataField = solrConfigurationBag.getFieldByName("name");
            if (dataField != null)
                aStringBuilder.append(String.format(" \"%s\": \"%s\"", configOperation, dataField.getValue()));
            break;
        case Solr.CONFIG_OPERATION_ADD_SC_HANDLER:
        case Solr.CONFIG_OPERATION_UPD_SC_HANDLER:
            aStringBuilder.append(String.format(" \"%s\": {%n", configOperation));
            appendCount = 0;
            fieldCount = solrConfigurationBag.count();
            for (int i = 0; i < fieldCount; i++) {
                dataField = solrConfigurationBag.getByOffset(i);
                if ((dataField.isValueNotEmpty()) && (dataField.isFeatureTrue(Field.FEATURE_IS_STORED))) {
                    if (appendCount > 0)
                        aStringBuilder.append(String.format(",%n"));
                    appendFieldValueJSON(aStringBuilder, dataField);
                    appendCount++;
                }
            }
            ArrayList<Document> componentDocuments = aDocument.getRelatedDocuments();
            if ((componentDocuments != null) && (componentDocuments.size() > 0)) {
                if (appendCount > 0) {
                    aStringBuilder.append(String.format(",%n"));
                    appendCount = 0;
                }
                boolean isArray = componentDocuments.size() > 1;
                for (Document componentDocument : componentDocuments) {
                    if (appendCount == 0) {
                        if (isArray)
                            aStringBuilder
                                    .append(String.format(" \"%s\": [%n", componentDocument.getBag().getName()));
                        else
                            aStringBuilder.append(String.format(" \"%s\": ", componentDocument.getBag().getName()));
                    } else
                        aStringBuilder.append(String.format(",%n"));
                    appendComponentJSON(aStringBuilder, componentDocument);
                    appendCount++;
                }
                if (isArray)
                    aStringBuilder.append("]");
            }
            aStringBuilder.append("}");
            break;
        case Solr.CONFIG_OPERATION_DEL_SC_HANDLER:
            dataField = solrConfigurationBag.getFieldByName("name");
            if (dataField != null)
                aStringBuilder.append(String.format(" \"%s\": \"%s\"", configOperation, dataField.getValue()));
            break;
        default:
            break;
        }

        appLogger.trace(mAppMgr.LOGMSG_TRACE_DEPART);
    }
}