mercredi 16 juin 2010

Slimshady:D5- JavaFX

I don’t know what is Slimshady :D5- JavaFX in comparison with the actual JavaFX, but I think it should be a great technology ! :)
See yourself
http://blogs.sun.com/ant/
And if you have some information about Slimshady:D5, let me know ;)



[Update: October 6, 2010]
I don't know why (may be because of that) , but the Anthony Rogers's blog is down... 
However, you could always see some videos of Slimshady on YouTube you could always see some videos of Slimshady on YouTube

mercredi 26 mai 2010

Distortion effect

After the simple scrolltexts and scrolltexts with a sinusoidal effect, here is just another old bitmap effect used in the demos written for home computers in the 1980s.



For standalone mode 
If you want to try with a bigger image, you can use the glassfish.jpg image instead of the fc-barcelone-logo.jpg and, for a better effect with a bigger image,  use this lines

var factor = (6 * Math.PI) / imageHeight;
    var v = (Math.sin(j * factor) * 20) + 20;
instead of this lines
var factor = (2 * Math.PI) / imageHeight;
    var v = (Math.sin(j * factor) * 10) + 20;
Distortion.fx
package distortion;

import javafx.scene.image.Image;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.ImageView;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.util.Math;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.runtime.ConditionalFeature;
import javafx.runtime.Platform;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;

/**
 * @author paddy
 */
println("Effect enabled: {Platform.isSupported(ConditionalFeature.EFFECT)}");
println("Input Method enabled: {Platform.isSupported(ConditionalFeature.INPUT_METHOD)}");
println("Scene 3D enabled: {Platform.isSupported(ConditionalFeature.SCENE3D)}");
println("Shape Clip enabled: {Platform.isSupported(ConditionalFeature.SHAPE_CLIP)}");

var img = Image {
            //url: "{__DIR__}images/glassfish.jpg"
            url: "{__DIR__}images/fc-barcelone-logo.jpg"
             }
def imageWidth = bind img.width as Integer;
def imageHeight = bind img.height as Integer;
def lineWidth = imageWidth;
def lineHeight = 1;
var distortionMap: Float[];
var index = 0;

for (j in [0..<imageHeight * 2]) {

    var factor = (2 * Math.PI) / imageHeight;
    var v = (Math.sin(j * factor) * 10) + 20;
    //var factor = (6 * Math.PI) / imageHeight;
    //var v = (Math.sin(j * factor) * 20) + 20;

    insert v into distortionMap;
}

def lineViewports = for (row in [0..imageHeight]) {
            Rectangle2D {
                minX: 0, minY: row, width: lineWidth, height: lineHeight
            }
        }
var distortionImg = bind
        for (row in [0..imageHeight]) {
            ImageView {
                x: bind distortionMap[row + index]
                y:  row
                viewport: bind lineViewports[row]
                image:  img
            }
        }
var t = Timeline {
            repeatCount: Timeline.INDEFINITE
            keyFrames: KeyFrame {
                time: 8ms
                canSkip: true
                action: function (): Void {
                    index = index + 1;
                    if (index > imageHeight) {
                        index = 0;
                    }
                }
            }
        }

t.play();

Stage {
    title: "Application title"
    scene: Scene {
        width: imageWidth +40
        height: imageHeight + 1

        content: [
            Rectangle {
                x: 0, y: 0
                width: imageWidth +40 , height: imageHeight + 1
                fill: Color.BLACK
                //fill: Color.GHOSTWHITE
            }
            distortionImg
        ]
    }
}

Note : The scrolltexts and the scrolltexts with sinusoidal effect work without migration on JavaFX 1.3 with AWT/Java2D/Swing toolkit.
On prism toolkit the animation is very fluid but you have to use a jpeg image instead of png for the font because the png is not supported in this early version of prism

samedi 8 mai 2010

JavaFX custom component in JavaFX 1.3

In JavaFX 1.2, Control extended CustomNode then we could override the create function to customize an existing component.  
With JavaFX 1.3, Control now extends
Parent directly then we do not have any more the create function and my custom component doesn’t compile any more … (because I used the create function to customize my components)

Then, to customize an existing component in JavaFX 1.3, I tried another way


