summaryrefslogtreecommitdiff
path: root/qpid/tools/src/java/qpid-qmf2/src/main/java/org/apache/qpid/qmf2/common/QmfQuery.java
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/tools/src/java/qpid-qmf2/src/main/java/org/apache/qpid/qmf2/common/QmfQuery.java')
-rw-r--r--qpid/tools/src/java/qpid-qmf2/src/main/java/org/apache/qpid/qmf2/common/QmfQuery.java343
1 files changed, 343 insertions, 0 deletions
diff --git a/qpid/tools/src/java/qpid-qmf2/src/main/java/org/apache/qpid/qmf2/common/QmfQuery.java b/qpid/tools/src/java/qpid-qmf2/src/main/java/org/apache/qpid/qmf2/common/QmfQuery.java
new file mode 100644
index 0000000000..c0555cc91f
--- /dev/null
+++ b/qpid/tools/src/java/qpid-qmf2/src/main/java/org/apache/qpid/qmf2/common/QmfQuery.java
@@ -0,0 +1,343 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.qmf2.common;
+
+// Misc Imports
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+// Reuse this class as it provides a handy mechanism to parse an predicate String into a Map
+import org.apache.qpid.messaging.util.AddressParser;
+
+/**
+ * A Query is a mechanism for interrogating the management database. A Query represents a selector which is sent to
+ * an Agent. The Agent applies the Query against its management database, and returns those objects which meet the
+ * constraints described in the query.
+ * <p>
+ * A Query must specify the class of information it is selecting. This class of information is considered the target
+ * of the query. Any data objects selected by the query will be of the type indicated by the target.
+ * <p>
+ * A Query may also specify a selector which is used as a filter against the set of all target instances. Only those
+ * instances accepted by the filter will be returned in response to the query.
+ * <p>
+ * N.B. There appear to be a number of differences in the description of the map encoding of a Query between the
+ * QMF2 API specified at <a href=https://cwiki.apache.org/confluence/display/qpid/QMFv2+API+Proposal>QMF2 API Proposal</a> and the
+ * QMF2 protocol that is specified at <a href=https://cwiki.apache.org/qpid/qmf-map-message-protocol.html>QMF Map
+ * Message Protocol</a> in particular the use of the underscore to specify key names e.g. "_what", "_where",
+ * "_object_id", "_schema_id".
+ * <p>
+ * This implementation trusts the protocol specification more than the API specification as the underscores are more
+ * consistent with the rest of the protocol and the underscored variants are what have been observed when querying
+ * the broker ManagementAgent.
+ * <p>
+ * A QmfQuery may be constructed as either an "ID" query (to query for a specific ObjectId or SchemaClassId) or a
+ * "PREDICATE" query (to query based upon an expression). Note that QMF considers string arguments in boolean
+ * expressions to be names of data values in the target object. When evaluating a predicate expression, QMF will fetch
+ * the value of the named data item from each candidate target object. The value is then used in the boolean expression.
+ * In other words, QMF considers string arguments to be variables in the expression. In order to indicate that a string
+ * should be treated as a literal instead, the string must be quoted using the "quote" expression.
+ * <p>
+ * <b>Examples</b>
+ * <p>
+ * Assume a QmfData type defines fields named "name", "address" and "town". The following predicate expression matches
+ * any instance with a name field set to "tross", or any instance where the name field is "jross", the address field is
+ * "1313 Spudboy Lane" and the town field is "Utopia":
+ * <p>
+ * <pre>
+ * ["or" ["eq" "name" ["quote" "tross"]]
+ * ["and" ["eq" "name" ["quote" "jross"]]
+ * ["eq" "address" ["quote" "1313 Spudboy Lane"]]
+ * ["eq" ["quote" "Utopia"] "town"]
+ * ]
+ * ]
+ * </pre>
+ * Assume a QmfData type with fields "name" and "age". A predicate to find all instances with name matching the regular
+ * expression "?ross" with an optional age field that is greater than the value 29 or less than 12 would be:
+ * <pre>
+ * ["and" ["re_match" "name" ["quote" "?ross"]]
+ * ["and" ["exists" "age"]
+ * ["or" ["gt" "age" 27] ["lt" "age" 12]]
+ * ]
+ * ]
+ * </pre>
+ * <p>
+ * The Expression structure is illustrated below in the context of its relationship with QmfQuery.
+ * <img src="doc-files/QmfQuery.png"/>
+ *
+ *
+ * @author Fraser Adams
+ */
+public final class QmfQuery extends QmfData
+{
+ public static final QmfQuery ID = new QmfQuery();
+ public static final QmfQuery PREDICATE = new QmfQuery();
+
+ private QmfQueryTarget _target;
+ private SchemaClassId _classId;
+ private String _packageName;
+ private String _className;
+ private ObjectId _objectId;
+ private List _predicate;
+ private Expression _expression;
+
+ /**
+ * This Constructor is only used to construct the ID and PREDICATE objects
+ */
+ private QmfQuery()
+ {
+ }
+
+ /**
+ * Construct an QmfQuery with no Selector from a QmfQueryTarget
+ * @param target the query target
+ */
+ public QmfQuery(final QmfQueryTarget target)
+ {
+ _target = target;
+ setValue("_what", _target.toString());
+ }
+
+ /**
+ * Construct an ID QmfQuery from a QmfQueryTarget and SchemaClassId
+ * @param target the query target
+ * @param classId the SchemaClassId to evaluate against
+ */
+ public QmfQuery(final QmfQueryTarget target, final SchemaClassId classId)
+ {
+ _target = target;
+ _classId = classId;
+ _packageName = _classId.getPackageName();
+ _className = _classId.getClassName();
+ setValue("_what", _target.toString());
+ setValue("_schema_id", _classId.mapEncode());
+ }
+
+ /**
+ * Construct an ID QmfQuery from a QmfQueryTarget and ObjectId
+ * @param target the query target
+ * @param objectId the ObjectId to evaluate against
+ */
+ public QmfQuery(final QmfQueryTarget target, final ObjectId objectId)
+ {
+ _target = target;
+ _objectId = objectId;
+ setValue("_what", _target.toString());
+ setValue("_object_id", _objectId.mapEncode());
+ }
+
+ /**
+ * Construct a PREDICATE QmfQuery from a QmfQueryTarget and predicate String
+ * @param target the query target
+ * @param predicateString the predicate to evaluate against
+ */
+ public QmfQuery(final QmfQueryTarget target, final String predicateString) throws QmfException
+ {
+ _target = target;
+
+ if (predicateString.charAt(0) == '[')
+ {
+ Map predicateMap = new AddressParser("{'_where': " + predicateString + "}").map();
+ _predicate = (List)predicateMap.get("_where");
+ _expression = Expression.createExpression(_predicate);
+ }
+ else
+ {
+ throw new QmfException("Invalid predicate format");
+ }
+
+ setValue("_what", _target.toString());
+ setValue("_where", _predicate);
+ }
+
+ /**
+ * Construct a QmfQuery from a Map encoding
+ * @param m encoding the query
+ */
+ public QmfQuery(final Map m) throws QmfException
+ {
+ super(m);
+
+ _target = QmfQueryTarget.valueOf(getStringValue("_what"));
+
+ if (hasValue("_object_id"))
+ {
+ _objectId = getRefValue("_object_id");
+ }
+
+ if (hasValue("_schema_id"))
+ {
+ _classId = new SchemaClassId((Map)getValue("_schema_id"));
+ _packageName = _classId.getPackageName();
+ _className = _classId.getClassName();
+ }
+
+ if (hasValue("_where"))
+ {
+ _predicate = (List)getValue("_where");
+ _expression = Expression.createExpression(_predicate);
+ }
+ }
+
+ /**
+ * Return target name.
+ * @return target name.
+ */
+ public QmfQueryTarget getTarget()
+ {
+ return _target;
+ }
+
+ /**
+ * Undefined by QMF2 API.
+ * <p>
+ * According to <a href=https://cwiki.apache.org/confluence/display/qpid/QMFv2+API+Proposal>QMF2 API Specification</a>
+ * "The value of the <target name string> map entry is ignored for now, its use is TBD."
+ * so this method returns a null Map.
+ */
+ public Map getTargetParam()
+ {
+ return null;
+ }
+
+ /**
+ * Return QmfQuery.ID or QmfQuery.PREDICATE or null if there is no Selector
+ * @return QmfQuery.ID or QmfQuery.PREDICATE or null if there is no Selector
+ */
+ public QmfQuery getSelector()
+ {
+ if (_predicate == null)
+ {
+ if (_objectId == null && _classId == null)
+ {
+ return null;
+ }
+ return ID;
+ }
+ return PREDICATE;
+ }
+
+ /**
+ * Return predicate expression if selector type is QmfQuery.PREDICATE
+ * @return predicate expression if selector type is QmfQuery.PREDICATE
+ */
+ public List getPredicate()
+ {
+ return _predicate;
+ }
+
+ /**
+ * Return the SchemaClassId if selector type is QmfQuery.ID
+ * @return the SchemaClassId if selector type is QmfQuery.ID
+ */
+ public SchemaClassId getSchemaClassId()
+ {
+ return _classId;
+ }
+
+ /**
+ * Return the ObjectId if selector type is QmfQuery.ID
+ * @return the ObjectId if selector type is QmfQuery.ID
+ */
+ public ObjectId getObjectId()
+ {
+ return _objectId;
+ }
+
+ /**
+ * Evaluate query against a QmfData instance.
+ * @return true if query matches the QmfData instance, else false.
+ */
+ public boolean evaluate(final QmfData data)
+ {
+ if (_predicate == null)
+ {
+ if (data instanceof QmfManaged)
+ {
+ QmfManaged managedData = (QmfManaged)data;
+ // Evaluate an ID query on Managed Data
+ if (_objectId != null && _objectId.equals(managedData.getObjectId()))
+ {
+ return true;
+ }
+ else if (_classId != null)
+ {
+ SchemaClassId dataClassId = managedData.getSchemaClassId();
+ String dataClassName = dataClassId.getClassName();
+ String dataPackageName = dataClassId.getPackageName();
+
+ // Wildcard the package name if it hasn't been specified when checking class name
+ if (_className.equals(dataClassName) &&
+ (_packageName.length() == 0 || _packageName.equals(dataPackageName)))
+ {
+ return true;
+ }
+
+ // Wildcard the class name if it hasn't been specified when checking package name
+ if (_packageName.equals(dataPackageName) &&
+ (_className.length() == 0 || _className.equals(dataClassName)))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ else
+ {
+ // Evaluate a PREDICATE query by evaluating against the expression created from the predicate
+ if (_predicate.size() == 0)
+ {
+ return true;
+ }
+
+ return _expression.evaluate(data);
+ }
+ }
+
+ /**
+ * Helper/debug method to list the QMF Object properties and their type.
+ */
+ @Override
+ public void listValues()
+ {
+ System.out.println("QmfQuery:");
+ System.out.println("target: " + _target);
+ if (_predicate != null)
+ {
+ System.out.println("selector: QmfQuery.PREDICATE");
+ System.out.println("predicate: " + _predicate);
+ }
+ else if (_classId != null)
+ {
+ System.out.println("selector: QmfQuery.ID");
+ _classId.listValues();
+ }
+ else if (_objectId != null)
+ {
+ System.out.println("selector: QmfQuery.ID");
+ System.out.println(_objectId);
+ }
+ }
+}
+