2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.addon
.config
;
11 import java
.io
.IOException
;
12 import java
.util
.Arrays
;
13 import java
.util
.List
;
15 import javax
.servlet
.annotation
.WebServlet
;
17 import org
.apache
.logging
.log4j
.LogManager
;
18 import org
.apache
.logging
.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
;
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
;
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
.dao
.common
.IPreferenceDao
;
59 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.GrantedAuthorityRevokingRegistrationUpdateLister
;
60 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.ITaxonGraphHibernateListener
;
61 import eu
.etaxonomy
.cdm
.vaadin
.permission
.annotation
.EnableAnnotationBasedAccessControl
;
62 import eu
.etaxonomy
.cdm
.vaadin
.ui
.CdmBaseUI
;
63 import eu
.etaxonomy
.cdm
.vaadin
.ui
.ConceptRelationshipUI
;
64 import eu
.etaxonomy
.cdm
.vaadin
.ui
.DistributionStatusUI
;
65 import eu
.etaxonomy
.cdm
.vaadin
.ui
.RegistrationUI
;
66 import eu
.etaxonomy
.cdm
.vaadin
.ui
.StatusEditorUI
;
67 import eu
.etaxonomy
.cdm
.vaadin
.ui
.UserAccountSelfManagementUI
;
68 import eu
.etaxonomy
.vaadin
.ui
.annotation
.EnableVaadinSpringNavigation
;
71 * @author a.kohlbecker
75 @ComponentScan(basePackages
={
76 "eu.etaxonomy.vaadin.ui",
77 "eu.etaxonomy.cdm.vaadin",
78 "eu.etaxonomy.cdm.service",
79 "org.springframework.context.event"
81 // exclude UI classes, these are provided via the @Bean annotated methods below
82 excludeFilters
={@Filter(
83 pattern
="eu\\.etaxonomy\\.cdm\\.vaadin\\.ui\\..*",
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
{
94 private final static Logger logger
= LogManager
.getLogger();
96 public static final String CDM_VAADIN_UI_ACTIVATED
= "cdm-vaadin.ui.activated";
97 public static final String CDM_SERVICE_MINTER_REGSTRATION_MINID
= "cdm.service.minter.registration.minLocalId";
98 public static final String CDM_SERVICE_MINTER_REGSTRATION_MAXID
= "cdm.service.minter.registration.maxLocalId";
99 public static final String CDM_SERVICE_MINTER_REGSTRATION_IDFORMAT
= "cdm.service.minter.registration.identifierFormatString";
103 private Environment env
;
106 private SessionFactory sessionFactory
;
109 private ApplicationConfiguration appConfig
;
112 @Qualifier("runAsAuthenticationProvider")
113 private AuthenticationProvider runAsAuthenticationProvider
;
116 private ITaxonGraphHibernateListener taxonGraphHibernateListener
;
119 private IPreferenceDao preferenceDao
;
123 private void setTermCacher(CdmCacherBase termCacher
){
124 CdmTransientEntityCacher
.setPermanentCacher(termCacher
);
127 private boolean registrationUiHibernateEventListenersDone
= false;
130 private ApplicationConfigurationFile configFile
= new ApplicationConfigurationFile(PROPERTIES_FILE_NAME
, APP_FILE_CONTENT
);
135 * (1) It is necessary to map the URLs starting with /VAADIN/* since none of the
136 * @WebServlets is mapped to the root path. It is sufficient to configure one of the
137 * servlets with this path see BookOfVaadin 5.9.5. Servlet Mapping with URL Patterns
139 @VaadinServletConfiguration(
140 productionMode
= false, // FIXME get value from application.properties or
141 // use two different CdmVaadinServlets for
142 // different spring profiles?
143 ui
= CdmBaseUI
.class, // TODO better default UI to use?
144 // If the closeIdleSessions parameter for the servlet is
145 // enabled (disabled by default), Vaadin closes the UIs and the session after the time specified
146 // in the session-timeout parameter expires after the last non-heartbeat request.
147 // For session-timeout see <session-config>
148 // Legacy mode to return the value of the property as a string from AbstractProperty.toString()
149 closeIdleSessions
=true,
150 legacyPropertyToStringMode
=LegacyProperyToStringMode
.ENABLED
152 @WebServlet(name
="CdmVaadinServlet", value
= {"/app/*", "/VAADIN/*"}, asyncSupported
= true)
153 public static class CdmVaadinServlet
extends SpringVaadinServlet
{
155 private static final long serialVersionUID
= 1L;
159 protected void servletInitialized() throws ServletException {
161 super.servletInitialized();
163 getService().addSessionInitListener(new SessionInitListener() {
165 private static final long serialVersionUID = 1L;
168 public void sessionInit(SessionInitEvent sessionInitEvent) throws ServiceException {
169 VaadinSession session = sessionInitEvent.getSession();
170 session.setErrorHandler(new DefaultErrorHandler(){
172 private static final long serialVersionUID = 1L;
175 public void error(ErrorEvent errorEvent) {
176 UIDisabledException uiDisbledException = findUIDisabledException(errorEvent.getThrowable());
177 if(uiDisbledException != null) {
178 logger.error("################## > UIDisabledException");
179 //throw uiDisbledException ;
180 doDefault(errorEvent);
182 doDefault(errorEvent);
186 private UIDisabledException findUIDisabledException(Throwable throwable) {
187 if(throwable instanceof UIDisabledException) {
188 return (UIDisabledException)throwable;
190 if(throwable.getCause() == null) {
193 return findUIDisabledException(throwable.getCause());
202 logger.debug("SpringVaadinServlet initialized");
207 public CdmVaadinConfiguration() {
208 logger
.debug("CdmVaadinConfiguration enabled");
213 public ConceptRelationshipUI
conceptRelationshipUI() {
214 return applyEnableConfig(new ConceptRelationshipUI());
219 public RegistrationUI
registrationUI() {
220 return applyEnableConfig(new RegistrationUI());
225 public UserAccountSelfManagementUI
userAccountSelfManagementUI() {
226 return applyEnableConfig(new UserAccountSelfManagementUI());
231 public DistributionStatusUI
distributionStatusUI() {
232 return applyEnableConfig( new DistributionStatusUI());
237 public StatusEditorUI
statusEditorUI() {
238 return applyEnableConfig(new StatusEditorUI());
242 * this is only a quick implementation for testing,
243 * TODO see also the NOTE on CdmListenerIntegrator class declaration for a prospective better solution
245 protected void registerRegistrationUiHibernateEventListeners() {
246 if(!registrationUiHibernateEventListenersDone
){
247 EventListenerRegistry listenerRegistry
= ((SessionFactoryImpl
) sessionFactory
).getServiceRegistry().getService(
248 EventListenerRegistry
.class);
250 listenerRegistry
.appendListeners(EventType
.POST_UPDATE
, new GrantedAuthorityRevokingRegistrationUpdateLister());
251 // TODO also POST_DELETE needed for GrantedAuthorityRevokingRegistrationUpdateLister?
254 taxonGraphHibernateListener
.registerProcessClass(
255 TaxonGraphBeforeTransactionCompleteProcess
.class,
256 new Object
[]{new RunAsAdmin(runAsAuthenticationProvider
), preferenceDao
},
257 new Class
[]{IRunAs
.class, IPreferenceDao
.class});
258 } catch (NoSuchMethodException
| SecurityException e
) {
259 // re-throw as RuntimeException as the context can not be created correctly
260 throw new RuntimeException(e
);
263 registrationUiHibernateEventListenersDone
= true;
268 public AbstractDataInserter
registrationRequiredDataInserter() throws BeansException
{
269 if(isUIEnabled(RegistrationUI
.class)){
270 RegistrationRequiredDataInserter inserter
= new RegistrationRequiredDataInserter();
272 inserter
.setRunAsAuthenticationProvider((AuthenticationProvider
) applicationContext
.getBean("runAsAuthenticationProvider"));
273 inserter
.setCdmRepository((CdmRepository
) applicationContext
.getBean("cdmRepository"));
276 // the return type implements ApplicationListener and thus must not be null,
277 // therefore we return a empty dummy implementation.
278 return new DummyDataInserter();
283 public RegistrationIdentifierMinter
registrationIdentifierMinter() throws IOException
{
284 RegistrationIdentifierMinter minter
= new RegistrationIdentifierMinter();
286 minter
.setMinLocalId(appConfig
.getProperty(configFile
, CDM_SERVICE_MINTER_REGSTRATION_MINID
));
287 minter
.setMaxLocalId(appConfig
.getProperty(configFile
, CDM_SERVICE_MINTER_REGSTRATION_MAXID
));
288 minter
.setIdentifierFormatString(appConfig
.getProperty(configFile
, CDM_SERVICE_MINTER_REGSTRATION_IDFORMAT
));
292 static final String PROPERTIES_FILE_NAME
= "vaadin-apps";
294 private ApplicationContext applicationContext
;
296 private List
<String
> activeUIpaths
;
299 private static final String APP_FILE_CONTENT
=
300 "################################################################\n"+
302 "# Vaadin application specific configurations \n"+
304 "################################################################\n"+
306 "# Enablement of vaadin uis. \n"+
308 "# Multiple uis can be defined as comma separated list. \n"+
309 "# Whitespace before and after the comma will be ignored. \n"+
310 "# Valid values are the path properties of the @SpringUI \n"+
311 "# annotation which is used for UI classes. \n"+
312 "cdm-vaadin.ui.activated=account,concept,distribution,editstatus \n";
316 * Checks if the ui class supplied is activated by listing it in the properties by its {@link SpringUI#path()} value.
318 private boolean isUIEnabled(Class
<?
extends UI
>uiClass
) {
320 String path
= uiClass
.getAnnotation(SpringUI
.class).path().trim();
322 if(activeUIpaths
== null){
323 String activatedVaadinUIs
= env
.getProperty(CDM_VAADIN_UI_ACTIVATED
);
324 if(activatedVaadinUIs
== null){
325 // not in environment? Read it from the config file!
326 activatedVaadinUIs
= appConfig
.getProperty(configFile
, CDM_VAADIN_UI_ACTIVATED
);
328 logger
.warn("Active UIs are defined via system properties -D" + CDM_VAADIN_UI_ACTIVATED
+ "=" + activatedVaadinUIs
+ " ignoring config file." );
331 if(activatedVaadinUIs
!= null) {
332 String
[] uiPaths
= activatedVaadinUIs
.split("\\s*,\\s*");
333 this.activeUIpaths
= Arrays
.asList(uiPaths
);
336 if(activeUIpaths
.stream().anyMatch(p
-> p
.trim().equals(path
))){
339 logger
.warn(" UI " + path
+ " not enabled in " + configFile
.getFileName() + ".properties" );
344 private <T
extends UI
> T
applyEnableConfig(T ui
) {
345 ui
.setEnabled(isUIEnabled(ui
.getClass()));
350 public void setApplicationContext(ApplicationContext applicationContext
) throws BeansException
{
351 this.applicationContext
= applicationContext
;