Jim Majure's Blog

Sunday Nov 18, 2007

CRUD Applications in Spring MVC

This blog describes the approach that I took in a recent project to implement simple CRUD functionality using Spring MVC. I had the following goals for this project:

  1. Allow basic CRUD functionality for a set of Hibernate-persisted objects.
  2. Maintain a clear separation between the Spring MVC-dependent code and the application code, and produce as little Spring MVC-dependent code as possible.
  3. Allow additional objects to be added with as little work as possible.

Basic Functionality

The basic functionality required is simple:

  1. Present a list of existing domain objects
  2. Allow a single instance in the list to be selected and edited
  3. Allow a new domain object instance to be created and populated
  4. Allow one or more domain object instances to be selected and deleted

Sample Domain Objects

To motivate the discussion, consider the following diagram showing the domain objects being managed. The domain objects include: Application, Company, and PropertySet.

Things to note in the diagram include:

  • The Application class contains a PropertySet class, which implies that saves and deletes are cascaded from an Application object to its contained PropertySet object. This is implemented with the Hibernate cascade capabilities.
  • The Application class refers to a Company class, which implies that saves and deletes are not cascaded from an Application object to its referenced Company object. Company objects are maintained separately from Application objects.

Basic Object Structure

To support the functionality, I used the following basic object structure:

Things to note in the diagram include:

  • There are 2 MVC controller classes: ManageDomainObjectController and EditDomainObjectController
  • The ManageDomainObjectController extends MultiActionController and is used to manage the list view of the domain object
  • The EditDomainObjectController extends SimpleFormController and is used to edit a single instance of the domain object
  • Both controller classes hold an instance of the AbstractManageDomainObjectsForm class. This class is a POJO that maintains a user's state for a particular type of domain object, and provides an interface for the CRUD actions on that object type.
  • The ManageApplicationsForm class inherits from AbstractManageDomainObjectsForm and holds a reference to the service object that it uses to conduct CRUD operations on the domain object

The AbstractManageDomainObjectsForm class plays an important role. It is shown in the following figure.

This class provides a public API that the controller objects can use to do the following:

  • Get the list of domain objects to display
  • Save a domain object that has been modified
  • Delete one or more domain objects
  • Create a new instance of the domain object
  • Filter the domain object list

Abstract methods (the "do" methods) are defined that must be implemented for each concrete implementation. These are the methods that are implemented for concrete classes to interact with the database to perform CRUD operations. (Note that the service objects could be similarly abstracted using Generics that would make these abstract methods unnecessary.)

Sample Interactions

The following interaction shows how the list view is shown.

In this interaction, the request is delegated to the controller, which calls the getModel method. This method delegates to the doRefreshDomainObjects implementation and returns a Map that contains the list of domain objects to display and (optionally) the currently selected object, plus a few other things. The controller returns a ModelAndView that forwards to the configured list view (e.g., a JSP)

The following interaction shows how the delete operation is done.

In this interaction, the request is delegated to the controller, which calls the deleteDomainObjects method. This method delegates to the doDeleteDomainObjects implementation which deletes the specified domain objects. The controller returns a ModelAndView that redirects to the configured list url. This effectively runs the previous interaction, showing the list minus the deleted objects.

Adding Support for a New Object Type

To add support for a new object type, the following steps must be taken:

  1. Create an implementation of the AbstractManageDomainObjects for the new object and configure it in the Spring configuration files as a session-scoped bean
  2. Create a JSP for the list view and a JSP for the edit view
  3. Configure an instance of the ManageDomainObjectController and EditDomainObjectController in the Spring configuration files

Typical configuration of the objects in Spring looks like this:

<bean id="manageApplicationsForm" class="com.bts.print.app.config.core.ManageApplicationsForm" scope="session">
    <aop:scoped-proxy />
    <property name="documentConfigurationService" ref="documentConfigurationService"/>
