Saturday Aug 08, 2009

JSF-JavaFX

JSF is specification from Sun Micro Systems. Popular implementations are MyFaces from Apache, ADF from Oracle, Reference implementation from sun etc.

I would like to discuss about creating a custom JavaFX component in JSF. Using this you can embedd JavaFX easily in jsp pages. You can

use these component just like any other tag.
Example
  <jfx:applet id:"TestApplet"  code="test.class" archive="test.jar" draggable="true" width="400" height="400"/>

How to create a custom JavaFX component:

Step1 : Creating JavaFXCompoent by extending UIComponentBase.

public class JavaFXComponent extends UIComponentBase {

    @Override
    public String getFamily() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void encodeEnd(FacesContext context) throws IOException {      
       String archive = (String)getAttributes().get("archive");
       String draggable = (String)getAttributes().get("draggable");
       String width = (String)getAttributes().get("width");
       ...
              String script = "<script src=" + javafx_script +"></script>\n" +
                "<script> \n" +
                "javafx(\n" +
                    "{\n" +
                          "archive:"+archive +",\n"+
                          "draggable:"+ draggable +",\n"+
         .....
                ");\n" +
                "</script>";
     }
Here is the complete JavaFXComponent classPlace this file under source directory.
Register this class name in faces-config.xml step3.

Step 2: Create the Tag handler by extending UIComponentELTag .

public class JavaFXTagHandler extends UIComponentELTag {

    private String id;
    private String archive;
    private String code;
   ...

    ValueExpression ve;
    FacesContext fc = FacesContext.getCurrentInstance();
    ExpressionFactory factory =  fc.getApplication().getExpressionFactory();


    private void setValue(UIComponent component, String name, String value) {
        if (value != null) {
            ve = factory.createValueExpression(fc.getELContext(), value, String.class);
            component.setValueExpression(name, ve);
        }
    }

