JavaFX progress bar component activated from Spring Listener Method - spring

I have three layer(gui->services->dao) desktop application (JavaFX+Spring 4).
Some of my methods realized in my services layer (without fx dependency) have long time computing. I'd like to report their progress in gui.
But there is a problem. My service layer hasn't got direct access to gui, make a log of IO operations and returns only summarize of it. GUI layer, doesn't know about progress of intermediate operations.
I try to make a generic progress component. It could be realized as a Spring Bean with listener method located id GUI layer. My services can publish ApplicationEvent, which can be received via progress component in gui. When it gets the event, should activate progress bar and report next events.
In init methods I have started ProgressTask, and bind progressProperty with my gui progress bar control.
Call method of my ProgressTask runs while actual counter is less that max number of steps.
There is my component:
#Component
#Controller
public class ProgressBarComponent {
#FXML
private ProgressBar progressBar;
private ProgressTask actTask;
#FXML
public void initialize(){
}
private void initProgressBar(int aMax){
actTask=new ProgressTask();
actTask.initState(aMax);
progressBar.progressProperty().bind(actTask.progressProperty());
new Thread(actTask).start();
}
#EventListener
public void handleProgressEvent(ProgressEvent event) {
if(event.getGetActStep()==0){
initProgressBar(event.getMaxStep());
}
actTask.changeState(event.getGetActStep());
}
class ProgressTask extends Task<Object>{
private int max=100;
private int act=0;
private volatile boolean cancelled;
public void initState(int aMax){
this.max=aMax;
}
public void changeState(int newVal){
act=newVal;
updateProgress(act, max);
updateMessage("progress: " + act);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new IllegalArgumentException(e);
}
}
#Override
protected Object call() throws Exception {
while(act<max){
}
return null;
}
}
}
But it doesn't work.
Progress bar control is activated but in one step and at once it has a 100 percent value.
I have found a lot of examples Progress Bar FX control, but all of them assume that main logic is computed inside call() method of ProgressTask. In my case it isn't possible.
It is possible to make a ProgressBar Component, which is activated from listener method ? If yes, what code should be in ProgressTask.call() method ?

