I'm trying to put together a simple application using Vaadin + Spring Boot with just two views: login and dashboard. I'm using pieces of code from the Vaadin Dashboard demo. There is this method in the DashboardUI class:
private void updateContent() {
User user = (User) VaadinSession.getCurrent().getAttribute(
User.class.getName());
if (user != null && "admin".equals(user.getRole())) {
// Authenticated user
setContent(new MainView());
removeStyleName("loginview");
getNavigator().navigateTo(getNavigator().getState());
} else {
setContent(new LoginView());
addStyleName("loginview");
}
}
As you see the views are instantiated via the new keyword. Since I'm using Spring I would like the container take care of that. But I cannot figure out how to have the views injected by Spring.
Annotate your view with #VaadinUIScope and #VaadinComponent. And then you can use #Autowired to inject your view.
You should probably setup two UI classes i.e LoginUI.class and MainUI.class. Have Spring Security redirect unauthorized to /login and authorized to /
LoginUI.class
#Theme("valo")
#SpringUI(path = "login")
#PreserveOnRefresh
public class LoginUI extends UI {
private SpringViewProvider viewProvider;
#Autowired
public LoginUI(SpringViewProvider viewProvider) {
this.viewProvider = viewProvider;
}
#Override
protected void init(VaadinRequest request) {
Navigator navigator = new Navigator(this, this);
navigator.addProvider(viewProvider);
navigator.navigateTo(LoginView.VIEW_NAME);
}
}
MainAppUI.class
#Theme("valo")
#SpringUI
#PreserveOnRefresh
public class MainAppUI extends UI {
private SpringViewProvider viewProvider;
#Autowired
public AppUI(SpringViewProvider viewProvider) {
this.viewProvider = viewProvider;
}
#Override
protected void init(VaadinRequest request) {
getPage().setTitle("Main App");
setSizeFull();
Navigator navigator = new Navigator(this, viewDisplay);
navigator.addProvider(viewProvider);
setContent();//viewport
navigator.navigateTo(DashboardView.VIEW_NAME);
}
}
Then just use #SpringView(name = "moduleOne", ui = MainAppUI.class) on your module views for your app as suggested by Morfic to have the main navigator and module views registered only when a user has logged in to save on resources.
Note: Taking a look at that example it seems that they're not actually using views & navigator, they're somewhat faking them. If you want to proceed on the same path you can simply autowire the fake views in the UI instead of using the navigator as below.
The Vaadin-spring boot wiki offers an introduction to this by the use of:
1) #SpringUI with autowired SpringViewProvider
#Theme("valo")
#SpringUI
public class MyVaadinUI extends UI {
// we can use either constructor autowiring or field autowiring
#Autowired
private SpringViewProvider viewProvider;
#Override
protected void init(VaadinRequest request) {
// other init stuff
Navigator navigator = new Navigator(this, viewContainer);
navigator.addProvider(viewProvider);
}
}
2) #SpringView
#SpringView(name = DefaultView.VIEW_NAME)
public class DefaultView extends VerticalLayout implements View {
public static final String VIEW_NAME = "";
#PostConstruct
void init() {
addComponent(new Label("This is the default view"));
}
#Override
public void enter(ViewChangeEvent event) {
// the view is constructed in the init() method()
}
}
For the decision whether the user should be redirected to the login view, or the other ones, I usually use a ViewChangeListener, something along the lines of:
navigator.addViewChangeListener(new ViewChangeListener() {
#Override
public boolean beforeViewChange(ViewChangeEvent event) {
if (VaadinSession.getCurrent().getAttribute("user") == null) {
navigator.getDisplay().showView(loginView);
return false;
} else {
return true;
}
}
#Override
public void afterViewChange(ViewChangeEvent event) {
// meh
}
});
Related
I created a class using ResourceBundle interface as shown below. This class is dependent on another class. The implementation class for ResourceBundle (QuestionnaireSource as shown below) always has null as dependencies. No matter if I use setter or constructor injection.
Could someone please help me with this issue. I am I missing some configuration here.
#Component
public class QuestionnaireSource extends ResourceBundle {
private final QuestionnaireCache questionnaireCache;
private static final Object lockObject = new Object();
#Override
protected Object handleGetObject(String key) {
// Gets an object for the given key from this resource bundle.
// Returns null if this resource bundle does not contain an object for the given key 0
Object value = null;
try {
value = getString(key, LocaleContextHolder.getLocale());
} catch (Exception ignored) {
}
return value;
}
public Questionnaire getString(String key, Locale locale) {
Locale l = safeLocale(locale);
return getResources(l).get(key);
}
private Locale safeLocale(Locale l) {
if (l.getLanguage().equalsIgnoreCase("DE")) {
return Locale.GERMAN;
} else {
return Locale.ENGLISH;
}
}
protected Map<String, Questionnaire> getResources(Locale locale) {
synchronized (lockObject) {
return questionnaireCache.getQuestionnaireCache().get(locale.getLanguage().toUpperCase());
}
}
#Override
public Enumeration<String> getKeys() {
return null;
}
public QuestionnaireSource(QuestionnaireCache questionnaireCache) {
super();
this.questionnaireCache = questionnaireCache;
}
}
Update:
I found that even simple dependency injection in resourceBundle is failing.
UPdate2:
The way I am using in the main class is as follows:
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.QuestionnaireSource", locale);
System.out.println(bundle.getString("some.test.string"));
Update3
I am writing a simple example to convey the scenario:
Some service class
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
Some example implementation of resource bundle
#Component
public class MyResourceBundle extends ResourceBundle {
private final SomeServiceTest someServiceTest;
#Autowired
public MyResourceBundle(SomeServiceTest someServiceTest) {
this.someServiceTest = someServiceTest;
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return "test";
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
Main.java
main(){
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.MyResourceBundle", locale);
System.out.println(bundle.getString("test"));
}
Update4:
I changed the annotation on classes as mentioned by on this post https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
but still I have the null dependency injection for SomeServiceTest class. The changes are as shown below.
SomeServiceTest.java
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
MyResourceBundle.java
#Configurable
public class MyResourceBundle extends ResourceBundle {
#Autowired
private SomeServiceTest someServiceTest;
public MyResourceBundle() {
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return someServiceTest.testMethod();
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
still SomeServiceTest class is null.
Can you please post an example on how you are using this class? Is it you (your code) or spring who instanciate it (on startup)?
#Component only works for beans which Spring instanciate. If you want to inject stuff in classes you instanciate in you code you can annotate the class with #Configurable.
Please see https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects for some examples.
Make sure you have initialized the spring context
If you are using spring boot
You can get the application context after it starts and use it to get the bean you want
For example
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(YouApplication.class, args);
MyResourceBundle resConfig = run.getBean("myResourceBundle", MyResourceBundle .class);
resConfig.handleGetObject("test");
}
Unfortunately ResourceBundle.getBundle does not initialize the spring application context
I am making a simple Social Media Website using Java Spring Boot. Now I want to add a profile edit page, where a logged in user can edit/update his profile data but other logged in users should not have access to it.
For example, there are two people John and Tom, John should be able to see only his profile edit page and Tom should see only his Profile edit page Only after login.
How to achieve this using Spring Security or by any other way ?
First of all you need to write BeanAccessor like following:
#Component
public class BeanAccessor implements ApplicationContextAware {
private static ApplicationContext context;
public static ObjectMapper getObjectMapper() {
return getBean(ObjectMapper.class);
}
public static <T> T getBean(Class<T> beanClass, Object... args) {
return context.getBean(beanClass, args);
}
private static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
then we need to write new class for method security like:
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
private Object filterObject;
private Object returnObject;
private Object target;
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
CustomMethodSecurityExpressionRoot setTarget(Object target) {
this.target = target;
return this;
}
#Override
public void setFilterObject(Object filterObject) {
this.filterObject = filterObject;
}
#Override
public Object getFilterObject() {
return filterObject;
}
#Override
public void setReturnObject(Object returnObject) {
this.returnObject = returnObject;
}
#Override
public Object getReturnObject() {
return returnObject;
}
#Override
public Object getThis() {
return target;
}
}
finally we need custom method security expressinon handler:
#Component
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
#Autowired
private CustomPermissionEvaluator customPermissionEvaluator;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
final CustomMethodSecurityExpressionRoot root = BeanAccessor.getBean(CustomMethodSecurityExpressionRoot.class, authentication);
root.setPermissionEvaluator(customPermissionEvaluator);
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
root.setTarget(invocation.getThis());
return root;
}
}
now on your controller method yo can define #PreAuthorize("isProfileOwner(#id)") annotations your user profile show page method looks like :
#PreAuthorize("isProfileOwner(#id)")
#GetMapping("{id}")
public String show(#PathVariable("id") Long id, Model model) {
//omitted
}
everything okey but we need to write isProfileOwner() method to our CustomMethodSecurityExpressionRoot class like:
public boolean isProfileOwner(Long id) {
//add logic here and you are ready
}
also you can check this post
I'm trying to Integrate Spring into Cucumber Selenium Project.
Project is already configured with PageObjectModel design pattern and working perfectly before integrating with Spring.
public abstract class BasePage {
public WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
}
public void verifyPage() {
//verify page
}
}
public class HomePage extends BasePage {
#FindBy(how = How.ID, using = "MENU_lINK")
private Link MENU_HEADER;
public HomePage(WebDriver driver) {
super(driver);
PageFactory.initElements(new HtmlElementDecorator(new HtmlElementLocatorFactory(driver)), this);
}
public Link getMENU_HEADER() {
return MENU_HEADER;
}
}
public class HomePageSteps {
private static final Logger LOGGER = Logger.getLogger(HomePageSteps.class.getName());
private WebDriver driver;
HomePage homePage;
HelperPage helperPage;
public HomePageSteps() {
driver = WebDriverProvider.driver; //Driver initiated here
homePage = new HomePage(driver);
}
#Given("I'm on home page")
public void navigateToHomePage() {
homePage.getMENU_HEADER().click();
}
After integrating with Spring issue noticed is all the features are executing in same browser.
Before Spring integration each feature used to execute in separated browser as #After and #Before annotations are used that are available in Cucumber
public class WebDriverProvider {
public static WebDriver driver;
#Before
public void openBrowser() throws MalformedURLException {
System.setProperty("webdriver.chrome.driver", new File(".").getAbsolutePath() + "/drivers/chromedriver.exe");
if (driver != null) driver.quit();
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(20,TimeUnit.SECONDS);
}
#After
public void embedScreenshot(Scenario scenario) {
driver.quit();
}
}
After integration with Spring in applicationcontext.xml file I have defined the Driver. I can't call driver from #After and #Before methods and all the tests are executed in same browser
Below is the code snippet attached.
<bean id="driverProvider" class="com.ecom.utils.DriverProvider" scope="prototype"/>
<bean id="webDriver"
class="org.openqa.selenium.WebDriver"
factory-bean="driverProvider"
factory-method="getDriver" scope="prototype"/>
public abstract class BasePage {
#Autowired
protected WebDriver driver;
public void verifyPage() {
//verify Page
}
}
#Component
public class HomePage extends BasePage {
#FindBy(how = How.XPATH, using = "//*[#id='js-siteMainNavigation']//a[#class='qa-Cl_Menu c-site-nav-main-link-1']")
private Link MENU_HEADER;
public HomePage(WebDriver driver) {
PageFactory.initElements(new HtmlElementDecorator(new HtmlElementLocatorFactory(driver)), this);
}
public Link getMENU_HEADER() {
return MENU_HEADER;
}
}
public class HomePageSteps {
private static final Logger LOGGER = Logger.getLogger(HomePageSteps.class.getName());
#Autowired
HomePage homePage;
#When("I navigate to pizza selection page")
public void navigateToMenuPage() {
homePage.getMENU_HEADER().click();
}
}
I don't see a way in calling spring bean from #After and #Before annotations.
I want to initiate a fresh browser for every feature.
I thought to create Web Driver in Steps and Pass across the Pages, but for this I have to define a constructor with WebDrviver
public HomePage(WebDriver driver) {
super(driver);
PageFactory.initElements(new HtmlElementDecorator(new HtmlElementLocatorFactory(driver)), this);
}
But Spring is complain for a bean associated to this.
Any help would be highly appreciated.
Why are you using Spring to inject the browser? Spring will make certain that the same instance, i.e. same browser, is used everywhere. That was dependency injection frameworks do.
Use Spring to inject objects that allows you to share state between steps if the steps are used in different step definition classes.
I have a Spring-boot VAADIN application with main classes as follows
Application Class
#SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
}
VAADIN-UI Class
#Theme("valo")
#SpringUI
public class MyAppUI extends UI {
#Autowired
private SpringViewProvider viewProvider;
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout mainLayout = new VerticalLayout();
setContent(mainLayout);
Navigator navigator = new Navigator(this, mainLayout);
navigator.addProvider(viewProvider);
}
}
VAADIN-View Class
#SpringView(name = "")
public class MyAppView extends VerticalLayout implements View {
#PostConstruct
void init() {
// Some logic here
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// Some logic here
}
}
Currently, the application handles the request in root URL i.e. say http://localhost:8080/. But I want the application to handle requests when a parameter is supplied by http://localhost:8080/<parameter_value>. How can I achieve this?
The logic I have to execute is the same in both cases i.e. I want MyAppView to process both root URL request and the one with a parameter value.
VAADIN's getUI().getPage().getLocation().getPath() method can be used to get the parameter value from the URL. This will give everything from '/' in the URL. Sample code is given below:
VAADIN-View Class
#SpringView(name = "")
public class MyAppView extends VerticalLayout implements View {
#PostConstruct
void init() {
// Some logic here
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// Remove spaces and also initial '/' in the path
String uriParamValue = getUI().getPage().getLocation().getPath().trim().substring(1);
// Do processing with uriParamValue
}
}
I am using JavaFX 2 in conjunction with the Spring Framework, however the injection happens to late. My Controller is instanciated by the FXML-Loader, the Spring injection for member variables of this Controller works, but it works only too late, this means that in (1) injection yet didn't happen and in (2) injection did happen:
public class MainController extends AbstractController
{
#Autowired
public StatusBarController statusbarController;
// Implementing Initializable Interface no longer required according to
// http://docs.oracle.com/javafx/2/fxml_get_started/whats_new2.htm:
private void initialize() {
BorderPane borderPane = (BorderPane)getView();
borderPane.setBottom(statusbarController.getView()); // (1) null exception!
}
// Linked to a button in the view
public void sayHello() {
BorderPane borderPane = (BorderPane)getView();
borderPane.setBottom(statusbarController.getView()); // (2) works!
}
}
Any way to let Spring inject statusbarController in an earlier state? I can't let the user have to click a button to load my GUI ;-)
My AppFactory is this:
#Configuration
public class AppFactory
{
#Bean
public MainController mainController() throws IOException
{
return (MainController) loadController("/main.fxml");
}
protected Object loadController(String url) throws IOException
{
InputStream fxmlStream = null;
try
{
fxmlStream = getClass().getResourceAsStream(url);
FXMLLoader loader = new FXMLLoader();
Node view = (Node) loader.load(fxmlStream);
AbstractController controller = (AbstractController) loader.getController();
controller.setView(view);
return controller;
}
finally
{
if (fxmlStream != null)
{
fxmlStream.close();
}
}
}
}
You should set a ControllerFactory on the FXMLLoader so that you are in charge of creating the controller instance.