How to display maven project version in your webapp

Overview

Sometimes it is useful to display the version of your web application, typically in the footer area of the web page. By displaying the version information, one can quickly determine which version of the app is running or users can use it for error reporting purposes. Displaying the version is also useful when there are frequent automated releases using the maven release plugin and it is hard to track which version is actually deployed on a server. Here’s an illustration of how version can be displayed in the footer.

Project Version shown in footer

This article describes how to display the version of your maven project in your web application.

Step 1: Create a View file to display the version

First we will create a view file which will display the version. The view file could be a JSP, HTML, XHTML, a Facelet or any format depending on your stack. In this example, we will create a JSP file which will print the version somewhere in the page. However, instead of the hardcoding an actual version, we put a special token ‘PROJECT_VERSION’. We don’t want to hard code the version information as it will change from release to release. The special token will be replaced with actual maven project version when the app is packaged.

Create the following jsp.

src/main/webapp/version.jsp

Here’s a simple file which displays the version information.

<html>
  <body>Project version is <i>PROJECT_VERSION</i></body>
</html>

Step 2: Add snippet to pom.xml

The maven replacer plugin is a simple and useful plugin which can replaces tokens in file of your choice. Add the following snippet of code to your pom.xml. (Note: you can also use the maven resources plugin’s filter feature to accomplish this).

<project>
 <build>
  <plugins>
   <!-- replace version in file -->
   <plugin>
    <groupId>com.google.code.maven-replacer-plugin</groupId>
    <artifactId>maven-replacer-plugin</artifactId>
    <version>1.3.2</version>
    <executions>
     <execution>
      <!-- the replace should happen before the app is packaged -->
      <phase>prepare-package</phase>
      <goals>
       <goal>replace</goal>
      </goals>
     </execution>
    </executions>

    <configuration>
     <includes>
      <!-- replace the token in this file -->
      <include>target/myproject/version.jsp</include>
     </includes>
     <regex>false</regex>
     <!-- the name of the token to replace -->
     <token>PROJECT_VERSION</token>
     <!-- replace it with the maven project version -->
     <value>${project.version}</value>
    </configuration>
   </plugin>
  </plugins>
 </build>
</project>

To test the above setup, you can run the following command:

mvn prepare-package

Navigate to the following file.

target/myproject/version.jsp

The contents of this file will look something like this (assuming that your project version is 1.0-SNAPSHOT):

<html>
  <body>Project version is <i>1.0-SNAPSHOT</i></body>
</html>

When your application ships, the version.jsp file will always have the correct version. You can see the version by invoking the version.jsp page.

http://localhost:8080/myproject/version.jsp

Note:

  • Your view file does not have to be a JSP, it can be any a view file suitable to your stack. It could be part of your footer which could be included at the bottom in each page.
  • The special token can be changed from PROJECT_VERSION to another value of your choice, just change it in the view file as well as the pom.xml
  • Instead of having the version contained in a view file, you could have the version in a properties file or a message bundle and then display a message from the message bundle on the view
  • Update – you can also accomplish the replacement using the filtering feature of maven resource plugin

References

How to copy bean properties with a single line of code

This article shows how to copy multiple properties from one bean to another with a single line of code, even if the property names in the source and target beans are different.

Copying properties from one bean is quite common especially if you are working with a lot of POJOs, for example working with JAXB objects. Lets walk through the following example where we want to copy all properties from the User object -> SystemUser object

Example: source and target objects

// source object that we want to copy the properties from
public class User {
    private String first;
    private String last;
    private String address1;
    private String city;
    private String state;
    private String zip;
    private String phone;

    // getters and setters
    // ...
}

// target object that we want to copy the properties to
public class SystemUser {
    private String firstName;
    private String lastName;
    private String phone;
    private String addressLine1;
    private String addressLine2;
    private String city;
    private String state;
    private String zip;

    // getters and setters
    // ...
}

Example continued: Preparing the objects

// initializing the source object with example values
User user = new User();
user.setFirst("John");
user.setLast("Smith");
user.setAddress1("555 Lincoln St");
user.setCity("Washington");
user.setState("DC");
user.setZip("00000");
user.setPhone("555-555-5555");

