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.
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
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
first firstName – indicates that the “first” property of the source object should be copied to the “firstName” property of the target object
last lastName – indicates that the “last” property of the source object should be copied to the “lastName” property of the target object
phone – indicates that the “phone” property of the source object should be copied to the “phone” property of the target object
…. 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>
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.
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:
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
the format String
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())
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.
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:
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.
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>
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.
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
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.
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.
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
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 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)
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.
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;