Seam conversations from JavaFX

This post covers starting/stopping Seam conversations from JavaFX. See other posts in this series:

Calling Seam component from JavaFX
Invoking Hibernate Validator from JavaFX
Binding to server-side context variable from JavaFX
Using Expression Language (EL) in JavaFX to communicate with server

Server side

Seam component:

@Name ("wizard")
@Scope (ScopeType.CONVERSATION)
public class Wizard {
   @In Conversation conversation;

   public String info (){
	return "Id: "+conversation.getId() +", active: "+conversation.isLongRunning();
   }
   @Begin
   public void start (){
	// do something
   }
   @Conversational
   public String nextStep (){
	return "Id: "+conversation.getId() +", active: "+conversation.isLongRunning();
   }
   @End
   public void end (){
       // do something
   }
}

Above is a pretty simple Seam component with conversation scope. It has method to start the conversation (@Begin) and end the conversation (@End). It also has nextStep() annotated with @Conversation, that means that the method can only be invoked within a long running conversation.

Client side

Component interface:

public interface Wizard {
   public void end();
   public void start();
   public String nextStep ();
   public String info ();
}

JavaFX script:

CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
FXServiceFactory.URL = "http://localhost:8080/server-javafx/seam/resource/hessian/";
var wizardService = WizardServiceFactory.getWizardService();

def red: RadialGradient = RadialGradient{
    centerX: 8, centerY: 8, radius: 12, proportional: false
    stops: [
    Stop {offset: 0.0 color: Color.WHITE},
    Stop {offset: 1.0 color: Color.RED}
    ]
}
def green: RadialGradient = RadialGradient{
    centerX: 8, centerY: 8, radius: 12, proportional: false
    stops: [
    Stop {offset: 0.0 color: Color.WHITE},
    Stop {offset: 1.0 color: Color.GREEN}
    ]
}
var light = Circle {
    centerX: 10 centerY: 10 radius: 10 stroke: Color.BLACK
    fill: bind if (longRunning) green else red
}
var infoButton:Button = Button {
    text : "Info"
    style : "-fx-font-size: x-large"
    action: function () {
        var newInfo = wizardService.info();
        insert newInfo into info;
    }
}
var longRunningOnly:Button = Button {
    text : "Only if active"
    style : "-fx-font-size: x-large"
    action: function () {
        try {
            var newInfo = wizardService.nextStep();
            insert newInfo into info;
        }
        catch (exception: Exception) {
            Alert.inform("Not active conversation");
        }
    }
}
var longRunning = false;
var convButton:Button = Button {
    style : "-fx-font-size: x-large"
    text : bind
    if (longRunning) "Stop" else "Start"
    action: function () {
        if (longRunning==false){
            wizardService.start();
            longRunning = true;
            var newInfo = wizardService.info();
            insert newInfo into info;
        }
        else if (longRunning==true) {
            wizardService.end();
            longRunning = false;
            var newInfo = wizardService.info();
            insert newInfo into info;
        }
    }
}
var info = [wizardService.info()];
var list:VBox = VBox {
    content: {
        VBox {
            content :  bind for (item in info){
                Text {
                    content: item
                    style : "-fx-font-size: x-large"
                }
            }
        }
    }
}
Stage {
    title: "Application"
    width: 450 height: 400
    scene: Scene {
        fill: LinearGradient {
            endX: 0.0
            stops: [Stop { offset: 0.0 color: Color.LIGHTGRAY }
            Stop { offset: 1.0 color: Color.GRAY }]
        }
        content: [
        VBox {
            spacing : 4
            content: [
            HBox {
                spacing: 4
                content: [
                infoButton, convButton, light, longRunningOnly
                ]
            },
            list
            ]
        }
        ]
    }
};

Line 3: we get reference to WizardService and now can call all methods in the interface.
Lines 23-30: info button which returns the current conversation status
Lines 31-43: button that calls a method which should be called only when a long running conversation is active. If clicked outside of a long running conversation, an error dialog will be shown
Lines 45-63: button that starts/stops the conversation

Notice that nothing special needs to be done when using conversations, it is just a simple component method invocation.

No long running conversation:

Long running conversation:

Trying to invoke a method with @Conversation outside of a long running conversation:

That’s it.

Using Expression Language (EL) in JavaFX to communicate with server

This post shows how to use Expression Language (EL) from JavaFX to communicate with a server using Exadel Flamingo. See my previous posts on Enterprise JavaFX: calling Seam component from JavaFX, invoking Hibernate Validator from JavaFX, and binding to server-side context variable from JavaFX.

Seam component:

@Name ("helloAndTime")
@Scope(ScopeType.SESSION)
public class HelloAndTime {

   private Date lastAccessTime;

   public Date getCurrentTime() {
	return new Date();
   }
   public String sayHello(String name) {
	lastAccessTime = getCurrentTime();
	return "Hello " + name;
   }
   public Date getLastAccessTime() {
	return lastAccessTime;
   }
}

JavaFX script:

def expressionService: ExpressionService = {
   var returnValue: ExpressionService = null;
   try {
      CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
      FXServiceFactory.URL = "http://localhost:8080/server-javafx/seam/resource/hessian/";
      returnValue = FXServiceFactory.getService(ExpressionService.class,
                        ExpressionService.NAME) as ExpressionService;
    } catch (exception: Exception) {
        Alert.inform(exception.getMessage());
    }
    returnValue
}