// creating an empty target object
SystemUser systemUser = new SystemUser();

Approach 1: Traditional code for copying properties

systemUser.setFirstName(user.getFirst());
systemUser.setLastName(user.getLast());
systemUser.setPhone(user.getPhone());
systemUser.setAddressLine1(user.getAddress1());
systemUser.setAddressLine2(user.getAddress2());
systemUser.setCity(user.getCity());
systemUser.setState(user.getState());
systemUser.setZipcode(user.getZip());

Approach 2: Single line code for copying properties

copyProperties(user, systemUser, "first firstName", "last lastName", "phone",
               "address1 addressLine1", "address2 addressLine2", "city", "state", "zip zipcode");

Parameters

  1. user – the source object
  2. systemUser – the target object
  3. first firstName – indicates that the “first” property of the source object should be copied to the “firstName” property of the target object
  4. last lastName – indicates that the “last” property of the source object should be copied to the “lastName” property of the target object
  5. phone – indicates that the “phone” property of the source object should be copied to the “phone” property of the target object
  6. …. and so on

The underlying code uses Apache Commons BeanUtils code to copy the property value from the source to the destination.

You can download the copyProperties code from Google Code. Simply copy the code to your project. The code requires apache BeanUtils. If you are using maven, you can get BeanUtils by adding the following to your pom.xml

 <!-- for maven only, add this to your pom to get BeanUtils, needed by the copyProperties code -->
 <dependency>
   <groupId>commons-beanutils</groupId>
   <artifactId>commons-beanutils</artifactId>
   <version>1.8.3</version>
 </dependency>

References

Calling a static method from EL

In my previous post I showed how to pass parameters in EL methods. In this post, I will describe how to call static methods from EL.

Step 1: Download Call.java and ELMethod.java

Download the classes Call.java and ELMethod.java, and copy them to your project.

Step 2: Add an instance of Call.java as an application scoped bean

There are many ways to do this, depending on this technology you are using. Here are some examples:

How to set request scope in JSP

request.setAttribute("call", new Call());

How to set session scope in JSP

session.setAttribute("call", new Call());

How to set application scope from Servlet

this.getServletContext().setAttribute("call", new Call())

How to set application scope from JSP

<jsp:useBean id="call" class="Call" scope="application" />

How to set application scope in Seam

Add these annotations to your class

@Name("call")
@Scope(ScopeType.APPLICATION)

Step 3: Call any static method from EL as follows

Let’s say you have a static method which formats date. The method takes 2 arguments, the date format string and the Date object to format.

package com.mycompany.util;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtils {
 public static String formatDate(String format, Date date) {
   return new SimpleDateFormat(format).format(date);
 }
}

You can call the above static method from EL as follows. In the example below, “account” is a request scoped bean with a Date property called “creationDate” which needs to be formatted.

  ${call["com.mycompany.util.DateUtils.formatDate"]["MMM, dd"][account.creationDate]}

Just like the above example, you can call any static method (except overloaded methods). Simply pass the full class and method name as the first argument, and any parameters required by the method as subsequent arguments. In general, the format is:

  ${call["full.package.name.MethodName"][arg1][arg2][...]}

Note:

  • Use map notation with [square brackets] when passing arguments to ${call}
  • First argument is the full package name + “.” + method name
  • If the static method you are calling takes arguments, simply pass those arguments using more [square brackets]
  • Overloaded methods are not supported, i.e. you cannot call methods where two methods with the same name exist in the class

References

How to pass parameters in EL methods

This article describes how to pass any number of parameters in EL functions in 3 simple steps.

Step 1: Extend the class ELMethod

Extend the class ELMethod.java, and implement the following method:

public abstract Object result(Object[] args);

For example, say we want to create a formatDate method which takes 2 arguments

  1. the format String
  2. the Date to be formatted

All you need to do is extend the ELMethod class and implement the result(Object[] args) method.

Example

import java.text.SimpleDateFormat;
import java.util.Date;

public class FormatDate extends ELMethod {
 private static final long serialVersionUID = 1L;