    @Override
    protected void setProperties(UIComponent component) {
        super.setProperties(component);
        setValue(component, "id", id);
        setValue(component, "archive", archive);
....
Register this class in tld file  see step4.
Here is complete JavaFXTagHandler class

Step3: Adding JavaFXComponet in faces-config.xml file.

<faces-config>

    <component>
        <component-type>JavaFX</component-type>
        <component-class>com.sun.training.JavaFXComponent</component-class>
    </component>
  ....
</faces-config>
faces-config.xml should be placed WEB-INF directory.

Step4: Creating the javafx.tld file .

<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
  <tlib-version>1.0</tlib-version>
  <short-name>javafx</short-name>
  <uri>http://java.sun.com/jsf/javafx/</uri>
    <tag>
        <name>JavaFX</name>
        <tagclass>com.sun.training.JavaFXTagHandler</tagclass>
        <bodycontent>JSP</bodycontent>
        <info>
            This is a custom JavaFX applet component
        </info>
        <attribute>
            <name>archive</name>
            <required>true</required>
        </attribute>
.......
Refer here for the complete tld file .
Create this file under WEB-INF or under its subdirectories.

I thought this would be usefull for your web pages.

Monday Jul 27, 2009

For Applet mode, click on above image

For standalone mode

source code - click here

Saturday May 09, 2009

javafxpacakger is a very good utility provided by javafx team. This was missing in java. We need to use javac , jar , keytool, jarsigner etc for packaging in java.  Where as it is very simple in JavaFX  - just by pointing the source (-src) directory and Main class name (-appClass ) we can generate .jar , .html  and .jnlp file.

 Here is some more information on javafxpacakger.

Simplest form of javafxpackager

javafxpacakger -src  <sourcedir> -appClass <main class name>

More options.

-res   represent the resource directory if you have images from different directory.

Example:  mySrc/com/xyz/a.fx    myResource/com/xyz/play.png  .  

javafxpackager -src  mySrc  -res myResource  -appClass  com.xyz.a

-d  <your own distribution directory>  using this you can override the default "dist" directory. By default  jar, jnlp, html will be placed  in "dist" directory.

Example:

javafxpackager -src  mySrc  -res myResource  -dist  myDist -appClass  com.xyz.a

Here is the options for changing the codebase, applet widht , height etc.

javafxpackager -src <src> -appVendor <vendor> -appVersion <appVersion>  -appWidth <application width> -appHeight <application height> -appCodebase <codebase> -appClass <Main class>

How to use your own key store.

You can use your own keystore using -keystore option.

Here is how you will create keystore -

$JAVA_HOME/bin/keytool -genkey  -dname "cn=FX packager,ou=CSG,o=SMI,c=US" -alias FxKey -keypass abc123 -keystore FxKeyStore -storepass xyz789 -validity 365

Now you can pass this information to javafxpackager

"$JAVAFX_HOME/bin/javafxpackager" -src src -res resource -appClass Sample -keyStore FxKeystore -keystorepassword xyz789 -keyalias FxKey -keyaliaspassword abc123 -sign

pack option and draggable.

You can use pack options to save download time. if draggable is used applet can be dragged out of browser provided you are using 6u10+

javafxpackager -src src -res resource -draggable -sign -pack200 -appClass <Main class>


Monday May 04, 2009

Here is a simple volume controller for a media player. 

 /**
 * @author Raghu Nair
 */

public class VolumeControl extends CustomNode{

    public-init var mediaPlayer:MediaPlayer;

    var width = Layout.volumeControlWidth;

    var volumeProgress =  bind mediaPlayer.volume with inverse;

    //Background.
    var progressBG : Rectangle = Rectangle {
        width: bind width
        height: bind Layout.iconSize
        fill: Color.LIGHTGRAY
        arcWidth: bind Layout.iconSize
        arcHeight: bind Layout.iconSize
        onMousePressed: function(e:MouseEvent){

            //Control the volume by pressing the mouse.
            println(progress.x);
            println(e.sceneX);
            volumeProgress =  (progress.x - e.sceneX)/width;
        }

    }


    var path = Path {
        elements: [
            MoveTo { x: progressBG.x +5    y: progress.y + Layout.iconSize },
            LineTo { x: progressBG.x + width y: progress.y },
            LineTo { x: progressBG.x + width y: progress.y + Layout.iconSize },
            LineTo { x: progressBG.x + width y: progress.y + Layout.iconSize },
        ]
        fill:Color.DARKBLUE
        clip:Rectangle {
            x: bind progressBG.layoutX
            y: bind progressBG.layoutY
            width: bind width
            height: bind Layout.iconSize
            stroke:Color.TRANSPARENT
            fill: Color.BLACK
            arcWidth: bind Layout.iconSize
            arcHeight: bind Layout.iconSize
        }

    }

    var progress : Rectangle = Rectangle {
        x: bind progressBG.layoutX
        y: bind progressBG.layoutY + 1
        height: bind Layout.iconSize
        width: bind (width * volumeProgress)
        opacity: 0.4
        fill: Color.WHITE
    }

    override public function create():Node {
        Group{
            content: [progressBG,path,progress]
        }
    }
}

 Change the Layout.iconSize to some 45 and width to 120. Your volume controller is ready.

Thursday Apr 30, 2009

In JavaFX you can create a Media Player. Here are some tips on creating a progress bar , volume controller etc.

 MediaPlayer has to be placed in a MediaView  ( this is a node  you can add it to where ever you want). MediaPlayer can play one Media at a time.

Here is a sample code for creating PlayProgress and Buffering progress .

/**
 * @author Raghu Nair
 */


public class MediaProgressBar extends CustomNode {


    public var mediaPlayer:MediaPlayer;



    var bufferProgress = 0.0;

    var progressBufferTime = bind mediaPlayer.bufferProgressTime on replace {
        var media = mediaPlayer.media;
        if(media != null) {
            if(media.duration.toMillis() != java.lang.Double.NEGATIVE_INFINITY and
            media.duration.toMillis() != Number.NEGATIVE_INFINITY and
            media.duration.toMillis() != 0 and
            mediaPlayer.bufferProgressTime.toMillis() != java.lang.Double.NEGATIVE_INFINITY and
            mediaPlayer.bufferProgressTime.toMillis() != Number.NEGATIVE_INFINITY) {
                bufferProgress = mediaPlayer.bufferProgressTime.toMillis() / media.duration.toMillis();
            }
        }
    }

    var currentTime = bind mediaPlayer.currentTime on replace {
        var media = mediaPlayer.media;
        if(media != null) {
            if(media.duration.toMillis() != java.lang.Double.NEGATIVE_INFINITY and
                media.duration.toMillis() != Number.NEGATIVE_INFINITY and
                media.duration.toMillis() != 0 and
                currentTime.toMillis() != java.lang.Double.NEGATIVE_INFINITY and
                currentTime.toMillis() != Number.NEGATIVE_INFINITY) {

                progress = currentTime.toMillis() / media.duration.toMillis();
            }
        }
    }

    var progress:Number = 0.0;

    public var width = bind Layout.progressBarWidth;

    var progressBG : Rectangle = Rectangle {
        width: bind width
        height: bind Layout.iconSize
        fill: Color.LIGHTGRAY
        arcWidth: bind Layout.iconSize
        arcHeight: bind Layout.iconSize
    }

    var progressBuffer : Rectangle = Rectangle {
        height: bind Layout.iconSize
        width: bind width * bufferProgress
        opacity: 0.4
        fill: Color.GREEN
        clip: Rectangle {
            width: bind width
            height: bind Layout.iconSize
            fill: Color.BLACK
            arcWidth: bind Layout.iconSize
            arcHeight: bind Layout.iconSize
        }
    }

    var progressPlay : Rectangle = Rectangle {
        height: bind Layout.iconSize
        width: bind width * progress
        fill: Color.GREEN
        clip: progressBG
    }

    override function create() : Node {

        blocksMouse = true;

        Group {
            content: [ progressBG, progressBuffer,progressPlay ]
        }
    }
}

progressBG  - is the background rectangle.  progressBuffer is representing the buffering percentage. progressPlay refers to play progress.

This blog copyright 2009 by Raghu Nair