RSS Entries RSS
RSS Subscribe by Email

Hibernate with JPA Annotations and Guice

In this tutorial, I use the H2 database, which you can utilize by downloading the jar or you can replace with your own DB.

First off, you need to create a persistence.xml in a folder named META-INF at the root of your classpath. For example, if you’re using Maven you can create src/main/java/META-INF/persistence.xml. It lists all the classes you want Hibernate to persist and any other Hibernate properties you might want to specify. I also define Hibernate properties later on in the tutorial when creating the EntityManagerFactory.

<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="db-manager">
    <class>com.benmccann.db.ExamplePersistedClass</class>
    <properties>
      <!-- Disable the second-level cache  -->
      <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>

      <!-- Default is false for backwards compatibility.  Should be used on all new projects -->
      <property name="hibernate.id.new_generator_mappings" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

We listed the class com.benmccann.db.ExamplePersistedClass as being the only persisted class, so now we’ll go ahead and create it. Note that you need a no-argument constructor. Fields in your bean will be persisted unless annotated with @Transient. More info about persisted classes is available in the Hibernate documentation.

package com.benmccann.db;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class ExamplePersistedClass {

  @Id
  @GeneratedValue
  private Long id;

  private String otherField;

  public ExamplePersistedClass() {}

  public void setId(Long id) {
    this.id = id;
  }

  public Long getId() {
    return id;
  }

  public void setOtherField(String otherField) {
    this.otherField = otherField;
  }

  public String getOtherField() {
    return otherField;
  }

}

And we’ll create a DAO for it:

package com.benmccann.db;

import javax.persistence.EntityManager;

public class ExamplePersistedClassDao {

  protected EntityManager entityManager;

  @Inject
  public ExamplePersistedClassDao(EntityManager entityManager) {
    this.entityManager = entityManager;
  }

  public void saveInNewTransaction(ExamplePersistedClass object) {
    entityManager.getTransaction().begin();
    save(object);
    entityManager.getTransaction().commit();
  }

  public void save(ExamplePersistedClass object) {
    entityManager.persist(object);
  }

  public ExamplePersistedClass getByOtherField(String otherField) {
    return (ExamplePersistedClass) entityManager
        .createQuery("select e from ExamplePersistedClass e where e.otherField=:otherField")
        .setParameter("otherField", otherField)
        .getSingleResult();
  }

}

And finally, we’ll create a Guice module to define the injection. Note that EntityManagerFactory is thread-safe while EntityManager is not. I use ThreadLocal storage here in order to have one entity manager per thread. When creating the EntityManagerFactory notice that I use the same “db-manager” string that is defined in the persistence.xml file. Also, I defined most of my Hibernate properties here rather than in the persistence.xml file to demonstrate how you can alter the values of these properties. For example, when writing tests it could be very helpful to override the connection string to point to a local test database.

package com.benmccann.db;

import java.util.HashMap;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;

/**
 * @author Ben McCann (benmccann.com)
 */
public class DbModule extends AbstractModule {

  private static final ThreadLocal ENTITY_MANAGER_CACHE
      = new ThreadLocal();

  public void configure() {
  }

  @Provides @Singleton
  public EntityManagerFactory provideEntityManagerFactory() {
    Map properties = new HashMap();
    properties.put("hibernate.connection.driver_class", "org.h2.Driver");
    properties.put("hibernate.connection.url", "jdbc:h2:test");
    properties.put("hibernate.connection.username", "sa");
    properties.put("hibernate.connection.password", "");
    properties.put("hibernate.connection.pool_size", "1");
    properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
    properties.put("hibernate.hbm2ddl.auto", "create");
    return Persistence.createEntityManagerFactory("db-manager", properties);
  }

  @Provides
  public EntityManager provideEntityManager(EntityManagerFactory entityManagerFactory) {
    EntityManager entityManager = ENTITY_MANAGER_CACHE.get();
    if (entityManager == null) {
      ENTITY_MANAGER_CACHE.set(entityManager = entityManagerFactory.createEntityManager());
    }
    return entityManager;
  }

}

Now we can run our code:

package com.benmccann.db;

import java.sql.SQLException;
import java.util.Date;

import org.junit.Assert;
import org.junit.Test;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.benmccann.db.DbModule;
import com.benmccann.db.ExamplePersistedClass;
import com.benmccann.db.ExamplePersistedClassDao;

public class H2DBTest {

  @Test
  public void testDb() throws SQLException {
    Injector injector = Guice.createInjector(new DbModule());
    ExamplePersistedClassDao examplePersistedClassDao = injector.getInstance(ExamplePersistedClassDao.class);   

    ExamplePersistedClass example = new ExamplePersistedClass();
    example.setOtherField("hello world");
    examplePersistedClassDao .saveInNewTransaction(quote);

    ExamplePersistedClass retrieved = examplePersistedClassDao.getByOtherField("hello world");

    Assert.assertEquals(example.getId(), retrieved.getId());
    Assert.assertEquals(example.getOtherField(), retrieved.getOtherField());
  }

}

Comments

openSUSE 11.2 Setup and Review

openSUSE is my favorite Linux distribution.  Linux in general has some usability frustrations as a desktop user, so I hope to share some of the ways they can be dealt with.  If you have questions of your own leave a comment.

Fix horrible bug that breaks buttons