 public FormatDate() {
   // number of arguments to the result method
   super(2);
 }

 public Object result(Object[] args) {
   String pattern = (String) args[0];
   Date date = (Date) args[1];

   return new SimpleDateFormat(pattern).format(date);
 }
}

Step 2: Add an instance of you class as an application scoped bean

There are many ways to do this, depending on this technology you are using. Here are some examples:

Set request scope in JSP

request.setAttribute("formatDate", new FormatDate());

Set session scope in JSP

session.setAttribute("formatDate", new FormatDate());

Set application scope from Servlet

this.getServletContext().setAttribute("formatDate", new FormatDate())

Set application scope from JSP

<jsp:useBean id="formatDate" class="FormatDate" scope="application" />

Set application scope in Seam

Add these annotations to your class

@Name("formatDate")
@Scope(ScopeType.APPLICATION)

Step 3: Call the function via map syntax

Simply pass each argument using the map syntax [] as follows

EL code

${formatDate["MMM, dd"][account.creationDate]}

All arguments passed via EL are sent to the result(Object[] args) method of the FormatDate class.

How does it work

Each argument is collected via the get(Object key) method and is added to an array. When all the arguments have been specified the result method is invoked and the resulting value is returned.

Reference

  • Source code of ELMethod.java on my Google Code project. This class is thread safe and can be application scoped.
  • FormatDate.java example on my Google Code project
  • Add.java example, which adds 2 numbers in EL (e.g. ${add[1][2]})

How to automatically recover Tomcat from crashes

Tomcat occasionally crashes if you do frequent hot-deploys or if you are running it on a machine with low memory. Every time tomcat crashes someone has to manually restart it, so I wrote a script which automatically detects that tomcat has crashed and restarts it.

Here’s the pseudo logic:

every few minutes {
  check tomcat status;

  if (status is "not running") {
    start tomcat;
  }
}

Here’s a shell script to implement the above logic. It assumes that you are running on a unix/linux system and have /etc/init.d/tomcat* script setup to manage tomcat.

Adjust the path to “/etc/init.d/tomcat” in the script below to reflect the correct path on your computer. Sometimes it is called /etc/init.d/tomcat5 or /etc/init.d/tomcat6 depending on your tomcat version. Also make sure that the message “Tomcat Servlet Container is not running.” matches with the message that you get when you run the script when tomcat is stopped.

#! /bin/sh
SERVICE=/etc/init.d/tomcat
STOPPED_MESSAGE="Tomcat Servlet Container is not running."

if [ "`$SERVICE status`" == "$STOPPED_MESSAGE"];
then
{
  $SERVICE start
}
fi

To run the script every 10 minutes:

1. Save the above script to “/root/bin/recover-tomcat.sh”.

2. Add execute permission:

chmod +x /root/bin/recover-tomcat.sh

3. Add this to root’s crontab, type the following as root:

crontab -e

4. Add the following lines to the crontab:

# monitor tomcat every 10 minutes
*/10 * * * * /root/bin/recover-tomcat.sh

What if I don’t have /etc/init.d/tomcat* script on my computer?

Tomcat creates a pid file, typically in the TOMCAT_HOME/bin directory. This file contains the process id of the tomcat process running on the machine. The pseudo logic in that case would be:

if (the PID file does not exist) {
  // conclude that tomcat is not running
  start tomcat
}
else {
  read the process id from the PID file
  if (no process that id is running) {
    // conclude that tomcat has crashed
    start tomcat
  }
}

You can implement the above logic as follows. The following is experimental and is merely a suggested way, test it on your computer before using it.

# adjust this to reflect tomcat home on your computer
TOMCAT_HOME=/opt/tomcat5

if [ -f $TOMCAT_HOME/bin/tomcat.pid ]
then
  echo "PID file exists"
  pid="`cat $TOMCAT_HOME/bin/tomcat.pid`"
  if [ "X`ps -p $pid | awk '{print $1}' | tail -1`" = "X"]
  then
    echo "Tomcat is running"
  else
    echo "Tomcat had crashed"
    $TOMCAT_HOME/bin/startup.sh
  fi