</bean>
<bean name="/manageapplications/*.htm" class="com.bts.print.app.util.mvc.ManageDomainObjectsController">
    <property name="manageConfigsForm" ref="manageApplicationsForm" />
    <property name="listViewName" value="manageApplications"/>
    <property name="redirectToList" value="redirect:/manageapplications/show.htm"/>
    <property name="redirectToEdit" value="redirect:/editapplication.htm"/>
</bean>

<bean name="/editapplication.htm" class="com.bts.print.app.util.mvc.EditDomainObjectController">
    <property name="formView" value="editApplication" />
    <property name="successView" value="redirect:/manageapplications/show.htm" />
    <property name="commandName" value="app" />
    <property name="commandClass" value="com.bts.print.domain.config.Application" />
    <property name="manageConfigsForm" ref="manageApplicationsForm"/>
    <property name="validator">
        <bean class="com.bts.print.app.config.mvc.ApplicationValidator"/>
    </property>
    <property name="requiredFields">
        <list>
            <value>applicationName</value>
            <value>priority</value>
        </list>
    </property>
</bean>

Notice that the ManageApplicationsForm bean is defined as scope="session" and includes the tag: "<aop:scoped-proxy />". This means that when this bean is injected into another bean, Spring actually injects a proxy that retrieves the appropriate instance from the current user's session.

Summary

This blog entry shows one way that you can create an editing framework that cleanly separates application code from framework code and allows support for new object types to be added quickly and easily.

Thursday Sep 13, 2007

Exception Handling in Java

As a consultant working on J2EE applications, it never ceases to amaze me how poorly exception handling is done in mission critical applications. It seems that often the first thing that I do when I begin working on an application in a new engagement is to review and rework the exception handling code.

In this blog entry I will discuss some common problems and present some strategies for effective exception handling. None of the information contained here is exactly new. Numerous books provide guidelines for exception handling and some frameworks have made changes in their use of exceptions so that they fit more nicely into an application-wide exception handling strategy. I'm mostly writing this so that I have a single, accessible spot to which to point interested people.

Common Problems

Let's look at the most common poor exception handling practice. The following code fragment is quite common in Java applications. I call this the "catch and gobble".

method m() {
    ...  // 1
    try {
        ... // 2
        someObject.someMethod(); // code that throws a checked exception...
        ... // 3
    } catch (SomeException e) {
        logger.error("An error occurred.", e);
    }
    ... // 4
}

A developer is coding along and runs into an API that throws a checked exception. When this happens, the compiler complains that the exception is not being handled correctly. The developer must either catch and "handle" the exception, or declare it to be thrown out of the method. Often the exception in question is quite general (i.e., could have been caused by a large number of specific conditions) and quite difficult to truly handle in the current method.

The easiest and most expedient approach is to "catch and gobble" the exception. The above code does the trick. The exception is taken care of and the developer can continue to work on the code path of interest.

This method is executing at the top of a call stack, which may look like this for a typical web application.

** a call stack to be inserted **

