Package com.splunk

Source Code of com.splunk.ResultsReaderJson$ExportHelper

/*
* Copyright 2012 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package com.splunk;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* The {@code ResultsReaderJson} class represents a streaming JSON reader for
* Splunk search results. This class requires the gson-2.1.jar file in your
* build path. If you want to access the preview events, use the
* {@link MultiResultsReaderJson} class.
*/
public class ResultsReaderJson extends ResultsReader {
    private JsonReader jsonReader;
    // Helper object that will only be constructed if the reader is handling
    // json format used by export.
    private ExportHelper exportHelper;
    // Whether the 'preview' flag is read
    private boolean previewFlagRead;

    /**
     * Class constructor.
     *
     * Constructs a streaming JSON reader for the event stream. You should only
     * attempt to parse a JSON stream with this reader. If you attempt to parse
     * a different type of stream, unpredictable results may occur.
     *
     * @param inputStream The JSON stream to parse.
     * @throws IOException
     */
    public ResultsReaderJson(InputStream inputStream) throws IOException {
        this(inputStream, false);
    }

    ResultsReaderJson(InputStream inputStream, boolean isInMultiReader)
            throws IOException {
        super(inputStream, isInMultiReader);
        jsonReader = new JsonReader(new InputStreamReader(inputStream, "UTF-8"));
        // if stream is empty, return a null reader.
        jsonReader.setLenient(true);
        if (isExportStream || isInMultiReader)
            exportHelper = new ExportHelper();
        finishInitialization();
    }

    // Advance in the json stream, reading meta data if available, and
    // get ready for readEvent method.
    // Return false if end of stream is encountered.
    boolean advanceIntoNextSetBeforeEvent() throws IOException {
        // jsonReader will be set to null once the end is reached.
        if (jsonReader == null)
            return false;

        // In Splunk 5.0 from the export endpoint,
        // each result is in its own top level object.
        // In Splunk 5.0 not from the export endpoint, the results are
        // an array at that object's key "results".
        // In Splunk 4.3, the
        // array was the top level returned. So if we find an object
        // at top level, we step into it until we find the right key,
        // then leave it in that state to iterate over.
        try {
            // Json single-reader depends on 'isExport' flag to function.
            // It does not support a stream from a file saved from
            // a stream from an export endpoint.
            // Json multi-reader assumes export format thus does not support
            // a stream from none export endpoints.
            if (exportHelper != null) {
                if (jsonReader.peek() == JsonToken.BEGIN_ARRAY)
                    throw new UnsupportedOperationException(
                        "A stream from an export endpoint of " +
                        "a Splunk 4.x server in the JSON output format " +
                        "is not supported by this class. " +
                        "Use the XML search output format, " +
                        "and an XML result reader instead.");
                /*
                 * We're on a stream from an export endpoint
                 * Below is an example of an input stream.
                 *      {"preview":true,"offset":0,"lastrow":true,"result":{"host":"Andy-PC","count":"62"}}
                 *      {"preview":true,"offset":0,"result":{"host":"Andy-PC","count":"1682"}}
                 */
                // Read into first result object of the next set.
                while (true) {
                    boolean endPassed = exportHelper.lastRow;
                    exportHelper.skipRestOfRow();
                    if (!exportHelper.readIntoRow())
                        return false;
                    if (endPassed)
                        break;
                }
                return true;
            }
            // Single-reader not from an export endpoint
            if (jsonReader.peek() == JsonToken.BEGIN_OBJECT) {
                 /*
                  * We're on Splunk 5 with a single-reader not from
                  * an export endpoint
                  * Below is an example of an input stream.
                  *     {"preview":false,"init_offset":0,"messages":[{"type":"DEBUG","text":"base lispy: [ AND index::_internal ]"},{"type":"DEBUG","text":"search context: user=\"admin\", app=\"search\", bs-pathname=\"/Users/fross/splunks/splunk-5.0/etc\""}],"results":[{"sum(kb)":"14372242.758775","series":"twitter"},{"sum(kb)":"267802.333926","series":"splunkd"},{"sum(kb)":"5979.036338","series":"splunkd_access"}]}
                  */
                jsonReader.beginObject();
                String key;
                while (true) {
                    key = jsonReader.nextName();
                    if (key.equals("preview"))
                        readPreviewFlag();
                    else if (key.equals("results")) {
                        jsonReader.beginArray();
                        return true;
                    } else {
                        skipEntity();
                    }
                }
            } else { // We're on Splunk 4.x, and we just need to start the array.
                /*
                 * Below is an example of an input stream
                 *   [
                 *       {
                 *           "sum(kb)":"14372242.758775",
                 *               "series":"twitter"
                 *       },
                 *       {
                 *           "sum(kb)":"267802.333926",
                 *               "series":"splunkd"
                 *       },
                 *       {
                 *           "sum(kb)":"5979.036338",
                 *               "series":"splunkd_access"
                 *       }
                 *   ]
                 */
                jsonReader.beginArray();
                return true;
            }
        } catch (EOFException e) {
            return false;
        }
    }

    private void readPreviewFlag() throws IOException {
        isPreview = jsonReader.nextBoolean();
        previewFlagRead = true;
    }

