/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.joiner; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.model.DataObject; /** * This object represents a single Class Query. * * @author watson * * @param <T extends DataObject> */ class JClass<T extends DataObject> { private static final Logger _log = LoggerFactory.getLogger(JClass.class); Class<T> clazz; // Java class of table being joined String alias; // the alias for this join ClassMetaData md; // meta-data for this type long maxCacheSize = Long.MAX_VALUE; // initialized no restriction private Set<URI> uris = new HashSet<URI>(); // URIs for this query String joinToAlias; // join to alias String joinToField; // field (attribute) name in joinTo class String field; // field in this class List<JSelection> selections = new ArrayList<JSelection>(); // Selections applied to this class for result private Map<URI, T> cachedObjects = new HashMap<URI, T>(); // cached objects boolean cacheValid = false; // set true if the cache contains valid results private Map<URI, Set<URI>> joinMap = new HashMap<URI, Set<URI>>(); Set<JClass> subJClasses = null; int index; // index in the jClasses list that orders evaluation of queries JClass(Class<T> clazz, String alias, int index) { this.clazz = clazz; this.alias = alias; this.index = index; md = new ClassMetaData(clazz); // If this class is abstract, process the sub-classes to find the real joins needed. if (md.isAbstract()) { long time = System.currentTimeMillis(); Set<Class<? extends DataObject>> subClasses = md.getSubclasses(clazz); time = System.currentTimeMillis() - time; _log.info("Subclass calculation time: " + time + " class: " + clazz.getSimpleName()); if (subClasses.isEmpty()) { throw new JoinerException("No meta-data for: " + clazz.getSimpleName()); } // Process each sub-class and make a JClass for the sub-class. subJClasses = new HashSet<JClass>(); for (Class subClass : subClasses) { JClass subJClass = new JClass(subClass, alias + "." + subClass.getSimpleName(), index); subJClasses.add(subJClass); } } } /** * Returns an iterator for the database objects from this query term. * * @param engine QueryEngine used to lookup objects * @return Iterator<T> that will iterate through the result objects */ Iterator<T> iterator(QueryEngine engine) { if (cacheValid) { return cachedObjects.values().iterator(); } else { return new JClassIterator<T>(this, engine); } } /** * Querys for a particular object given its URI. This method handles * subclasses correctly, and will query whatever subclass Column Family * is needed for the particular URI. * * @param engine -- the Query Engine to be used for the request * @param uri -- The URI of the requested Object, either of type T or a subclass * @return */ T queryObject(QueryEngine engine, URI uri) { if (md.isAbstract()) { String type = URIUtil.getTypeName(uri); for (JClass subJc : getSubJClasses()) { if (subJc.getClazz().getSimpleName().equals(type)) { return (T) engine.queryObject(subJc.getClazz(), uri); } } } else { return engine.queryObject(getClazz(), uri); } return null; } /** * Add an object to the cache if the cache is enabled and the cache is not * already at maximum capacity. Otherwise disable the cache and clear contents. * * @param object */ void addToCache(T object) { if (cacheValid == true) { if (cachedObjects.size() < maxCacheSize) { URI uri = object.getId(); cachedObjects.put(uri, object); } else { cacheValid = false; cachedObjects.clear(); } } } /** * Add a mapping between an object in the joinTo (i.e. the query * results this class is joined to) and an object in this query result. * The joinToMap maps each URI in the joinTo result to the set of objects * in this result. It is used for constructing output representations. * * @param joinToURI -- URI of object in joinToAlias query result * @param thisURI -- URI of result in this query result */ void addToJoinMap(URI joinToURI, URI thisURI) { if (joinMap.get(joinToURI) == null) { joinMap.put(joinToURI, new HashSet<URI>()); } joinMap.get(joinToURI).add(thisURI); } Class<T> getClazz() { return clazz; } void setClazz(Class<T> clazz) { this.clazz = clazz; } String getAlias() { return alias; } void setAlias(String alias) { this.alias = alias; } ClassMetaData getMetaData() { return md; } void setMd(ClassMetaData md) { this.md = md; } long getMaxCacheSize() { return maxCacheSize; } void setMaxCacheSize(long maxCacheSize) { this.maxCacheSize = maxCacheSize; } Set<URI> getUris() { return uris; } void setUris(Set<URI> uris) { this.uris = uris; } String getJoinToAlias() { return joinToAlias; } void setJoinToAlias(String joinToAlias) { this.joinToAlias = joinToAlias; } String getJoinToField() { return joinToField; } void setJoinToField(String joinToField) { this.joinToField = joinToField; } List<JSelection> getSelections() { return selections; } void setSelections(List<JSelection> selections) { this.selections = selections; } Map<URI, T> getCachedObjects() { return cachedObjects; } void setCachedObjects(Map<URI, T> cachedObjects) { this.cachedObjects = cachedObjects; } boolean isCacheValid() { return cacheValid; } void setCacheValid(boolean cacheValid) { this.cacheValid = cacheValid; } String getField() { return field; } void setField(String field) { this.field = field; } Map<URI, Set<URI>> getJoinMap() { return joinMap; } void setJoinMap(Map<URI, Set<URI>> joinMap) { this.joinMap = joinMap; } Set<JClass> getSubJClasses() { return subJClasses; } void setSubJClasses(Set<JClass> subJClasses) { this.subJClasses = subJClasses; } }