diff options
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.java | 343 |
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); + } + } +} + |