else
  echo "PID file does not exist. Restarting..."
  $TOMCAT_HOME/bin/startup.sh
fi

Why would tomcat crash?

The most common reason is low memory. For example, if you have allocated 1024MB of max memory to tomcat and enough memory is not available on that machine. Other reasons may involve repeated hot-deploys causing memory leaks, rare JVM bugs causing the JVM to crash.

Getting started with Nexus Maven Repo Manager

Overview

This tutorial outlines steps required to install Nexus (Maven Repository Manager) under Tomcat, or another webapp container. It shows you practical configuration and includes code snippets that go in your pom.xml and settings.xml in order to read and publish artifacts to your Nexus server.

Step 1: Download

Download Nexus from here (at the time of writing, latest is 1.6.0)

Step 2: Install

Copy the war to TOMCAT_HOME/webapps/nexus.war

Though not required, it is a generally good idea to restart tomcat after installing a new war.

/etc/init.d/tomcat restart

Step 3: Configure security

a) Change default admin password: The default admin username/password is admin/admin123. Login as admin and change the password to a secure password.

Login -> [admin, admin123] -> Left Menu -> Security -> Change Password -> click “Change Password”

b) Anonymous Access: By default Nexus is open to the public. If you want to secure access to nexus, disable ‘Nexus anonymous user’

Admin -> Left Menu -> Users -> ‘Nexus anonymous user’ -> Status=Disabled

c) Deployment user: Change password for deployment user

Admin -> Left menu -> Users -> Deployment user -> Change email address

Admin -> Left menu -> Users -> Right click on ‘Deployment user’ in the user list -> Set Password -> click ‘Set password’ to finish

Step 4: Set SMTP server

It is a good idea to configure SMTP server, so that you can receive emails from Nexus.

Admin login -> Left menu -> Administration -> Server ->SMTP Settings -> (host localhost, port 25, no login, no password mostly works on a linux machine)

Step 5: Change Base Url

If you are running Nexus behind Apache using mod_jk or mod_proxy, change your base url here.

Admin login -> Left menu -> Administration -> Server -> Application Server Settings -> Base url

Step 6: Add a task to periodically remove old snapshots

If you or your CI server publishes snapshots to Nexus several times a day, then you should consider adding a task to delete duplicate/old snapshots for the same GAV (group, artifact, version). If you don’t do this, you will notice that the Nexus disk usage will increase with time.

Admin login -> Left menu -> Administration -> Scheduled tasks -> Add… -> name=”Remove old snapshots”, Repository/Group=Snapshots (Repo), Minimum Snapshot Count=1, Snapshot Retention(days)=3, Recurrence=Daily, Recurring time=2:00 -> click ‘Save’

Step 7: Using Nexus: reading and publishing artifacts

If you want to deploy your artifacts to your Nexus, you need to configure 2 files: pom.xml and settings.xml

a) pom.xml – for each project which wishes to publish to Nexus, add your repo to the pom.xml

<distributionManagement>
 <!-- Publish the versioned releases here -->
 <repository>
  <id>vineetmanohar-nexus</id>
  <name>vineetmanohar nexus</name>
  <url>dav:http://nexus.vineetmanohar.com/nexus/content/repositories/releases</url>
 </repository>

 <!-- Publish the versioned releases here -->
 <snapshotRepository>
  <id>vineetmanohar-nexus</id>
  <name>vineetmanohar nexus</name>
  <url>dav:http://nexus.vineetmanohar.com/nexus/content/repositories/snapshots</url>
 </snapshotRepository>
</distributionManagement>

<!-- download artifacts from this repo -->
<repositories>
 <repository>
  <id>vineetmanohar-nexus</id>
  <name>vineetmanohar</name>
  <url>http://nexus.vineetmanohar.com/nexus/content/groups/public</url>
  <releases>
   <enabled>true</enabled>
  </releases>

  <snapshots>
   <enabled>true</enabled>
  </snapshots>
 </repository>
</repositories>