    /**
     * Skip the next value, whether it is atomic or compound, in the JSON
     * stream.
     */
    private void skipEntity() throws IOException {
        if (jsonReader.peek() == JsonToken.STRING) {
            jsonReader.nextString();
        } else if (jsonReader.peek() == JsonToken.BOOLEAN) {
            jsonReader.nextBoolean();
        } else if (jsonReader.peek() == JsonToken.NUMBER) {
            jsonReader.nextDouble();
        } else if (jsonReader.peek() == JsonToken.NULL) {
            jsonReader.nextNull();
        } else if (jsonReader.peek() == JsonToken.NAME) {
            jsonReader.nextName();
        } else if (jsonReader.peek() == JsonToken.BEGIN_ARRAY) {
            jsonReader.beginArray();
            while (jsonReader.peek() != JsonToken.END_ARRAY) {
                skipEntity();
            }
            jsonReader.endArray();
        } else if (jsonReader.peek() == JsonToken.BEGIN_OBJECT) {
            jsonReader.beginObject();
            while (jsonReader.peek() != JsonToken.END_OBJECT) {
                skipEntity();
            }
            jsonReader.endObject();
        }
    }

    /** {@inheritDoc} */
    @Override public void close() throws IOException {
        super.close();
        if (jsonReader != null)
            jsonReader.close();
        jsonReader = null;
    }

    /** {@inheritDoc} */
    public boolean isPreview(){
        if (!previewFlagRead)
            throw new UnsupportedOperationException(
                "isPreview() is not supported " +
                "with a stream from a Splunk 4.x server by this class. " +
                "Use the XML format and an XML result reader instead.");
        return isPreview;
    }

    /**
     * This method is not supported.
     * @return Not applicable.
     */
    public Collection<String> getFields(){
        throw new UnsupportedOperationException(
                "getFields() is not supported by this subclass.");
    }

    @Override Event getNextEventInCurrentSet() throws IOException {
        if (exportHelper != null) {
            // If the last row has been passed and moveToNextStreamPosition
            // has not been called, end the current set.
            if (exportHelper.lastRow && !exportHelper.inRow ) {
                return null;
            }
            exportHelper.readIntoRow();
        }

        Event returnData = readEvent();

        if (exportHelper != null) {
            exportHelper.skipRestOfRow();
            return returnData;
        }
        // Single reader not from export
        if (returnData == null)
            close();
        return returnData;
    }

    private Event readEvent() throws IOException {
        Event returnData = null;
        String name = null;
        List<String> values = new ArrayList<String>();

        if (jsonReader == null)
            return null;

        // Events are almost flat, so no need for a true general parser
        // solution. But the Gson parser is a little unintuitive here. Nested
        // objects, have their own relative notion of hasNext. This
        // means that for every object or array start, hasNext() returns false
        // and one must consume the closing (END) object to get back to the
        // previous object.
        while (jsonReader.hasNext()) {
            if (returnData == null) {
                returnData = new Event();
            }
            if (jsonReader.peek() == JsonToken.BEGIN_OBJECT) {
                jsonReader.beginObject();
            }
            if (jsonReader.peek() == JsonToken.BEGIN_ARRAY) {
                jsonReader.beginArray();
                // The Gson parser is a little unintuitive here. Nested objects,
                // have their own relative notion of hasNext; when hasNext()
                // is done, it is only for this array.
                while (jsonReader.hasNext()) {
                    JsonToken jsonToken2 = jsonReader.peek();
                    if (jsonToken2 == JsonToken.STRING) {
                        values.add(jsonReader.nextString());
                    }
                }
                jsonReader.endArray();

                String[] valuesArray =
                    values.toArray(new String[values.size()]);
                returnData.putArray(name, valuesArray);

                values.clear();
            }
            if (jsonReader.peek() == JsonToken.NAME) {
                name = jsonReader.nextName();
            }
            if (jsonReader.peek() == JsonToken.STRING) {
                String delimitedValues = jsonReader.nextString();
                returnData.putSingleOrDelimited(name, delimitedValues);
            }
            if (jsonReader.peek() == JsonToken.END_OBJECT) {
                jsonReader.endObject();
                break;
            }
            if (jsonReader.peek() == JsonToken.END_ARRAY) {
                jsonReader.endArray();
            }
        }
        return returnData;
    }

    @Override boolean advanceStreamToNextSet() throws IOException{
        return advanceIntoNextSetBeforeEvent();
    }

    /**
     * Contains code only used for streams from the export endpoint.
     */
    private class ExportHelper {
        // Initial value must be true so that
        // the first row is treated as the start of a new set.
        boolean lastRow = true;
        boolean inRow;

        ExportHelper() { }

        // Return false if end of stream is encountered.
        private boolean readIntoRow() throws IOException {
            if (inRow)
                return true;
            if (jsonReader.peek() == JsonToken.END_DOCUMENT)
                return false;
            inRow = true;
            jsonReader.beginObject();
            // lastrow name and value pair does not appear if the row
            // is not the last in the set.
            lastRow = false;
            while (jsonReader.hasNext()) {
                String key = jsonReader.nextName();
                if (key.equals("preview")) {
                    readPreviewFlag();
                } else if (key.equals("lastrow")) {
                    lastRow = jsonReader.nextBoolean();
                } else if (key.equals("result")) {
                    return true;
                } else {
                    skipEntity();
                }
            }
            return false;
        }
                          
        private void skipRestOfRow() throws IOException {
            if (!inRow)
                return;
            inRow = false;
            while (jsonReader.peek() != JsonToken.END_OBJECT) {
                skipEntity();
            }
            jsonReader.endObject();
        }
    }
}
TOP

Related Classes of com.splunk.ResultsReaderJson$ExportHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.