Friday, October 23, 2015

Maven phases and goals

Maven has a life cycle in its build process. The key concept is "Phase". Do not be confused by another concept "goal". There are different but related. For example, "package" is a phase. It is not a goal.

Maven can support a number of different life cycles. But the mostly used one is the default Maven life cycle.

The default life cycle has the following phases (For a more detailed description, see reference[1]). And they are stepped through in order until it reaches at the phase that is specified in the Maven command.

  1. process-resources
  2. compile
  3. process-classes
  4. process-test-resources
  5. test-compile
  6. test
  7. prepare-package
  8. package
  9. install

Maven delegates the work to Maven plugins.

Plugin goals can be attached to a life cycle phase. When Maven moves through the phases, it executes the goals attached to each phase. Each phase can have zero or more goals bound to it.

A phase may mean different things to different projects. For example, for the package phase, a JAR project will produce a JAR file, while a web application will produce a WAR file.

If no plugin is specified in the pom file, Maven seems to use its default plugins. See reference[2].

You can run Maven by specifying a phase as in "mvn install". You can also run Maven by specifying the actual goals as in "mvn resources:resources compiler:compile".

References

[1] https://maven.apache.org/ref/3.3.3/maven-core/lifecycles.html
[2] https://maven.apache.org/ref/3.3.3/maven-core/default-bindings.html

Wednesday, October 21, 2015

Spring session scoped bean and HTTP session attribute

A simple JSF application app has a jsf file a.faces and a jsp file b.jsp. The jsp file has the following:
<%
    HttpSession sess = rquest.getSession();
    FooBean foo = (FooBean)sess.getAttribute("fooBean");
    foo.someMethod();
%>
And the jsf file has the following:
<ice:form onsubmit="#{fooBean.doAction}">
Here the tag ice is used because it is using icefaces. The name fooBean is for a Spring bean defined in applicationContext.xml as below:
<bean id="fooBean" class="FooBean" scope="session">
   <constructor-arg><ref bean="x" /> <constructor-arg>
   <constructor-arg><ref bean="y" /> <constructor-arg>
</bean>

<bean id="x" class="X"/>
<bean id="y" class="Y" scope="session"/>
And in the faces-config.xml, there is the following:
<faces-config>
 <application>
  <variable-resolver>
   org.springframework.web.jsf.DelegatingVariableResolver
  </variable-resolver>
 </application>
</faces-config>

The application is accessed by app/a.faces. And then you can access the jsp file app/b.jsp without any problem. But if you access the application using app/b.jsp as the first step, it will give NullPointerException when calling foo.someMethod() because foo is null.

The tricky thing here is how the "fooBean" is put into the session as an attribute. The answer is in the Spring DelegatingVariableResolver class and DefaultListableBeanFactory class.

When user types app/a.faces, the resolver sees #{fooBean.doAction}. It checks the applicationContext.xml and finds the bean definition. So it creates the bean x, y, and then fooBean. The bean x is a singleton. It is already created at the application startup. So the resolver only needs to get the cached instance of x. But the bean y has a scope of session. So the resolver calls DefaultListableBeanFactory and creates a brand new instance of y. And it puts it as an attribute into the HTTP Session object. After both x and y are created, a new fooBean is created and put into the session as an attribute since its scope is also session. So these session beans are created lazily when there are referenced.

Now if you access app/b.jsp without firstly accessing app/a.faces, the Spring resolver is not invoked. The session object has no attribute named "fooBean". And you will get a null object for this attribute.

The following is the method stacks showing the Spring bean is resolved and set into the HTTP session as an attribute.

owns: InterceptingServletSession  (id=15648) 
 MyAttributeListener.attributeAdded(HttpSessionBindingEvent) line: 13 
 EventsManager.notifySessionAttributeChange(HttpSession, String, Object, Object) line: 302 
 MemorySessionContext(SessionContext).notifySessionAttributeChange(HttpSession, String, Object, Object) line: 1479 
 MemorySessionData(SessionData).setAttribute(String, Object, boolean) line: 1121 
 MemorySessionData(SessionData).setAttribute(String, Object) line: 959 
 InterceptingServletSession(ProxyHttpSession).setAttribute(String, Object) line: 107 
 ServletSessionAttributeMap.setAttribute(String, Object) line: 20 
 ServletSessionAttributeMap(AbstractAttributeMap).put(Object, Object) line: 157 
 FacesRequestAttributes.setAttribute(String, Object, int) line: 108 
 SessionScope(AbstractRequestAttributesScope).get(String, ObjectFactory) line: 44 
 SessionScope.get(String, ObjectFactory) line: 90 
 DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class, Object[], boolean) line: 298 
 DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 185 
 DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 164 
 XmlWebApplicationContext(AbstractApplicationContext).getBean(String) line: 881 
 DelegatingVariableResolver.resolveSpringBean(FacesContext, String) line: 136 
 DelegatingVariableResolver.resolveVariable(FacesContext, String) line: 109 
 NamedValue.evaluate(ExpressionInfo) line: 145 
 ComplexValue.evaluate(ExpressionInfo) line: 166 
 ExpressionEvaluatorImpl.evaluate(ExpressionInfo) line: 263 
 ValueBindingImpl.getValue(FacesContext, String) line: 160 
 ValueBindingImpl.getValue(FacesContext) line: 143 
 HtmlForm(HtmlForm).getOnsubmit() line: 501