siark.com blog

November 6, 2010

Javadoc for Maven Dependencies in Eclipse Helios

Filed under: Eclipse, Java, Maven 2 — Tags: , , — Mark Gould @ 7:47 pm

To be able to view Javadoc for classes packaged in Maven dependencies in Eclipse, go to the Java Resources -> Libraries -> Maven Dependencies in the project and right click on one of the dependencies listed. From the ‘Maven’ menu option choose ‘Download Sources’ and both source and Javadoc will be downloaded. Note that choosing the ‘Javadoc’ option didn’t seem to download the Javadoc!

October 14, 2010

Hibernate 3, Spring Framework 3, MySQL and Maven 2

To add database connectivity to the siark.com webapp I’m using a MySQL database To add database connectivity to the siark.com webapp I’m using a MySQL database and the Hibernate ORM framework. I need to add some dependencies to my pom.xml, some of which (Hibernate) are not in any of the repositories supported by Nexus in it’s out-of-the-box state. The Hibernate jar files are available from the JBoss repository.

To add a new proxy repository to Nexus.

1 Log in to Nexus as an admin.

2 Select ‘Proxy Repository’ from the ‘Add’… menu.

3 Enter the ‘Repository ID’ ‘jboss’, ‘Repository Name’ ‘JBoss Repository’, ‘Remote Storage Location’ ‘http://repository.jboss.org/nexus/content/groups/public’ and press the ‘Save’ button.

4 Select the ‘Public Repositories’ from the repository list. Select the ‘JBoss Repository’ from the ‘Available Repositories’ list and add it to the ‘Ordered Group Repositories’ list. Press the ‘Save’ button.

I can now add Hibernate to my pom.xml.

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-core</artifactId>
	<version>3.5.6-Final</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-annotations</artifactId>
	<version>3.5.6-Final</version>
</dependency>

For some reason it’s also necessary to add javassist to the pom.xml (A Hibernate dependency that isn’t included in the dependencies!)

<dependency>
	<groupId>javassist</groupId>
	<artifactId>javassist</artifactId>
	<version>3.11.0.GA</version>
</dependency>

