package org.cloudgraph.web.model.cache; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.faces.bean.ApplicationScoped; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.model.SelectItem; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cloudgraph.web.WebConstants; import org.cloudgraph.web.query.ClassQuery; import org.cloudgraph.web.query.EnumerationQuery; import org.cloudgraph.web.query.PackageQuery; import org.cloudgraph.web.query.PrimitiveTypeQuery; import org.cloudgraph.web.query.PropertyQuery; import org.cloudgraph.web.sdo.categorization.Category; import org.cloudgraph.web.sdo.categorization.Taxonomy; import org.cloudgraph.web.sdo.categorization.TaxonomyMap; import org.cloudgraph.web.sdo.core.Organization; import org.cloudgraph.web.sdo.meta.Clazz; import org.cloudgraph.web.sdo.meta.Enumeration; import org.cloudgraph.web.sdo.meta.Package; import org.cloudgraph.web.sdo.meta.PrimitiveType; import org.cloudgraph.web.sdo.meta.Property; import org.cloudgraph.web.sdo.visitor.OrganizationCollector; import org.plasma.query.Query; import org.plasma.sdo.PlasmaDataGraph; import org.plasma.sdo.PlasmaDataGraphVisitor; import org.plasma.sdo.PlasmaDataObject; import org.plasma.sdo.access.client.SDODataAccessClient; import org.plasma.sdo.helper.PlasmaXMLHelper; import org.plasma.sdo.xml.DefaultOptions; import commonj.sdo.DataGraph; import commonj.sdo.DataObject; import commonj.sdo.helper.XMLDocument; @ManagedBean(name="ReferenceDataCache") @ApplicationScoped public class ReferenceDataCache implements Serializable { private static Log log = LogFactory.getLog(ReferenceDataCache.class); private static final long serialVersionUID = 1L; private Map<String, Taxonomy> taxonomyMap = new HashMap<String, Taxonomy>(); /** * Maps categories for all taxonomies by sequence id for quick lookup */ private Map<Long, Category> categorySeqIdMap = new HashMap<Long, Category>(); /** * Maps categories for all taxonomies back to their taxonomy */ private Map<Long, Taxonomy> categorySeqIdTaxonomyMap = new HashMap<Long, Taxonomy>(); public static String TAXONOMY_NAME_SAM = "Segment Architecture Model"; public static String TAXONOMY_NAME_GMSPM = "gEMS Perspective Model"; public static String TAXONOMY_NAME_INVPM = "Inventory Perspective Model"; public static String TAXONOMY_NAME_ICAMPM = "ICAM Perspective Model"; public static String TAXONOMY_NAME_ORDERING = "Ordering Model"; private String[] TAX_NAMES = { TAXONOMY_NAME_SAM, TAXONOMY_NAME_GMSPM, TAXONOMY_NAME_INVPM, TAXONOMY_NAME_ICAMPM, TAXONOMY_NAME_ORDERING }; private static int TAXONOMY_SAM = 0; private static int TAXONOMY_GMSPM = 1; private static int TAXONOMY_INVPM = 2; private static int TAXONOMY_ICAMPM = 3; private static int TAXONOMY_ORDERING = 4; private List<Taxonomy> taxonomyList; private List<TaxonomyMap> taxonomyMapList; private List<Organization> deputyAreaList; private List<Organization> businessUnitList; private Map<Long, List<Property>> sourceClassSeqIdPropertyMap = new HashMap<Long, List<Property>>(); private Map<String, List<Property>> sourceClassNamePropertyMap = new HashMap<String, List<Property>>(); private ReferenceDataCacheMonitor referenceDataCacheMonitor = null; /** * Only to support managed bean facility and test harnesses. NOT * for client code in general. * Start a cache monitor Thread for expiration and eviction purposes. */ public ReferenceDataCache() { log.debug("ReferenceDataCache CTOR!!!"); referenceDataCacheMonitor = new ReferenceDataCacheMonitor(this); } /** * Returns a mapped category based on the given sequence id pri-key. * @param seqId * @return the Category */ public synchronized Category getCategory(Long seqId) { return this.categorySeqIdMap.get(seqId); } /** * Returns a mapped Taxonomy based on the given category sequence id pri-key. * @param categoryId * @return the Taxonomy */ public synchronized Taxonomy getTaxonomyForCategoryId(Long categoryId) { return this.categorySeqIdTaxonomyMap.get(categoryId); } public synchronized Taxonomy getSegmentArchitectureModel() { return getTaxonomy(this.TAX_NAMES[TAXONOMY_SAM]); } public synchronized Taxonomy getGemsPerspectiveModel() { return getTaxonomy(this.TAX_NAMES[TAXONOMY_GMSPM]); } public synchronized Taxonomy getInventoryPerspectiveModel() { return getTaxonomy(this.TAX_NAMES[TAXONOMY_INVPM]); } public synchronized Taxonomy getICAMPerspectiveModel() { return getTaxonomy(this.TAX_NAMES[TAXONOMY_ICAMPM]); } public synchronized Taxonomy getOrderingModel() { return getTaxonomy(this.TAX_NAMES[TAXONOMY_ORDERING]); } public synchronized Taxonomy getTaxonomy(String name) { Taxonomy result = this.taxonomyMap.get(name); if (result == null) { SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(TaxonomyQuery.createQuery( name)); DataGraph graph = results[0]; if (log.isDebugEnabled()) try { log.debug(this.serializeGraph(graph)); } catch (IOException e) { } final Taxonomy tax = (Taxonomy)graph.getRootObject(); log.debug("Caching Taxonomy " + name); this.taxonomyMap.put(name, tax); referenceDataCacheMonitor.monitor("taxonomyMap:" + name); // Map categories and remove the data-graph header from the graph nodes as this is // reference data and will will want to reference it from other graphs. PlasmaDataGraphVisitor visitor = new PlasmaDataGraphVisitor() { public void visit(DataObject target, DataObject source, String sourceKey, int level) { if (target instanceof Category) { Category cat = (Category)target; log.debug("Caching Taxonomy Category Seq. ID" + cat.getSeqId()); Long longId = Long.valueOf(cat.getSeqId()); categorySeqIdMap.put(longId, cat); categorySeqIdTaxonomyMap.put(longId, tax); // Refreshed when Taxonomy name expires. // referenceDataCacheMonitor.monitor("categorySeqIdMap:" + cat.getSeqId()); } ((PlasmaDataObject)target).setDataGraph(null); } }; ((PlasmaDataObject)tax).accept(visitor); ((PlasmaDataGraph)graph).removeRootObject(); result = tax; } return result; } private String getHashKey(String name, int year) { return name + ":" + String.valueOf(year); } public synchronized List<Taxonomy> getTaxonomies() { if (taxonomyList == null) { taxonomyList = new ArrayList<Taxonomy>(); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(TaxonomyQuery.createQuery()); log.debug("Caching Taxonomy List"); for (int i = 0; i < results.length; i++) { Taxonomy tax = (Taxonomy)results[i].getRootObject(); taxonomyList.add(tax); ((PlasmaDataGraph)results[i]).removeRootObject(); } referenceDataCacheMonitor.monitor("taxonomyList"); } return taxonomyList; } public synchronized List<TaxonomyMap> getTaxonomyMaps() { if (taxonomyMapList == null) { taxonomyMapList = new ArrayList<TaxonomyMap>(); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(TaxonomyMapQuery.createQuery()); log.debug("Caching Taxonomy Map List"); for (int i = 0; i < results.length; i++) { TaxonomyMap map = (TaxonomyMap)results[i].getRootObject(); taxonomyMapList.add(map); ((PlasmaDataGraph)results[i]).removeRootObject(); } referenceDataCacheMonitor.monitor("taxonomyMapList"); } return taxonomyMapList; } private List<SelectItem> deputyAreaOrgItems; private List<SelectItem> businessUnitOrgItems; public synchronized List<SelectItem> getDeputyAreaItems() { if (deputyAreaOrgItems == null) { initOrgItems(); } return deputyAreaOrgItems; } public synchronized List<SelectItem> getBusinessUnitItems() { if (businessUnitOrgItems == null) { initOrgItems(); } return businessUnitOrgItems; } private Map<Long, Organization> organizationMap; public synchronized Organization getOrganization(long id) { if (organizationMap == null) { initOrgItems(); } return organizationMap.get(id); } public synchronized List<Organization> getDeputyAreas() { if (deputyAreaList == null) { initOrgItems(); } return deputyAreaList; } public synchronized List<Organization> getBusinessUnits() { if (businessUnitList == null) { initOrgItems(); } return businessUnitList; } private void initOrgItems() { organizationMap = new HashMap<Long, Organization>(); deputyAreaOrgItems =new ArrayList<SelectItem>(); deputyAreaOrgItems.add(new SelectItem(new Long(-1), WebConstants.ANY_SELECTION)); businessUnitOrgItems =new ArrayList<SelectItem>(); businessUnitOrgItems.add(new SelectItem(new Long(-1), WebConstants.ANY_SELECTION)); deputyAreaList = new ArrayList<Organization>(); businessUnitList = new ArrayList<Organization>(); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(OrganizationQuery.createHierarchyQuery("USDA")); Organization root = (Organization)results[0].getRootObject(); log.debug("Caching Org Items"); Map<String, Organization> sorted = new TreeMap<String, Organization>(); // collect level 3 orgs OrganizationCollector collector = new OrganizationCollector(2); ((PlasmaDataObject)root).accept(collector); for (Organization org : collector.getResult()) { sorted.put(org.getCode(), org); organizationMap.put(org.getSeqId(), org); } for (Organization org : sorted.values()) { deputyAreaList.add(org); deputyAreaOrgItems.add(new SelectItem(org.getSeqId(), "(" + org.getCode() + ") " + org.getName())); } // collect level 5 orgs sorted.clear(); collector = new OrganizationCollector(4); ((PlasmaDataObject)root).accept(collector); for (Organization org : collector.getResult()) { sorted.put(org.getCode(), org); organizationMap.put(org.getSeqId(), org); } for (Organization org : sorted.values()) { businessUnitList.add(org); businessUnitOrgItems.add(new SelectItem(org.getSeqId(), "(" + org.getCode() + ") " + org.getName())); } referenceDataCacheMonitor.monitor("initOrgItems"); } private List<SelectItem> packageItems; public synchronized List<SelectItem> getPackageItems() { if (packageItems == null) { packageItems = new ArrayList<SelectItem>(); SelectItem item = new SelectItem(new Long(-1), WebConstants.DEFAULT_SELECTION); packageItems.add(item); for (Package type : getPackages()) { String def = type.getDefinition(); if (def != null && def.length() > 26) def = def.substring(0, 23) + "..."; classItems.add(new SelectItem( type.getSeqId(), type.getName(), def)); } referenceDataCacheMonitor.monitor("packageItems"); } return classItems; } private Map<Long, Package> packageSeqIdMap = new HashMap<Long, Package>(); public synchronized Package getPackage(Long seqId) { if (this.packageSeqIdMap.size() == 0) getPackages(); return this.packageSeqIdMap.get(seqId); } private Map<String, Package> packageUUIDMap = new HashMap<String, Package>(); public synchronized Package getPackage(String uuid) { if (this.packageUUIDMap.size() == 0) getPackages(); return this.packageUUIDMap.get(uuid); } private List<Package> packages; public synchronized List<Package> getPackages() { if (packages == null) { packages = new ArrayList<Package>(); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(PackageQuery.createQuery()); log.debug("Caching package types"); for (int i = 0; i < results.length; i++) { Package pkg = (Package)results[i].getRootObject(); packages.add(pkg); packageSeqIdMap.put(pkg.getSeqId(), pkg); packageUUIDMap.put(pkg.getExternalId(), pkg); if (log.isDebugEnabled()) { try { log.debug(serializeGraph(pkg.getDataGraph())); } catch (IOException e) { } } } referenceDataCacheMonitor.monitor("classes"); } return packages; } public synchronized void expirePackages() { this.packageItems = null; this.packages = null; this.packageSeqIdMap.clear(); this.packageUUIDMap.clear(); } private List<SelectItem> classItems; public synchronized List<SelectItem> getClassItems() { if (classItems == null) { classItems = new ArrayList<SelectItem>(); SelectItem item = new SelectItem(new Long(-1), WebConstants.DEFAULT_SELECTION); classItems.add(item); for (Clazz type : getClasses()) { String def = type.getClassifier().getDefinition(); if (def != null && def.length() > 26) def = def.substring(0, 23) + "..."; // NOTE: using classifier seq-id here // making result items more "generic" as classes // are linked to many entities through many properties classItems.add(new SelectItem( type.getClassifier().getSeqId(), type.getClassifier().getName(), def)); } referenceDataCacheMonitor.monitor("classItems"); } return classItems; } private Map<Long, Clazz> classSeqIdMap = new HashMap<Long, Clazz>(); public synchronized Clazz getClazz(Long seqId) { if (this.classSeqIdMap.size() == 0) getClasses(); return this.classSeqIdMap.get(seqId); } private Map<String, Clazz> classUUIDMap = new HashMap<String, Clazz>(); public synchronized Clazz getClazz(String uuid) { if (this.classUUIDMap.size() == 0) getClasses(); return this.classUUIDMap.get(uuid); } public synchronized List<Clazz> getClassesByPackageId(Long id) { List<Clazz> result = new ArrayList<Clazz>(); for (Clazz clzz : getClasses()) { if (clzz.getClassifier().getPackageableType().get_package().getSeqId() == id) result.add(clzz); } return result; } private List<Clazz> classes; public synchronized List<Clazz> getClasses() { if (classes == null) { classes = new ArrayList<Clazz>(); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(ClassQuery.createQuery()); log.debug("Caching class types"); for (int i = 0; i < results.length; i++) { Clazz clazz = (Clazz)results[i].getRootObject(); classes.add(clazz); classSeqIdMap.put(clazz.getSeqId(), clazz); classUUIDMap.put(clazz.getExternalId(), clazz); if (log.isDebugEnabled()) { try { log.debug(serializeGraph(clazz.getDataGraph())); } catch (IOException e) { } } } referenceDataCacheMonitor.monitor("classes"); } return classes; } public synchronized void expireClasses() { this.classItems = null; this.classes = null; this.classSeqIdMap.clear(); this.classUUIDMap.clear(); } public synchronized void expireEnumerations() { this.expire("enumerationItems"); this.expire("enumerations"); this.enumerationItems = null; this.enumerations = null; } private List<SelectItem> enumerationItems; public synchronized List<SelectItem> getEnumerationItems() { if (enumerationItems == null) { enumerationItems = new ArrayList<SelectItem>(); SelectItem item = new SelectItem(new Long(-1), WebConstants.DEFAULT_SELECTION); enumerationItems.add(item); for (Enumeration type : getEnumerations()) { String def = type.getDataType().getClassifier().getDefinition(); if (def != null && def.length() > 26) def = def.substring(0, 23) + "..."; enumerationItems.add(new SelectItem( type.getDataType().getClassifier().getSeqId(), type.getDataType().getClassifier().getName(), def)); } referenceDataCacheMonitor.monitor("enumerationItems"); } return enumerationItems; } List<Enumeration> enumerations = null; public synchronized List<Enumeration> getEnumerations() { if (enumerations == null) { enumerations = new ArrayList<Enumeration>(); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(EnumerationQuery.createQuery()); log.debug("Caching Enumeration Types"); for (int i = 0; i < results.length; i++) { Enumeration type = (Enumeration)results[i].getRootObject(); enumerations.add(type); } referenceDataCacheMonitor.monitor("enumerations"); } return enumerations; } private List<SelectItem> primitiveTypeItems; public synchronized List<SelectItem> getPrimitiveTypeItems() { if (primitiveTypeItems == null) { primitiveTypeItems = new ArrayList<SelectItem>(); SelectItem item = new SelectItem(new Long(-1), WebConstants.DEFAULT_SELECTION); primitiveTypeItems.add(item); for (PrimitiveType type : getPrimitiveTypes()) { String def = type.getDataType().getClassifier().getDefinition(); if (def != null && def.length() > 26) def = def.substring(0, 23) + "..."; primitiveTypeItems.add(new SelectItem( type.getDataType().getClassifier().getSeqId(), type.getDataType().getClassifier().getName(), def)); } referenceDataCacheMonitor.monitor("primitiveTypeItems"); } return primitiveTypeItems; } List<PrimitiveType> primitiveTypes = null; public synchronized List<PrimitiveType> getPrimitiveTypes() { if (primitiveTypes == null) { primitiveTypes = new ArrayList<PrimitiveType>(); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(PrimitiveTypeQuery.createQuery()); log.debug("Caching Primitive Types"); for (int i = 0; i < results.length; i++) { PrimitiveType type = (PrimitiveType)results[i].getRootObject(); primitiveTypes.add(type); } referenceDataCacheMonitor.monitor("primitiveTypes"); } return primitiveTypes; } public synchronized List<Property> getProperties(Long sourceClassId) { List<Property> result = this.sourceClassSeqIdPropertyMap.get(sourceClassId); if (result == null) { result = new ArrayList<Property>(); Query query = PropertyQuery.createQueryBySourceClassId( sourceClassId); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(query); log.debug("Caching properties for source class: " + sourceClassId); for (int i = 0; i < results.length; i++) { Property prop = (Property)results[i].getRootObject(); result.add(prop); } this.sourceClassSeqIdPropertyMap.put(sourceClassId, result); } return result; } public synchronized List<Property> getProperties(String sourceClassName) { List<Property> result = this.sourceClassNamePropertyMap.get(sourceClassName); if (result == null) { result = new ArrayList<Property>(); Query query = PropertyQuery.createQueryBySourceClassifierName( sourceClassName); SDODataAccessClient service = new SDODataAccessClient(); DataGraph[] results = service.find(query); log.debug("Caching properties for source class: " + sourceClassName); for (int i = 0; i < results.length; i++) { Property prop = (Property)results[i].getRootObject(); result.add(prop); } this.sourceClassNamePropertyMap.put(sourceClassName, result); } return result; } public synchronized void expireProperties(Long sourceClassId) { List<Property> removed = this.sourceClassSeqIdPropertyMap.remove(sourceClassId); if (removed != null) { if (log.isDebugEnabled()) log.debug("removed " + removed.size() + " properties"); } else log.warn("no properties removed for source class: " + sourceClassId); Clazz c = this.classSeqIdMap.get(sourceClassId); if (c != null) { removed = this.sourceClassNamePropertyMap.remove(c.getClassifier().getName()); if (removed != null) { if (log.isDebugEnabled()) log.debug("removed " + removed.size() + " properties"); } else log.warn("no properties removed for source class: " + c.getClassifier().getName()); } } /** * The ReferenceDataCacheMonitor calls this method when it detects a cached item * that has expired. Evicts by resetting Map or List to null, causing a next time * access DB reload. * @param objName - the cached item that has expired. */ public synchronized void expire(String objName) { log.debug("Expire " + objName); if (objName.startsWith("taxonomyMap:")) { String taxName = objName.substring("taxonomyMap:".length()); this.taxonomyMap.remove(taxName); } else if (objName.startsWith("categorySeqIdMap:")) { String seqId = objName.substring("categorySeqIdMap:".length()); categorySeqIdMap.remove(new Long(seqId)); } else if ("taxonomyList".equals(objName)) { taxonomyList = null; } else if ("taxonomyMapList".equals(objName)) { taxonomyMapList = null; } else if ("initOrgItems".equals(objName)) { organizationMap = null; deputyAreaOrgItems = null; businessUnitOrgItems = null; deputyAreaList = null; businessUnitList = null; } } // expire private String serializeGraph(DataGraph graph) throws IOException { DefaultOptions options = new DefaultOptions( graph.getRootObject().getType().getURI()); options.setRootNamespacePrefix("dump"); XMLDocument doc = PlasmaXMLHelper.INSTANCE.createDocument(graph.getRootObject(), graph.getRootObject().getType().getURI(), null); ByteArrayOutputStream os = new ByteArrayOutputStream(); PlasmaXMLHelper.INSTANCE.save(doc, os, options); os.flush(); os.close(); String xml = new String(os.toByteArray()); return xml; } }