7535c2d896320a48e0e8c38ecaae5c40f3744633
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / cdm / addon / config / CdmVaadinConfiguration.java
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
15 import javax.servlet.annotation.WebServlet;
16
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;
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.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;
69
70 /**
71 * @author a.kohlbecker
72 * @since Feb 8, 2017
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 private final static Logger logger = LogManager.getLogger();
95
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";
100
101
102 @Autowired
103 private 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 IPreferenceDao preferenceDao;
120
121
122 @Autowired
123 private void setTermCacher(CdmCacherBase termCacher){
124 CdmTransientEntityCacher.setPermanentCacher(termCacher);
125 }
126
127 private boolean registrationUiHibernateEventListenersDone = false;
128
129
130 private ApplicationConfigurationFile configFile = new ApplicationConfigurationFile(PROPERTIES_FILE_NAME, APP_FILE_CONTENT);
131
132 /*
133 * NOTES:
134 *
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
138 */
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
151 )
152 @WebServlet(name="CdmVaadinServlet", value = {"/app/*", "/VAADIN/*"}, asyncSupported = true)
153 public static class CdmVaadinServlet extends SpringVaadinServlet {
154
155 private static final long serialVersionUID = 1L;
156
157 /*
158 @Override
159 protected void servletInitialized() throws ServletException {
160
161 super.servletInitialized();
162
163 getService().addSessionInitListener(new SessionInitListener() {
164
165 private static final long serialVersionUID = 1L;
166
167 @Override
168 public void sessionInit(SessionInitEvent sessionInitEvent) throws ServiceException {
169 VaadinSession session = sessionInitEvent.getSession();
170 session.setErrorHandler(new DefaultErrorHandler(){
171
172 private static final long serialVersionUID = 1L;
173
174 @Override
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);
181 } else {
182 doDefault(errorEvent);
183 }
184 }
185
186 private UIDisabledException findUIDisabledException(Throwable throwable) {
187 if(throwable instanceof UIDisabledException) {
188 return (UIDisabledException)throwable;
189 } else {
190 if(throwable.getCause() == null) {
191 return null;
192 } else {
193 return findUIDisabledException(throwable.getCause());
194 }
195 }
196 }
197
198 });
199
200 }});
201
202 logger.debug("SpringVaadinServlet initialized");
203 }
204 */
205 }
206
207 public CdmVaadinConfiguration() {
208 logger.debug("CdmVaadinConfiguration enabled");
209 }
210
211 @Bean
212 @UIScope
213 public ConceptRelationshipUI conceptRelationshipUI() {
214 return applyEnableConfig(new ConceptRelationshipUI());
215 }
216
217 @Bean
218 @UIScope
219 public RegistrationUI registrationUI() {
220 return applyEnableConfig(new RegistrationUI());
221 }
222
223 @Bean
224 @UIScope
225 public UserAccountSelfManagementUI userAccountSelfManagementUI() {
226 return applyEnableConfig(new UserAccountSelfManagementUI());
227 }
228
229 @Bean
230 @UIScope
231 public DistributionStatusUI distributionStatusUI() {
232 return applyEnableConfig( new DistributionStatusUI());
233 }
234
235 @Bean
236 @UIScope
237 public StatusEditorUI statusEditorUI() {
238 return applyEnableConfig(new StatusEditorUI());
239 }
240
241 /**
242 * this is only a quick implementation for testing,
243 * TODO see also the NOTE on CdmListenerIntegrator class declaration for a prospective better solution
244 */
245 protected void registerRegistrationUiHibernateEventListeners() {
246 if(!registrationUiHibernateEventListenersDone){
247 EventListenerRegistry listenerRegistry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(
248 EventListenerRegistry.class);
249
250 listenerRegistry.appendListeners(EventType.POST_UPDATE, new GrantedAuthorityRevokingRegistrationUpdateLister());
251 // TODO also POST_DELETE needed for GrantedAuthorityRevokingRegistrationUpdateLister?
252
253 try {
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);
261 }
262
263 registrationUiHibernateEventListenersDone = true;
264 }
265 }
266
267 @Bean
268 public AbstractDataInserter registrationRequiredDataInserter() throws BeansException{
269 if(isUIEnabled(RegistrationUI.class)){
270 RegistrationRequiredDataInserter inserter = new RegistrationRequiredDataInserter();
271
272 inserter.setRunAsAuthenticationProvider((AuthenticationProvider) applicationContext.getBean("runAsAuthenticationProvider"));
273 inserter.setCdmRepository((CdmRepository) applicationContext.getBean("cdmRepository"));
274 return inserter;
275 } else {
276 // the return type implements ApplicationListener and thus must not be null,
277 // therefore we return a empty dummy implementation.
278 return new DummyDataInserter();
279 }
280 }
281
282 @Bean
283 public RegistrationIdentifierMinter registrationIdentifierMinter() throws IOException {
284 RegistrationIdentifierMinter minter = new RegistrationIdentifierMinter();
285
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));
289 return minter;
290 }
291
292 static final String PROPERTIES_FILE_NAME = "vaadin-apps";
293
294 private ApplicationContext applicationContext;
295
296 private List<String> activeUIpaths;
297
298 //@formatter:off
299 private static final String APP_FILE_CONTENT=
300 "################################################################\n"+
301 "# \n"+
302 "# Vaadin application specific configurations \n"+
303 "# \n"+
304 "################################################################\n"+
305 " \n"+
306 "# Enablement of vaadin uis. \n"+
307 "# \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";
313 //@formatter:on
314
315 /**
316 * Checks if the ui class supplied is activated by listing it in the properties by its {@link SpringUI#path()} value.
317 */
318 private boolean isUIEnabled(Class<? extends UI>uiClass) {
319
320 String path = uiClass.getAnnotation(SpringUI.class).path().trim();
321
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);
327 } else {
328 logger.warn("Active UIs are defined via system properties -D" + CDM_VAADIN_UI_ACTIVATED + "=" + activatedVaadinUIs + " ignoring config file." );
329 }
330
331 if(activatedVaadinUIs != null) {
332 String[] uiPaths = activatedVaadinUIs.split("\\s*,\\s*");
333 this.activeUIpaths = Arrays.asList(uiPaths);
334 }
335 }
336 if(activeUIpaths.stream().anyMatch(p -> p.trim().equals(path))){
337 return true;
338 }
339 logger.warn(" UI " + path + " not enabled in " + configFile.getFileName() + ".properties" );
340 return false;
341
342 }
343
344 private <T extends UI> T applyEnableConfig(T ui) {
345 ui.setEnabled(isUIEnabled(ui.getClass()));
346 return ui;
347 }
348
349 @Override
350 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
351 this.applicationContext = applicationContext;
352 }
353
354
355 }