Monday, 6 December 2010

jchannel - a Java imageboard

I love the chans, so I though I'd build my own. jchannel is simple, linearly-directed, forced anonymous imageboard, implemented using enterprise Java, EJB 3.1, JPA/EclipseLink, with a Google Web Toolkit front end, designed to run on GlassFish application server.

Features

  • GWT AJAX powered front end
  • JPA RDBMS agnostic back end
  • Rich text editor message input
  • Role based administration & moderation
  • Catgegory/Topic administration
  • Post reporting & IP ban management

Screen Shots

(click to enlarge)
Auto generated contents home page

Thread view with rich text editor input

Topic administration view

Demo

A demo of jchannel with just one topic (/b/) is running at http://jchannel.org

Source Code

The latest source code can be downloaded from Google Code using svn:

svn checkout http://jchannel.googlecode.com/svn/trunk/ jchannel-read-only

or downloaded here:

Usage

Run the project with 'mvn clean install embedded-glassfish:run' and point your brower at http://127.0.0.1:8080/jchannel/.

Running the project with all the tests will create an embedded Derby database to test against, run an embedded GlassFish server, create some default topics and a default admin user.

To log into the admin interface go to http://127.0.0.1:8080/jchannel/#login

and use the login credentials:

username: admin
password: adminadmin

Update 8/12/10

The http://jchannel.org demo now has a bunch of 4chan style topics.

Thursday, 26 August 2010

Maven, Spring, Hibernate & JPA skeleton project

Today I read that Spring-JPA is the 'dream team' for POJO development. So I thought I'd see what all the hype is about. Here is a skeleton Maven project using Spring, JPA implemented with Hibernate and backed with an embedded Derby database.

pom.xml

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.adrianwalker.maven.skeleton.spring.jpa</groupId>
  <artifactId>maven-spring-jpa-skeleton</artifactId>
  <version>0.1.0</version>
  <name>Maven Spring JPA Skeleton</name>

  <description>
    Skeleton project for using Spring, Hibernate and Derby 
    database accessed through JPA.
 
    Usage: mvn clean install
  </description>

  <url>http://www.adrianwalker.org</url>

  <organization>
    <name>adrianwalker.org</name>
    <url>http://www.adrianwalker.org</url>
  </organization>

  <developers>
    <developer>
      <name>Adrian Walker</name>
      <email>ady.walker@gmail.com</email>
      <organization>adrianwalker.org</organization>
      <organizationUrl>http://www.adrianwalker.org</organizationUrl>
    </developer>
  </developers>

  <repositories>
    <repository>
      <id>java.net</id>
      <url>http://download.java.net/maven/2</url>
    </repository>
    
    <repository>
      <id>java.net - legacy</id>
      <url>http://download.java.net/maven/1</url>
      <layout>legacy</layout>
    </repository>    
  </repositories>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

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

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

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

    <dependency>
      <groupId>org.apache.derby</groupId>
      <artifactId>derby</artifactId>
      <version>10.6.1.0</version>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate</artifactId>
      <version>3.2.1.ga</version>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>3.2.1.ga</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.4</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Spring Configuration

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

  <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
    <property name="url"
      value="jdbc:derby:target/database/message;create=true" />
    <property name="username" value="app" />
    <property name="password" value="app" />
  </bean>

  <bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <property name="jpaVendorAdapter">
      <bean
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="databasePlatform" value="org.hibernate.dialect.DerbyDialect" />
      </bean>
    </property>
    <property name="jpaProperties">
      <props>
        <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
      </props>
    </property>
  </bean>

  <bean name="jpaMessageDao"
    class="org.adrianwalker.maven.skeleton.spring.jpa.JpaMessageDao">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
  </bean>
  
  <bean name="messageJpaController"
    class="org.adrianwalker.maven.skeleton.spring.jpa.MessageJpaController">
  </bean>  

  <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="dataSource" ref="dataSource" />
  </bean>

</beans>

Persistence Configuration

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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_1_0.xsd">
    
  <persistence-unit name="messagePersistenceUnit" transaction-type="RESOURCE_LOCAL">
    <class>org.adrianwalker.maven.skeleton.spring.jpa.MessageEntity</class>
  </persistence-unit>
</persistence>

JPA Entity

A very simple entity which has an id and some message text.

MessageEntity.java

package org.adrianwalker.maven.skeleton.spring.jpa;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;

@Entity
@NamedQueries({
  @NamedQuery(
    name="MessageEntity.find",
    query = "SELECT m FROM MessageEntity m"),
  @NamedQuery(
      name = "MessageEntity.count", 
      query = "SELECT COUNT(m) FROM MessageEntity m")
})
public class MessageEntity implements Serializable {

  private static final long serialVersionUID = 1L;
 
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  private String text;

  public MessageEntity() {
  }

  public MessageEntity(final String text) {
    this.text = text;
  }

  public long getId() {
    return id;
  }

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

  public String getText() {
    return text;
  }

  public void setText(final String text) {
    this.text = text;
  }

  @Override
  public String toString() {
    return "MessageEntity [id=" + id + ", text=" + text + "]";
  }
}

Spring DAO vs JPA Controller

Spring provides a JpaDaoSupport class for extension, but this couples your DAO objects to the Spring framework, and you have to do some pretty ugly stuff to execute your named queries:

JpaMessageDao.java

package org.adrianwalker.maven.skeleton.spring.jpa;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;

public final class JpaMessageDao extends JpaDaoSupport {

  public long count() {
    return (Long) getJpaTemplate().execute(new JpaCallback() {

      public Object doInJpa(final EntityManager em) throws PersistenceException {
        Query q = em.createNamedQuery("MessageEntity.count");
        return q.getSingleResult();
      }
    });
  }

  public void create(final MessageEntity message) {
    getJpaTemplate().persist(message);
  }

  public MessageEntity read(final long id) {
    return getJpaTemplate().find(MessageEntity.class, id);
  }

  @SuppressWarnings("unchecked")
  public List<MessageEntity> read(final int firstResult, final int maxResults) {
    return (List<MessageEntity>) getJpaTemplate().execute(new JpaCallback() {

      public Object doInJpa(final EntityManager em) throws PersistenceException {
        Query q = em.createNamedQuery("MessageEntity.find");
        q.setFirstResult(firstResult);
        q.setMaxResults(maxResults);
        return q.getResultList();
      }
    });
  }

  public MessageEntity update(final MessageEntity message) {
    return getJpaTemplate().merge(message);
  }

  public void delete(final MessageEntity message) {
    getJpaTemplate().remove(message);
  }
}

A much neater alternative is to use a JPA style controller which gives you direct access to the EntityManager. The @PersistenceContext annotation is understood by Spring and your entity manager is injected into the controller class.

MessageJpaController.java

package org.adrianwalker.maven.skeleton.spring.jpa;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.PersistenceContext;

public class MessageJpaController {

  @PersistenceContext(unitName = "messagePersistenceUnit")
  private EntityManager em;

  public long count() {
    Query q = em.createNamedQuery("MessageEntity.count");
    return (Long) q.getSingleResult();
  }

  public void create(MessageEntity messageEntity) {
    em.persist(messageEntity);
  }

  public MessageEntity read(final long id) {
    return em.find(MessageEntity.class, id);
  }

  @SuppressWarnings("unchecked")
  public List<MessageEntity> read(int firstResult, int maxResults) {
    Query q = em.createNamedQuery("MessageEntity.find");
    q.setFirstResult(firstResult);
    q.setMaxResults(maxResults);
    return q.getResultList();
  }

  public MessageEntity update(MessageEntity messageEntity) {
    return em.merge(messageEntity);
  }

  public void delete(final MessageEntity message) {
    em.remove(message);
  }
}

Usage & Tests

And finally here are some JUnit 4 tests for both the DAO and Controller.

MessageTest.java

package org.adrianwalker.maven.skeleton.spring.jpa;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:context.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
@Transactional
public final class MessageTest {

  @Autowired
  private MessageJpaController messageJpaController;
  
  @Autowired
  private JpaMessageDao jpaMessageDao;
   
  @Test
  public void daoCreate() throws Exception {
    
    for (int i = 1; i <= 10; i++) {
      jpaMessageDao.create(new MessageEntity(String.format("Message %s", i)));
    }

    assertEquals(10, jpaMessageDao.count());
  }

  @Test
  public void daoRead() throws Exception {
    List<MessageEntity> messages = jpaMessageDao.read(1, 2);
    assertNotNull(messages);
    assertEquals(2, messages.size());
  }  
  
  @Test
  public void daoUpdate() throws Exception {
    
    MessageEntity message = jpaMessageDao.read(1);

    assertNotNull(message);
    assertEquals("Message 1", message.getText());

    message.setText("Message X");
    message = jpaMessageDao.update(message);

    message = jpaMessageDao.read(1);
    assertEquals("Message X", message.getText());
  }

  @Test
  public void daoDelete() throws Exception {
    long beforeCount = jpaMessageDao.count();
    assertTrue(beforeCount == 10);

    for (int i = 1; i <= 10; i++) {
      MessageEntity message = jpaMessageDao.read(i);
      jpaMessageDao.delete(message);
    }

    long afterCount = jpaMessageDao.count();
    assertTrue(afterCount == 0);
  }
  
  @Test
  public void controllerCreate() throws Exception {    

    for (int i = 11; i <= 20; i++) {
      messageJpaController.create(new MessageEntity(String.format("Message %s", i)));
    }

    assertEquals(10, messageJpaController.count());
  }

  @Test
  public void controllerRead() throws Exception {
    List<MessageEntity> messages = messageJpaController.read(1, 2);
    
    assertNotNull(messages);
    assertEquals(2, messages.size());
  }    
  
  @Test
  public void controllerUpdate() throws Exception {
    MessageEntity message = messageJpaController.read(11);

    assertNotNull(message);
    assertEquals("Message 11", message.getText());

    message.setText("Message X");
    message = messageJpaController.update(message);

    message = messageJpaController.read(11);
    assertEquals("Message X", message.getText());
  }

  @Test
  public void controllerDelete() throws Exception {
    long beforeCount = messageJpaController.count();
    assertTrue(beforeCount == 10);

    for (int i = 11; i <= 20; i++) {
      MessageEntity message = messageJpaController.read(i);
      messageJpaController.delete(message);
    }

    long afterCount = messageJpaController.count();
    assertTrue(afterCount == 0);
  }  
}

Don't Believe The Hype

So basically, for me anyway, using Spring in conjunction with JPA gives you nothing above and beyond using JPA directly. By the the time you decide not to bother with JpaDaoSupport, all that has been achieved is moving connection properties form persistence.xml to context.xml, which just creates more Spring configuration XML clutter.

Spring recognising the @PersistenceContext is a nice touch for testing, but using constructor/setter injection to inject an EntityManager into a JPA controller class is no big deal if you don't want to use spring.

