Project

General

Profile

Download (10.7 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2015 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
package eu.etaxonomy.cdm.api.validation.batch;
10

    
11
import java.util.List;
12
import java.util.Set;
13

    
14
import javax.validation.ConstraintViolation;
15
import javax.validation.Validation;
16
import javax.validation.Validator;
17
import javax.validation.ValidatorFactory;
18

    
19
import org.apache.log4j.Logger;
20
import org.hibernate.validator.HibernateValidator;
21
import org.hibernate.validator.HibernateValidatorConfiguration;
22
import org.springframework.context.ApplicationContext;
23
import org.springframework.context.ApplicationContextAware;
24
import org.springframework.orm.hibernate5.HibernateTransactionManager;
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.api.application.ICdmRepository;
32
import eu.etaxonomy.cdm.api.service.ICommonService;
33
import eu.etaxonomy.cdm.api.service.IEntityValidationService;
34
import eu.etaxonomy.cdm.api.service.IService;
35
import eu.etaxonomy.cdm.model.common.CdmBase;
36
import eu.etaxonomy.cdm.model.common.ICdmBase;
37
import eu.etaxonomy.cdm.model.validation.CRUDEventType;
38
import eu.etaxonomy.cdm.persistence.dao.jdbc.validation.EntityValidationCrudJdbcImpl;
39
import eu.etaxonomy.cdm.validation.Level2;
40
import eu.etaxonomy.cdm.validation.Level3;
41

    
42
/**
43
 * @author ayco_holleman
44
 * @author a.mueller
45
 \* @since 27 jan. 2015
46
 *
47
 */
48
@Component("batchValidator")
49
public class BatchValidator implements Runnable, ApplicationContextAware {
50

    
51
    static final Class<?>[] DEFAULT_VALIDATION_GROUPS = new Class<?>[] { Level2.class, Level3.class };
52

    
53
    private static final Logger logger = Logger.getLogger(BatchValidator.class);
54

    
55

    
56
    private ICdmRepository repository;
57

    
58
    private ApplicationContext appContext;
59

    
60
    private Validator validator;
61
    private Class<?>[] validationGroups;
62

    
63

    
64
    @Override
65
    public void setApplicationContext(ApplicationContext appContext) {
66
        this.appContext = appContext;
67
    }
68

    
69
    @Override
70
    public void run() {
71
        Thread.currentThread().setPriority(1);
72
        initValidator();
73
        validate();
74
    }
75

    
76
    /**
77
     *
78
     */
79
    private void initValidator() {
80
        if (getValidator() == null){
81
            HibernateValidatorConfiguration config = Validation.byProvider(HibernateValidator.class).configure();
82
            ValidatorFactory factory = config.buildValidatorFactory();
83
            setValidator(factory.getValidator());
84
        }
85
        if (validationGroups == null) {
86
            validationGroups = DEFAULT_VALIDATION_GROUPS;
87
        }
88
    }
89

    
90

    
91

    
92
    private <T extends ICdmBase, S extends T> void validate() {
93
        logger.info("Starting batch validation");
94

    
95
       // Get service for saving errors to database
96
//        IEntityValidationService validationResultService = context.getEntityValidationService();
97
        IEntityValidationService entityValidationService = appContext.getBean(IEntityValidationService.class);
98

    
99
        EntityValidationCrudJdbcImpl jdbcPersister = appContext.getBean(EntityValidationCrudJdbcImpl.class);
100

    
101
        // Get all services dealing with "real" entities
102
        List<Class<CdmBase>> classesToValidate = BatchValidationUtil.getClassesToValidate();
103

    
104
        for (Class<CdmBase> entityClass : classesToValidate) {
105
            //TODO currently this seems to work only on the exact class, we may move it down
106
            //to single entity validation again but cache the information for each class
107
            if (true || BatchValidationUtil.isConstrainedEntityClass(validator, entityClass)){
108

    
109
    //          ICommonService commonService = repository.getCommonService();
110
                ICommonService commonService = appContext.getBean(ICommonService.class);
111
                logger.info("Loading entities of type " + entityClass.getName());
112
                //false for saving validation results
113
                //TODO can we handle results in a different transaction?
114
                boolean readOnly = false;
115
                TransactionStatus txStatus =  startTransaction(readOnly);
116
                handleSingleClass(commonService, entityClass, entityValidationService, jdbcPersister);
117
                commitTransaction(txStatus);
118
            }
119
        }
120

    
121
        logger.info("Batch validation complete");
122
    }
123

    
124
    /**
125
     * @param txStatus
126
     */
127
    private void commitTransaction(TransactionStatus txStatus) {
128
        PlatformTransactionManager txManager = getTransactionManager();
129
        txManager.commit(txStatus);
130

    
131
    }
132

    
133
    /**
134
     * @param readOnly
135
     * @return
136
     *
137
     */
138
    private TransactionStatus startTransaction(boolean readOnly) {
139
        PlatformTransactionManager txManager = getTransactionManager();
140

    
141
        DefaultTransactionDefinition defaultTxDef = new DefaultTransactionDefinition();
142
        defaultTxDef.setReadOnly(readOnly);
143
        TransactionDefinition txDef = defaultTxDef;
144
        TransactionStatus txStatus = txManager.getTransaction(txDef);
145
        return txStatus;
146
    }
147

    
148
    /**
149
     * @return
150
     */
151
    private PlatformTransactionManager getTransactionManager() {
152
        PlatformTransactionManager txManager = appContext.getBean(HibernateTransactionManager.class);
153
        return txManager;
154
    }
155

    
156
    private void handleSingleClass(ICommonService commonService, Class<CdmBase> entityClass, IEntityValidationService entityValidationService, EntityValidationCrudJdbcImpl jdbcPersister) {
157
        int n = commonService.count(entityClass);
158
        int pageSize = 1000;
159
        for (int page = 0; page < n ; page = page + pageSize ){
160
            handlePage(commonService, entityClass, entityValidationService, jdbcPersister,
161
                    page/pageSize, pageSize);
162
        }
163
    }
164

    
165

    
166
    /**
167
     * @param commonService
168
     * @param entityClass
169
     * @param entityValidationService
170
     * @param jdbcPersister
171
     *
172
     */
173
    private void handlePage(ICommonService commonService, Class<CdmBase> entityClass, IEntityValidationService entityValidationService, EntityValidationCrudJdbcImpl jdbcPersister, int start, int pageSize) {
174

    
175
        List<CdmBase> entities;
176

    
177
        try {
178
//            commonService.count()
179
            entities = commonService.list(entityClass, pageSize, 0, null, null);
180
        } catch (Throwable t) {
181
            //TODO handle exception
182
            logger.error("Failed to load entities", t);
183
            return;
184
        }
185
        for (CdmBase entity : entities) {
186
            try {
187
                Set<ConstraintViolation<CdmBase>> errors = getValidator().validate(entity, getValidationGroups());
188
                if (errors.size() != 0) {
189
                    if (logger.isInfoEnabled()){logger.info(errors.size() + " constraint violation(s) detected in entity " + entity.toString());}
190
//                    entityValidationService.saveEntityValidation(entity, errors, CRUDEventType.NONE,
191
//                            getValidationGroups());
192

    
193
                    jdbcPersister.saveEntityValidation(entity, errors, CRUDEventType.NONE, getValidationGroups());
194
                }
195
            } catch (Exception e) {
196
                // TODO Exception handling
197
                e.printStackTrace();
198
            }
199
        }
200

    
201
    }
202

    
203
    private <T extends ICdmBase, S extends T> void validate_old() {
204
        logger.info("Starting batch validation");
205

    
206
        if (validationGroups == null) {
207
            validationGroups = DEFAULT_VALIDATION_GROUPS;
208
        }
209

    
210
        // Get service for saving errors to database
211
        IEntityValidationService validationResultService = repository.getEntityValidationService();
212

    
213
        // Get all services dealing with "real" entities
214
        List<EntityValidationUnit<T, S>> validationUnits = BatchValidationUtil.getAvailableServices(repository);
215

    
216
        for (EntityValidationUnit<T, S> unit : validationUnits) {
217
            Class<S> entityClass = unit.getEntityClass();
218
            IService<T> entityLoader = unit.getEntityLoader();
219
            logger.info("Loading entities of type " + entityClass.getName());
220
            List<S> entities;
221
            try {
222
                entities = entityLoader.list(entityClass, 0, 0, null, null);
223
            } catch (Throwable t) {
224
                logger.error("Failed to load entities", t);
225
                continue;
226
            }
227
            for (S entity : entities) {
228
                if (BatchValidationUtil.isConstrainedEntityClass(getValidator(), entity.getClass())) {
229
                    Set<ConstraintViolation<S>> errors = getValidator().validate(entity, validationGroups);
230
                    if (errors.size() != 0) {
231
                        logger.warn(errors.size() + " error(s) detected in entity " + entity.toString());
232
                        validationResultService.saveEntityValidation(entity, errors, CRUDEventType.NONE,
233
                                validationGroups);
234
                    }
235
                }
236
            }
237
        }
238

    
239
        logger.info("Batch validation complete");
240
    }
241

    
242
    /**
243
     * Get the application context that will provide the services that will, on
244
     * their turn, provide the entities to be validated.
245
     *
246
     * @return The application context
247
     */
248
    public ICdmRepository getAppController() {
249
        return repository;
250
    }
251

    
252
    /**
253
     * Set the application context.
254
     *
255
     * @param context
256
     *            The application context
257
     */
258
    public void setAppController(ICdmRepository context) {
259
        this.repository = context;
260
    }
261

    
262
    /**
263
     * Get the {@code Validator} instance that will carry out the validations.
264
     *
265
     * @return The {@code Validator}
266
     */
267
    public Validator getValidator() {
268
        return validator;
269
    }
270

    
271
    /**
272
     * Set the {@code Validator} instance that will carry out the validations.
273
     *
274
     * @param validator
275
     *            The {@code Validator}
276
     */
277
    public void setValidator(Validator validator) {
278
        this.validator = validator;
279
    }
280

    
281
    /**
282
     * Get the validation groups to be applied by the {@code Validator}.
283
     *
284
     * @return The validation groups
285
     */
286
    public Class<?>[] getValidationGroups() {
287
        return validationGroups;
288
    }
289

    
290
    /**
291
     * Set the validation groups to be applied by the {@code Validator}. By
292
     * default all Level2 and Level3 will be checked. So if that is what you
293
     * want, you do not need to call this method before calling {@link #run()}.
294
     *
295
     * @param validationGroups
296
     *            The validation groups
297
     */
298
    public void setValidationGroups(Class<?>... validationGroups) {
299
        this.validationGroups = validationGroups;
300
    }
301

    
302
}
(2-2/4)