All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.spring.navigator.SpringViewProvider Maven / Gradle / Ivy

/*
 * Copyright 2015 The original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.vaadin.spring.navigator;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;

import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewProvider;
import com.vaadin.spring.access.ViewAccessControl;
import com.vaadin.spring.access.ViewInstanceAccessControl;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.spring.internal.Conventions;
import com.vaadin.spring.internal.ViewCache;
import com.vaadin.spring.internal.ViewScopeImpl;
import com.vaadin.spring.server.SpringVaadinServletService;
import com.vaadin.ui.UI;

/**
 * A Vaadin {@link ViewProvider} that fetches the views from the Spring
 * application context. The views must implement the {@link View} interface and
 * be annotated with the {@link SpringView} annotation.
 * 

* Use like this: * *

 * @SpringUI
 * public class MyUI extends UI {
 *
 *     @Autowired
 *     SpringViewProvider viewProvider;
 *
 *     protected void init(VaadinRequest vaadinRequest) {
 *         Navigator navigator = new Navigator(this, this);
 *         navigator.addProvider(viewProvider);
 *         setNavigator(navigator);
 *         // ...
 *     }
 * }
 * 
* * View-based security can be provided by creating a Spring bean that implements * the interface {@link com.vaadin.spring.access.ViewAccessControl} (for view * bean name and annotation based security) or * {@link com.vaadin.spring.access.ViewInstanceAccessControl} (if view instance * specific contextual data is needed). It is also possible to set an 'Access * Denied' view by using {@link #setAccessDeniedViewClass(Class)}. * * @author Petter Holmström ([email protected]) * @author Henri Sara ([email protected]) * @see SpringView */ public class SpringViewProvider implements ViewProvider { private static final long serialVersionUID = 6906237177564157222L; /* * Note! This is should be a singleton bean but it is probably not if you * serialize and deserialize a VaadinSession. * * This should be fixed so that SpringViewProvider is not a singleton bean * but is UIScoped. This should also remove the need for using * UI.getCurrent(). */ // We can have multiple views with the same view name, as long as they // belong to different UI subclasses private final Map> viewNameToBeanNamesMap = new ConcurrentHashMap>(); private transient BeanDefinitionRegistry beanDefinitionRegistry; private transient ApplicationContext applicationContext; private static final Logger LOGGER = LoggerFactory .getLogger(SpringViewProvider.class); private Class accessDeniedViewClass; @Autowired public SpringViewProvider(ApplicationContext applicationContext, BeanDefinitionRegistry beanDefinitionRegistry) { this.applicationContext = applicationContext; this.beanDefinitionRegistry = beanDefinitionRegistry; } /** * Returns the class of the access denied view. If set, a bean of this type * will be fetched from the application context and showed to the user when * a {@link com.vaadin.spring.access.ViewAccessControl} or a * {@link com.vaadin.spring.access.ViewInstanceAccessControl} denies access * to a view. * * @return the access denied view class, or {@code null} if not set. */ public Class getAccessDeniedViewClass() { return accessDeniedViewClass; } /** * Sets the class of the access denied view. If set, a bean of this type * will be fetched from the application context and showed to the user when * a {@link com.vaadin.spring.access.ViewAccessControl} or a * {@link com.vaadin.spring.access.ViewInstanceAccessControl} denies access * to a view. * * @param accessDeniedViewClass * the access denied view class, may be {@code null}. */ public void setAccessDeniedViewClass( Class accessDeniedViewClass) { this.accessDeniedViewClass = accessDeniedViewClass; } @PostConstruct void init() { LOGGER.info("Looking up SpringViews"); int count = 0; final String[] viewBeanNames = getWebApplicationContext() .getBeanNamesForAnnotation(SpringView.class); for (String beanName : viewBeanNames) { final Class type = getWebApplicationContext().getType(beanName); if (View.class.isAssignableFrom(type)) { final SpringView annotation = getWebApplicationContext() .findAnnotationOnBean(beanName, SpringView.class); final String viewName = getViewNameFromAnnotation(type, annotation); LOGGER.debug("Found SpringView bean [{}] with view name [{}]", beanName, viewName); if (getWebApplicationContext().isSingleton(beanName)) { throw new IllegalStateException("SpringView bean [" + beanName + "] must not be a singleton"); } Set beanNames = viewNameToBeanNamesMap.get(viewName); if (beanNames == null) { beanNames = new ConcurrentSkipListSet(); viewNameToBeanNamesMap.put(viewName, beanNames); } beanNames.add(beanName); count++; } else { LOGGER.error("The view bean [{}] does not implement View", beanName); throw new IllegalStateException("SpringView bean [" + beanName + "] must implement View"); } } if (count == 0) { LOGGER.warn("No SpringViews found"); } else if (count == 1) { LOGGER.info("1 SpringView found"); } else { LOGGER.info("{} SpringViews found", count); } } protected String getViewNameFromAnnotation(Class beanClass, SpringView annotation) { return Conventions.deriveMappingForView(beanClass, annotation); } @Override public String getViewName(String viewAndParameters) { LOGGER.trace("Extracting view name from [{}]", viewAndParameters); String viewName = null; if (isViewNameValidForCurrentUI(viewAndParameters)) { viewName = viewAndParameters; } else { int lastSlash = -1; String viewPart = viewAndParameters; while ((lastSlash = viewPart.lastIndexOf('/')) > -1) { viewPart = viewPart.substring(0, lastSlash); LOGGER.trace("Checking if [{}] is a valid view", viewPart); if (isViewNameValidForCurrentUI(viewPart)) { viewName = viewPart; break; } } } if (viewName == null) { LOGGER.trace("Found no view name in [{}]", viewAndParameters); } else { LOGGER.trace("[{}] is a valid view", viewName); } return viewName; } private boolean isViewNameValidForCurrentUI(String viewName) { final Set beanNames = viewNameToBeanNamesMap.get(viewName); if (beanNames != null) { for (String beanName : beanNames) { if (isViewBeanNameValidForCurrentUI(beanName)) { // if we have an access denied view, this is checked by // getView() return getAccessDeniedView() != null || isAccessGrantedToBeanName(beanName); } } } return false; } private boolean isViewBeanNameValidForCurrentUI(String beanName) { try { final Class type = getWebApplicationContext().getType(beanName); Assert.isAssignable(View.class, type, "bean did not implement View interface"); final UI currentUI = UI.getCurrent(); final SpringView annotation = getWebApplicationContext() .findAnnotationOnBean(beanName, SpringView.class); Assert.notNull(annotation, "class did not have a SpringView annotation"); if (annotation.ui().length == 0) { LOGGER.trace( "View class [{}] with view name [{}] is available for all UI subclasses", type.getCanonicalName(), getViewNameFromAnnotation(type, annotation)); } else { Class validUI = getValidUIClass(currentUI, annotation.ui()); if (validUI != null) { LOGGER.trace( "View class [%s] with view name [{}] is available for UI subclass [{}]", type.getCanonicalName(), getViewNameFromAnnotation(type, annotation), validUI.getCanonicalName()); } else { return false; } } return true; } catch (NoSuchBeanDefinitionException ex) { return false; } } private Class getValidUIClass(UI currentUI, Class[] validUIClasses) { for (Class validUI : validUIClasses) { if (validUI.isAssignableFrom(currentUI.getClass())) { return validUI; } } return null; } @Override public View getView(String viewName) { final Set beanNames = viewNameToBeanNamesMap.get(viewName); if (beanNames != null) { for (String beanName : beanNames) { if (isViewBeanNameValidForCurrentUI(beanName)) { return getViewFromApplicationContext(viewName, beanName); } } } LOGGER.warn("Found no view with name [{}]", viewName); return null; } private View getViewFromApplicationContext(String viewName, String beanName) { View view = null; if (isAccessGrantedToBeanName(beanName)) { final BeanDefinition beanDefinition = getBeanDefinitionRegistry() .getBeanDefinition(beanName); if (beanDefinition.getScope() .equals(ViewScopeImpl.VAADIN_VIEW_SCOPE_NAME)) { LOGGER.trace("View [{}] is view scoped, activating scope", viewName); final ViewCache viewCache = ViewScopeImpl .getViewCacheRetrievalStrategy() .getViewCache(getWebApplicationContext()); viewCache.creatingView(viewName); try { view = getViewFromApplicationContextAndCheckAccess( beanName); } finally { viewCache.viewCreated(viewName, view); } } else { view = getViewFromApplicationContextAndCheckAccess(beanName); } } if (view != null) { return view; } else { return getAccessDeniedView(); } } private BeanDefinitionRegistry getBeanDefinitionRegistry() { if (beanDefinitionRegistry == null) { AutowireCapableBeanFactory factory = getWebApplicationContext() .getAutowireCapableBeanFactory(); beanDefinitionRegistry = (BeanDefinitionRegistry) factory; } return beanDefinitionRegistry; } private View getViewFromApplicationContextAndCheckAccess(String beanName) { final View view = (View) getWebApplicationContext().getBean(beanName); if (isAccessGrantedToViewInstance(beanName, view)) { return view; } else { return null; } } private View getAccessDeniedView() { if (accessDeniedViewClass != null) { return getWebApplicationContext().getBean(accessDeniedViewClass); } else { return null; } } private boolean isAccessGrantedToBeanName(String beanName) { final UI currentUI = UI.getCurrent(); final Map accessDelegates = getWebApplicationContext() .getBeansOfType(ViewAccessControl.class); for (ViewAccessControl accessDelegate : accessDelegates.values()) { if (!accessDelegate.isAccessGranted(currentUI, beanName)) { LOGGER.debug( "Access delegate [{}] denied access to view with bean name [{}]", accessDelegate, beanName); return false; } } return true; } private boolean isAccessGrantedToViewInstance(String beanName, View view) { final UI currentUI = UI.getCurrent(); final Map accessDelegates = getWebApplicationContext() .getBeansOfType(ViewInstanceAccessControl.class); for (ViewInstanceAccessControl accessDelegate : accessDelegates .values()) { if (!accessDelegate.isAccessGranted(currentUI, beanName, view)) { LOGGER.debug("Access delegate [{}] denied access to view [{}]", accessDelegate, view); return false; } } return true; } protected ApplicationContext getWebApplicationContext() { if (applicationContext == null) { // Assume we have serialized and deserialized and Navigator is // trying to find a view so UI.getCurrent() is available UI ui = UI.getCurrent(); if (ui == null) { throw new IllegalStateException( "Could not find application context and no current UI is available"); } applicationContext = ((SpringVaadinServletService) ui.getSession() .getService()).getWebApplicationContext(); } return applicationContext; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy