Showing posts with label lifecycle. Show all posts
Showing posts with label lifecycle. Show all posts

Friday, December 26, 2008

A study of ESRI Java web adf lifecycle & events - how they fit it in the servlet and JSF lifecycle and events

ESRI has a Java web Application Development Framework (ADF) for ArcGIS Server, similar to Oracle's web ADF and other web frameworks. The ESRI web ADF is an API for building mapping web applications and consists of a set of JSF server controls, e.g. TOC control, Map control, OverviewMap control, Navigation control, PrintTask control etc. 
A very high-level workflow is:
  1. use a full-fledged WYSIWYG desktop editor for creating and symbolizing maps and data, aka ArcMap
  2. publish that map as a web mapping service (MapService) using ArcGIS Server
  3. consume that MapService in a web application created using the ESRI web ADF (either in .net or java)
  4. make changes to the map if necessary and restart MapService. 
  5. The data changes are automatically picked up, e.g. data added/deleted/updated
It is a complicated and sophisticated framework built on top of the JSF framework. 
The JSF components of the ESRI java web ADF are also Ajax enabled.
It exposes its own task framework for building tasks/wizards/workflows on using the various JSF controls it exposes.
Sun outlines a few approaches for creating Ajax-enabled components in JSF. Link,
At ArcGIS Server 9.2, the JSF components are ajax-enabled using the PhaseListener approach. This approach is nicely explained in this pdf slideshow and the below diagram:
The ADF uses a PhaseListener (PostBackPhaseListener.java) to handle all Ajax requests. It us also responsible for rendering xml fragments that contain the update state for each ajax-enabled web adf control. The xml fragments are transformed to html via an xslt using some pre-existing xsl files and the client javascript updates the UI elements on the browser. 
Similar to the doc and case study outlined by Sun, the PhaseListener is triggered by keywords in the HttpRequest QueryString (Request Parameters). The keyword being "doAJAX=true" in this case.
Each ADF JSF component can have 1 or more AjaxRenderers associated with it. The purpose of the AjaxRenderer is to detect changes in the JSF component's state, and render xml fragments of any changes to the state. The way to detect change is flexible, they serialize the variables of the JSF component and compare it with a new serialized state, or custom code can be written.  
Anyways, I've digressed, the ajax implementation of the JSF controls is another topic for another post. 
Today I'd like to talk about something that's very important when developing other phaseListeners, servlets in a web adf application - when do the ADF lifecycle & events get kicked off in relation to the standard jsf phases and servlet lifecycles? 
Web ADF lifecycle:
1) very first request to the Java EE container for the web app:
new WebApplication()
webcontext.Initialization - webcontext instance1
webcontext.Passivation - webcontext instance1
2) 2nd ajax request
webcontext.activation - webcontext instance1
webcontext.passivation - webcontext instance1
3) 1st request from a new client
webcontext.initialization - webcontext instance2
webcontext.passivation - webcontext instance2
4)  2nd ajax request from the 2nd client
webcontext.activation - webcontext instance2
webcontext.passivation - webcontext instance2
5) final request from 2nd client
webcontext.activation  - webcontext instance2
webcontext.passivation - webcontext instance2
webcontext.destruction - webcontext instance2
6) final request from 1st client
webcontext.activation - webcontext instance1
webcontext.passivation - webcontext instance1
webcontext.destruction - webcontext instance1
7) Web Application is removed or Java EE container is shut down
WebApplication.destroy()
There are 6 JSF phases and the ADF phases exist in between those.
Servlet lifecycle:
ContextInitialized - creates an ADF WebApplication class instance (1 per web application, exists in application scope)
ADFServletContextListener
ContextDestroyed - destroys an ADF WebApplication class instance (1 per web application, exists in application scope)
JSF lifecycle:
RESTORE_VIEW:
beforePhase: ADF webContext.activate()  - there is no predefined phase for this, it can happen as soon as possible
APPLY_REQUEST_VALUES:
PostBackPhaseListener is kicked off if an ajax request is made- calls all AjaxRenderers to gather xml fragments of changed JSF web control states. 
RENDER_RESPONSE:
afterPhase: ADF webContext.passivate() - this event can happen before render_response or before response_complete (since response_complete may happen before render_response), basically this is the last event of the web adf lifecycle for an http request (once the webcontext has been initialized).
RESPONSE_COMPLETE event:
ADF webContext.passivate
The below is a java class that will show you when the various lifecycle/events of the ESRI java web ADF are being executed
package com.ncedis.adf.web.common;

import java.util.Iterator;
import java.util.Map;

import org.apache.log4j.Logger;

import com.esri.adf.web.data.WebContext;
import com.esri.adf.web.data.WebContextInitialize;
import com.esri.adf.web.data.WebContextObserver;
import com.esri.adf.web.data.WebLifecycle;

public class WebADFLifecycleNotifier implements WebContextObserver, WebContextInitialize, WebLifecycle
{
 private static final long serialVersionUID = -5663664360161434104L;
 private static final Logger logger = Logger.getLogger(WebADFLifecycleNotifier.class);
 
 private boolean isStartup = true;
 private WebContext webContext;

 public void update(WebContext webContext, Object obj)
 {
  logger.debug("WebContext.refresh() has been invoked, which invoked me(WebContextObserver) ...");
  if (isStartup)
  {  }
  this.webContext = webContext; 
  isStartup = false;
 }
 
 
 public static String arrayToString(Object[] array)
 {
  if (array == null)
   return null;
  
  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < array.length; i++)
  {
   sb.append("\t"+array[i]+"\n");
  }
  return sb.toString();
 } 
 
 public void destroy()
 {
  logger.debug("WebContext.destory() has been invoked, which invoked me ... [Terminating Client session]\n");
  logger.debug("Printing stack trace [NOT ERROR]-\n" + arrayToString(Thread.currentThread().getStackTrace()));  
  isStartup = true;
  webContext = null;
 }

 public void init(WebContext webContext)
 {
  logger.debug("WebContext.init() has been invoked, which invoked me ... [Received new Client Session Request]");
  webContext.addObserver(this);
  this.webContext = webContext;
  
  if (logger.isDebugEnabled())
  {
   try
   {
    StringBuffer sb = new StringBuffer("NONE");
    Map attribs = webContext.getAttributes();
    if (!attribs.isEmpty())
    { 
     sb = new StringBuffer("[");
     for (Iterator iter = attribs.entrySet().iterator(); iter.hasNext();)
     {
      Map.Entry element = (Map.Entry) iter.next();
      sb.append(element.getKey()).append(", ");   
     }
     sb.replace(sb.length()-1, sb.length(), "]");
    }
    logger.debug("WebContext.attributes = " + sb.toString());
   } 
   catch (Exception ex) 
   {
    logger.warn("Unable to print webContext.attributes - " + ex.getMessage() + " - " + ex.getStackTrace()[1]);
   }
  }
  
  /**
   * NOTE IMPORTANT: some managed-beans could be instantiated at this stage, 
   * but their properties are set later during the webapp execution.
   * 
   */
 }
 
 public void activate()
 {
  logger.debug("WebContext.activate() has been invoked, which invoked me ... [Client http-request is received]"); 
  isStartup = false;
 }

 public void passivate()
 {
  logger.debug("WebContext.passivate() has been invoked, which invoked me ... [Done with servicing HTTP-request] \n");
  isStartup = false;
 }
}
To better understand the web adf lifecycle with respect to the adf events/servlet events, we should create a PhaseListener class that outputs a message before and after every phase. This class should be as follows:
package com.ncedis.adf.web.faces;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import org.apache.log4j.Logger;

public class VerboseTestPhaseListener implements PhaseListener
{
 private static final long serialVersionUID = 131479863251034948L;
 private static final Logger logger = Logger.getLogger(VerboseTestPhaseListener.class);

 public void afterPhase(PhaseEvent event)
 {
   logger.debug(event.getPhaseId().toString() + " afterPhase");   
   if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) 
    logger.debug("Done with Request!\n"); 
   
 }

 public void beforePhase(PhaseEvent event)
 {
  //this is actually not the beginning of the new request.
  //if (event.getPhaseId() == PhaseId.RESTORE_VIEW) logger.debug("Processing new Request!");          
    logger.debug(event.getPhaseId().toString() + " beforePhase");
 }

 public PhaseId getPhaseId()
 {
  return PhaseId.ANY_PHASE;
 }
 
}
The above java class must be instantiated by the jsf managed-bean facility and inserted as an attribute of webcontext using the dependency injection framework of jsf. In simpler words, you'll have to modify the faces-config.xml file and declaratively add this class as an attribute of the webcontext instance as follows below:

Tuesday, December 23, 2008

ADF Task Framework lifecycle events

For ArcGIS Server 9.3 ADF for .NET, and as of today, the task framework lifecycle flow is undocumented in the ADF docs, so I had to write some test code to see how & when the events occur with respect to each other and the rest of the aspx page lifecycle. Whey is this info important? Because, it will allow me to understand when to execute certain code, e.g. removing nodes in a TreeViewPlusNode control, checking if user is logged in before rendering etc. 
The below summarizes my observations of the ADF task framework lifecycle events, hopefully the lifecycle flow won't change much in 9.4: 
  1. Page_Init
  2. Task.CreateChildControls
  3. Page_PreLoad
  4. Page_Load
  5. Page_PreRender
  6. Task.OnPreRender
  7. Page_PreRenderComplete
  8. Task.RenderControl
  9. Task.Render
  10. Task.RenderContents
  11. Task.OnRenderContents
Next is the list of the adf task framework's other events:
  1. Task.OnClose
  2. Task.OnExpand
  3. Task.OnMinimize
  4. Task.OnShow

Comparison of JSF / Java Servlet and asp.net lifecycle and events (similarities, differences)

Asp.net applications similar to JavaEE web apps have application events, and we can configure listeners/handlers for those events.
For a Java web app, the following events can be handled by wiriing up our listeners in the web.xml file (web deployment descriptor) (Ref: http://edocs.bea.com/wls/docs61/webapp/app_events.html#177041)
1) Application startup
2) Application shutdown
3) Session creation
4) Session invalidation/passivation
5) Session level attribute added/removed/replaced
6) Servlet attribute added/removed/replaced
JSF lifecycle events for a jsf page: (Listeners/Handlers are defined as PhaseListeners)
Similar to the IIS engine which passes the aspx request to a HttpHandler in a asp.net engine, the JavaEE app container (Tomcat, JBoss, WebLogic) passes the jsf request to the JSF Servlet (FacesServlet). The FacesServlet then goes through the below lifecycle events (the PhaseListener can execute code before and/or after a phase): 
 
1) Restore_View
2) Apply_Request_Values (can process events and exit lifecycle)
3) Process_Validations (can process events and exit lifecycle)
4) Update_Model_Values (can process events and exit lifecycle)
5) Invoke_Application (can process events and exit lifecycle)
6) Render_Response
  1. Servlet Context events: 1) Servlet Context created - javax.servlet.ServletContextListener.contextInitialized() 2) Servlet Context about to be shut down - javax.servlet.ServletContextListener.contextDestroyed() 3) Attribute added - javax.servlet.ServletContextListener.attributeAdded() 4) Attribute removed - javax.servlet.ServletContextListener.attributeRemoved() 5) Attribute replaced - javax.servlet.ServletContextListener.attributeReplaced()
  2. HTTP Session events: 1) Http session activated - javax.servlet.http.HttpSessionListener.sessionCreated() 2) Http session about to be passivated - javax.servlet.http.HttpSessionListener.sessionDestroyed() 3) Attribute added - javax.servlet.http.HttpSessionListener.attributeAdded() 4) Attribute removed - javax.servlet.http.HttpSessionListener.attributeRemoved() 5) Attribute replaced - javax.servlet.http.HttpSessionListener.attributeReplaced()
For a ASP.net web application: the below lifecycle-events (in order): 
(Ref: Pro asp.net 2.0 in C# 2005 from Apress)
These events occur for every request. Here an aspx page request is discussed.
The systems/actors in action here are the IIS engine (inetinfo.exe), and the asp.net engine (aspnet_wp.exe). The event handlers/listeners are registered/defined in the global.asax file.
1) Application_BeginRequest
2) Application_AuthenticateRequest
3) Application_AuthorizeRequest
4) Application_ResolveRequestCache
5) At this stage the http request is handed off the appropriate HttpHandler, e.g. for an aspx request it is passed to the asp.net engine (aspnet_wp.exe), where the page is compiled (if necessary) and instantiated. 
6) Application_AcquireRequestState
7) Application_PreRequestHandlerExecute
8) At this stage, the appropriate handler executes the request,e.g. for an aspx request, the event-handling code for the page is executed, page rendered to HTML, i.e. the ASPX page lifecycle is executed (start, init, load, validate/control events, postback, loadComplete, preRender, saveViewState, render, unload
9) Application_PostRequestHandlerExecute
10) Application_ReleaseRequestState
11) Application_UpdateRequestCache
12) Application_EndRequest
Asp.net application events:
These events do not always occur, they are triggered by the application or code.
These are commonly used to perform app initialization, cleanup, usage/stats logging/monitoring, profiling, and troubleshooting. 
1) Application_start
2) Session_start
3) Application_error
4) Session_end
5) Application_End
6) Application_Disposed
Asp.net aspx pafe lifecycle events:
This lifecycle is executed inside the asp.net engine (aspnet_wp.exe) for each aspx page. 
1) Page_Start
2) Page_PreInit
3) Page_Init
4) Page_InitComplete
5) Page_PreLoad
6) Page_Load
7) Process Control events/postback 
8) Page_LoadComplete
9) Page_PreRender
10) SaveStateComplete
11) Page_Render
12) Page_RenderComplete
13) Page_Unload