Source Code

Wednesday, 18 August 2010

Using Ordnance Survey OpenData Street View Rasters With GeoServer

Getting the data

The Ordnance Survey OpenData can be downloaded or ordered on DVD from here. I thought I'd order the lot on DVD since its free and it would save my bandwidth and time burning gigs of data to disc.

The data comes on EcoDisc DVDs, packaged in cardboard sleeves:

For this post I'll only be using data on disc 1/6 and 3/6 on the OS Street View discs.

Getting GeoServer

GeoServer stable release can be downloaded here. I punted for the binary download format, because I want to run GeoServer on Linux. After unzipping and starting GeoServer, you should be able to browse to http://localhost:8080/geoserver/web/ and log in using the default username/password:

admin/geoserver

Using the Street View Rasters

First we need to decide what set of tiles we want to use. I only want maps for York and the surrounding area, so using the grid map:

I can see I only want to use the SE data.

Disc 3 of the OS Street View set contains the tif image files I want, located in the directory:

data/se
The files need to be coppied to a location in GeoServers data directory tree. I coppied the images to:
geoserver-2.0.2/data_dir/data/OpenData/StreetView/se

The image files aren't GeoTIFFs, so need geo-referencing data not contained in the image file. This information is held on the disc 1 of the OS Street View set, in the directory:

data/georeferencing files/TFW
We only need the files which start with se, and these need to be copied along side the images in the data directory:
geoserver-2.0.2/data_dir/data/OpenData/StreetView/se
We also need to make sure that the file extension of the tfw is lower cased, using a Linux shell:
rename .TFW .tfw *.TFW
We should now have a directory containing .tif image files and .tfw world files. Each of the tif/tfw pairs needs a file containing projection information before it can be loaded into GeoServer. The files aren't supplied with the raster data, but they are simple to create. The name of the file must match the name of the tif/tfw, and each of the files must contain the same data:

