Project

General

Profile

Download (14.1 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2017 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.addon.config;
10

    
11
import java.io.IOException;
12
import java.util.Arrays;
13
import java.util.List;
14
import java.util.Properties;
15

    
16
import javax.servlet.annotation.WebServlet;
17

    
18
import org.apache.log4j.Logger;
19
import org.hibernate.SessionFactory;
20
import org.hibernate.event.service.spi.EventListenerRegistry;
21
import org.hibernate.event.spi.EventType;
22
import org.hibernate.internal.SessionFactoryImpl;
23
import org.springframework.beans.BeansException;
24
import org.springframework.beans.factory.annotation.Autowired;
25
import org.springframework.beans.factory.annotation.Qualifier;
26
import org.springframework.context.ApplicationContext;
27
import org.springframework.context.ApplicationContextAware;
28
import org.springframework.context.annotation.Bean;
29
import org.springframework.context.annotation.ComponentScan;
30
import org.springframework.context.annotation.ComponentScan.Filter;
31
import org.springframework.context.annotation.Configuration;
32
import org.springframework.context.annotation.FilterType;
33
import org.springframework.core.env.Environment;
34
import org.springframework.security.authentication.AuthenticationProvider;
35
import org.vaadin.spring.events.annotation.EnableEventBus;
36

    
37
import com.vaadin.annotations.VaadinServletConfiguration;
38
import com.vaadin.server.DeploymentConfiguration.LegacyProperyToStringMode;
39
import com.vaadin.spring.annotation.EnableVaadin;
40
import com.vaadin.spring.annotation.SpringUI;
41
import com.vaadin.spring.annotation.UIScope;
42
import com.vaadin.spring.server.SpringVaadinServlet;
43
import com.vaadin.ui.UI;
44

    
45
import eu.etaxonomy.cdm.api.application.AbstractDataInserter;
46
import eu.etaxonomy.cdm.api.application.CdmRepository;
47
import eu.etaxonomy.cdm.api.application.DummyDataInserter;
48
import eu.etaxonomy.cdm.api.application.IRunAs;
49
import eu.etaxonomy.cdm.api.application.RunAsAdmin;
50
import eu.etaxonomy.cdm.api.cache.CdmCacherBase;
51
import eu.etaxonomy.cdm.api.config.ApplicationConfiguration;
52
import eu.etaxonomy.cdm.api.config.ApplicationConfigurationFile;
53
import eu.etaxonomy.cdm.api.service.idminter.RegistrationIdentifierMinter;
54
import eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphBeforeTransactionCompleteProcess;
55
import eu.etaxonomy.cdm.cache.CdmTransientEntityCacher;
56
import eu.etaxonomy.cdm.config.CdmHibernateListener;
57
import eu.etaxonomy.cdm.dataInserter.RegistrationRequiredDataInserter;
58
import eu.etaxonomy.cdm.persistence.hibernate.GrantedAuthorityRevokingRegistrationUpdateLister;
59
import eu.etaxonomy.cdm.persistence.hibernate.ITaxonGraphHibernateListener;
60
import eu.etaxonomy.cdm.vaadin.permission.annotation.EnableAnnotationBasedAccessControl;
61
import eu.etaxonomy.cdm.vaadin.ui.CdmBaseUI;
62
import eu.etaxonomy.cdm.vaadin.ui.ConceptRelationshipUI;
63
import eu.etaxonomy.cdm.vaadin.ui.DistributionStatusUI;
64
import eu.etaxonomy.cdm.vaadin.ui.PasswordResetUI;
65
import eu.etaxonomy.cdm.vaadin.ui.RegistrationUI;
66
import eu.etaxonomy.cdm.vaadin.ui.StatusEditorUI;
67
import eu.etaxonomy.vaadin.ui.annotation.EnableVaadinSpringNavigation;
68

    
69
/**
70
 *
71
 * @author a.kohlbecker
72
 * @since Feb 8, 2017
73
 *
74
 */
75
@Configuration
76
@ComponentScan(basePackages={
77
        "eu.etaxonomy.vaadin.ui",
78
        "eu.etaxonomy.cdm.vaadin",
79
        "eu.etaxonomy.cdm.service",
80
        "org.springframework.context.event"
81
        },
82
        // exclude UI classes, these are provided via the @Bean annotated methods below
83
        excludeFilters={@Filter(
84
                pattern="eu\\.etaxonomy\\.cdm\\.vaadin\\.ui\\..*",
85
                type=FilterType.REGEX
86
                )
87
            })
88
@EnableVaadin   // this imports VaadinConfiguration
89
@EnableVaadinSpringNavigation // activate the NavigationManagerBean
90
@EnableAnnotationBasedAccessControl // enable annotation based per view access control
91
@EnableEventBus // enable the vaadin spring event bus
92
@CdmHibernateListener // enable the configuration which activates the TaxonGraphHibernateListener bean
93
public class CdmVaadinConfiguration implements ApplicationContextAware  {
94

    
95
    public static final String CDM_VAADIN_UI_ACTIVATED = "cdm-vaadin.ui.activated";
96
    public static final String CDM_SERVICE_MINTER_REGSTRATION_MINID = "cdm.service.minter.registration.minLocalId";
97
    public static final String CDM_SERVICE_MINTER_REGSTRATION_MAXID = "cdm.service.minter.registration.maxLocalId";
98
    public static final String CDM_SERVICE_MINTER_REGSTRATION_IDFORMAT = "cdm.service.minter.registration.identifierFormatString";
99

    
100
    public static final Logger logger = Logger.getLogger(CdmVaadinConfiguration.class);
101

    
102
    @Autowired
103
    Environment env;
104

    
105
    @Autowired
106
    private SessionFactory sessionFactory;
107

    
108
    @Autowired
109
    private ApplicationConfiguration appConfig;
110

    
111
    @Autowired
112
    @Qualifier("runAsAuthenticationProvider")
113
    private AuthenticationProvider runAsAuthenticationProvider;
114

    
115
    @Autowired
116
    private ITaxonGraphHibernateListener taxonGraphHibernateListener;
117

    
118
    @Autowired
119
    private void  setTermCacher(CdmCacherBase termCacher){
120
        CdmTransientEntityCacher.setPermanentCacher(termCacher);
121
    }
122

    
123
    private boolean registrationUiHibernateEventListenersDone = false;
124

    
125

    
126
    ApplicationConfigurationFile configFile = new ApplicationConfigurationFile(PROPERTIES_FILE_NAME, APP_FILE_CONTENT);
127

    
128
    /*
129
     * NOTES:
130
     *
131
     * (1) It is necessary to map the URLs starting with /VAADIN/* since none of the
132
     * @WebServlets is mapped to the root path. It is sufficient to configure one of the
133
     * servlets with this path see BookOfVaadin 5.9.5. Servlet Mapping with URL Patterns
134
     */
135
    @VaadinServletConfiguration(
136
            productionMode = false,    // FIXME get value from application.properties or
137
                                       // use two different CdmVaadinServlets for
138
                                       // different spring profiles?
139
            ui = CdmBaseUI.class,      // TODO better default UI to use?
140
            // If the closeIdleSessions parameter for the servlet is
141
            // enabled (disabled by default), Vaadin closes the UIs and the session after the time specified
142
            // in the session-timeout parameter expires after the last non-heartbeat request.
143
            // For session-timeout see <session-config>
144
            // Legacy mode to return the value of the property as a string from AbstractProperty.toString()
145
            closeIdleSessions=true,
146
            legacyPropertyToStringMode=LegacyProperyToStringMode.ENABLED
147
            )
148
    @WebServlet(name="CdmVaadinServlet", value = {"/app/*", "/VAADIN/*"}, asyncSupported = true)
149
    public static class CdmVaadinServlet extends SpringVaadinServlet {
150

    
151
        private static final long serialVersionUID = -2615042297393028775L;
152

    
153
        /**
154
         *
155
        @SuppressWarnings("serial")
156
        @Override
157
        protected void servletInitialized() throws ServletException {
158
            logger.debug("SpringVaadinServlet initialized");
159

    
160
        }
161
            getService().addSessionInitListener(new SessionInitListener() {
162

    
163
                @Override
164
                public void sessionInit(SessionInitEvent sessionInitEvent) throws ServiceException {
165
                    VaadinSession session = sessionInitEvent.getSession();
166
                    session.setErrorHandler(new DefaultErrorHandler(){
167

    
168
                        @Override
169
                        public void error(ErrorEvent errorEvent) {
170
                            // ...
171
                        }
172

    
173
                    });
174
                    ).getServiceRegistry().getService
175

    
176
                }});
177

    
178
        }
179
         */
180

    
181
    }
182

    
183
    public CdmVaadinConfiguration() {
184
        logger.debug("CdmVaadinConfiguration enabled");
185
    }
186

    
187
    @Bean
188
    @UIScope
189
    public ConceptRelationshipUI conceptRelationshipUI() {
190
        if(isUIEnabled(ConceptRelationshipUI.class)){
191
            return new ConceptRelationshipUI();
192
        }
193
        return null;
194
    }
195

    
196
    @Bean
197
    @UIScope
198
    public RegistrationUI registrationUI() {
199
        if(isUIEnabled(RegistrationUI.class)){
200
            registerRegistrationUiHibernateEventListeners();
201
            return new RegistrationUI();
202
        }
203
        return null;
204
    }
205

    
206
    @Bean
207
    @UIScope
208
    public PasswordResetUI passwordResetUI() {
209
        if(isUIEnabled(PasswordResetUI.class)){
210
            registerRegistrationUiHibernateEventListeners();
211
            return new PasswordResetUI();
212
        }
213
        return null;
214
    }
215

    
216
    /**
217
     * this is only a quick implementation for testing,
218
     * TODO see also the NOTE on CdmListenerIntegrator class declaration for a prospective better solution
219
     */
220
    protected void registerRegistrationUiHibernateEventListeners() {
221
        if(!registrationUiHibernateEventListenersDone){
222
            EventListenerRegistry listenerRegistry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(
223
                    EventListenerRegistry.class);
224

    
225
            listenerRegistry.appendListeners(EventType.POST_UPDATE, new GrantedAuthorityRevokingRegistrationUpdateLister());
226
            // TODO also POST_DELETE needed for GrantedAuthorityRevokingRegistrationUpdateLister?
227

    
228
            try {
229
                taxonGraphHibernateListener.registerProcessClass(TaxonGraphBeforeTransactionCompleteProcess.class, new Object[]{new RunAsAdmin(runAsAuthenticationProvider)}, new Class[]{IRunAs.class});
230
            } catch (NoSuchMethodException | SecurityException e) {
231
                // re-throw as RuntimeException as the context can not be created correctly
232
                throw new RuntimeException(e);
233
            }
234

    
235
            registrationUiHibernateEventListenersDone = true;
236
        }
237
    }
238

    
239
    @Bean
240
    public AbstractDataInserter registrationRequiredDataInserter() throws BeansException{
241
        if(isUIEnabled(RegistrationUI.class)){
242
            RegistrationRequiredDataInserter inserter = new RegistrationRequiredDataInserter();
243

    
244
            inserter.setRunAsAuthenticationProvider((AuthenticationProvider) applicationContext.getBean("runAsAuthenticationProvider"));
245
            inserter.setCdmRepository((CdmRepository) applicationContext.getBean("cdmRepository"));
246
            return inserter;
247
        } else {
248
            // the return type implements ApplicationListener and thus must not be null,
249
            // therefore we return a empty dummy implementation.
250
            return new DummyDataInserter();
251
        }
252
    }
253

    
254
    @Bean
255
    public RegistrationIdentifierMinter registrationIdentifierMinter() throws IOException {
256
        RegistrationIdentifierMinter minter = new RegistrationIdentifierMinter();
257

    
258
        minter.setMinLocalId(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_MINID));
259
        minter.setMaxLocalId(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_MAXID));
260
        minter.setIdentifierFormatString(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_IDFORMAT));