and slf4j (See http://www.slf4j.org/codes.html#StaticLoggerBinder for more options with slf4j.)

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.5.8</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-simple</artifactId>
	<version>1.5.8</version>
</dependency>

As many of the annotations used in the Hibernate POJO’s are java persistence annotations, it’s necessary to add persistence-api to the pom.xml.

<dependency>
	<groupId>javax.persistence</groupId>
	<artifactId>persistence-api</artifactId>
	<version>1.0</version>
</dependency>

As I’m using Spring I also need to add spring-orm.

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-orm</artifactId>
	<version>${org.springframework.version}</version>
</dependency>

Now I need to configure Spring to use MySQL and Hibernate.

I’ve decided to move my Spring MVC configuration into a file called mvc-config.xml and my Spring database configuraion in db-config.xml. Both of these files are referenced from the igallery-servlet.xml file (see https://siark.wordpress.com/2010/09/29/web-mvc-project-using-spring-framework-3-eclipse-helios-and-maven-2-on-os-x-snow-leopard/). All three of these files are located in the WEB-INF directory.

igallery-servlet.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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- Scans the classpath of this application for @Components (@Service, @Controller, etc...) annotations to deploy as beans -->
	<context:component-scan base-package="com.siark.igallery" />
	
	<!-- Configures Spring MVC -->
	<import resource="mvc-config.xml" />
	
	<!-- Configures Hibernate - Database Config -->
	<import resource="db-config.xml" />
	
</beans>

mvc-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:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

	<!-- Configures the @Controller programming model -->
	<mvc:annotation-driven />

	<!-- Resolves view names to protected .jsp resources within the /WEB-INF/jsp directory -->
	<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
		<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
		<property name="prefix" value="/WEB-INF/jsp/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
</beans>

db-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:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	
	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:jdbc.properties</value>
				<value>classpath:igallery.properties</value>
			</list>
		</property>
	</bean>

	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName">
			<value>${jdbc.driverClassName}</value>
		</property>
		<property name="url">
			<value>${jdbc.url}</value>
		</property>
		<property name="username">
			<value>${jdbc.username}</value>
		</property>
		<property name="password">
			<value>${jdbc.password}</value>
		</property>
	</bean>
	
	<!-- Hibernate SessionFactory -->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource"><ref local="dataSource" /></property>
		<property name="packagesToScan" value="com.siark.igallery.domain" />
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
				<prop key="hibernate.show_sql">false</prop>
			</props>
		</property>
	</bean>

	<!-- Transaction manager for a Hibernate SessionFactory -->
	<tx:annotation-driven />
	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory">
			<ref local="sessionFactory" />
		</property>
	</bean>
</beans>

Finally my database properties are in a file called jdbc.properties which is in the src/main/resources directory but will end up in the WEB-INF/classes directory when the jar is packaged.

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/siark
jdbc.username=username
jdbc.password=password

October 5, 2010

Java Message Resources

I want the siark.com website to have support internationalisation. The first step in achieving this is to use message resources. I have a default resource file called Messages.properties that is stored in the src/main/resources directory. When maven packages the war file, this resource file will be located on the classpath of the war file in WEB-INF/classes (by default).

I also have another message resource file called Messages_en_US.properties. The Messages.properties file is the default and includes all of the website text in English, but as some words have a differnt spelling in the United States (internationalisation is a good example), the alternative spelling is included in the Messages_en_US.properties file. The Messages_en_US.properties file is used if the locale of the browser is set to the United States. The file has the same name as the default messages resource file, but with the language code (en) and country or dialect code (US) appended.

The Messages.properties file.

home.title=siark.com home
home.greeting=Welcome to the internationalised siark.com website.

The Messages_en_US.properties file.

home.greeting=Welcome to the internationalized siark.com website.

The message resource file is used in JSP page by setting the default resource using . An entry in the resource file is accesed using

<?xml version="1.0" encoding="ISO-8859-1" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" 
		xmlns:c="http://java.sun.com/jsp/jstl/core"
		xmlns:fmt="http://java.sun.com/jsp/jstl/fmt"
		version="2.0">
	<fmt:setBundle basename="Messages" />
    <jsp:directive.page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" />
    <!-- According to the W3C XHTML 1.0 Recommendation, an XML declaration is not required, but authors are strongly encouraged to use XML declarations in documents. -->
    <jsp:text>
        <![CDATA[ <?xml version="1.0" encoding="ISO-8859-1" ?> ]]>
    </jsp:text>
    <!-- According to the W3C XHTML 1.0 Recommendation, there must be a DOCTYPE declaration prior to the root element. -->
    <jsp:text>
        <![CDATA[ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ]]>
    </jsp:text>
	<html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
			<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/styles/base.css" />
			<title><fmt:message key="home.title" /></title>
		</head>
		<body>
			<fmt:message key="home.greeting" />
		</body>
	</html>
</jsp:root>

October 1, 2010

Behaviour Driven Development With JBehave Web 3, Selenium and Maven 2 on OS X Snow Leopard

For integration testing on the siark.com webapp I’m using JBehave 3 and Selenium. As an initial test all I’m going to do is test that the home page displays the message “Welcome to the siark.com website.” when a user navigates to /igallery, the root of the siark.com website.I’m using the Page Object pattern to construct the tests.

1 Write a scenario and save it in a file called home.story in src/test/resources/com/siark/igallery/stories

Scenario: User opens home page

Given the user opens the home page
Then the home page should be displayed

2 Create a generic page class that I call IGalleryPage. The java file is created in src/test/java/com/siark/igallery.

package com.siark.igallery.pages;

import org.jbehave.web.selenium.SeleniumPage;

import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.condition.ConditionRunner;

public class IGalleryPage extends SeleniumPage {
	
	public IGalleryPage(Selenium selenium, ConditionRunner conditionRunner) {
		super(selenium, conditionRunner);
	}
}

3 Create a HomePage class that deals with the home page specifically. The java file is created in src/test/java/com/siark/igallery/pages.

package com.siark.igallery.pages;

import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.condition.ConditionRunner;

public class HomePage extends IGalleryPage {
	
	public HomePage(Selenium selenium, ConditionRunner conditionRunner) {
		super(selenium, conditionRunner);
	}
	
	public void open() {
		open("/igallery/");
	}
	
	public void verifyPage() {
		textIsVisible("Welcome to the siark.com website.");
	}
}

4 Now create a class called IGallerySteps that defines the annotations used in the story. The java file is created in src/test/java/com/siark/igallery.

package com.siark.igallery;

import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;

import com.siark.igallery.pages.FindSteps;
import com.siark.igallery.pages.HomePage;
import com.siark.igallery.pages.PageFactory;

public class IGallerySteps {
	private final PageFactory pageFactory;
	private HomePage home;
	
	public IGallerySteps(PageFactory pageFactory) {
		this.pageFactory = pageFactory;
	}
	
	@Given("the user opens the home page")
	public void theUserOpensTheHomePage() {        
		home = pageFactory.home();
		home.open();        
	}
	
	@Then("the home page should be displayed")
	public void theHomePageShouldBeDisplayed(){
		home.verifyPage();
	}
}

5 A factory class called PageFactory is used to get page classes. The java class is created in src/test/java/com/siark/igallery/pages.

package com.siark.igallery.pages;

import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.condition.ConditionRunner;

public class PageFactory {
	private final Selenium selenium;
	private final ConditionRunner conditionRunner;
	
	public PageFactory(Selenium selenium, ConditionRunner conditionRunner) {
		this.selenium = selenium;
		this.conditionRunner = conditionRunner;
	}
	
	public HomePage home() {
		return new HomePage(selenium, conditionRunner);
	}
}

6 To configure and run the tests, an embedable runnable class is created, which I have called IGalleryStories. The java file is created in src/test/java/com/siark/igallery.

package com.siark.igallery;

import static java.util.Arrays.asList;
import static org.jbehave.core.io.CodeLocations.codeLocationFromClass;
import static org.jbehave.core.reporters.StoryReporterBuilder.Format.CONSOLE;
import static org.jbehave.core.reporters.StoryReporterBuilder.Format.HTML;
import static org.jbehave.core.reporters.StoryReporterBuilder.Format.TXT;
import static org.jbehave.core.reporters.StoryReporterBuilder.Format.XML;

import java.util.List;

import org.jbehave.core.Embeddable;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.LoadFromClasspath;
import org.jbehave.core.io.StoryFinder;
import org.jbehave.core.junit.JUnitStories;
import org.jbehave.core.reporters.ConsoleOutput;
import org.jbehave.core.reporters.StoryReporter;
import org.jbehave.core.reporters.StoryReporterBuilder;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InstanceStepsFactory;
import org.jbehave.core.steps.SilentStepMonitor;
import org.jbehave.web.selenium.SeleniumConfiguration;
import org.jbehave.web.selenium.SeleniumContext;
import org.jbehave.web.selenium.SeleniumStepMonitor;

import com.siark.igallery.pages.PageFactory;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.condition.ConditionRunner;

public class IGalleryStories extends JUnitStories {
	
	private Selenium selenium = SeleniumConfiguration.defaultSelenium();
	private ConditionRunner conditionRunner = SeleniumConfiguration.defaultConditionRunner(selenium);
	private PageFactory pageFactory = new PageFactory(selenium, conditionRunner);
	private SeleniumContext seleniumContext = new SeleniumContext();

	@Override
	public Configuration configuration() {
		Class<? extends Embeddable> embeddableClass = this.getClass();
		return new SeleniumConfiguration()
			.useSelenium(selenium)
			.useSeleniumContext(seleniumContext)
			.useStepMonitor(new SeleniumStepMonitor(selenium, seleniumContext, new SilentStepMonitor()))
			.useStoryLoader(new LoadFromClasspath(embeddableClass))
			.useStoryReporterBuilder(new StoryReporterBuilder() {
			
				@Override
				public StoryReporter reporterFor(String storyPath, Format format) {
					if (format == CONSOLE) {
						return new ConsoleOutput() {
							@Override
							public void beforeScenario(String title) {
								seleniumContext.setCurrentScenario(title);
								super.beforeScenario(title);
							}
						};
					} else {
						return super.reporterFor(storyPath, format);
					}
				}
			}
			.withCodeLocation(CodeLocations.codeLocationFromClass(embeddableClass))
			.withDefaultFormats()
			.withFormats(CONSOLE, TXT, HTML, XML));
	}
	
	@Override
	public List<CandidateSteps> candidateSteps() {
		return new InstanceStepsFactory(configuration(), new IGallerySteps(pageFactory), new FailingScenarioScreenshotCapture(selenium))
		.createCandidateSteps();
	}
	
	@Override
	protected List<String> storyPaths() {
		return new StoryFinder().findPaths(codeLocationFromClass(this.getClass()).getFile(), asList("**/*.story"), null);
	}
}

This class is actually the same class provided on the JBehave website 🙂

7 The last class is also from the JBehave website and is the FailingScenarioScreenshotCapture used in IGalleryStories. It is also created in src/test/java/com/siark/igallery.

package com.siark.igallery;

import org.jbehave.core.annotations.AfterScenario;
import org.jbehave.core.annotations.AfterScenario.Outcome;
import org.jbehave.web.selenium.PerScenarioSeleniumSteps;

import com.thoughtworks.selenium.Selenium;

public class FailingScenarioScreenshotCapture extends PerScenarioSeleniumSteps {
	public FailingScenarioScreenshotCapture(Selenium selenium) {
		super(selenium);
	}
	
	@AfterScenario(uponOutcome = Outcome.FAILURE)
	public void afterFailingScenario() throws Exception {
		String home = System.getenv("HOME");
		selenium.captureScreenshot(home+"/failedScenario.png");
	}
}

8 Finally I need to add a dependency for jbehave-web-selenium, the maven-jetty-plugin plugin so that the Jetty application server can be started and stopped and the igallery.war file can be deployed to it, the selenium-maven-plugin plugin to start and stop the Selenium server and lastly the jbehave-maven-plugin plugin to run the integration tests. The Jetty server will listen on port 8080 for HTTP requests, and the Selenium server will listen on port 4444.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.siark.igallery</groupId>
	<artifactId>igallery</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>Siark iGallery Webapp</name>
	<url>http://www.siark.com</url>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${org.springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${org.springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.jbehave.web</groupId>
			<artifactId>jbehave-web-selenium</artifactId>
			<version>3.0</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>igallery</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>maven-jetty-plugin</artifactId>
				<configuration>
					<webApp>${project.build.directory}/igallery.war</webApp>
					<webAppConfig>
						<contextPath>/igallery</contextPath>
					</webAppConfig>
				</configuration>
				<executions>
					<execution>
						<id>start-jetty</id>
						<phase>pre-integration-test</phase>
						<goals>
							<goal>run-war</goal>
						</goals>
						<configuration>
							<scanIntervalSeconds>0</scanIntervalSeconds>
							<daemon>true</daemon>
						</configuration>
					</execution>
					<execution>
						<id>stop-jetty</id>
						<phase>post-integration-test</phase>
						<goals>
							<goal>stop</goal>
						</goals>
						<configuration>
							<stopKey>stopJetty</stopKey>
							<stopPort>9966</stopPort>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>selenium-maven-plugin</artifactId>
				<version>1.0.1</version>
				<executions>
					<execution>
						<id>start-selenium</id>
						<phase>pre-integration-test</phase>
						<goals>
							<goal>start-server</goal>
						</goals>
						<configuration>
							<background>true</background>
							<debug>false</debug>
							<logOutput>true</logOutput>
						</configuration>
					</execution>
					<execution>
						<id>stop-selenium</id>
						<phase>post-integration-test</phase>
						<goals>
							<goal>stop-server</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.jbehave</groupId>
				<artifactId>jbehave-maven-plugin</artifactId>
				<version>3.0</version>
				<executions>
					<execution>
						<id>run-stories</id>
						<phase>integration-test</phase>
						<configuration>
							<includes>
								<include>**/*Stories.java</include>
							</includes>
							<scope>test</scope>
						</configuration>
						<goals>
							<goal>run-stories-as-embeddables</goal>
						</goals>
					</execution>
				</executions>
				<dependencies>
					<dependency>
						<groupId>org.jbehave.web</groupId>
						<artifactId>jbehave-web-selenium</artifactId>
						<version>3.0</version>
					</dependency>
				</dependencies>
			</plugin>
		</plugins>
	</build>
	<properties>
		<org.springframework.version>3.0.4.RELEASE</org.springframework.version>
	</properties>
</project>

September 29, 2010

Web MVC Project using Spring Framework 3 Eclipse Helios and Maven 2 on OS X Snow Leopard

I want to create the simplest of webapps using Spring Framework 3 and JSP documents. The version of JSP is 2.1 as that is the version used by Tomcat 6.

1 Update the pom.xml with the Spring Framework dependencies.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.siark.igallery</groupId>
	<artifactId>igallery</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>Siark iGallery Webapp</name>
	<url>http://www.siark.com</url>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${org.springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${org.springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>igallery</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<properties>
		<org.springframework.version>3.0.4.RELEASE</org.springframework.version>
	</properties>
</project>

2 Update the web.xml file to include the Spring Framework Dispatcher Servlet.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
	version="2.5">
	<display-name>igallery</display-name>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	<servlet>
		<servlet-name>igallery</servlet-name>
			<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>igallery</servlet-name>
		<url-pattern>*.html</url-pattern>
	</servlet-mapping>
</web-app>

The Dispatcher Servlet handles requests to all resources that have the extension html. The DispatcherServlet class is in the spring-webmvc artifact.

3 I want the JSP pages to be in the WEB-INF/jsp directory. As this directory is not part of the public hierarchy, the welcome page (index.jsp) must redirect to the home page (home.html) which is then intercepted by the Spring Framework Dispatcher servlet.

<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" info="siark.com home page" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
		<title>siark.com home</title>
	</head>
	<body>
		<%-- Redirected because we can't set the welcome page to a virtual URL. --%>
		<c:redirect url="home.html" />
	</body>
</html>

4 The Spring Framework Web MVC uses a special file to configure it’s web application context and is based on the servlet name assigned to the dispatcher servlet (igallery). This file then is igallery-servlet.xml and by default is in the WEB-INF directory.

<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	
	<context:component-scan base-package="com.siark.igallery" />
	
	<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
		<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
		<property name="prefix" value="/WEB-INF/jsp/"/>
		<property name="suffix" value=".jspx"/>
	</bean>
</beans>

This minimal web application context configuration file includes an entry to enable auto-detection of annotated controllers and an entry to configure the view resolver.

5 The controller for the home page is as simple as possible and just handles an HTTP GET request.

package com.siark.igallery.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

@Controller
@RequestMapping("/home")
public class HomeController {
	
	protected final Log logger = LogFactory.getLog(getClass());
	
	/**
	 *
	 */
	@RequestMapping(method=RequestMethod.GET)
	public void get() {
		logger.info("Returning the home view.");
	}
}

The Controller annotation is in the spring-context artifact and the RequestMapping and RequestMethod annotations are in the spring-web artifact.

6 The home page itself is home.jspx and is in the WEB-INF/jsp directory. It is a JSP document.

<?xml version="1.0" encoding="ISO-8859-1" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" 
		xmlns:c="http://java.sun.com/jsp/jstl/core"
		xmlns:fmt="http://java.sun.com/jsp/jstl/fmt"
		version="2.0">
    <jsp:directive.page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" />
    <!-- According to the W3C XHTML 1.0 Recommendation, an XML declaration is not required, but authors are strongly encouraged to use XML declarations in documents. -->
    <jsp:text>
        <![CDATA[ <?xml version="1.0" encoding="ISO-8859-1" ?> ]]>
    </jsp:text>
    <!-- According to the W3C XHTML 1.0 Recommendation, there must be a DOCTYPE declaration prior to the root element. -->
    <jsp:text>
        <![CDATA[ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ]]>
    </jsp:text>
	<html xmlns="http://www.w3.org/1999/xhtml">
		<head>
			<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
		</head>
		<body>
			<p>Welcome to the siark.com website.</p>
		</body>
	</html>
</jsp:root>

September 20, 2010

Installing Hudson CI on OS X Snow Leopard and Configuring it to use Subversion, Maven 2 and Nexus

Filed under: Maven 2, Systems Administration — Tags: , , , , , — Mark Gould @ 2:59 pm

1 Download the hudson.war file.

2 Create a HUDSON_HOME directory. As I did with Nexus, I am creating this in the Tomcat home directory. Create the .m2 directory for maven and copy the settings.xml that configures Maven 2 to use Nexus.

$ cd /Library/Tomcat/Home
$ mkdir .hudson
$ cd .hudson
$ mkdir .m2
$ cp ~/.m2/settings.xml .
$ sudo chown tomcat settings.xml

3 Edit the Tomcat5.sh script used to start Tomcat and add a HUDSON_HOME environment variable.

$ cd bin
$ sudo vi Tomcat5.sh
...
CATALINA_OPTS="-DHUDSON_HOME=$CATALINA_HOME/.hudson/ -Xmx512m -Djava.awt.headless=true"
...

4 Stop and restart Tomcat.

$ sudo ./Tomcat5.sh stop
$ sudo ./Tomcat5.sh start

5 Deploy the hudson.war using the Tomcat Web Application Manager. To do this navigate to http://localhost:8080/manager, and half way down the page is the Deploy section with a WAR file to deploy subsection. Press the Choose File button to choose the hudson.war file. then press the Deploy button to deploy it.

6 Navigate to http://localhost:8080/hudson to check the installation worked.

7 On the Hudson home page click the ‘New Job’ link. Add the job name igallery and choose the ‘Build a maven 2 project’ radio button. Click the ‘OK’ button.

8 On the next page, choose the ‘Subversion’ radio button under the ‘Source Code Management’ section. Add ‘http://localhost/svn/repos&#8217; in the ‘Modules Repository URL’. You will need to enter the username and password setup for Subversion. Uncheck the ‘Use update’ checkbox and check the ‘Revert’ checkbox. In the ‘Build Triggers’ section check the ‘Poll SCM’ checkbox and choose a schedule. In the ‘Build’ section click the ‘system configuration’ link for the Maven Version. On the Hudson configuration page uncheck the tick box ‘Install automatically’ in the ‘Maven’ section and add ‘/usr/share/maven’ to ‘MAVEN_HOME’. Add a name for this maven installation. In the ‘Global properties’ section tick the ‘Environment variables’ checkbox and add the environment variable ‘HOME’ and give it the value ‘/Library/Tomcat/Home/.hudson’.

9 Back on the igallery job configuration page add ‘package’ to the ‘Goals and Options’ of the ‘Build’ section. Click the ‘Save’ button.

September 16, 2010

Creating a Simple Maven 2 Webapp in Eclipse

Filed under: Eclipse, Maven 2 — Tags: , , , , — Mark Gould @ 3:53 pm

1 The first component to create is the pom.xml file. An easy way to generate a simple pom.xml file is to use the Maven 2 Archetype plugin to generate a basic Maven 2 project. By default the plugin operates in interactive, so a sequence of questions must be answered to generate the project (This Maven 2 plugin could have been used to generate the original iGallery project that was committed to Subversion in the post Configuring Subversion with Apache on OS X Snow Leopard).

$ cd ~/Development
$ mvn archetype:generate
Choose archetype:
...
82: remote -> maven-archetype-webapp (An archetype which contains a sample Maven Webapp project.)
...
Choose a number: 79:82
Choose version:
...
5: 1.0
Choose a number: 5:5
Define value for property 'groupId': :com.siark.igallery
Define value for property 'artifactId': :igallery
Define value for property 'version': 1.0-SNAPSHOT:
Define value for property 'package': com.siark.igallery:
Confirm properties configuration:
groupId: com.siark.igallery
artifactId: igallery
version: 1.0-SNAPSHOT
package: com.siark.igallery
Y:

3 Copy the contents of the pom.xml in the ~/Development/igallery directory to the empty pom.xml in the igallery Eclipse project.

4 In Eclipse, open the ‘Team Synchronising’ perspective from the ‘Window -> Open Perspective -> Other…’ dialog box.

3 Click the ‘Synchronize’ button to display the ‘Synchronize’ dialog. Select the ‘SVN’ option and press the ‘Next >’ button.

4 On the next ‘Synchronise’ dialog choose the ‘igallery’ project from the ‘Availible resources to Synchronize:’ section. Click the ‘Finish’ button. The igallery project should appear in the ‘Synchronise’ tab of this perspective.

5 The pom.xml file should be listed under the igallery project in the ‘Synchronize’ tab. Right click the pom.xml file and choose ‘Commit…’. In the dialog box that appears, enter a suitable comment to accompany the commit and press the ‘ok’ button.

6 To build the project using the m2eclipse Eclipse plugin, from the Java EE perspective right click the igallery project in the ‘Project Explorer’ and choose ‘Maven package’ from the ‘Run As’ menu item. This will generate a ‘target’ directory in the igallery directory containing amongst other things an igallery.war file. The ‘package’ Maven 2 lifecycle phase does everything except integration tests, installing the WAR file into the repository and deploying the WAR file to Tomcat.

September 15, 2010

Maven 2 and Nexus on OS X Snow Leopard

Filed under: Maven 2 — Tags: , , , — Mark Gould @ 9:31 am

If you have the OS X Developer Tools (Xcode) installed (I have version 3.2.3), it comes with Maven 2. A quick ‘$ which mvn’ reveals mvn to be in /usr/bin. /usr/bin/mvn is a sym link to /usr/share/maven/bin/mvn and /usr/share/maven is actually a sym link to /usr/share/java/maven-2.2.0.

Download the Nexus WAR file (I downloaded nexus-webapp-1.7.2.war). Rename the WAR file nexus.war. I did consider creating a nexus user as Nexus writes information to a directory called sonatype-work, which by default is located in the home directory of the user running Tomcat, but I thought might be better located in a nexus user home directory. However, I decided just to relocate the sonatype-work directory to /Library/Tomcat/Home instead.

I deployed the nexus.war using the Tomcat Web Application Manager. To do this navigate to http://localhost:8080/manager, and half way down the page is the Deploy section with a WAR file to deploy subsection. This will deploy (and expand) the nexus.war file, but doesn’t run it. Once the WAR file is expanded, the location of the sonatype-work directory can be changed by editing the nexus-work property in the plexus.poperties file located in the WEB-INF directory of the expanded Nexus WAR.

Start Nexus by pressing the start button in the Nexus entry of the Applications section of the Tomcat Web Application Manager. If you can’t see the Applications section, press the List Applications button in the Manager section.

Blog at WordPress.com.