PROJCS["OSGB 1936 / British National Grid", GEOGCS["OSGB 1936", DATUM["OSGB_1936", SPHEROID["Airy 1830",6377563.396,299.3249646, AUTHORITY["EPSG","7001"]], AUTHORITY["EPSG","6277"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4277"]], UNIT["metre",1, AUTHORITY["EPSG","9001"]], PROJECTION["Transverse_Mercator"], PARAMETER["latitude_of_origin",49], PARAMETER["central_meridian",-2], PARAMETER["scale_factor",0.9996012717], PARAMETER["false_easting",400000], PARAMETER["false_northing",-100000], AUTHORITY["EPSG","27700"], AXIS["Easting",EAST], AXIS["Northing",NORTH]]

You can create the files by hand, or running the following Python script in the directory containing the .tif files will do it for you:

import glob

content = 'PROJCS["OSGB 1936 / British National Grid", GEOGCS["OSGB 1936", DATUM["OSGB_1936", SPHEROID["Airy 1830",6377563.396,299.3249646, AUTHORITY["EPSG","7001"]], AUTHORITY["EPSG","6277"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4277"]], UNIT["metre",1, AUTHORITY["EPSG","9001"]], PROJECTION["Transverse_Mercator"], PARAMETER["latitude_of_origin",49], PARAMETER["central_meridian",-2], PARAMETER["scale_factor",0.9996012717], PARAMETER["false_easting",400000], PARAMETER["false_northing",-100000], AUTHORITY["EPSG","27700"], AXIS["Easting",EAST], AXIS["Northing",NORTH]]'

tifs = glob.glob('*.tif')

for tif in tifs:
  prj = tif.split('.')[0] + '.prj'
  file = open(prj,'w')
  file.writelines(content)
  file.close()


If you have a data directory with files that look a bit like this, then you're ready to load them into GeoServer:

  1. Load GeoServer up in a browser http://localhost:8080/geoserver/web/ and login. Click the 'Workspaces' link in the 'Data' section of the left hand navigation bar. Now click the 'Add new workspace' link.

  2. Name the workspace OpenData and give it the URI https://www.ordnancesurvey.co.uk/, and click save.

  3. Click the 'Stores' link in the 'Data' section of the left hand navigation bar. Now click the 'Add new Store' link. Under the 'Raster Data Sources' heading, click the 'ImageMosaic' link.

  4. Select the OpenData workspace, name the data source OS Street View SE and point the URL connection parameter to the data directory:
    file:data/OpenData/StreetView/se
    and then click save.

  5. You should be forwarded to the 'New Layer chooser page', click the 'Publish' link next to the 'se' layer in the table.

  6. About halfway down the page there should be inputs for 'Native SRS' and 'Declared SRS', make sure they both contain:
    EPSG:27700
    and click the 'Compute from data' and 'Compute from native bounds' links.

  7. Finally click save.

If all went well you should be able to use GeoServers 'Layer Preview' built in OpenLayers client to view the map:

Source Code

Friday, 13 August 2010

ColdFusion Is Dead (To Me)

So as from today I never have to program any ColdFusion again, it feels pretty good. ColdFusion just doesn't knock my frock off, it never did, and it probably never will.

This isn't one of those ColdFusion is dead, or ColdFusion sucks rants, those things aren't really my call, and besides, I can't be bothered to deal with ColdTard butthurt on my blog.

A Helmet

All I really know is that programming in CF made me feel a lot like this picture, and you secretly know it makes you feel like this too:

Saturday, 26 June 2010

GlassFish Security Review

GlassFish is Sun Microsystems open source application server. It is a competitor to Jboss AS and Apache Geronimo in the open source arena, and is my app server of choice.

Packt Publishing requested that I review one of their latest titles on the subject of GlassFish: GlassFish Security by Masoud Kalali, available to buy from Packt's web site.

I first took a look at GlassFish around 2007 whilst investigating EJB 3.0 and container managed persistence using JPA. Since then GlassFish has been my favourite platform for building web applications and web services, backed by EJB and JPA, sometimes taking advantage of GlassFish's load balancing and clustering features and excellent integration with other open source projects.

GlassFish Security has been a worth while read, adding to my awareness and knowledge of Java EE security best practices. I will definitely be applying the information presented in the book to current projects and future system design and development work.

GlassFish Security covers a very wide range of security topics, some of which will be applicable to web applications deployed on any JEE application server, whilst others are GlassFish and even host operating system specific.

The book doesn't just focus on programmatic security, making use of security APIs, annotations and XML configuration, but takes more of a complete systems view. OS and network security constraints, as well as enterprise wide system architecture considerations are explored.

The book is targeted at developers and system administrators, who have a sound footing working with JEE application servers, EJB development and have a working knowledge of Linux. To fully take advantage of this book you should know your way around the latest versions of GlassFish and probably NetBeans, have a Debian or Ubuntu install available, and have a keen interest in designing systems with security built in from the start.

The title of the book could quite easily have been GlassFish Security with OpenDS and OpenSSO, as they feature heavily in the later chapters. If your project has no need to interact with an LDAP server, or your organisation has no strategy for a single sign on solution or identity federation management, then these chapters may not be useful to you.

The book starts out from first principals with an overview of Java EE architecture and application modularisation and deployment. It's a slow start for an audience with experience of developing enterprise Java applications, but it establishes common terminology and is a good starting point to introduce fundamental security topics from.

The pace quickly picks up, security concepts such as authentication and authorisation, programmatic EJB security, XML configuration vs annotations, and roles, principals and groups are explained and demonstrated.

Covered next are the default security realms available to GlassFish and how to implementing custom realms and authentication methods.

A source code download for this and subsequent chapters is provided, although currently the source code for chapter two seems to be a duplicate of chapter three. Also, the book states that the build and deployment system used for the source is Maven, which it isn't. The source comes with standard NetBeans project files which use Ant for build and deployment.

Chapter three is probably the most useful for developers starting out with EJB development and web application security, and where the first real bit of programming starts. The chapter guides us through the end-to-end development and deployment of a JEE web application. The code uses a JPA persistence layer for accessing a MySQL database, a servlet containing some business logic and a simple JSP front end. Security constraints are are applied to the application, with authentication and authorisation done via interaction with a file security realm.

Some Linux administration knowledge is required for the next chapter concerned with locking down security on the host operating system and Java virtual machine. The examples given are intended for Debian based Linux distributions and consist of configuring user, file system, disk quota, network interface and port restrictions. JVM and GlassFish policy file configuration are explored, as well as a discussion on the advantages of enabling default auditing modules.

Naturally following on from the previous chapter, chapter five is concerned with configuring the GlassFish server itself, both from the command line and the admin interface. Restricting the IP addresses authorised to access network listeners and isolating applications using virtual servers are covered.

From chapter six onwards the book takes more of an architectural approach to security considerations.

This chapter describes to us the hierarchical nature of security data, and why it might not be best stored in a relational database, and instead in a directory service. OpenDS, an open source LDAP server is introduced, and its installation, administration, configuration and integration with our applications are explored.

Chapters seven, eight and nine, the remainder of the book, revolve around another open source project OpenSSO. OpenSSO is a single sign on solution which integrates seamlessly with GlassFish. Useful topics from chapter seven include using RESTful calls to the OpenSSO API to authenticate and authorise users.

Chapter eight introduces SSO Agents and filters configured in an applications web.xml to intercept calls and apply security measures. OpenSSO allows for very fine grained access controls which require no changes to application code and can be managed all from one place, very useful stuff that I'd like to take advantage of in future systems.

Finally, chapter nine builds on how to use OpenSSO in conjunction with a Web Services Agent to secure a simple SOAP web service deployed on GlassFish.

Overall this is a good book, covering a much wider range of security aspects than I expected. It gives you a great starting point on a breadth of topics but doesn't get to cover them in great depth.

As a developer I would have liked to see more advanced examples for chapters one to five, really getting to the nitty gritty of some real world examples. Maybe some advice on good practice when faced with tough design choices, how to avoid common pitfalls, security patterns and anti-patterns; the sort off stuff above and beyond what you might get from online GlassFish tutorials and Javadoc.

OpenSSO probably deserves a book in its own right, and although I have no immediate application for the information in the chapters that feature it, I'm glad I read them and now have a basic understanding to build on in the future. GlassFish Security benefits form these inclusions and they help it to be the comprehensive introduction to security that it is.

I was disappointed that source code printed in the book and available for download occasionally contained errors and wasn't supplied with Maven build scripts. These things slightly reduce the quality of an otherwise well written and well structured book.

I will definitely be implementing some of the information presented in this book in future Enterprise Java projects; I'll always consider the pros and cons of using a directory server before storing user credentials in relational tables; and GlassFish Security will be my first reference when considering system security design and implementation.

Downloads From Packt

Source

Here is some of the Packt example code repackaged as Maven projects.

Errata

Here are some errors I found in the printed code and xml configuration. Check the Packt support page for a complete listing.

  • Page 22 - missing closing XML tag </web-resource-name>.
  • Page 32 - missing double quotes and comma for @DeclareRoles annotation parameters.
  • Page 50 - missing single quotes around manager on second sql insert statement.
  • Page 85 - empty, unused method proccessRequest in Converter servlet.
  • Page 237 - stringEcho method annotation should be @WebMethod.

Wednesday, 21 April 2010

map, foldr & foldl higher-order functions in Java

Here are my implementations of some higher-order functions in java, foldl, foldr, map and others.

Functionals.java

package org.adrianwalker.functional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class Functionals {

  private static final String EMPTY_STRING = "";
  private static final List EMPTY_LIST = Collections.EMPTY_LIST;

  private Functionals() {
  }

  public static <T1, T2> T2 foldr(final Function<T1, T2> f, final T2 b, final List<T1> lst) {
    if (lst.isEmpty()) {
      return b;
    }

    return f.call(head(lst), foldr(f, b, tail(lst)));
  }

  public static <T1, T2> T2 foldl(final Function<T1, T2> f, final T2 b, final List<T1> lst) {
    if (lst.isEmpty()) {
      return b;
    }

    return foldl(f, f.call(head(lst), b), tail(lst));
  }

  public static <T1, T2> List<T2> map(final Function<T1, T2> f, final List<T1> lst) {
    return foldr(new Function<T1, List<T2>>() {

      @Override
      public List<T2> call(final T1 t1, final List<T2> t2) {
        return cons(f.call(t1), t2);
      }
    }, (List<T2>) EMPTY_LIST, lst);
  }

  public static <T1> List<T1> cons(final T1 x, final List<T1> xs) {
    List<T1> lst = newList(xs.size() + 1);
    lst.add(0, x);
    lst.addAll(1, xs);
    return Collections.unmodifiableList(lst);
  }

  public static <T1> T1 head(final List<T1> lst) {
    return lst.get(0);
  }

  public static <T1> List<T1> tail(final List<T1> lst) {
    List<T1> xs = newList(lst.size() - 1);
    xs.addAll(0, lst.subList(1, lst.size()));
    return Collections.unmodifiableList(xs);
  }

  public static List<Character> explode(final String string) {
    if (string.isEmpty()) {
      return (List<Character>) EMPTY_LIST;
    }

    return cons(string.charAt(0), explode(string.substring(1)));
  }

  public static String implode(final List<Character> lst) {
    return foldl(new Function<Character, String>() {

      @Override
      public String call(final Character t1, final String t2) {
        return t2 + t1;
      }
    }, EMPTY_STRING, lst);
  }

  public static <T1> List<T1> rev(final List<T1> lst) {
    return foldl(new Function<T1, List<T1>>() {

      @Override
      public List<T1> call(final T1 t1, final List<T1> t2) {
        return cons(t1, t2);
      }
    }, (List<T1>) EMPTY_LIST, lst);
  }

  private static <E> List<E> newList(final int initialCapacity) {
    return new ArrayList<E>(initialCapacity);
  }
}

Example Usage

Summing a list of integers using foldr.

List<Integer> input = Arrays.asList(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

Integer output = Functionals.foldr(new Function<Integer, Integer>() {

  @Override
  public Integer call(final Integer t1, final Integer t2) {
    return t1 + t2;
  }
}, 0, input);

System.out.println(String.format("Sum: %s", output));

Source Code

Download the source and see the unit tests for other examples of usage.

Monday, 12 April 2010

ColdFusion Head First Design Patterns: Decorator

Continuing in this design patterns series taken from Head First Design Patterns, is a ColdFusion implementation of the Decorator Pattern.

Abstract Beverage

Beverage.cfc

<cfcomponent output="false">

  <cfset VARIABLES.description = "Unknown Beverage">
  
  <cffunction access="public" returntype="string" name="getDescription">
    <cfreturn VARIABLES.description>
  </cffunction>

  <cffunction access="public" returntype="numeric" name="cost" >    
  </cffunction>

</cfcomponent>

Abstract Decorator

CondimentDecorator.cfc

<cfcomponent extends="Beverage" output="false">
  
  <cffunction access="public" returntype="string" name="getDescription">
  </cffunction>
  
</cfcomponent>

Concrete Beverages

HouseBlend.cfc

<cfcomponent extends="Beverage" output="false">

  <cffunction access="public" name="init" returntype="HouseBlend">
    <cfset VARIABLES.description = "House Blend Coffee">
    
    <cfreturn THIS>
  </cffunction>  

  <cffunction access="public" returntype="numeric" name="cost" >
    <cfreturn 0.89>    
  </cffunction>  

</cfcomponent>

Espresso.cfc

<cfcomponent extends="Beverage">

  <cffunction access="public" name="init" returntype="Espresso">
    <cfset VARIABLES.description = "Espresso">
    
    <cfreturn THIS>
  </cffunction>

  <cffunction access="public" returntype="numeric" name="cost" >
    <cfreturn 1.99>    
  </cffunction>
  
</cfcomponent>

Concrete Decorators

Mocha.cfc

<cfcomponent extends="CondimentDecorator">

  <cffunction access="public" name="init" returntype="Mocha">
    <cfargument type="Beverage" name="beverage">
    
    <cfset VARIABLES.beverage = ARGUMENTS.beverage>
    
    <cfreturn THIS>
  </cffunction>

  <cffunction access="public" returntype="string" name="getDescription">
    <cfreturn VARIABLES.beverage.getDescription() & ", Mocha">
  </cffunction>

  <cffunction access="public" returntype="numeric" name="cost" >
    <cfreturn 0.20 + VARIABLES.beverage.cost()>
  </cffunction>

</cfcomponent>

Test Page

StarbuzzCoffee.cfm

<cfset beverage = createObject("component", "Espresso").init()>

<cfset beverage2 = createObject("component", "HouseBlend").init()>
<cfset beverage2 = createObject("component", "Mocha").init(beverage2)>
<cfset beverage2 = createObject("component", "Mocha").init(beverage2)>

<cfoutput>
  #beverage.getDescription()# $#beverage.cost()#
  <br />
  #beverage2.getDescription()# $#beverage2.cost()#
</cfoutput>

Source Code

Thursday, 8 April 2010

ColdFusion Builder Crack

ColdFusion Builder has been my weapon of choice for writing CFML since it has been in beta and free. Now it's at version 1.0.0, and Adobe has decided to charge $299 for it, I think I'll be going back to CFEclipse.

Shouldn't Adobe be trying to build it's user base by giving CFBuilder away for free? Even Microsoft has got in on the act with it's Express tools.

I also find it quite a poor show that Adobe has taken an Open Source project, Eclipse, added very little to it, re-branded it and is trying to charge the CF developer community, who are inexplicably loyal to Adobe, $299 a pop for it.

So just for shits and giggles here is how you might crack CFBuilder to continue using it for free.

You will need:-

  1. ColdFusion Builder Trial
  2. JD-GUI Java Decompiler
  3. 7-Zip

If you installed CFBuilder in the default location, the class files we need to modify are in:

C:\Program Files\Adobe\Adobe ColdFusion Builder\plugins\com.adobe.ide.coldfusion.common_1.0.0.271911.jar

Start the decompiler and open the above JAR file. The class we need to edit is:

com.adobe.ide.coldfusion.common.util.TestUtil.class

Navigate to the TestUtil class, the JD decompiler produces the following code:

TestUtil.java

package com.adobe.ide.coldfusion.common.util;

import com.adobe.ide.amt.Activator;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.widgets.Display;

public class TestUtil
{
  private Calendar _expDate = null;
  private Calendar _installDate = null;
  private int _evalDays = 30;
  private boolean _expired = false;
  private File _hiddenFile = null;
  private static final long MAGIC = -889275714L;
  private String _code = null;
  private boolean isBeta = true;
  private static final TestUtil instance = new TestUtil();
  private boolean isCheckDone = false;
  private boolean isValidLicense = false;
  private static final Object lockObject = new Object();
  private boolean cfbStartupPluginLoaded = false;

  public static final Object getLockObject() {
    return lockObject;
  }

  public static TestUtil getInstance() {
    return instance;
  }

  private TestUtil()
  {
    Timer timer = new Timer();

    TimerHelper timerTask = new TimerHelper(null);

    timer.schedule(timerTask, 150000L);

    this._expDate = Calendar.getInstance();
    this._expDate.set(2010, 2, 30);
    checkExpired();
  }

  static Long encodeDate(long time) {
    return Long.valueOf(0xCAFEBABE ^ (time >>> 32 | time << 32));
  }

  private void setCode() {
    Calendar now = Calendar.getInstance();
    this._code = encodeDate(now.getTime().getTime()).toString();
  }

  private void checkExpired() {
    if (!this.isBeta) {
      this._expired = false;
      return;
    }

    setHiddenFile();
    if (!hiddenFileExists()) {
      createFileIfNeeded();
    }

    setHiddenProps();
    if (!checkHiddenFile())
      this._expired = true;
  }

  private void createFileIfNeeded()
  {
    Process p = null;
    setCode();
    try {
      PrintWriter out = new PrintWriter(
        new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this._hiddenFile))));
      out.println(this._code);
      out.close();
      if (!File.separator.equalsIgnoreCase("\\")) {
        break label99;
      }

      String command = "attrib +H " + this._hiddenFile.getPath();
      p = Runtime.getRuntime().exec(command);
      label99: p.waitFor();
    }
    catch (Exception localException) {
    }
    finally {
      if (p != null)
        p.destroy();
    }
  }

  private int getDaysDiffFromHiddenFile()
  {
    int _installYear = this._installDate.get(1);
    int _expYear = this._expDate.get(1);
    int yearDiff = _expYear - _installYear;

    return this._expDate.get(6) - 
      this._installDate.get(6) + 365 * yearDiff;
  }

  private void emptyHiddenFile() {
    Process p = null;
    try {
      this._hiddenFile.delete();
      this._hiddenFile.createNewFile();
      if (!File.separator.equalsIgnoreCase("\\")) {
        break label66;
      }

      String command = "attrib +H " + this._hiddenFile.getPath();
      p = Runtime.getRuntime().exec(command);
      label66: p.waitFor();
    }
    catch (Exception localException) {
    }
    finally {
      if (p != null)
        p.destroy();
    }
  }

  private boolean checkHiddenFile()
  {
    this._installDate = Calendar.getInstance();
    if (!isEmpty(this._code)) {
      this._installDate.setTime(decodeDate(Long.parseLong(this._code)));
      this._evalDays = getDaysDiffFromHiddenFile();
      if (getEvalDaysLeftForBeta() <= 0L) {
        this._expired = true;
      }

      return true;
    }
    return false;
  }

  private void checkSystemDate() {
    this._evalDays = getDaysDiffFromBeta();
    if (this._evalDays <= 0)
      this._expired = true;
  }

  private boolean hiddenFileExists()
  {
    return this._hiddenFile.exists();
  }

  private boolean isEmpty(String s)
  {
    return (s == null) || (s.length() == 0);
  }

  static final Date decodeDate(long code) {
    Date d = null;
    if (code != 0L) {
      code = 0xCAFEBABE ^ code;
      long time = code << 32 | code >>> 32;
      d = new Date(time);
    }
    return d;
  }

  private void setHiddenProps() {
    try {
      BufferedReader br = new BufferedReader(new FileReader(this._hiddenFile));
      String line = br.readLine();

      if (!isEmpty(line)) {
        this._code = line;
      }
      br.close();
    }
    catch (Exception localException) {
    }
  }

  private void setHiddenFile() {
    if (File.separator.equalsIgnoreCase("\\"))
    {
      String _hiddenfilename = getSystemDrive() + "\\.tolb3755.bin";
      this._hiddenFile = new File(_hiddenfilename);
    } else {
      String _hiddenfilename = getUnixHome() + "/.tolb3755.bin";
      this._hiddenFile = new File(_hiddenfilename);
    }
  }

  private String getEnv(String envvar)
  {
    Map variables = System.getenv();
    String value = "";
    for (Map.Entry entry : variables.entrySet()) {
      String name = (String)entry.getKey();
      value = (String)entry.getValue();
      if (name.equalsIgnoreCase(envvar.toUpperCase())) {
        break;
      }
    }
    return value;
  }

  private String getUnixHome()
  {
    return getEnv("HOME");
  }

  private String getSystemDrive() {
    return getEnv("SYSTEMDRIVE");
  }

  private int getDaysDiffFromBeta()
  {
    Calendar now = Calendar.getInstance();
    now.setTime(new Date());
    int _nowYear = now.get(1);

    int _expYear = this._expDate.get(1);

    int yearDiff = _expYear - _nowYear;

    int daysLeft = this._expDate.get(6) - 
      now.get(6) + 365 * yearDiff;
    return daysLeft;
  }

  public long getEvalDaysLeftForBeta()
  {
    int daysLeft = getDaysDiffFromBeta();
    if (daysLeft < 0)
      daysLeft = 0;
    return daysLeft;
  }

  public boolean isExpired() {
    return this._expired;
  }

  public boolean isValidAtStartup()
  {
    return isValidHelper(true);
  }

  public boolean isValid() {
    return isValidHelper(false);
  }

  public boolean isValidHelper(boolean calledFromStartup) {
    synchronized (getLockObject()) {
      if (this.isCheckDone) {
        return this.isValidLicense;
      }
    }
    return validate(calledFromStartup);
  }

  private boolean validate(boolean calledFromStartup)
  {
    if (!calledFromStartup)
    {
      return true;
    }

    synchronized (getLockObject())
    {
      try {
        if (this.isCheckDone)
          break label56;
        this.cfbStartupPluginLoaded = true;
        Activator.AMT_Initialize();
        int iProductActionIndicator = Activator.AMT_ObtainLicense(
          "ColdFusionBuilder_Base", 1, 0);
        label56: if (iProductActionIndicator > 1)
          this.isValidLicense = false;
        else {
          this.isValidLicense = true;
        }

      }
      catch (Throwable localThrowable)
      {
        this.isValidLicense = false;
      } finally {
        this.isCheckDone = true;
      }

      return this.isValidLicense;
    }
  }

  private class TimerHelper extends TimerTask
  {
    private TimerHelper()
    {
    }

    public void run()
    {
      try
      {
        if (TestUtil.this.cfbStartupPluginLoaded) {
          break label58;
        }
        Runnable task = new Runnable()
        {
          public void run()
          {
            TestUtil.this.isValidHelper(true);
          }
        };
        if (Platform.getOS().equalsIgnoreCase("macosx"))
        {
          Display.getDefault().syncExec(task);
        }
        else
        {
          Display display1 = new Display();
          display1.syncExec(task);
          label58: display1.dispose();
        }

      }
      catch (Throwable localThrowable)
      {
      }
      finally
      {
        TestUtil.this.isCheckDone = true;
      }
    }
  }
}

What we want to do is remove any validation logic and always return values indicating the the license is valid. After a bit of editing, your new TestUtil.java should look something like:

TestUtil.java

package com.adobe.ide.coldfusion.common.util;

import java.util.Date;

public class TestUtil {
  private static final TestUtil instance = new TestUtil();
  private static final Object lockObject = new Object();

  public static final Object getLockObject() {
    return lockObject;
  }

  public static TestUtil getInstance() {
    return instance;
  }

  private TestUtil() {
  }

  static Long encodeDate(long time) {
    return Long.valueOf(0xCAFEBABE ^ (time >>> 32 | time << 32));
  }

  static final Date decodeDate(long code) {
    Date d = null;
    if (code != 0L) {
      code = 0xCAFEBABE ^ code;
      long time = code << 32 | code >>> 32;
      d = new Date(time);
    }
    return d;
  }

  public long getEvalDaysLeftForBeta() {
    return 1;
  }

  public boolean isExpired() {
    return false;
  }

  public boolean isValidAtStartup() {
    return true;
  }

  public boolean isValid() {
    return true;
  }

  public boolean isValidHelper(boolean calledFromStartup) {
    return true;
  }
}

Recompile this class, and using 7-Zip, replace the old class in:

C:\Program Files\Adobe\Adobe ColdFusion Builder\plugins\com.adobe.ide.coldfusion.common_1.0.0.271911.jar

with the one you just compiled.

Now just start ColdFusion Builder and the registration screen should not appear.

As always with this type of thing, I won't be held responsible for anyone actually doing this, it's naughty and you might get a telling off, so don't do it. Either pay for CFBuilder or go download CFEclipse for free.

Friday, 12 March 2010

Codility.com Demo Test

I like online programming challenges like this demo offered by Codility, it reassures me that I might still be able to think, check it out here:

Sample Test

This was my solution:

private static int equi(int[] A) {

  if (null == A) {
    return -1;
  }

  int size = A.length;

  if (size == 0) {
    return -1;
  }

  long runningSum = 0;
  long[] runningSums = new long[size];

  for (int i = 0; i < size; i++) {
    runningSum += A[i];
    runningSums[i] = runningSum;      
  }

  for (int i = 0; i < size; i++) {
    long lhsSum = runningSums[i] - A[i];
    long rhsSum = runningSum - runningSums[i];

    if(lhsSum == rhsSum) {
      return i;
    }
  }

  return -1;
}

Monday, 1 March 2010

Maven, Jython & Glassfish Example

I keep meaning to get stuck in and learn some Python, and especially make use of some Jython. After coming across this post about using Jython with Glassfish, I thought I'd give it a go myself.

This post is about creating a Maven WAR project making use of Python scripts running on embedded Jython on an embedded Glassfish v3 instance.

Here is the projects POM:

pom.xml

<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>org.adrianwalker.maven.skeleton.war.jython.glassfish</groupId>
  <artifactId>maven-war-jython-glassfish-example</artifactId>
  <packaging>war</packaging>
  <version>0.1.0</version>
  <name>Maven WAR-Jython-Glassfish Example</name>

  <description>
    Example project for creating a Jython WAR running on Glassfish.
    
    Usage: mvn clean install embedded-glassfish:run
  </description>

  <url>http://www.adrianwalker.org</url>

  <organization>
    <name>adrianwalker.org</name>
    <url>http://www.adrianwalker.org</url>
  </organization>

  <developers>
    <developer>
      <name>Adrian Walker</name>
      <email>ady.walker@gmail.com</email>
      <organization>adrianwalker.org</organization>
      <organizationUrl>http://www.adrianwalker.org</organizationUrl>
    </developer>
  </developers>

  <repositories>
    <!--
    Use project lib directory as repository
    -->
    <repository>
      <id>project</id>
      <url>file://${basedir}/lib</url>
    </repository>
  </repositories>

  <pluginRepositories>
    <pluginRepository>
      <id>java.net</id>
      <url>http://download.java.net/maven/glassfish</url>
    </pluginRepository>
  </pluginRepositories>

  <build>
    <finalName>example</finalName>

    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <configuration>
          <webResources>
            <webResource>
              <directory>${basedir}/src/main/python</directory>
              <includes>
                <include>**/*.py</include>
              </includes>
            </webResource>
          </webResources>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.glassfish</groupId>
        <artifactId>maven-embedded-glassfish-plugin</artifactId>
        <configuration>
          <port>8080</port>
          <app>${project.build.directory}/${build.finalName}.war</app>
          <instanceRoot>${project.build.directory}/glassfish</instanceRoot>
          <contextRoot>${build.finalName}</contextRoot>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <!--
    Jython version 2.5.1 and it's standard library
    are included in the project lib directory
    -->
    <dependency>
      <groupId>org.python</groupId>
      <artifactId>jython</artifactId>
      <version>2.5.1</version>
    </dependency>
    <dependency>
      <groupId>org.python</groupId>
      <artifactId>jython-lib</artifactId>
      <version>2.5.1</version>
    </dependency>
  </dependencies>
</project>

I couldn't find the latest version (2.5.1) of the Jython JAR in any Maven repository, so I have included it with the project source code in the lib directory. Also, I wanted the Jython instance to be completely self contained, so instead of referencing an install external to the project, I have JAR'ed up the standard library and included it in the project as lib/jython-lib-2.5.1.jar.

The project uses the embedded Glassfish v3 plugin to quickly start a basic configuration Glassfish server.

PyServlet, distributed with Jython is used to create Java Servlets using Jython source files. It is configured in the project's web.xml:

web.xml

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="Message" version="2.5">

  <servlet>
    <servlet-name>PyServlet</servlet-name>
    <servlet-class>org.python.util.PyServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>PyServlet</servlet-name>
    <url-pattern>*.py</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>Calendar.py</welcome-file>
  </welcome-file-list>
</web-app>

Python code to create a Java servlet which uses the Python calendar library:

Calendar.py

import time
import calendar
from javax.servlet.http import HttpServlet

class Calendar (HttpServlet):
  def doGet(self, request, response):
    response.setContentType ("text/html")
    out = response.getWriter()

    out.println ("""
    <html>
      <head>
        <title>Calendar</title>
      </head>
      <body>
        <h1>Calendar</h1>
        <pre>%s</pre>
      </body>
    </html>
    """ % calendar.calendar(time.localtime()[0]))

Run the project with 'mvn clean install embedded-glassfish:run' and point your brower at http://localhost:8080/example/.

Source Code

Sunday, 28 February 2010

ColdFusion Head First Design Patterns: Observer

Continuing in this design patterns series taken from Head First Design Patterns, is a ColdFusion implementation of the Observer Pattern.

Interfaces

Subject.cfc

<cfinterface>
  <cffunction access="public" returntype="void" name="registerObserver">
    <cfargument required="true" type="Observer" name="o">
  </cffunction>

  <cffunction access="public" returntype="void" name="removeObserver">
    <cfargument required="true" type="Observer" name="o">
  </cffunction>
  
  <cffunction access="public" returntype="void" name="notifyObservers">
  </cffunction>
</cfinterface>

Observer.cfc

<cfinterface>
  <cffunction access="public" returntype="void" name="update">
    <cfargument required="true" type="numeric" name="temperature">
    <cfargument required="true" type="numeric" name="humidity">
    <cfargument required="true" type="numeric" name="pressure">        
  </cffunction>
</cfinterface>

DisplayElement.cfc

<cfinterface>
  <cffunction access="public" returntype="void" name="display">
  </cffunction>
</cfinterface>

Subject Implementation

WeatherData.cfc

<cfcomponent implements="Subject" output="false">

  <cffunction access="public" returntype="Subject" name="init">
    <cfset VARIABLES.observers = arrayNew(1)>
    
    <cfreturn THIS>
  </cffunction> 

  <cffunction access="public" returntype="void" name="registerObserver">
    <cfargument required="yes" type="Observer" name="o" />
    
    <cfset arrayAppend(VARIABLES.observers, ARGUMENTS.o)>
  </cffunction>

  <cffunction access="public" returntype="void" name="removeObserver">
    <cfargument required="yes" type="Observer" name="o" />
    
    <cfset arrayDelete(VARIABLES.observers, ARGUMENTS.o)>    
  </cffunction>

  <cffunction access="public" returntype="void" name="notifyObservers">
    <cfset var LOCAL = {}>
  
    <cfloop index="LOCAL.o" array="#VARIABLES.observers#">
      <cfset LOCAL.o.update(VARIABLES.temperature, VARIABLES.humidity, VARIABLES.pressure)>
    </cfloop>
  </cffunction>
  
  <cffunction access="public" returntype="void" name="measurementsChanged">
    <cfset notifyObservers()>
  </cffunction>
  
  <cffunction access="public" returntype="void" name="setMeasurements">
    <cfargument required="yes" type="numeric" name="temperature" />
    <cfargument required="yes" type="numeric" name="humidity" />
    <cfargument required="yes" type="numeric" name="pressure" />
    
    <cfset VARIABLES.temperature = ARGUMENTS.temperature>
    <cfset VARIABLES.humidity = ARGUMENTS.humidity>
    <cfset VARIABLES.pressure = ARGUMENTS.pressure>
    <cfset measurementsChanged()>
  </cffunction>
</cfcomponent>

Display Elements

CurrentConditionsDisplay.cfc

<cfcomponent implements="Observer, DisplayElement" output="true">

  <cffunction access="public" returntype="CurrentConditionsDisplay" name="init">
    <cfargument required="true" type="WeatherData" name="weatherData">
    
    <cfset VARIABLES.weatherData = ARGUMENTS.weatherData>
    <cfset VARIABLES.weatherData.registerObserver(THIS)>
    
    <cfreturn THIS>
  </cffunction>

  <cffunction access="public" returntype="void" name="update">
    <cfargument required="true" type="numeric" name="temperature">
    <cfargument required="true" type="numeric" name="humidity">
    <cfargument required="true" type="numeric" name="pressure">
    
    <cfset VARIABLES.temperature = ARGUMENTS.temperature>
    <cfset VARIABLES.humidity = ARGUMENTS.humidity>
    <cfset display()>
  </cffunction>

  <cffunction access="public" returntype="void" name="display">
    <cfoutput>
      Current conditions: #VARIABLES.temperature#F degrees and #VARIABLES.humidity#% humidity
      <br />
    </cfoutput>
  </cffunction>
</cfcomponent>

HeatIndexDisplay.cfc

<cfcomponent implements="Observer, DisplayElement" output="true">

  <cffunction access="public" returntype="HeatIndexDisplay" name="init">
    <cfargument required="true" type="WeatherData" name="weatherData">
    
    <cfset VARIABLES.weatherData = ARGUMENTS.weatherData>
    <cfset VARIABLES.weatherData.registerObserver(THIS)>
    
    <cfreturn THIS>
  </cffunction>

  <cffunction access="public" returntype="void" name="update">
    <cfargument required="true" type="numeric" name="temperature">
    <cfargument required="true" type="numeric" name="humidity">
    <cfargument required="true" type="numeric" name="pressure">
    
    <cfset VARIABLES.temperature = ARGUMENTS.temperature>
    <cfset VARIABLES.humidity = ARGUMENTS.humidity>
    <cfset VARIABLES.heatIndex = computeHeatIndex(VARIABLES.temperature, VARIABLES.humidity)>
    <cfset display()>
  </cffunction>

  <cffunction access="public" returntype="void" name="display">
    <cfoutput>
      Heat index is #VARIABLES.heatIndex#
      <br />
    </cfoutput>
  </cffunction>

  <cffunction access="private" returntype="numeric" name="computeHeatIndex">
    <cfargument required="true" type="numeric" name="t">
    <cfargument required="true" type="numeric" name="rh">

    <cfset var LOCAL = {}>
    <cfset LOCAL.index = ((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) +
            (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) +
            (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +
            (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 *  
            (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) +
            (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +     
            0.000000000843296 * (t * t * rh * rh * rh)) -
            (0.0000000000481975 * (t * t * t * rh * rh * rh)))>
  
    <cfreturn LOCAL.index>
  </cffunction> 
</cfcomponent>

Test Page

WeatherStation.cfm

<cfset weatherData = createObject("component", "WeatherData").init()>

<cfset currentDisplay = createObject("component", "CurrentConditionsDisplay").init(weatherData)>
<cfset heatDisplay = createObject("component", "HeatIndexDisplay").init(weatherData)>

<cfset weatherData.setMeasurements(80, 65, 30.4)>
<cfset weatherData.setMeasurements(82, 70, 29.2)>
<cfset weatherData.setMeasurements(78, 90, 29.2)>

Source Code

Saturday, 27 February 2010

Maven GWT WAR & Glassfish skeleton project

Another skeleton project implementing the same functionality as this previous post, a Google Web Toolkit front-end, with an embedded Derby database, accessed using the Java Persistence API; but this time running on an embedded Glassfish 3 instance and making use of EJB3.1 features.

Here is the projects POM:

pom.xml

<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>org.adrianwalker.maven.skeleton.war.gwt.glassfish</groupId>
  <artifactId>maven-war-gwt-glassfish-skeleton</artifactId>
  <packaging>war</packaging>
  <version>0.1.0</version>
  <name>Maven WAR-GWT-Glassfish Skeleton</name>

  <description>
    Skeleton project for creating a GWT WAR running on Glassfish, with a Derby
    database accessed through JPA.

    Usage: mvn clean install embedded-glassfish:run
  </description>

  <url>http://www.adrianwalker.org</url>

  <organization>
    <name>adrianwalker.org</name>
    <url>http://www.adrianwalker.org</url>
  </organization>

  <developers>
    <developer>
      <name>Adrian Walker</name>
      <email>ady.walker@gmail.com</email>
      <organization>adrianwalker.org</organization>
      <organizationUrl>http://www.adrianwalker.org</organizationUrl>
    </developer>
  </developers>

  <repositories>
    <repository>
      <id>java.net</id>
      <url>http://download.java.net/maven/2</url>
    </repository>

    <repository>
      <id>codehaus.org</id>
      <url>http://repository.codehaus.org</url>
    </repository>
  </repositories>

  <pluginRepositories>
    <pluginRepository>
      <id>java.net</id>
      <url>http://download.java.net/maven/glassfish</url>
    </pluginRepository>
  </pluginRepositories>

  <properties>
    <gwt.version>2.0.2</gwt.version>
  </properties>

  <build>
    <outputDirectory>war/WEB-INF/classes</outputDirectory>

    <finalName>message</finalName>

    <resources>
      <resource>
        <directory>${basedir}/src/main/resources</directory>
        <includes>
          <include>META-INF/persistence.xml</include>
        </includes>
      </resource>
    </resources>

    <plugins>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <configuration>
          <filesets>
            <fileset>
              <directory>war</directory>
              <includes>
                <include>**/*</include>
              </includes>
            </fileset>
          </filesets>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>gwt-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.glassfish</groupId>
        <artifactId>maven-embedded-glassfish-plugin</artifactId>
        <configuration>
          <app>${project.build.directory}/${build.finalName}.war</app>
          <instanceRoot>${project.build.directory}/glassfish</instanceRoot>
          <contextRoot>${build.finalName}</contextRoot>
          <configFile>${basedir}/src/test/resources/config/domain.xml</configFile>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.glassfish.extras</groupId>
      <artifactId>glassfish-embedded-all</artifactId>
      <version>3.0</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-servlet</artifactId>
      <version>${gwt.version}</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
      <version>${gwt.version}</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <reporting>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>cobertura-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jxr-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pmd-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>findbugs-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </reporting>
</project>

The embedded Glassfish server is configured using a domain.xml taken from a fresh install of Glassfish 3, located \src\test\resources\config\domain.xml and referenced from the embedded Glassfish Maven plugin configuration element in the pom.xml

The default domain.xml has been amended to include jdbc-resource and jdbc-datasource entries for the web applications database. The domain.xml below shows the amendments, with the rest of the file removed for brevity:

domain.xml

<domain log-root="${com.sun.aas.instanceRoot}/logs" application-root="${com.sun.aas.instanceRoot}/applications" version="10.0">

  ...

  <resources>

    ...

    <jdbc-resource pool-name="MessagePool" jndi-name="jdbc/message" />

    ...

    <jdbc-connection-pool name="MessagePool" datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource" res-type="javax.sql.DataSource">
      <property name="databaseName" value="target/database/message" />
      <property name="connectionAttributes" value=";create=true" />
    </jdbc-connection-pool>
  </resources>
  <servers>
    <server name="server" config-ref="server-config">

      ...

      <resource-ref ref="jdbc/message" />
    </server>
  </servers>

  ...

</domain>

The configured datasource is referenced in the projects persistence.xml, which uses EclipseLink to implement the JPA.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">
  <persistence-unit name="messagePersistenceUnit" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/message</jta-data-source>
    <class>org.adrianwalker.maven.skeleton.jpa.entity.MessageEntity</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Run the project with 'mvn clean install embedded-glassfish:run' and point your brower at http://localhost:8080/message/.

Source Code

Monday, 22 February 2010

ColdFusion Head First Design Patterns: Singleton

Everyone's favorite, The Singleton Pattern, is probably the most overused and least well implemented design pattern.

There are at least seven different variations on the singleton implementation in Java, some are detailed in Head First Design Patterns, some are documented in Joshua Bloch's awesome book Effective Java, and some I've found along the way on the web.

Not all of the variations can be implemented in ColdFusion, but lets list them and pick the best one to code in CFML.

None of them can be implemented in ColdFusion, but lets list them and see why.

Lazily created Singleton with static factory method

Introduced as the first singleton example in Head First Design Patterns. It isn't thread safe.

public final class Singleton {

  private static Singleton instance;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (null == instance) {
      instance = new Singleton();
    }

    return instance;
  }
}

Synchronized lazily created Singleton with static factory method

Introduced as the second example in Head First, it is thread safe but uses a computationally expensive synchronized method.

public final class Singleton {

  private static Singleton instance;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (null == instance) {
      instance = new Singleton();
    }

    return instance;
  }
}

