Saturday, January 15, 2011

Spring - Using Beans in HTTP Servlet

So you want to use your beautiful and shining Spring Beans in ugly and ordinary HTTP Servlet? Don't be ashamed ;) Sometimes we all have to do it, and we do it in many different ways ;) Below you may find few examples.

Old-fashioned way :) - used by me till some beautiful Thursday morning ...
public class BeanTest extends HttpServlet {

    private EmployerManager employerManager;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // Do something amazing with the Spring Bean ...
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());
        employerManager = applicationContext.getBean("business-logic.EmployerManager", EmployerManager.class);
        super.init(config);
    }

}
As you see, you can access Spring WebApplicationContext and then the desired bean by its identifier. Main disadvantage of this method is the bean identifier hardcoded into Servlet code. Especially if you are performing automatic component scan in Spring (ex. <context:component-scan base-package="[...].logic.impl" />). In this case you have two choices:
  1. Define the identifier of the bean using annotations:
    
    @Service("business-logic.EmployerManager")
    public class DefaultEmployerManager implements EmployerManager {
       ...
    }
    
  2. Use default bean identifier created by Spring in your Servlet - see: Naming autodetected components
Now relax, close your eyes, think about the refactoring the code, and all those hardcoded names which you should change by hand. It's time to forget this method my Friend, as I did. :)

Thankfully there are many young people, with open mind, who don't use the old-fashioned ways of doing things, and invent their own, as did my colleague - Bartek. Below you may find method doing the same as the above one, but based on @Autowired annotation usage.
public class BeanTest extends HttpServlet {

    @Autowired
    private EmployerManager employerManager;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // Do something amazing with the Spring Bean ...
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
        super.init(config);
    }

}

SpringBeanAutowiringSupport will inject the appropriate Spring Beans into all your Servlet's properties marked with @Autowired annotation. That's all :) No hardcoding of bean names, no worries about future refactorings. Now relax, close your eyes, ...

Sunday, January 9, 2011

Spring MVC - Session Attributes handling

Spring Framework 2.5 has introduced annotation based programming model for MVC Controllers. Spring Framework 3 has made next step, and deprecated widely used SimpleFormController. Now we are all doomed to use annotations ;) but do we really understand what is going on behind the scenes?

Let's take a look at the @SessionAttributes and SessionStatus today :) If you are not familiar with them, you should start with Specifying attributes to store in a session with @SessionAttributes and Supported handler method arguments and return types before you will continue reading this post.

@SessionAttributes annotation is used on the Controller class level to declare used session attributes. All Model attributes having names or types defined in this annotation will be automatically persisted and restored between the subsequent requests. Sounds easy and beautiful, isn't it? Small declaration instead of boring HttpSession's getAttribute / setAttribute calls. Tempting :), try it and you'll find it can be dangerous too ...

"Automatically persisted" should alarm you :) - where? how? when? and for how long? - you should ask immediately :) - Well, let's take a deeper look at it :)

Strategy used for persisting Model attributes is defined by SessionAttributeStore implementation, Spring Framework by default uses DefaultSessionAttributeStore which in turn relies on HttpSession as the storage (for Servlet environment). You can provide custom implementation of the SessionAttributeStore interface, which you then enable by supplying a custom bean configuration for an AnnotationMethodHandlerAdapter (see Spring MVC pitfall - overriding default beans).

Model attributes are persisted at the end of request handling by AnnotationMethodHandlerAdapter, after calling your handler method responsible for request handling and after determining the ModelAndView for the response. Their names in Model are used as their names in the persistent storage, ex. @SessionAttributes("form") will trigger persisting Model attribute named "form" as HttpSession attribute named "form".

Persisted Model attributes will be removed only if you'll call SessionStatus.setComplete() method in some of your handler methods.

If at this point you still don't have any doubts, let me share my own with you:
  1. Cleaning not needed session attributes is a little tricky, because in a little more complicated applications users usually have possibilities to use Breadcrumb Navigation, some Menus, etc., each of them can cause switching the flow from one to another without calling SessionStatus.setComplete() method (and thus without cleaning the attributes used by the flow). 
  2. Using default SessionAttributeStore implementation and same names for Model attributes for different types of data will sooner or later lead to beautiful ClassCastException. Why? Because when some part of your application will persist attribute of class A under name "form", and user will then switch without cleaning it to other functionality, using also "form" attribute, but expecting it to be of class B, that will not end with the exception only if A extends B.
We all struggle with the HttpSession attributes cleaning, even not using Spring Framework, in fact. Both the problems mentioned by me above can be found outside the Spring Framework based application too. Using this beautiful and shining @SessionAttributes annotation you have to know how does it work, otherwise it will make you mad, instead of helping you.