<!-- download plugins from this repo -->
<pluginRepositories>
 <pluginRepository>
  <id>vineetmanohar-nexus</id>
  <name>vineetmanohar</name>
  <url>http://nexus.vineetmanohar.com/nexus/content/groups/public</url>
  <releases>
   <enabled>true</enabled>
  </releases>
  <snapshots>
   <enabled>true</enabled>
  </snapshots>
 </pluginRepository>
</pluginRepositories>

b) settings.xml – If you have disabled anonymous access to Nexus, add the deployment password to your ~/.m2/repository/settings.xml file

<settings>
 <servers>
  <server>
   <!-- this id should match the id of the repo server in pom.xml -->
   <id>vineetmanohar-nexus</id>
   <username>deployment</username>
   <password>password_goes_here</password>
  </server>
 </servers>
</settings>

What is Google TV … in 2 minutes

Google TV is a Smart TV (or set-top box) which aims to seamlessly combine your Internet and TV experience. It runs Android OS, Chrome browser and Flash 10. Third party developers can create interesting applications and make them available to end users via an App marketplace.

Google TV aims to take TV viewing experience to the next level, similar to what smart phones (Android, iPhone) did for phones. The key here, in my opinion, is creating a framework and open up the TV-internet application space to third-party developers.

Summary

  • Google TV was announced at 2010 Google I/O, in stores Fall 2010
  • Google has partnered with Sony (TVs), Intel (atom processor), Best Buy (sales to end users), Dish Network (Set-top box), Logitech (peripherals) and Adobe (Flash player)
  • Google TV units will be available (in form of TVs and/or Set-top boxes) this fall, well in time for the Christmas season
  • Runs Android OS, Chrome Browser, Flash 10
  • Third party developers can write apps and users can download apps via a Marketplace
  • End users can surf channels on TV as they normally do, and also access relevant internet related apps like Youtube, Picasa and discover apps written by third party developers
  • Sign up here to receive updates about Google TV

2 ways to convert Java Map to String

This article shows 2 ways to convert Java Map to String.

  • Approach 1: simple, lightweight – produces query string like output, but restrictive.
  • Approach 2: uses Java XML bean serialization, more robust but produces overly verbose output.

Approach 1: Map to query string format

Approach 1 converts a map to a query-string like output. Here’s what an output looks like:

name1=value1&name2=value2

Full code:

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

public class MapUtil {
 public static String mapToString(Map<String, String> map) {
   StringBuilder stringBuilder = new StringBuilder();

   for (String key : map.keySet()) {
    if (stringBuilder.length() > 0) {
     stringBuilder.append("&");
    }
    String value = map.get(key);
    try {
     stringBuilder.append((key != null ? URLEncoder.encode(key, "UTF-8") : ""));
     stringBuilder.append("=");
     stringBuilder.append(value != null ? URLEncoder.encode(value, "UTF-8") : "");
    } catch (UnsupportedEncodingException e) {
     throw new RuntimeException("This method requires UTF-8 encoding support", e);
    }
   }

   return stringBuilder.toString();
  }

  public static Map<String, String> stringToMap(String input) {
   Map<String, String> map = new HashMap<String, String>();

   String[] nameValuePairs = input.split("&");
   for (String nameValuePair : nameValuePairs) {
    String[] nameValue = nameValuePair.split("=");
    try {
     map.put(URLDecoder.decode(nameValue[0], "UTF-8"), nameValue.length > 1 ? URLDecoder.decode(
     nameValue[1], "UTF-8") : "");
    } catch (UnsupportedEncodingException e) {
     throw new RuntimeException("This method requires UTF-8 encoding support", e);
    }
   }

   return map;
  }
}

Example usage code

 Map<String, String> map = new HashMap<String, String>();
 map.put("color", "red");
 map.put("symbols", "{,=&*?}");
 map.put("empty", "");
 String output = MapUtil.mapToString(map);
 Map<String, String> parsedMap = MapUtil.stringToMap(output);
 for (String key : map.keySet()) {
  Assert.assertEquals(parsedMap.get(key), map.get(key));
 }

Output with Approach 1

symbols=%7B%2C%3D%26*%3F%7D&color=red&empty=

Caveat

  • Only supports String keys and values.
  • Due to the nature of serialization, null keys and values are not supported. Null will be converted to an empty String. This is because there is no way to distinguish between a null and an empty String in the serialized form. If you need support for null keys and values, use java.beans.XMLEncoder as shown below.

