Saturday, February 19, 2011

Setup Eclipse JEE6 Primefaces and Glassfish - Part IV

In the first three parts of this series we have gone over the IDE Setup, Arquillian Test configuration, and a basic design of a cabin finder application that I want to build.  Now it's time to get started with the cabin finder application development.

Since we'll be using Maven, deploy to Glassfish and use embedded Derby database, there's very few dependencies we need to add to our project pom.  I'll be using GitHub for the all project source, so you can find the pom at https://github.com/sbasinge/primetest/blob/master/pom.xml.  It has primefaces, derby, arquillian, weld and logging dependencies.

I'm a visual person, so I like to start with the page definitions and work my way thru the page beans to the persistent entities (let's call this top down versus bottom up where we design the entities first and the pages last).  Most often it takes several passes of top-down, then bottom-up, then top-down, etc. until I get everything the way I want.  This affords me the opportunity to refine and refactor as I go.  The downside of this pattern is that from a test first perspective, it is more difficult to write UI tests than application logic tests.  Perhaps a middle-out approach would be best, where you define the pagebeans first and work out to the pages and down to the entities.  That approach would allow for writing of application logic tests early, the definition of UI to Server contracts and division of work into separate teams.

Let's try the middle-out approach.  A quick review of our mock-up shows we need a search bean that can take in several search parameters and return a list of cabins that we will plot on google maps.  JSF development makes me think a little more server sided, so I don't think of passing parameters from a page post to the server, as much as I think of a backing bean that can hold cabin attributes that I want to base a search on.  I also like query by example based application, since it allows me to extend searching capability to whatever attributes are supported by the underlying entities/data.  So let's start by creating a CabinSearchBean that will hold a Cabin instance where we'll collect the input from the search page.  We'll need to have a search method that takes the collected data and searches for matching Cabins, but that's pretty easy with the JPA Criteria Queries.

I'll start be creating the com.examples.cabin package in my src/main/java and src/test/java folders.  Then I'll create a CabinSearchBean in main and a CabinSearchBeanTest in test.  Since I like to have an Abstract class for most every package, I'll create an AbstractPageBean class in com.examples.cabin and have CabinSearchBean extend it.  That way if I need to add some behavior for every page bean it'll already be setup and ready.  As for the CabinSearchBean I need to add the @ConversationScoped and @Named annotations so it will be usable.  Also I need to inject the JPA persistence context and CDI conversation using @Inject PersistenceContext em; and @Inject Conversation conversation; respectively.  I'll also want a Cabin attribute for query by example and a List<Cabin> to hold the search results.  With JSF, you need to be sure not to put logic in getters, i.e. always separate the search event handling from the getCabins method.  So I'll add a search() method that will perform the jpa query and store the results in the cabin list.  But wait a second.....  I don't have a Cabin class yet.  This is where our middle-out approach is going to branch into the entity layer for a bit.  If you have a hibernate/jpa expert around, this is where they'd get started.

Create a package for com.examples.cabin.entity and add an AbstractEntity class.  The AbstractEntity needs annotated with @MappedSuperClass so it can be used as a basis for all other entities.  Make it implement Serializable so your application is cluster ready.  Also, create the Id and Version attributes on it, since all of our entities will get an autocreated Id and optimistic locking version.

Then create a Cabin class that extends AbstractEntity.   The easiest form of a JPA entity is annotated with @Entity and some simple attributes with getters and setters.  Add the attributes for
  String name;
String url;
String imageUrl;
boolean hotTub;
boolean firePit;
boolean firePlace;
String phoneNumber;

and have eclipse generate the getters and setters.  Now we have a basic Cabin entity we can use for persistence, query by example and to store values to/from our pages.

Back to the CabinSearchBean.  Now we can have the Cabin and List<Cabin> attributes with no compiler failures.  Also, we can build a search method using the Criteria API to populate the list.

  public void search() {
log.warn("Searching cabins for {}", cabin);
List<Cabin> results = null;
results = buildAndRunQuery();
log.info("Results: {}", results.size());
setCabins(results);
}