Eagerly created Singleton with static factory method

Detailed as an alternative to synchronizing the getInstance() method in Head First, and presented as a possible implementation in Joshua Bloch's Effective Java. Bloch goes on to note that this implementation and the following implementation can not be made serializable by just adding implements Serializable. All instance fields would need to be declared transient and a readResolve() method provided.

public final class Singleton {

  private static final Singleton INSTANCE = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return INSTANCE;
  }
}

Eagerly created Singleton with public final field

Presented by Bloch as an alternative to the above implementation, it's main advantage is that the class definition clearly shows it is a singleton.

public final class Singleton {

public static final Singleton INSTANCE = new Singleton();

  private Singleton() {
  }
}

Double checked locking

The third and final example in Head First, only synchronizes the first time through the getInstance() method. This implementation of the singleton makes use of the volatile keyword. In Java versions 1.4 and earlier, volatile could allow improper synchonization, this implementation is not portable to older JVMs.

public final class Singleton {

  private volatile static Singleton instance;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (null == instance) {
      synchronized (Singleton.class) {
        if (null == instance) {
          instance = new Singleton();
        }
      }
    }

    return instance;
  }
}

The solution of Bill Pugh

The solution of Bill Pugh uses a nested class to be as lazy as possible, is thread safe without using synchronized or volatile, and is usable with all JVM versions.