Here is a synopsis of the relevant parts of the stack:

  • The servlet that accepts the HTTP request
  • The controller that manages session state and delegates to business logic
  • The interceptor(s) that begin a transaction boundary
  • Our method, which is implementing some business logic
  • In the rare case that this exception is actually thrown, the code that corresponds to line 3 is not executed, while line 4 is executed (as well as the remainder of the call stack). This can lead to a range of side effects, depending on what this code does. Here are a few possiblities:

    1. The current request is completed without throwing any other exceptions and a response is presented back to the user. The user is unaware that an exception occurred, even though the transaction was not completed successfully (because not all of the code executed).
    2. The missing code causes another exception to occur after the method finishes executing. This exception is reported to the user, but the error message (and the corresponding logged exception) in no way indicate the true root cause of the problem.
    3. Because this code is executing within transaction boundaries, it is possible that bad data is inserted into the database without ever notifying the user that a problem has been encountered. This bad data might cause problems in subsequent transactions or even subsequent portions of the business process being executed by another user. This might happen later the same day, the next day, or possibly months later.

    The bottom line is that we don't really know what will happen in this situation, but this type of coding can introduce problems that are very difficult to find and can be enormously costly if the persistent data store is corrupted.

    There are exceptions, and there are exceptions...

    It's helpful, at this point, to be more specific about the types of exceptions that we are talking about. I find it useful to think about 3 types of exceptions:

    1. Coding errors
    2. System failures
    3. Business logic exceptions

    Coding errors are mistakes made by programmers that cause an exception to be thrown at runtime. For example, if a third-party API expects a non-null parameter, but the programmer passes a null value. This generates a NullPointerException at runtime (or, if we're lucky, a IllegalArgumentException).

    System failures occur when some part of the computing infrastructure fails. Examples include the network going down, the db server crashing, etc. These situations generate many different types of exceptions at runtime.

    Business logic exceptions represent business rules that are violated during processing. An example might be creating two instances of a Customer object and give them both the same SSN. This might be indicated, for example, as a db constraint violation.

    Business logic exceptions, by their nature, require the developer to write specific exception handling logic in order to notify the user of the problem and provide enough information to resolve it. When I'm doing development, I should know about the various business logic exceptions that I might encounter and also know what to do to in response.

    Coding errors and system failures - however, are completely unpredictable. (If you could predict a programming error, wouldn't you fix it? We never expect systems to fail, but it does happen.) These are the types of problems that lead to the poor exception handling practices illustrated above. In the following discussion, we will focus on these two types of exceptions and see if we can identify strategies that handle them effectively without placing an undue burden on the developer.

    Checked vs Unchecked Exceptions

    Many of the exception handling problems are due to the fact that Java supports both checked and unchecked exceptions. When checked exceptions are used, the Java compiler forces the developer to explicitly handle the exception with a try/catch(/finally) block, or declare it to be thrown out of the method. When APIs throw checked exceptions for coding errors or system failures, the most common response by developers is to do the catch and gobble.

    An unchecked exception, on the other hand, does not have to be handled by the code. An unchecked exception with be thrown out of the method and continue to pop methods off the stack until it is explicitly trapped in the code. Unchecked exceptions include all exception classes that extend (directly or indirectly) from RuntimeException.

    If unchecked exceptions were used by all API's that throw exceptions for coding errors or system failures, there would be a lot less bad exception handling. A number of frameworks have changed their exception handling strategies due to this problem. Both Spring and Hibernate now throw unckecked exceptions for most framework related problems. There are, however, still lots and lots of APIs around that throw checked exceptions, so we need a way to deal with it.

    Goals for Exception Handling

    Our exception handling strategy should accomplish the following goals:

    1. Stop the current execution path to ensure that the exception doesn't cause additional problems in subsequent execution
    2. Rollback any db transactions that have been begun, but not completed
    3. Insure consistency of the conversational state of the application given that the current request was not completed
    4. Pass the exception to a global exception handler that handles the exception by logging the exception to the system log file, sending emails, firing JMX notifications, and/or any other appropriate actions
    5. Provide immediate notification to the user that an exception occurred and that the current request did not complete
    6. Make handling exceptions easy for the developer

    Handling Exceptions 101

    The most straightforward way to deal with a checked exception that cannot be handled is the following:

    method m() {
        ...  // 1
        try {
            ... // 2
            someObject.someMethod(); // code that throws a checked exception...
            ... // 3
        } catch (SomeException e) {
            throw new RuntimeException("some message", e);
        }
        ... // 4
    }
    

    This code simply wraps the checked exception in an unchecked exception and re-throws it. This unchecked exception will travel back down the call stack until it is explicitly handled. Let's see how this stacks up to our goals:

    1. The current execution path is stopped, no subsequent code in this method or down the call stack is executed
    2. When the exception reaches the transactional interceptor, any db transactions in progress are rolled back. After the transactions are rolled back, the exception is re-thrown out of the interceptor so that it can be handled by the application.
    3. To ensure the consistence of conversational state, the code that manages such state must be developed to take potential exceptions into account. We'll talk more about this in a later section.
    4. A global exception handler must be registered at the appropriate location of the call stack. This typically in the UI application framework or the Servlet API. The framework will trap any exceptions that reach it, and pass them to the registered exception handler. We'll talk more about the exception handler in a later section.
    5. The last step is to notify the user of the error. Again, most UI frameworks tie this capability in with the exception handler.
    6. This approach clearly relieves the developer of much responsibility. If you can't deal with an exception, then simply wrap it in an unchecked exception and trust that somebody registered an exception handler in the right location.

    If you do nothing but this in your applications, your exception handling will have improved tremendously. You are guaranteed to know about ALL exceptions that are thrown at runtime during the execution of your system. It's far better to know about exceptions and then develop responses in specific situations, than to bury the exceptions and hope it doesn't lead to negative consequences.

    This is the starting point for better exception handling. In the following sections, I will put a finer point on some details.

    Global Exception Handlers

    A key element to a good exception handling strategy is a global exception handler. The global exception handler has two primary responsibilities:

    1. to present a message to the user (if there is one) indicating that an exception has occurred and that the request was not completely satisfied
    2. to notify operations staff of the exception through logging, emails, JMX notifications, SNMP traps, etc.

    Presenting an error page is important because it lets the user know that an error occurred, which must be done. It also ensures that the exact technical nature of the error is not presented back to the user. It's not uncommon, for example, to see a stack trace or low-level exception message displayed in a web page when an error occurs. This type of information provides important knowledge to hackers attempting to compromise an application.

    Most UI frameworks provide a mechanism to declaratively define pages to be displayed when exceptions are caught. The servlet specification even allows declarative exception handling using the <errorpage> tag in the web.xml file.

    In addition to showing an appropriate error page, the exception must be communicated to operational personnel so that they can take steps to correct the problem if necessary. This typically requires the creation of an exception handling class that contains the code to conduct the notifications. Start with an interface like this:

    
    public interface ExceptionHandler {
        void handleException(Throwable t);
    }
    
    

    The implementation of this class contains the notification code. Some possible behaviors include:

    1. Logging exceptions to the system log file
    2. Sending an email to an email group
    3. Firing SNMP traps

    This class can contain any logic necessary to effectively handle exceptions. The key is that there is a single code path that is responsible for handling all exceptions in the application.

    After the exception handler class is written with the appropriate logic, it must be installed in the application. Most UI frameworks have a mechanism to register exception handling classes so that exceptions are delegated appropriately. If not, it is usually possible to introduce a superclass in the right location that traps exceptions and passes them to the exception handler.

    Example

    Let's look at a example of an exception handler for a Spring MVC web application. Spring MVC has the HandlerExceptionResolver interface to resolve exceptions encountered in request handlers to views. The SimpleMappingExceptionResolver is a simple implementation that maps exception class names to view names. This class can be extended to delegate to our global exception handler prior to redirecting to an appropriate error view.

    class MyHandlerExceptionResolver extends SimpleMappingExceptionHandler {
        ExceptionHandler myHandler;
    
        ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
            myHandler.handleException(ex);
            return super.resolveException(request,response, handler, ex);
        }
    }
    
    

    By installing this exception resolver into a Spring MVC application, we've done two things: 1) directed all unhandled exceptions to our exception handling class; and 2) redirected the UI to an appropriate view.

    Managing Conversational State

    More to come...

    Adding Information to Exceptions

    More to come...

    Configuration Problems

    Coding Errors

    More to come...

    Handling Exceptions in Batch Processing

    More to come...

    Monday Apr 30, 2007

    Packaging for Flexibility

    A packaging strategy is a critical component to a flexible and adaptable code base. In many systems, however, there is no explicitly stated approach to the way system resources are separated into packages. This entry will discuss some possible strategies for a typical enterprise Java application.

    Before we start talking about specific issues around packaging, we should probably describe the environment in which we are working and the goals we would like to achieve. First of all, we are talking about enterprise-grade applications. These are applications that are mission critical and will be maintained and evolved into the future. Some characteristics of our system environment include:

    • Our set of requirements is imperfect, at best, and will certainly change as we develop and implement the system in a production environment
    • The business environment in which the system operates is continuously evolving
    • The business users will expect the application to be able to evolve along with the business environment, and do so quickly

    About the only thing that we are certain about in this type of environment is that the understanding of the problem space will continue to change for both the business users and the development staff. As things change, we need to have our system structured so that it can be adapted to the changes as quickly as possible.

    One of the most important elements to maintaining an adaptable and extensible code base is strong static dependency management. The packaging strategy that we choose is one of the key aspects to controlling and managing dependencies.

    Given this environment, here are some of the things I think a packaging strategy should achieve:

    1. Separate logical areas of functionality (i.e., componentize)
    2. Separate the core logic of our applications from the frameworks that we use to support that logic
    3. Clearly manage the dependencies different components, and between components and frameworks
    4. Clearly identify the components' public interfaces

    In the Beginning...

    In Java, the traditional way to establish a namespace for a system is using the following pattern:

    {com_or_org}.{company_name}.{application_name}
    

    For Source Allies, Inc., our name spaces start with com.sourceallies. And we'll use an application called "Hit" as an example, so the name space will be: com.sourceallies.hit.

    To Layer or not to Layer...

    One common approach to packaging is to begin by introducing layers. A common 3 layer approach includes:

    • Application Layer - contains packages and classes that directly support the software usage scenarios
    • Domain Layer - contains packages and classes that represent core domain concepts and persistent data
    • Infrastructure Layer - Contains utility classes, third party frameworks, and other resources that are completely abstracted away from the domain area of the application being implemented

    In a layered system, dependencies always move from higher layers to lower layers. Classes in the application layer can depend on classes in the domain and infrastructure layers, but classes in the domain or infrastructure layer can never have static dependencies on the classes in the application layer.

    Implicit vs. Explicit Layers...

    In an explicit layering approach, the layer becomes part of the package structure. For example,

    com.sourcallies.hit.app.*
    com.sourcallies.hit.domain.*
    com.sourcallies.hit.infra.*
    

    At a package level, the hit.app name space can depend on the hit.domain namespace, but not vice-versa. As development proceeds, if there is a resource in the application layer that can be used by the domain layer, then it probably needs to be refactored into a more abstract resource and moved to the infrastructure layer. This is shown in the following diagram.

    Often layers are used implicitly (sometimes by accident). In this case, a dependency analysis shows a clear flow of dependencies between packages without the introduction of large circular dependencies between high-level packages.

    Layers are not Tiers...

    Although there is correlation, a layer is not the same concept as a tier. For example, if there is some form of data that is required to support the user interface, and that data needs to be persisted to the database, then all of the classes required to do this can live in the application layer. In other words, classes corresponding to all tiers can be located in a single layer.

    Regardless of whether an implicit or explicit layering scheme is used, the principles of layering should always be followed. Dependencies should managed at the name space level, and they should move from packages that directly support the application usage scenarios, through packages that contain more general domain concepts, and finally to packages that are completely abstracted from the application domain.

    Layering begins to help us achieve a couple two goals of a packaging strategy - it is our first level of explicit dependency management.

    Packaging for Componentization

    One of our other goals is to separate our code base into functional areas, or components. When looking at this, let's work from an existing packaging approach to see how we can improve it to support components.

    First, let's think about the classes that are required to implement a typical domain concept. For a "Contract" domain concept, we would have the classes shown in the following diagram:

    • Contract - a "model" object, that represents a contract instance and contains the persistent data
    • IContractService - a service interface that provides the API to save and retrieve Contracts from the database, and to apply business logic
    • ContractException - an exception class that may be thrown by the service API
    • ContractServiceImpl - an implementation of the service interface, that is still platform independent
    • IContractDaoService - an interface that defines the API used to persist the data to the database
    • HibernateContractDaoServiceImpl - a Hibernate-specific implementation class for the Dao interface

    Now, let's think about where we put these classes. The Hit application uses a common practice, which is to package based on the archtype of the object class. So, we put all "model" classes in one package (regardless of the domain area of the classes), and we put all "service" objects in another package.

    Consider the "Contract" concept, shown above, and a "System Variable" concept. Based on the patterns we use in Hit, we would have the following classes:

    hit.model.Contract
    hit.model.SystemVariable
    hit.service.exception.ContractException
    hit.service.exception.SystemVariableException
    hit.service.IContractService
    hit.service.ISystemVariableService
    hit.service.impl.ContractServiceImpl
    hit.service.impl.SystemVariableServiceImpl
    hit.service.dao.IContractDaoService
    hit.service.dao.ISystemVariableDaoService
    hit.service.dao.hibernate.HibernateContractDaoServiceImpl
    hit.service.dao.hibernate.HibernateSystemVariableDaoServiceImpl
    

    The package dependcy relationships are shown in the following diagram:

    From this diagram, we can see that the "model" package is not dependent on the logic or other implementation details. We can also see that our logic (in .service.impl) is not dependent on our Hibernate-specific implementation (in .service.dao.hibernate). These are good things. However, we have no sense of whether "Contract" depends on "System Variable" or vice-versa or if the two have no dependencies on one another. Also, "SystemVariable" is clearly an application-independent concept. What if we want to use it in another application?

    So we could try something like this:

    hit.domain.contract.Contract
    hit.domain.contract.ContractException
    hit.domain.contract.IContractService
    hit.domain.contract.ContractServiceImpl
    hit.domain.contract.IContractDaoService
    hit.domain.contract.HibernateContractDaoServiceImpl
    
    hit.domain.systemvariable.SystemVariable
    hit.domain.systemvariable.SystemVariableException
    hit.domain.systemvariable.ISystemVariableService
    hit.domain.systemvariable.SystemVariableServiceImpl
    hit.domain.systemvariable.ISystemVariableDaoService
    hit.domain.systemvariable.HibernateSystemVariableDaoServiceImpl
    

    Now let's look at the package dependcy relationships:

    This solution clearly separates the classes based on functionality. We can see from this diagram, that the "Contract" component depends on the "System Variable" component.

    This solution doesn't, however, accomplish a couple of the other goals, one of which is to clearly identify the component's public API. The public API for a component should include the model objects, the service interface, and any exceptions thrown. In this case, a client of the "System Variable" component needs to "know" about these classes:

    hit.domain.systemvariable.SystemVariable
    hit.domain.systemvariable.SystemVariableException
    hit.domain.systemvariable.ISystemVariableService
    

    but not these, which represent the internal implementation details:

    hit.domain.systemvariable.SystemVariableServiceImpl
    hit.domain.systemvariable.ISystemVariableDaoService
    hit.domain.systemvariable.HibernateSystemVariableDaoServiceImpl
    

    An additional goal is to clearly separate our core logic from the external frameworks we use. In this case, the HibernateSystemVariableDaoServiceImpl class depends on the Hibernate API. So let's consider one more change:

    hit.domain.systemvariable.core.SystemVariable
    hit.domain.systemvariable.core.SystemVariableException
    hit.domain.systemvariable.core.ISystemVariableService
    hit.domain.systemvariable.impl.SystemVariableServiceImpl
    hit.domain.systemvariable.impl.ISystemVariableDaoService
    hit.domain.systemvariable.db.HibernateSystemVariableDaoServiceImpl
    

    Now the package dependcy relationships look like this:

    The core subpackage represents the component's public API. Clients of this component should be able to depend on core only. In this case, "Contract" depends on the core subpackage of the "System Variable" component, but doesn't depend on its internal implementation. The core package is also independent of its peer packages (which contain implementation specifics) both impl and db will depend on core, but not vice-versa.

    So this strategy separates by component, and also controls dependencies with supporting frameworks.

    Calendar

    Feeds

    Search

    Links

    Navigation

    Referrers