Monday, August 22, 2011

Our Seam 2 to JEE 6 replacement list

JBoss Seam 2 and JEE 5

In the last years we had great success with JBoss Seam 2.x. This very innovative framework makes JEE 5 usable. The classic multi-tiered JEE-based Web application primarily consists of JavaServer Faces (JSF) in the presentation layer, Enterprise JavaBeans (EJB) in the business layer and Java Persistence API (JPA) as O/R mapper for the database access.
The biggest problem was the missing link between JSF and EJB / JPA, some kind of binding framework. How can we access business methods in EJBs via Expression Language (EL)? How can we inject Entity data into the presentation layer and automatically validate input? Yes it's possible, but the classic JSF Managed Beans where really painful to use. JBoss Seam added this binding layer in form of an annotation based Dependency Injection (DI) framework. The addition of Web-relevant Contexts like Event (Request), Conversation, Session etc., the navigation and EL extensions, the Bean validation and much more resulted in a very powerful framework that just felt right.

JEE 6!

Many of this Seam-Core elements where standardized and integrated into JEE 6. The Context and Dependency Injection (CDI) is heavily influenced through Seam (and learned from past mistakes). Numerous JSF and JPA extensions have a counterpart in JBoss Seam 2.
Recently we switched for an ongoing project from JEE 5 / Seam 2 to JEE 6. The main reasons where security issues with older Application servers, performance issues with Seam and also functional aspects like validation groups in JSF. Even though I did know about the common roots of Seam 2 ideas and many JEE 6 innovations I was amazed how smooth the transition was.
Many things only needed minor adjustments. E.g. the JPA Enties should use the new Bean validation annotations (mostly only change the package name). We exchanged some Seam- against new JSF-components. Main work: The navigation rules must be reconfigured.

A (not exhaustive) replacement list...


Often there is really more to it than just a syntax replacement, e.g. @In and @Inject differ heavily. But on the surface you can start this way. Sry for the terrible Copy & Paste HTML:

JEE 5 / Seam 2 JEE 6
xmlns:c="http://java.sun.com/jstl/core" xmlns:c="http://java.sun.com/jsp/jstl/core"

(avoid JSTL in JSF, but sometimes interesting out of performance reasons if you know what you are doing)
xmlns:s="http://jboss.com/products/seam/taglib" (delete namespace 's', nearly for all of Seams JSF-Components now standard alternatives in JSF 2, see below)
xmlns:a="http://richfaces.org/a4j"

<a:support event="onchange" reRender="filter" />
(delete namespace 'a')


 xmlns:f="http://java.sun.com/jsf/core"

<f:ajax render=":filter"/>

(event-attribute isn't necessary in this case, the components default change-event is used; use <h:head> to render the proper jsf.js link)
<h:selectOneMenu value="#{...}">
  <s:selectItems label="#{_i.name}"
    value="#{userFilter.allOrgs}" var="_i"  noSelectionLabel="choose..." hideNoSelectionLabel="true"/>
</h:selectOneMenu>
<h:selectOneMenu value="#{...}" hideNoSelectionOption="true">
  <f:selectItem noSelectionOption="true" itemLabel="choose..." />
  <f:selectItems itemLabel="#{i_.name}"
    value="#{userFilter.allOrgs}" var="_i" />
</h:selectOneMenu>
<s:link includePageParams="false" 
    view="edit.xhtml" value="Edit"> 
    <f:param name="id" value="#{_user.id}" />
</s:link>
<h:link includeViewParams="false" 
    outcome="edit" value="Edit"> 
    <f:param name="id" value="#{_user.id}" />
</h:link>
<s:selectItems
    label
="#{messages[_action.name()]}" ... 
<s:convertEnum />
<f:selectItems
    itemLabel="#{messages[_action]}" ...

(delete s:convertEnum, global default converter for single enum values)

<template>.page.xml or pages.xml:

<param name="id" value="#{userHome.id}" />
<action execute="#{userHome.wire}" />

insert into <template>.xhtml:

<f:metadata>
    <f:viewParam name="id" value="#{userHome.id}" />
    <f:event type="preRenderView" listener="#{userHome.init()}" />
    <f:viewAction action="userHome.init()" /> (just since JSF 2.2)
</f:metadata>



import org.hibernate.annotations.Cache;
import org.hibernate.annotations
    .CacheConcurrencyStrategy;
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)

