001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.datadictionary; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.commons.logging.Log; 020import org.apache.commons.logging.LogFactory; 021import org.springframework.beans.factory.support.KualiDefaultListableBeanFactory; 022 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029/** 030 * Encapsulates a set of statically generated (typically during startup) 031 * DataDictionary indexes 032 * 033 * @author Kuali Rice Team (rice.collab@kuali.org) 034 */ 035public class DataDictionaryIndex implements Runnable { 036 private static final Log LOG = LogFactory.getLog(DataDictionaryIndex.class); 037 038 private KualiDefaultListableBeanFactory ddBeans; 039 040 // keyed by BusinessObject class 041 private Map<String, BusinessObjectEntry> businessObjectEntries; 042 private Map<String, DataObjectEntry> objectEntries; 043 044 // keyed by documentTypeName 045 private Map<String, DocumentEntry> documentEntries; 046 // keyed by other things 047 private Map<Class, DocumentEntry> documentEntriesByBusinessObjectClass; 048 private Map<Class, DocumentEntry> documentEntriesByMaintainableClass; 049 private Map<String, DataDictionaryEntry> entriesByJstlKey; 050 051 // keyed by a class object, and the value is a set of classes that may block the class represented by the key from inactivation 052 private Map<Class, Set<InactivationBlockingMetadata>> inactivationBlockersForClass; 053 054 public DataDictionaryIndex(KualiDefaultListableBeanFactory ddBeans) { 055 this.ddBeans = ddBeans; 056 } 057 058 public Map<String, BusinessObjectEntry> getBusinessObjectEntries() { 059 return this.businessObjectEntries; 060 } 061 062 public Map<String, DataObjectEntry> getDataObjectEntries() { 063 return this.objectEntries; 064 } 065 066 public Map<String, DocumentEntry> getDocumentEntries() { 067 return this.documentEntries; 068 } 069 070 public Map<Class, DocumentEntry> getDocumentEntriesByBusinessObjectClass() { 071 return this.documentEntriesByBusinessObjectClass; 072 } 073 074 public Map<Class, DocumentEntry> getDocumentEntriesByMaintainableClass() { 075 return this.documentEntriesByMaintainableClass; 076 } 077 078 public Map<String, DataDictionaryEntry> getEntriesByJstlKey() { 079 return this.entriesByJstlKey; 080 } 081 082 public Map<Class, Set<InactivationBlockingMetadata>> getInactivationBlockersForClass() { 083 return this.inactivationBlockersForClass; 084 } 085 086 private void buildDDIndicies() { 087 // primary indices 088 businessObjectEntries = new HashMap<String, BusinessObjectEntry>(); 089 objectEntries = new HashMap<String, DataObjectEntry>(); 090 documentEntries = new HashMap<String, DocumentEntry>(); 091 092 // alternate indices 093 documentEntriesByBusinessObjectClass = new HashMap<Class, DocumentEntry>(); 094 documentEntriesByMaintainableClass = new HashMap<Class, DocumentEntry>(); 095 entriesByJstlKey = new HashMap<String, DataDictionaryEntry>(); 096 097 // loop over all beans in the context 098 Map<String, DataObjectEntry> boBeans = ddBeans.getBeansOfType(DataObjectEntry.class); 099 for ( DataObjectEntry entry : boBeans.values() ) { 100 101 DataObjectEntry indexedEntry = objectEntries.get(entry.getJstlKey()); 102 if (indexedEntry == null){ 103 indexedEntry = businessObjectEntries.get(entry.getJstlKey()); 104 } 105 if ( (indexedEntry != null) 106 && !(indexedEntry.getDataObjectClass().equals(entry.getDataObjectClass()))) { 107 throw new DataDictionaryException(new StringBuffer("Two object classes may not share the same jstl key: this=").append(entry.getDataObjectClass()).append(" / existing=").append(indexedEntry.getDataObjectClass()).toString()); 108 } 109 110 // put all BO and DO entries in the objectEntries map 111 objectEntries.put(entry.getDataObjectClass().getName(), entry); 112 objectEntries.put(entry.getDataObjectClass().getSimpleName(), entry); 113 114 // keep a separate map of BO entries for now 115 if (entry instanceof BusinessObjectEntry){ 116 BusinessObjectEntry boEntry = (BusinessObjectEntry)entry; 117 118 businessObjectEntries.put(boEntry.getBusinessObjectClass().getName(), boEntry); 119 businessObjectEntries.put(boEntry.getBusinessObjectClass().getSimpleName(), boEntry); 120 // If a "base" class is defined for the entry, index the entry by that class as well. 121 if (boEntry.getBaseBusinessObjectClass() != null) { 122 businessObjectEntries.put(boEntry.getBaseBusinessObjectClass().getName(), boEntry); 123 businessObjectEntries.put(boEntry.getBaseBusinessObjectClass().getSimpleName(), boEntry); 124 } 125 } 126 127 entriesByJstlKey.put(entry.getJstlKey(), entry); 128 } 129 130 //Build Document Entry Index 131 Map<String,DocumentEntry> docBeans = ddBeans.getBeansOfType(DocumentEntry.class); 132 for ( DocumentEntry entry : docBeans.values() ) { 133 String entryName = entry.getDocumentTypeName(); 134 135 if ((entry instanceof TransactionalDocumentEntry) 136 && (documentEntries.get(entry.getFullClassName()) != null) 137 && !StringUtils.equals(documentEntries.get(entry.getFullClassName()).getDocumentTypeName(), entry.getDocumentTypeName())) { 138 throw new DataDictionaryException(new StringBuffer("Two transactional document types may not share the same document class: this=") 139 .append(entry.getDocumentTypeName()) 140 .append(" / existing=") 141 .append(((DocumentEntry)documentEntries.get(entry.getDocumentClass().getName())).getDocumentTypeName()).toString()); 142 } 143 if ((documentEntries.get(entry.getJstlKey()) != null) && !((DocumentEntry)documentEntries.get(entry.getJstlKey())).getDocumentTypeName().equals(entry.getDocumentTypeName())) { 144 throw new DataDictionaryException(new StringBuffer("Two document types may not share the same jstl key: this=").append(entry.getDocumentTypeName()).append(" / existing=").append(((DocumentEntry)documentEntries.get(entry.getJstlKey())).getDocumentTypeName()).toString()); 145 } 146 147 if (entryName != null) { 148 documentEntries.put(entryName, entry); 149 } 150 //documentEntries.put(entry.getFullClassName(), entry); 151 documentEntries.put(entry.getDocumentClass().getName(), entry); 152 if (entry.getBaseDocumentClass() != null) { 153 documentEntries.put(entry.getBaseDocumentClass().getName(), entry); 154 } 155 entriesByJstlKey.put(entry.getJstlKey(), entry); 156 157 if (entry instanceof TransactionalDocumentEntry) { 158 TransactionalDocumentEntry tde = (TransactionalDocumentEntry) entry; 159 160 documentEntries.put(tde.getDocumentClass().getSimpleName(), entry); 161 if (tde.getBaseDocumentClass() != null) { 162 documentEntries.put(tde.getBaseDocumentClass().getSimpleName(), entry); 163 } 164 } 165 if (entry instanceof MaintenanceDocumentEntry) { 166 MaintenanceDocumentEntry mde = (MaintenanceDocumentEntry) entry; 167 168 documentEntriesByBusinessObjectClass.put(mde.getDataObjectClass(), entry); 169 documentEntriesByMaintainableClass.put(mde.getMaintainableClass(), entry); 170 documentEntries.put(mde.getDataObjectClass().getSimpleName() + "MaintenanceDocument", entry); 171 } 172 } 173 } 174 175 private void buildDDInactivationBlockingIndices() { 176 inactivationBlockersForClass = new HashMap<Class, Set<InactivationBlockingMetadata>>(); 177 Map<String,DataObjectEntry> doBeans = ddBeans.getBeansOfType(DataObjectEntry.class); 178 for ( DataObjectEntry entry : doBeans.values() ) { 179 List<InactivationBlockingDefinition> inactivationBlockingDefinitions = entry.getInactivationBlockingDefinitions(); 180 if (inactivationBlockingDefinitions != null && !inactivationBlockingDefinitions.isEmpty()) { 181 for (InactivationBlockingDefinition inactivationBlockingDefinition : inactivationBlockingDefinitions) { 182 registerInactivationBlockingDefinition(inactivationBlockingDefinition); 183 } 184 } 185 } 186 } 187 188 private void registerInactivationBlockingDefinition(InactivationBlockingDefinition inactivationBlockingDefinition) { 189 Set<InactivationBlockingMetadata> inactivationBlockingDefinitions = inactivationBlockersForClass.get(inactivationBlockingDefinition.getBlockedBusinessObjectClass()); 190 if (inactivationBlockingDefinitions == null) { 191 inactivationBlockingDefinitions = new HashSet<InactivationBlockingMetadata>(); 192 inactivationBlockersForClass.put(inactivationBlockingDefinition.getBlockedBusinessObjectClass(), inactivationBlockingDefinitions); 193 } 194 boolean duplicateAdd = ! inactivationBlockingDefinitions.add(inactivationBlockingDefinition); 195 if (duplicateAdd) { 196 throw new DataDictionaryException("Detected duplicate InactivationBlockingDefinition for class " + inactivationBlockingDefinition.getBlockingReferenceBusinessObjectClass().getClass().getName()); 197 } 198 } 199 200 public void run() { 201 LOG.info( "Starting DD Index Building" ); 202 buildDDIndicies(); 203 LOG.info( "Completed DD Index Building" ); 204// LOG.info( "Starting DD Validation" ); 205// validateDD(); 206// LOG.info( "Ending DD Validation" ); 207 LOG.info( "Started DD Inactivation Blocking Index Building" ); 208 buildDDInactivationBlockingIndices(); 209 LOG.info( "Completed DD Inactivation Blocking Index Building" ); 210 } 211}