/* * Copyright 2014 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.JsonArray; import com.google.gson.JsonElement; import java.util.*; import java.util.Map.Entry; /** * DataModelObject represents one of the structured views in a data model. */ public class DataModelObject { private DataModel model; private String name; private String[] lineage; private String displayName; private String parentName; private Map<String, DataModelField> autoextractedFields; private Collection<DataModelConstraint> constraints; private Map<String, DataModelCalculation> calculations; protected DataModelObject(DataModel model) { this.model = model; } /** * Checks whether there is a field with the given name in this * data model object. * * @param fieldName name of the field to check for. * @return true if there is such a field; false otherwise. */ public boolean containsField(String fieldName) { if (autoextractedFields.containsKey(fieldName)) { return true; } for (DataModelCalculation c : calculations.values()) { if (c.containsGeneratedField(fieldName)) { return true; } } return false; } /** * Local acceleration is tsidx acceleration of a data model object that is handled * manually by a user. You create a job which generates an index, and then use that * index in your pivots on the data model object. * * The namespace created by the job is 'sid={sid}' where {sid} is the job's sid. You * would use it in another job by starting your search query with * * | tstats ... from sid={sid} | ... * * The tsidx index created by this job is deleted when the job is garbage collected * by Splunk. * * It is the user's responsibility to manage this job, including cancelling it. * * @return a Job writing a tsidx index. */ public Job createLocalAccelerationJob() { return createLocalAccelerationJob(null); } /** * Local acceleration is tsidx acceleration of a data model object that is handled * manually by a user. You create a job which generates an index, and then use that * index in your pivots on the data model object. * * The namespace created by the job is 'sid={sid}' where {sid} is the job's sid. You * would use it in another job by starting your search query with * * | tstats ... from sid={sid} | ... * * The tsidx index created by this job is deleted when the job is garbage collected * by Splunk. * * It is the user's responsibility to manage this job, including cancelling it. * * @param earliestTime A time modifier (e.g., "-2w") setting the earliest time to index. * @return a Job writing a tsidx index. */ public Job createLocalAccelerationJob(String earliestTime) { String query = "| datamodel " + this.model.getName() + " " + this.getName() + " search | tscollect"; JobArgs args = new JobArgs(); if (earliestTime != null) { args.setEarliestTime(earliestTime); } return this.model.getService().search(query, args); } /** * Return the calculations done by this data model object to produce fields. * * Each calculation has a unique ID assigned to it by splunkd, which is the key * in the returned map. For most purposes you will probably only want the values. * * @return a map of calculation IDs to DataModelCalculation objects. */ public Map<String, DataModelCalculation> getCalculations() { return Collections.unmodifiableMap(this.calculations); } /** * Fetch a calculation by its unique ID. * * @param calculationId a splunkd assigned unique ID for this calculation. * @return a DataModelCalculation object. */ public DataModelCalculation getCalculation(String calculationId) { return this.calculations.get(calculationId); } /** * @return a collection of the constraints limiting events that will appear in this data model object. */ public Collection<DataModelConstraint> getConstraints() { return Collections.unmodifiableCollection(this.constraints); } /** * Fetch the data model on which this object is defined. * * @return A DataModel instance containing this object. */ public DataModel getDataModel() { return this.model; } /** * @return the human readable name of this data model object. */ public String getDisplayName() { return this.displayName; } /** * Fetch a single field of a given name from this data model object. * * @param fieldName Name of the field to fetch. * @return A DataModelField object, or null if there is no field of the given name. */ public DataModelField getField(String fieldName) { if (autoextractedFields.containsKey(fieldName)) { return autoextractedFields.get(fieldName); } for (DataModelCalculation c : this.calculations.values()) { if (c.containsGeneratedField(fieldName)) { return c.getGeneratedField(fieldName); } } return null; } /** * Get a collection of objects specifying all the fields that were automatically extracted * from events (as opposed to generated by calculations in a data model). * * @return a collection of DataModelField objects. */ public Collection<DataModelField> getAutoExtractedFields() { return Collections.unmodifiableCollection(autoextractedFields.values()); } /** * Return all the fields, whether input or created by calculations. * @return a collection of DataModelField objects. */ public Collection<DataModelField> getFields() { Collection<DataModelField> fields = new ArrayList<DataModelField>(); fields.addAll(this.autoextractedFields.values()); for (DataModelCalculation c : this.calculations.values()) { fields.addAll(c.getGeneratedFields()); } return fields; } public String getQuery() { return "| datamodel " + this.getDataModel().getName() + " " + this.getName() + " search"; } /** * @return Splunk's identifier for this data model object. */ public String getName() { return this.name; } /** * Data model objects can inherit from other data model objects * in the same data model (or from a couple of global base objects * such as BaseEvent and BaseTransaction). The lineage is a list of * data model object names tracing this inheritance, starting with the * most remote ancestor and ending with this object. * * @return An array of names, starting with this object's name, followed by * the names up the hierarchy. */ public String[] getLineage() { return this.lineage; } /** * Returns the name of the parent of this object. * * @return a String giving the name. */ public String getParentName() { return this.parentName; } /** * @return the data model object this one inherits from if it is a user defined data model object * in the same data model; otherwise returns null (for example if the data model object inherits from BaseEvent * or BaseTransaction). */ public DataModelObject getParent() { return this.getDataModel().getObject(this.parentName); } /** * Create a PivotSpecification on this data model object. * * @return a PivotSpecification instance. */ public PivotSpecification createPivotSpecification() { return new PivotSpecification(this); } /** * Start a job that fetches all the events of this data model object. * * @return a Job object. */ public Job runQuery() { return runQuery("", null); } /** * Start a job that fetches all the events of this data model object. * * @param args arguments specifying behavior of the job. * @return a Job object. */ public Job runQuery(JobArgs args) { return runQuery("", args); } /** * Start a job that applies querySuffix to all the events in this data model object. * * @param querySuffix a search query, starting with a '|' that will be appended to the command to fetch * the contents of this data model object (e.g., "| head 3"). * @return a Job object. */ public Job runQuery(String querySuffix) { return runQuery(querySuffix, null); } /** * Start a job that applies querySuffix to all the events in this data model object. * * @param querySuffix a search query, starting with a '|' that will be appended to the command to fetch * the contents of this data model object (e.g., "| head 3"). * @param args arguments to control the job. * @return a Job object. */ public Job runQuery(String querySuffix, JobArgs args) { return getDataModel().getService().search(getQuery() + querySuffix, args); } /** * Produce a data model object from a JSON dictionary specifying it plus a data model that contains it. * @param dataModel a DataModel instance that contains this data model object. * @param object a JsonElement (as produced by Gson) specifying this data model object (usually one of * the entries in the array of objects in the JSON description of the data model). * @return a DataModelObject instance. */ static DataModelObject parse(DataModel dataModel, JsonElement object) { String name = null; String displayName = null; String comment = null; String[] lineage = new String[0]; String parentName = null; Map<String, DataModelField> fields = new HashMap<String, DataModelField>(); Collection<String> children = new ArrayList<String>(); Collection<DataModelConstraint> constraints = new ArrayList<DataModelConstraint>(); Map<String, DataModelCalculation> calculations = new HashMap<String, DataModelCalculation>(); // Fields specific to objects inheriting directly from BaseSearch. String baseSearch = null; // Fields specific to objects inheriting directly from BaseTransaction String transactionMaxPause = null; String transactionMaxTimeSpan = null; Collection<String> groupByFields = new ArrayList<String>(); Collection<String> objectsToGroup = new ArrayList<String>(); for (Entry<String, JsonElement> entry : object.getAsJsonObject().entrySet()) { if (entry.getKey().equals("objectName")) { name = entry.getValue().getAsString(); } else if (entry.getKey().equals("displayName")) { displayName = entry.getValue().getAsString(); } else if (entry.getKey().equals("lineage")) { lineage = entry.getValue().getAsString().split("\\."); } else if (entry.getKey().equals("parentName")) { parentName = entry.getValue().getAsString(); } else if (entry.getKey().equals("fields")) { JsonArray fieldsJson = entry.getValue().getAsJsonArray(); fields.clear(); for (JsonElement fieldJson : fieldsJson) { DataModelField field = DataModelField.parse(fieldJson); fields.put(field.getName(), field); } } else if (entry.getKey().equals("constraints")) { JsonArray constraintsJson = entry.getValue().getAsJsonArray(); for (JsonElement constraintJson : constraintsJson) { DataModelConstraint constraint = DataModelConstraint.parse(constraintJson); constraints.add(constraint); } } else if (entry.getKey().equals("calculations")) { calculations.clear(); for (JsonElement cjson : entry.getValue().getAsJsonArray()) { DataModelCalculation c = DataModelCalculation.parse(cjson); String cid = c.getCalculationID(); calculations.put(cid, c); } } else if (entry.getKey().equals("baseSearch")) { baseSearch = entry.getValue().getAsString(); } else if (entry.getKey().equals("transactionMaxPause")) { transactionMaxPause = entry.getValue().getAsString(); } else if (entry.getKey().equals("transactionMaxTimeSpan")) { transactionMaxTimeSpan = entry.getValue().getAsString(); } else if (entry.getKey().equals("groupByFields")) { for (JsonElement e : entry.getValue().getAsJsonArray()) { groupByFields.add(e.getAsString()); } } else if (entry.getKey().equals("objectsToGroup")) { for (JsonElement e : entry.getValue().getAsJsonArray()) { objectsToGroup.add(e.getAsString()); } } } DataModelObject dmo; // Create the right subclass of DataModelObject. if (baseSearch != null) { dmo = new DataModelSearch(dataModel); } else if (transactionMaxPause != null) { dmo = new DataModelTransaction(dataModel); } else { dmo = new DataModelObject(dataModel); } // Set the fields common to all data model objects dmo.name = name; dmo.displayName = displayName; dmo.lineage = lineage; dmo.parentName = parentName; dmo.autoextractedFields = fields; dmo.constraints = constraints; dmo.calculations = calculations; // Set the fields of particular types if (baseSearch != null) { ((DataModelSearch)dmo).baseSearch = baseSearch; } else if (transactionMaxPause != null) { ((DataModelTransaction)dmo).groupByFields = groupByFields; ((DataModelTransaction)dmo).objectsToGroup = objectsToGroup; ((DataModelTransaction)dmo).maxPause = transactionMaxPause; ((DataModelTransaction)dmo).maxSpan = transactionMaxTimeSpan; } else { // Has no additional fields } return dmo; } }