public final class Singleton {

  private Singleton() {
  }

  private static class SingletonHolder {

    private static final Singleton INSTANCE = new Singleton();
  }

  public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}

Enum Type

Since version 1.5, Java has had support for enums. The final implementation in Effective Java uses an enumerated type with one element. This is equivalent to the public final field implementation but takes care of serialization automatically, and guarantees only a single instance.

public enum Singleton {

  INSTANCE;
}

From Effective Java:

"a single-element enum type is the best way to implement a singleton."

ColdFusion

ColdFusion doesn't support enum types or nested CFCs, so those are out straight away.

Lazily created Singleton with factory method

A lazily created Singleton fails to be initialized because the component can't see its own private constructor.

LazyFactoryMethod/Singleton.cfc

<cfcomponent output="false" >

  <cffunction access="private" returntype="Singleton" name="init">
    <cfreturn THIS>
  </cffunction>

  <cffunction access="public" returntype="Singleton" name="getInstance">
    <cfif NOT isDefined("VARIABLES.INSTANCE")>
      <cfset VARIABLES.INSTANCE = createObject("component", "Singleton").init()>
    </cfif>
  
    <cfreturn VARIABLES.INSTANCE>  
  </cffunction>
  
</cfcomponent>

Eagerly created Singleton with factory method