For the dessert :) - few pitfalls you may encounter while using @SessionAttributes:
  1. When you refer to non-existing session attribute using @ModelAttribute annotation, HttpSessionRequiredException will be raised, like in following example:
    @SessionAttributes("abc")
    public class MyController {
    
        @RequestMapping(method = RequestMethod.GET)
        public void onGet(@ModelAttribute("abc") String something) {
            ...
        }
    }
    
    This exception, like the any other, can be then handled by you, using @ExceptionHandler annotation for example.
  2. SessionStatus.setComplete() method will trigger cleaning of Session Attributes, but only those which Spring will find "actual session attribute". Suppose that you declare 3 session attributes, but use only 1 of them in your handler method parameters, like in following example:
    @SessionAttributes({ "abc", "def", "ghi" })
    public class BindingTestController {
    
        @ModelAttribute("abc")
        public String createABC() {
            return "abc";
        }
    
        @RequestMapping(method = RequestMethod.GET)
        public void onGet(@ModelAttribute("abc") String something) {
            // do nothing :)
        }
    
        @RequestMapping(method = RequestMethod.POST)
        public void onPost(@ModelAttribute("abc") String something, BindingResult bindingResult, SessionStatus sessionStatus) {
            sessionStatus.setComplete();
        }
    
    }
    
    Only the "abc" attribute will be considered as "actual session attribute", and removed on POST request.
  3. Attribute will be flagged as "actual session attribute" while resolving @ModelAttribute annotated method parameters and AFTER request processing - in this case all Model attributes are checked if they should be persisted (according to @SessionAttributes annotation). 
  4. Assuming that you pass the parameter to some flow, using @SessionAttributes mechanism may lead to surprises, sometimes unpleasant :( Suppose that you want to start new flow programmatically, from Controller A, you put the flow parameter on session, under name "parameter", and mark the Controller B (handling the entry point to the flow) with the @SessionAttribute("parameter"). That will not work my Friend, "parameter" will not be restored into B Controller's Model on entry, because it is not an "actual session attribute" (see above), at least before the first request handling. (Spring 3.1 brings remedy for this problem, named Flash Attributes - see my post Spring MVC - Flash Attributes)

Wednesday, January 5, 2011

33rd degree - Conference for Java Masters - Kraków, 6-8 April 2011

As you see I'm still alive :). After the heavy December (ugly virus stroke, Christmas, etc.), it's time to recover and start blogging again :) Something easy to write for the beginning ;)

33rd degree - Conference for Java Masters is coming up (Kraków, Poland) - you may register now on http://33degree.org/registration.html

Tuesday, December 7, 2010

JPA - insert instead of update

Few days ago I was digging through the code in some project to find the reason why EclipseLink (2.1.1) is performing database insert instead of update. Let me say, that it was the hardest thing to find in my whole JPA-using developer career - so for the records, and to let you find it faster than me ;)

There was an association between entities Alpha and Beta, made in following manner:
public class Alpha ... {
    ...
    private Beta beta;

    @ManyToOne(..., optional = false)
    @JoinColumn(name = "beta_id")
    @JoinFetch(JoinFetchType.INNER)
    public Beta getBeta() { 
        return beta;
    }
    ...
}
I wrote about the @JoinFetch annotation in one of my previous posts already (see: JPA Demystified (episode 1) - @OneToMany and @ManyToOne mappings) - it is used here, to fetch the associated Beta along with the Alpha, in one query.

In the project analyzed by me, instance of Alpha was created at one point, edited in few steps, with persisting into database between the steps. Everything was fine after the first step. Alpha was written into database (first insert), but after the second step, primary key violation occurred each time I tested it (same entity was again inserted into DB, while it should be updated at this point).

My first hint was missing equals and hashcode methods on Alpha and Beta. I've added it, but it doesn't helped at all. So I meet with the best friend of all Developers - Debugger :) - and started to dig into the project and EclipseLink source code. At this point let me give you first suggestion - when you trace the JPA problems use the Debugger and verify performed database operations in parallel, because only both those things can give you full view of the problem lurking beneath the Application's surface ;)

Watching the SQL queries, I've found that EclipseLink is performing query fetching the Alpha along with the Beta (as directed by @JoinFetch annotation), right before the invalid insert. Matching this operation to the Java code, I've found that EclipseLink is trying to check what has changed in the Alpha entity, to perform only the required updates to the database. The SQL query was using inner join ...

Do you suppose what was wrong? :) - There was no Beta entity associated to the Alpha, and therefore this query - "select ... from Alpha inner join Beta ..." was returning no records at all. At this point EclipseLink was assuming that Alpha entity was not written into database yet, and tried to do it again - Alpha has already generated identifier (while first insert into database), and ... beautiful exception :)

What you should learn from the above situation is that optional attribute of association definition is informational only! - It's provider choice to throw an exception if the associated value will be null, or do something completely else, as you see EclipseLink developers are ignoring it silently :(