GDK has a horrible debilitating bug that was not caught before release and at the time of writing the fix has not been backported as an online update.  This makes buttons in both Flash and Eclipse unresponsive to clicking.  You need to “export GDK_NATIVE_WINDOWS=true” in order for things to work correctly.  I added this in my ~/.bashrc file:

# .bashrc

# User specific aliases and functions
alias untargz="tar zxvf"

export GDK_NATIVE_WINDOWS=true
export PATH=/usr/local/eclipse/eclipse-3.5:$PATH

# Source global definitions
if [ -f /etc/bashrc ]; then
********. /etc/bashrc
fi

1-click installers – Setup graphics card, multimedia, and fonts

An easy way to install most of the software you need is to use a 1-click installer.  If you have an NVIDIA or ATI graphics card, then you’ll want to install the drivers using one of these installers.  I also recommend installing “Codecs pack for KDE”, “VLC Media Player”, and “Fonts with subpixel hinting enabled”.

Install Chrome

Download it from Google.

Setup multimedia

This is a perennial setup step on Linux distributions.  We’ll install the codecs needed to watch videos on Linux.

  1. YaST > “Software” > “Software Repositories”
  2. Click “Add”
  3. Select “Community Repositories”
  4. Select “Packman Repository” if it is not already
  5. YaST > “Software” > “Software Management”
  6. Install libxine1-codecs.

Setup time synchronization

NTP (Network Time Protocol) can be used to synch your system’s clock to a server on the internet.

  1. YaST > “System” > “Date and Time”
  2. Click “Change…”
  3. Select “Synchronize with NTP Server”
  4. Add the server for your region from pool.ntp.org (e.g. I used 0.us.pool.ntp.org since I’m in the United States)
  5. Check the “Save NTP Configuration”
  6. If you hit “Configure…” you can also add backup servers (e.g. 1.us.pool.ntp.org , 2.us.pool.ntp.org , etc.)

Setup a static IP address

This step is optional and is meant for people that know what a static IP is and want to set one up.  Having a static IP address is very nice when you want to remote desktop to your server or access it in some other way without worrying about what the IP address is.  There may also need to be some configuration done on your router for this one.  Or you may prefer to investigate DHCP reservations if your router supports them.

  1. YaST > “Network Devices” > “Network Settings”
  2. Under Hostname/DNS, you may change your hostname and Domain Name if you prefer
  3. Under Global Options, switch to “Traditional Method with ifup”
  4. Under “Overview”, select your network card and click “Edit”
  5. Enter your static IP (besure to also enter DNS and gateway information)
  6. Hit save

Setup a network file share (NFS) using Samba

Another optional step, Samba allows you to share files on your computer with others on the network.

  1. YaST > “Software” > “Software Management”
  2. Install “samba”
  3. YaST > “Network Services” > “Samba Server”
  4. Choose “Not a domain controller”, start during boot, and tell it to unblock the firewall
  5. Change sharing settings as you’d like and hit “Finish”
  6. Add a user to Samba by running “smbpasswd -a username” where username is the user you’d like to create.
  7. Connect from your Windows machine by right clicking “My Computer” and browsing your network.  If you have trouble connecting you might also try opening a “Run…” dialog off the start menu and typing in your IP address with two leading slashes e.g. “\\192.168.10.2″

Setup remote desktop through NX

The two main remote desktop softwares for Linux are VLC and NX.  NX is much faster and is what I would recommend.  Unfortunately, I have not been able to get desktop sharing to fully work.  If you get desktop shadowing to work properly then please let me know.  In addition to installing NX, we’ll also open the corresponding port in the firewall so that we can connect from another machine.

  1. Download the NX Linux packages
  2. Run “rpm -iv nxclient-3.4.0-5.x86_64.rpm”, “rpm -iv nxnode-3.4.0-5.x86_64.rpm”, and “rpm -iv nxserver-3.4.0-5.x86_64.rpm”
  3. Run “/usr/NX/scripts/setup/nxserver –install”
  4. Run “/usr/NX/bin/nxserver –keygen”
  5. In your NX client, open “Configure…” > “General” tab > “Key …”
  6. Copy the contents of “/usr/NX/share/keys/default.id_dsa.key” into the key window and save it
  7. Open “/usr/NX/etc/server.cfg”
  8. Uncomment ‘EnableSessionShadowingAuthorization = “1″ and change the value to “0″ which will enable you to select “Shadow” in the client under the General > Desktop if you’d like to do desktop sharing.
  9. YaST > “Security and Users” > “Firewall” > “Allowed Services”
  10. Allow “Secure Shell Server”

Upgrade to the ext4 file system

ext4 is the new default file system on openSUSE 11.2.  If you did a clean install, you will be running ext4 by default.  However, if you have a drive you did not reformat when installing the OS then you may be running ext3.  For example, I have two drives: 1 60 gig drive I use as my root partition and a 500 gig drive I use as my home directory.  When I installed openSUSE 11.2, I wiped the root partition and reformatted it as ext4, but I wanted to keep the data on my home directory, so I couldn’t reformat it and left it as ext3.

  1. Run “df -T” to see your file system types.  For me it showed /dev/sdb1 as ext4 mounted on /home
  2. Run init 1 to switch runlevels
  3. Unmount the drive:  e.g. “umount /home”
  4. Run e2fsck on the drive : e.g. e2fsck /dev/sdb1
  5. Run tune2fs: e.g. tune2fs -O extents,uninit_bg /dev/sdb1
  6. Edit /etc/fstab to change ext3 to ext4 on the drive
  7. Run “mount -a” to remount the drive

