Project

General

Profile

Download (12.6 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.spring.annotation.EnableVaadin;
38
import com.vaadin.spring.annotation.SpringUI;
39
import com.vaadin.spring.annotation.UIScope;
40
import com.vaadin.spring.server.SpringVaadinServlet;
41
import com.vaadin.ui.UI;
42

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

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

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

    
97
    public static final Logger logger = Logger.getLogger(CdmVaadinConfiguration.class);
98

    
99
    @Autowired
100
    Environment env;
101

    
102
    @Autowired
103
    private SessionFactory sessionFactory;
104

    
105
    @Autowired
106
    private ApplicationConfiguration appConfig;
107

    
108
    @Autowired
109
    @Qualifier("runAsAuthenticationProvider")
110
    private AuthenticationProvider runAsAuthenticationProvider;
111

    
112
    @Autowired
113
    private ITaxonGraphHibernateListener taxonGraphHibernateListener;
114

    
115
    @Autowired
116
    private void  setTermCacher(CdmCacher termCacher){
117
        CdmTransientEntityCacher.setPermanentCacher(termCacher);
118
    }
119

    
120
    private boolean registrationUiHibernateEventListenersDone = false;
121

    
122

    
123
    ApplicationConfigurationFile configFile = new ApplicationConfigurationFile(PROPERTIES_FILE_NAME, APP_FILE_CONTENT);
124

    
125
    /*
126
     * NOTES:
127
     *
128
     * (1) It is necessary to map the URLs starting with /VAADIN/* since none of the
129
     * @WebServlets is mapped to the root path. It is sufficient to configure one of the
130
     * servlets with this path see BookOfVaadin 5.9.5. Servlet Mapping with URL Patterns
131
     *
132
     * (2) @VaadinServletConfiguration is not used here, all DeploymentConfiguration is
133
     * configured in the web.xml. This way it it easier to modify the parameters for different
134
     * build environments like test and production
135
     */
136
    @WebServlet(value = {"/app/*", "/VAADIN/*"}, asyncSupported = true)
137
    public static class Servlet extends SpringVaadinServlet {
138

    
139
        private static final long serialVersionUID = -2615042297393028775L;
140

    
141

    
142
        /**
143
         *
144
        @SuppressWarnings("serial")
145
        @Override
146
        protected void servletInitialized() throws ServletException {
147
            getService().addSessionInitListener(new SessionInitListener() {
148

    
149
                @Override
150
                public void sessionInit(SessionInitEvent sessionInitEvent) throws ServiceException {
151
                    VaadinSession session = sessionInitEvent.getSession();
152
                    session.setErrorHandler(new DefaultErrorHandler(){
153

    
154
                        @Override
155
                        public void error(ErrorEvent errorEvent) {
156
                            // ...
157
                        }
158

    
159
                    });
160
                    ).getServiceRegistry().getService
161

    
162
                }});
163

    
164
        }
165
         */
166

    
167
    }
168

    
169
    public CdmVaadinConfiguration() {
170
        logger.debug("CdmVaadinConfiguration enabled");
171
    }
172

    
173
    @Bean
174
    @UIScope
175
    public ConceptRelationshipUI conceptRelationshipUI() {
176
        if(isUIEnabled(ConceptRelationshipUI.class)){
177
            return new ConceptRelationshipUI();
178
        }
179
        return null;
180
    }
181

    
182
    @Bean
183
    @UIScope
184
    public RegistrationUI registrationUI() {
185
        if(isUIEnabled(RegistrationUI.class)){
186
            registerRegistrationUiHibernateEventListeners();
187
            return new RegistrationUI();
188
        }
189
        return null;
190
    }
191

    
192
    /**
193
     * this is only a quick implementation for testing,
194
     * TODO see also the NOTE on CdmListenerIntegrator class declaration for a prospective better solution
195
     */
196
    protected void registerRegistrationUiHibernateEventListeners() {
197
        if(!registrationUiHibernateEventListenersDone){
198
            EventListenerRegistry listenerRegistry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(
199
                    EventListenerRegistry.class);
200

    
201
            listenerRegistry.appendListeners(EventType.POST_UPDATE, new GrantedAuthorityRevokingRegistrationUpdateLister());
202
            // TODO also POST_DELETE needed for GrantedAuthorityRevokingRegistrationUpdateLister?
203

    
204
            try {
205
                taxonGraphHibernateListener.registerProcessClass(TaxonGraphBeforeTransactionCompleteProcess.class, new Object[]{new RunAsAdmin(runAsAuthenticationProvider)}, new Class[]{IRunAs.class});
206
            } catch (NoSuchMethodException | SecurityException e) {
207
                // re-throw as RuntimeException as the context can not be created correctly
208
                throw new RuntimeException(e);
209
            }
210

    
211
            registrationUiHibernateEventListenersDone = true;
212
        }
213
    }
214

    
215
    @Bean
216
    public AbstractDataInserter registrationRequiredDataInserter() throws BeansException{
217
        if(isUIEnabled(RegistrationUI.class)){
218
            RegistrationRequiredDataInserter inserter = new RegistrationRequiredDataInserter();
219

    
220
            inserter.setRunAsAuthenticationProvider((AuthenticationProvider) applicationContext.getBean("runAsAuthenticationProvider"));
221
            inserter.setCdmRepository((CdmRepository) applicationContext.getBean("cdmRepository"));
222
            return inserter;
223
        } else {
224
            // the return type implements ApplicationListener and thus must not be null,
225
            // therefore we return a empty dummy implementation.
226
            return new DummyDataInserter();
227
        }
228
    }
229

    
230
    @Bean
231
    public RegistrationIdentifierMinter registrationIdentifierMinter() throws IOException {
232
        RegistrationIdentifierMinter minter = new RegistrationIdentifierMinter();
233

    
234
        minter.setMinLocalId(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_MINID));
235
        minter.setMaxLocalId(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_MAXID));
