Java on the client… again.

Yakov Fain blogged that Java on the client has no chance succeeding with the current complicated installation process. Unfortunately I agree. But, I also think there is even a bigger and very closely related problem, launching Java client applications. I blogged about it here. Launching Java applications on the client could be very challenging.

With JavaFX 2.0 undergoing dramatic changes (JavaFX script is dropped), we are going to start looking early next year how to update Exadel JavaFX Plug-in for Eclipse and Exadel Flamingo to work with new JavaFX. With JavaFX script being dropped, applications will be developed using Java API. I get a feeling that a few years ago it was popular to use declarative languages to create user interfaces and now we are going back to using Java. Maybe GWT has something to do with that. Two things must happen for JavaFX or Java on the client to succeed: easy virtual machine install and easy deployment. Even with major, and I believe positive changes in JavaFX 2.0, without the two things I mentioned above, it’s going to lose the battle against plug-in free browser applications.

I still find one thing very interesting. With JavaFX 1.x out for about three years during which it didn’t produce any real applications and JavaFX 2.0 is still in development (and with somewhat unclear future), you still get JavaFX presented at various conferences. Just last month at Devoxx, JavaFX was covered by 4-5 sessions. The community is strong. At the same time, you never hear HTML5 vs Java client, it’s always against Flash. So again, client Java is kind of there but also not there.

JavaOne 2010: what happened with JavaFX (part 2)

This is part two of my JavaOne 2010 review and thoughts. To read part one, click here.

What happened with JavaFX?

As everyone knows by now, Oracle is making significant changes to JavaFX. Oracle is stopping any further development of JavaFX Script. Instead, it will develop Java APIs which will become a part of JavaFX 2.0 to be released in the second half of 2011. So, instead of using JavaFX Script to develop rich Java applications, you will use the new Java API. If you followed Twitter during JavaOne, many people called it the next Swing or Swing++ or something similar. No matter what you call it, you will be able to open your favorite Java IDE and start creating JavaFX applications.

Oracle is finally making significant changes to the platform which is very good. I blogged about the future of JavaFX in late July and said that Oracle should either start pushing JavaFX hard or just discontinue it (or let the community drive it). I said Oracle had about 6-12 months to do this. I doubt they listened to me, but Oracle is definitely going to push JavaFX, although with significant changes (dropping script) from the state it was in when I blogged. You can view the road map for JavaFX 2.0.
Continue reading “JavaOne 2010: what happened with JavaFX (part 2)”

Exadel Flamingo now supports CDI and Bean Validation for JavaFX

Exadel Flamingo now has support for CDI (JSR299) and Bean Validation (JSR303). You can try the features from a nightly build.

The following features are supported so far:

  • Calling CDI bean methods
  • Support for EL (Expression Language). Bind to values and invoke methods with EL in JavaFX
  • CDI conversations
  • Bean Validation (JSR303)

To download a nightly build, go to http://exadel.org/flamingo and click on Nightly builds on the right.

To get started with JavaFX and CDI/Bean Validation, you can look at these examples as reference. The client side code is the same, the only difference is instead of Seam components, you now use CDI beans on the server (and the appropriate annotations).

We are also planning to add CDI events. If there is anything else we should add, please let us know.

RichFaces workshop in Germany

I will be presenting and teaching a RichFaces workshop in Nuremberg, Germany during Herbstcampus conference, September 12 – September 15, 2010.

With the upcoming RichFaces 4 release, the workshop will cover RichFaces version 4. This is a great opportunity to learn standard JSF 2 Ajax features and how RichFaces 4 advanced features, tags, customization and richness add on top of JSF 2. RichFaces 4 is a major upgrade. A lot of core functionality is being rewritten to make RichFaces faster and better. Many components are being rewritten to make them simpler, perform faster and consistent across board.

Discount
Get 25% discount by using this URL. Alternatively, you can use this booking code: ceHioRcpiD.

RichFaces workshop (full day)
Sunday, September 12, 2010
More info >>

I also have two general sessions.

Ajax Applications with JSF and the two New RichFaces 4
Monday, September 13, 2010
More info >>

Enterprise Applications with JavaFX and Flamingo
Tuesday, September 14, 2010
More info >>