261
        return minter;
262
    }
263

    
264
    @Bean
265
    @UIScope
266
    public DistributionStatusUI distributionStatusUI() {
267
        if(isUIEnabled(DistributionStatusUI.class)){
268
            return new DistributionStatusUI();
269
        }
270
        return null;
271
    }
272

    
273
    @Bean
274
    @UIScope
275
    public StatusEditorUI statusEditorUI() {
276
        if(isUIEnabled(StatusEditorUI.class)){
277
            return new StatusEditorUI();
278
        }
279
        return null;
280
    }
281

    
282
    static final String PROPERTIES_FILE_NAME = "vaadin-apps";
283

    
284
    private Properties appProps = null;
285

    
286
    private ApplicationContext applicationContext;
287

    
288
    private List<String> activeUIpaths;
289

    
290
    //@formatter:off
291
    private static final String APP_FILE_CONTENT=
292
            "########################################################\n"+
293
            "#                                                       \n"+
294
            "# Vaadin application specific configurations            \n"+
295
            "#                                                       \n"+
296
            "########################################################\n"+
297
            "                                                        \n"+
298
            "# Enablement of vaadin uis.                             \n"+
299
            "#                                                       \n"+
300
            "# Multiple uis can be defined as comma separated list.  \n"+
301
            "# Whitespace before and after the comma will be ignored.\n"+