I use the initBlock to create the nodes that I want to add to the existing component to customize it
and then I add these nodes to the children sequence of the existing component.

...
   init {
          var g = Group {
                    content: [
                                Group {
                                    ...
                                    content: [
                                        Circle {
                                               ...
                                        }
                                        Rectangle {
                                               ....
                                        }
                                     ...
                                    ]
                                    ...
                    ]
                }
           insert g into children;
        }
   ...
Now, I have a problem with the css, I want to have a default css for my SearchTextBox which is loaded by default by my component (not in the Scene by the developer) and which can be overridden by the developer.
If you have a solution, please let me know!
This example works on: Windows (Seven), Linux (Ubuntu 10.04) and Mac OS X (10.6.3)  with the Default toolkit and the Prism toolkit (JVM argument: -Xtoolkit prism) but there is one bug when you run it as an applet on Mac OS X.
The button have a wrong size, the text is too long….
[update August 8, 2010]this issue is fixed in JavaFX 1.3.1




SearchTextBox.fx
package customcomponent;

import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.input.MouseEvent;
import javafx.scene.control.TextBox;
import javafx.scene.input.KeyEvent;

/**
 * @author Patrick
 */
public class SearchTextBox extends TextBox {

    public var onResetSearch: function(): Void;
    public var onSearch: function(s: String): Void;

    override var style = "-fx-padding: 0 16 0 0; ";
    //override var styleClass = "searchTextBox text-box";
    //override var styleClass = "searchTextBox";

    override var onKeyTyped = function(event:KeyEvent){
        if (this.rawText!=""){
            onSearch(this.rawText);
        }else{
            onResetSearch();
        }
    }

    init {
          var g = Group {
                    content: [
                                Group {
                                    var crossWidth = bind this.layoutBounds.height * 0.45;
                                    var crossHeight = bind this.layoutBounds.height * 0.05;
                                    visible: bind this.rawText.length() > 0
                                    layoutX: bind this.layoutBounds.width - (this.layoutBounds.height / 2.0)
                                    layoutY: bind this.layoutBounds.height / 2.0
                                    content: [
                                        Circle {
                                            fill: Color.GRAY
                                            radius: bind this.layoutBounds.height * 0.325 // 65% of the texbox height and div 2 for the radius
                                        }
                                        Rectangle {
                                            width: bind crossWidth
                                            height: bind crossHeight
                                            translateX: bind 0 - crossWidth / 2.0
                                            translateY: bind 0 - crossHeight / 2.0
                                            fill: Color.WHITE
                                            rotate: 45
                                        }
                                        Rectangle {
                                            width: bind crossWidth
                                            height: bind crossHeight
                                            translateX: bind 0 - crossWidth / 2.0
                                            translateY: bind 0 - crossHeight / 2.0
                                            fill: Color.WHITE
                                            rotate: -45
                                        }
                                    ]
                                    onMouseClicked: function (event: MouseEvent) {
                                        this.commit();
                                        this.text = "";
                                        onResetSearch();
                                    }
                                }
                    ]
                }           
           insert g into children;
        }
}

Main.fx
package searchtextbox;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.control.Button;
import javafx.scene.control.RadioButton;

import javafx.scene.control.ToggleGroup;
import customcomponent.SearchTextBox;

/**
 * @author Patrick
 */

class ExtRadioButton extends RadioButton {
    public var action:function();

    override public var selected on replace {
        if (selected) {
            action();
        }
    }
}

def macStyleSheet="{__DIR__}resources/mac.css";
def defaultStyleSheet = "/customcomponent/stbcaspian.css";
var stylesheets:String = macStyleSheet;

Stage {
    title: "Application title"
    width: 250
    height: 200

    var cssToggleGroup = ToggleGroup {};

    scene: Scene {
        stylesheets : bind stylesheets
        content: [
            Text {
                font : Font {
                    size : 16
                }
                x: 10
                y: 30
                content: "Search "
            }
            SearchTextBox {
                    styleClass : "searchTextBox"
                    translateX :10
                    translateY :40


                    onResetSearch:function(){
                        println("reset !");
                    }
                    onSearch:function(s: String){
                        println("Search of : {s}");
                    }
            }
            Button {
                    text: "just a button to change focus"
                    translateX :10
                    translateY :80
                    action: function() {
                        println("Hello !")
                   }
            }

            ExtRadioButton {
                            text: "use the default CSS"
                            translateX :10
                            translateY :110
                            toggleGroup: cssToggleGroup

                            action: function() {
                                stylesheets = defaultStyleSheet;
                            }
            }

            ExtRadioButton {
                            text: "use the mac CSS"
                            translateX :10
                            translateY :130
                            toggleGroup: cssToggleGroup
                            selected: true

                            action: function() {
                                stylesheets = macStyleSheet;
                            }
            }
        ]
    }
}

mardi 27 avril 2010

Duke Anim in JavaFX 1.3

In JavaFX 1.3 the evaluation of the KeyValues in the Timeline changed. Now, the KeyValues are evaluated once before the Timeline starts (see "Animation: Changed semantics of KeyValue evaluation" in Java FX 1.3 Migration Guide) and thus my Duke Anim doesn’t work in Java FX 1.3 :(.
To correct it, the function Timeline.evaluateKeyValues()  has been added. This function has to be used for re-evaluates all the keyValues when it is called.
Now, let us correct Duke Anim :)
To correct Duke Anim, I have made only 2 changes:
·         I call the function Timeline.evaluateKeyValues()  at the end of the action function of KeyFrame at 1s.
anim.evaluateKeyValues();
·         And I declare the variable anim with his type to be able to call the function Timeline.evaluateKeyValues()  in the action function of the KeyFrame.
public var anim:Timeline = Timeline { …


This Duke Anim's version works on : Windows (Seven), Linux (Ubuntu 9.10) and Mac OS X (10.6.3)  with the Default toolkit and the Prism toolkit (JVM argument: -Xtoolkit prism)

Main.fx
package dukeanim;

import java.util.Random;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.Scene;
import javafx.scene.transform.Translate;
import javafx.runtime.ConditionalFeature;
import javafx.runtime.Platform;
import javafx.stage.Stage;

class DukeAnimModel {
  
    public var imageURL = "{__DIR__}images/duke.png";
  
    public var x = 0;
    public var y = 0;

    var xTarget = 0;
    var yTarget = 0;

    var xTemp = 0;
    var yTemp = 0;

    public var anim:Timeline =
    Timeline {
        autoReverse: false
        keyFrames: [
            KeyFrame {
                time: 0s
                values: [x => xTemp, y => yTemp]

            },
            KeyFrame {
                time: 1s
                values: [x => xTarget tween
                    Interpolator.SPLINE(0,.5,.5,1),y => yTarget tween
                    Interpolator.SPLINE(0,.5,.5,1)]

                action: function(): Void{

                    var generator = new Random();
                    xTarget = (generator.nextFloat() * 240 + 1) as Integer;
                    yTarget = (generator.nextFloat() * 320 + 1) as Integer;
                  
                    xTemp=x;
                    yTemp=y;

                    anim.evaluateKeyValues();
                }
            },
        ]
        repeatCount: Timeline.INDEFINITE
    };
}

println("Effect enabled: {Platform.isSupported(ConditionalFeature.EFFECT)}");
println("Input Method enabled: {Platform.isSupported(ConditionalFeature.INPUT_METHOD)}");
println("Scene 3D enabled: {Platform.isSupported(ConditionalFeature.SCENE3D)}");
println("Shape Clip enabled: {Platform.isSupported(ConditionalFeature.SHAPE_CLIP)}");

var dukeAnimModel = DukeAnimModel {}
dukeAnimModel.anim.play();

Stage {
    title: "Application title"
    width: 240  
    height: 320 
    scene: Scene {
        content: ImageView {
            transforms: Translate {
                x: bind dukeAnimModel.x
                y: bind dukeAnimModel.y
            }
            image: Image {
                url: dukeAnimModel.imageURL
            }
        }
    }
    onClose: function(){
        println("exit");
    }
}

jeudi 22 avril 2010

jeudi 15 avril 2010

EJB 3.1 Asynchronous Session Beans


Before EJB 3.1, if you want to execute an asynchronous processing you had to use JMS and a MDB, not so easy and rather heavy in most cases.
Now with EJB 3.1, you can use a simple session EJB with the @Asynchronous annotation on the method which must be called asynchronously.
@Stateless
@Remote(HelloEjbAsynchronousRemote.class)
public class HelloEjbAsynchronous implements HelloEjbAsynchronousRemote {

    @Asynchronous
    @Override
    public Future<String> ejbAsynchronousSayHello(String name){
If your method has a return value, your method has to return an AsyncResult object which is an implementation of Future.
return new AsyncResult<String>("Hello "+name);
For the client, you just have to call the remote ejbAsynchronousSayHello by using the new portable global JNDI names.
The EJB container returns the control to the client immediately, in my case with a Future object in return, and then executes the method.
HelloEjbAsynchronousRemote ha = 
            (HelloEjbAsynchronousRemote)ic.lookup("java:global/EjbAsynchronous/HelloEjbAsynchronous");
            Future future = ha.ejbAsynchronousSayHello("Patrick");
The client can retrieve the result value with Future.get() and can get the state of the processing with Future.isDone().
String ret = (String)future.get();

Full source code of the EJB - HelloEjbAsynchronous.java
package fr.paddy.ejb31;

import java.util.Date;
import java.util.concurrent.Future;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Remote;
import javax.ejb.Stateless;

@Stateless
@Remote(HelloEjbAsynchronousRemote.class)
public class HelloEjbAsynchronous implements HelloEjbAsynchronousRemote {

    @Asynchronous
    @Override
    public Future<String> ejbAsynchronousSayHello(String name){
        System.out.println(new Date().toString()+" - Begin - HelloEjbAsynchronos->ejbAsynchronousSayHello "+name);

        try{
           Thread.sleep(5*1000);
        }catch (Exception e){
            e.printStackTrace();
        }

        System.out.println(new Date().toString()+" - End - HelloEjbAsynchronos->ejbAsynchronousSayHello "+name);

        return new AsyncResult<String>("Hello "+name);
        
    }
}
The ejbAsynchronousSayHello method, which is declared asynchronous, just wait 5 seconds to simulate a long processing and return the String   "Hello "+name, the variable name being passed as parameter.

Full source code of the remote interface - HelloEjbAsynchronousRemote.java
package fr.paddy.ejb31;

import java.util.concurrent.Future;

public interface HelloEjbAsynchronousRemote {    
    public Future<String> ejbAsynchronousSayHello(String name);
}


Full source code of the EJB Client - ClientMain.java
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import fr.paddy.ejb31.HelloEjbAsynchronousRemote;
import java.util.Date;

public class ClientMain {

    public static void main(String[] args) {
        Properties props = new Properties();
        props.setProperty("java.naming.factory.initial",
                             "com.sun.enterprise.naming.SerialInitContextFactory");
        props.setProperty("java.naming.factory.url.pkgs",
                             "com.sun.enterprise.naming");
        props.setProperty("java.naming.factory.state",
                             "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
        props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
        props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");

        try{
            InitialContext ic = new InitialContext(props);
            String ret="";

            HelloEjbAsynchronousRemote ha =
            (HelloEjbAsynchronousRemote)ic.lookup("java:global/EjbAsynchronous/HelloEjbAsynchronous");
            Future future = ha.ejbAsynchronousSayHello("Patrick");

            while (!future.isDone()){
                Thread.sleep(1000);
                System.out.println(new Date().toString()+" - I do other things ...");
            }

            ret = (String)future.get();
            System.out.println(new Date().toString()+" - ret : "+ret);

        }catch (NamingException ne){
            ne.printStackTrace();
        }catch (InterruptedException ie){
            ie.printStackTrace();
        }catch (ExecutionException ee){
            ee.printStackTrace();
        }
    }
}

Get the Netbeans projects:  EJB , Client
Because of this issue, you must use GlassFish V3.0.1 promoted build 12 to test this example.
Or used this workaround
while (!future.isDone()){
                Thread.sleep(1000);
                System.out.println(new Date().toString()+" - I do other things ...");
                // System.out.println("futur.isDone"+future.isDone());
                // just a hack, because at the moment future.isDone doesn't work !
                try{
                    Object o = future.get(10,TimeUnit.MILLISECONDS);
                }catch (TimeoutException te){
                   // te.printStackTrace();
                }
                // end of the hack
                // System.out.println("futur.isDone"+future.isDone());
            }