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