Review: The Problem with Linux is Usability and Resource Allocation

openSUSE is as good a Linux desktop as you’ll find.  Unfortunately that’s not saying as much as I’d like.  There are so many broken things on the system and yet so much effort was put into developing things that average person doesn’t give a rat’s ass about. There’s a whole load of crap that comes with the system that’s a waste.  It’s incredibly frustrating to me to see so many resources go into this software that few people care about.  I don’t need Marble when the far superior Google Earth is available on Linux.  I don’t need Kaffeine when VLC is a better alternative.  Most of the games are a waste and can’t hold a candle to the hundreds of Flash games available online.   I’ve never used digiKam, but have to assume that at best it’s Picasa‘s ugly stepsister.  There are dozens of programs I’d rather use than KDE PIM: GMail, Thunderbird, LinkedIn, etc.  The list goes on, and on, and on.  Why are we building this stuff?  The single biggest improvement in openSUSE 11.2 is that Firefox is now the default browser.  This took way too long, but is a welcomed change.  I wish VLC would replace Kaffeine.  Someone at Novell, Canonical, et. al. needs to do some user testing.  Any 12-year-old kid would tell you you’re crazy if you think people would rather use Marble than Google Earth.  Linux distros have done an absolutely horrifyingly awful job of picking the best software to be the default.  Instead of wasting all this time, how about we come together and decide to make an operating system that just works?  There are so many usability frustrations that I’d like to see solved instead.  Novell, Canonical, and the other leaders in this area need to step up.

As mentioned, there are a lot of usability problems.  For example, Flash was unusable in full screen mode after install.  It turned out this was because I hadn’t yet installed my NVIDIA driver.  But this is a big problem.  How would any casual user know they had to do this?  My parents, brother, or sister would not have known to do this despite being reasonably computer savvy.  I knew because I’ve used Linux for many years, but if it requires a computer science degree to watch a YouTube video then you’ve failed.  And when I did realize I needed to install the driver, I needed to know which of three drivers I wanted.  Are you serious?  I could just imagine asking my mom what graphics card she has.  That would be a fun conversation.  It’s 2009.  And you don’t have the ability to detect my hardware?  I understand that there are licensing issues, but once I’ve said I want to install a graphics driver you should be able to tell me which one I need.  I want to get rid of the “Recently Used” option on the Kicker menu like I can with classic, but there’s no option to.  I don’t understand why YaST has a “Media Check” option.  Can’t you tell there’s no CD/DVD in my drive?  I tried to edit my network settings with YaST and it told me to use NetworkManager or change the setup method to Traditional with ifup.  How do I do either of those things?  I don’t know what NetworkManager is or how to open it.  There’s nothing in any of the menus called NetworkManager.  When I started Firefox it asked me if I wanted to use Flash, gnash, or swfdec.  But I tried gnash and swfdec and neither could play a Hulu video, so are you insane?  Of course I don’t want to use either of those.  Why are you confusing me?  Flash on Linux is a big problem.  The controls on the Hulu just stop working with frustrating frequency.  I can’t figure out why Amarok and Kaffeine are both broken.  I could go on, but you get my point.  Let’s fix these problems instead of building more crap no one asked for.  I know how much of this work has been done by volunteers and am extremely grateful for it.  I’ve contributed to several open source projects myself and know what it’s like, so I hope those reading know I am thankful to have this amazing free software.  I’m happy for what’s been done and am hoping the leaders of KDE and openSUSE will make some hard choices about what’s worth investing in and supporting, so that these projects can become successful in the mainstream world because as it stands now, even as great as it is, Linux simply isn’t ready.

Install NVIDIA drivers

If you have an NVIDIA card, then you’ll want to install the drivers.  Use the NVIDIA driver 1-click installer.

Comments (8)

Maven on Eclipse Tutorial

Install Java and Maven

First off, make sure you have a Java JDK installed and that your JAVA_HOME environment variable points to it. You can check by typing “echo %JAVA_HOME%” in Windows or “echo $JAVA_HOME” in Linux. You’ll next want to download and install Maven by following the directions on their website.  Once Maven is installed, you’ll want to create a settings.xml file in your .m2 directory, which is located at ~/.m2 in Linux or C:\Documents and Settings\%USER%\.m2 on Windows.  You can do this by copying the settings.xml file from the directory where Maven in installed to you .m2 directory.

Install m2eclipse

You’ll want to make sure your Eclipse installation is pointing at your JDK.  You can check by going to Window > Preferences > Java > Installed JREs.  You’ll also want to set the -vm flag in your eclipse.ini file which is located at the root of your Eclipse installation.  Here’s what my file looks like:

-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256M
-framework
plugins\org.eclipse.osgi_3.4.0.v20080605-1900.jar
-vm
C:\Program Files\Java\jdk1.6.0_11\bin\javaw.exe
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms40m
-Xmx512m

Finally, you get to install m2eclipse by opening up Eclipse and selecting Help > Software Updates… > Available Software > Add Site…  Then enter http://m2eclipse.sonatype.org/sites/m2e, check the newly added boxes, and press Install.

Create Your First Maven Project

Now for the fun.  You can create a Maven project in Eclipse by selecting New > Project… > Maven > Maven Project.  Check “Create a simple project” on the first screen and hit Next.  Enter the Group Id, Artifact Id, and Name.  For example, “com.benmccann.robot”, “window-robot”, “Window Robot”.  Now, at long last, you can do some actual programming.