An eagerly created Singleton with a factory method goes in to a self referential loop and fills up the stack.

EagerFactoryMethod/Singleton.cfc

<cfcomponent output="false" >

  <cfset VARIABLES.INSTANCE = createObject("component", "Singleton").init()>

  <cffunction access="private" returntype="Singleton" name="init">
    <cfreturn THIS>
  </cffunction>

  <cffunction access="public" returntype="Singleton" name="getInstance">
    <cfreturn VARIABLES.INSTANCE>  
  </cffunction>
  
</cfcomponent>

Eagerly created Singleton with a public field

Same as with the previous eagerly created Singleton, a java.lang.StackOverflowError is thrown on instantiation.

EagerField/Singleton.cfc

<cfcomponent output="false" >

  <cfset THIS.INSTANCE = createObject("component", "Singleton").init()>

  <cffunction access="private" returntype="Singleton" name="init">
    <cfreturn THIS>
  </cffunction>

</cfcomponent>

So ColdFusion can't create real singletons, how bad does that suck?

Source Code

Thursday, 18 February 2010

ColdFusion Head First Design Patterns: Strategy

Following on from my previous post on a ColdFusion implementation of the Head First Design Patterns web MVC Pattern is a ColdFusion implementation of the Strategy Pattern.

The Strategy Pattern is probably the most useful pattern, to quote the Head First book:

"The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it."

Abstract Duck

Duck.cfc

<cfcomponent output="true">

  <cffunction access="public" returntype="void" name="display">    
  </cffunction>
  
  <cffunction access="public" returntype="void" name="performFly">
    <cfset VARIABLES.flyBehaviour.fly()>    
  </cffunction>
  
  <cffunction access="public" returntype="void" name="performQuack">
    <cfset VARIABLES.quackBehaviour.quack()>    
  </cffunction>

  <cffunction access="public" returntype="void" name="swim">
    <cfoutput>All ducks float, even decoys!<br/></cfoutput>    
  </cffunction>

  <cffunction access="public" returntype="void" name="setFlyBehaviour">
    <cfargument required="true" type="FlyBehaviour" name="flyBehaviour">
    <cfset VARIABLES.flyBehaviour = ARGUMENTS.flyBehaviour>
  </cffunction>

  <cffunction access="public" returntype="void" name="setQuackBehaviour">
    <cfargument required="true" type="QuackBehaviour" name="quackBehaviour">
    <cfset VARIABLES.quackBehaviour = ARGUMENTS.quackBehaviour>
  </cffunction>
    
</cfcomponent>

Quack Strategies

Quack.cfc

<cfcomponent implements="QuackBehaviour" output="true">

  <cffunction access="public" returntype="Quack" name="init">
    <cfreturn THIS>
  </cffunction>

  <cffunction access="public" returntype="void" name="quack">
    <cfoutput>Quack<br/></cfoutput>  
  </cffunction>
  
</cfcomponent>

Fly Strategies

FlyWithWings.cfc

<cfcomponent implements="FlyBehaviour" output="true">
  
  <cffunction access="public" returntype="FlyWithWings" name="init">
    <cfreturn THIS>
  </cffunction>   
  
  <cffunction access="public" returntype="void" name="fly">
    <cfoutput>I'm flying!<br/></cfoutput>
  </cffunction>
  
</cfcomponent>

FlyNoWay.cfc

<cfcomponent implements="FlyBehaviour" output="true">
  
  <cffunction access="public" returntype="FlyNoWay" name="init">
    <cfreturn THIS>
  </cffunction>
  
  <cffunction access="public" returntype="void" name="fly">
    <cfoutput>I can't fly<br/></cfoutput>
  </cffunction>
  
</cfcomponent>

FlyRocketPowered.cfc

<cfcomponent implements="FlyBehaviour" output="true">
  
  <cffunction access="public" returntype="FlyRocketPowered" name="init">
    <cfreturn THIS>
  </cffunction>  
  
  <cffunction access="public" returntype="void" name="fly">
    <cfoutput>I'm flying with a rocket!<br/></cfoutput>
  </cffunction>
  
</cfcomponent>

Duck Implementations

MallardDuck.cfc

<cfcomponent extends="Duck" output="true">

  <cffunction access="public" returntype="MallardDuck" name="init">
    <cfset VARIABLES.flyBehaviour = createObject("component", "FlyWithWings").init()>
    <cfset VARIABLES.quackBehaviour = createObject("component", "Quack").init()>
    <cfreturn THIS>
  </cffunction>
  
  <cffunction access="public" returntype="void" name="display">
    <cfoutput>I'm a real Mallard duck<br/></cfoutput>
  </cffunction>
   
</cfcomponent>

ModelDuck.cfc

<cfcomponent extends="Duck" output="true">

  <cffunction access="public" returntype="ModelDuck" name="init">
    <cfset VARIABLES.flyBehaviour = createObject("component", "FlyNoWay").init()>
    <cfset VARIABLES.quackBehaviour = createObject("component", "Quack").init()>
    <cfreturn THIS>
  </cffunction>
  
  <cffunction access="public" returntype="void" name="display">
    <cfoutput>I'm a model duck<br/></cfoutput>
  </cffunction>
   
</cfcomponent>

Test Page

MiniDuckSimulator.cfm

<cfset mallard = createObject("component", "MallardDuck").init()>
<cfset mallard.performQuack()>
<cfset mallard.performFly()>

<cfset model = createObject("component", "ModelDuck").init()>
<cfset model.performFly()>
<cfset model.setFlyBehaviour(createObject("component", "FlyRocketPowered").init())>
<cfset model.performFly()>

Source Code

Tuesday, 16 February 2010

ColdFusion Head First Design Patterns: MVC

Head First Design Patterns is a pretty good book, with loads of good code examples in Java and not nearly as dry as GoF. It's even got a fit bird on the front cover. Careful though, looks like she's rocking an itchy twunt.

Source code for the book is available here.

For this post I've taken the web application MVC code written in Java/Servlets/JSP and re-coded it in CFML.

Model

BeatModel.cfc

<cfcomponent output="false">

  <cfset VARIABLES.bpm = 90>

  <cffunction returntype="BeatModel" name="init">
    <cfreturn this>
  </cffunction>

  <cffunction returntype="void" name="on">
    <cfset setBPM(90)>
  </cffunction>

  <cffunction returntype="void" name="off">
    <cfset setBPM(0)>
  </cffunction>

  <cffunction returntype="void" name="setBPM">
    <cfargument required="yes" type="numeric" name="bpm" />

    <cfset VARIABLES.bpm = ARGUMENTS.bpm>
  </cffunction>

  <cffunction returntype="numeric" name="getBPM">
    <cfreturn VARIABLES.bpm>
  </cffunction>
  
</cfcomponent>

View

DJView.cfm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>DJ View</title>
  </head>
 <body>
    <h1>DJ View</h1> 
    Beats per minute = <cfoutput>#APPLICATION.beatModel.getBPM()#</cfoutput>    
    <br />
    <hr />
    <br />    
    <form method="post" action="DJView.cfc">
      BPM: <input type="text" name="bpm" value="<cfoutput>#APPLICATION.beatModel.getBPM()#</cfoutput>" />
       
      <input type="submit" name="set" value="set" />
      <br />
      <input type="submit" name="decrease" value="<<" />
      <input type="submit" name="increase" value=">>" />
      <br />
      <input type="submit" name="on" value="on" />
      <input type="submit" name="off" value="off" />
    </form>
  </body>  
</html>

Controller

DJView.cfc