var userName: String = "Enter name";
var serverAnswer: String;
var serverTime: Date;
var lastAccessTime: String;
var poller = Timeline {
   repeatCount: Timeline.INDEFINITE;
   keyFrames: [
   KeyFrame {
      time: 1s;
      action: function () {
          serverTime = expressionService.getValue("helloAndTime.currentTime")
                                   as Date;
      }
  }
 ]
}
poller.playFromStart();

var stage: Stage;
stage = Stage {
    title: "EL-Expression Sample"
    width:  420
    height: 400
    scene: Scene {
        fill: LinearGradient {
            endX: 0.0
            stops: [Stop { offset: 0.0 color: Color.LIGHTGRAY }
            Stop { offset: 1.0 color: Color.GRAY }]
        }
        stylesheets: [ "{__DIR__}style.css" ]
        content: VBox {
            translateX: 5
            translateY: 5
            spacing: 10
            content: [
               TextBox {
                  text: bind userName with inverse
               },
               Button  {
                  text: "Say Hello"
                  style : "-fx-font-size: large"
                  action: function() {
                  var classes: Class[] = String.class;
                  serverAnswer = expressionService.
                      invokeMethod("helloAndTime.sayHello", String.class, classes, userName)
                         as String;
                  lastAccessTime = String.valueOf(expressionService.
                      getValue("helloAndTime.lastAccessTime") as Date);
                  }
               },
               Text {
                  content: bind "Server says: {serverAnswer}"
                  styleClass: "text"
               },
               Text {
                  content: bind "Last access time: {lastAccessTime}"
                  styleClass: "text"
               },
               Text {
                  content: bind "Current server time: {serverTime}"
                  styleClass: "text"
               }
            ]
        }
    }
    onClose: function() {
        poller.stop();
        FX.exit();
    }
}

Line 6: we get a reference to ExpressionService instance. This class gives us all the functionality to work with EL from JavaFX.
Line 24: we bind to currentTime property in helloAndTime component (bean). The syntax we use is helloAndTime.currentTime. If you are have are familiar with JSF then you would expect this format: #{helloAndTime.currentTime}. Due to JavaFX syntanx we can’t use {} brackets. I think helloAndTime.currentTime is just as simple.
Line 57: we invoked a method
Line 60: we bind to now another property: helloAndTime.lastAccessTime

When using EL, we don’t have to create an interface for the remote component as we did in calling Seam component from JavaFX. On the other hand, the compiler can’t check that such method actually exits. When using an interface, the compiler will complain if we try to invoke a method that’s not in the interface.

Result:

That’s it.

Calling Seam component from JavaFX

We very close to moving Flamingo and Fiji to exadel.org. It will probably (finally) happen next week. I just wanted to show how simple it is to call a Seam component from JavaFX using Flamingo:

Seam component (server):

@Name ("currentTime")
public class CurrentTime {
   public Date currentTime () {
	return (new java.util.Date());
   }
}

Service interface (client):

public interface CurrentTime {
   public Date currentTime ();
}

JavaFX script (client):

FXServiceFactory.URL = "http://localhost:8080/server-javafx/seam/resource/hessian/";
var ct = (FXServiceFactory.getService(CurrentTime.class,"currentTime") as CurrentTime);
var now = ct.currentTime();
FX.println(now);

That’s it!

Seam component events

A very powerful feature in Seam that usually doesn’t get enough attention in my opinion is component-driven events. Seam component can raise or throw arbitrary event and any number of other Seam components can observe or listen to these events. This truly creates a loose coupled architecture. Components raise events and anyone who cares listens to the event. The components are not connected to each other in any way.

Let’s start with CourseManager component:

@Name("courseManager")
public class CourseManager {

   private String name; // setter and getter

   @Out(required=false)
   private String studentName;

   @Logger Log log;

   @RaiseEvent("courseDropped")
   public void drop() {
     studentName = name;
     log.info("course dropped");
   }
}

In component above when drop() method is invoked, courseDropped event will be raised via @RaiseEvent annotation. Once invocation is done, the component will outject studentName variable.

Although data can passed with the event, a better way is to use bijection in Seam. The name variable would most likely be submitted from a JSF page. Its value is assigned to studentName component variable and studentName is outjected after the method invokation. Anyone who is interested in this event can listen to it and also get the data associated with the event (via injection).
Continue reading

CITYTECH: enterprise application with JavaFX, Seam, and Exadel Flamingo

Great post by John Kraus from CITYTECH on how they used Exadel Flamingo to connect rich JavaFX UI to their existing Seam application. It’s good to see JavaFX slowly penetrating the enterprise. And with Flamingo, it becomes even simpler. Companies can leverage their existing services and easily connect them to JavaFX rich UI as CITYTECH did.

There are a number of interesting new features in Flamingo. There is now server-side push and off-line applications for JavaFX. There is also a connector for Google’s Android platform. I’m planning to blog about these technologies and more very soon. As for JavaFX tooling, we have been working on JavaFX plug-in for Eclipse. Try it out. The plug-in is going to be open source any day now.

Read blog post here