Add Dependencies

It won’t be long before you’ll want to add dependancies.  Open the Maven Indexes View by selecting Window > Show View > Other… > Maven > Maven Indexes.  You’ll want to make sure you have the Maven Central Repository, so right-click the view and choose “Add Index”.  Enter http://repo1.maven.org/maven2/ for the Repository URL and hit Retrieve to fill in the Repository Id of central.  Now you can right-click the project and choose Maven > Add Dependancy.  We’ll add jUnit since you’ll probably want it anyway.  Type junit and you should see it populated into the box below.  Use the dropdown at the bottom to set a scope of “test” and hit OK.

Comments (3)

Struts 2 AJAX Tutorial – Dojo Autocompleter Example

This tutorial was written for Struts 2.1.2. A lot has changed in newer versions. The Dojo Plugin is no longer officially supported by the Struts 2 team. Also, please see Muhammad’s comment in the comment section if you’re using the latest version of Struts 2.  You may want to check out Google Closure if you’re looking for a good JS library.

Struts 2 comes with a Dojo Toolkit plugin which makes developing an AJAX application easier than ever.  In addition, the AJAX plugin I would most recommend is the Struts 2 JSON plugin.  In this example, we will use the two side-by-side to create an autocompleter.  In order to use the Ajax support provided in Struts 2, I would recommend Struts 2.1.2 or later.  Include struts2-dojo-plugin-2.1.2.jar and jsonplugin-0.30.jar in your WEB-INF/lib directory.

First, we will create the action for the field that we wish to autocomplete:

package com.lumidant.tutorial.struts2.action;

import java.util.HashMap;
import java.util.Map;

import com.opensymphony.xwork2.ActionSupport;
import com.lumidant.tutorial.struts.dao.*;

public class AutocompleteField extends ActionSupport {

    private String city;
    private Map<String,String> json;

    public String execute() throws Exception() {
        return SUCCESS;
    }

    public String getCities() throws Exception() {
        json = new HashMap<String,String>();

        if(city != null && city.length() > 0) {
            CityDao dao = new CityDao();
            List<City> cities = dao.getCitiesStartingWith(city);
            for(City city : cities) {
                json.put(city.getId(), city.getName() + ", " + city.getState().getAbbreviation());
            }
        }

        return SUCCESS;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public Map<String,String> getJson() {
        return json;
    }

}

The getCities method in our action acts on the input String city that we’re given and creates a Map of cities starting with that text.  The key of our Map is going to be the hidden option value while the value of our Map is going to be our option text.

Next, we will modify our struts.xml configuration file to utilize the JSON plugin by extending json-default:

<package name="example" extends="json-default">
  <action name="AutocompleteField" class="com.lumidant.tutorial.struts2.action.AutocompleteField">
    <result type="json><param name="root">json</param></result>
  </action>
</package>

The root parameter that we specify is the name of the variable from our Action that we want to have converted and serialized to a JSON output.

If instead of serializing a Java object to JSON you wish to create the JSON string directly yourself, you can instead use a result type of “stream”:

<action name="jsondata" class="com.lumidant.tutorial.struts2.action.JsonDataAction">
  <result type="stream">
    <param name="contentType">application/json</param>
  </result>
</action>

And finally, we get to create our .jsp view:

<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>

<html>
  <head>
    <sx:head />
  </head>
  <body>
    <s:url id="cityList" action="AutocompleteField" method="getCities" />
    <sx:autocompleter name="city" theme="ajax" href="%{cityList}" size="24" />
  </body>
</html>

For any of the Dojo plug-in features that you use, you will need to include the <sx:head /> tag, which includes the relevant JavaScript tags into your page’s head tag.  Then you can see that the autocompleter’s name attribute aligns with the city field in our Action and the url’s method attribute aligns with our Action’s getCities method.  Now, when you visit your new page and start typing in the text box, you should see the field autocompleting.  There are some other cool built-in Dojo tags that you should look at as well like the datetimepicker, which provides a really nice calendar.

Comments (27)

Struts 2 Tutorial – Struts Configuration

The struts.xml file defines the relationship between actions and .jsp views, the inclusion of interceptors, and possible result types.  It’s essential that you be able to handle at least basic configuration changes, so we’ll demonstrate the most important and frequently used below.

Packages: Extension, Namespaces, and More

For more complex applications, you will likely want to split the configuration between multiple packages.  In Struts 2.0.x, the the default package is struts-default.  Most of the time, extending this package is a pretty good place to start from.

<package name="example" extends="struts-default">
  <interceptors>
    <interceptor name="authorization" class="com.lumidant.tutorial.struts2.interceptor.AuthorizationInterceptor">

    <interceptor-stack name="authStack">
      <interceptor-ref name="authorization" />
      <interceptor-ref name="defaultStack" />
    </interceptor-stack>
  </interceptors>

  <global-results>
    <result name="error">/WEB-INF/pages/error.jsp</result>
  </global-results>

</package>

In the package above, we did not define any action mappings, but rather we defined some more general properties that we can share between other packages.   We can now extend the package we just defined when creating new packages.  This package and any that extend it will have all Action.ERROR results go to error.jsp.  We also defined a new interceptor and interceptor stack that our actions and packages can make use of.  In this example, you could imagine that we would have two other packages extending this one.  One package would contain pages that any user could visit.  The other package could contain pages requiring the user to be logged in, so we would set the default-interceptor-ref to “authStack” as shown below:

<package name="admin" namespace="/admin" extends="example">
  <default-interceptor-ref name="authStack"/>