<cfcomponent output="false">

  <cfif NOT isDefined("FORM.bpm")>
    <cfset FORM.bpm = APPLICATION.beatModel.getBPM()>
  </cfif>
  
  <cfif isDefined("FORM.set")>
    <cfset APPLICATION.beatModel.setBPM(trim(FORM.bpm))>
  </cfif>
  
  <cfif isDefined("FORM.decrease")>
    <cfset APPLICATION.beatModel.setBPM(APPLICATION.beatModel.getBPM() - 1)>
  </cfif>
 
  <cfif isDefined("FORM.increase")>
    <cfset APPLICATION.beatModel.setBPM(APPLICATION.beatModel.getBPM() + 1)>
  </cfif>
 
  <cfif isDefined("FORM.on")>
    <cfset APPLICATION.beatModel.on()>
  </cfif>

  <cfif isDefined("FORM.off")>
    <cfset APPLICATION.beatModel.off()>
  </cfif>

  <cflocation url="DJView.cfm">

</cfcomponent>

Initialization

Application.cfc

<cfcomponent output="false">

  <cfset THIS.Name = "Head First MVC" />
   
  <cffunction access="public" returntype="void" name="onApplicationStart">
    <cfset APPLICATION.beatModel = createObject("component", "BeatModel").init()>
  </cffunction>
  
</cfcomponent>

Source Code

SQL NULL Rage

Just don’t allow nulls in your relational database tables, just don’t do it. No. No, not even then, no. No. NO.

Null, it doesn’t mean empty string, it doesn’t mean zero, it doesn’t mean missing relationship, it means undefined, it is a mark for missing data. Why would you want to store a mark indicating that data isn’t there instead of just not storing anything?

At what point does using null become overuse? When a couple of columns in a row contain nulls? When most of them contain nulls? When all of them contain nulls and foreign key columns contain nulls? Why don’t you just fill your entire database to capacity with nulls because you expect it to contain real data at some point in the future?

Yeah that's right, because that would be stupid, wouldn’t it?

E. F. Codd, the daddy of the relational model, suggests in his The Relational Model for Database Management Version 2 (go buy it here) that the null mark should be further refined into two marks, one for information that is applicable but absent (A-marks) and another for inapplicable information (I-marks).

He goes on to recommend that A-marks not be allowed in foreign key columns:

"I strongly recommend that database administrators or users consider very carefully the question of whether to permit or prohibit A-marks in foreign-key columns, and also that they document how and why that decision was made."

Codd then gives us two rules for all databases, the first, don’t allow either mark for a primary key and don’t allow I-marks for foreign keys. And the second rule, don’t have foreign keys point to stuff that’s not there:

"There are two integrity rules that apply to every relational database

  1. Type E, entity integrity. No component of a primary key is allowed to have a missing value of any type. No component of a foreign key is allowed to have an I-marked value (missing-and-inapplicable).
  2. Type R, referential integrity. For each distinct, unmarked foreign-key value in a relational database, there must exist in the database an equal value of a primary key from the same domain. If the foreign key is composite, those components that are themselves foreign keys and un-marked must exist in the database as components of at least one primary-key value drawn from the same domain."

This is all good for maintaining referential integrity, but for me, it doesn’t really address the issues. The quad-value logic seems to me to make the problem worse. How do I do arithmetic and aggregation on these two kinds of null mark? How would these two marks eventually manifest themselves in the code? Would I treat them differently? Why do I even care what kind of null it is, I just don’t want nulls to bite me on the arse later.

We should treat null marks as seriously in all columns as Codd proposes we treat them in relation defining columns. Why not keep partitioning the data until we get rid of the nulls? Normalizing to 5th and 6th normal form if we have to!

Much smarter people than me have thought about this and if you are at all bothered in relational database design then check out The Third Manifesto. Especially this PDF, Missing info without nulls.

From the Missing info without nulls abstract:

""The Third Manifesto", by C.J. Date and Hugh Darwen (3rd edition, Addison-Wesley, 2005), contains a categorical proscription against support for anything like SQL’s NULL, in its blueprint for relational database language design."

Sounds good to me.

Monday, 8 February 2010

Java, JDBC & SOAP on Windows Mobile 2003 Pocket PC

Symbol MC50

Check it out! The Symbol MC50 looks smart enough to divide by zero and tough enough to club a seal to death with! Here is how to program it!

This post is about getting Java running on Pocket PC, setting up an Apache Derby embedded database, and using kSOAP 2 to communicate with a web service.

ActiveSync

First off you're going to need to use ActiveSync to create a guest partnership between your Pocket PC and your development machine. If your running Windows XP, get ActiveSync v4.5 from here.

Java ME

Sun and Microsoft don't do a JVM for Pocket PC/Windows Mobile 2003, so a 3rd party JVM is the way to go. There are a few options but I'm having a punt on WebSphere Everyplace Micro Environment (WEME).

You can grab a trial version of WebSphere Everyplace Micro Environment Personal Profile 1.0 for Windows Mobile 2003 from here, or here

Run the EXE file contained in the downloaded WEME ZIP to install the JRE onto your local machine and onto the Pocket PC via ActiveSync.

Java ME JDBC

To use JDBC on the Pocket PC with WEME you will need The JDBC Optional Package for CDC-Based J2ME Applications. The source code is available for download here, or is available as a Maven build at the bottom of this page.

The JDBC optional package Maven build produces a 'jdbc-cdc-1.0.jar' JAR file. Copy the JAR to the Pocket PC JRE folder:

\Program Files\J9\PPRO10\lib\jclPPro10\ext

Web Service

Next knock out a quick SOAP web service for the Pocket PC to access. Below is a simple ColdFusion component which has a getStockPrice method that returns a price for a given stock name:

<cfcomponent namespace="http://www.example.org/stock">

  <cffunction access="remote" returnType="numeric" name="getStockPrice">
    <cfargument required="true" type="string" name="stockName" >
  
    <cfset var stockPrice = -1>
    
    <cfswitch expression="#Trim(ARGUMENTS.stockName)#">      
      <cfcase value="IBM">
        <cfset stockPrice = 123.91>
      </cfcase>

      <cfcase value="MSFT">
        <cfset stockPrice = 28.25>
      </cfcase>

      <cfcase value="ADBE">
        <cfset stockPrice = 32.74>
      </cfcase>

      <cfcase value="ORCL">
        <cfset stockPrice = 23.43>
      </cfcase>      
    </cfswitch>
  
    <cfreturn stockPrice>
  </cffunction>
  
</cfcomponent>

Java Application

The Java application to access the stock web service is built with Maven, and has the following POM file:

pom.xml

