merge-update from trunk
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / database / PersistentTermInitializer.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.database;
11
12 import java.util.HashMap;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.UUID;
16
17 import javax.annotation.PostConstruct;
18
19 import org.apache.log4j.Logger;
20 import org.hibernate.Hibernate;
21 import org.joda.time.DateTime;
22 import org.joda.time.Period;
23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
25 import org.springframework.stereotype.Component;
26 import org.springframework.transaction.PlatformTransactionManager;
27 import org.springframework.transaction.TransactionDefinition;
28 import org.springframework.transaction.TransactionStatus;
29 import org.springframework.transaction.support.DefaultTransactionDefinition;
30
31 import eu.etaxonomy.cdm.model.common.DefaultTermInitializer;
32 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
33 import eu.etaxonomy.cdm.model.common.Language;
34 import eu.etaxonomy.cdm.model.common.Representation;
35 import eu.etaxonomy.cdm.model.common.TermVocabulary;
36 import eu.etaxonomy.cdm.model.common.VocabularyEnum;
37 import eu.etaxonomy.cdm.model.common.init.TermLoader;
38 import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
39
40 /**
41 * Spring bean class to initialize the {@link IVocabularyStore IVocabularyStore}.
42 * To initialize the store the {@link TermLoader TermLoader} and the {@link IVocabularyStore IVocabularyStore}
43 * are injected via spring and the initializeTerms method is called as an init-method (@PostConstruct).
44
45 * @author a.mueller
46 */
47
48 @Component
49 public class PersistentTermInitializer extends DefaultTermInitializer {
50 private static final Logger logger = Logger.getLogger(PersistentTermInitializer.class);
51
52 private boolean omit = false;
53 protected ITermVocabularyDao vocabularyDao;
54
55 protected PlatformTransactionManager transactionManager;
56 protected DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
57
58 public PersistentTermInitializer() {
59 txDefinition.setName("PersistentTermInitializer.initialize()");
60 txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
61 }
62
63 public void setOmit(boolean omit) {
64 this.omit = omit;
65 }
66
67 public boolean isOmit() {
68 return omit;
69 }
70
71
72 @Autowired
73 public void setVocabularyDao(ITermVocabularyDao vocabularyDao) {
74 this.vocabularyDao = vocabularyDao;
75 }
76
77 @Autowired
78 public void setTransactionManager(PlatformTransactionManager transactionManager) {
79 this.transactionManager = transactionManager;
80 }
81
82 /*
83 * After a bit of head-scratching I found section 3.5.1.3. in the current spring
84 * reference manual - @PostConstruct / afterPropertiesSet() is called
85 * immediatly after the bean is constructed, prior to any AOP interceptors being
86 * wrapped round the bean. Thus, we have to use programmatic transactions, not
87 * annotations or pointcuts.
88 */
89 @PostConstruct
90 @Override
91 public void initialize() {
92 super.initialize();
93 }
94
95
96 @Override
97 public void doInitialize(){
98 logger.info("PersistentTermInitializer initialize start ...");
99
100 if (omit){
101 logger.info("PersistentTermInitializer.omit == true, returning without initializing terms");
102 return;
103 } else {
104 Map<UUID,DefinedTermBase> terms = new HashMap<UUID,DefinedTermBase>();
105 logger.info("PersistentTermInitializer.omit == false, initializing " + VocabularyEnum.values().length + " term classes");
106
107 DateTime start = new DateTime();
108
109 TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
110
111 //load uuids from csv files
112 logger.info("Start new ... " );
113 Map<UUID, Set<UUID>> uuidMap = new HashMap<UUID, Set<UUID>>();
114 Map<UUID, VocabularyEnum> vocTypeMap = new HashMap<UUID, VocabularyEnum>();
115
116 for(VocabularyEnum vocabularyType : VocabularyEnum.values()) {
117 UUID vocUUID = termLoader.loadUuids(vocabularyType, uuidMap);
118 if (! vocUUID.equals(vocabularyType.getUuid())){
119 throw new IllegalStateException("Vocabulary uuid in csv file and vocabulary type differ for vocabulary type " + vocabularyType.toString());
120 }
121 vocTypeMap.put(vocUUID, vocabularyType);
122 }
123
124 //find and create missing terms and load vocabularies from repository
125 logger.info("Create missing terms ... " );
126 Map<UUID, TermVocabulary<?>> vocabularyMap = new HashMap<UUID, TermVocabulary<?>>();
127 Map<UUID, Set<UUID>> missingTermUuids = new HashMap<UUID, Set<UUID>>();
128
129 vocabularyDao.missingTermUuids(uuidMap, missingTermUuids, vocabularyMap);
130 for(VocabularyEnum vocabularyType : VocabularyEnum.values()) { //required to keep the order (language must be the first vocabulary to load)
131 UUID vocUuid = vocabularyType.getUuid();
132 if (missingTermUuids.keySet().contains(vocabularyType.getUuid()) || vocabularyMap.get(vocUuid) == null ){
133
134 VocabularyEnum vocType = vocTypeMap.get(vocUuid); //TODO not really necessary, we could also do VocType.getUuuid();
135 TermVocabulary<?> voc = vocabularyMap.get(vocUuid);
136 if (voc == null){
137 //vocabulary is missing
138 voc = termLoader.loadTerms(vocType, terms);
139 vocabularyDao.save(voc);
140 vocabularyMap.put(voc.getUuid(), voc);
141 }else{
142 //single terms are missing
143 Set<UUID> missingTermsOfVoc = missingTermUuids.get(vocUuid);
144 Set<? extends DefinedTermBase> createdTerms = termLoader.loadSingleTerms(vocType, voc, missingTermsOfVoc);
145 vocabularyDao.saveOrUpdate(voc);
146 }
147 }
148 initializeAndStore(vocabularyType, terms, vocabularyMap); //TODO
149 }
150
151 transactionManager.commit(txStatus);
152
153 DateTime end = new DateTime();
154 Period period = new Period(start, end);
155 logger.info ("Term loading took " + period.getSeconds() + "." + period.getMillis() + " seconds ");
156
157 }
158 logger.info("PersistentTermInitializer initialize end ...");
159 }
160
161
162 /**
163 * Initializes the static fields of the <code>TermVocabulary</code> classes.
164 *
165 * @param clazz the <code>Class</code> of the vocabulary
166 * @param vocabularyUuid the <code>UUID</code> of the vocabulary
167 * @param terms a <code>Map</code> containing all already
168 * loaded terms with their <code>UUID</code> as key
169 * @param vocabularyMap
170 */
171 protected void initializeAndStore(VocabularyEnum vocType, Map<UUID,DefinedTermBase> terms, Map<UUID, TermVocabulary<?>> vocabularyMap) {
172 Class<? extends DefinedTermBase<?>> clazz = vocType.getClazz();
173 UUID vocabularyUuid = vocType.getUuid();
174
175 if (logger.isDebugEnabled()){ logger.debug("Loading vocabulary for class " + clazz.getSimpleName() + " with uuid " + vocabularyUuid );}
176
177 TermVocabulary<?> persistedVocabulary;
178 if (vocabularyMap == null || vocabularyMap.get(vocabularyUuid) == null ){
179 persistedVocabulary = vocabularyDao.findByUuid(vocabularyUuid);
180 }else{
181 persistedVocabulary = vocabularyMap.get(vocabularyUuid);
182 }
183
184 if (logger.isDebugEnabled()){ logger.debug("Initializing terms in vocabulary for class " + clazz.getSimpleName() + " with uuid " + vocabularyUuid );}
185 //not really needed anymore as we do term initializing from the beginning now
186 if (persistedVocabulary != null){
187 for(Object object : persistedVocabulary.getTerms()) {
188 DefinedTermBase<?> definedTermBase = (DefinedTermBase) object;
189 Hibernate.initialize(definedTermBase.getRepresentations());
190 for(Representation r : definedTermBase.getRepresentations()) {
191 Hibernate.initialize(r.getLanguage());
192 }
193 terms.put(definedTermBase.getUuid(), definedTermBase);
194 }
195 }else{
196 logger.error("Persisted Vocabulary does not exist in database: " + vocabularyUuid);
197 throw new IllegalStateException("Persisted Vocabulary does not exist in database: " + vocabularyUuid);
198 }
199
200
201 //fill term store
202 if (logger.isDebugEnabled()){ logger.debug("Setting defined Terms for class " + clazz.getSimpleName() + ", " + persistedVocabulary.getTerms().size() + " in vocabulary");}
203 super.setDefinedTerms(clazz, persistedVocabulary);
204 if (logger.isDebugEnabled()){ logger.debug("Second pass - DONE");}
205
206 }
207
208 /**
209 * This method loads the vocabularies from CSV files and compares them to the vocabularies
210 * already in database. Non-existing vocabularies will be created and vocabularies with missing
211 * terms will be updated.
212 *
213 * @param clazz the <code>Class</code> of the vocabulary
214 * @param persistedTerms a <code>Map</code> containing all already
215 * loaded terms with their <code>UUID</code> as key
216 * @return the <code>UUID</code> of the loaded vocabulary as found in CSV file
217 */
218 public UUID firstPass(VocabularyEnum vocabularyType, Map<UUID, DefinedTermBase> persistedTerms) {
219 logger.info("Loading terms for '" + vocabularyType.name() + "': " + vocabularyType.getClazz().getName());
220 Map<UUID,DefinedTermBase> terms = new HashMap<UUID,DefinedTermBase>();
221
222 for(DefinedTermBase persistedTerm : persistedTerms.values()) {
223 terms.put(persistedTerm.getUuid(), persistedTerm);
224 }
225
226 TermVocabulary<?> loadedVocabulary = termLoader.loadTerms(vocabularyType, terms);
227
228 UUID vocabularyUuid = loadedVocabulary.getUuid();
229
230
231 if (logger.isDebugEnabled()){logger.debug("loading persisted vocabulary " + vocabularyUuid);}
232 TermVocabulary<DefinedTermBase> persistedVocabulary = vocabularyDao.findByUuid(vocabularyUuid);
233 if(persistedVocabulary == null) { // i.e. there is no persisted vocabulary
234 //handle new vocabulary
235 if (logger.isDebugEnabled()){logger.debug("vocabulary " + vocabularyUuid + " does not exist - saving");}
236 saveVocabulary(loadedVocabulary);
237 }else {
238 //handle existing vocabulary
239 if (logger.isDebugEnabled()){logger.debug("vocabulary " + vocabularyUuid + " does exist and already has " + persistedVocabulary.size() + " terms");}
240 boolean persistedVocabularyHasMissingTerms = false;
241 for(Object t : loadedVocabulary.getTerms()) {
242 if(!persistedVocabulary.getTerms().contains(t)) {
243 persistedVocabularyHasMissingTerms = true;
244 persistedVocabulary.addTerm((DefinedTermBase)t);
245 }
246 }
247 if(persistedVocabularyHasMissingTerms) {
248 if (logger.isDebugEnabled()){logger.debug("vocabulary " + vocabularyUuid + " exists but does not have all the required terms - updating");}
249 updateVocabulary(persistedVocabulary);
250 }
251 }
252 return vocabularyUuid;
253 }
254
255 private void updateVocabulary(TermVocabulary vocabulary) {
256 TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
257 vocabularyDao.update(vocabulary);
258 transactionManager.commit(txStatus);
259 }
260
261 private void saveVocabulary(TermVocabulary vocabulary) {
262 TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
263 vocabularyDao.save(vocabulary);
264 transactionManager.commit(txStatus);
265 }
266 }