  <action name="DeleteSomething_*" method="{1}" class="com.lumidant.tutorial.struts2.action.DeleteSomething">
    <result name="input">/WEB-INF/pages/deleteSomething.jsp</result>
    <result>/WEB-INF/pages/deleteSuccessful.jsp</result>
  </action>
</package>

We also introduced additional new topics in this example.  Firstly, you’ll see that we created a namespace.  That means that all the actions in this package will be accessed through a spearate URL.  In this example it is an “/admin” URL.  Also, you’ll see that we put the “*” wildcard in the action name.  What this is doing is taking what was located in the * and replacing the “{1}” with that string.  Since the “{1}” is located in the method attribute, we will be calling a method on our action.  So let’s say the user accesses /admin/DeleteSomething_input.action.  Then the input method of the DeleteSomething.java action will be called.  Assuming we did not define our own input method, then we will inherit the input method from ActionSupport, which will take us to the result named “input”, which in this example is the deleteSomething.jsp page.

Better URL Structure

By adding the following a child of the struts tag, you no longer need to append .action to your URLs.

<constant name="struts.action.extension" value="action,,"/>

Action Chaining

<action name="LogIn_*" method="{1}" class="com.lumidant.tutorial.struts2.action.LogIn">
  <result type="chain">ShowHomepage</result>
</action>

What happens when we don’t want to show a .jsp page as the result of our action, but instead want to continue on directly to another action?  We simply use the result type “chain” to forward to another action.

Redirect After Post

<action name="SubmitOrder_*" method="{1}" class="com.lumidant.tutorial.struts2.action.SubmitOrder">
  <result name="redirect" type="redirect-action">
    <param name="actionName">DisplayOrder</param>
    <param name="namespace">/user</param>
  </result>
</action>

Very often, after an action is taken, we want to redirect the user to a separate action.  For example, let’s say the user submits an order.  If we leave the URL in their browser as SubmitOrder.action and they hit refresh, then the order could be submitted a second time.  So after they submit an order, it’s probably better if we redirect them to another action, such as one displaying the order they just placed with a thank you message.

Comments (3)

Struts 2 Tutorial – Interceptors

Interceptors are my favorite aspect of Struts 2. They inspect and/or act on a user’s request. There are three main uses cases that I’ll discuss here: intercepting before the action, between the action and view, and after the view. At the end, I’ll show you how to add your brand spankin’ new interceptor to your struts.xml file so that it is called on each request.

Intercepting Before the Action:

package org.lumidant.tutorial.struts2.interceptor;

import java.util.Map;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ValidationAware;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class AuthorizationInterceptor extends AbstractInterceptor {

	private static final String USER_KEY = "user";

	public String intercept(ActionInvocation invocation) throws Exception {
		Map session = invocation.getInvocationContext().getSession();
		if(session.get(USER_KEY) == null) {
			addActionError(invocation, "You must be authenticated to access this page");
			return Action.ERROR;
		}

		return invocation.invoke();
	}

	private void addActionError(ActionInvocation invocation, String message) {
		Object action = invocation.getAction();
		if(action instanceof ValidationAware) {
			((ValidationAware) action).addActionError(message);
		}
	}

}

You can see that we check to see if the user is authorized and present an error message if he/she is not logged in. We do this by adding an ActionError, which can be displayed on your view. If the user is logged in, then he/she proceeds normally.

Intercepting Between the Action and Generation of View:

package org.lumidant.tutorial.struts2.interceptor;

import java.util.Map;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultInterceptor;
import org.apache.struts2.ServletActionContext;

public class WirelessInterceptor extends AbstractInterceptor {

	private static final String RESULT_CODE_SUFFIX_WIRELESS = "Wireless";
	private static final String REQUEST_HEADER_ACCEPT = "Accept";
	private static final String ACCEPT_HEADER_WIRELESS = "vnd.wap";

	public String intercept(ActionInvocation invocation) throws Exception {

		invocation.addPreResultListener()(new PreResultListener() {
			public void beforeResult(ActionInvocation invocation, String resultCode) {

				// check if a wireless version of the page exists
				// by looking for a wireless action mapping in the struts.xml
				Map results = invocation.getProxy().getConfig().getResults();
				if(!results.containsKey(resultCode + RESULT_CODE_SUFFIX_WIRELESS)) {
					return;
				}

				// send to wireless version if wireless device is being used
				final String acceptHeader = ServletActionContext.getRequest().getHeader(REQUEST_HEADER_ACCEPT);
				if(acceptHeader != null && acceptHeader.toLowerCase().contains(ACCEPT_HEADER_WIRELESS)) {
					invocation.setResultCode(resultCode + RESULT_CODE_SUFFIX_WIRELESS);
				}
			}
		});

		return invocation.invoke();
	}
}

In this example, the action has already been taken, but we have not generated the view that the user will see yet. This allows us to send the user to a separate page if he/she is using a Blackberry. We check the Accept HTML header to see if the string “vnd.wap” is present and if so then we append “Wireless” to the result code. So if the action was going to send the user to the result “Success” then we will instead show them the result “SuccessWireless”. We could easily adopt this technique for a Google Android or an iPhone as well.

Intercepting after View Creation:

package org.lumidant.tutorial.struts2.interceptor;

import java.util.Map;

import org.hibernate.Transaction;
import org.marketcharts.data.dao.util.HibernateSessionFactory;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class HibernateInterceptor extends AbstractInterceptor {

	public String intercept(ActionInvocation invocation) throws Exception {
		try {
			return invocation.invoke();
		} catch(Exception e) {
			Transaction tx = HibernateSessionFactory.getSession().getTransaction();
			if(tx != null && tx.isActive()) {
				tx.rollback();
			}
			return Action.ERROR;
		} finally {
			HibernateSessionFactory.getSession().close();
		}
	}

}

This example uses Hibernate, an Object Relational Mapper (ORM), which is library for easy database access. Hibernate needs an open session when the view is created (Open Session in View Pattern) because it lazily accesses the database. If the session is closed and we go to access the database, then Hibernate will throw an Exception. So this interceptor provides a method to close the session after the view has already been created and all the database calls have been made.

Adding an Interceptor to your struts.xml:

<package name="example" extends="struts-default">
  <interceptors>
    <!--
      One possible use of an interceptor is to send the user to special version of the .jsp
  	view if they are using a Blackberry as shown above.
    -->
    <interceptor name="wireless" class="com.lumidant.tutorial.struts2.interceptor.WirelessInterceptor">

