Thursday, September 15, 2011

Thursday, September 8, 2011

Problems with Mojarra

In recent Mojarra versions, there was always a problem. JBoss AS 7 comes with Mojarra version 2.0.4-b09. Once the tag a4j:ajax will be used on JSF components you receive the following error message:
javax.faces.FacesException: Unexpected error restoring state for component with id form:inputComponent:input.  Cause: java.lang.ClassCastException: javax.faces.component.StateHolderSaver cannot be cast to [Ljava.lang.Object;.
    at com.sun.faces.application.view.StateManagementStrategyImpl$1.visit(StateManagementStrategyImpl.java:273)
    at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1485)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1496)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1496)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1496)
    at javax.faces.component.UIForm.visitTree(UIForm.java:335)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1496)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1496)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1496)
    at com.sun.faces.component.visit.VisitUtils.doFullNonIteratingVisit(VisitUtils.java:75)
    at com.sun.faces.application.view.StateManagementStrategyImpl.restoreView(StateManagementStrategyImpl.java:257)
    at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:181)
    at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:123)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:448)
    at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:148)
    at com.ocpsoft.pretty.faces.application.PrettyViewHandler.restoreView(PrettyViewHandler.java:109)
    at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:288)
    at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:288)
    at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:288)
    at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:187)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:111)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
    at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:67)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
    at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:118)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
    at org.jboss.seam.servlet.exception.CatchExceptionFilter.doFilter(CatchExceptionFilter.java:65)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
    at org.jboss.seam.servlet.event.ServletEventBridgeFilter.doFilter(ServletEventBridgeFilter.java:72)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
    at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:139)
    at org.jboss.as.web.NamingValve.invoke(NamingValve.java:57)
    at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:49)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:154)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:667)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:952)
    at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.ClassCastException: javax.faces.component.StateHolderSaver cannot be cast to [Ljava.lang.Object;
    at org.ajax4jsf.component.behavior.AjaxBehavior.restoreState(AjaxBehavior.java:343)
    at javax.faces.component.UIComponentBase.restoreBehaviors(UIComponentBase.java:2056)
    at javax.faces.component.UIComponentBase.restoreBehaviorsState(UIComponentBase.java:2023)
    at javax.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1443)
    at javax.faces.component.UIOutput.restoreState(UIOutput.java:256)
    at javax.faces.component.UIInput.restoreState(UIInput.java:1379)
    at com.sun.faces.application.view.StateManagementStrategyImpl$1.visit(StateManagementStrategyImpl.java:265)
    ... 51 more

The workaround
The best way to solve this problem, is to switch Mojarra version back to 2.0.2-b10. Download the package from the Mojarra repository. Extract zip file and drop the jsf-impl-2.0.2-b10.jar into:
/your/path/jboss-as-web-7.0.1.Final/modules/com/sun/jsf-impl/main 
Change the module.xml in the same directory to:
<module xmlns="urn:jboss:module:1.0" name="com.sun.jsf-impl">
    <dependencies>
        <module name="javax.faces.api"/>
        <module name="javaee.api"/>
        <module name="javax.servlet.jstl.api"/>
        <module name="org.apache.xerces" services="import"/>
        <module name="org.apache.xalan" services="import"/>
    </dependencies>

    <resources>
        <resource-root path="jsf-impl-2.0.2-b10.jar"/>
    </resources>
</module>

Why not the lastest version?
Currently the latest Mojarra version doesn't invoke Seam Faces s:viewAction methods. Lets cross fingers and hope this issue will be fixed with the upcoming 2.2.0 version.

Important Links:

Upgrade Hibernate Core 3.6 to 4.0

Be careful with updating Hibernate Core 3.6 to 4.0 with MySQL as database and strategy GenerationType.AUTO. For me it was an update of JBoss AS 6 (Hibernate 3.6) to AS 7 (Hibernate 4). If you have mapped tables like this:
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Hibernate 3.6 uses the auto increment functionality to increment the id. But Hibernate 4 creates a sequence table and uses this to increment the id. Even if you say sequences are ok, you will run into exceptions on a given database, since Hibernate tries to save the first object with id 1 which already exists. Better change the strategy to:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Now both Hibernate versions will create the table structure in the same way.

PrettyFaces for SEO-friendly URLs

We all know JSF2 out of the box has still disadvantages with RESTFUL URLs. This problem can easily be solved by PrettyFaces, which allows SEO-friendly URLs with a simple on annotation based configuration.

