Commit 42222960 authored by Ryan Berkheimer's avatar Ryan Berkheimer
Browse files

added doc comments and added final modifiers where possible in request and schema utils classes

parent 10d2f671
......@@ -5,6 +5,7 @@
"conditions": [{"id": "is-relative-path",
"type": "comparison",
"operator": "contains",
"factory": "dummy",
"field": "file-path",
"value": "{}"}],
"collections": [{"id": "coll-1",
......
......@@ -3,18 +3,19 @@
* @author Ryan Berkheimer
*/
import gov.noaa.messageapi.interfaces.ISession;
import gov.noaa.messageapi.interfaces.IRequest;
import gov.noaa.messageapi.interfaces.IResponse;
import gov.noaa.messageapi.interfaces.IRejection;
import gov.noaa.messageapi.interfaces.IRecord;
import gov.noaa.messageapi.interfaces.IField;
import gov.noaa.messageapi.interfaces.ISession
import gov.noaa.messageapi.interfaces.IRequest
import gov.noaa.messageapi.interfaces.IResponse
import gov.noaa.messageapi.interfaces.IRecord
import gov.noaa.messageapi.sessions.DefaultSession;
import gov.noaa.messageapi.sessions.DefaultSession
/**
This Spock test class demonstrates end to end behaviors for reading and validating reads of files.
It should hold file reader transformation and file reader endpoint, and things like that.
*/
class FileReaderTests extends spock.lang.Specification {
/*def "Tests submission of a full file reader task with 1 small input."() {
given: "A standard condition test request"
ISession session = new DefaultSession("{}/resources/test/file-reader/manifest.json")
......@@ -27,19 +28,18 @@ class FileReaderTests extends spock.lang.Specification {
then: "We should have no rejections and there should be 7 records in the return set."
response.getRejections().size() == 0
response.getRecords().size() == 7
}*/
}*/
def "Tests submission of a full file reader task with 1 large input."() {
given: "A standard condition test request"
ISession session = new DefaultSession("{}/resources/test/file-reader/manifest.json")
IRequest request = session.createRequest();
IRecord record = request.createRecord();
record.setField("file-path", "{}/resources/test/inputs/file-reader/proc_sm_gtsnp_data_ftp_CF6_cf6_20190506.txt");
when: "We submit the test session and wait for completion"
IResponse response = request.submit();
given: 'A standard condition test request'
ISession session = new DefaultSession('{}/resources/test/file-reader/manifest.json')
IRequest request = session.createRequest()
IRecord record = request.createRecord()
record.setField('file-path', '{}/resources/test/inputs/file-reader/proc_sm_gtsnp_data_ftp_CF6_cf6_20190506.txt')
when: 'We submit the test session and wait for completion'
IResponse response = request.submit()
while (!response.isComplete()) {}
then: "We should have no rejections and there should be 79794 records in the return set."
//println "FIELD VALUE!!!"
then: 'We should have no rejections and there should be 79794 records in the return set.'
//println response.getRecords().get(0).getField("value").getValue()
response.getRejections().size() == 0
response.getRecords().size() == 79794
......
......@@ -12,7 +12,7 @@ import gov.noaa.messageapi.parsers.schemas.ConditionParser;
import gov.noaa.messageapi.parsers.schemas.ConditionOperatorParser;
/**
* A SchemaDefinition is used by the StorageService to parse a specified
* A SchemaDefinition is used to parse a specified
* configuration and create a blueprint for how the user interacts with
* a session. SchemaDefinitions hold raw and/or simple datatypes to be turned
* held, referenced, and converted to more complex types over the course of
......@@ -46,7 +46,7 @@ public class SchemaDefinition {
* @throws Exception An exception is thrown if there is an issue parsing the schema definition.
*/
@SuppressWarnings("unchecked")
public SchemaDefinition(Map<String, Object> properties) throws Exception {
public SchemaDefinition(final Map<String, Object> properties) throws Exception {
if (properties.containsKey("metadata")) {
this.parseMetadataSpec((String) properties.get("metadata"));
} else {
......@@ -58,82 +58,92 @@ public class SchemaDefinition {
throw new Exception("Missing necessary 'fields' key when parsing schema definition.");
}
if (properties.containsKey("conditions")) {
this.parseConditionSpec((Map<String,String>) properties.get("conditions"));
this.parseConditionSpec((Map<String, String>) properties.get("conditions"));
} else {
this.setEmptyConditions();
}
}
/**
* Copy constructor for SchemaDefinition. In the context of the Storage
* Service, this constructor is used to maintain copies of the schema
* definition per-request. This ensures no issues will be caused in the
* case that the definition is ever modified by another action during
* a specific request. The definition of a schema SHOULD be immutable,
* however the configurability of the service makes this impossible to
* guarantee.
* @param definition The original schema definition for the service when
* the request was submitted.
* Copy constructor for SchemaDefinition. In the context of the Storage Service,
* this constructor is used to maintain copies of the schema definition
* per-request. This ensures no issues will be caused in the case that the
* definition is ever modified by another action during a specific request. The
* definition of a schema SHOULD be immutable, however the configurability of
* the service makes this impossible to guarantee.
*
* @param definition The original schema definition for the service when the
* request was submitted.
*/
public SchemaDefinition(SchemaDefinition definition) {
this.metadataMap = new HashMap<String,Object>(definition.getMetadataMap());
this.fieldMaps = new ArrayList<Map<String,Object>>(definition.getFieldMaps());
this.conditionMaps = new ArrayList<Map<String,Object>>(definition.getConditionMaps());
public SchemaDefinition(final SchemaDefinition definition) {
this.metadataMap = new HashMap<String, Object>(definition.getMetadataMap());
this.fieldMaps = new ArrayList<Map<String, Object>>(definition.getFieldMaps());
this.conditionMaps = new ArrayList<Map<String, Object>>(definition.getConditionMaps());
this.conditionOperatorFactory = definition.getOperatorFactory().getCopy();
}
/**
* This method constructs the operator factory used by certain request
* comparisons. This class is bootstrapped at runtime to provide the
* user with an ability to either extend a basic set of comparisons, or
* specify complex or customized comparison types (on object values).
* @param operatorClass A class that defines operators used in condition-field comparisons
* @throws Exception Throws an exception if there is an error loading the operator class.
* comparisons. This class is bootstrapped at runtime to provide the user with
* an ability to either extend a basic set of comparisons, or specify complex or
* customized comparison types (on object values).
*
* @param operatorClass A class that defines operators used in condition-field
* comparisons
* @throws Exception Throws an exception if there is an error loading the
* operator class.
*/
private void createOperatorFactory(String operatorClass) throws Exception {
private void createOperatorFactory(final String operatorClass) throws Exception {
this.conditionOperatorFactory = ConditionOperatorParser.build(operatorClass);
}
/**
* Parses a metadata map from a location specified by a string.
* This map holds metadata information related to a user schema - a type,
* a schema name, etc.
* @param spec A string pointing to a JSON map containing a schema metadata spec.
* @throws Exception Throws an exception in the case that the map could not be parsed.
* Parses a metadata map from a location specified by a string. This map holds
* metadata information related to a user schema - a type, a schema name, etc.
*
* @param spec A string pointing to a JSON map containing a schema metadata
* spec.
* @throws Exception Throws an exception in the case that the map could not be
* parsed.
*/
private void parseMetadataSpec(String spec) throws Exception {
MetadataParser parser = new MetadataParser(spec);
private void parseMetadataSpec(final String spec) throws Exception {
final MetadataParser parser = new MetadataParser(spec);
this.metadataMap = parser.getMetadataMap();
}
/**
* Parses a field map from a location specified by a string.
* This map holds a flat list of fields that the user will interact with
* through the record interface. Every field holds a set of properties.
* @param spec A string pointing to a JSON map containing a schema field spec.
* @throws Exception Throws an exception in the case that the map could not be parsed.
* Parses a field map from a location specified by a string. This map holds a
* flat list of fields that the user will interact with through the record
* interface. Every field holds a set of properties.
*
* @param spec A string pointing to a JSON map containing a schema field spec.
* @throws Exception Throws an exception in the case that the map could not be
* parsed.
*/
private void parseFieldSpec(String spec) throws Exception {
FieldParser parser = new FieldParser(spec);
private void parseFieldSpec(final String spec) throws Exception {
final FieldParser parser = new FieldParser(spec);
this.fieldMaps = parser.getFieldMaps();
}
/**
* Parses a condition map from a location specified by a string and a factory from a given class string.
* The condition map holds a flat list of conditions that the user will interact with
* through the record interface. Every condition holds a set of properties.
* Properties of conditions are mostly immutable, except for values of basic
* comparison conditions.
* The factory class usually should contain switches for each condition operator referenced
* in a condition map. If no factory is provided, the default factory is used, which handles
* basic (=, /=, <, >, <=, >=) operations on basic (boolean, float, double, int, string, datetime) datatypes.
* If no conditions are found, currently the
* @param conditionMap A conditionMap containing map and factory keywords
* @throws Exception Throws an exception in the case that the map could not be parsed.
* Parses a condition map from a location specified by a string and a factory
* from a given class string. The condition map holds a flat list of conditions
* that the user will interact with through the record interface. Every
* condition holds a set of properties. Properties of conditions are mostly
* immutable, except for values of basic comparison conditions. The factory
* class usually should contain switches for each condition operator referenced
* in a condition map. If no factory is provided, the default factory is used,
* which handles basic (=, /=, <, >, <=, >=) operations on basic (boolean,
* float, double, int, string, datetime) datatypes. If no conditions are found,
* currently the
*
* @param conditionMap A conditionMap containing map and factory keywords
* @throws Exception Throws an exception in the case that the map could not be
* parsed.
*/
private void parseConditionSpec(Map<String,String> conditionMap) throws Exception {
private void parseConditionSpec(final Map<String, String> conditionMap) throws Exception {
if (conditionMap.containsKey("map")) {
ConditionParser parser = new ConditionParser((String) conditionMap.get("map"));
final ConditionParser parser = new ConditionParser((String) conditionMap.get("map"));
this.conditionMaps = parser.getConditionMaps();
if (conditionMap.containsKey("factory")) {
this.createOperatorFactory((String) conditionMap.get("factory"));
......
package gov.noaa.messageapi.exceptions;
import java.lang.Exception;
public class ConfigurationParsingException extends Exception {
public class ConfigurationParsingException extends MessageApiException {
/**
* Required (generated) serial version ID for serializable class
......
package gov.noaa.messageapi.exceptions;
import java.lang.Exception;
public class MessageApiException extends Exception {
/**
*
*/
private static final long serialVersionUID = -4783565590470822212L;
/**
* Required (generated) serial version ID for serializable class
*/
public MessageApiException(String message, Throwable err) {
super(message, err);
}
public MessageApiException(String message) {
super(message);
}
}
......@@ -4,6 +4,11 @@ import java.util.Map;
import java.util.List;
/**
* The Transformation is a container that consumes a map of transformation ids
* and corresponding record lists. Any business logic that manipulates records can be used in a transformation.
* Transformation implementations should endeavor to maintain statelessness. The default Transformation implementation
* is automatically stateless in the case that the transformed records contain basic field types (Integer, String, Byte, etc.)
* and calls a copy method for user defined object types.
* @author Ryan Berkheimer
*/
public interface ITransformation {
......
......@@ -15,6 +15,9 @@ import gov.noaa.messageapi.utils.containers.CollectionUtils;
/**
* This class contains static utilities for creating, parsing, or otherwise manipulating
* containers. Containers are record collections and include collections, transformations,
* and classifiers.
* @author Ryan Berkheimer
*/
public class ContainerUtils {
......@@ -29,10 +32,12 @@ public class ContainerUtils {
* @param records The schema records to be converted to container records
* @return A list of container records
*/
public static List<IContainerRecord> convertSchemaRecords(ISchema schema, IContainer container, IRecord requestRecord, List<IRecord> schemaRecords) {
IContainerRecord containerRecordTemplate = createRecordTemplate(container);
public static List<IContainerRecord> convertSchemaRecords(final ISchema schema, final IContainer container,
final IRecord requestRecord, final List<IRecord> schemaRecords) {
final IContainerRecord containerRecordTemplate = createRecordTemplate(container);
return schemaRecords.stream().map(schemaRecord -> {
IContainerRecord containerRecord = CollectionUtils.setFieldValues(containerRecordTemplate.getCopy(), schemaRecord.getFields());
final IContainerRecord containerRecord = CollectionUtils.setFieldValues(containerRecordTemplate.getCopy(),
schemaRecord.getFields());
return CollectionUtils.validateCollectionConditions(schema, containerRecord, requestRecord);
}).collect(Collectors.toList());
}
......@@ -41,11 +46,14 @@ public class ContainerUtils {
* Creates an empty record for use in record conversion by copy. Uses the
* definition of the provided container to create a new empty record object,
* with
* @param container The container containing a definition that the template ContainerRecord object will be based on
* @return A new, empty,
*
* @param container The container containing a definition that the template
* ContainerRecord object will be based on
* @return A new, empty,
*/
public static IContainerRecord createRecordTemplate(IContainer container) {
List<ICollection> collections = CollectionUtils.buildCollections(container.getDefinition().getCollectionMaps());
public static IContainerRecord createRecordTemplate(final IContainer container) {
final List<ICollection> collections = CollectionUtils
.buildCollections(container.getDefinition().getCollectionMaps());
return new ContainerRecord(collections);
}
......
......@@ -13,30 +13,34 @@ import gov.noaa.messageapi.interfaces.IRecord;
import gov.noaa.messageapi.packets.DefaultPacket;
import gov.noaa.messageapi.utils.general.ListUtils;
//import gov.noaa.messageapi.utils.request.SchemaUtils;
//import gov.noaa.messageapi.utils.request.RejectionUtils;
/**
* This class contains static utilities related to creating, parsing, routing, or otherwise
* manipulating Packets. Packets are containers that hold records and rejections and are
* the return type of endpoints.
* @author Ryan Berkheimer
*/
public class PacketUtils {
public static IPacket create(ISchema schema, List<IRecord> records) {
IPacket dataPacket = new DefaultPacket();
List<IRejection> primaryRejections = RejectionUtils.getRequiredFieldRejections(records);
List<IRecord> filteredRecords = SchemaUtils.filterFieldlessConditions(
SchemaUtils.filterNonValuedConditions(
SchemaUtils.filterRejections(records, primaryRejections)));
List<IRejection> secondaryRejections = RejectionUtils.getFieldConditionRejections(schema, filteredRecords);
public static IPacket create(final ISchema schema, final List<IRecord> records) {
final IPacket dataPacket = new DefaultPacket();
final List<IRejection> primaryRejections = RejectionUtils.getRequiredFieldRejections(records);
final List<IRecord> filteredRecords = SchemaUtils.filterFieldlessConditions(
SchemaUtils.filterNonValuedConditions(SchemaUtils.filterRejections(records, primaryRejections)));
final List<IRejection> secondaryRejections = RejectionUtils.getFieldConditionRejections(schema,
filteredRecords);
dataPacket.setRecords(SchemaUtils.filterRejections(filteredRecords, secondaryRejections));
dataPacket.setRejections(ListUtils.flatten(new ArrayList<List<IRejection>>(Arrays.asList(primaryRejections, secondaryRejections))));
dataPacket.setRejections(ListUtils
.flatten(new ArrayList<List<IRejection>>(Arrays.asList(primaryRejections, secondaryRejections))));
return dataPacket;
}
public static IPacket combineResults(List<IPacket> packets) {
List<IRecord> allRecords = ListUtils.flatten(packets.stream().map(packet -> packet.getRecords()).collect(Collectors.toList()));
List<IRejection> allRejections = ListUtils.flatten(packets.stream().map(packet -> packet.getRejections()).collect(Collectors.toList()));
public static IPacket combineResults(final List<IPacket> packets) {
final List<IRecord> allRecords = ListUtils
.flatten(packets.stream().map(packet -> packet.getRecords()).collect(Collectors.toList()));
final List<IRejection> allRejections = ListUtils
.flatten(packets.stream().map(packet -> packet.getRejections()).collect(Collectors.toList()));
return new DefaultPacket(allRecords, allRejections);
}
......
......@@ -21,6 +21,10 @@ import gov.noaa.messageapi.records.protocol.ProtocolRecord;
/**
* This class contains static utilities related to creating, parsing, analyzing, or otherwise manipulating
* protocol records. Recall that protocol records belong to the protocol layer and are the primary access method
* for container records of any container type (transformation, collection, classifier, or UUID group) from
* within an endpoint.
* @author Ryan Berkheimer
*/
public class ProtocolUtils {
......@@ -35,89 +39,115 @@ public class ProtocolUtils {
* @param containerRecords A list of container Records to be parsed, categorized, and loaded into protocol records
* @return A list of protocol records, one per connection
*/
public static List<IProtocolRecord> convertContainerRecords(IProtocol protocol, List<IContainerRecord> containerRecords) {
public static List<IProtocolRecord> convertContainerRecords(final IProtocol protocol,
final List<IContainerRecord> containerRecords) {
return protocol.getConnections().stream().map(conn -> {
return createProtocolRecord(conn, containerRecords);
}).collect(Collectors.toList());
}
/**
* The singular method called by convertContainerRecords, accepts a single connection and a list of container records,
* and creates a protocol record based on what the connection specified as data needs
* @param connection A connection containing information on what type of records it needs (classifications, collections, transformations)
* @param containerRecords A list of container records that will be the basis for records added to the protocol record
* @return A protocol record, containing a record map based on the containerRecords, for the given connection
* The singular method called by convertContainerRecords, accepts a single
* connection and a list of container records, and creates a protocol record
* based on what the connection specified as data needs
*
* @param connection A connection containing information on what type of
* records it needs (classifications, collections,
* transformations)
* @param containerRecords A list of container records that will be the basis
* for records added to the protocol record
* @return A protocol record, containing a record map based on the
* containerRecords, for the given connection
*/
public static IProtocolRecord createProtocolRecord(IConnection connection, List<IContainerRecord> containerRecords) {
List<String> connCollections = connection.getCollections();
Map<String,List<Object>> connClassifiers = connection.getClassifiers();
List<Map<IRecord, Map<String,Object>>> recordList = ListUtils.flatten(containerRecords.stream().map(containerRecord -> {
UUID recordId = containerRecord.getId();
return ListUtils.removeAllNulls(containerRecord.getCollections().stream().map(collection -> {
return convertCollectionToRecord(collection, recordId, connCollections, connClassifiers);
}).collect(Collectors.toList()));
}).collect(Collectors.toList()));
public static IProtocolRecord createProtocolRecord(final IConnection connection,
final List<IContainerRecord> containerRecords) {
final List<String> connCollections = connection.getCollections();
final Map<String, List<Object>> connClassifiers = connection.getClassifiers();
final List<Map<IRecord, Map<String, Object>>> recordList = ListUtils
.flatten(containerRecords.stream().map(containerRecord -> {
final UUID recordId = containerRecord.getId();
return ListUtils.removeAllNulls(containerRecord.getCollections().stream().map(collection -> {
return convertCollectionToRecord(collection, recordId, connCollections, connClassifiers);
}).collect(Collectors.toList()));
}).collect(Collectors.toList()));
return new ProtocolRecord(connection, MapUtils.mergeMapList(recordList));
}
/**
* Creates a new collection record map entry for the IProtocolRecord map. This entry has a key of
* the record itself (created from the collection field set),
* with a map value, that itself has keys of UUID (uuid type), COLLECTION (string collID), and CLASSIFIERS (Map<String, Object>).
* Creates a new collection record map entry for the IProtocolRecord map. This
* entry has a key of the record itself (created from the collection field set),
* with a map value, that itself has keys of UUID (uuid type), COLLECTION
* (string collID), and CLASSIFIERS (Map<String, Object>).
*
* @param collection The collection that will serve as the basis for the record (record uses collection fields)
* @param recordId A record ID to associated with this record - the UUID of the container it came from
* @param connCollections A list of collections referenced by a connection that will be used to
* determine if this record fits in the given record
* @param connClassifiers A map of classifiers referenced by a connection that this record will be attached to
* @return A new map consisting of a record (IRecord) and a set of associated properties (in a map).
* If none of the properties match, this method returns null.
* @param collection The collection that will serve as the basis for the
* record (record uses collection fields)
* @param recordId A record ID to associated with this record - the UUID
* of the container it came from
* @param connCollections A list of collections referenced by a connection that
* will be used to determine if this record fits in the
* given record
* @param connClassifiers A map of classifiers referenced by a connection that
* this record will be attached to
* @return A new map consisting of a record (IRecord) and a set of associated
* properties (in a map). If none of the properties match, this method
* returns null.
*/
public static Map<IRecord, Map<String,Object>> convertCollectionToRecord(ICollection collection, UUID recordId,
List<String> connCollections, Map<String,List<Object>> connClassifiers) {
String collectionId = collection.getId();
Map<String, Object> collectionClassifiers = collection.getClassifiers();
public static Map<IRecord, Map<String, Object>> convertCollectionToRecord(final ICollection collection,
final UUID recordId, final List<String> connCollections, final Map<String, List<Object>> connClassifiers) {
final String collectionId = collection.getId();
final Map<String, Object> collectionClassifiers = collection.getClassifiers();
if (collectionMatch(collectionId, connCollections) || classifierMatch(collectionClassifiers, connClassifiers)) {
return makeCollectionRecordMap(recordId, new SchemaRecord(collection.getFields()), collectionId, collectionClassifiers);
return makeCollectionRecordMap(recordId, new SchemaRecord(collection.getFields()), collectionId,
collectionClassifiers);
}
return null;
}
/**
* Creates a record map for the given record. This record map will be one of many that are merged into
* a single record map for the ProtocolRecord (one map per protocol record). This map
* associates the record object (as a key) with a map of associated properties (as the value).
* These properties included the UUID of the ancestral container, the collectionId of the collection which
* was used in the record creation, and a map of classifiers (key/value pairs) that categorize the record.
* Please note - that in general, objects are not good candidates for map keys, due to key operations being
* indeterminate if objects are changed. However, in this case, we use the value map as the search criteria.
* The record as the key is what's used in processing if some value map parameter matches, making this pattern
* unconcerned with holding the record as a map key.
* @param id A UUID belonging to the parent container (all collections get the same UUID)
* @param record A record created from the collection field set
* @param collectionId The id (string) of the collection used to create the record
* @param classifiers A map of classifiers that
* @return a map for the record
* Creates a record map for the given record. This record map will be one of
* many that are merged into a single record map for the ProtocolRecord (one map
* per protocol record). This map associates the record object (as a key) with a
* map of associated properties (as the value). These properties included the
* UUID of the ancestral container, the collectionId of the collection which was
* used in the record creation, and a map of classifiers (key/value pairs) that
* categorize the record. Please note - that in general, objects are not good
* candidates for map keys, due to key operations being indeterminate if objects
* are changed. However, in this case, we use the value map as the search
* criteria. The record as the key is what's used in processing if some value
* map parameter matches, making this pattern unconcerned with holding the
* record as a map key.
*
* @param id A UUID belonging to the parent container (all collections
* get the same UUID)
* @param record A record created from the collection field set
* @param collectionId The id (string) of the collection used to create the
* record
* @param classifiers A map of classifiers that
* @return a map for the record
*/
public static Map<IRecord,Map<String,Object>> makeCollectionRecordMap(UUID id, IRecord record, String collectionId, Map<String,Object> classifiers) {
Map<IRecord,Map<String,Object>> retMap = new HashMap<IRecord, Map<String,Object>>();
Map<String, Object> valMap = new HashMap<String, Object>();
public static Map<IRecord, Map<String, Object>> makeCollectionRecordMap(final UUID id, final IRecord record,
final String collectionId, final Map<String, Object> classifiers) {
final Map<IRecord, Map<String, Object>> retMap = new HashMap<IRecord, Map<String, Object>>();
final Map<String, Object> valMap = new HashMap<String, Object>();
valMap.put("UUID", id);
valMap.put("COLLECTION", collectionId);
valMap.put("CLASSIFIERS",classifiers);
valMap.put("CLASSIFIERS", classifiers);
retMap.put(record, valMap);
return retMap;
}
/**
* Determines if a collection ID is contained within a connection list of
* associated collections (or if the connection wants all collections) and returns
* a boolean indicating true (if it matches) or false in the negative case
* @param collectionId An id of the collection in question
* @param connCollections A list of collection ids associated with a given connection
* @return true if a match, otherwise false
* associated collections (or if the connection wants all collections) and
* returns a boolean indicating true (if it matches) or false in the negative
* case
*
* @param collectionId An id of the collection in question
* @param connCollections A list of collection ids associated with a given
* connection
* @return true if a match, otherwise false
*/
public static Boolean collectionMatch(String collectionId, List<String> connCollections) {
public static Boolean collectionMatch(final String collectionId, final List<String> connCollections) {
if (connCollections.contains(collectionId) || connCollections.contains("*")) {
return true;
}
......@@ -125,15 +155,19 @@ public class ProtocolUtils {
}
/**
* Determines if a given collection classifier matches any of the associated classifiers
* referenced by a connection. This method first checks for classifier key matches,
* and then for any matching key, checks the value. Returns true if a match, otherwise false
* @param collectionClassifiers A set of classifiers attached to the collection
* @param connClassifiers A set of classifers attached to the connection
* @return true if classifiers match, false otherwise
* Determines if a given collection classifier matches any of the associated
* classifiers referenced by a connection. This method first checks for
* classifier key matches, and then for any matching key, checks the value.
* Returns true if a match, otherwise false
*
* @param collectionClassifiers A set of classifiers attached to the collection
* @param connClassifiers A set of classifers attached to the connection
* @return true if classifiers match, false otherwise
*/