As a reminder, we are also doing a webinar on September 8, 2010:
Ajax Applications with JSF 2 and the New RichFaces 4
Register >>

New Exadel Flamingo 2.2.0 is now on exadel.org

After months of promising we have finally moved Exadel Flamingo to exadel.org and released version 2.2.0. exadel.org is our community site for hosting open source projects. Flamingo is a light weight framework for connecting rich web and mobile user interfaces to enterprise back end.


Click on image to enlarge

Flamingo connects Seam, Spring, and Java EE 6 (soon via CDI/JSR299) with the following user interfaces:

  • JavaFX
  • Flash
  • iPhone/Android
  • Swing
  • JavaME

Version 2.2.0 is updated with Hessian 4 and JavaFX 1.3 support.

Server-side components, methods or services can be easily invoked from the above listed clients with minimal code. Flamingo provides the following features:

  • CRUD tools
  • Server-side integration
    • Calling server components/beans
    • Context variable binding/updating
    • Conversation support
    • Validation (Hibernate Validator)
    • EL support (Expression Language)
  • Server-side push
  • Client push (off line applications)


Click image to enlarge

As you can see from the image above, no matter what client you are using, invoking a method in your enterprise applications looks very similar which allows for great reuse.

How to get started?

  • Download Flamingo from http://exadel.org/flamingo.
  • The following are good resources to get started or learn more about Flamingo.
  • Check out the recent Enterprise JavaFX and Seam series. This series shows most server integration features in separate post and easy to follow examples.
  • We want to hear your feedback, please use project Jira or the forum to report bugs or any features you would like to see.

Enterprise JavaFX with Seam and Flamingo – complete edition

I wrote a series of posts on how to connect JavaFX to a server running with Seam framework. Client-server communication is realized with the help of Flamingo framework. Here is the complete series:

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.

Binding to server-side context variable from JavaFX

In addition to calling methods and Hibernate Validator, it’s also very simple to bind to a context variable and update it from JavaFX. A context variable is any variable inside a scope on the server.

Server

Seam component:

@Name ("shop")
@Scope (ScopeType.SESSION)
public class Store {

   IceCream iceCream; // getter and setter

   @Create
   public void init (){
	iceCream = new IceCream();
	iceCream.setName("Best ice cream");
	iceCream.setFlavors(2);
   }
}

Model:

@Entity
@Name ("icecream")
public class IceCream implements java.io.Serializable{

   @Max(value=5, message="Sorry, you can't have more than {value} flavors")
   private Integer flavors;

   private String name;

   // getters and setters
}

Client

Model:

public class IceCream implements java.io.Serializable{
   private String name;
   private Integer flavors;
   // setters and getters
}

BindingManager interface:

public interface BindingManager {
   public String commit(String componentName, Object value);
   public Object getObject(String name);
}
public class IceCreamServiceFactory {
   public static BindingManager getBindingManager() {
	return (BindingManager) FXServiceFactory.getService(BindingManager.class,
		"com.exadel.flamingo.service.binding.bindingManager");
   }
   public static String commit (String componentName, Object value){
	return getBindingManager().commit(componentName, value);
   }
   public static IceCream getIceCream (String componentName){
	return (IceCream)getBindingManager().getObject(componentName);
   }
}

JavaFX script:

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

FX.println("Getting ice cream from server");
var iceCream = IceCreamServiceFactory.getIceCream('shop.iceCream');
FX.println ("  {iceCream}");

iceCream.setName ("Gelato");
iceCream.setFlavors(4);

FX.println("Starting commit");
IceCreamServiceFactory.commit("shop.iceCream", iceCream);
FX.println("Getting ice cream from the server after update");
FX.println ("   {IceCreamServiceFactory.getIceCream('shop.iceCream')}");

Line 5: Getting ice cream from the server (binding to context variable)
Line 8-9: Making changes to ice cream
Line 12: Committing changes to the server
Line 14: Getting the updated object from the server

Output:

Getting ice cream from server
  Name: Best ice cream, flavors: 2
Starting commit
Getting ice cream from the server after update
   Name: Gelato, flavors: 4

That’s it.