Approach 2: Java Bean XMLEncoder: Map to String

Java provides XMLEncoder and XMLDecoder classes as part of the java.beans package as a standard way to serialize and deserialize objects. This

 Map<String, String> map = new HashMap<String, String>();
 map.put("color", "red");
 map.put("symbols", "{,=&*?}");
 map.put("empty", "");
 ByteArrayOutputStream bos = new ByteArrayOutputStream();
 XMLEncoder xmlEncoder = new XMLEncoder(bos);
 xmlEncoder.writeObject(map);
 xmlEncoder.flush();

 String serializedMap = bos.toString()
 System.output.println(serializedMap);

Output with Approach 2

The serialized value is shown below. As you can see this is more verbose, but can accommodate different data types and null keys and values.

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.5.0_17">
 <object>
  <void method="put">
   <string>symbols</string>
   <string>{,=&amp;*?}</string>
  </void>
  <void method="put">
   <string>color</string>
   <string>red</string>
  </void>
  <void method="put">
   <string>empty</string>
   <string></string>
  </void>
 </object>

Java Bean XMLDecoder: String to Map

 XMLDecoder xmlDecoder = new XMLDecoder(new ByteArrayInputStream(serializedMap.getBytes()));
 Map<String, String> parsedMap = (Map<String, String>) xmlDecoder.readObject();

 for (String key : map.keySet()) {
  Assert.assertEquals(parsedMap.get(key), map.get(key));
 }

Summary

While Java provides a standard (and overly verbose) way to serialize and deserialize objects, this articles discusses an alternative lightweight way to convert a Java Map to String and back. If you are serializing a map with non-null String keys and values, then you should be able to use this alternative way, otherwise use the Java bean serialization.

How to configure multiple page.xml files in Seam 2.2

Overview

Seam lets you specify navigation rules in the pages.xml file which can become very large for apps with many pages and transitions. Seam allows you to break this file into multiple small files. This allows you to easily manage large applications by creating multiple pages.xml files, typically one per page. This article describes how to create multiple pages.xml files in Seam 2.2.

Step 1: Configure /WEB-INF/components.xml

Make sure you declare the navigation namespace at the top of components.xml and each file under <navigation:pages> as follows

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
 xmlns:core="http://jboss.com/products/seam/core" xmlns:persistence="http://jboss.com/products/seam/persistence"
 xmlns:drools="http://jboss.com/products/seam/drools" xmlns:bpm="http://jboss.com/products/seam/bpm"
 xmlns:security="http://jboss.com/products/seam/security" xmlns:mail="http://jboss.com/products/seam/mail"
 xmlns:web="http://jboss.com/products/seam/web" xmlns:navigation="http://jboss.com/products/seam/navigation"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.2.xsd
 http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.2.xsd
 http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.2.xsd
 http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.2.xsd
 http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.2.xsd
 http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.2.xsd
 http://jboss.com/products/seam/navigation http://jboss.com/products/seam/navigation-2.2.xsd
 http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.2.xsd
 http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd">

 <mail:mail-session host="localhost" tls="false" ssl="false" port="25" />

 <component>
  <property name="createTempFiles">true</property>
  <property name="maxRequestSize">1000000000</property>
 </component>

 <component name="org.jboss.seam.core.init">
  <!-- enabling this slows down Seam significantly -->
  <property name="debug">false</property>
 </component>

 <core:init transaction-management-enabled="false" />

 <security:identity authenticate-method="#{loginController.authenticate}"
  remember-me="true" />

 <event type="org.jboss.seam.security.notLoggedIn">
  <action execute="#{redirect.captureCurrentView}" />
 </event>

 <event type="org.jboss.seam.security.postAuthenticate">
  <action execute="#{redirect.returnToCapturedView}" />
 </event>

 <navigation:pages>
  <navigation:resources>
   <!-- global -->
   <value>/WEB-INF/pages.xml</value>

   <!-- one per page -->
   <value>/WEB-INF/createAccount.page.xml</value>
   <value>/WEB-INF/login.page.xml</value>
  </navigation:resources>
 </navigation:pages>