and 
private List<Cabin> buildAndRunQuery() {
List<Cabin> retVal = null;
CriteriaBuilder builder = db.getCriteriaBuilder();
CriteriaQuery<Cabin> query = builder.createQuery(Cabin.class);
Root<Cabin> root = query.from(Cabin.class);

Predicate temp = builder.conjunction();;
if (cabin.isFirePit()) {
temp = builder.and(temp,builder.isTrue(root.get(Cabin_.firePit)));
}
if (cabin.isFirePlace()) {
temp = builder.and(temp,builder.isTrue(root.get(Cabin_.firePlace)));
}
if (cabin.isHotTub()) {
temp = builder.and(temp,builder.isTrue(root.get(Cabin_.hotTub)));
}
query.where(temp);
retVal = db.createQuery(query).getResultList();
return retVal;
}

You'll notice that the buildAndRunQuery makes reference to Cabin_.  That is a reference to static JPA metadata.  Setup eclipse to create the metadata by adding the JPA facet to the project under project/properties.  Also, goto the Java Persistence properties and set the Canonical Metamodel path to src/main/java.  Then as you make changes to the Cabin entity the metadata will be generated into the same folder and package as the entity and you'll have a Cabin_.java.

Ok, now we have the basic cabin entity, and search bean.  We need to flush out more of the entities per our class diagram.  We'll add additional entities for Address, GeoLocation, RentalTerms, Review and Bedroom.  We'll add @OneToOne and @OneToMany annotations on the Cabin entity for the attributes.

@OneToOne(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="ADDRESS_ID")
Address address;

@OneToOne(cascade = CascadeType.ALL)
RentalTerms rentalTerms;

@OneToMany(cascade = CascadeType.ALL)
List<Review> reviews;

@OneToMany(cascade = CascadeType.ALL)
List<Bedroom> bedrooms;

On the Address entity we'll embed a GeoLocation (means it will be stored in it's own table similar to OneToOne) and have a State enumeration.

@Enumerated(EnumType.STRING)
State state;
@Embedded
GeoLocation geoLocation;


With those in place we can add a little more to our search -- like a State search

if(cabin.getAddress().getState()!=null) {
Join<Cabin,Address> address = root.join( Cabin_.address );
temp = builder.and(temp,builder.equal(address.get(Address_.state),cabin.getAddress().getState()));
}


and a filter for average rating:
if (this.getRating() >= 1) {
List<Cabin> tempResult = new ArrayList<Cabin>();
for (Cabin cabin: retVal) {
if (cabin.getAverageRating()>= getRating()) {
tempResult.add(cabin);
}
}
retVal = tempResult;
}

Next time we'll move to the page design and hooking up to our CabinSearchBean using expression language. Sounds tough, but it looks like 
     <p:commandButton id="searchButton" action="#{cabinSearchBean.search}" value="Search"/>

Whew!  That was quite a bit of ground to cover.  We've now created a backing bean with QBE capabilities and supporting entity model.  Essentially the server side aspects of our Cabin Finder application.  We still have a ways to go.  We need to work on tests, creating test data and pages so we can see what this will all look like, but we made good progress!




Tuesday, February 8, 2011

Setup Eclipse JEE6 Primefaces and Glassfish - Part III

In the first 2 installments we setup eclipse, glassfish, maven, primefaces, JPA2 and arquillian for web application development. Now it's time to put it to practical use.

First, we need an application to exercise the JEE6 stack. We'll build a cabin finder which is a project I needed to build for a retreat I am planning. I want to evaluate cabins where we can stay. Several factors determine whether it's a place we may like -- location, amenities and cost. So what I am picturing is a search page with a left panel of search criteria and a content panel that will show matching results on google maps. We'll need a way to get cabins entered/loaded into the system. For that we'll use Yahoo local search to populate some basic data and then manual entry to add amenities, cost and the like. Here's an mock up I made using Gliffy (http://www.gliffy.com/publish/2461936/).  And here is a basic class diagram (http://www.gliffy.com/publish/2461999/)

I also picture menu navigation using primefaces dock component to get to maintenance pages to load data and maintain data.  So we'll need at least 4 pages to get started (search, load, list, edit).

One other thing I'd like to do.  I'd like to share the code between my home computer, work computer and this posting.  I'll try github for that. git://github.com/sbasinge/primetest.git.  Install the eGit plugin into eclipse from http://download.eclipse.org/egit/updates by adding it as an update site (help/install new software/Add).  Once installed use the eclipse import menu to import  "projects from git".  Clone the repository (this makes a copy of the repository to your local machine).

egit documentation: http://www.eclipse.org/egit/documentation/
   Search for "Working with Remote Repositories" to learn about cloning - step 1.
   Then go up to "Starting from existing Git Repositories" to setup the eclipse project.

Next time we'll take the entity model and create our JPA entities and associated tests.