236
        minter.setIdentifierFormatString(appConfig.getProperty(configFile , CDM_SERVICE_MINTER_REGSTRATION_IDFORMAT));
237
        return minter;
238
    }
239

    
240
    @Bean
241
    @UIScope
242
    public DistributionStatusUI distributionStatusUI() {
243
        if(isUIEnabled(DistributionStatusUI.class)){
244
            return new DistributionStatusUI();
245
        }
246
        return null;
247
    }
248

    
249
    @Bean
250
    @UIScope
251
    public StatusEditorUI statusEditorUI() {
252
        if(isUIEnabled(StatusEditorUI.class)){
253
            return new StatusEditorUI();
254
        }
255
        return null;
256
    }
257

    
258
    static final String PROPERTIES_FILE_NAME = "vaadin-apps";
259

    
260
    private Properties appProps = null;
261

    
262
    private ApplicationContext applicationContext;
263

    
264
    private List<String> activeUIpaths;
265

    
266
    //@formatter:off
267
    private static final String APP_FILE_CONTENT=
268
            "########################################################\n"+
269
            "#                                                       \n"+
270
            "# Vaadin application specific configurations            \n"+
271
            "#                                                       \n"+
272
            "########################################################\n"+
273
            "                                                        \n"+
274
            "# Enablement of vaadin uis.                             \n"+
275
            "#                                                       \n"+
276
            "# Multiple uis can be defined as comma separated list.  \n"+
277
            "# Whitespace before and after the comma will be ignored.\n"+
278
            "# Valid values are the path properties of the @SpringUI \n"+
279
            "# annotation which is used for UI classes.              \n"+
280
            "cdm-vaadin.ui.activated=concept,distribution,editstatus \n";
281
    //@formatter:on
282

    
283
    /**
284
     * Checks if the ui class supplied is activated by listing it in the properties by its {@link SpringUI#path()} value.
285
     *
286
     * TODO see https://dev.e-taxonomy.eu/redmine/issues/7139 (consider using spring profiles to enable vaadin UI contexts)
287
     *
288
     * @param type
289
     * @return
290
     */
291
    private boolean isUIEnabled(Class<? extends UI>uiClass) {
292

    
293
        String path = uiClass.getAnnotation(SpringUI.class).path().trim();
294

    
295
        if(activeUIpaths == null){
296
            String activatedVaadinUIs = env.getProperty(CDM_VAADIN_UI_ACTIVATED);
297
            if(activatedVaadinUIs == null){
298
                // not in environment? Read it from the config file!
299
                activatedVaadinUIs = appConfig.getProperty(configFile , CDM_VAADIN_UI_ACTIVATED);
300
            }
301

    
302
            if(activatedVaadinUIs != null) {
303
                String[] uiPaths = activatedVaadinUIs.split("\\s*,\\s*");
304
                this.activeUIpaths = Arrays.asList(uiPaths);
305
            }
306
        }
307
        if(activeUIpaths.stream().anyMatch(p -> p.trim().equals(path))){
308
            return true;
309
        }
310
        return false;
311

    
312
    }
313

    
314

    
315

    
316
    /**
317
     * {@inheritDoc}
318
     */
319
    @Override
320
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
321
        this.applicationContext = applicationContext;
322
    }
323

    
324

    
325
}
(1-1/2)