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
}
}
Related
I wrote an ApplicationListener that should check if the environment is prepared during context initialization. I'm having trouble testing the scenario since I'm adding the listener manually both in my configure() and main() methods.
ApplicationListener class:
public class EnvironmentPrepared implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
//code that checks if conditions are met
if (checkTrue) {
throw new RuntimeException();
}
}
}
Main class:
public class MyApp extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
setRegisterErrorPageFilter(false);
return application.listeners(new EnvironmentPrepared()).sources(MyApp.class);
}
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MyApp.class);
springApplication.addListeners(new EnvironmentPrepared());
springApplication.run(args);
}
}
The test I want to execute:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(loader = OverriddenProfilesTest.CustomLoader.class)
public class OverriddenProfilesTest {
public static class CustomLoader extends SpringBootContextLoader {
#Override
protected SpringApplication getSpringApplication() {
SpringApplication app = super.getSpringApplication();
app.addListeners(new EnvironmentPrepared());
return app;
}
}
/**
* Checks if spring can bootstrap everything
*/
#Test(expected = RuntimeException.class)
public void test() {
}
}
This would be the test I want. A RuntimeException is thrown but the exception happens during context initialization so the test doesn't even start.
Here is the solution I used. I removed the manual adding of the listener to the application and used spring.factories file instead.
Regarding the test, I first created a custom runner class:
public class SpringRunnerWithExpectedExceptionRule extends SpringJUnit4ClassRunner {
public SpringRunnerWithExpectedExceptionRule(Class<?> clazz) throws InitializationError {
super(clazz);
}
#Override
protected Statement methodBlock(FrameworkMethod frameworkMethod) {
List<ExpectedException> testRules = getTestClass().getAnnotatedFieldValues(null, ExpectedExceptionClassRule.class, ExpectedException.class);
Statement result = super.methodBlock(frameworkMethod);
for (TestRule item : testRules) {
result = item.apply(result, getDescription());
}
return result;
}}
Then I create following annotation:
#Retention(RUNTIME)
#Target({ FIELD })
public #interface ExpectedExceptionClassRule {
}
And finally, I was able to run the test with my runner:
#RunWith(SpringRunnerWithExpectedExceptionRule.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class OverriddenProfilesTest {
#ExpectedExceptionClassRule
public static ExpectedException expectedException = ExpectedException.none();
#BeforeClass
public static void before() {
expectedException.expectCause(runtimeExceptionMethod());
}
#Test
public void testThatShouldThrowExceptionWhileSettingContext {
}
static Matcher<Throwable> runtimeExceptionMethod() {
return new IsRuntimeException();
}
static class IsRuntimeException extends TypeSafeMatcher<Throwable> {
//do stuff
}
More on the solution can be found here.
To start my application, I'm avoiding the "implements CommandLineRunner" process to do the setup but I am facing a problem in this line
fxmlLoader.setControllerFactory(springContext::getBean);
where fxmlLoader is an instance of FxmlLoader and springContext ia an instance of ConfigurableApplicationContext. I am facing this error,
"The method setControllerFactory(Callback<Class<?>,Object>) in the
type FXMLLoader is not applicable for the arguments
(springContext::getBean)".
Can anyone help me with the exact syntax? My imported package reports an error as
"The type org.springframework.beans.factory.annotation.Autowire
cannot be resolved. It is indirectly referenced from required .class
files" .
ok, ive understood that this would require a little code, but ive found already built project with a solution similar to what ive proposed - heres example https://github.com/ruslanys/sample-spring-boot-javafx
you just to tie javafx to spring with context.getAutowireCapableBeanFactory().autowireBean(this);
in AbstractJavaFxApplicationSupport.java file
code will look like this
public abstract class AbstractJavaFxApplicationSupport extends Application {
private static String[] savedArgs;
protected ConfigurableApplicationContext context;
#Override
public void init() throws Exception {
context = SpringApplication.run(getClass(), savedArgs);
context.getAutowireCapableBeanFactory().autowireBean(this);
}
#Override
public void stop() throws Exception {
super.stop();
context.close();
}
protected static void launchApp(Class<? extends AbstractJavaFxApplicationSupport> appClass, String[] args) {
AbstractJavaFxApplicationSupport.savedArgs = args;
Application.launch(appClass, args);
}
}
and tie all you view like bean
#Configuration
public class ControllersConfiguration {
#Bean(name = "mainView")
public ViewHolder getMainView() throws IOException {
return loadView("fxml/main.fxml");
}
#Bean
public MainController getMainController() throws IOException {
return (MainController) getMainView().getController();
}
protected ViewHolder loadView(String url) throws IOException {
InputStream fxmlStream = null;
try {
fxmlStream = getClass().getClassLoader().getResourceAsStream(url);
FXMLLoader loader = new FXMLLoader();
loader.load(fxmlStream);
return new ViewHolder(loader.getRoot(), loader.getController());
} finally {
if (fxmlStream != null) {
fxmlStream.close();
}
}
}
public class ViewHolder {
private Parent view;
private Object controller;
public ViewHolder(Parent view, Object controller) {
this.view = view;
this.controller = controller;
}
public Parent getView() {
return view;
}
public void setView(Parent view) {
this.view = view;
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
}
}
then in controller you may enjoy spring magic and javafx magic together
public class MainController {
#Autowired private ContactService contactService;
#FXML private TableView<Contact> table;
#FXML private TextField txtName;
#FXML private TextField txtPhone;
#FXML private TextField txtEmail;}
and just start your app like this
#SpringBootApplication
public class Application extends AbstractJavaFxApplicationSupport {
#Value("${ui.title:JavaFX приложение}")//
private String windowTitle;
#Qualifier("mainView")
#Autowired
private ControllersConfiguration.ViewHolder view;
#Override
public void start(Stage stage) throws Exception {
stage.setTitle(windowTitle);
stage.setScene(new Scene(view.getView()));
stage.setResizable(true);
stage.centerOnScreen();
stage.show();
}
public static void main(String[] args) {
launchApp(Application.class, args);
}}
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 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
}
});
I am using AsyncTask and want to use getApplication() to work with class Application.
But gets error on getApplication() cannot find symbol.
my code:
public class splash extends AsyncTask {
protected config app;
public splash(Context context) {
super();
app = ((config) getApplication());
this.context=context;
}
and the class that I want to use:
public class config extends Application
{
public Boolean con=true;
public int status=-1;
public String actNum;
public void onCreate()
{
super.onCreate();
}
}
If you want to get the Application instance, you can initialize a member in onCreate() with it and have it returned by a class method:
public class ApplicationConfig extends Application {
private static ApplicationConfig instance;
public void onCreate() {
super.onCreate();
instance = this;
}
public static ApplicationConfig getConfig() {
return instance;
}
}
Then you can retrieve this instance everywhere via:
ApplicationConfig conf = ApplicationConfig.getConfig();