0

RIA technologies: lets compare font rendering.

Posted by JOKe on 8/20/2009 12:30:00 PM in , ,

The idea is to compare the current RIA frameworks like Flex(Flash), Silverlight and JavaFX and see what are the fonts that they produces. We are in 21th century most of the people have laptops and use TFT screens and windows is the most usedOS this days, so most of the people use clear type font rendering. Lets see how each technology uses clear type :

For me the best fonts with clear type enabled are fonts in WinForms and native windows applications. I’ve found very interesting comparison here: http://www.scottlogic.co.uk/blog/wpf/2009/07/silverlight-v3-cleartype-font-rendering-a-comparison/ I will use some of the screenshots from there.

WinForms
(ClearType OFF)

You can see that WinForms with ClearType off are still best for CRT screens but not good for TFT screens.

WinForms
(ClearType On)

The WinForms + ClearType is the best from microsoft for now.
Silverlight v2.0 Silverlight v2 fonts are so bad I cant see the RIA when I have bad fonts.
Silverlight v3.0 Silverlight v3 have ClearType but it is still far far worst than the WinForms ClearType.

WPF

(ClearType off)

WPF with ClearType off is worst than WinForms with ClearType Off it is bad for CRT and bad for TFT displays. It is bad for everything maybe this is the reason why there is no WPF applications.

WPF

(ClearType on)

WPF with ClearType on is better than Silverlight for me but it is still bad. In .net 4.0 it will be finally fixed if we can believe to Microsoft.

JavaFX/Swing (Java 1.6u10+)

JavaFX and Swing with Java 1.6 update 10 have very good ClearType support I didn't see the difference between this and WinForms. But to have ClearType in JavaFX you are stick to SwingXXXX controls.

JavaFX 1.2

(node based controls)

JavaFX 1.2 with Node based controls are maybe the worst choice. They are so ugly that I cant believe on my eyes. I was hoping this fonts problem to be fixed in the next JavaFX versions.

Flash 10/Flex 4

Flash 10 and flex 4 looks AMAZING. This is the first “RIA” framework that done the web perfect. I know that Adobe clients are more designers and I like that Adobe is working to make things beautiful.

 

Summary:

If you ask me and you want nice RIA framework for your good looking 21th century applications use flash. JavaFX is still bad if there is a nice development and designing tools it will be bad again. Silverlight is version 3.0 but it is still far far worst than Flash sometimes my browser freezes because of Silverlight, Expression Blend is WPF application and looks very bad and there is no designers that use Expression Design.

Note : You think Expression Blend is black because Microsoft likes Black GUI ? NO it is black because if was “white” you will see the bad looking fonts. In Expression blend 3 you can change the theme with “white theme” but when you change it you will see “the beauty”.


|
10

RichFaces server-side paging with DataTable.

Posted by JOKe on 8/19/2009 04:32:00 PM

Most of the component toolkits have build in support for server-side paging this days but in rest of the cases you need to customize a little the data model or data provider or component itself to have data paging. The reason why I write this post is because when I first saw richfaces everythink was perfect except the server-side paging. They have paging but it was client side based on JavaScript witch just doesn't work in many cases and I loose a lot of time to understand all models, which model I must extend and how to do it to create a data model with server side paging. At the current version of RichFaces on the demo page information about how to do server-side paging at least exist http://livedemo.exadel.com/richfaces-demo/richfaces/dataTable.jsf?tab=dataModel&cid=3608154 but like always the JBoss/RedHat/Exadel doesn’t provide us a fast full easy working example and we must loose a lot of time to search for classes in the demo.

The reason why I write this post is to give you simple application that uses server side paging and a little “directions” what you need to customize to have server side paging in all cases. If you have read my previous post about hibernate + spring + jsf + richfaces you will have very easy way to extend this simple demo and to make all Richfaces tables to have server-side paging instead of client-side javascript paging.

So lets start.

First what we want to create ? We want to create a serveri-side paging that looks like this :

Table