<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>org.adrianwalker.pocketpc</groupId>
  <artifactId>pocketpc-derby-ksoap2</artifactId>
  <packaging>jar</packaging>
  <version>0.1.0</version>
  <name>Pocket PC Derby kSOAP2</name>

  <description>
    Pocket PC Derby kSOAP2 example.
  </description>

  <repositories>
    <repository>
      <id>codehaus.org</id>
      <url>http://repository.codehaus.org</url>
    </repository>
    <!--
     ksoap2 isn't in any maven repoository, 
     so I'm distributing it with the project
    -->
    <repository>
      <id>project</id>
      <url>file://${basedir}/lib</url>
    </repository>
  </repositories>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.3</source>
          <target>1.3</target>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <id>fat</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
            <configuration>
              <descriptors>
                <descriptor>src/main/assembly/fat.xml</descriptor>
              </descriptors>
            </configuration>
          </execution>
          <execution>
            <id>distribution</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
            <configuration>
              <descriptors>
                <descriptor>src/main/assembly/distribution.xml</descriptor>
              </descriptors>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>

    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <excludes>
          <exclude>**/*</exclude>
        </excludes>
      </resource>
    </resources>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.apache.derby</groupId>
      <artifactId>derby</artifactId>
      <version>10.2.2.0</version>
    </dependency>

    <!--
    Included in project lib directory
    -->
    <dependency>
      <groupId>org.ksoap2</groupId>
      <artifactId>ksoap2-j2me-core</artifactId>
      <version>2.1.2</version>
    </dependency>
    
    <!--
      To emulate javax.microedition classes for testing,
      NOT to be deployed with the application
    -->
    <dependency>
      <groupId>org.microemu</groupId>
      <artifactId>microemulator</artifactId>
      <version>2.0.4</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <reporting>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pmd-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>findbugs-maven-plugin</artifactId>
        <configuration>
          <threshold>low</threshold>
          <effort>max</effort>
        </configuration>
      </plugin>
    </plugins>
  </reporting>
</project>

The build contains 2 Maven assembly descriptors. The first creates a fat jar which contains the project classes and all of required dependency classes. The second creates a directory distribution containing the fat jar and lnk file needed to run the application on the Pocket PC.

Pocket PC link files have a maximum command string length of 255 characters, listing seperate jars as java classpath arguments would exceed this length, hence the need for a single fat jar file.

I coudn't find kSOAP 2 in any maven repositories so I've included it with project source code in the /lib directory. In a real project you will probably want to install the jar into you local/company repository.

The microemulator dependency allows you to run the application from a desktop PC for testing, but is excluded from the distribution that will go on the Pocket PC.

The application itself is a simple AWT frame with a drop down list of stock names, a text field for the stock price and two buttons. The first button retrieves the stock price from the web service, and the second saves the value in the stock value field to the Derby database. When a stock name is selected, the application queries the database to see if it contains a row for that stock:

The main class, Stock.java, is an AWT frame which makes calls to a controller class.

Stock.java

package org.adrianwalker.pocketpc.stock;

import java.awt.event.WindowEvent;

public final class Stock extends java.awt.Frame {

  private static final String TITLE = "Pocket PC Example";
  private StockController stockController;

  public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(new Runnable() {

      public void run() {
        Stock stock = new Stock();
        stock.setVisible(true);
      }
    });
  }

  public Stock() {
    setTitle(TITLE);

    initComponents();
    initListeners();

    stockNameChoice.add("IBM");
    stockNameChoice.add("MSFT");
    stockNameChoice.add("ADBE");
    stockNameChoice.add("ORCL");

    try {
      stockController = new StockController();
    } catch (Throwable t) {
      stockPriceField.setText("Error");
    }
  }

  // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
  private void initComponents() {
    java.awt.GridBagConstraints gridBagConstraints;

    stockNameLabel = new java.awt.Label();
    stockNameChoice = new java.awt.Choice();
    stockPriceLabel = new java.awt.Label();
    stockPriceField = new java.awt.TextField();
    getStockPriceButton = new java.awt.Button();
    saveStockPriceButton = new java.awt.Button();

    addWindowListener(new java.awt.event.WindowAdapter() {
      public void windowClosing(java.awt.event.WindowEvent evt) {
        exitForm(evt);
      }
    });
    setLayout(new java.awt.GridBagLayout());

    stockNameLabel.setText("Stock Name:");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    add(stockNameLabel, gridBagConstraints);

    stockNameChoice.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(java.awt.event.ItemEvent evt) {
        stockNameChoiceItemStateChanged(evt);
      }
    });
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    add(stockNameChoice, gridBagConstraints);

    stockPriceLabel.setText("Stock Value:");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    add(stockPriceLabel, gridBagConstraints);
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    add(stockPriceField, gridBagConstraints);

    getStockPriceButton.setLabel("Get Stock Value");
    getStockPriceButton.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        getStockPriceButtonActionPerformed(evt);
      }
    });
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 2;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    add(getStockPriceButton, gridBagConstraints);

    saveStockPriceButton.setLabel("Save Stock Value");
    saveStockPriceButton.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        saveStockPriceButtonActionPerformed(evt);
      }
    });
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 2;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    add(saveStockPriceButton, gridBagConstraints);

    pack();
  }// </editor-fold>                        

    private void exitForm(java.awt.event.WindowEvent evt) {                          
      try {
        stockController.close();
      } catch (Throwable t) {
        // ignore shutdown error
      } finally {
        dispose();
        System.exit(0);
      }
    }                         

    private void stockNameChoiceItemStateChanged(java.awt.event.ItemEvent evt) {                                                 
      String name = stockNameChoice.getItem(stockNameChoice.getSelectedIndex());
      double price;
      try {
        price = stockController.getDatbaseStockPrice(name);
      } catch (Throwable t) {
        stockPriceField.setText("Error");
        return;
      }

      if (price == -1) {
        stockPriceField.setText("Unknown");
      } else {
        stockPriceField.setText(String.valueOf(price));
      }
    }                                                

    private void getStockPriceButtonActionPerformed(java.awt.event.ActionEvent evt) {                                                    
      String name = stockNameChoice.getItem(stockNameChoice.getSelectedIndex());
      double price;
      try {
        price = stockController.getWebserviceStockPrice(name);
      } catch (Throwable t) {
        stockPriceField.setText("Error");
        return;
      }

      if (price == -1) {
        stockPriceField.setText("Unknown");
      } else {
        stockPriceField.setText(String.valueOf(price));
      }
    }                                                   

    private void saveStockPriceButtonActionPerformed(java.awt.event.ActionEvent evt) {                                                     

      String name = stockNameChoice.getItem(stockNameChoice.getSelectedIndex());
      double price;
      try {
        price = Double.parseDouble(stockPriceField.getText());
        stockController.saveStock(name, price);
      } catch (Throwable t) {
        stockPriceField.setText("Error");
        return;
      }
    }                                                    

  // Variables declaration - do not modify                     
  private java.awt.Button getStockPriceButton;
  private java.awt.Button saveStockPriceButton;
  private java.awt.Choice stockNameChoice;
  private java.awt.Label stockNameLabel;
  private java.awt.TextField stockPriceField;
  private java.awt.Label stockPriceLabel;
  // End of variables declaration                   

  private void initListeners() {
    addWindowListener(new java.awt.event.WindowAdapter() {

      public void windowIconified(WindowEvent evt) {
        exitForm(evt);
      }
    });
  }
}

Two useful things to note about this code are:

  1. When you close a Java frame on the Pocket PC the program does not fire the window closing event, but instead is actually minimised. To exit the application you must listen for the window iconified event and use that to exit the application.
  2. You must call dispose() on the frame when exiting, otherwise the memory the application was using it not released, even when System.exit(0) is called.

StockController.java contains some Derby configuration settings and the web service end point URL which you will probaly want to change before deploying to the Pocket PC. The actual database and web service logic is delegated to 2 other classes, DatabaseController.java and WebServiceController.java.

StockController.java

package org.adrianwalker.pocketpc.stock;

import org.adrianwalker.pocketpc.stock.database.DatabaseController;
import org.adrianwalker.pocketpc.stock.webservice.WebServiceController;

public final class StockController {

  private static final String DATABASE_NAME = "\\StockDB";
  private static final String ENDPOINT = "http://localhost:8500/pocketpc-web-service/Stock.cfc";
  private static final String NAMESPACE = "http://www.example.org/stock";

  // default 1000, minimum 40
  private static final String PAGE_CACHE_SIZE = "40";
  // values: 4096 (default), 8192, 16384, or 32768
  private static final String PAGE_SIZE = "4096";
  private static final String ROW_LOCKING = "false";
  private final DatabaseController databaseController;
  private final WebServiceController webServiceController;

  public StockController() throws Exception {

    System.setProperty("derby.storage.pageCacheSize", PAGE_CACHE_SIZE);
    System.setProperty("derby.storage.pageSize", PAGE_SIZE);
    System.setProperty("derby.storage.rowLocking", ROW_LOCKING);

    databaseController = new DatabaseController(DATABASE_NAME);
    webServiceController = new WebServiceController(ENDPOINT, NAMESPACE);
  }

  public void close() throws Exception {
    try {
      webServiceController.close();
    } finally {
      try {
        databaseController.close();
      } catch (Exception e) {
        // ignore shutdown error
      }
    }
  }

  public double getWebserviceStockPrice(final String name) throws Exception {
    return webServiceController.getStockPrice(name);
  }

  public double getDatbaseStockPrice(final String name) throws Exception {
    return databaseController.readStockPrice(name);
  }

  public void saveStock(final String name, final double price) throws Exception {

    if (price < 0) {
      throw new IllegalArgumentException("Price must be positive");
    }

    if (databaseController.readStockPrice(name) == -1) {
      databaseController.createStock(name, price);
    } else {
      databaseController.updateStock(name, price);
    }
  }
}

WebServiceController.java

package org.adrianwalker.pocketpc.stock.webservice;

import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransport;

public final class WebServiceController {

  private static final String STOCK_PRICE_METHOD = "getStockPrice";
  private final String endpoint;
  private final String namespace;
  private final HttpTransport httpTransport;

  public WebServiceController(final String endpoint, final String namespace) {
    this.endpoint = endpoint;
    this.namespace = namespace;
    this.httpTransport = new HttpTransport(this.endpoint);
  }

  public double getStockPrice(final String stockName) throws Exception {
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapSerializationEnvelope.VER11);
    SoapObject request = new SoapObject(this.namespace, STOCK_PRICE_METHOD);
    envelope.setOutputSoapObject(request);
    request.addProperty("stockName", new SoapPrimitive(this.namespace, "string", stockName));
    httpTransport.call("", envelope);
    SoapObject body = (SoapObject) envelope.bodyIn;

    SoapPrimitive stockPrice = (SoapPrimitive) body.getProperty("getStockPriceReturn");

    return Double.parseDouble(stockPrice.toString());
  }

  public void close() {
    httpTransport.reset();
  }
}

DatabaseController.java

package org.adrianwalker.pocketpc.stock.database;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.derby.jdbc.EmbeddedSimpleDataSource;

public final class DatabaseController {

  private static final String CREATE_DATABASE = "create";
  private static final String DATABASE_SHUTDOWN = "shutdown";
  private static final String CREATE_TABLE =
          "CREATE TABLE APP.STOCK ( " +
          "ID    BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY," +
          "NAME  VARCHAR(10) NOT NULL UNIQUE," +
          "PRICE DOUBLE NOT NULL" +
          ")";
  private static final String INSERT_STOCK =
          "INSERT INTO APP.STOCK(NAME, PRICE) " +
          "VALUES(?,?)";
  private static final String SELECT_STOCK_PRICE =
          "SELECT PRICE " +
          "FROM APP.STOCK " +
          "WHERE NAME = ?";
  private static final String UPDATE_STOCK_PRICE =
          "UPDATE APP.STOCK " +
          "SET PRICE = ? " +
          "WHERE NAME = ?";
  private static final String DROP_TABLE =
          "DROP TABLE APP.STOCK";
  private EmbeddedSimpleDataSource dataSource;
  private Connection connection;
  private PreparedStatement insertStock;
  private PreparedStatement selectStockPrice;
  private PreparedStatement updateStockPrice;

  public DatabaseController(final String databaseName) throws SQLException {
    createDataSource(databaseName);
    createConnection();

    if (!tableExists()) {
      createTable();
    }

    createPreparedStatements();
  }

  private void createDataSource(final String databaseName) {
    this.dataSource = new EmbeddedSimpleDataSource();
    this.dataSource.setDatabaseName(databaseName);
    this.dataSource.setCreateDatabase(CREATE_DATABASE);
  }

  private void createConnection() throws SQLException {
    this.connection = dataSource.getConnection();
  }

  private void createPreparedStatements() throws SQLException {
    this.insertStock = connection.prepareStatement(INSERT_STOCK);
    this.selectStockPrice = connection.prepareStatement(SELECT_STOCK_PRICE);
    this.updateStockPrice = connection.prepareStatement(UPDATE_STOCK_PRICE);
  }

  private boolean tableExists() throws SQLException {
    ResultSet tables = connection.getMetaData().getTables(null, "APP", "STOCK", new String[]{"TABLE"});
    return tables.next();
  }

  public void createTable() throws SQLException {
    Statement statement = connection.createStatement();
    try {
      statement.execute(CREATE_TABLE);
    } catch (SQLException se) {
      se.printStackTrace();
    } finally {
      statement.close();
    }
  }

  public void dropTable() throws SQLException {
    Statement statement = connection.createStatement();
    try {
      statement.execute(DROP_TABLE);
    } catch (SQLException se) {
      se.printStackTrace();
    } finally {
      statement.close();
    }
  }

  public double readStockPrice(final String name) throws SQLException {
    this.selectStockPrice.setString(1, name);

    double price = -1;
    ResultSet resultSet = selectStockPrice.executeQuery();
    try {
      while (resultSet.next()) {
        price = resultSet.getDouble(1);
        return price;
      }
    } finally {
      resultSet.close();
    }

    return price;
  }

  public void createStock(final String name, final double price) throws SQLException {
    this.insertStock.setString(1, name);
    this.insertStock.setDouble(2, price);
    this.insertStock.execute();
  }

  public void updateStock(final String name, final double price) throws SQLException {
    this.updateStockPrice.setDouble(1, price);
    this.updateStockPrice.setString(2, name);
    this.updateStockPrice.execute();
  }

  public void close() throws SQLException {
    try {
      this.insertStock.close();
      this.selectStockPrice.close();
      this.updateStockPrice.close();
    } finally {
      try {
        this.connection.close();
      } finally {
        this.dataSource.setShutdownDatabase(DATABASE_SHUTDOWN);
        this.dataSource.getConnection();
      }
    }
  }
}

Build & Deploy

Build the project like an regular Maven build, 'mvn clean install', making sure your web service is available to pass all the unit tests.

The build should create a directory distribution in:

target\pocketpc-derby-ksoap2-0.1.0-distribution.dir

Copy the contents of this directory to the Pocket PC via ActiveSync to the directory:

\Program Files\Java\

Once the files have copied you can run the stock link file to run the app:

\Program Files\Java\pocketpc-derby-ksoap2-0.1.0\stock.lnk

Source Code