public class User extends BaseEntity {

import javax.persistence.Cacheable;
@Entity
@Cacheable
public class User extends BaseEntity {

(additionally add in persistence.xml: <shared-cache-mode>ENABLE_SELECTIVE </shared-cache-mode>)



import org.hibernate.validator.Length;
import org.hibernate.validator.NotEmpty;
@Length(max = 50)
@NotEmpty
private String name;

import javax.validation.constraints.NotNull;

import javax.validation.constraints.Size;
@NotNull
@Size(max = 50, min = 1)
private String name;

import org.jboss.seam.framework.EntityQuery;
@Name(userList")
public class UserList extends EntityQuery<User> {
(Seam 2 style list controller from seam-gen: don't use them, even with Seam 2)

import org.jboss.seam.annotations.In;
@In
private UserHome userHome;

@Out(required = false)
private User user;

@Factory(autoCreate = true, scope = ScopeType.APPLICATION)
public Map<String, String> getProps() {
    return PROPERTY_MAP;
}


import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces; 
import javax.inject.Inject;

@Inject
private UserHome userHome;

@Produces
@Named
@ApplicationScoped
public Map<String, String> getProps() {
    return PROPERTY_MAP;
}

@DataModel
private List<Veranstaltungstyp> userList;
... 
@DataModelSelection
private User selectedUser;

e.g. via @Produces, no "Outject" available:

private ListDataModel<User> userList;
...
User selectedUser = this.userList.getRowData();

@Name("userService")
@AutoCreate
@Stateless
@Local(UserService.class)
public class UserServiceBean implements UserService {
@Stateless
public class UserService {

(@Named only necessary if we use this in EL, all Stateful / Stateless / POJO Beans are automatically CDI Beans, default @Inject access via Type & optional Annotations, not via Named)
final UserService userService = (UserService) Component.getInstance("userService");
final UserService userService = CdiUtil.getSingleton(UserService.class);

(was often necessary in Seam 2 out of performance reasons with @BypassInterceptors and in many none-Seam-Components, now only in Filter/Entity if really necessary, helper class for static BeanManager e.g.: https://issues.jboss.org/secure/attachmentzip/unzip/12429820/12341024%5B26%5D/swe2Web/src/de/swe2/util/CdiUtil.java)

@Logger 
private static Log log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger LOG =
    LoggerFactory.getLogger(
        UserService.
class);

(in Seam 3 also possible "@Inject jboss.Logger log" but we really favor static access here, don't overdo injections)

@In
private FacesMessages facesMessages;

facesMessages.add(
    Severity.INFO,
    "Info info #{el.info} info.");

FacesContext.getCurrentInstance()
    .addMessage(

        "
<componentId>" (or null),
    new FacesMessage(
        FacesMessage.
SEVERITY_INFO,
        "
Info info info."));

(in Seam 3 also possible "@Inject Msg msg" but this cannot evaluate embedded EL yet and we favor own static wrapper here, like:)

FacesMessages.addInfo(
    "<componentId>", 
    "Info info info.");

#{identity.login}

@In
private Identity identity;

@Name("authenticator")
public class Authenticator {
    @In
    private Credentials credentials;
    @Override
    public boolean authenticate() {

with Seam 3 Security:

#{identity.login}

@Inject
private Identity identity;

@Named
@Stateless
public class Authenticator {
    @Inject 
    private Credentials credentials;
    @Override
    public void authenticate() {
<exception class="java.lang.IllegalStateException" log="false">
    <end-conversation />
    <redirect view-id="/error.xhtml" />
</exception>
see follow up post JSF 2 - Global Exception Handling


 

Seam 3?

I started with checking out the Seam 3 Booking example project (beware of PrettyFaces 3.2.0 in this example, it adds 500 ms CPU time to each request in development mode). I stripped the project down and build a new empty project template for us. During the conversion from Seam 2 to the new technology stack I noticed that we don't really need Seam anymore. There are some useful modules and ideas in Seam 3 but they are really optional and not a "must" like in JEE 5 days. Another interesting observation was that these extensions very often consists only of a few lines of code - CDI is _very_ powerful! The Seam Security Annotation tricks are amazing!

If Seam is a selling point for JBoss then I would say from a business perspective the standardization of Seam core was like shooting themselves in the foot. If not - congratulations for the great work!

Update 29.09.11

I kind of expected something like that: http://planet.jboss.org/post/so_what_s_happening_with_seam

Spring as CDI alternative?

Some people might say I should use Spring as binding layer between JSF and JPA but I really don't like Spring in the JEE environment. It's like deploying Glassfish into JBoss AS because I like Grizzly. IMHO this are two heavy overlapping technology worlds. Decide for one or the other but only mix if it's really really necessary. It's OK to use Spring inside JEE for something like a great embedded Spring-based Process Engine but really not for something like 50 code lines less for a JNDI wrapper or some "I didn't want to learn CDI" developer.

Friday, August 12, 2011

Oracle / PostgreSQL Datasource in JBoss AS 7

First follows a description for an Oracle RDBMS based datasource. The PostgreSQL variant works identical, the last section contains the necessary information.

Install Oracle JDBC Driver

We assume a ready-to-use Oracle connection URL and access data. You could e.g. install Oracle XE on your local machine. But beware: Then you have to switch the default port 8080 (or the interface) for your JBoss AS server because Oracle XE uses this port for a Web based administration console.

Download the Oracle JDBC Driver from here: Oracle Database 11g Release 2 (11.2.0.2.0) JDBC Drivers
(I use ojdbc6.jar for JDK 6.)

You have two options for installing the driver:
  • as Deployment
  • as Module
I prefer the second option but I  describe both here:

    Driver as Deployment

    Put ojdbc6.jar under .../jboss-as-7.2.0.Alpha1-SNAPSHOT/standalone/deployments.

    After some seconds the file "ojdbc6.jar.deployed" should appear in the same folder. JBoss AS 7 uses such files to trigger deployment events (e.g. create empty file "<artifact>.dodeploy") or to signal results (e.g. "<artifact>.deployed" or "<artifact>.failed"). You can also use the Web-based Admin console under http://localhost:8080/, but really...this thing is outright unusable and _years_ away from something like the Oracle WebLogic Server Admin console.

    Add  datasource in .../jboss-as-7.2.0.Alpha1-SNAPSHOT/standalone/configuration/standalone.xml
    (just like java:jboss/datasources/ExampleDS):
    <datasource jndi-name="java:/jdbc/testAppDS" pool-name="OracleDS" enabled="true" jta="true" use-java-context="true" use-ccm="true">
        <connection-url>
            jdbc:oracle:thin:@localhost:1521:XE
        </connection-url>
        <driver>
            ojdbc6.jar
        </driver>
        ...
    </datasource>
    Only if you need a 2PC / XA datasource you also must add a driver section in the datasource configuration (like H2):
    <driver name="ojdbc6.jar" module="">
        <xa-datasource-class>
            oracle.jdbc.xa.client.OracleXADataSource
        </xa-datasource-class>
    </driver>
    Restart the application server. You can and _should_ check the deployment state in the running application server. Use the command line:
    .../jboss-as-7.2.0.Alpha1-SNAPSHOT/bin/jboss-cli.bat
    You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
    [disconnected /] connect
    Connected to standalone controller at localhost:9999
    [standalone@localhost:9999 /] /subsystem=datasources:installed-drivers-list
    {
        "outcome" => "success",
        "result" => [
            {
                "driver-name" => "h2",
                "deployment-name" => undefined,
                "driver-module-name" => "com.h2database.h2",
                "module-slot" => "main",
                "driver-datasource-class-name" => "",
                "driver-xa-datasource-class-name" => "org.h2.jdbcx.JdbcDataSource",
                "driver-class-name" => "org.h2.Driver",
                "driver-major-version" => 1,
                "driver-minor-version" => 2,
                "jdbc-compliant" => true
            },
            {
                "driver-name" => "ojdbc6.jar",
                "deployment-name" => "ojdbc6.jar",
                "driver-module-name" => undefined,
                "module-slot" => undefined,
                "driver-datasource-class-name" => undefined,
                "driver-xa-datasource-class-name" => undefined,
                "driver-class-name" => "oracle.jdbc.OracleDriver",
                "driver-major-version" => 11,
                "driver-minor-version" => 2,
                "jdbc-compliant" => true
            }
        ]
    }
    [standalone@localhost:9999 /] /subsystem=datasources/data-source=java\:\/jdbc\/testAppDS:test-connection-in-pool
    {
        "outcome" => "success",
        "result" => [true]
    }
    [standalone@localhost:9999 /] quit
    Closed connection to localhost:9999

    You have done something wrong if you see this instead:
    [standalone@localhost:9999 /] /subsystem=datasources/data-source=java\:\/jdbc\/testAppDS:test-connection-in-pool
    {
        "outcome" => "failed",
        "failure-description" => "Operation handler failed: java.util.NoSuchElementException: \"data-source\" => \"java:/jdbc/testAppDS\"",
        "rolled-back" => true
    }
    In this case your Web app deployments (that use this datasource) will fail with this unwilling message:
    12:04:06,628 INFO  [org.jboss.as.server.controller] (DeploymentScanner-threads - 2) Deployment of "test.war" was rolled back with failure message Operation handler failed to complete
    12:04:06,629 ERROR [org.jboss.as.deployment] (DeploymentScanner-threads - 1) {"Composite operation failed and was rolled back. Steps that failed:" => {"Operation step-2" => "Operation handler failed to complete"}}
    12:04:06,632 ERROR [org.jboss.as.deployment] (DeploymentScanner-threads - 1) Composite operation was rolled back
    According to the documentation the driver deployment is the recommended option but I had some problems with it. Sometimes the driver spontaneously undeploys and I have to trigger a redeployment - not impossible but annoying. My database views where also much faster after the switch but I don't know if this was another effect (also upgraded JBoss AS to a newer build). So I prefer the module version:

    Driver as Module

    Add folder oracle/db/main under .../jboss-as-7.2.0.Alpha1-SNAPSHOT/modules/com. Put ojdbc6.jar and the following file module.xml under this folder:

    <module xmlns="urn:jboss:module:1.0" name="com.oracle.db">
        <resources>
            <resource-root path="ojdbc6.jar"/>
        </resources>
        <dependencies>
            <module name="javax.api"/>
            <module name="javax.transaction.api"/>
        </dependencies>
    </module>

    Add this to the datasource drivers section (see above standalone.xml):
    <datasource jndi-name="java:/jdbc/testAppDS" pool-name="OracleDS" enabled="true" jta="true" use-java-context="true" use-ccm="true">
        <connection-url>
            jdbc:oracle:thin:@localhost:1521:XE
        </connection-url>
        <driver>
            oracle
        </driver>
        <security>
            <user-name>user</user-name>
            <password>pwd</password>
        </security>
    
        <new-connection-sql>select * from dual</new-connection-sql>
        <validation>
            <check-valid-connection-sql>select * from dual</check-valid-connection-sql>
            <background-validation>true</background-validation>
            <background-validation-millis>10000</background-validation-millis>
        </validation>
        <timeout>
            <allocation-retry>1</allocation-retry>
        </timeout>
    </datasource>
        ...
    <drivers>
        <driver name="oracle" module="com.oracle.db">
            <xa-datasource-class>
                oracle.jdbc.xa.client.OracleXADataSource
            </xa-datasource-class>
        </driver>
    Now you should see this in the driver list (see above jboss-admin.bat):
            {
                "driver-name" => "oracle",
                "deployment-name" => undefined,
                "driver-module-name" => "com.oracle.db",
                "module-slot" => "main",
                "driver-datasource-class-name" => "",
                "driver-xa-datasource-class-name" => "oracle.jdbc.xa.client.OracleXADataSource",
                "driver-class-name" => "oracle.jdbc.OracleDriver",
                "driver-major-version" => 11,
                "driver-minor-version" => 2,
                "jdbc-compliant" => true
            }
    

    persistence.xml

    The persistence.xml of your Web application deployment is like usual:
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
     version="2.0">
     <persistence-unit name="database">
      <jta-data-source>java:/jdbc/testAppDS</jta-data-source>
      <properties>
       <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
       <property name="hibernate.hbm2ddl.auto" value="create-drop" />
       <property name="hibernate.show_sql" value="true" />
       <property name="hibernate.format_sql" value="false" />
       <property name="hibernate.use_sql_comments" value="true" />
      </properties>
     </persistence-unit>
    </persistence>
    

    Oracle11gDialect?

    Update: The following trick isn't necessary anymore with Hibernate Since 4.1 and JBoss 7.1.

    We use schema autocreation for development and Hibernate 4.0.0 trys to map boolean attributes to the datatype boolean. This type doesn't exist in Oracle DB.
    create table Organisation (id number(19,0) not null, aktiv boolean not null, ...
    There are different ways to fix this. We simply add this class to our Web app:
    package org.hibernate.dialect;
    
    import java.sql.Types;
    
    public class Oracle11gDialect extends Oracle10gDialect {
    
     public Oracle11gDialect() {
      registerColumnType(Types.BOOLEAN, "number(1,0)");
     }
    
    }
    And declare this as dialect in the persistence.xml:
       <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle11gDialect" />
    Add this as Maven dependency to your Web app (also good for debugging and some Hibernate annotations):
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.0.0.Beta5</version>
        <scope>provided</scope>
    </dependency>
    Now everything works just fine.

    PostgreSQL datasource with XA

    Add folder postgresql/main under .../jboss-as-7.2.0.Alpha1-SNAPSHOT/modules/org. Put postgresql-9.1-901.jdbc4.jar and the following file module.xml under this folder:
    <?xml version="1.0" encoding="UTF-8"?>
    <module xmlns="urn:jboss:module:1.1" name="org.postgresql">
        <resources>
            <resource-root path="postgresql-9.1-901.jdbc4.jar"/>
        </resources>
        <dependencies>
            <module name="javax.api"/>
            <module name="javax.transaction.api"/>
        </dependencies>
    </module>
    

    Add this to the datasource drivers section (see above standalone.xml):
    <xa-datasource jndi-name="java:/testAppDS" pool-name="PostgresDS" enabled="true" use-java-context="true">
        <xa-datasource-property name="ServerName">
            abc.de
        </xa-datasource-property>
        <xa-datasource-property name="PortNumber">
            5432
        </xa-datasource-property>
        <xa-datasource-property name="DatabaseName">
            dbname
        </xa-datasource-property>
        <driver>postgresql</driver>
        <security>
            <user-name>user</user-name>
            <password>pwd</password>
        </security>
    </datasource>
        ...
    <drivers>
        <driver name="postgresql" module="org.postgresql">
            <xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
        </driver>
    

    You might also be interested in

    Thursday, August 11, 2011

    Build JBoss Application Server 7.x

    JBoss AS 7 - Final?

    Recently we decided to give JBoss AS 7 a try. The first release was 7.0.0.Final and like expected there where some bugs. A major factor for the decision to evaluate JBoss AS 7 was not the 4 second startup time if you don't deploy or use anything (the main reason for this is lazy loading of modules on usage) but was the fact that JBoss switched back to a more open development. We really had a problem with the quality of JBoss AS 5.0/5.1/6.0 GA in combination with the changed Red Hat business model from the post-4.x era:
    General Availability (GA) releases are feature releases, not bugfix release.
    In opposition to this policy there will be a JBoss AS 6.1 bugfix release and we can now even find the development source repositories at GitHub (only GA source code was really open in the past, not even patches). Something has changed for good. After all we are not going to to use our heavily patched JBoss AS 4.2.3 for another 4 years and where already looking for alternatives.

    Checkout from Git


    The easiest solution to me for checking out code from a Git-based version control system is using Eclipse with the EGit plugin.
    • Help / Install new Software...
    • Work with: Indigo - http://download.eclipse.org/releases/indigo
    • Collaboration / Eclipse EGit
    • ...Install / Restart...
    • It works similar to the CVS / SVN plugins:
    • Open Perspective "Git Repository Exploring"
    • Clone a Git Repository...
    • URI: https://github.com/jbossas/jboss-as.git
    • master
    • adjust Directory path and Finish
    •  
       




        So this is simple and working - but we only checkout code here. I don't know how usable EGit is for everyday work after my experiences with the Eclipse Subversion Plugins. You can also download Git and use the shell commands but I had problems with Windows and my profile name (known MinGW problems).

          Building

          The JBoss AS build tool is Maven 3 and it's as easy as that:
          andre@entwsnb11 /cygdrive/D/Data/git/jboss-as
          $ mvn clean install -DskipTests=true
          You only need some time, especially for the first run because Maven must download all necessary dependencies. You can also download all sources for the used Maven dependencies for later debugging:
          $ mvn dependency:sources
          The ready to use Application Server is here:
          .../git/jboss-as/build/target/jboss-as-7.1.1.Final-SNAPSHOT
          Copy this folder to any location and call
          bin/standalone.bat
          The application server should be up and running after seconds: http://localhost:8080/

          If you have port conflicts (e.g. Oracle XE database blocks 8080 with an admin web app...oh my) than you can change ports here:
          ...jboss-as-7.../standalone/configuration/standalone.xml
          You will notice that JBoss AS 7 differs heavily from previous versions. It has a new folder and configuration structure and you will find nothing where you expect it. This could be a problem for your administrators and should be considered. I would say switching from JBoss 4/5/6 to 7 is like switching from JBoss AS to Glassfish or other products.

          Start via Eclipse


          We use the Eclipse plugins from JBoss Tools for web development (in addition to Eclipse JEE / WTP). This plugins have nice support for JBoss Seam and CDI / Weld. The current milestone 3.3.0 (JBoss Tools - Core 3.3.0.M5 :: Eclipse 3.7.1) supports JBoss AS 7.1 as runtime environment.

          Yo can register the server as usual and start it. An interesting observation is that JBoss AS 7.0.0.Final is endlessly starting and at sometime the startup  timeout triggers. There seem to be problems with the management port 9999. I hadn't this problems with this own build - so this is one problem we can (unexpectedly) resolve with this own build.

          Certainly you shouldn't use this in production yet. If you have a release date at least 6 month from now on, than this may be an interesting option.



          Building JBoss App Server isn't a "kudos to the great wizard" anymore. That was easy - not even worth a blog post. I think sometimes people only have to see how astonishing simple some things are.

          You might also be interested in

          • Oracle Datasource in JBoss AS 7 - The next step could be the configuration of a JDBC data source. This Oracle DB specific description works for other DBs too.