The point of a Task is to define some functionality that should be run on a background thread. So if this functionality is already implemented elsewhere, there is no need for a task.
Assuming the service is being invoked on a background thread (i.e. not on the FX Application Thread), you should just be able to do:
#Component
#Controller
public class ProgressBarComponent {
private int max ;
#FXML
private ProgressBar progressBar;
#FXML
public void initialize(){
}
private void initProgressBar(int aMax){
this.max = aMax ;
}
#EventListener
public void handleProgressEvent(ProgressEvent event) {
if(event.getGetActStep()==0){
Platform.runLater(() -> initProgressBar(event.getMaxStep()));
}
Platform.runLater(() ->
progressBar.setProgress(1.0 * event.getGetActStep() / max));
}
}
Here's a complete SSCCE:
ProgressViewer.java:
package springevent;
import org.springframework.context.event.EventListener;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ProgressViewer {
private Stage stage = new Stage();
private Scene scene ;
private ProgressBar progressBar ;
public ProgressViewer() {
progressBar = new ProgressBar();
StackPane root = new StackPane(progressBar);
root.setPadding(new Insets(20));
scene = new Scene(root);
stage.setScene(scene);
}
#EventListener
public void progressUpdate(ProgressEvent event) {
if (event.getCurrent() < event.getMax()) {
Platform.runLater(stage::show);
} else {
Platform.runLater(stage::hide);
}
final double progress = 1.0 * event.getCurrent() / event.getMax() ;
Platform.runLater(() -> progressBar.setProgress(progress));
}
}
Service.java:
package springevent;
import javax.inject.Inject;
import org.springframework.context.ApplicationContext;
public class Service {
#Inject
private ApplicationContext context ;
public void runService() {
context.publishEvent(new ProgressEvent(0, 100));
try {
for (int i = 1 ; i <= 100; i++) {
Thread.sleep(200);
context.publishEvent(new ProgressEvent(i, 100));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
ProgressEvent.java
package springevent;
public class ProgressEvent {
private final int current ;
private final int max ;
public ProgressEvent(int current, int max) {
this.current = current ;
this.max = max ;
}
public int getCurrent() {
return current ;
}
public int getMax() {
return max ;
}
}
MainController.java
package springevent;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import org.springframework.context.event.EventListener;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
public class MainController {
#Inject
private Executor exec ;
#Inject
private Service service ;
#FXML
private Button startButton ;
public void startProcess() {
exec.execute(service::runService);
}
#EventListener
public void progressUpdate(ProgressEvent event) {
startButton.setDisable(event.getCurrent() < event.getMax());
}
}
Main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<StackPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="springevent.MainController">
<padding>
<Insets top="20" bottom="20" left="20" right="20"/>
</padding>
<Button fx:id="startButton" text="Start process" onAction="#startProcess" />
</StackPane>
Main.java
package springevent;
import java.io.IOException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private AbstractApplicationContext context ;
#Override
public void start(Stage primaryStage) throws IOException {
context = new AnnotationConfigApplicationContext(AppConfig.class);
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
loader.setControllerFactory(context::getBean);
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
and AppConfig.java:
package springevent;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AppConfig {
#Bean
public Service service() {
return new Service();
}
#Bean
public MainController mainController() {
return new MainController();
}
#Bean
public ProgressViewer progressViewer() {
return new ProgressViewer();
}
#Bean
public Executor exec() {
return Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
}
}

Related

How to Capture ApplicatonEvent in Spring boot integration test?

The issue is that Application Event is not being captured in Spring boot test While it works fine for files listening to event in app project.
I want to capture an ApplicationEvent in Spring boot test(don't want to do Unit testing). My goal is to capture this application event and then perform few tasks in my test to verify the end-to-end functionality. Since, the event is not being captured in test case so I am not able to write integration tests.
Please let me know what is wrong with the code.
Thanks All.
package com.example.demo;
import org.springframework.context.ApplicationEvent;
public class CacheRefreshEvent extends ApplicationEvent {
private String message;
private static final long serialVersionUID = 1L;
public CacheRefreshEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
package com.example.demo;
import org.springframework.context.ApplicationEvent;
public class CacheRefreshCompleteEvent extends ApplicationEvent {
private String message;
private static final long serialVersionUID = 1L;
public CacheRefreshCompleteEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
package com.example.demo;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
#Component
public class CaptureCacheRefreshCompleteEvent implements ApplicationListener<CacheRefreshCompleteEvent> {
private ApplicationEventPublisher applicationEventPublisher;
void applicationEvent() throws InterruptedException {
applicationEventPublisher.publishEvent(new CacheRefreshEvent(this, "event triggered from SolrUtilitiesTest()"));
Thread.sleep(5000);
System.out.println("Finished execution of test.");
}
public void onApplicationEvent(CacheRefreshCompleteEvent cs) {
System.out.println("gotcha in CaptureCachedRefreshCompleteEvent");
}
public void setApplicationEventPublisher(ApplicationEventPublisher arg0) {
this.applicationEventPublisher = arg0;
}
}
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#DirtiesContext
#SpringBootTest
class DemoApplicationTests implements ApplicationEventPublisherAware, ApplicationListener<CacheRefreshCompleteEvent> {
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#Test
void applicationEvent() throws InterruptedException {
applicationEventPublisher.publishEvent(new CacheRefreshEvent(this, "event triggered from Springboot test"));
for(int i=0; i< 20; i ++) {
Thread.sleep(1000);
}
System.out.println("Finished execution of test.");
}
public void onApplicationEvent(CacheRefreshCompleteEvent cs) {
System.out.println("gotcha");
}
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher arg0) {
this.applicationEventPublisher = arg0;
}
}
One way could be to create a very simple listener with #TestComponent inside your test and autowire it as a #MockBean.
Proof of concept (tested with Spring Boot 2.2 and 2.1):
#SpringBootTest
public class PublishTest {
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#MockBean
private Consumer consumer;
#Test
public void test() {
applicationEventPublisher.publishEvent(new TestEvent(this));
// events are synchronous by default
verify(consumer).consumeEvent(any(TestEvent.class));
}
#TestComponent
private static class Consumer {
#EventListener
public void consumeEvent(TestEvent testEvent) {
}
}
private static class TestEvent extends ApplicationEvent {
public TestEvent(Object source) {
super(source);
}
}
}

how to pass the parameters to Websocket endpoint hander

When I create jetty websocket, I register my endpoint handler like this:
public class MyWebSocketEndpoint extends WebSocketServlet {
#Override
public void configure(WebSocketServletFactory webSocketServletFactory) {
webSocketServletFactory.register(MyEndpointHandler.class);
}
}
for MyEndpoingHandler class, I can't define a constructor with some parameters, or it will got runtime exception. How can I pass some parameters when create the MyEndpointHandler instance?
Use a WebSocketCreator.
When you call WebSocketServletFactory.register(MyEndpoingHandler.class) all that's happening internally is the equivalent of ...
#Override
public void register(Class<?> websocketPojo)
{
this.setCreator(new SingleEndpointCreator(websocketPojo));
}
Complete example on WebSocketCreator:
package websocket;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
public class DemoWebSocketCreator
{
public static class MyWebSocketServlet extends WebSocketServlet
{
#Override
public void configure(WebSocketServletFactory wsFactory)
{
wsFactory.setCreator(new MyWebSocketCreator());
}
}
public static class MyWebSocketCreator implements WebSocketCreator
{
private AtomicInteger idGen = new AtomicInteger(0);
#Override
public Object createWebSocket(ServletUpgradeRequest servletUpgradeRequest, ServletUpgradeResponse servletUpgradeResponse)
{
String id = "ws" + idGen.incrementAndGet();
return new MyWebSocket(id);
}
}
#WebSocket
public static class MyWebSocket
{
private final String id;
public MyWebSocket(String id)
{
this.id = id;
}
#OnWebSocketMessage
public void onMessage(Session session, String msg)
{
try
{
session.getRemote().sendString("Hello, my id is [" + id + "]: You said: " + msg);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
context.addServlet(MyWebSocketServlet.class, "/ws/");
// always last, and on default pathspec
context.addServlet(DefaultServlet.class, "");
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
server.start();
server.join();
}
}

How to inject a typed map of beans based on a typesafe qualifier in Spring?

See the example below, I'm trying to get a Map of my TypedService beans but I would prefer if the keys were the Type enum values specified in the TypeSafeQualifier instead of the unsafe String "serviceName".
package org.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Map;
import static org.test.Application.Type.ONE;
import static org.test.Application.Type.TWO;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#SpringBootApplication
public class Application {
#Autowired
Map<String, TypedService> works;
#Autowired
Map<Type, TypedService> fails;
public static void main(String [] args) {
SpringApplication.run(Application.class, args);
}
public enum Type {
ONE,
TWO
}
#Target({TYPE, METHOD, FIELD, CONSTRUCTOR})
#Retention(RUNTIME)
#Qualifier
public #interface TypeSafeQualifier {
Type value();
}
public interface TypedService {
void startSignup();
void activate();
}
#Service
#TypeSafeQualifier(ONE)
public class TypeOneService implements TypedService {
#Override
public void startSignup() {
}
#Override
public void activate() {
}
}
#Service
#TypeSafeQualifier(TWO)
public class TypeTwoService implements TypedService {
#Override
public void startSignup() {
}
#Override
public void activate() {
}
}
}
SpringBoot version: springBootVersion=1.5.3.RELEASE
Spring offers a special approach to handle this type of injection: AutowireCandidateResolver.
In your case the code might be:
package org.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.LinkedHashMap;
import java.util.Map;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#SpringBootApplication
public class Application {
#Autowired
Map<String, TypedService> works;
#Autowired
Map<Type, TypedService> fails;
#PostConstruct
private void init() {
System.out.println(fails);
}
public static void main(String[] args) {
final SpringApplication application = new SpringApplication(Application.class);
application.addInitializers(context -> {
context.addBeanFactoryPostProcessor(beanFactory -> {
final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
dlbf.setAutowireCandidateResolver(new MyAutowireCandidateResolver(dlbf));
});
});
application.run(args);
}
#QualifierValue(TypeSafeQualifier.class)
public enum Type {
ONE,
TWO
}
#Target({TYPE, METHOD, FIELD, CONSTRUCTOR})
#Retention(RUNTIME)
#Qualifier
public #interface TypeSafeQualifier {
Type value();
}
public interface TypedService {
void startSignup();
void activate();
}
#Service
#TypeSafeQualifier(Type.ONE)
public class TypeOneService implements TypedService {
#Override
public void startSignup() {
}
#Override
public void activate() {
}
}
#Target({TYPE})
#Retention(RUNTIME)
public #interface QualifierValue {
Class<? extends Annotation> value();
}
#Service
#TypeSafeQualifier(Type.TWO)
public class TypeTwoService implements TypedService {
#Override
public void startSignup() {
}
#Override
public void activate() {
}
}
private static class MyAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver {
private final DefaultListableBeanFactory beanFactory;
private MyAutowireCandidateResolver(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
#Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
final Object result = super.getSuggestedValue(descriptor);
if (result != null) {
return result;
}
if (descriptor.getDependencyType() != Map.class) {
return null;
}
final ResolvableType dependencyGenericType = descriptor.getResolvableType().asMap();
final ResolvableType[] typeParams = dependencyGenericType.getGenerics();
final QualifierValue qualifierValue = typeParams[0].getRawClass().getAnnotation(QualifierValue.class);
if (qualifierValue == null) {
return null;
}
final String[] candidateBeanNames = beanFactory.getBeanNamesForType(typeParams[1]);
final LinkedHashMap<Object, Object> injectedMap = new LinkedHashMap<>(candidateBeanNames.length);
for (final String candidateBeanName : candidateBeanNames) {
final Annotation annotation = beanFactory.findAnnotationOnBean(candidateBeanName, qualifierValue.value());
if (annotation == null) {
continue;
}
final Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation, false);
final Object value = annotationAttributes.get("value");
if (value == null || value.getClass() != typeParams[0].getRawClass()) {
continue;
}
injectedMap.put(value, beanFactory.getBean(candidateBeanName));
}
return injectedMap;
}
}
}
First of all, we add TypeQualifierValue annotation to make Spring know about a qualifier with values of the given type.
The second is to customize the SpringApplication in the main method: we use BeanFactoryPostProcessor to set a custom AutowireCandidateResolver.
And the final step: we write MyAutowireCandidateResolver extending ContextAnnotationAutowireCandidateResolver (delegation instead of inheritance is applicable to, it's even a little bit better since one day Spring can migrate to `YetAnotherAutowireCandidateResolver' by default).
The crucial part here is the overridden getSuggestedValue method: here we can customize the injection logic considering the generic types of the dependency (field, method parameter) and by applying some getBean...-like methods from the BeanFactory with some magic of Spring AnnotationUtils class.

control on a java-fx Button by 'Advice' class on aop spring framework

im trying to acsess an application button on javafx program by extenal 'Aspect' class using the spring aop .
This is the aspect class:
public class AspectControl {
private List<String> UserClicks;
private String[] expectedClick={"yellow","greean","red","blue","blue","blue","grean"};
public void beforeClickYellow(){
UserClicks.add("yellow");
}
public void afterClickYellow(){
Thread t=new Thread();
Main.yellowBtn.disableProperty();
}
}
this is the configuration xml :
<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop = "http://www.springframework.org/schema/aop"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:aspectj-autoproxy/>
<aop:config>
<aop:aspect id ="aspectcontrol" ref = "control">
<aop:pointcut id ="YellowPointcut"
expression = "execution(*
application.ButtonsNotify.yellowNotify(..))"/>
<aop:before pointcut-ref ="YellowPointcut" method="beforeClickYellow"/>
<aop:after pointcut-ref ="YellowPointcut" method ="afterClickYellow"/>
</aop:aspect>
application class:
package application;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;
public class Main extends Application {
private static final int SCENE_WIDTH = 800;
private static final int SCENE_HEIGHT = 500;
private Group GroupButtns;
private final int TOTAL_CLICKS = 10;
private int numOfClick = 0;
public static Button yellowBtn;
#Override
public void start(final Stage stage) {
stage.initStyle(StageStyle.TRANSPARENT);
stage.sizeToScene();
stage.setScene(new AppScene());
//close application
final Button close = new Button("X");
close.setFont(Font.font("Arial", FontWeight.BOLD, 20));
close.setStyle("-fx-background-color:transparent;-fx-text-fill:red;");
close.setOpacity(0);
close.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
Platform.exit();
System.exit(0);
}
});
close.setTranslateY(stage.getScene().getHeight() - 20);
stage.getScene().setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
close.setOpacity(1);
}
});
stage.getScene().setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
close.setOpacity(0);
}
});
// close application
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
Platform.exit();
System.exit(0);
}
});
yellowBtn = new Button();
SetButnView(yellowBtn, "yellow");
yellowBtn.setTranslateY(stage.getScene().getHeight() - 250);
yellowBtn.setTranslateX(stage.getScene().getX() - 200);
yellowBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
numOfClick += 1;
ApplicationContext context = new ClassPathXmlApplicationContext("BeanConfig.xml");
ButtonsNotify notify = (ButtonsNotify) context.getBean("btnNotify");
notify.yellowNotify(numOfClick);
}
});
Button blueBtn = new Button();
SetButnView(blueBtn, "blue");
blueBtn.setTranslateY(stage.getScene().getHeight() - 250);
blueBtn.setTranslateX(stage.getScene().getX() - 10);
Button redbtn = new Button();
SetButnView(redbtn, "red");
redbtn.setTranslateY(stage.getScene().getHeight() - 420);
redbtn.setTranslateX(stage.getScene().getX() - 10);
Button greenbtn = new Button();
SetButnView(greenbtn, "green");
greenbtn.setTranslateY(stage.getScene().getHeight() - 420);
greenbtn.setTranslateX(stage.getScene().getX() - 200);
GroupButtns.getChildren().add(close);
GroupButtns.getChildren().add(yellowBtn);
GroupButtns.getChildren().add(blueBtn);
GroupButtns.getChildren().add(greenbtn);
GroupButtns.getChildren().add(redbtn);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private class AppScene extends Scene {
public AppScene() {
super(GroupButtns = new Group(), SCENE_WIDTH, SCENE_HEIGHT, Color.TRANSPARENT);
GroupButtns.setClip(new Ellipse(0, SCENE_HEIGHT / 2, SCENE_WIDTH / 3, SCENE_HEIGHT / 2)); //Scene shape and size
Rectangle background = new Rectangle(-SCENE_WIDTH / 2, 0, SCENE_WIDTH, SCENE_HEIGHT);
background.setFill(new LinearGradient(0, 0, 0, SCENE_HEIGHT, false, CycleMethod.NO_CYCLE, new Stop(0, Color.GRAY), new Stop(0.3, Color.GRAY),
new Stop(1., new Color(1, 1, 1, 0)))); //background color
GroupButtns.getChildren().add(background);
GroupButtns.getTransforms().addAll(new Translate(SCENE_WIDTH / 2, SCENE_HEIGHT), new Rotate(180));
}
}
public void SetButnView(Button btn, String color) {
if (color.equals("yellow")) {
Image src = new Image(getClass().getResourceAsStream("yellow1.png"));
btn.setGraphic(new ImageView(src));
}
if (color.equals("blue")) {
Image src = new Image(getClass().getResourceAsStream("blue1.png"));
btn.setGraphic(new ImageView(src));
}
if (color.equals("red")) {
Image src = new Image(getClass().getResourceAsStream("red1.png"));
btn.setGraphic(new ImageView(src));
}
if (color.equals("green")) {
Image src = new Image(getClass().getResourceAsStream("green1.png"));
btn.setGraphic(new ImageView(src));
}
}
}
And this is the poincut class which I call every time I press a btn on the application class:
package application;
public class ButtonsNotify {
public ButtonsNotify(){
}
public void yellowNotify(int clickNumber){
System.out.println("yellow");
}
public void RedNotify(int clickNumber){
System.out.println("red");
}
public void BlueNotify(int clickNumber){
System.out.println("blue");
}
public void GreenNotify(int clickNumber){
System.out.println("green");
}
}
How can I control the button's visability by the 'AspectControl' class??

