Project

General

Profile

Download (13.8 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.RegistrationUI;
65
import eu.etaxonomy.cdm.vaadin.ui.StatusEditorUI;
66
import eu.etaxonomy.vaadin.ui.annotation.EnableVaadinSpringNavigation;
67

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

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

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

    
101
    @Autowired
102
    Environment env;
103

    
104
    @Autowired
105
    private SessionFactory sessionFactory;
106

    
107
    @Autowired
108
    private ApplicationConfiguration appConfig;
109

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

    
114
    @Autowired
115
    private ITaxonGraphHibernateListener taxonGraphHibernateListener;
116

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

    
122
    private boolean registrationUiHibernateEventListenersDone = false;
123

    
124

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

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

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

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

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

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

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

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

    
175
                }});
176

    
177
        }
178
         */
179

    
180
    }
181

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

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

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

    
205
    /**
206
     * this is only a quick implementation for testing,
207
     * TODO see also the NOTE on CdmListenerIntegrator class declaration for a prospective better solution
208
     */
209
    protected void registerRegistrationUiHibernateEventListeners() {
210
        if(!registrationUiHibernateEventListenersDone){
211
            EventListenerRegistry listenerRegistry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(
212
                    EventListenerRegistry.class);
213

    
214
            listenerRegistry.appendListeners(EventType.POST_UPDATE, new GrantedAuthorityRevokingRegistrationUpdateLister());
215
            // TODO also POST_DELETE needed for GrantedAuthorityRevokingRegistrationUpdateLister?
216

    
217
            try {
218
                taxonGraphHibernateListener.registerProcessClass(TaxonGraphBeforeTransactionCompleteProcess.class, new Object[]{new RunAsAdmin(runAsAuthenticationProvider)}, new Class[]{IRunAs.class});
219
            } catch (NoSuchMethodException | SecurityException e) {
220
                // re-throw as RuntimeException as the context can not be created correctly
221
                throw new RuntimeException(e);
222
            }
223

    
224
            registrationUiHibernateEventListenersDone = true;
225
        }
226
    }
227

    
228
    @Bean
229
    public AbstractDataInserter registrationRequiredDataInserter() throws BeansException{
230
        if(isUIEnabled(RegistrationUI.class)){
231
            RegistrationRequiredDataInserter inserter = new RegistrationRequiredDataInserter();
232

    
233
            inserter.setRunAsAuthenticationProvider((AuthenticationProvider) applicationContext.getBean("runAsAuthenticationProvider"));
234
            inserter.setCdmRepository((CdmRepository) applicationContext.getBean("cdmRepository"));
235
            return inserter;
236
        } else {
237
            // the return type implements ApplicationListener and thus must not be null,
238
            // therefore we return a empty dummy implementation.
239
            return new DummyDataInserter();
240
        }
241
    }
242

    
243
    @Bean
244
    public RegistrationIdentifierMinter registrationIdentifierMinter() throws IOException {
245
        RegistrationIdentifierMinter minter = new RegistrationIdentifierMinter();
246

    
247
        minter.setMinLocalId(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_MINID));
248
        minter.setMaxLocalId(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_MAXID));
249
        minter.setIdentifierFormatString(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_IDFORMAT));
250
        return minter;
251
    }
252

    
253
    @Bean
254
    @UIScope
255
    public DistributionStatusUI distributionStatusUI() {
256
        if(isUIEnabled(DistributionStatusUI.class)){
257
            return new DistributionStatusUI();
258
        }
259
        return null;
260
    }
261

    
262
    @Bean
263
    @UIScope
264
    public StatusEditorUI statusEditorUI() {
265
        if(isUIEnabled(StatusEditorUI.class)){
266
            return new StatusEditorUI();
267
        }
268
        return null;
269
    }
270

    
271
    static final String PROPERTIES_FILE_NAME = "vaadin-apps";
272

    
273
    private Properties appProps = null;
274

    
275
    private ApplicationContext applicationContext;
276

    
277
    private List<String> activeUIpaths;
278

    
279
    //@formatter:off
280
    private static final String APP_FILE_CONTENT=
281
            "########################################################\n"+
282
            "#                                                       \n"+
283
            "# Vaadin application specific configurations            \n"+
284
            "#                                                       \n"+
285
            "########################################################\n"+
286
            "                                                        \n"+
287
            "# Enablement of vaadin uis.                             \n"+
288
            "#                                                       \n"+
289
            "# Multiple uis can be defined as comma separated list.  \n"+
290
            "# Whitespace before and after the comma will be ignored.\n"+
291
            "# Valid values are the path properties of the @SpringUI \n"+
292
            "# annotation which is used for UI classes.              \n"+
293
            "cdm-vaadin.ui.activated=concept,distribution,editstatus \n";
294
    //@formatter:on
295

    
296
    /**
297
     * Checks if the ui class supplied is activated by listing it in the properties by its {@link SpringUI#path()} value.
298
     *
299
     * TODO see https://dev.e-taxonomy.eu/redmine/issues/7139 (consider using spring profiles to enable vaadin UI contexts)
300
     *
301
     * @param type
302
     * @return
303
     */
304
    private boolean isUIEnabled(Class<? extends UI>uiClass) {
305

    
306
        String path = uiClass.getAnnotation(SpringUI.class).path().trim();
307

    
308
        if(activeUIpaths == null){
309
            String activatedVaadinUIs = env.getProperty(CDM_VAADIN_UI_ACTIVATED);
310
            if(activatedVaadinUIs == null){
311
                // not in environment? Read it from the config file!
312
                activatedVaadinUIs = appConfig.getProperty(configFile , CDM_VAADIN_UI_ACTIVATED);
313
            } else {
314
                logger.warn("Active UIs are defined via system properties -D" + CDM_VAADIN_UI_ACTIVATED + "=" +  activatedVaadinUIs + " ignoring config file." );
315
            }
316

    
317
            if(activatedVaadinUIs != null) {
318
                String[] uiPaths = activatedVaadinUIs.split("\\s*,\\s*");
319
                this.activeUIpaths = Arrays.asList(uiPaths);
320
            }
321
        }
322
        if(activeUIpaths.stream().anyMatch(p -> p.trim().equals(path))){
323
            return true;
324
        }
325
        logger.warn(" UI " + path + " not enabled in " + configFile.getFileName() + ".properties" );
326
        return false;
327

    
328
    }
329

    
330

    
331

    
332
    /**
333
     * {@inheritDoc}
334
     */
335
    @Override
336
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
337
        this.applicationContext = applicationContext;
338
    }
339

    
340

    
341
}
(1-1/2)