1) Configuration

Download PrettyFaces binaries and add them to the classpath. If you use Apache Maven, you only need to add PrettyFaces as dependency to your project:
<dependency>
    <groupId>com.ocpsoft</groupId>
    <artifactId>prettyfaces-jsf2</artifactId>
    <version>3.3.0</version>
</dependency>

To ensure maximum flexibility, we configure PrettyFaces on anntotations. The web.xml parameter com.ocpsoft.pretty.BASE_PACKAGE tells PrettyFaces which package have to be scanned recursively for beans with @URLMapping annotation:
<context-param>
    <param-name>com.ocpsoft.pretty.BASE_PACKAGES</param-name>
    <param-value>your.package.structure</param-value>
</context-param>

Congratulations, you have added PrettyFaces to your project successfully.

2) Basic Pages

Now just add @URLMapping's to your action beans like:
@Named
@RequestScoped
@URLMapping(id = "startpage", pattern = "/", viewId = "/pages/startpage.seam")
public class StartpageAction implements Serializable 
{
...
}

The annotation based configuration of PrettyFaces keeps the amount of navigation rules in faces-config.xml small. The new prefix pretty:, could be used as return string on methods of managed beans or as outcome from h:button and h:link components. For a simple reload of the current page just return pretty:. In case of return pretty:startpage PrettyFaces search for the annotation based bean with @URLMapping(id = "startpage", ... and redirect to this page.

On the JSF side you just need to put the id of the @URLMapping as outcome:
<h:link outcome="pretty:startpage" value="Startpage" />

3) Pages With URL Parameters

Pass additional parameters as URL value isn't much harder. Just setup the bean:
@Named
@RequestScoped
@URLMapping(id = "category", pattern = "/cat/#{catId : boardCategoryAction.catId}.html", viewId = "/pages/category.seam")
public class CategoryAction implements Serializable
{
...
    private String catId;
...
    public String getCatId() {
        return catId;
    }

    public void setCatId(String catId) {
        this.catId = catId;
    }
}

And parse the parameter in the "h:link":
<h:link outcome="pretty:category" value="Category">
    <f:param name="catId" value="123" />
</h:link>

Was it ever easier to set up pretty URLs? I don't think so :)
Thanks to Lincoln and the whole OCPSoft Team.

Wednesday, September 7, 2011

Tuesday, September 6, 2011

Clustering with JBoss mod_cluster and AS 7

Today I want to introduce you to JBoss mod_cluster. The Apache-based load balancer works similiar to mod_jk and mod_proxy, but is much more flexible. New application server nodes will introduce theirselves automatically to mod_cluster at runtime. There is no configuration like the worker.properties on mod_jk.

Why should I use mod_cluster?

Clustering was never easier, mod_cluster can balance heavy traffic on registered application server nodes. The web interface allows to enable or disable single applications for each node. If all nodes went down e.g. updating your application mod_cluster will provide a clean error code 503 (service temporary unavailable) page instead of just the default page is not available browser view. Another advantage is mod_cluster 1.1.x and AS 7 fits perfect together and work awesome after minimal amount of configuration.

Prepare Jboss AS 7 for clustering

First, open the configuration file standalone.xml of your Jboss AS 7 instance and add modcluster extension and subsystem on the provided sections:
<extension module="org.jboss.as.modcluster"/>
...
<subsystem xmlns="urn:jboss:domain:modcluster:1.0" />
Keep in mind to set up the http connector port properly. Since mod_cluster handle all requests for us, the port doesn't really matter and should be blocked by firewall anyways. The first application node could be run for example on http port 8085, the next one on 8086 and so on.

Quick Start mod_cluster

The Quick Start Guide provides simple steps to install mod_cluster on Windows and Linux. After some minutes mod_cluster should run on http port 80 and returns It Works page on request to http://localhost/. The upcoming mod_cluster configuration is a bit tricky especially for setting up multiple vhosts.

By default mod_cluster provides the application server cluster in each vhost, means other provided content will disappear as soon as the first application node registered to the mode_cluster manager. If you just want the cluster responding to a specific vhost add this additional parameter to your httpd.conf:
CreateBalancers 1

The manager listener should be pre-configurated by mod_cluster. Maybe you will have to change the ip address here:
<IfModule manager_module>
    Listen 127.0.0.1:6666
    ManagerBalancerName mycluster
 
    <VirtualHost 127.0.0.1:6666> 
        KeepAliveTimeout 300
        MaxKeepAliveRequests 0
        AdvertiseFrequency 5
        ServerAdvertise On
 
        <Location />
            Order deny,allow
            Allow from 127.0.0
        </Location>
  </VirtualHost>
