April 1, 2008
TSSJS was a great conference this year. Neal Ford, well-known author and ThoughtWorker, initiated the conference by delivering an inspiring keynote address discussing dynamic languages, DSLs (Domain-specific languages), and their role in the evolution of software development. Matt Raible blogged about the presentation and you can find a brief summary here. Polyglot programming became a theme of the conference with Ted Neward delivering yet another inspiring keynote encouraging developers to create their own language.
Building on this inspiration, Scott Davis discussed taking the Groovy Red Pill. Well, you would be happy to know I have taken the Red Pill, but of course with a Seam twist. I have been working on a time-tracking application (for my company’s internal use) utilizing Seam and Groovy that I will release as a freely available example. Groovy and Seam integration will be discussed in-depth in JBoss Seam 2E, but let’s look at the Groovy way to initialize a Timesheet:
@Entity
class GroovyTimesheet
{
@Id @GeneratedValue
Long id;
@OneToMany
@JoinColumn(name="TIMESHEET_ID")
List<GroovyTimeEntry> entries = new ArrayList<GroovyTimeEntry>();
GroovyTimesheet(PayPeriod payPeriod, int month, int year)
{
(payPeriod.getStartDate(month, year)..
payPeriod.getEndDate(month, year)).each
{
entries << new GroovyTimeEntry(hours:0, date:it);
}
}
// ... ...
}
So what is going on here? Essentially we define a range of dates that are iterated over. The PayPeriod is a fairly simple enum that determines the start and end dates of a pay period. By specifying (startDate..endDate) we define a range. Groovy understands the meaning of a range of dates allowing us to express this in a very concise manner (try expressing this in Java and you’ll get the picture). In addition, we use the each operation on this range. The each operation allows us to define a closure that executes as Groovy loops through our range of dates a day at a time. This allows us to initialize each GroovyTimeEntry instance for the pay period.
You will also note use of the << operator (or the leftShift operator). This operator is defined for a List allowing us to add elements to the list through this syntactic sugar. The GroovyTimeEntry instance is initialized using the default construction approach. By default, named constructor parameters can be specified in any order to initialize an object instance. If you define a constructor this is no longer provided by default. Finally, you will notice the use of the keyword it in the closure we defined for the each operation. The keyword it provides the value of the current element in the iteration. So in our instance, as we loop through the date range, each date will be provided iteratively in the range.
@Entity
class GroovyTimeEntry {
@Id @GeneratedValue
Long id;
BigDecimal hours;
Date date;
}
Wow, is that all the code? Looks pretty nice doesn’t it. As mentioned, the default constructor allows us to specify named parameters. In addition, getters and setters are automatically provided for each of our attributes.
You’ve probably noticed the use of JPA annotations here. This is perfectly legal and your groovy class will be a JPA entity. The same is true for Seam components annotated with @Name. So how does this work? Groovy classes are compiled to Java bytecode under the covers so JEE and Seam features are fully available to your Groovy classes at runtime. Simply use the groovyc compiler that can be accessed here or include the groovyc Ant task into your build. This will be covered in-depth in JBoss Seam 2E.
So what if we want to add GroovyTimeEntry instances to the GroovyTimesheet instance programmatically? The following code implements this:
@Entity
class GroovyTimesheet
{
// ... ...
void leftShift(GroovyTimeEntry entry)
{
entries << entry;
}
// ... ...
}
As mentioned, the leftShift operator is provided by Groovy and can be overloaded within your custom implementations. By defining a custom leftShift implementation we are now able to add a GroovyTimeEntry instance through the following:
// ... ...
timesheet << new GroovyTimeEntry(new Date());
// ... ...
Operator overloading isn’t limited to the leftShift operation. We can also overload other operators such as +:
// ... ...
BigDecimal plus(GroovyTimeEntry entry)
{
this.hours + entry.hours;
}
// ... ...
This allows us to add the hours of two GroovyTimeEntry instances using the simple + operator. Notice that a return is not specified. This is optional as the last line is assumed to be a return statement.
As mentioned, a complete time-tracking application will be provided that not only demonstrates Groovy entities but also Groovy Seam components. In addition, the time-tracking application will make use of the ExpandoMetaClass to extend final Java classes at run-time! The code for the application is in development and will be made available through Google code. Also, stay tuned for JBoss Seam 2E which will provide the intimate details of Groovy and Seam integration. So take the Red Pill and use the meta-programming features of Groovy in your own Seam applications!
Posted in JBoss Seam, JPA, groovy
6 Comments »
February 13, 2008
The first cut preview chapters for JBoss Seam, Second Edition, 2/E have been released. You can access the preview chapters here and can retrieve the code examples discussed throughout the chapters from this previous posting. We decided to throw in a bonus preview of the Nested Conversations chapter, so enjoy!
Very soon you should expect the full versions of these chapters to be available through Safari from the rough cuts program as well as a number of other chapters. Coming soon, you can expect more coverage of core Seam as well as using Seam with Groovy, Rules-based Security, JBoss Cache, Quartz integration, and much, much more. If you are planning to attend TSSJS, I look forward to seeing you there! Michael will be presenting so be sure to attend his presentation.
Stay tuned for more updates!
Posted in JBoss Seam, JBoss Seam 2E News
11 Comments »
February 11, 2008
The preview chapters will soon be released but you can get a sneak peak by accessing the code examples here. Feel free to download the examples and try them out! Each example project included in the source bundle is a variant of the Seam Hotel Booking example. The booking project demonstrates the use of natural conversations and navigation-based conversation management while the nestedbooking project demonstrates the use of nested conversations.
You can deploy the examples by first editing the included build.properties file to include the following settings:
- jboss.home - the location of your local JBoss instance (the examples have been tested against JBoss AS 4.2.2.GA)
- seam.home - the location of the latest Seam distribution (the examples have been tested against JBoss Seam 2.0.1.GA)
Once you have made these changes simply execute ant main deploy. This will build the example and deploy it to the configured JBoss instance. If you have any questions or run into any issues with the examples, please let us know!
Posted in JBoss Seam, JBoss Seam 2E News
No Comments »
January 31, 2008
The much anticipated Second Edition of JBoss(R) Seam: Simplicity and Power Beyond Java(TM) EE (Prentice Hall JBoss) is coming soon. Michael Yuan and myself have teamed up to develop the content for the upcoming Second Edition which will cover the ins and outs of Seam 2, and I don’t just mean bijection
The first preview chapters were released around the time of JBoss World along with 2 code examples. The full draft chapters will be available soon from Safari rough cut. The initial electronic release provides a preview of the following chapters:
- Chapter 7: Conversations
- What is a Conversation?
- The Default Conversation Scope
- Display JSF Messages
- Long-running Conversations
- Introducing the Hotel Booking Example
- The Lifecycle of a Long-running Conversation
- Conversation Timeout
- Managing Long-Running Conversations
- Beginning a Long-running Conversation
- Inside the Conversation
- End a Long-running Conversation
- Links and Buttons
- New Frontiers
- Chapter 8: Workspaces
- What is a Workspace?
- Workspace Management
- The Workspace Switcher
- Carry a Conversation Across Workspaces
- Managing the Conversation ID
- Natural Conversations
- Beginning a Natural Conversation through Linking
- Redirecting to a Natural Conversation
- Resuming a Natural Conversation
- Rewriting to User-friendly URLs
- Workspace Timeout
- Desktop Features in a Stateless Web
- Chapter 9: Nested Conversations
- Why are Nested Conversations Needed?
- Continuing the Conversation
- Understanding the Nested Conversation Context
- Nesting Conversations
- The Conversation Stack
- Managing the Conversation Stack
- Displaying Breadcrumbs
- Nested Conversation Timeout
- Fine-grained State Management
The code examples demonstrate how to manage conversations through navigation, the use of natural conversations, a new feature included in the latest release of Seam, and usage of nested conversations. To stay up-to-date you can watch for announcements at both my blog and Michael’s. You can also keep watch at the official Pearson product page. Stay tuned!
Posted in JBoss Seam
No Comments »
January 8, 2008
With Seam you have options for component configuration. One of those options is configuration through use of components.xml. This is especially valuable when you have components that are reused among application instances as it allows the configuration to be specific based on the needs of the application. Seam provides a very simple approach to limit or even eliminate configuration errors through the use of Namespaces and schemas.
It is quite ugly and error-prone to configure components in the following manner:
@Name("myComponent")
public class MyComponent {
private Map<String, String> componentMapping;
private BigDecimal someDecimalValue;
... ...
// component getters and setters
}
In components.xml:
... ...
<component name="myComponent">
<property name="someDecimalValue" value="22.2" />
<property name="componentMapping">
<key>someKey</key> <value>someValue</value>
... ...
</property>
... ...
</component>
... ...
Generating a Namespace and schema for your Seam components is actually quite simple and by taking the time to do this, you gain the benefits of:
- auto-completion within the IDE (or your favorite XML editor)
- validation of your configuration prior to deployment (attributes and even values if restrictions are defined)
- very lean configuration (makes your custom components look good
)
The Seam Reference Guide simply references the schemas shipped with Seam as examples for generating your own, but I thought a brief tutorial for those less familiar with XML schemas might be helpful.
Creating a Namespace
First off, you need to create a Namespace. As the documentation demonstrates, this is actually quite simple with Seam through the use of the @Namespace annotation. Just create a file named package-info.java in the package of your components:
@Namespace(value="http://solutionsfit.com/example/custom")
package com.solutionsfit.example.custom;
import org.jboss.seam.annotations.Namespace;
All of the components for this Namespace must be in this package. You cannot have the same Namespace referenced in multiple package-info.java files. While this makes sense it can be a hinderance in situations where you have sub-packaging.
Now we can reference the Namespace in our components.xml:
<components xmlns="http://jboss.com/products/seam/components"
xmlns:custom="http://solutionsfit.com/example/custom"
... ...
<custom:my-component ... />
As you will note, component names and attribute names are specified in the hyphenated form when using Namespaces. I refer you to the Seam Reference Guide for more details on configuring Namespaced elements.
Manually generating the schema
Now for the schema. So we have a Namespace defined for our custom components, but it would be great to have auto-complete and validation in our components.xml. The following template should help you get there:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://solutionsfit.com/example/custom"
xmlns:custom="http://solutionsfit.com/example/custom"
xmlns:components="http://jboss.com/products/seam/components"
attributeFormDefault="unqualified">
<xs:import namespace="http://jboss.com/products/seam/components"
schemaLocation="http://jboss.com/products/seam/components-2.0.xsd"/>
<xs:element name="my-component">
<xs:complexType>
<xs:sequence>
<xs:element name="component-mapping" minOccurs="1" maxOccurs="1"
type="components:mapProperty" />
</xs:sequence>
<xs:attributeGroup
ref="components:attlist.component"/>
<xs:attributeGroup
ref="custom:attlist.my-component" />
</xs:complexType>
... ...
<xs:attributeGroup id="attlist.my-component">
<xs:attribute name="some-decimal-value"
type="xs:decimal" required="false">
... ...
</xs:attributeGroup>
</xs:element>
... ...
You’ll notice that we import the Seam components-2.0.xsd and reference it as a namespace. This gives us access to the basic component attributes that will be configurable on your component.
Also notice, on my-component we reference an attribute group components:attlist.component. This attribute group is defined in the components Namespace we imported and provides the attributes you’d expect for a Seam component (i.e. name, scope, auto-create, …). In addition, we define an element named component-mapping of type components:mapProperty. Here we have used a type defined in the components Namespace. This is very convenient as we can now specify the following our components.xml:
... ...
<custom:my-component some-decimal-value="22.2">
<custom:component-mapping>
<key>someKey</key> <value>someValue</value>
... ...
</custom:component-mapping>
... ...
Another type multiValuedProperty can be used for Lists configured within your component.
You may also note that I specified restrictions within my component-mapping element. minOccurs=1 and maxOccurs=1 will ensure that the component-mapping element is provided if my-component is specified in the components.xml definition. It is good practice to provide these types of restrictions to avoid configuration errors at run-time which can be difficult to debug. By specifying them in our schema, our XML editor will notify us of validation issues. A good tutorial on schema definitions can be found at w3schools.
Referencing the schema
Once my schema is specified, I can simply reference the schema in components.xml in the xsi:schemaLocation attribute:
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.com/products/seam/core
http://jboss.com/products/seam/core-2.0.xsd
http://jboss.com/products/seam/components
http://jboss.com/products/seam/components-2.0.xsd
http://solutionsfit.com/example/component
http://solutionsfit.com/example/component-1.0.xsd">
... ...
But, there is one final step to get auto-complete and validation. If you attempt to validate the XML at this point you will notice errors on your custom component configuration. This is simply because Eclipse cannot find the XSD we specified.
Craig Walls posted a blog entry back in August on how to fix errors in Eclipse with Spring Modules in your XML. The same fix applies for your own custom XSDs. Simply reference the XSD on your local drive from the “Add XML Catalog Entry” dialog as Craig describes. Once you’ve referenced your schema, refresh your components.xml and you are in business! You will now have auto-complete and validation for your custom components.
Sidenote: Auto-generation of schemas for custom components would be a great feature addition for seam-gen. While it would not be trivial to implement, if interest in this posting is high enough I would be happy to contribute a patch as soon as my schedule permits
.
Posted in JBoss Seam
1 Comment »
December 20, 2007
Update: Pete Muir recently posted an alternative to the example described here for the UserProfile object. It avoids potential LazyInitializationException if you access a property which was not initialized inside the loading conversation since the UserProfile is SESSION scoped. I prefer Pete’s approach to the implementation but the concept is the same 
______________________________________________________________
One thing I have really come to appreciate in seam is the ask and ye shall receive approach of injection of not only components but also core domain objects (or concepts depending on how you look at it). One issue I’ve always struggled with is the requirement to constantly go and get some domain object that is used very often. This really reminds me of the EJB2 style lookups which was really why we starting using DI frameworks, right? Whether you look up the domain object through a DAO, through the session, or through some holder object, you still have to go and get what you need.
For example, let’s say we have a UserProfile object that is needed by most of our pages and many of the components in our application to retrieve user information. It may not always be needed (we have an Identity of course thanks to Seam which provides our authentication and authorization), but it is needed if we require personal information about our user.
In the general world of Java you could implement this in several ways but lets say we do so through something like the following:
public class UserInformationBean implements UserInformation
...
private UserProfile userProfile;
// injected by your favorite DI framework
// (Seam, EJB3, Spring, even PicoContainer)
private UserProfileDAO userProfileDAO;
...
public boolean isUserLocal()
{
UserProfile userProfile = this.getUserProfile();
if("Texas".equals(userProfile.getState())
&& "Dallas".equals(userProfile.getCity()))
{
return true;
}
log.info("ya'll ain't from around here are ya");
return false;
}
...
public UserProfile getUserProfile()
{
if(userProfile == null)
{
this.userProfile = userProfileDAO.getUserProfile(
SomeContext.getUserPrincipal().getName());
}
return this.userProfile;
}
...
Here is the associated dependency diagram for our UserInformationBean component:

This is all well and good but I would prefer if the call to retrieve the UserProfile was centralized since I use it often. This is quite easy with Seam:
@Name("userProfileManager")
public class UserProfileManager {
...
@In private UserProfileDAO userProfileDAO;
...
@Factory(value="userProfile", scope=ScopeType.SESSION,
autoCreate=true)
public UserProfile initCurrentUserProfile()
{
return userProfileDAO.getUserProfile(
Identity.instance().getUsername());
}
...
Note that I scope my UserProfile to the session which makes sense since the user is not likely changing profiles (or we may be in for some legal issues) and we are using it throughout the application. Note that conversation scope should be considered if the object is only needed in a section of the application to ensure that our state is cleaned up appropriately. The use of other contexts will be discussed further shortly. So now my component looks like:
@Name("userInformation")
public class UserInformationBean implements UserInformation
...
@In UserProfile userProfile;
...
public boolean isUserLocal()
{
if("Texas".equals(userProfile.getState())
&& "Dallas".equals(userProfile.getCity()))
{
return true;
}
log.info("ya'll ain't from around here are ya");
return false;
}
...
Below is the dependency diagram for our UserInformationBean after the change. Note that our UserInformationBean knows nothing about the UserProfileDAO (nor does it care):

So you may ask what does this buy me? I still have to call the DAO to get what I need. Sure, you may have to call your DAO (or the EntityManager if your using EJB3), but the dependency on the DAO is in one place, not scattered. Your component is now only dependent on what it needs to get the job done and we’ve removed unnecessary layering thereby reducing coupling.
In addition, the factory method only executes once during the course of a user session as I’ve scoped my UserProfile to the session. Thus, the UserProfile is instantiated lazily (since the @Factory method only executes on demand) and if it is used elsewhere it is simply provided back from the context rather than invoking the DAO again.
I also find this approach much easier to test. Rather than having to provide a mock DAO (or even worse a mock EntityManager) to my component, I simply set the UserProfile to whatever I need it to be to perform my unit test:
...
public void testIsUserLocal()
{
UserProfile = new UserProfile("Jacob Orshalick", "Dallas", "Texas");
UserInformationBean bean = new UserInformationBean();
bean.setUserProfile(userProfile);
assertTrue("Howdy! Ya'll oughta be returnin' true.",
bean.isUserLocal());
}
...
Additional Considerations
One drawback to this approach is determining where these domain objects are coming from. It may not be immediately obvious as to how these objects are just appearing in the context for developers. One approach to help with this is using symbolic constants. Using our example above we could create a constant for the UserProfile:
...
public final static String USER_PROFILE = "userProfile";
...
We can use this in both the factory and the injection:
...
@Factory(value=DomainConstant.USER_PROFILE, scope=ScopeType.SESSION,
autoCreate=true)
...
@In(value=DomainConstant.USER_PROFILE)
UserProfile userProfile;
...
In this way a user can easily seek out where USER_PROFILE is referenced rather than having to do a text search (which could easily result in a vast number of results especially if used in EL expressions and comments).
Another consideration here is how your domain object will be used. This is necessary to determine which context to outject to. Is my UserProfile being used heavily in all of the application or is it only a section of the application? Conversation context would be a better candidate if the UserProfile is heavily required in a certain section of the application. Also, what are the memory penalties involved? Would it be better to simply request when needed to avoid the additional memory allocation? This may be the case if the object is used infrequently. It may be best to inject a local instance only that is only used for the invocation. Developers should always carefully consider the context.
Bijection and Disinjection
One way to explicitly control the scope of the creation of the UserProfile would be to leave off the ScopeType in your @Factory definition. Because a POJO is by default EVENT scoped, the UserProfile would only be available to your component and you choose whether to outject it to a broader context. This can simply be accomplished through use of the @Out annotation.
If you choose to leave off the ScopeType and inject a local instance in your component, the @Factory method would be invoked once for each request that your component is invoked. Why is that you ask? Essentially, Seam disinjects any attributes in your component annotated with @In after the invocation. Disinject is really just a fancy way of saying set to null.
This is an important concept as you may expect your attribute to remain set in between requests if I scoped my component to the Conversation or the Session. This behavior makes sense though as Seam maintains contextual state. The current context state is always injected into your component for each invocation. Therefore, it makes no sense to maintain the attribute instance as it will be overwritten prior to the next invocation.
This type of domain concept injection is very useful as it helps to reduce coupling and the heavy layering constantly introduced into systems. There are many situations where this type of behavior is quite handy but it is only achievable due to the contextual injection that Seam provides.
Posted in JBoss Seam, JSF
2 Comments »
December 13, 2007
Conversation timeout seems to be a commonly misunderstood Seam concept. I often see postings on the Seam forums claiming that the conversation-timeout doesn’t work! Well, actually it does, you simply have to understand the semantics.
Configuring the conversation-timeout period can be accomplished through the following in your components.xml:
<core:manager conversation-timeout="900000" />
At first glance most developers relate conversation-timeout to session timeout, where any conversation will simply timeout after the configured conversation timeout period. As you will quickly notice during testing, this is not the case. To execute a quick experiment, try the following configuration in the components.xml of the seam-booking example:
<core:manager conversation-timeout="60000" />
Because the conversation-timeout is set in milliseconds, the above configuration sets the conversation-timeout to 1 minute. Now in the web.xml set the session timeout to 5 minutes:
<session-config>
<session-timeout>5</session-timeout>
</session-config>
Now in the destroy method of the HotelBookingAction add the following line:
...
@Destroy @Remove
public void destroy() {
log.info("Destroying HotelBookingAction...");
}
...
This will log our message when the conversation ends and the HotelBookingAction is destroyed. Now deploy the seam-booking example to your local JBoss instance and start up a conversation. This can be accomplished by logging in, navigating to the hotels listing, and selecting a hotel for booking. At this point, wait for the 1 minute period… nothing happens. Now wait for another 4 minutes, the message is displayed. The conversation timed out along with the session.
Foreground vs. Background Conversations
So why didn’t our conversation timeout as configured? This is because the conversation-timeout only affects background conversations. The foreground conversation will only timeout when the session times out. Foreground, background, what? The foreground conversation is the conversation that the user last interacted with. A background conversation is any other conversation in the user’s session. Thus, in our previous scenario the foreground conversation timed out with the session as expected.
Now lets try another approach. Perform the same steps as before to proceed to the booking screen. Now open a new window and perform the same steps. We now have a foreground conversation and a background conversation in progress. Again, wait 1 minute. Nothing happened. If you wait an additional 4 minutes, both conversations will timeout. So what is going on here, I thought we had a background conversation? We did, Seam simply checks the conversation timeout on each request. Thus, if I interact with the foreground conversation after 1 minute, the background conversation times out. Try it, perform the same steps, wait 1 minute and then click on the window of the foreground conversation and you will see the log message.
This is very desirable behavior. Essentially when a user leaves his or her desk for a period of time and comes back, if the session is still active it would be desirable to maintain the state the user was previously in. This is the foreground conversation state. All other background conversation state is left to timeout after the configured conversation-timeout period which reduces overall memory consumption. This enables a developer to think less about memory usage and cleaning up state and more about developing business logic. That’s why we’re here right?
Letting the user choose
So you may be asking at this point why the conversation-timeout doesn’t use polling. As we said, you must interact with the foreground conversation to cause the background conversations to timeout after the conversation-timeout period. Imagine that the user had many windows open and leaves his or her desk. Based on which window the user clicks on when they return, that becomes the foreground conversation timing out any other background conversations. This gives the user a chance to resume whichever conversation he or she chooses, not the one the developer chooses.
Posted in JBoss Seam
11 Comments »
December 4, 2007
Query by example (QBE) is a great feature for easily executing Criteria using Hibernate. By simply filling in an object and submitting it, Hibernate automatically generates the query based on the values set in the object. While this is an attractive option, in my experience it usefulness quickly becomes limited. In many cases, specifying Restrictions is required which quickly leads to ugly code. For example, lets take a Product class:
@Entity
@Table(name="PRODUCTS")
public class Product {
@Id
@Column(name="ID")
private BigDecimal id;
@Column(name="SKU")
private String sku;
@Column(name="CATEGORY")
private Category category;
@Column(name="NAME")
private String name;
@Column(name="DESCRIPTION")
private String description;
@Column(name="STARTED_OFFERING")
private Date startedOffering;
@Column(name="PRICE")
private BigDecimal price;
...
}
This class has a number of attributes we could search on. The easiest of which are probably the category and name. With the id and name we can simply populate and object instance and use QBE:
...
Product product = new Product();
product.setCategory(Category.APPAREL);
product.setName("slacks");
Example productExample =
Example.create(product).ignoreCase().enableLike();
List results = session.createCriteria(Product.class)
.add(productExample)
.list();
...
This works fine, but what if we want to search for products in a price range say gift ideas from $30.00 to $50.00, or products that we started offering between December 1, 2006 and March 31, 2007. Obviously we can’t accomplish this by simply setting values into our example product. We need to use the Criteria API in this case:
...
List products = session.createCriteria(Product.class)
.add(Restrictions.between("price", minPrice, maxPrice))
.add(Restrictions.
between("startedOffering", startDate, endDate))
.list();
...
This seems pretty simple but what if we need to add this to the UI. Well, first we need an object to bind our data to:
...
public class ProductCriteria {
private BigDecimal minPrice;
private BigDecimal maxPrice;
private Date beginStartedOffering;
private Date endStartedOffering;
...
// getters and setters for attributes
...
}
Now we will need to perform checks to determine how to execute the search:
...
if(productCriteria.getMaxPrice() != null)
{
criteria.add(Restrictions.less("price",
productCriteria.getMaxPrice()))
}
if(productCriteria.getMinPrice() != null)
{
criteria.add(Restrictions.greater("price",
productCriteria.getMinPrice()))
}
...
Etc, etc… As you can imagine the problem is exacerbated the more Restrictions that become required to execute the search. So what about a hybrid approach? Lets define a few annotations:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Criteria {
Class entity;
}
...
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Criterion {
String property;
Restriction operator default Restriction.EQUALS;
}
...
Restriction is simply a Java5 enum that will map to a Hibernate Restriction (this can be done through a polymorphic method in your Restriction implementation). Alright, now lets place these annotations on our Criteria object that we bound to the UI:
@Criteria(entity=Product.class)
public class ProductCriteria {
@Criterion(property="price", operator=Restrictions.LESS)
private BigDecimal minPrice;
@Criterion(property="price", operator=Restrictions.GREATER)
private BigDecimal maxPrice;
@Criterion(property="startedOffering",
operator=Restrictions.GREATER)
private Date beginStartedOffering;
@Criterion(property="startedOffering",
operator=Restrictions.LESS)
private Date endStartedOffering;
...
// getters and setters for attributes
...
}
At run-time the annotations can be processed to create and execute the criteria provided the object that we bound to the UI. Assuming we have a ProductCriteria instance populated with data from our UI interaction, the call would look like:
...
List <Product> products =
criteriaManager.execute(productCriteria, entityManager);
...
The CriteriaManager knows the entity type we’re searching for (we placed it in the @Criteria annotation), it knows the restrictions we’re applying and which @Entity property we are applying them to (that is in the @Criterion annotations), and it knows the values that will be used in executing (based on the attribute the @Criterion annotation is placed on). We could also directly inject the EntityManager instance into the CriteriaProcessor.
So you may say this just seems like we’re moving the problem around. Well, not really. There are some benefits to this approach:
- Vendor independence: The use of custom APIs is eliminated. Instead of calling on the Hibernate Criteria API, it becomes very simple to swap out our implementation. Simply alter the code that processes the annotations we defined.
- One-stop shop: The annotations place the meta-data about how a criteria should execute with the actual criteria values. If changes need to be made to your example query, you make the changes in a single location rather than having to alter both your object bound to the UI and some DAO that processes that object. Additionally, the logic associated with executing criteria is right where it belongs, on the criteria object.
- Remove unnecessary DAOs: Removes the need for a custom DAO to process the object bound to the UI with a bunch of if/thens. The EntityManager provided by JPA was a recognition that the constant DAOs implementing CRUD interfaces could be generalized. This approach recognizes that constant implementation of DAOs handling if/then scenarios can be generalized.
- JPA Consistency: The @Criteria and @Criterion annotations look very similar to @Entity and @Column. This is intentional to create consistency between our @Entity mappings and our @Criteria mappings.
- Testability: All logic for creation of criteria is in one place, the annotation processor. This one place can be heavily tested ensuring its functionality. A custom annotation processor could be developed to make testing your criteria classes very simple (development of this annotation processor is actually very simple).
- Code generation: Tools like grails use compile-time annotation processing through an APT to auto-generate things like JSPs, classes, configuration, etc. This same approach can be taken using @Criteria. It would be fairly simple to write an APT that generates search criteria JSPs based on the @Criteria instance and the provided annotations.
Essentially we have a very reusable criteria execution strategy. There are of course many extensions that could be included with this including:
- conjunctions - what about ANDs and ORs?
- associations - what happens if my mappings contain a @OneToOne, @OneToMany, or even a @ManyToMany?
- dynamic operators - what if my operators are not static (i.e. the user can choose an operator)?
These questions will be answered in an upcoming post so stay tuned.
Posted in Hibernate, JPA
5 Comments »
November 16, 2007
Differentiating between an initial login, a session expired, and a logout when displaying messages to the user on the login screen seems like a simple task. The implementation is unfortunately not quite as simple as it sounds and requires a little legwork. The following tutorial will guide you through the requirements.
The first component is a PhaseListener that allows you to determine whether the user’s session expired or whether this is the first time the user accessed the application. You have to make some assumptions here, but you can basically notify the user when the server session has ended by adding the following to a PhaseListener:
...
@Observer("org.jboss.seam.beforePhase")
public void beforePhase(PhaseEvent event)
{
if(event.getPhaseId() == PhaseId.RESTORE_VIEW)
{
HttpServletRequest request =
(HttpServletRequest) FacesContext.getCurrentInstance()
.getExternalContext().getRequest();
if(request.getRequestedSessionId() != null
&& request.getSession().isNew())
Events.instance().raiseEvent("security.sessionExpired");
...
Based on general cookie settings this will raise the event when the user still has the browser window open, the http session expired, and the user tries to access the application. If the user closes and reopens the browser to start the application, the event will not be raised. This of course makes the assumption that cookies expire when the browser session is ended (which is generally the case).
This works well, but unfortunately the FacesMessages component is not available until after the RENDER_RESPONSE phase has executed. This is because the FacesMessages component is conversation scoped and the conversation is not started until after the RENDER_RESPONSE phase. Thus, you have to have a component available to store your message until the FacesMessages component becomes available. This can be accomplished by using an appropriately scoped component. For example:
@Name("customAuthenticator")
@AutoCreate
@Scope(SESSION)
public class CustomAuthenticator implements Authenticator {
@Logger
private Log log;
...
private List<string> messages;
...
@Observer("security.sessionExpired")
public void sessionExpired(String message)
{
log.info("Adding session expired message...");
this.messages.add("User session expired.");
}
public void flushMessages()
{
for(String message : this.messages)
FacesMessages.instance().add(message);
this.messages.clear();
}
}
The flushMessages method should be invoked either by an action on your pages.xml or by flushing after the INVOKE_APPLICATION phase (this is up to your preference). Simply ensure that the flushing occurs only once the conversation has been initialized so that FacesMessages is available in the context.
This takes care of differentiating between a session timeout and a new session. So what about the logout? If you flush the security messages on every request, the user will end up seeing a message saying that the “User session ended” on logout.
To resolve this we can place a page in between and perform a meta redirect. First define a Seam page that does not require a login. Then in this page, add the following meta tags:
<meta http-equiv="Refresh" content="3; URL=/myApp/myPage.seam" />
This page can display a simple message to the user along the lines of: “You have successfully logged out.” The user will then be redirected to myPage after 3 seconds. As long as the messages are flushed on each request, the “User session ended” message will be long gone once the meta redirect executes.
Next add an entry in your pages.xml to redirect to this page when a logout occurs:
<page view-id="*">
<navigation from-action="#{identity.logout}">
<redirect view-id="/logoutConfirmation.xhtml" />
</navigation>
</page>
A feature request has been placed to include the security.sessionExpired event as well as a security.newSession event. If you are interested in Seam providing this behavior out-of-the-box, please vote!
Update: Are you looking for an advanced polling solution for session expiration? Christian Bauer blogged about an approach to polling for session expiration here.
Posted in JBoss Seam, JSF
8 Comments »
November 13, 2007
In an implementation using the JBoss RichFaces rich:dataScroller component, I noticed the first attribute of a UIData component is maintained even if the DataModel it is displaying has changed. It turns out, this is an issue with JSF. The UIData component does not track the DataModel it represents. Thus, when the DataModel is updated it has no effect on the first attribute of the UIData component. In defense of JSF, it would be difficult to know when a DataModel update should trigger an update of the UIData as this would require making some assumptions about the model.
As you can imagine, this can lead to consistency issues with data versus the state of the UI when pagination is used. For example, if I have a DataModel that wraps a List of 100 elements that are the results of a search. In the display, I can show 10 elements per page and allow the user to paginate freely with the rich:dataScroller component. If the user paginates to the fifth listing, the first attribute of the UIData component now points to 50. If the user then narrows the search (by changing some search criteria and re-submitting) the result set is reduced to 10. Now we have an issue. The user sees no results because the UIData is looking for index 50 to start the List. The first attribute does not get updated to reflect the change in the model.
So how can we resolve this? A simple Seam interceptor would do the trick (if you are already using stereotypes this will be a snap). Assuming you have not yet created a stereotype for your Action classes, the following steps can be taken,
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Interceptors(ActionInterceptor.class)
@Inherited
public @interface Action {}
This will ensure that any @Action class will be intercepted by the ActionInterceptor. Now create a method-level annotation similar to the following:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Search {
String dataTableId();
}
The dataTableId is the component ID of the DataTable in your page. Note: This will require that you specify unique IDs for your components which is good practice in JSF anyway. Next, you must provide the interceptor which does the work of reseting the first attribute for the component. When an @Search action is executed that resets the DataModel, we want to reset the UIData:
@Interceptor(stateless=true)
public class ActionInterceptor implements Serializable
{
...
@AroundInvoke
public Object aroundInvoke(InvocationContext invocation)
throws Exception
{
Method method = invocation.getMethod();
if(method.isAnnotationPresent(Search.class))
{
UIComponent component = FacesContext
.getCurrentInstance().getViewRoot()
.findComponent(searchAnnotation.dataTableId());
if(component != null && component instanceof UIData)
{
UIData dataTable = (UIData) component;
dataTable.setFirst(0);
}
}
result = invocation.proceed();
}
...
You would probably want to add your own exception handling to this as well. Finally, we can use it in our application:
@Name("myAction")
@Stateful
@Action
public class MyActionBean implements MyAction {
...
@DataModel
List<myentity> searchResults;
...
@Search(dataTableId="myForm:searchResults")
public void search() throws Exception
{
searchResults = entityManager.createQuery(...)
.getResultList();
}
...
As I mentioned before, if stereotypes are already being used, this becomes a snap. Simply add the @Search annotation and add the code to handle the dataTable reset in your existing ActionInterceptor.
Posted in JBoss Rich Faces, JBoss Seam, JSF
5 Comments »