Tagebuch eines Technikers

Tuesday, May 20, 2008

JMock 1 short how-to

This document is a nice resource on how to perform a unit test with JMock (which is part of AppFuse).

For some small remarks on how to build the test, here is a portion of the code I used:
 List<Home> homes = new ArrayList<Home>();

// set expected behavior on dao
mock.expects(once()).method("findInPerimeter").with(eq(center),
eq(radius / Constants.KM_PER_DEGREE)).will(returnValue(homes));

List<Home> result = manager.findInPerimeter(center, radius);
assertEquals(result, homes);
This tells the mock that we expect the next method call of the proxied object (in this case a dao) to be of parameter findInPerimeter with the given parameters and to have a result of returnValue. manager uses this proxy to verify the call.

Monday, May 19, 2008

By-directional one-to-many relations with Hibernate and annotations

If have a POJO User and Home. One user can have multiple homes, and homes should know their user. Thus, one user should have many homes, and homes have one owner. Since I ran into some trouble while doing this, this is a short example of what to do. Another simple example (in German) helped me to come to a correct solution

My POJOs look like:
If have a POJO User and Home. One user can have multiple homes, and homes should know their user. Thus, one user should have many homes, and homes have one owner. Since I ran into some trouble while doing this, this is a short example of what to do. Another simple example (in German) helped me to come to a correct solution

My POJOs look like:

