View Javadoc

1   /**
2    * Copyright 2005-2012 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.ArrayList;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  /**
31   * Encapsulates a set of statically generated (typically during startup)
32   * DataDictionary indexes
33   *
34   * @author Kuali Rice Team (rice.collab@kuali.org)
35   */
36  public class DataDictionaryIndex implements Runnable {
37      private static final Log LOG = LogFactory.getLog(DataDictionaryIndex.class);
38  
39      private KualiDefaultListableBeanFactory ddBeans;
40  
41      // keyed by BusinessObject class
42      private Map<String, BusinessObjectEntry> businessObjectEntries;
43      private Map<String, DataObjectEntry> objectEntries;
44  
45      // keyed by documentTypeName
46      private Map<String, DocumentEntry> documentEntries;
47      // keyed by other things
48      private Map<Class, DocumentEntry> documentEntriesByBusinessObjectClass;
49      private Map<Class, DocumentEntry> documentEntriesByMaintainableClass;
50      private Map<String, DataDictionaryEntry> entriesByJstlKey;
51  
52      // keyed by a class object, and the value is a set of classes that may block the class represented by the key from inactivation
53      private Map<Class, Set<InactivationBlockingMetadata>> inactivationBlockersForClass;
54  
55      private Map<String, List<String>> dictionaryBeansByNamespace;
56  
57      public DataDictionaryIndex(KualiDefaultListableBeanFactory ddBeans) {
58          this.ddBeans = ddBeans;
59  
60          // primary indices
61          businessObjectEntries = new HashMap<String, BusinessObjectEntry>();
62          objectEntries = new HashMap<String, DataObjectEntry>();
63          documentEntries = new HashMap<String, DocumentEntry>();
64  
65          // alternate indices
66          documentEntriesByBusinessObjectClass = new HashMap<Class, DocumentEntry>();
67          documentEntriesByMaintainableClass = new HashMap<Class, DocumentEntry>();
68          entriesByJstlKey = new HashMap<String, DataDictionaryEntry>();
69  
70          dictionaryBeansByNamespace = new HashMap<String, List<String>>();
71      }
72  
73      private void buildDDIndicies() {
74          // primary indices
75          businessObjectEntries = new HashMap<String, BusinessObjectEntry>();
76          objectEntries = new HashMap<String, DataObjectEntry>();
77          documentEntries = new HashMap<String, DocumentEntry>();
78  
79          // alternate indices
80          documentEntriesByBusinessObjectClass = new HashMap<Class, DocumentEntry>();
81          documentEntriesByMaintainableClass = new HashMap<Class, DocumentEntry>();
82          entriesByJstlKey = new HashMap<String, DataDictionaryEntry>();
83  
84          // loop over all beans in the context
85          Map<String, DataObjectEntry> boBeans = ddBeans.getBeansOfType(DataObjectEntry.class);
86          for (DataObjectEntry entry : boBeans.values()) {
87  
88              DataObjectEntry indexedEntry = objectEntries.get(entry.getJstlKey());
89              if (indexedEntry == null) {
90                  indexedEntry = businessObjectEntries.get(entry.getJstlKey());
91              }
92  
93              if ((indexedEntry != null) && !(indexedEntry.getDataObjectClass().equals(entry.getDataObjectClass()))) {
94                  throw new DataDictionaryException(new StringBuffer(
95                          "Two object classes may not share the same jstl key: this=").append(entry.getDataObjectClass())
96                          .append(" / existing=").append(indexedEntry.getDataObjectClass()).toString());
97              }
98  
99              // put all BO and DO entries in the objectEntries map
100             objectEntries.put(entry.getDataObjectClass().getName(), entry);
101             objectEntries.put(entry.getDataObjectClass().getSimpleName(), entry);
102 
103             // keep a separate map of BO entries for now
104             if (entry instanceof BusinessObjectEntry) {
105                 BusinessObjectEntry boEntry = (BusinessObjectEntry) entry;
106 
107                 businessObjectEntries.put(boEntry.getBusinessObjectClass().getName(), boEntry);
108                 businessObjectEntries.put(boEntry.getBusinessObjectClass().getSimpleName(), boEntry);
109 
110                 // If a "base" class is defined for the entry, index the entry by that class as well.
111                 if (boEntry.getBaseBusinessObjectClass() != null) {
112                     businessObjectEntries.put(boEntry.getBaseBusinessObjectClass().getName(), boEntry);
113                     businessObjectEntries.put(boEntry.getBaseBusinessObjectClass().getSimpleName(), boEntry);
114                 }
115             }
116 
117             entriesByJstlKey.put(entry.getJstlKey(), entry);
118         }
119 
120         //Build Document Entry Index
121         Map<String, DocumentEntry> docBeans = ddBeans.getBeansOfType(DocumentEntry.class);
122         for (DocumentEntry entry : docBeans.values()) {
123             String entryName = entry.getDocumentTypeName();
124 
125             if ((entry instanceof TransactionalDocumentEntry)
126                     && (documentEntries.get(entry.getFullClassName()) != null)
127                     && !StringUtils.equals(documentEntries.get(entry.getFullClassName()).getDocumentTypeName(),
128                     entry.getDocumentTypeName())) {
129                 throw new DataDictionaryException(new StringBuffer(
130                         "Two transactional document types may not share the same document class: this=").append(
131                         entry.getDocumentTypeName()).append(" / existing=").append(((DocumentEntry) documentEntries.get(
132                         entry.getDocumentClass().getName())).getDocumentTypeName()).toString());
133             }
134 
135             if ((documentEntries.get(entry.getJstlKey()) != null) && !((DocumentEntry) documentEntries.get(
136                     entry.getJstlKey())).getDocumentTypeName().equals(entry.getDocumentTypeName())) {
137                 throw new DataDictionaryException(new StringBuffer(
138                         "Two document types may not share the same jstl key: this=").append(entry.getDocumentTypeName())
139                         .append(" / existing=").append(((DocumentEntry) documentEntries.get(entry.getJstlKey()))
140                                 .getDocumentTypeName()).toString());
141             }
142 
143             if (entryName != null) {
144                 documentEntries.put(entryName, entry);
145             }
146 
147             //documentEntries.put(entry.getFullClassName(), entry);
148             documentEntries.put(entry.getDocumentClass().getName(), entry);
149             if (entry.getBaseDocumentClass() != null) {
150                 documentEntries.put(entry.getBaseDocumentClass().getName(), entry);
151             }
152             entriesByJstlKey.put(entry.getJstlKey(), entry);
153 
154             if (entry instanceof TransactionalDocumentEntry) {
155                 TransactionalDocumentEntry tde = (TransactionalDocumentEntry) entry;
156 
157                 documentEntries.put(tde.getDocumentClass().getSimpleName(), entry);
158                 if (tde.getBaseDocumentClass() != null) {
159                     documentEntries.put(tde.getBaseDocumentClass().getSimpleName(), entry);
160                 }
161             }
162 
163             if (entry instanceof MaintenanceDocumentEntry) {
164                 MaintenanceDocumentEntry mde = (MaintenanceDocumentEntry) entry;
165 
166                 documentEntriesByBusinessObjectClass.put(mde.getDataObjectClass(), entry);
167                 documentEntriesByMaintainableClass.put(mde.getMaintainableClass(), entry);
168                 documentEntries.put(mde.getDataObjectClass().getSimpleName() + "MaintenanceDocument", entry);
169             }
170         }
171     }
172 
173     private void buildDDInactivationBlockingIndices() {
174         inactivationBlockersForClass = new HashMap<Class, Set<InactivationBlockingMetadata>>();
175 
176         Map<String, DataObjectEntry> doBeans = ddBeans.getBeansOfType(DataObjectEntry.class);
177         for (DataObjectEntry entry : doBeans.values()) {
178             List<InactivationBlockingDefinition> inactivationBlockingDefinitions =
179                     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(
190                 inactivationBlockingDefinition.getBlockedBusinessObjectClass());
191         if (inactivationBlockingDefinitions == null) {
192             inactivationBlockingDefinitions = new HashSet<InactivationBlockingMetadata>();
193             inactivationBlockersForClass.put(inactivationBlockingDefinition.getBlockedBusinessObjectClass(),
194                     inactivationBlockingDefinitions);
195         }
196         boolean duplicateAdd = !inactivationBlockingDefinitions.add(inactivationBlockingDefinition);
197         if (duplicateAdd) {
198             throw new DataDictionaryException(
199                     "Detected duplicate InactivationBlockingDefinition for class " + inactivationBlockingDefinition
200                             .getBlockingReferenceBusinessObjectClass().getClass().getName());
201         }
202     }
203 
204     public void run() {
205         LOG.info("Starting DD Index Building");
206         buildDDIndicies();
207         LOG.info("Completed DD Index Building");
208 
209         //        LOG.info( "Starting DD Validation" );
210         //        validateDD();
211         //        LOG.info( "Ending DD Validation" );
212 
213         LOG.info("Started DD Inactivation Blocking Index Building");
214         buildDDInactivationBlockingIndices();
215         LOG.info("Completed DD Inactivation Blocking Index Building");
216     }
217 
218     public Map<String, BusinessObjectEntry> getBusinessObjectEntries() {
219         return this.businessObjectEntries;
220     }
221 
222     public Map<String, DataObjectEntry> getDataObjectEntries() {
223         return this.objectEntries;
224     }
225 
226     public Map<String, DocumentEntry> getDocumentEntries() {
227         return this.documentEntries;
228     }
229 
230     public Map<Class, DocumentEntry> getDocumentEntriesByBusinessObjectClass() {
231         return this.documentEntriesByBusinessObjectClass;
232     }
233 
234     public Map<Class, DocumentEntry> getDocumentEntriesByMaintainableClass() {
235         return this.documentEntriesByMaintainableClass;
236     }
237 
238     public Map<String, DataDictionaryEntry> getEntriesByJstlKey() {
239         return this.entriesByJstlKey;
240     }
241 
242     public Map<Class, Set<InactivationBlockingMetadata>> getInactivationBlockersForClass() {
243         return this.inactivationBlockersForClass;
244     }
245 
246     /**
247      * Mapping of namespace codes to bean definition names that are associated with that namespace
248      *
249      * @return Map<String, List<String>> where map key is namespace code, and map value is list of bean names
250      */
251     public Map<String, List<String>> getDictionaryBeansByNamespace() {
252         return dictionaryBeansByNamespace;
253     }
254 
255     /**
256      * Associates a list of bean names with the given namespace code
257      *
258      * @param namespaceCode - namespace code to associate beans with
259      * @param beanNames - list of bean names that belong to the namespace
260      */
261     public void addBeanNamesToNamespace(String namespaceCode, List<String> beanNames) {
262         List<String> namespaceBeans = new ArrayList<String>();
263         if (dictionaryBeansByNamespace.containsKey(namespaceCode)) {
264             namespaceBeans = dictionaryBeansByNamespace.get(namespaceCode);
265         } else {
266             dictionaryBeansByNamespace.put(namespaceCode, namespaceBeans);
267         }
268         namespaceBeans.addAll(beanNames);
269     }
270 }