When you click on the pager at the bottom it will make ajax call to the server and will fetch the the results for the next page. As you can read in the richFaces demo page you must make custom(extended) data model extending org.ajax4jsf.model.ExtendedDataModel and org.ajax4jsf.model.SerializableDataModel. These two classes work together to provide functions that missing in the standard DataModel.

The most important additional functions are:

  • access for rows by primary keys instead of index position
  • implementation of "visitor" pattern over the "range" of rows to support "table scroller" or "paginator" functions
  • ability to serialize table data, so it can be used on post-back processing without additional database query

In most cases you don’t care about most of this. In our Simple Server-Side richfaces application we will have a simple entity called User. It can be Hibernate/JPA entity or whatever you want for now it will be just a POJO with getters and setters.

package org.joke.demo.extendeddatamodel;

public class User {
private Integer pk;
private String username;
private String password;
private String fullName;

public Integer getPk() {
return pk;
}

public void setPk(Integer pk) {
this.pk = pk;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getFullName() {
return fullName;
}

public void setFullName(String fullName) {
this.fullName = fullName;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

private String email;

public User(Integer pk) {
this.pk = pk;
}
}

next we will use a simple RandomDataHelper class that will give us random strings and data. This class is downloaded from exadel simple(test) sources.

package org.joke.demo.utils;

public class RandomDataHelper {
public static int random(int min, int max) {
assert(min<=max);
return min+(int)Math.round(Math.random()*(double)(max-min));
}
public static Object random(Object values[]) {
assert(values!=null);
return values[random(0,values.length-1)];
}
private static char randomChar() {
if (Math.random()>0.5) {
return (char)((int)'0'+random(0,9));
} else {
return (char)((int)'A'+random(0,25));
}
}
public static String randomString(int length) {
StringBuffer buf = new StringBuffer();
for (int counter=0;counter<length;counter++) {
buf.append(randomChar());
}
return buf.toString();
}
}

When we have this we are ready to create our model. The model is copy paste from the exadel model shown in the demo page

package org.joke.demo.extendeddatamodel;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.context.FacesContext;

import org.ajax4jsf.model.DataVisitor;
import org.ajax4jsf.model.Range;
import org.ajax4jsf.model.SequenceRange;
import org.ajax4jsf.model.SerializableDataModel;
/**
*
* @author ias
* This is example class that intended to demonstrate use of ExtendedDataModel and SerializableDataModel.
* This implementation intended to be used as a request scope bean. However, it actually provides serialized
* state, so on a post-back we do not load data from the data provider. Instead we use data that was used
* during rendering.
* This data model must be used together with Data Provider, which is responsible for actual data load
* from the database using specific filtering and sorting. Normally Data Provider must be in either session, or conversation
* scope.
*/
public class AuctionDataModel extends SerializableDataModel {

private AuctionDataProvider dataProvider;
private Integer currentPk;
private Map<Integer,User> wrappedData = new HashMap<Integer,User>();
private List<Integer> wrappedKeys = null;

/**
*
*/
private static final long serialVersionUID = -1956179896877538628L;

/**
* This method never called from framework.
* (non-Javadoc)
* @see org.ajax4jsf.model.ExtendedDataModel#getRowKey()
*/
@Override
public Object getRowKey() {
return currentPk;
}
/**
* This method normally called by Visitor before request Data Row.
*/
@Override
public void setRowKey(Object key) {
this.currentPk = (Integer) key;

}
/**
* This is main part of Visitor pattern. Method called by framework many times during request processing.
*/
@Override
public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException {
int firstRow = ((SequenceRange)range).getFirstRow();
int numberOfRows = ((SequenceRange)range).getRows();
wrappedKeys = new ArrayList<Integer>();
for (User item:dataProvider.getItemsByrange(new Integer(firstRow), numberOfRows, null, true)) {
wrappedKeys.add(item.getPk());
wrappedData.put(item.getPk(), item);
visitor.process(context, item.getPk(), argument);
}
}
/**
* This method must return actual data rows count from the Data Provider. It is used by pagination control
* to determine total number of data items.
*/
private Integer rowCount; // better to buffer row count locally
@Override
public int getRowCount() {
if (rowCount==null) {
rowCount = new Integer(getDataProvider().getRowCount());
return rowCount.intValue();
} else {
return rowCount.intValue();
}
}
/**
* This is main way to obtain data row. It is intensively used by framework.
* We strongly recommend use of local cache in that method.
*/
@Override
public Object getRowData() {
if (currentPk==null) {
return null;
} else {
User ret = wrappedData.get(currentPk);
if (ret==null) {
ret = getDataProvider().getAuctionItemByPk(currentPk);
wrappedData.put(currentPk, ret);
return ret;
} else {
return ret;
}
}
}

/**
* Unused rudiment from old JSF staff.
*/
@Override
public int getRowIndex() {
return 0;
}

/**
* Unused rudiment from old JSF staff.
*/
@Override
public Object getWrappedData() {
throw new UnsupportedOperationException();
}

/**
* Never called by framework.
*/
@Override
public boolean isRowAvailable() {
if (currentPk==null) {
return false;
} else {
return getDataProvider().hasAuctionItemByPk(currentPk);
}
}

/**
* Unused rudiment from old JSF staff.
*/
@Override
public void setRowIndex(int rowIndex) {
//ignore
}

/**
* Unused rudiment from old JSF staff.
*/
@Override
public void setWrappedData(Object data) {
throw new UnsupportedOperationException();
}

/**
* This method suppose to produce SerializableDataModel that will be serialized into View State and used on a post-back.
* In current implementation we just mark current model as serialized. In more complicated cases we may need to
* transform data to actually serialized form.
*/
public SerializableDataModel getSerializableModel(Range range) {
if (wrappedKeys!=null) {
return this;
} else {
return null;
}
}
/**
* This is helper method that is called by framework after model update. In must delegate actual database update to
* Data Provider.
*/
@Override
public void update() {
getDataProvider().update();
}

public AuctionDataProvider getDataProvider() {
return dataProvider;
}

public void setDataProvider(AuctionDataProvider dataProvider) {
this.dataProvider = dataProvider;
}

}

Just some notes about it. The important parts for you are two : method getRowIndex() must not be used in JSF 1.2 but MyFaces implementation invokes it some times so in model provided by exadel the method throws exception here we make it to return 0; just because we don't want to have exception with myfaces implementation.

The method public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) is the miracle this is the method invoked every time when you click the pager. The parameter Range contains the firstRow and countRows that you want to show in the dataTable so you just need to get them. When you have them you need to call your class that provides data. This can be a Service or some kind of data provider. In this simple case we have :

for (User item:dataProvider.getItemsByrange(new Integer(firstRow), numberOfRows, null, true))

so we have class dataProvider and method getItemsByRange. This dataProvider is a field in our dataModel but you can pass it or inject it from spring or from jsf faces-config.

The auction data provider class looks like this :

package org.joke.demo.extendeddatamodel;

import java.util.ArrayList;
import java.util.List;

import org.joke.demo.utils.RandomDataHelper;

public class AuctionDataProvider {

private String allNames[] = {
"Naiden Gochev",
"Toshko Poshkov",
"Chocho Monchov",
"Trallaa Trart",
"Peter Taharov",
"Djanko bakov",
"Hektor Mektor",
"Tartal tartalov",
"Djadja badja",
"Aza Daraz",
"Arazd azasd ",
"Max Payne",
"Max Damage",
"Nqkoi Drug",
"Treti peti",
"Shesti Sedmi",
"Talafal Kalafalov",
"Iumgur mungurov",
"Hrisi hrisi",
"Tartalan tartalanov",
"Az Taz",
"Maz Praz",
"Liz languare",
"Axa Ratra",
"Daraz rasas",
"acb asd",
"ascv asdv",
"ara araaaa",
"mon4o gon4o",
"petq petrova" };
private List<User> allItems = null;
private static final int VOLUME = 200;

private synchronized void initData() {
List<User> data = new ArrayList<User>();
for (int counter = 0; counter < VOLUME; counter++) {
User item = new User(new Integer(counter));
item.setFullName((String) RandomDataHelper
.random(allNames));
item.setUsername(RandomDataHelper.randomString(8));
item.setPassword(RandomDataHelper.randomString(8));
data.add(item);

}
allItems = data;
}

public List<User> getAllItems() {
if (allItems != null && allItems.size() > 0) {
return allItems;
} else {
initData();
return allItems;
}
}

public User getAuctionItemByPk(Integer pk) {
for (User item : getAllItems()) {
if (item.getPk().equals(pk)) {
return item;
}
}
throw new RuntimeException("Auction Item pk=" + pk.toString()
+ " not found");
}

public boolean hasAuctionItemByPk(Integer pk) {
for (User item : getAllItems()) {
if (item.getPk().equals(pk)) {
return true;
}
}
return false;

}

public List<User> getItemsByrange(Integer startPk,
int numberOfRows, String sortField, boolean ascending) {
System.out.println("load items from "+startPk + " and count of rows " + numberOfRows);
List<User> ret = new ArrayList<User>();
for (int counter = 0; counter < numberOfRows; counter++) {
ret.add(getAllItems().get(startPk.intValue() + counter));
}
return ret;
}

public void update() {
// nothing need to do
}

public int getRowCount() {
return getAllItems().size();
}
}

Thats all this provider contains all the operations that are needed from the model to work. Note the names of the provider and model AuctionDataProvider and AuctionDataModel this was the names in the exadel demo/test application which is not included in exadels demo site. You can find them easy in google, the difference there is that they use AuctionItem not User and the Exadel demo is not so simple and short.

All we need is the view and the faces-config.

The view looks like this :

<?xml version="1.0" encoding="UTF-8" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:h="http://java.sun.com/jsf/html" version="2.1">
<jsp:directive.page language="java"
contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" />
<jsp:text>
<![CDATA[ <?xml version="1.0" encoding="UTF-8" ?> ]]>
</jsp:text>
<jsp:text>
<![CDATA[ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ]]>
</jsp:text>

<f:view>
<h:form>
<rich:dataTable id="auction" value="#{auctionDataModel}"
var="item" rows="10">
<rich:column>
<f:facet name="header">
<h:outputText value="full name" />
</f:facet>
<h:outputText id="fullName" value="#{item.fullName}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="username" />
</f:facet>
<h:outputText id="username" value="#{item.username}">
</h:outputText>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="password" />
</f:facet>

<h:inputText id="password" value="#{item.password}" label="password">
</h:inputText>

</rich:column>
<f:facet name="footer">
<rich:datascroller for="auction" maxPages="5" />
</f:facet>
</rich:dataTable>
</h:form>
</f:view>
</jsp:root>
And the faces-config.xml looks like this :
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<managed-bean>
<managed-bean-name>auctionDataModel</managed-bean-name>
<managed-bean-class>org.joke.demo.extendeddatamodel.AuctionDataModel</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>dataProvider</property-name>
<value>#{auctionDataProvider}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>auctionDataProvider</managed-bean-name>
<managed-bean-class>org.joke.demo.extendeddatamodel.AuctionDataProvider</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
</faces-config>

You can notice that I inject the auctionDataProvider in the auctionDataModel here. If you use spring and Spring EL Resolver you can use spring bean container as well.

The full source of this example can be found here: http://dl.getdropbox.com/u/887821/JsfHelloWorld.zip


|
10

Hibernate Generic DAO.

Posted by JOKe on 8/19/2009 11:53:00 AM

When you use Hibernate and DAO pattern it is a good idea to use a Generic Base Dao. The fallowing code snippet contains GenericDAO that is a base class for all my DAO classes. This GenericDAO uses HibernateDaoSupport from Spring for its implementation if you want you can use JpaDaoSupport or JdbcDaoSupport in your projects.

My Generic DAO interface looks like this :

package org.joke.myproject.dao.base;

import java.io.Serializable;

import java.util.List;

/**

* @author Naiden Gochev

* @param <E>

* @param <PK>

*/

public interface GenericDao<E,PK  extends Serializable> {

