/***************************************************************** * 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.cayenne.map; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; import org.apache.commons.collections.collection.CompositeCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @since 4.0 */ class MappingCache implements MappingNamespace { private static final ObjEntity OBJ_DUPLICATE_MARKER = new ObjEntity(); protected static final Logger logger = LoggerFactory.getLogger(MappingCache.class); protected Collection<DataMap> maps; protected Map<String, QueryDescriptor> queryDesriptors; protected Map<String, Embeddable> embeddables; protected Map<String, SQLResult> results; protected Map<String, DbEntity> dbEntities; protected Map<String, ObjEntity> objEntities; protected Map<String, ObjEntity> objEntitiesByClassName; protected Map<String, Procedure> procedures; protected Map<String, EntityInheritanceTree> entityInheritanceCache; MappingCache(Collection<DataMap> maps) { this.maps = maps; this.embeddables = new HashMap<>(); this.queryDesriptors = new HashMap<>(); this.dbEntities = new HashMap<>(); this.objEntities = new HashMap<>(); this.objEntitiesByClassName = new HashMap<>(); this.procedures = new HashMap<>(); this.entityInheritanceCache = new HashMap<>(); this.results = new HashMap<>(); index(); } private void index() { // index DbEntities separately and before ObjEntities to avoid infinite // loops when looking up DbEntities during ObjEntity index op for (DataMap map : maps) { dbEntities.putAll(map.getDbEntityMap()); } for (DataMap map : maps) { // index ObjEntities by name objEntities.putAll(map.getObjEntityMap()); // index ObjEntities by class name for (ObjEntity oe : map.getObjEntities()) { // use class name as a key to avoid class loading here... String className = oe.getJavaClassName(); if (className == null) { continue; } // allow duplicates, but put a special marker indicating // that this entity can't be looked up by class Object existing = objEntitiesByClassName.get(className); if (existing != null && existing != OBJ_DUPLICATE_MARKER) { objEntitiesByClassName.put(className, OBJ_DUPLICATE_MARKER); } else { objEntitiesByClassName.put(className, oe); } } // index stored procedures procedures.putAll(map.getProcedureMap()); // index embeddables embeddables.putAll(map.getEmbeddableMap()); // index query descriptors queryDesriptors.putAll(map.getQueryDescriptorMap()); } // restart the map iterator to index inheritance for (DataMap map : maps) { // index ObjEntity inheritance for (ObjEntity oe : map.getObjEntities()) { // build inheritance tree EntityInheritanceTree node = entityInheritanceCache.get(oe.getName()); if (node == null) { node = new EntityInheritanceTree(oe); entityInheritanceCache.put(oe.getName(), node); } String superOEName = oe.getSuperEntityName(); if (superOEName != null) { EntityInheritanceTree superNode = entityInheritanceCache.get(superOEName); if (superNode == null) { // do direct entity lookup to avoid recursive cache // rebuild ObjEntity superOE = objEntities.get(superOEName); if (superOE != null) { superNode = new EntityInheritanceTree(superOE); entityInheritanceCache.put(superOEName, superNode); } else { // bad mapping? Or most likely some classloader // issue logger.warn("No super entity mapping for '" + superOEName + "'"); continue; } } superNode.addChildNode(node); } } } } public Embeddable getEmbeddable(String className) { return embeddables.get(className); } public SQLResult getResult(String name) { return results.get(name); } public EntityInheritanceTree getInheritanceTree(String entityName) { return entityInheritanceCache.get(entityName); } public Procedure getProcedure(String procedureName) { return procedures.get(procedureName); } public QueryDescriptor getQueryDescriptor(String queryName) { return queryDesriptors.get(queryName); } public DbEntity getDbEntity(String name) { return dbEntities.get(name); } public ObjEntity getObjEntity(Class<?> entityClass) { ObjEntity entity = objEntitiesByClassName.get(entityClass.getName()); if (entity == OBJ_DUPLICATE_MARKER) { throw new CayenneRuntimeException("Can't perform lookup. There is more than one ObjEntity mapped to %s" , entityClass.getName()); } return entity; } public ObjEntity getObjEntity(Persistent object) { ObjectId id = object.getObjectId(); if (id != null) { return getObjEntity(id.getEntityName()); } else { return getObjEntity(object.getClass()); } } public ObjEntity getObjEntity(String name) { return objEntities.get(name); } public Collection<DbEntity> getDbEntities() { // TODO: LEGACY SUPPORT: // some downstream code (like Modeler and merge framework) expect // always fresh list here, so instead of doing the right thing of // refreshing the cache and returning cache.entries(), we are scanning // the list of DataMaps. if (maps.size() == 0) { return Collections.emptyList(); } if (maps.size() == 1) { return maps.iterator().next().getDbEntities(); } CompositeCollection c = new CompositeCollection(); for (DataMap map : maps) { c.addComposited(map.getDbEntities()); } return c; } public Collection<Procedure> getProcedures() { // TODO: LEGACY SUPPORT: // some downstream code (like Modeler and merge framework) expect // always fresh list here, so instead of doing the right thing of // refreshing the cache and returning cache.entries(), we are scanning // the list of DataMaps. if (maps.size() == 0) { return Collections.emptyList(); } if (maps.size() == 1) { return maps.iterator().next().getProcedures(); } CompositeCollection c = new CompositeCollection(); for (DataMap map : maps) { c.addComposited(map.getProcedures()); } return c; } public Collection<QueryDescriptor> getQueryDescriptors() { // TODO: LEGACY SUPPORT: // some downstream code (like Modeler and merge framework) expect // always fresh list here, so instead of doing the right thing of // refreshing the cache and returning cache.entries(), we are scanning // the list of DataMaps. if (maps.size() == 0) { return Collections.emptyList(); } if (maps.size() == 1) { return maps.iterator().next().getQueryDescriptors(); } CompositeCollection c = new CompositeCollection(); for (DataMap map : maps) { c.addComposited(map.getQueryDescriptors()); } return c; } public Collection<ObjEntity> getObjEntities() { // TODO: LEGACY SUPPORT: // some downstream code (like Modeler and merge framework) expect // always fresh list here, so instead of doing the right thing of // refreshing the cache and returning cache.entries(), we are scanning // the list of DataMaps. if (maps.size() == 0) { return Collections.emptyList(); } if (maps.size() == 1) { return maps.iterator().next().getObjEntities(); } CompositeCollection c = new CompositeCollection(); for (DataMap map : maps) { c.addComposited(map.getObjEntities()); } return c; } public Collection<Embeddable> getEmbeddables() { // TODO: LEGACY SUPPORT: // some downstream code (like Modeler and merge framework) expect // always fresh list here, so instead of doing the right thing of // refreshing the cache and returning cache.entries(), we are scanning // the list of DataMaps. if (maps.size() == 0) { return Collections.emptyList(); } if (maps.size() == 1) { return maps.iterator().next().getEmbeddables(); } CompositeCollection c = new CompositeCollection(); for (DataMap map : maps) { c.addComposited(map.getEmbeddables()); } return c; } public Collection<SQLResult> getResults() { // TODO: LEGACY SUPPORT: // some downstream code (like Modeler and merge framework) expect // always fresh list here, so instead of doing the right thing of // refreshing the cache and returning cache.entries(), we are scanning // the list of DataMaps. if (maps.size() == 0) { return Collections.emptyList(); } if (maps.size() == 1) { return maps.iterator().next().getResults(); } CompositeCollection c = new CompositeCollection(); for (DataMap map : maps) { c.addComposited(map.getResults()); } return c; } }