</components>

Step 2. Save all .page.xml files in WEB-INF directory

WEB-INF/createAccount.page.xml
WEB-INF/login.page.xml

Step 3. Create the .page.xml files.

See the sample file: createAccount.page.xml below. Note that the root tag is <pages>. Make sure that the same page is not repeated in another file.

<?xml version="1.0" encoding="UTF-8"?>
<pages xmlns="http://jboss.com/products/seam/pages" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd">

 <page view-id="/createAccount.xhtml" login-required="false">
 <!-- Create account -->
 <navigation
  from-action="#{createAccountController.createAccountFormCreateAccount}">

   <!-- Account created -->
   <rule if-outcome="accountCreated">
    <redirect view-id="/login.xhtml">
     <message>Your account has been created. Please log in.</message>
    </redirect>
   </rule>
  </navigation>
 </page>
</pages>

FAQ

Can I use Seam 2.0 style <view id>.page.xml files?

You should be able to use viewid.page.xml xml files according to the examples that ship with Seam 2.2.0. However, some users have complained that it doesn’t work in Seam 2.1. I could not get it to work with Seam 2.2.0 as well.

Spring classpath scan breaks when migrating from JBoss4 to JBoss5

Spring 2.5 classpath scan does not work on JBoss5 due to internal architecture changes in JBoss5. You may face this issue when migrating from JBoss4 to JBoss5. This article walks you through a simple fix, with code snippets.

Classpath scan issue

In JBoss 4, your application was probably using Spring 2.5.6 with classpath scan to load spring beans (as shown below). When migrating your code to JBoss5, you will notice that component-scan does not work with JBoss5. Symptoms may include null Spring components or if you were using Spring Seam integration and the Spring beans were loaded through classpath scan then you may be the following error:

Seam Code

public class LoginController extends Controller {
    @In("#{userService}")
    protected demo.entity.service.UserService userService;

Stacktrace

Caused by: org.jboss.seam.RequiredException: @In attribute requires non-null value: loginController.#{userService}
 at org.jboss.seam.Component.getValueToInject(Component.java:2335)
 at org.jboss.seam.Component.injectAttributes(Component.java:1736)
 at org.jboss.seam.Component.inject(Component.java:1554)
 at org.jboss.seam.core.BijectionInterceptor.aroundInvoke(BijectionInterceptor.java:61)
 at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)

Spring 2.5.6 dependency in pom.xml

 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring</artifactId>
 <version>2.5.6</version>
 </dependency

Old spring config XML

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd


http://www.springframework.org/schema/tx


http://www.springframework.org/schema/tx/spring-tx-2.5.xsd


http://www.springframework.org/schema/context

 http://www.springframework.org/schema/context/spring-context-2.5.xsd"
 default-init-method="init" default-destroy-method="shutdown">
 <context:annotation-config />
 <context:component-scan base-package="demo.dao.jpa" />
</beans>

If you run the above code in JBoss5, component-scan does not work. See this JIRA issue on Spring’s website.

Solution

You need to migrate your spring version from 2.5.6 to 3.0.0.RELEASE. Remove your old spring dependency for 2.5.6 and add the following. Add snippets for the components that your application needs. Here’s a list of common spring components and their maven dependencies.

New dependency

 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-orm</artifactId>
 <version>3.0.0.RELEASE</version>
 </dependency>

 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-web</artifactId>
 <version>3.0.0.RELEASE</version>
 </dependency>

 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-tx</artifactId>
 <version>3.0.0.RELEASE</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-test</artifactId>
 <version>3.0.0.RELEASE</version>
 <scope>test</scope>
 </dependency>

Complete list is available here. After making this change, build your project with the new dependencies. Your old code should work with Spring 3 on JBoss 5.1.0.GA.

public abstract class GeneratedLoginController extends Controller {
@In(“#{userDao}”)
protected demo.dao.UserDao userDao;

@In(“#{userService}”)
protected demo.entity.service.UserService userService;

Get Adobe Flash playerPlugin by wpburn.com wordpress themes