302
            "# Valid values are the path properties of the @SpringUI \n"+
303
            "# annotation which is used for UI classes.              \n"+
304
            "cdm-vaadin.ui.activated=concept,distribution,editstatus \n";
305
    //@formatter:on
306

    
307
    /**
308
     * Checks if the ui class supplied is activated by listing it in the properties by its {@link SpringUI#path()} value.
309
     *
310
     * TODO see https://dev.e-taxonomy.eu/redmine/issues/7139 (consider using spring profiles to enable vaadin UI contexts)
311
     *
312
     * @param type
313
     * @return
314
     */
315
    private boolean isUIEnabled(Class<? extends UI>uiClass) {
316

    
317
        String path = uiClass.getAnnotation(SpringUI.class).path().trim();
318

    
319
        if(activeUIpaths == null){
320
            String activatedVaadinUIs = env.getProperty(CDM_VAADIN_UI_ACTIVATED);
321
            if(activatedVaadinUIs == null){
322
                // not in environment? Read it from the config file!
323
                activatedVaadinUIs = appConfig.getProperty(configFile , CDM_VAADIN_UI_ACTIVATED);
324
            } else {
325
                logger.warn("Active UIs are defined via system properties -D" + CDM_VAADIN_UI_ACTIVATED + "=" +  activatedVaadinUIs + " ignoring config file." );
326
            }
327

    
328
            if(activatedVaadinUIs != null) {
329
                String[] uiPaths = activatedVaadinUIs.split("\\s*,\\s*");
330
                this.activeUIpaths = Arrays.asList(uiPaths);
331
            }
332
        }
333
        if(activeUIpaths.stream().anyMatch(p -> p.trim().equals(path))){
334
            return true;
335
        }
336
        logger.warn(" UI " + path + " not enabled in " + configFile.getFileName() + ".properties" );
337
        return false;
338

    
339
    }
340

    
341

    
342

    
343
    /**
344
     * {@inheritDoc}
345
     */
346
    @Override
347
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
348
        this.applicationContext = applicationContext;
349
    }
350

    
351

    
352
}
(1-1/2)