    <interceptor-stack name="wirelessStack">
      <interceptor-ref name="exception" />
      <interceptor-ref name="servlet-config" />
      <interceptor-ref name="i18n" />
      <interceptor-ref name="chain" />
      <interceptor-ref name="params" />
      <interceptor-ref name="wireless" />
    </interceptor-stack>
  </interceptors>

  <default-interceptor-ref name="wirelessStack" />
</package>

In this example, we’ve change the default interceptor stack to be a custom stack which we’ve created. You can also extend existing stacks.

Comments (16)

Struts 2 Tutorial – Creating Views

One of the first things you’ll want to do after getting started with Struts 2 is create more complex views.  For this example, let’s assume we have an action that returns a list of all the employees at our company:

package com.lumidant.tutorial.struts2.action;

import com.opensymphony.xwork2.ActionSupport;
import com.lumidant.tutorial.struts.dao.*;
import com.lumidant.tutorial.struts.model.*;

public class ExampleAction extends ActionSupport {

    private static final long SerialVersionUID = 1L;
    private List<Employee> employees;

    public String execute() throw Exception {
        EmployeeDao dao = new EmployeeDao();
        employees = dao.getAllEmployees();
        return SUCCESS;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

}

Now let’s create a page that iterates through our list of employees and prints each employee’s name in alternating row colors:

<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
  <head>
    <title>Struts 2 Tutorial Example</title>
    <style type="text/css">
      .odd td { background-color: #fff; }
      .even td { background-color: #eee; }
    </style>
  </head>
  <body>
    <h2>All Employees:</h2>
    <table>
      <s:iterator value="employees" status="rowstatus">
        <tr <s:if test="#rowstatus.odd">class="odd"</s:if><s:else>class="even"</s:else>>
          <td class="fn n">
            <span class="given-name"><s:property value="firstName" /></span>
            <span class="additional-name"><s:property value="middleName" /></span>
            <span class="family-name"><s:property value="lastName" /></span>
          </td>
        </tr>
      </s:iterator>
    </table>
  </body>
</html>

What we just demonstrated is a fairly common use case and should provide a good example for doing the same in the future.  Also notice our use of the if tag, which you will undoubtedly find a need for.  Another common use case is to access the session from within the view.  For example, you may want to show a link only to logged in users.  Again, we will use OGNL to accomplish this.

<s:if test="%{#session.user.isAdmin()}">
  <a href="Admin.action">Only Admins Should See This Link</a>
</s:if>

That should give you a pretty good idea of the basics, so we’ll move onto an internationalization example.  You’ll want to create a package.properties file in the directory where your actions reside:

form.label.firstName=First Name:
form.label.lastName=Last Name:

This file contains text for the default translation of your site.  All static text on your site should be contained in an internationalization file.  You can then include the text in your JSP with the Struts 2 text tag in the following manner: <s:text name=”form.label.firstName” />.  You can create a package.properties file for each translation and append the suffix for each translation language (and optionally country – eg. en_US). For example, below is a package_pt.properties file, which contains a Portuguese translation of our example site:

form.label.firstName=Primeiro Nome:
form.label.lastName=Último Nome:

Now when the user visits the site, Struts will detect their browser language and serve the appropriate page to them.

That should give you pretty decent idea of how to create views for your site.  Please check out our other Struts 2 tutorials for information on other aspects of Struts 2.

Comments (3)

Struts 2 Tutorial – Getting Started

Struts 2 is an MVC web development framework.  It is based off of Web Work and has far less configuration than the original Struts.  I would strongly recommend Struts 2 over Struts for new development due to the faster development times it allows.  This is a very simple example and follow up posts will be coming shortly to help you develop more complex apps with Struts 2.

Getting Started

Below is an example project layout that you can use for your project.  It may be useful in helping you to decide where in your project to place your various files.

Struts 2 Project Layout

To get started, you’ll need to download the Struts libraries.  At a minimum, you will need to put the following in your WEB-INF/lib directory:

  • struts2-core.jar (The framework itself)
  • xwork.jar (Struts 2 is built on the XWork 2 framework)
  • ognl.jar (Object Graph Notation Language is used throughout the framework to access object properties)
  • freemarker.jar (Freemarker is used to create UI tag templates in Struts 2)
  • commons-logging.jar (Used to log to log4j or JDK logging)

Now we need to add a filter mapping to your web.xml file in order to have the Struts called whenever a page on your site is accessed:

<?xml version="1.0"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

It’s now time to make your first action.

package com.lumidant.tutorial.struts2.action;

import com.opensymphony.xwork2.ActionSupport;
import com.lumidant.tutorial.struts2.dao.*;
import com.lumidant.tutorial.struts2.model.*;

public class ExampleAction extends ActionSupport {

    private static final long SerialVersionUID = 1L;
    private String id;
    private Employee employee;

    public String execute() throw Exception {
        EmployeeDao dao = new EmployeeDao();
        employee = dao.getEmployeeById(id);
        return SUCCESS;
    }

    public Employee getEmployee() {
        return employee;
    }

    public void setId(String id) {
        this.id = id;
    }

}

There are a couple important things to note here.  The first is that our Action extends ActionSupport, which is a pattern you’ll want to continue for future actions.  Secondly, the input to our page has a setter which the framework needs to have present and the output has a getter.  Finally, our Action returns the result type SUCCESS.  The other predefined result types are ERROR, INPUT, LOGIN, and NONE.

Struts places its configuration in a struts.xml file. A good place to put it is at the root of your source folder. We’ll need to add our new action to the struts.xml file.

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <!-- Configuration for the default package. -->
  <package name="default" extends="struts-default">
    <action name="ExampleAction" class="com.lumidant.tutorial.struts.action.ExampleAction">
      <result name="success">/WEB-INF/pages/displayEmployee.jsp</result>
    </action>
  </package>
</struts>

And finally, we need to create a .jsp view for our results to be rendered in.

<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
  <head>
    <title>Struts 2 Tutorial Example</title>
  </head>
  <body>
    <h2>Selected Employee:</h2>
    <span class="fn n">
      <span class="given-name"><s:property value="employee.firstName" /></span>
      <span class="additional-name"><s:property value="employee.middleName" /></span>
      <span class="family-name"><s:property value="employee.lastName" /></span>
    </span>
  </body>
</html>

The important takeaway for the view is the manner in which properties can be accessed from the view.  Struts 2 uses OGNL, which for the majority of cases means you’ll just leave the get off of your method name.  For example <s:property value=”employee.firstName” /> will call your Action’s getEmployee() method and then will call the Employee’s getFirstName() method.

So now you should have an end-to-end working example.  Depending on which application server you’re using, you should be able to access your sample page at a url like http://localhost:9090/ExampleAction.action  You’ll of course need to create some sort of dummy EmployeeDao if you’re going to use this exact example, but you should be well on your way.

You’ll also want to continue reading about Struts 2 in the continuation of this tutorial:
Struts 2 Tutorial – Creating Views
Struts 2 Tutorial – Interceptors
Struts 2 Tutorial – Struts Configuration

As an optional, but nice complement, I’d also recommend SiteMesh Tutorial with Examples.

Comments (10)

SiteMesh Tutorial with Examples

SiteMesh is a web layout framework for Java.  It differs from from frameworks such as Tiles in that it utilizes the decorator pattern.  For example, you create a number of pages and then you tell SiteMesh that you’d like to add the same header, footer, and menus to each of those pages.  This tutorial will give you a simple example of how SiteMesh can be used to give you a cleaner layout architecture and speed development times.

Start by downloading SiteMesh and adding sitemesh-2.3.jar to your WEB-INF/lib directory.  Then add the SiteMesh filter to your web.xml file like so:

<filter>
    <filter-name>sitemesh<filter-name>
    <filter-class>
        com.opensymphony.module.sitemesh.filter.PageFilter
    </filter-class>
<filter>

<filter-mapping>
    <filter-name>sitemesh<filter-name>
    <url-pattern>/*</url-pattern>
<filter-mapping>

This will call the SiteMesh filter whenever a page on your site is accessed.  Now we’ll need to create a /WEB-INF/decorators.xml file:

<decorators defaultdir="/WEB-INF/decorators">
    <decorator name="main" page="main.jsp">
        <pattern>/WEB-INF/pages/*</pattern>
    </decorator>
</decorators>

This will decorate all of the pages located under /WEB-INF/pages/ with the decorator /WEB-INF/decorators/main.jsp, which we’ll create next:

<%@ taglib prefix="decorator" uri="http://www.opensymphony.com/sitemesh/decorator" %>

<head>
  <title>
    Lumidant.com - <decorator:title default="SiteMesh Tutorial Example" />
  </title>
  <style type="text/css">@import "css/global.css";</style>
  <decorator:head />
  <body>
    <div id="header">
      <h2><a href="http://www.lumidant.com/">Lumidant.com</a> Tutorials</h2>
    </div>
    <div id="content">
      <decorator:body />
    </div>
  </body>
</html>

This decorator does a couple of things.  First off, it create an HTML title tag, which starts as “Lumidant.com – ” and then appends the title of the page that is being decorated.  If that page does not have a title tag, then a default is used to render “Lumidant.com – SiteMesh Tutorial Example”.  It then adds a global style sheet to every page being decorated and appends whatever is in that page’s head tag, which is useful for page-specific JavaScript, etc.  The decorator continues by adding a header to each page reading “Lumidant.com Tutorials” followed by the decorated page’s content.

Pretty cool, right?  If anyone is familiar with a similar decorator framework in PHP, please let me know.

Comments (14)

Apache CXF Tutorial – WS-Security with Spring

This tutorial will cover adding an authentication component to your web service though WS-Security. If you need an overview of how to setup CXF then you may find our previous tutorial helpful. Another helpful resource is CXF’s own WS-Security tutorial. However, it does not include information on how to setup the client through Spring.

To begin with, make sure you have at least the following .jars in addition to the required base CXF .jars:

spring-beans-2.0.6.jar
spring-context-2.0.6.jar
spring-core-2.0.6.jar
spring-web-2.0.6.jar
wss4j-1.5.1.jar
xmlsec-1.3.0.jar

Now we will add a security interceptor to the server’s Spring configuration file, which we named cxf.xml in the last tutorial in order to match the CXF documentation.

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xsi:schemaLocation="http://www.springframework.org/schema/beans

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

http://cxf.apache.org/jaxws

                          http://cxf.apache.org/schemas/jaxws.xsd">

  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

  <jaxws:endpoint id="auth"
                  implementor="com.company.auth.service.AuthServiceImpl"
                  address="/corporateAuth">

    <jaxws:inInterceptors>
      <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
      <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
        <constructor-arg>
          <map>
            <entry key="action" value="UsernameToken" />
            <entry key="passwordType" value="PasswordText" />
            <entry key="passwordCallbackClass" value="com.company.auth.service.ServerPasswordCallback" />
          </map>
        </constructor-arg>
      </bean>
    </jaxws:inInterceptors>

  </jaxws:endpoint>

</beans>

You can change the action and passwordType to do more advanced authentication. In this example, we will simply require all authenticating clients to know a single password specified by the server. If you’d like each client to have it’s own password you can specify that in the callback, which is the next thing we must implement:

package com.company.auth.service;

import java.io.IOException;
import java.util.ResourceBundle;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;

public class ServerPasswordCallback implements CallbackHandler {

    private static final String BUNDLE_LOCATION = "com.company.auth.authServer";
    private static final String PASSWORD_PROPERTY_NAME = "auth.manager.password";

    private static String password;
    static {
        final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_LOCATION);
        password = bundle.getString(PASSWORD_PROPERTY_NAME);
    }

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

        // Set the password on the callback. This will be compared to the
        //     password which was sent from the client.
        // We can call pc.getIdentifer() right here to check the username
        //     if we want each client to have it's own password.
        pc.setPassword(password);
    }

}

The server is now setup to require a password. The password we are requiring is one that we specified in a properties file and then read in through a ResourceBundle. You may find it easier to simply hard code the password on the initial run and then replace it with your own means of authentication once the service is up and running.

If you are running on WebLogic 9, as I was, then you will get an error “java.lang.UnsupportedOperationException: This class does not support SAAJ 1.1“. In order to correct that, make sure your version of the SAAJ classes are being used by adding the following to your weblogic.xml descriptor file:

<container-descriptor>
    <prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>

You WebLogic folks must also then set two properties in your WebLogic JDK:

-Djavax.xml.soap.MessageFactory=com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl
-Djavax.xml.soap.SOAPConnectionFactory=weblogic.wsee.saaj.SOAPConnectionFactoryImpl

We now have to setup the client to supply a password. Firstly, we will create another Spring file at com/company/auth/service/cxfClient.xml to setup the application context for the client:

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jaxws="http://cxf.apache.org/jaxws"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                      http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

  <bean id="proxyFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
    <property name="serviceClass" value="com.company.auth.service.AuthService"/>
    <property name="address" value="http://localhost:7001/authManager/services/corporateAuth"/>
    <property name="inInterceptors">
      <list>
        <ref bean="logIn" />
      </list>
    </property>
    <property name="outInterceptors">
      <list>
        <ref bean="logOut" />
        <ref bean="saajOut" />
        <ref bean="wss4jOut" />
      </list>
    </property>
  </bean>

  <bean id="client" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean" factory-bean="proxyFactory" factory-method="create" />

  <bean id="logIn" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
  <bean id="logOut" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
  <bean id="saajOut" class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />
  <bean id="wss4jOut" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
    <constructor-arg>
      <map>
        <entry key="action" value="UsernameToken" />
        <entry key="user" value="ws-client" />
        <entry key="passwordType" value="PasswordText" />
        <entry key="passwordCallbackClass" value="com.company.auth.service.ClientPasswordCallback" />
      </map>
    </constructor-arg>
  </bean>    

</beans>

We then need to set the password for our message:

package com.company.auth.service;

import java.io.IOException;
import java.util.ResourceBundle;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;

public class ClientPasswordCallback implements CallbackHandler {

    private static final String BUNDLE_LOCATION = "com.company.auth.authClient";
    private static final String PASSWORD_PROPERTY_NAME = "auth.manager.password";	

    private static String password;
    static {
        final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_LOCATION);
        password = bundle.getString(PASSWORD_PROPERTY_NAME);
    }	

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

        // set the password for our message.
        pc.setPassword(password);
    }

}

Finally, we create the service factory, which is extremely easy since all the work was done in the Spring file:

package com.company.auth.service;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class AuthServiceFactory {

    private static final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {
                "com/company/auth/service/cxfClient.xml"
            });

    public AuthServiceFactory() {
    }

    public AuthService getService() {
        return (AuthService) context.getBean("client");
    }
}

Congratulations. Your web service now utilizes a basic implementation of WS-Security. Hopefully, that will be enough background to get you on your way.

Comments (40)