How to display form validation constraints message on GWT Material inputs using Editor and Validation Framework from Presenter's class?

I use:
Gwt-platform
Gwt Validation
Gwt UiEditor framework
Gwt Material inputs
After constraints validation I would like to display error messages. This is supported by gwt material inputs, like MaterialTextBox using method:  materialTextBox.setError("Please provide your name");
The problem is that it can be only executed from view class:
public class LoginView extends ViewWithUiHandlers<LoginUiHandlers> implements LoginPresenter.MyView {
interface Binder extends UiBinder<Widget, LoginView> {}
/** The driver to link the proxy bean with the view. */
public interface EditorDriver extends SimpleBeanEditorDriver<LoginModel, LoginView> { }
#UiField MaterialTextBox email;
#UiField MaterialTextBox password;
#UiField MaterialButton loginButton;
#UiField MaterialCheckBox keepMeLoggedInCheckbox;
#Inject
LoginView(Binder uiBinder) {
initWidget(uiBinder.createAndBindUi(this));
addClickHandlerToLoginButton();
}
//#UiHandler("loginButton")
private void onLoginButtonClick(ClickEvent e){
getUiHandlers().onLoginButtonClick();
}
private void addClickHandlerToLoginButton() {
loginButton.addClickHandler(new ClickHandler() {
#Override public void onClick(ClickEvent event) {
onLoginButtonClick(event);
}
});
}
#Override
public SimpleBeanEditorDriver<LoginModel, ?> createEditorDriver() {
EditorDriver driver = GWT.create(EditorDriver.class);
driver.initialize(this);
return driver;
}
public void test() {}
}
But I do all my editor/validation action in Presenter, and I do not have there any View connection I could use:
public class LoginPresenter extends Presenter<LoginPresenter.MyView, LoginPresenter.MyProxy> implements LoginUiHandlers {
public interface MyView extends BeanEditView<LoginModel>, HasUiHandlers<LoginUiHandlers> {}
public static final Type<RevealContentHandler<?>> SLOT_Login = new Type<RevealContentHandler<?>>();
#ProxyStandard
#NameToken(NameTokens.login)
public interface MyProxy extends ProxyPlace<LoginPresenter> {}
// Editor
private SimpleBeanEditorDriver<LoginModel, ?> editorDriver;
private static final LoginService service = GWT.create(LoginService.class);
private LoginModel model = new LoginModel("","");
#Override
public void onLoginButtonClick() {
if (editorDriver.isDirty()) {
model = editorDriver.flush();
validateModel();
if (editorDriver.hasErrors()) {
MaterialToast.fireToast("Errors occur");
StringBuilder errorBuilder = new StringBuilder();
for (EditorError error : editorDriver.getErrors()) {
errorBuilder.append(error.getMessage() + "\n");
}
MaterialToast.fireToast(errorBuilder.toString());
} else {
service.login(
model, new MethodCallback<Integer>() {
#Override
public void onSuccess(Method method, Integer response) {
MaterialToast.fireToast("Succefully set info. status code: " + response);
}
#Override
public void onFailure(Method method, Throwable exception) {
MaterialToast.fireToast("Error setting");
}
});
}
} else {
MaterialToast.fireToast("Data has not changed");
}
}
private void validateModel() {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<LoginModel>> violations = validator.validate(model);
if (violations.size() > 0) {
editorDriver.setConstraintViolations(new ArrayList<ConstraintViolation<?>>(violations));
}
}
#Inject
LoginPresenter(EventBus eventBus,MyView view, MyProxy proxy) {
super(eventBus, view, proxy, RevealType.Root);
getView().setUiHandlers(this);
editorDriver = getView().createEditorDriver();
editorDriver.edit(model);
}
}
I think that I should add an interface where I will declare methods to access inputs in view. And do implementation of that. But I don't know how. Please help me.
I've make it working. But really I am not quite sure what did happened that it start to work. So I will just share my working code:
Presenter
package pl.korbeldaniel.cms.client.login;
import java.util.ArrayList;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.fusesource.restygwt.client.Method;
import org.fusesource.restygwt.client.MethodCallback;
import gwt.material.design.client.ui.MaterialToast;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.editor.client.SimpleBeanEditorDriver;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.inject.Inject;
import com.google.web.bindery.event.shared.EventBus;
import com.gwtplatform.mvp.client.Presenter;
import com.gwtplatform.mvp.client.annotations.ProxyStandard;
import com.gwtplatform.mvp.client.proxy.ProxyPlace;
import com.gwtplatform.mvp.client.annotations.NameToken;
import com.gwtplatform.mvp.client.proxy.RevealContentHandler;
import com.gwtplatform.mvp.client.HasUiHandlers;
import pl.korbeldaniel.cms.client.editor.BeanEditView;
import pl.korbeldaniel.cms.client.model.LoginModel;
import pl.korbeldaniel.cms.client.place.NameTokens;
import pl.korbeldaniel.cms.client.service.LoginService;
public class LoginPresenter extends Presenter<LoginPresenter.MyView, LoginPresenter.MyProxy> implements LoginUiHandlers {
#ProxyStandard
#NameToken(NameTokens.login)
public interface MyProxy extends ProxyPlace<LoginPresenter> {}
public interface MyView extends BeanEditView<LoginModel>, HasUiHandlers<LoginUiHandlers> {}
public static final Type<RevealContentHandler<?>> SLOT_Login = new Type<RevealContentHandler<?>>();
// Editor
private SimpleBeanEditorDriver<LoginModel, ?> editorDriver;
private static final LoginService service = GWT.create(LoginService.class);
private LoginModel model = new LoginModel();
#Override
public void onLoginButtonClick() {
if (editorDriver.isDirty()) {
model = editorDriver.flush();
validateModel();
if (editorDriver.hasErrors()) {
MaterialToast.fireToast("Errors occur");
} else {
service.login(
model, new MethodCallback<Integer>() {
#Override
public void onSuccess(Method method, Integer response) {
MaterialToast.fireToast("Succefully set info. status code: " + response);
}
#Override
public void onFailure(Method method, Throwable exception) {
MaterialToast.fireToast("Error setting");
}
});
}
} else {
MaterialToast.fireToast("Data has not changed");
}
}
private void validateModel() {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<LoginModel>> violations = validator.validate(model);
GWT.log(String.valueOf(violations.size()));
if (violations.size() > 0) {
editorDriver.setConstraintViolations(new ArrayList<ConstraintViolation<?>>(violations));
}
model.validate();
}
#Inject
LoginPresenter(EventBus eventBus,MyView view, MyProxy proxy) {
super(eventBus, view, proxy, RevealType.Root);
getView().setUiHandlers(this);
editorDriver = getView().createEditorDriver();
editorDriver.edit(model);
}
public enum EditorMode {
VIEW, EDIT, CREATE
};
}
Ui Handler
package pl.korbeldaniel.cms.client.login;
import com.gwtplatform.mvp.client.UiHandlers;
interface LoginUiHandlers extends UiHandlers {
void onLoginButtonClick();
}
ValidatorFactory
package pl.korbeldaniel.cms.client.login;
import javax.validation.Validator;
import pl.korbeldaniel.cms.client.model.LoginModel;
import com.google.gwt.core.client.GWT;
import com.google.gwt.validation.client.AbstractGwtValidatorFactory;
import com.google.gwt.validation.client.GwtValidation;
import com.google.gwt.validation.client.impl.AbstractGwtValidator;
public final class SampleValidatorFactory extends AbstractGwtValidatorFactory {
/**
* Validator marker for the Validation Sample project. Only the classes and
* groups listed in the {#link GwtValidation} annotation can be validated.
*/
#GwtValidation(LoginModel.class)
public interface GwtValidator extends Validator {
}
#Override
public AbstractGwtValidator createValidator() {
return GWT.create(GwtValidator.class);
}
}
View
package pl.korbeldaniel.cms.client.login;
import gwt.material.design.client.ui.MaterialButton;
import gwt.material.design.client.ui.MaterialCheckBox;
import gwt.material.design.client.ui.MaterialTextBox;
import javax.inject.Inject;
import pl.korbeldaniel.cms.client.model.LoginModel;
import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.SimpleBeanEditorDriver;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Widget;
import com.gwtplatform.mvp.client.ViewWithUiHandlers;
public class LoginView extends ViewWithUiHandlers<LoginUiHandlers> implements LoginPresenter.MyView {
interface Binder extends UiBinder<Widget, LoginView> {}
/** The driver to link the proxy bean with the view. */
public interface EditorDriver extends SimpleBeanEditorDriver<LoginModel, LoginView> { }
#UiField MaterialTextBox email;
#UiField MaterialTextBox password;
#UiField MaterialButton loginButton;
#UiField MaterialCheckBox keepMeLoggedInCheckbox;
#Inject
LoginView(Binder uiBinder) {
initWidget(uiBinder.createAndBindUi(this));
addClickHandlerToLoginButton();
}
//#UiHandler("loginButton")
private void onLoginButtonClick(ClickEvent e){
getUiHandlers().onLoginButtonClick();
}
private void addClickHandlerToLoginButton() {
loginButton.addClickHandler(new ClickHandler() {
#Override public void onClick(ClickEvent event) {
onLoginButtonClick(event);
}
});
}
#Override
public SimpleBeanEditorDriver<LoginModel, ?> createEditorDriver() {
EditorDriver driver = GWT.create(EditorDriver.class);
driver.initialize(this);
return driver;
}
}
View Binder
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:m="urn:import:gwt.material.design.client.ui"
xmlns:e="urn:import:pl.korbeldaniel.cms.client.login">
<m:MaterialRow ui:field="loginWidget">
<m:MaterialColumn grid="s12 m4 l4" offset="l4 m4" >
<m:MaterialTitle title="Login" description="Please provide your account credentials."/>
<m:MaterialPanel padding="5" shadow="1" addStyleNames="{style.panel}">
<m:MaterialPanel addStyleNames="{style.fieldPanel}">
<!-- <m:MaterialImage url="http://b.vimeocdn.com/ps/339/488/3394886_300.jpg" type="CIRCLE" addStyleNames="{style.imgProfile} z-depth-1"/> -->
<m:MaterialTextBox ui:field="email" type="EMAIL" placeholder="Email"/>
<m:MaterialTextBox ui:field="password" type="PASSWORD" placeholder="Password"/>
<m:MaterialRow addStyleNames="{style.rowAction}">
<m:MaterialColumn grid="s12 m12 l6">
<m:MaterialCheckBox ui:field="keepMeLoggedInCheckbox" text="Keep me logged in"/>
</m:MaterialColumn>
</m:MaterialRow>
<m:MaterialButton ui:field="loginButton" waves="LIGHT" text="Log In" width="100%"/>
</m:MaterialPanel>
</m:MaterialPanel>
</m:MaterialColumn>
</m:MaterialRow>
</ui:UiBinder>

Resources