    PK save(E newInstance);

    void update(E transientObject);

    void saveOrUpdate(E transientObject);

    void delete(E persistentObject);

    E findById(PK id);

    List<E> findAll();

    List<E> findAllByProperty(String propertyName,Object value);

}

All method names are very common so I don't think they need some explanation.

The implementation of this GenericDAO :

package org.joke.myproject.dao.base;

 

import java.io.Serializable;

import java.util.List;

import org.hibernate.criterion.DetachedCriteria;

import org.hibernate.criterion.Restrictions;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**

* @author JOKe

* @param <E>

* @param <PK>

*/

public abstract class GenericDaoImpl<E, PK extends Serializable> extends

            HibernateDaoSupport implements GenericDao<E, PK> {

      @SuppressWarnings("unchecked")

      public PK save(E newInstance) {

            return (PK) getHibernateTemplate().save(newInstance);

      }

      @SuppressWarnings("unchecked")

      public E findById(PK id) {

            return (E) getHibernateTemplate().get(getEntityClass(), id);

      }

      @SuppressWarnings("unchecked")

      public List<E> findAll() {

            return getHibernateTemplate().findByCriteria(createDetachedCriteria());

      }

      @SuppressWarnings("unchecked")

      public List<E> findAllByProperty(String propertyName, Object value) {

            DetachedCriteria criteria = createDetachedCriteria();

            criteria.add(Restrictions.eq(propertyName, value));

            return getHibernateTemplate().findByCriteria(criteria);

      }

      public List<E> findByExample(E object) {

            List<E> resultList = getHibernateTemplate().findByExample(object, 0, 1);

            return resultList;

      }

      public List<E> findByExample(E object, int firstResult, int maxResults) {

            List<E> resultList = getHibernateTemplate().findByExample(object,

                        firstResult, maxResults);

            return resultList;

      }

      public void update(E transientObject) {

            getHibernateTemplate().update(transientObject);

      }

      public void saveOrUpdate(E transientObject) {

            getHibernateTemplate().saveOrUpdate(transientObject);

      }

      public void delete(E persistentObject) {

            getHibernateTemplate().delete(persistentObject);

      }

      protected abstract Class<E> getEntityClass();

      protected DetachedCriteria createDetachedCriteria() {

            return DetachedCriteria.forClass(getEntityClass());

      }

}

The GenericDaoImpl is abstract because the actual DAOs will extend it and will override only one method :  protected abstract Class<E> getEntityClass(); This method is used for creating of the DetachedCriteria object in the createDetachedCriteria() method.

Every DAO class that implements GenericDaoImpl will look like this :

package org.joke.myproject.dao;

 

import java.util.List;

import org.hibernate.criterion.DetachedCriteria;

import org.hibernate.criterion.Restrictions;

import org.joke.myproject.dao.base.GenericDaoImpl;

import org.joke.myproject.entity.Message;

 

public class MessagesDAO extends GenericDaoImpl<Message, Integer> {

      @Override

      protected Class<Message> getEntityClass() {

            return Message.class;

      }

      public List<Message> findNotDeletedTextMessages() {

            DetachedCriteria criteria = createDetachedCriteria();

            criteria.add(Restrictions.eq("deleted", false));

            criteria.add(Restrictions.or(Restrictions.eq("custom", false), Restrictions.isNull("custom")));

            List<Message> textMessages = getHibernateTemplate().findByCriteria(criteria);

            return textMessages;

      }

}

The method here called findNotDeletedTextMessages() is optional it is just another DAO method added in the implementation. If you use Spring you can just add the annotation @Component as class annotation and you have DAO ready for use.

That's all have fun.

Edit : Check out my next article called  JSF, Spring, Hibernate – lets make development easy. It will use this GenericDAO to make architecture of an application.


|

Copyright © 2009 JOKe's Blog All rights reserved. Theme based on the Theme by Laptop Geek with changes by JOKe. | Bloggerized by FalconHive.