View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.datadictionary;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.springframework.beans.factory.support.KualiDefaultListableBeanFactory;
22  
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  /**
30   * Encapsulates a set of statically generated (typically during startup)
31   * DataDictionary indexes 
32   * 
33   * @author Kuali Rice Team (rice.collab@kuali.org)
34   */
35  public class DataDictionaryIndex implements Runnable {
36  	private static final Log LOG = LogFactory.getLog(DataDictionaryIndex.class);
37  	
38  	private KualiDefaultListableBeanFactory ddBeans;
39  	
40  	// keyed by BusinessObject class
41  	private Map<String, BusinessObjectEntry> businessObjectEntries;
42  	private Map<String, DataObjectEntry> objectEntries;
43  	
44  	// keyed by documentTypeName
45  	private Map<String, DocumentEntry> documentEntries;
46  	// keyed by other things
47  	private Map<Class, DocumentEntry> documentEntriesByBusinessObjectClass;
48  	private Map<Class, DocumentEntry> documentEntriesByMaintainableClass;
49  	private Map<String, DataDictionaryEntry> entriesByJstlKey;
50  
51  	// keyed by a class object, and the value is a set of classes that may block the class represented by the key from inactivation 
52  	private Map<Class, Set<InactivationBlockingMetadata>> inactivationBlockersForClass;
53  
54  	public DataDictionaryIndex(KualiDefaultListableBeanFactory ddBeans) {
55  		this.ddBeans = ddBeans;
56  	}
57  
58  	public Map<String, BusinessObjectEntry> getBusinessObjectEntries() {
59  		return this.businessObjectEntries;
60  	}
61  
62  	public Map<String, DataObjectEntry> getDataObjectEntries() {
63  		return this.objectEntries;
64  	}
65  
66  	public Map<String, DocumentEntry> getDocumentEntries() {
67  		return this.documentEntries;
68  	}
69  
70  	public Map<Class, DocumentEntry> getDocumentEntriesByBusinessObjectClass() {
71  		return this.documentEntriesByBusinessObjectClass;
72  	}
73  
74  	public Map<Class, DocumentEntry> getDocumentEntriesByMaintainableClass() {
75  		return this.documentEntriesByMaintainableClass;
76  	}
77  
78  	public Map<String, DataDictionaryEntry> getEntriesByJstlKey() {
79  		return this.entriesByJstlKey;
80  	}
81  
82  	public Map<Class, Set<InactivationBlockingMetadata>> getInactivationBlockersForClass() {
83  		return this.inactivationBlockersForClass;
84  	}
85  
86  	private void buildDDIndicies() {
87          // primary indices
88          businessObjectEntries = new HashMap<String, BusinessObjectEntry>();
89          objectEntries = new HashMap<String, DataObjectEntry>();
90          documentEntries = new HashMap<String, DocumentEntry>();
91  
92          // alternate indices
93          documentEntriesByBusinessObjectClass = new HashMap<Class, DocumentEntry>();
94          documentEntriesByMaintainableClass = new HashMap<Class, DocumentEntry>();
95          entriesByJstlKey = new HashMap<String, DataDictionaryEntry>();
96          
97          // loop over all beans in the context
98          Map<String, DataObjectEntry> boBeans = ddBeans.getBeansOfType(DataObjectEntry.class);
99          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 }