Last but not least - use @JoinFetch(JoinFetchType.INNER) if and only if you are absolutely sure, that associated entity will always exist - ex. when the database scheme doesn't allow other possibility. Don't trust JPA provider completely ;)

Sunday, December 5, 2010

JPA - Lazy Loading and View rendering

JPA would be unusable without Lazy Loading, which is a feature allowing fetching of correlated entities from the database when they are referenced for the first time, rather than immediately when you perform the database query. Imagine that you cannot use Lazy Loading ... One query for the single result can return the whole database in this case, if you are lucky enough ;)

Each time when you use @OneToMany or @ManyToMany annotations, Lazy Loading is active by default, without any effort. For all other types of associations you may control it, using fetch attribute of annotation defining the association.

Sounds wonderful, isn't it? Everything is done automagically without my intervention, required entities are fetched from the database when they are needed, the world is beautiful and free of violence ... Well, not quite, my dear Watson ...

You have to be aware, that some entities may be referenced for the first time at the View rendering level. For Spring Framework based application with JSP used for View layer (but not only in this setup) this can lead to miscellaneous problems, which depends on used JPA provider.

Let's take a deeper look at the potential problems, using usual Employer - Employee - Benefit example from my previous JPA related posts. Suppose that we want to fetch the Employer information from the database, but the Employees and Benefits only when they are referenced. Associations between Employer, Employees and Benefits look like this:
@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {
    ...
    private List<Employee> employees;
    ...
    @OneToMany(mappedBy = "employer")
    public List<Employee> getEmployees() {
        return employees;
    }
    ...
}

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
    ...
    private List<Benefit> benefits;
    private Employer employer;
    ...
    @OneToMany(mappedBy = "employee")
    public List<Benefit> getBenefits() {
        return benefits;
    }

    @ManyToOne
    @JoinColumn(name = "EMPLOYER_ID")
    public Employer getEmployer() {
        return employer;
    }
    ...
}

@Entity
@Table(name = "BENEFITS")
public class Benefit implements Serializable {
    ...
    private Employee employee;

    @ManyToOne
    @JoinColumn(name = "EMPLOYEE_ID")
    public Employee getEmployee() {
        return employee;
    }
    ...
}
DAO fetching the Employer data for Model:
@Repository
public class DefaultEmployerDAO implements EmployerDAO {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public Employer get(Long identifier) {
        return entityManager.find(Employer.class, identifier);
    }
    ...
}
And the JSP view displaying the Employer information, along with the Employees, and their Benefits:
<h1><c:out value="${employer.businessName}" /></h1>
...
<c:forEach items="${employer.employees}" var="e">
    <tr>
        <td><c:out value="${e.firstName}" /></td>
        <td>
            <ul>
               <c:forEach items="${e.benefits}" var="b">
                   <li><c:out value="${b.name}" /></li>
               </c:forEach>
            </ul>
        </td>
    </tr>     
</c:forEach>
Let's examine how it will behave for different JPA providers, starting from EclipseLink 2.1.1. When we try to open desired view in browser, we will see something like this:


Application activity while preparing the Model (seen from the Spring Insight):


and while rendering the view itself:


All the SQL queries visible at the view rendering level are caused by Lazy Loading.
As you see the EclipseLink and Lazy Loading works without any special effort :)

Now it's time to check Hibernate 3.4.0.GA. For this provider, when we try to open the desired view in browser, we will see beautiful
LazyInitializationException: failed to lazily initialize a collection of role: [...].entities.domain.Employer.employees, no session or session was closed
Sweet, isn't it? :) - old hibernate session closed problem strikes again, this time in JPA ;) - but believe me, this is not the worst problem you may encounter using JPA ;), because with a little help from OpenEntityManagerInViewFilter it can be quickly corrected (if you are using hibernate for a little longer, you are probably well aware of very useful OpenSessionInViewFilter used in similar situations with "pure" hibernate). Let's add this filter to the web.xml:
<filter>
    <filter-name>OpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    <init-param>
        <param-name>entityManagerFactoryBeanName</param-name>
        <param-value>persistence.EntityManagerFactory</param-value>
    </init-param>
</filter>
...
<filter-mapping>
    <filter-name>OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
The entityManagerFactoryBeanName parameter should match the name of your Entity Manager factory bean in Spring Framework configuration.

With this filter we can get finally the desired results. This time Application activity while preparing the Model looks like this:

and while the view rendering:

Finally OpenJPA 2.0.1 - first try without the OpenEntityManagerInViewFilter, and we will see in browser:

Good Lord! No data loaded at all ... no exceptions reported ... the worst situation, because you don't know if there are no data to view in the database, or there is something wrong with the application! :(

Let's add the OpenEntityManagerInViewFilter, and try again:

Now it looks as expected!
Application activity while preparing the Model:


and the view rendering:


As you see above, Lazy Loading requires for both Hibernate and OpenJPA OpenEntityManagerInViewFilter to be used, while EclipseLink work without any special effort.