@Entity
@Table(name=\"app_user\")
public class User implements Serializable {

private Long id;

private Set<Home> homes = new HashSet<Home>();

/**
* Default constructor - creates a new instance with no values set.
*/
public User() {}

@Id @GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}

@OneToMany(mappedBy=\"owner\")
public Set<Home> getHomes()
{
return homes;
}

// setters, ...
}
and
@Entity @Table(name=\"home\")
public class Home extends BaseObject {

private Long id;


private User owner;


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}

@ManyToOne
@JoinColumn(name=\"user_id\")
public User getOwner() {
return owner;
}

// setters, ...
}
The relationship is saved in Home in the property owner (Home.owner). The foreign key is saved in the field user_id of table home.

When you get the error
mappedBy reference an unknown target
this means that the mappedBy is not set correctly. Ensure that the target is set correctly -- in this scenario, mappedBy specifies field owner of class Home as target.

How to edit the AppFuse user class (and others)

For my AppFuse application, I need to add some functionality regarding the basic user class. Fortunately, this seems to be rather easy, if the AppFuse documentation is correct. In this way, one should be able to edit the core classes.

I tested everything up to really editing the core classes. Still, I do not expect any problems from what I've seen so far.

Saturday, May 17, 2008

Some Eclipse plug-ins worth to look at

There are some cool Eclipse Europe plugins worth to look at. Here is just a small collection one can use when developing with Maven, Spring and AppFuse:

  • Subclipse -- SVN client from within Eclipse. I prefer to have the team functionality in my IDE. I can still use TortoiseSVN at the same time, which is my default SVN client in Windows.

  • Maven Integration for Eclipse to get some of the Maven stuff into Eclipse.

  • Spring IDE -- I haven't checked out the full functionality yt, but it seems to be quite nice.

  • Candy for Appfuse -- again, I don't know the full functionality yet.

  • CommonClipse -- provides generators for methods like toString(), hashCode(), ...

Thursday, May 15, 2008

Get Jetty to run without WAR

After looking at Appfuse for quite a while, I finally found out how to enable the mvn jetty:run command. This is especially important for editing the front-end, since it does not need packaging the war file when there were just JSP pages edited.

The solution, hidden somewhere in the AppFuse FAQ:
  1. Run mvn war:inplace in the project's base folder
  2. Delete the folder src/main/webapp/WEB-INF/lib
Nothing more to do. Now you can run mvn jetty:run. Your web page will be updated if you update your jsp files and refresh the browser.

PIX application and Jetty

I do not like to install new software on my production environment, especially when it comes to servers. And I'm currently too lazy to start up a virtual machine and configure it as I need to.

Therefore, I'm pretty fond of the maven jetty plugin. Jetty is a Java web server which can be used to serve your content. The good thing: By using the jetty plugin, I can avoid to set-up a full web server. Instead, I only type mvn jetty:run in order to test my web app on localhost:8080.

I copied the necessary plugin information from an Appfuse 2 project and included it into the pom.xml of the PIX application. The lines I included are:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.6</version>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>3</scanIntervalSeconds>
<scanTargetPatterns>
<scanTargetPattern>
<directory>src/main/webapp/WEB-INF</directory>
<excludes>
<exclude>**/*.jsp</exclude>
</excludes>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</scanTargetPattern>
</scanTargetPatterns>
</configuration>
</plugin>


You can even shorten all this stuff down to:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<contextPath>/</contextPath>
</configuration>
</plugin>

By doing this, you do not rely on a particular version as well. The only thing necessary was the contextPath. Otherwise, jetty will complain that there is "No context on this server matched or handled this request." You will have to click on one link to select the correct context.

Getting the PIX application to run

The pix-demo of the Beginning Spring Framework 2 book seems to be nice to get to know all the technology we're planning to use a little bit better. However, it does not compile as-is, unfortunately, as I've received the error
The plugin 'org.codehaus.mojo:xfire-maven-plugin' does not exist or no valid version could be found


So I entered the build error I received in Google and voilĂ  -- just one hit, but it was the right one. Thanks to user aneesha for providing the hint I needed:

You have to edit your pom.xml file in order to get the app to compile. I'm no pro with maven, so I don't quite get what was done, but here's my working version of pom.xml:
<?xml version="1.0" encoding="UTF-8"?><project>
<modelVersion>4.0.0</modelVersion>
<groupId>wrox</groupId>
<artifactId>pixweb</artifactId>
<packaging>war</packaging>
<version>0.0.1</version>
<description></description>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<filtering>true</filtering>
<directory>src/test/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<wtpversion>1.0</wtpversion>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<version>1.0-20061017.160413-3</version>
<artifactId>xfire-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>wsgen</goal>
</goals>
</execution>
</executions>
<configuration>
<package>com.wrox.webservice.emailvalidation.client</package>
<overwrtite>true</overwrtite>
<generateServerStubs>false</generateServerStubs>
<forceBare>false</forceBare>
<outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
<wsdls>
<wsdl>http://ws.xwebservices.com/XWebEmailValidation/V2/XWebEmailValidation.wsdl</wsdl>
</wsdls>
</configuration>
</plugin>
</plugins>
</build>
<pluginRepositories>
<pluginRepository>
<id>Codehaus Snapshots</id>
<url>http://snapshots.repository.codehaus.org/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>${taglibs-standard-version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl-version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet-api-version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jpa</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.2.1.ga</version>
<exclusions>
<exclusion>
<artifactId>jta</artifactId>
<groupId>javax.transaction</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>geronimo-spec</groupId>
<artifactId>geronimo-spec-jta</artifactId>
<version>1.0.1B-rc4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflow</artifactId>
<version>${spring-webflow-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
<version>${spring-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>${hsqldb-version}</version>
</dependency>
<dependency>
<groupId>org.directwebremoting</groupId>
<artifactId>dwr</artifactId>
<version>${dwr-version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang-version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j-version}</version>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>1.4.8</version>
</dependency>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>0.9</version>
</dependency>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-aegis</artifactId>
<version>${xfire-version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-core</artifactId>
<version>${xfire-version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-spring</artifactId>
<version>${xfire-version}</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-java5</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-generator</artifactId>
<version>${xfire-version}</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-jaxws</artifactId>
<version>${xfire-version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>${activemq-version}</version>
</dependency>
<dependency>
<groupId>fm.void.jetm</groupId>
<artifactId>jetm</artifactId>
<version>${jetm-version}</version>
</dependency>
<dependency>
<groupId>fm.void.jetm</groupId>
<artifactId>jetm-optional</artifactId>
<version>${jetm-version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>${cglib-version}</version>
</dependency>
</dependencies>
<properties>
<activemq-version>4.1.1</activemq-version>
<spring-webflow-version>1.0.3</spring-webflow-version>
<spring-version>2.0.5</spring-version>
<junit-version>3.8.2</junit-version>
<commons-lang-version>2.1</commons-lang-version>
<dwr-version>2.0.1</dwr-version>
<jstl-version>1.0</jstl-version>
<taglibs-standard-version>1.1.1</taglibs-standard-version>
<hsqldb-version>1.8.0.7</hsqldb-version>
<servlet-api-version>2.5</servlet-api-version>
<log4j-version>1.2.14</log4j-version>
<commons-fileupload-version>1.1.1</commons-fileupload-version>
<xfire-version>1.2.4</xfire-version>
<jetm-version>1.2.1</jetm-version>
<cglib-version>2.1_3</cglib-version>
</properties>
</project>


Another thing: If you want the unit tests to succeed, you have to put a file called football.jpg into the folder src\test\resources. Otherwise, you will get an error when calling the unit tests.

By performing this action, I was able to run mvn test successfully. After downloading loads and loads of packages, of course :)

First steps with Spring and Maven

I just started to get acquainted with Spring and Maven two, after currently working on a project using Spring and Hibernate with the Appfuse maven2 archetype. Since this archetype gives me some headache when trying to compile and run the project, I decided to restart from the basics in order to get a grip on all the technology we're using.

I start off with the tutorials and demo projects of the book Beginning Spring Framework 2. You can find all the demos online.

The first example can be found in the directory src\chapter1\springfirst. It is a modularized calculator, taking command line arguments and -- depending on the configuration -- either adds or multiplies the arguments.

The pom.xml file in the base directory contains all dependencies needed, so Maven2 will download these dependencies to compile and run the application. The compilation process ist started by calling
mvn compile
on the command line. Dependencies (spring-aspects, spring-aop, junit) will be downloaded upon the first call.

The target directory now contains the compiled code and needed resources. The example can be started by calling

mvn exec:java -Dexec.mainClass=com.wrox.begspring.CalculateSpring -Dexec.args="3000 3"

This starts the application and calculates the result (either add oder multiply 3000 and 3).

How it works:

The main class CalculateSpring uses Spring in order to execute the multiplication. It uses a bean factory that can be configured in the bean.xml file. The bean.xml looks something like this:

<bean id="screen" class="com.wrox.begspring.ScreenWriter" />
<bean id="multiply" class="com.wrox.begspring.OpMultiply" />
<bean id="add" class="com.wrox.begspring.OpAdd" />

<bean id="opsbean" class="com.wrox.begspring.CalculateSpring">
<property name="ops" ref="multiply" />
<property name="writer" ref="screen"/>
</bean>
Here, the beans are registered and wired.

How the program execution works: Spring creates instances of ScreenWriter, OpMultiply and OpAdd and then another instance of CalculateSpring where it sets references to the defined writer (screen) and operation (multiply). If you change the last part of beans.xml to
<bean id="opsbean" class="com.wrox.begspring.CalculateSpring">
<property name="ops" ref="add" />
<property name="writer" ref="screen"/>
</bean>
and execute the program, you can see that the operation has changed from multiplication to addition.

These are the first steps with Spring and its dependency injection mechanism. More to come, hopefully.

Edit: There is another way of wiring the objects, namely auto-wiring by type. This looks something like this:
<bean id="screen" class="com.wrox.begspring.ScreenWriter" />
<bean id="add" class="com.wrox.begspring.OpAdd" />
<bean id="opsbean" class="com.wrox.begspring.CalculateSpring" autowire="byType" />
Put this in your beans.xml and the wiring will happen automagically.

Wednesday, May 14, 2008

Brainfukk blog launched

Finally, I launched my first blog. It's more a notepad for myself and some other guys than a serious blog. Nevertheless, perhaps somebody might find some of this information useful.

I'll mainly my texts in English, since this is the main language my sources are using (even though I'm a native German speaker -- well, Austrian, but I can speak German fluently as well :)).