</IfModule>
Keep in mind to use 127.0.0.1 if all application servers run on the same server as mod_cluster, otherwise use the internal server ip and allow an internal ip address range.

Next step is to configure the vhost for our cluster. The /mod_cluster_manager location should be only allowed from localhost or internal ip address range:
NameVirtualHost *:80

<VirtualHost *:80> 
    ServerAdmin info@domain.de
    ServerName www.domain.com
    ServerAlias www.domain.com

    ProxyPass / balancer://mycluster stickysession=JSESSIONID|jsessionid nofailover=On
    ProxyPassReverse / balancer://mycluster
    ProxyPreserveHost On

    <Location />
        Order deny,allow
        Allow from All
    </Location>

    <Location /mod_cluster_manager>
        SetHandler mod_cluster-manager
        Order deny,allow
        Deny from all
        Allow from 127.0.0
    </Location>
</VirtualHost>

Next we could add additional vhosts. For example add a vhost to provide direct access to file structure (e.g. content repository) for the subdomain static.domain.com:
<VirtualHost *:80>
    ServerAdmin info@domain.com
    ServerName static.domain.com
    ServerAlias static.domain.com

    DocumentRoot /PRODUCTION/repository/
    <Directory /PRODUCTION/repository/>
        Options FollowSymLinks
        AllowOverride None
        Order Deny,Allow
        Allow from all
    </Directory>

    ErrorLog "/PRODUCTION/mod_cluster/httpd/logs/error.log"

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel warn

    CustomLog "/PRODUCTION/mod_cluster/httpd/logs/access.log" combined
</VirtualHost>

Now start mod_cluster and application server nodes. You will see the following result:

www.domain.com -> Request via manager to any application node
static.domain.com -> Access to local directory /PRODUCTION/repository/

Jboss AS 7 and double slash redirect problem

The current version of mod_cluster has a bug with mixing ProxyPass and ProxyPassReverse, means double slashes occur on faces redirects. For example redirect to /test resolves in www.mydomain.com//test. Currently the parameter ProxyPreserveHost On could be used as workaround. For more informations about this bug follow MODCLUSTER-250.

Happy clustering :)

Important Links:

Accessing database during application initialization

In development state most applications need dummy data which should be imported on application initialization. During this phase Seam's TransactionManager is not initialized yet. This means we have to build our UserTransaction manually:

@PersistenceContext
private EntityManager em;

@Inject
private UserTransaction utx;

public void importData(@Observes @Initialized WebApplication webapp) {
    try {
        utx.begin();
        em.persist(new MyDatabaseEntity());
        utx.commit();
    } catch (Exception e) {
        log.error("Import failed. Seed data will not be available.", e);
        try {
            if (utx.getStatus() == Status.STATUS_ACTIVE) {
                try {
                    utx.rollback();
                } catch (Exception rbe) {
                    log.error("Error rolling back transaction", rbe);
                }
            }
        } catch (Exception se) {
        }
    }
}

Monday, September 5, 2011

PrimeFaces 3.0.M3 UTF-8 encoding issues

Once PrimeFaces 3.0.M3 is added as dependency to the project, utf-8 encoding problems occur on form submits. To avoid these problems you will have to add a custom filter:
public class CharacterEncodingFilter implements Filter, Serializable 
{
    private static final long serialVersionUID = -4246457499875267088L;

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        chain.doFilter(req, resp);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }
}

Add this part to your web.xml:
<filter>
    <filter-name>Character Encoding Filter</filter-name>
    <filter-class>com.your.package.to.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>Character Encoding Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

After these changes are made, the utf-8 encoding problems won't occur anymore.

The Beginning

Welcome to the Seam Space. I will be using this blog as a place to hold my knowledge, interests and solutions for common problems.

Who am I?

My name is Sebastian Sachtleben, a software developer with five years experience of web development. My experience includes Struts, Spring, Seam Framework, Richfaces, Primefaces 3, EJB, JSF, Hibernate etc. I love the direction JEE is taking at the moment and I'm delighted to develop applications with it. My current project is ReplicaScene.

Why did I want to start this blog?

I've gained a lot of experience in the past years while developing different projects and I want to share solutions for common problems which may occur in the future.

Final

Well friends. I'm done. I've set up my own blog, and published my first post.