When you build an application and deploy it to weblogic server, you may need to set the environment first such as calling the following:
C:\bea1032\wlserver_10.3\server\bin\setWLSEnv.cmd. Then you can run the Ant targets that use the weblogic task wlcompile, wlappc, wldeploy, etc. This can be easily done on command line. But how to do it in the Eclipse IDE?
To solve this, just check what setWLSEnv.cmd does for the Ant. This script puts some jars on the classpath. We can put these jars on the classpath of the Ant that Eclipse invokes. You can configure the Ant for your build file. The following is an example for Weblogic workshop Eclipse that comes with Weblogic 10.3.
Right-click on build.xml. Select "Run as" -> "Open External tools Dialog". This will open up the configuration screen for running Ant on the build file.
In the Classpath tab, choose C:\bea10\modules\org.apache.ant_1.6.5 as the Ant Home. Then add the following jars to the classpath:
C:\bea10\wlserver_10.3\server\lib\weblogic.jar
C:\bea10\modules\features\weblogic.server.modules_10.3.0.0.jar
C:\bea10.slwerver_10.3\server\lib\webservices.jar
C:\bea10\modules\org.apache.ant_1.6.5\lib\ant-all.jar
C:\bea10\modules\net.sf.antcontrib_1.0.0.0_1-ob2\lib\ant-contrib.jar
That's it. You can now run the Ant target in the build file directly from Eclipse now.
Tuesday, October 19, 2010
Thursday, October 7, 2010
How to Debug Application Initialization Using Eclipse
We use weblogic here. In Eclipse, you can start a server in the debug mode. Usually you do the remote debugging of an Java enterprise application by setting break points in the source code and then invoke the application by using the web browser or other clients. But this process does not apply to the initialization process of the application. After the application is deployed and is running, its initialization such as the context initialization has already been finished. What if you want to debug the initialization?
You can start the application in the debug mode from Eclipse. Set the break points in the source code that is called during the initialization. Then invoke the ant build target from Eclipse to deploy the application again. The deploy process will stop at the break points during the initialization and you can debug it now.
You can start the application in the debug mode from Eclipse. Set the break points in the source code that is called during the initialization. Then invoke the ant build target from Eclipse to deploy the application again. The deploy process will stop at the break points during the initialization and you can debug it now.
Sunday, September 12, 2010
Blog One Year Anniversary
Today is Sep. 12, 2010, Sunday. It has been a year since I started this blog. The fall season seems coming earlier than last year. Some leaves are already red or fell down. There was some rain last night. Today is half cloudy and half sunny. We picked up nine tomatos and one zucchini from the garden. It has been a rewarding harvest in the last several weeks.
Sunday, August 22, 2010
IceFaces, Spring web flow and JSF lifecycle
Some people think that Icefaces is an implementation of JSF. That is actually not right. The heaviest duty that Icefaces put in is its implementation of ViewHandler: D2DViewHandler. It is configured in the faces-config.xml inside icefaces.1.6.2.jar:
In the D2DViewHandler, Icefaces uses its own JsfJspDigester to parse the JSF/JSP files. But the core of the whole program is still the LifecycleImpl class ( This class implements javax.faces.lifecycle.Lifecycle ) from SUN to carry out the JSF lifecycle.
Note that the ICEfaces facelet viewHandler is a subclass of the D2DViewHandler:
We'll check the details of how it works. In this analysis, Icefaces 1.6.2 is used.
The major class of Icefaces is com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet.
The PersistentFacesServlet is actually a MainServlet. It has a PathDispather:
The method dispathOn is the following:
In short, when an Icefaces URL is sent to the server, this servlet PersistentFacesServlet is invoked. It creates many objects and then services the request. Eventually a PageServer object services the request. The class is the following:
The code of LifecycleImpl for the two methods is basically the following:
Looking at the method execute() and phase(), we can see the following:
Now the next activity is the call lifecycle.render(facesContext). Recall that the lifecycle.execute() method does not invoke the Render phase.
Note: The methods renderResponse() and responseComplete() of the class FacesContext just signal the JavaServer faces what has been done. They do not actually do the rendering. See Javadoc of FacesContext.
When Spring webflow is used, the FlowPhaseListener's beforePhase() is called in this phase. If it is the start of the flow, the beforePhase() method will call
After the beforePhase() method is called, the execute() method of the RestoreViewPhase will be called. For the first time flow entry, the execute() method will call the following:
http://localhost:7001/foo/xyz.iface?_flowId=xyz-flow, the viewId is xyz.iface. The line
However, in the RenderResponsePhase, the FlowPhaseListener will update the ViewRoot in its beforePhase() method and set it to the start view of the web flow. Recall from before that the FlowPhaseListener's beforePhase() has picked up the ViewSelection and stored it in the FlowExecutionHolder. So it seems that the execute() method of the RestoreViewPhase does something but is then ignored. For details of how the ViewRoot is updated in the RenderResponsePhase, see the prepareApplicationView() method and the updateViewRoot() method of the class FlowPhaseListener.
UpdateModelValuesPhase
Notes on UIViewRoot:
UICommand is in the package javax.faces.component
ViewSelection from the FlowExecution and save it in the FlowExecutionHolder:
RenderResponsePhase
The execute() method has the following main code:
The handleQuietly() method deals with several cases. For one of the cases, it calls
FlowPhaseListener
The Spring class FlowPhaseListener is a key in integrating Spring web flow into JSF. The following are some important methods of the class.
So the beforePhase only reacts to the RESTORE_VIEW and RENDER_RESPONSE phases.
FlowNavigationHandler
The class FlowNavigationHandler is another key in integration of JSF and Spring web flow:
D2dViewHandler
<application>
<view-handler>com.icesoft.faces.application.D2DViewHandler</view-handler>
</application>
<factory>
<faces-context-factory>
com.icesoft.faces.context.FacesContextFactoryImpl
</faces-context-factory>
</factory>
....
In the D2DViewHandler, Icefaces uses its own JsfJspDigester to parse the JSF/JSP files. But the core of the whole program is still the LifecycleImpl class ( This class implements javax.faces.lifecycle.Lifecycle ) from SUN to carry out the JSF lifecycle.
Note that the ICEfaces facelet viewHandler is a subclass of the D2DViewHandler:
public class D2DFaceletViewHandler extends D2DViewHandlerD2DFaceletViewHandler inherits from D2DViewHandler all the public methods including the following
public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesExceptionThis method calls the renderResponse method:
protected void renderResponse(FacesContext facesContext) throws IOExceptionD2DFaceletViewHandler overrides this method.
We'll check the details of how it works. In this analysis, Icefaces 1.6.2 is used.
The major class of Icefaces is com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet.
The PersistentFacesServlet is actually a MainServlet. It has a PathDispather:
private PathDispatcher dispatcher = new PathDispatcher();In the init() method, it has the following:
PseudoServlet resourceServer = new BasicAdaptingServlet(new ResourceServer(configuration, mimeTypeMatcher, localFileLocator));
PseudoServlet sessionServer = new SessionDispatcher() {
protected PseudoServlet newServlet(HttpSession session, Listener.Monitor sessionMonitor) {
return new MainSessionBoundServlet(session, sessionMonitor, idGenerator, configuration);
}
};
dispatcher.dispatchOn(".*(\\.iface$|\\.jsf|\\.faces$|\\.jsp$|\\.jspx$|\\.html$|\\.xhtml$|\\.seam$|uploadHtml$|block\\/)", sessionServer);
dispatcher.dispatchOn(".*", resourceServer);
The method dispathOn is the following:
public void dispatchOn(String pathExpression, PseudoServlet toServer) {
matchers.add(new Matcher(pathExpression, toServer));
}
In short, when an Icefaces URL is sent to the server, this servlet PersistentFacesServlet is invoked. It creates many objects and then services the request. Eventually a PageServer object services the request. The class is the following:
public class PageServer implements Server {
private final static LifecycleFactory LIFECYCLE_FACTORY = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
private Lifecycle lifecycle = LIFECYCLE_FACTORY.getLifecycle(LIFECYCLE_FACTORY.DEFAULT_LIFECYCLE);
private ResponseHandler responseHandler = new ResponseHandler() {
public void respond(Response response) throws Exception {
FacesContext facesContext = FacesContext.getCurrentInstance();
response.setHeader("Cache-Control", new String[]{"no-cache", "no-store", "must-revalidate"});//HTTP 1.1
response.setHeader("Pragma", "no-cache");//HTTP 1.0
response.setHeader("Expires", 0);//prevents proxy caching
response.setHeader("Content-Type", "text/html");
com.icesoft.util.SeamUtilities.removeSeamDebugPhaseListener(lifecycle);
lifecycle.execute(facesContext);
lifecycle.render(facesContext);
}
};
Here the lifecycle object and its factory are of the following two classes:com.sun.faces.lifecycle.LifecycleFactoryImpl com.sun.faces.lifecycle.LifecycleImplThe PersistentFacesServlet has created these objects already before using the PageServer.
The code of LifecycleImpl for the two methods is basically the following:
public void execute(FacesContext context) throws FacesException
{
if(context == null)
throw new NullPointerException(Util.getExceptionMessageString("com.sun.faces.NULL_PARAMETERS_ERROR"));
if(log.isDebugEnabled())
log.debug("execute(" + context + ")");
for(int i = 1; i < phases.length; i++)
{
if(context.getRenderResponse() || context.getResponseComplete())
break;
phase((PhaseId)PhaseId.VALUES.get(i), phases[i], context);
if(reload((PhaseId)PhaseId.VALUES.get(i), context))
{
if(log.isDebugEnabled())
log.debug("Skipping rest of execute() because of a reload");
context.renderResponse();
}
}
}
public void render(FacesContext context) throws FacesException
{
if(context == null)
throw new NullPointerException(Util.getExceptionMessageString("com.sun.faces.NULL_PARAMETERS_ERROR"));
if(log.isDebugEnabled())
log.debug("render(" + context + ")");
if(!context.getResponseComplete())
phase(PhaseId.RENDER_RESPONSE, response, context);
}
private void phase(PhaseId phaseId, Phase phase, FacesContext context)
throws FacesException
{
List firedListeners;
PhaseEvent event;
if(log.isTraceEnabled())
log.trace("phase(" + phaseId.toString() + "," + context + ")");
List ourListeners = listeners;
int numListeners = ourListeners.size();
firedListeners = new ArrayList(numListeners);
if(numListeners <= 0)
break MISSING_BLOCK_LABEL_573;
event = new PhaseEvent(context, phaseId, this);
int i = 0;
try
{
while(i < numListeners)
{
PhaseListener listener = (PhaseListener)ourListeners.get(i);
PhaseId listenerPhaseId = listener.getPhaseId();
if(phaseId.equals(listenerPhaseId) || PhaseId.ANY_PHASE.equals(listenerPhaseId))
{
listener.beforePhase(event);
firedListeners.add(listener);
}
i++;
}
}
catch(Throwable e)
{
if(log.isTraceEnabled())
log.trace("beforePhase(" + phaseId.toString() + "," + context + ") threw exception: " + e + " " + e.getMessage() + "\n" + Util.getStackTraceString(e));
}
if(!skipping(phaseId, context))
phase.execute(context);
int i = firedListeners.size() - 1;
try
{
while(i >= 0)
{
PhaseListener listener = (PhaseListener)firedListeners.get(i);
listener.afterPhase(event);
i--;
}
}
catch(Throwable e)
{
if(log.isTraceEnabled())
log.trace("afterPhase(" + phaseId.toString() + "," + context + ") threw exception: " + e + " " + e.getMessage() + "\n" + Util.getStackTraceString(e));
}
break MISSING_BLOCK_LABEL_587;
Exception exception;
exception;
int i = firedListeners.size() - 1;
try
{
while(i >= 0)
{
PhaseListener listener = (PhaseListener)firedListeners.get(i);
listener.afterPhase(event);
i--;
}
}
catch(Throwable e)
{
if(log.isTraceEnabled())
log.trace("afterPhase(" + phaseId.toString() + "," + context + ") threw exception: " + e + " " + e.getMessage() + "\n" + Util.getStackTraceString(e));
}
throw exception;
if(!skipping(phaseId, context))
phase.execute(context);
}
private Phase phases[] = {
null, new RestoreViewPhase(), new ApplyRequestValuesPhase(), new ProcessValidationsPhase(), new UpdateModelValuesPhase(), new InvokeApplicationPhase()
};
Looking at the method execute() and phase(), we can see the following:
- The execute() method basically calls each phase except for the last phase "Render Response"
- In each phase, the phase listerners for that phase will be invoked for their beforePhase() methods. Then the phase will execute. And then the afterPhase() method will be called. Note that these phase listeners are kept in the LifecycleImpl instance. Each phase just loops through these listeners and invokes only those with the same phase id or the ANY phase id.
- During the executions, the methods call the methods renderResponse() and responseComplete() of the class FacesContext frequently to check if the lifecycle needs to be short curcuited.
- Each phase implements its own execute() method.
Now the next activity is the call lifecycle.render(facesContext). Recall that the lifecycle.execute() method does not invoke the Render phase.
Note: The methods renderResponse() and responseComplete() of the class FacesContext just signal the JavaServer faces what has been done. They do not actually do the rendering. See Javadoc of FacesContext.
RestoreView Phase
The execute() method has the following piece of code:public void execute(FacesContext facesContext)
throws FacesException
{
if(log.isDebugEnabled())
log.debug("Entering RestoreViewPhase");
if(null == facesContext)
throw new FacesException(Util.getExceptionMessageString("com.sun.faces.NULL_CONTEXT_ERROR"));
UIViewRoot viewRoot = facesContext.getViewRoot();
java.util.Locale locale = null;
if(viewRoot != null)
{
if(log.isDebugEnabled())
log.debug("Found a pre created view in FacesContext");
locale = facesContext.getExternalContext().getRequestLocale();
facesContext.getViewRoot().setLocale(locale);
doPerComponentActions(facesContext, viewRoot);
return;
}
Map requestMap = facesContext.getExternalContext().getRequestMap();
String viewId = (String)requestMap.get("javax.servlet.include.path_info");
if(viewId == null)
viewId = facesContext.getExternalContext().getRequestPathInfo();
if(viewId == null)
viewId = (String)requestMap.get("javax.servlet.include.servlet_path");
if(viewId == null)
{
Object request = facesContext.getExternalContext().getRequest();
if(request instanceof HttpServletRequest)
viewId = ((HttpServletRequest)request).getServletPath();
}
if(viewId == null)
{
if(log.isDebugEnabled())
log.debug("viewId is null");
throw new FacesException(Util.getExceptionMessageString("com.sun.faces.NULL_REQUEST_VIEW_ERROR"));
}
if(null == (viewRoot = Util.getViewHandler(facesContext).restoreView(facesContext, viewId)))
{
if(log.isDebugEnabled())
log.debug("New request: creating a view for " + viewId);
viewRoot = Util.getViewHandler(facesContext).createView(facesContext, viewId);
facesContext.renderResponse();
} else
if(log.isDebugEnabled())
log.debug("Postback: Restored view for " + viewId);
Util.doAssert(null != viewRoot);
facesContext.setViewRoot(viewRoot);
doPerComponentActions(facesContext, viewRoot);
if(log.isDebugEnabled())
log.debug("Exiting RestoreViewPhase");
}
When Spring webflow is used, the FlowPhaseListener's beforePhase() is called in this phase. If it is the start of the flow, the beforePhase() method will call
restoreFlowExecution(event.getFacesContext());If the flowId is used in the URL, this method will call the following code:
String flowId = argumentHandler.extractFlowId(context); FlowDefinition flowDefinition = getLocator(context).getFlowDefinition(flowId); FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition); FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution); FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext); ViewSelection selectedView = flowExecution.start(createInput(context), context); holder.setViewSelection(selectedView);We can see that the key objects are FlowDefinition and FlowExecution. At the end, it gets the ViewSelection and saves this in the holder. This ViewSelection will be used in the RenderResponsePhase to render the view.
After the beforePhase() method is called, the execute() method of the RestoreViewPhase will be called. For the first time flow entry, the execute() method will call the following:
if(null == (viewRoot = Util.getViewHandler(facesContext).restoreView(facesContext, viewId)))
{
if(log.isDebugEnabled())
log.debug("New request: creating a view for " + viewId);
viewRoot = Util.getViewHandler(facesContext).createView(facesContext, viewId);
facesContext.renderResponse();
}
where the "if" condition is true. This is a place where the ViewHandler comes into play. When Icefaces is used, the ViewHandler is D2dViewHandler or D2DFaceletViewHandler. The createView() method is the following:public UIViewRoot createView(FacesContext context, String viewId) {
initializeParameters(context);
if (delegateView(viewId)) {
return delegate.createView(context, viewId);
}
// #2139 Remove this token here as well
ExternalContext externalContext = context.getExternalContext();
externalContext.getRequestParameterMap().remove(
PersistentFacesCommonlet.SEAM_LIFECYCLE_SHORTCUT);
UIViewRoot root = new NamespacingViewRoot(context);
// UIViewRoot root = new UIViewRoot();
root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
Map contextServletTable =
getContextServletTable(context);
if (null == viewId) {
root.setViewId("default");
context.setViewRoot(root);
contextServletTable
.put(DOMResponseWriter.RESPONSE_VIEWROOT, root);
Locale locale = calculateLocale(context);
root.setLocale(locale);
return root;
}
root.setViewId(viewId);
context.setViewRoot(root);
contextServletTable.put(DOMResponseWriter.RESPONSE_VIEWROOT, root);
return root;
}
When Spring web flow is also used and the URL from web client is likehttp://localhost:7001/foo/xyz.iface?_flowId=xyz-flow, the viewId is xyz.iface. The line
UIViewRoot root = new NamespacingViewRoot(context);is called and is set to the context.
However, in the RenderResponsePhase, the FlowPhaseListener will update the ViewRoot in its beforePhase() method and set it to the start view of the web flow. Recall from before that the FlowPhaseListener's beforePhase() has picked up the ViewSelection and stored it in the FlowExecutionHolder. So it seems that the execute() method of the RestoreViewPhase does something but is then ignored. For details of how the ViewRoot is updated in the RenderResponsePhase, see the prepareApplicationView() method and the updateViewRoot() method of the class FlowPhaseListener.
ApplyRequestValuesPhase
public void execute(FacesContext facesContext)
throws FacesException
{
if(log.isDebugEnabled())
log.debug("Entering ApplyRequestValuesPhase");
UIComponent component = facesContext.getViewRoot();
Util.doAssert(null != component);
try
{
component.processDecodes(facesContext);
}
catch(RuntimeException re)
{
String exceptionMessage = re.getMessage();
if(null != exceptionMessage && log.isErrorEnabled())
log.error(exceptionMessage, re);
throw new FacesException(exceptionMessage, re);
}
if(log.isDebugEnabled())
log.debug("Exiting ApplyRequestValuesPhase");
}
ProcessValidationsPhase
public void execute(FacesContext facesContext)
throws FacesException
{
if(log.isDebugEnabled())
log.debug("Entering ProcessValidationsPhase");
UIComponent component = facesContext.getViewRoot();
Util.doAssert(null != component);
try
{
component.processValidators(facesContext);
}
catch(RuntimeException re)
{
String exceptionMessage = re.getMessage();
if(null != exceptionMessage && log.isErrorEnabled())
log.error(exceptionMessage, re);
throw new FacesException(exceptionMessage, re);
}
if(log.isDebugEnabled())
log.debug("Exiting ProcessValidationsPhase");
}
UpdateModelValuesPhase
public void execute(FacesContext facesContext)
{
if(log.isDebugEnabled())
log.debug("Entering UpdateModelValuesPhase");
UIComponent component = facesContext.getViewRoot();
Util.doAssert(null != component);
String exceptionMessage = null;
try
{
component.processUpdates(facesContext);
}
catch(IllegalStateException e)
{
exceptionMessage = e.getMessage();
}
catch(FacesException fe)
{
exceptionMessage = fe.getMessage();
}
if(null != exceptionMessage)
{
Object params[] = new Object[3];
params[2] = exceptionMessage;
facesContext.addMessage(component.getClientId(facesContext), MessageFactory.getMessage(facesContext, "com.sun.faces.MODELUPDATE_ERROR", params));
if(log.isErrorEnabled())
log.error(exceptionMessage);
if(log.isDebugEnabled())
log.debug("Exiting UpdateModelValuesPhase");
}
}
InvokeApplicationPhase
public void execute(FacesContext facesContext)
throws FacesException
{
if(log.isDebugEnabled())
log.debug("Entering InvokeApplicationsPhase");
UIViewRoot root = facesContext.getViewRoot();
Util.doAssert(null != root);
try
{
root.processApplication(facesContext);
}
catch(RuntimeException re)
{
String exceptionMessage = re.getMessage();
if(null != exceptionMessage && log.isErrorEnabled())
log.error(exceptionMessage, re);
throw new FacesException(exceptionMessage, re);
}
if(log.isDebugEnabled())
log.debug("Exiting InvokeApplicationsPhase");
}Notes on UIViewRoot:
- All the events are stored in this object. The instance variable events is a list of list. Each event has a PhaseId. The event is queued in the list for that phase.
- Each event is associated with the component which is the source of the event.
- The broadcastEvents() method delegate the handling to the same method of the event source component. It was done first for the ANY
PhaseId, and then the PhaseId of this phase.
package javax.faces.component;
....
public class UIViewRoot extends UIComponentBase
private int lastId;
private String renderKitId;
private String viewId;
private transient List events[];
private Locale locale;
public void queueEvent(FacesEvent event)
{
if(event == null)
throw new NullPointerException();
int i = 0;
int len = 0;
if(events == null)
{
events = new List[len = PhaseId.VALUES.size()];
for(i = 0; i < len; i++)
events[i] = new ArrayList(5);
}
events[event.getPhaseId().getOrdinal()].add(event);
}
public void processApplication(FacesContext context)
{
broadcastEvents(context, PhaseId.INVOKE_APPLICATION);
}
private void broadcastEvents(FacesContext context, PhaseId phaseId)
{
List eventsForPhaseId = null;
if(null == events)
return;
boolean hasMoreAnyPhaseEvents = true;
boolean hasMoreCurrentPhaseEvents = true;
eventsForPhaseId = events[PhaseId.ANY_PHASE.getOrdinal()];
do
{
if(null != eventsForPhaseId)
{
for(int cursor = 0; cursor < eventsForPhaseId.size(); eventsForPhaseId.remove(cursor))
{
FacesEvent event = (FacesEvent)eventsForPhaseId.get(cursor);
UIComponent source = event.getComponent();
try
{
source.broadcast(event);
}
catch(AbortProcessingException e) { }
}
}
if(null != (eventsForPhaseId = events[phaseId.getOrdinal()]))
{
for(int cursor = 0; cursor < eventsForPhaseId.size(); eventsForPhaseId.remove(cursor))
{
FacesEvent event = (FacesEvent)eventsForPhaseId.get(cursor);
UIComponent source = event.getComponent();
try
{
source.broadcast(event);
}
catch(AbortProcessingException e) { }
}
}
hasMoreAnyPhaseEvents = null != (eventsForPhaseId = events[PhaseId.ANY_PHASE.getOrdinal()]) && eventsForPhaseId.size() > 0;
hasMoreCurrentPhaseEvents = null != events[phaseId.getOrdinal()] && events[phaseId.getOrdinal()].size() > 0;
} while(hasMoreAnyPhaseEvents || hasMoreCurrentPhaseEvents);
}
UICommand is in the package javax.faces.component
public class UICommand extends UIComponentBase
implements ActionSource
{
.......
public void broadcast(FacesEvent event)
throws AbortProcessingException
{
super.broadcast(event);
if(event instanceof ActionEvent)
{
FacesContext context = getFacesContext();
MethodBinding mb = getActionListener();
if(mb != null)
mb.invoke(context, new Object[] {
event
});
ActionListener listener = context.getApplication().getActionListener();
if(listener != null)
listener.processAction((ActionEvent)event);
}
}
}
The HtmlCommandLink and HtmlCommandButton are subclasses of UICommand. Icefaces has the class com.icesoft.faces.component.ext.HtmlCommandLink, which extends javax.faces.component.HtmlCommandLink.package com.sun.faces.application
public class ActionListenerImpl
implements ActionListener
{
.....
public void processAction(ActionEvent event)
{
if(log.isDebugEnabled())
log.debug("processAction(" + event.getComponent().getId() + ")");
UIComponent source = event.getComponent();
ActionSource actionSource = (ActionSource)source;
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
String outcome = null;
MethodBinding binding = null;
binding = actionSource.getAction();
if(binding != null)
try
{
outcome = (String)binding.invoke(context, null);
}
catch(MethodNotFoundException e)
{
throw new FacesException(binding.getExpressionString() + ": " + e, e);
}
catch(EvaluationException e)
{
throw new FacesException(binding.getExpressionString() + ": " + e, e);
}
NavigationHandler navHandler = application.getNavigationHandler();
navHandler.handleNavigation(context, null == binding ? null : binding.getExpressionString(), outcome);
context.renderResponse();
}
}
Note that this is where the NavigationHandler comes into play . When Spring webflow is used, the NavigationHandler is FlowNavigationHandler. Its handleNavigation() method will calculate the ViewSelection from the FlowExecution and save it in the FlowExecutionHolder:
holder.setViewSelection(selectedView);
RenderResponsePhase
The execute() method has the following main code:public void execute(FacesContext facesContext)
throws FacesException
{
if(log.isDebugEnabled())
log.debug("Entering RenderResponsePhase");
if(log.isDebugEnabled())
log.debug("About to render view " + facesContext.getViewRoot().getViewId());
try
{
facesContext.getApplication().getViewHandler().renderView(facesContext, facesContext.getViewRoot());
}
catch(IOException e)
{
throw new FacesException(e.getMessage(), e);
}
if(log.isDebugEnabled())
log.debug("Exiting RenderResponsePhase");
}
Notice that this is another place where the ViewHandler comes into play. It renders the view which is the ViewRoot from the FacesContext. When Spring webflow is used, the FlowPhaseListener sets the ViewRoot in its beforePhase() method. In this method, it calls the prepareResponse() method. This method gets the ViewSelection in the FlowExecutionHolder:ViewSelection selectedView = holder.getViewSelection();Recall that in the InvokeApplicationPhase, the Spring NavigationHandler has set the ViewSelection in the holder. Then the prepareResponse() method creates a ResponseInstructionHandler object and calls its handleQuietly() method.
The handleQuietly() method deals with several cases. For one of the cases, it calls
handleApplicationView((ApplicationView)responseInstruction.getViewSelection());The ResponseInstructionHandler object implements this method as follows:
protected void handleApplicationView(ApplicationView view) throws Exception {
prepareApplicationView(context.getFacesContext(), holder);
}
The method prepareApplicationView() is a method in the FlowPhaseListener class as the ResponseInstructionHandler object is a created inside this class:protected void prepareApplicationView(FacesContext facesContext, FlowExecutionHolder holder) {
ApplicationView view = (ApplicationView) holder.getViewSelection();
if (view != null) {
// expose the view's "model map" in the request map
putInto(facesContext.getExternalContext().getRequestMap(), view.getModel());
// update the root component if necessary
updateViewRoot(facesContext, viewIdMapper.mapViewId(view.getViewName()));
}
String flowExecutionKey = holder.getFlowExecution().isActive() ? holder.getFlowExecutionKey().toString() : null;
if (flowExecutionKey != null) {
saveInViewRoot(facesContext, flowExecutionKey);
}
Map requestMap = facesContext.getExternalContext().getRequestMap();
argumentHandler.exposeFlowExecutionContext(flowExecutionKey, holder.getFlowExecution(), requestMap);
}
We can see that this method updates the ViewRoot in the FacesContext. And this completes the preparation because a ViewHandler will get the ViewRoot from FacesContext and render it.Some key Classes
PhaseIdpackage javax.faces.event;
import java.util.*;
public class PhaseId
implements Comparable
{
......
public static final List VALUES;
static
{
ANY_PHASE = new PhaseId("ANY");
RESTORE_VIEW = new PhaseId("RESTORE_VIEW");
APPLY_REQUEST_VALUES = new PhaseId("APPLY_REQUEST_VALUES");
PROCESS_VALIDATIONS = new PhaseId("PROCESS_VALIDATIONS");
UPDATE_MODEL_VALUES = new PhaseId("UPDATE_MODEL_VALUES");
INVOKE_APPLICATION = new PhaseId("INVOKE_APPLICATION");
RENDER_RESPONSE = new PhaseId("RENDER_RESPONSE");
values = (new PhaseId[] { ANY_PHASE, RESTORE_VIEW, APPLY_REQUEST_VALUES, PROCESS_VALIDATIONS, UPDATE_MODEL_VALUES, INVOKE_APPLICATION, RENDER_RESPONSE
});
VALUES = Collections.unmodifiableList(Arrays.asList(values));
}
}
FlowPhaseListener
The Spring class FlowPhaseListener is a key in integrating Spring web flow into JSF. The following are some important methods of the class.
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
public void beforePhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
if (event.getPhaseId() == PhaseId.RESTORE_VIEW) {
ExternalContextHolder.setExternalContext(new JsfExternalContext(context));
restoreFlowExecution(event.getFacesContext());
// we do not need to worry about clean up here since other phases will continue to run even if an exception
// occurs in restoreFlowExecution(FacesContext)
} else if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
if (FlowExecutionHolderUtils.isFlowExecutionRestored(event.getFacesContext())) {
try {
prepareResponse(getCurrentContext(), FlowExecutionHolderUtils.getFlowExecutionHolder(context));
} catch (RuntimeException e) {
// we must cleanup here since this is the render response phase and the after phase callback will
// NOT run when an exception occurs (which typically does the cleanup--see below)
cleanupResources(context);
throw e;
} catch (Error e) {
cleanupResources(context);
throw e;
}
}
}
}
public void afterPhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
if (FlowExecutionHolderUtils.isFlowExecutionRestored(context)) {
try {
saveFlowExecution(getCurrentContext(), FlowExecutionHolderUtils.getFlowExecutionHolder(context));
} finally {
// always cleanup after save - we are done with flow execution request processing
cleanupResources(context);
}
}
} else {
// cleanup if some other JSF artifact marked 'response complete' to short-circuit the lifecycle early
if (context.getResponseComplete()) {
cleanupResources(context);
}
}
}
protected void restoreFlowExecution(FacesContext facesContext) {
JsfExternalContext context = new JsfExternalContext(facesContext);
if (argumentHandler.isFlowExecutionKeyPresent(context)) {
// restore flow execution from repository so it will be available to JSF artifacts
// (this could happen as part of a flow execution redirect or browser refresh)
FlowExecutionRepository repository = getRepository(context);
FlowExecutionKey flowExecutionKey = repository.parseFlowExecutionKey(argumentHandler
.extractFlowExecutionKey(context));
try {
FlowExecutionLock lock = repository.getLock(flowExecutionKey);
lock.lock();
try {
FlowExecution flowExecution = repository.getFlowExecution(flowExecutionKey);
if (logger.isDebugEnabled()) {
logger.debug("Loaded existing flow execution with key '" + flowExecutionKey
+ "' due to browser access "
+ "[either via a flow execution redirect or direct browser refresh]");
}
FlowExecutionHolderUtils.setFlowExecutionHolder(new FlowExecutionHolder(flowExecutionKey,
flowExecution, lock), facesContext);
} catch (RuntimeException e) {
lock.unlock();
throw e;
} catch (Error e) {
lock.unlock();
throw e;
}
} catch (FlowExecutionAccessException e) {
// thrown if access to the execution could not be granted
handleFlowExecutionAccessException(e, facesContext);
}
} else if (argumentHandler.isFlowIdPresent(context)) {
// launch a new flow execution
// (this could happen as part of direct browser access or a flow definition redirect)
String flowId = argumentHandler.extractFlowId(context);
FlowDefinition flowDefinition = getLocator(context).getFlowDefinition(flowId);
FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition);
FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution);
FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext);
ViewSelection selectedView = flowExecution.start(createInput(context), context);
holder.setViewSelection(selectedView);
if (logger.isDebugEnabled()) {
logger.debug("Launched a new flow execution due to browser access "
+ "[either via a flow redirect or direct browser URL access]");
}
}
}
/**
* Prepare the JSF view for rendering.
* @param facesContext the faces context
* @param holder the holder of the current flow execution
*/
protected void prepareApplicationView(FacesContext facesContext, FlowExecutionHolder holder) {
ApplicationView view = (ApplicationView) holder.getViewSelection();
if (view != null) {
// expose the view's "model map" in the request map
putInto(facesContext.getExternalContext().getRequestMap(), view.getModel());
// update the root component if necessary
updateViewRoot(facesContext, viewIdMapper.mapViewId(view.getViewName()));
}
String flowExecutionKey = holder.getFlowExecution().isActive() ? holder.getFlowExecutionKey().toString() : null;
if (flowExecutionKey != null) {
saveInViewRoot(facesContext, flowExecutionKey);
}
Map requestMap = facesContext.getExternalContext().getRequestMap();
argumentHandler.exposeFlowExecutionContext(flowExecutionKey, holder.getFlowExecution(), requestMap);
}
private void updateViewRoot(FacesContext facesContext, String viewId) {
UIViewRoot viewRoot = facesContext.getViewRoot();
if (viewRoot == null || hasViewChanged(viewRoot, viewId)) {
// create the specified view so that it can be rendered
ViewHandler handler = facesContext.getApplication().getViewHandler();
UIViewRoot view = handler.createView(facesContext, viewId);
facesContext.setViewRoot(view);
}
}
So the beforePhase only reacts to the RESTORE_VIEW and RENDER_RESPONSE phases.
FlowNavigationHandler
The class FlowNavigationHandler is another key in integration of JSF and Spring web flow:
public class FlowNavigationHandler extends DecoratingNavigationHandler {
......
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome,
NavigationHandler originalNavigationHandler) {
try {
JsfExternalContext context = getCurrentContext();
// record the navigation handler context
context.handleNavigationCalled(fromAction, outcome);
// first see if we need to launch a new flow execution if the flow id is present
if (argumentExtractor.isFlowIdPresent(context)) {
// a flow execution launch has been requested - create the new execution
String flowId = argumentExtractor.extractFlowId(context);
FlowDefinition flowDefinition = getLocator(context).getFlowDefinition(flowId);
FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition);
// check to see if this execution was created while another was running
if (FlowExecutionHolderUtils.isFlowExecutionRestored(facesContext)) {
// replace the current flow execution with the new one
FlowExecutionHolderUtils.getFlowExecutionHolder(facesContext).replaceWith(flowExecution);
} else {
// bind the new execution as the 'current execution'
FlowExecutionHolderUtils.setFlowExecutionHolder(new FlowExecutionHolder(flowExecution),
facesContext);
}
// start the new execution
ViewSelection selectedView = flowExecution.start(createInput(context), context);
// set the starting view to render
FlowExecutionHolderUtils.getFlowExecutionHolder(facesContext).setViewSelection(selectedView);
} else {
// not a launch request - see if this is a resume request to continue an existing execution
if (FlowExecutionHolderUtils.isFlowExecutionRestored(facesContext)) {
// a flow execution has been restored - see if we need to signal an event against it
if (argumentExtractor.isEventIdPresent(context)) {
// signal the event against the current flow execution
String eventId = argumentExtractor.extractEventId(context);
try {
FlowExecutionHolder holder = FlowExecutionHolderUtils.getFlowExecutionHolder(facesContext);
ViewSelection selectedView = holder.getFlowExecution().signalEvent(eventId, context);
// set the next view to render
holder.setViewSelection(selectedView);
} catch (NoMatchingTransitionException e) {
if (logger.isDebugEnabled()) {
logger.debug("No flow state transition found for event '" + eventId
+ "'; falling back to standard navigation handler.");
}
// not a valid event in the current state: proceed with standard navigation
originalNavigationHandler.handleNavigation(facesContext, fromAction, outcome);
}
}
} else {
// neither a flow launch or resume request: proceed with standard navigation
originalNavigationHandler.handleNavigation(facesContext, fromAction, outcome);
}
}
} catch (RuntimeException e) {
cleanupResources(facesContext);
throw e;
} catch (Error e) {
cleanupResources(facesContext);
throw e;
}
}
D2dViewHandler
package com.icesoft.faces.application;
...
public class D2DViewHandler extends ViewHandler {
......
/**
* Create a new ViewRoot
*
* @param context FacesContext
* @param viewId ViewId identifying the root
* @return A new viewRoot
*/
public UIViewRoot createView(FacesContext context, String viewId) {
initializeParameters(context);
if (delegateView(viewId)) {
return delegate.createView(context, viewId);
}
// #2139 Remove this token here as well
ExternalContext externalContext = context.getExternalContext();
externalContext.getRequestParameterMap().remove(
PersistentFacesCommonlet.SEAM_LIFECYCLE_SHORTCUT);
UIViewRoot root = new NamespacingViewRoot(context);
// UIViewRoot root = new UIViewRoot();
root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
Map contextServletTable =
getContextServletTable(context);
if (null == viewId) {
root.setViewId("default");
context.setViewRoot(root);
contextServletTable
.put(DOMResponseWriter.RESPONSE_VIEWROOT, root);
Locale locale = calculateLocale(context);
root.setLocale(locale);
return root;
}
root.setViewId(viewId);
context.setViewRoot(root);
contextServletTable.put(DOMResponseWriter.RESPONSE_VIEWROOT, root);
return root;
}
}
Tuesday, August 10, 2010
Some Typical Hibernate Operations
First some concepts of Hibernate. Hibernate defines the following object states:
1. To Load and Update an Object from Database
2. To Update an Object Whose Hibernate State may be Transient or Detached
3. To Load and Initialize an Object
- Transient - an object is transient if it is just created but has no primary key (identifier) and not associated with session.
- Persistent - The object is in persistent state if session is open, and you just saved the instance in the database or retrieved the instance from the database.
- Detached - a detached instance is an object that has been persistent, but its Session has been closed. A detached instance can be reattached to a new Session at a later point in time, making it persistent again. After detached state, object comes to persistent state if you call lock() or update() method.
1. To Load and Update an Object from Database
HibernateTemplate hTemplate = getHibernateTemplate(); MyDto dto = (MyDto)hTemplate.load(MyDto.class, myObjectId); dto.setUpdatedBy(user.getUsername()); dto.setUpdatedDt(new Date()); dto.setStatus(status); hTemplate.flush();
2. To Update an Object Whose Hibernate State may be Transient or Detached
HibernateTemplate hTemplate = getHibernateTemplate(); // myDto is an input. It may be in transient or detached state MyDto persistentDto = (MyDto) hTemplate.merge(myDto); hTemplate.update(persistentDto);Note: The merge method returns an object in the persistent state
3. To Load and Initialize an Object
MyDto dto = (MyDto) getHibernateTemplate().load(MyDto.class,myObjectId); Hibernate.initialize(dto);Or use the following that does not need initialization:
Mydto dto= (MyDto) getHibernateTemplate().get(MyDto.class, myObjectId);
References
- Hibernate Reference Documentation Version 3.2.2
Thursday, August 5, 2010
How to Use Weblogic WLST to Redeploy An Application, etc.
How to redeploy an application
Suppose the application is an ear file and it has already been deployed. Now you have made some changes and generated a new ear file. To redeploy the ear, you can do the following steps:- Overwrite the old ear with the new ear file.
- Open wlst and perform the redeployment.
Suppose the name of the application is "myApp". The following is some trace of the wlst redeployment:
C:\bea921\weblogic92\common\bin>wlst
......
Initializing WebLogic Scripting Tool (WLST) ...
Welcome to WebLogic Server Administration Scripting Shell
Type help() for help on available commands
wls:/offline> connect("userFoo","password","t3://localhost:7001");
Connecting to t3://localhost:7001 with userid userFoo
wls:/mydomain/serverConfig>
wls:/mydomain/serverConfig> progress=redeploy('myApp');
Redeploying application myApp ...
<Aug 5, 2010 11:45:06 AM EDT> <Info> <J2EE Deployment SPI> <BEA-260121> <Initiat
ing redeploy operation for application, ......
>
..................Completed the redeployment of Application with status complete
d
Current Status of your Deployment:
Deployment command type: redeploy
Deployment State : completed
Deployment Message : no message
wls:/mydomain/serverConfig>
Note 1: The WLST redeploy command takes some optional arguments. You can use appPath to specify the name of the archive file or root of the exploded archive directory to be redeployed. My experiment is that if you put the new ear in a different location and use appPath to point to it, you will get the error message "Changing the source location is not allowed for a previously attempted deployment. Try deploying without specifying the source."
Note 2: The redeploy command will create a directory "config" under the current directory. In the example above, it will be C:\bea921\weblogic92\common\bin\config. If you do not have the right to create a sub-directory, you will get an error like "java.io.IOException: [J2EE Deployment SPI:260080]Unable to define application install directory at '/opt/bea/wl9.2.1/weblogic92/common/bin/config/deployments/...'. Directory could not be created or is a file.". If that is the case, you can go to a different directory where you can create a sub-directory and call the WLST command there.
Other WLST Commands for Deployment
You can also use WLST to start/stop/deploy/undeploy an application. The following is the simplified trace of performing these tasks.C:\bea921\weblogic92\common\bin>wlst
wls:/offline> progress=stopApplication('myApp')
You will need to be connected to a running server to execute this command
wls:/offline> connect("userFoo","password","t3://localhost:7001");
Connecting to t3://localhost:7001 with userid userFoo ...
wls:/mydomain/serverConfig> progress=stopApplication('myApp');
..Completed the stop of Application with status completed
wls:/mydomain/serverConfig> progress=startApplication('myApp');
Starting application myApp.
......Completed the start of Application with status completed
wls:/mydomain/serverConfig> stopApplication('myApp');
Stopping application myApp.
.Completed the stop of Application with status completed
wls:/mydomain/serverConfig> undeploy('myApp', timeout=60000);
Undeploying application myApp ...
.Completed the undeployment of Application with status completed
wls:/mydomain/serverConfig> progress=deploy(appName='myApp',path='C:/home/deploy
/LOC/app/myApp.ear');
.........................Completed the deployment of Application with status completed
wls:/mydomain/serverConfig>
Note that you can undeploy the application without stopping it first.
It seems that when the ear is uploaded to weblogic, undeploy will delete the ear file. But if the ear is at outside location, undeploy will not delete the ear file. However, more investigation is needed for this.
Reference
http://download.oracle.com/docs/cd/E13222_01/wls/docs92/config_scripting/reference.html#wp1024321Sunday, May 30, 2010
Client Request, Thread, and Java EE Container
For each web request, in general, the web container will not create a new servlet instance. Actually only one servelet instance object is needed for each type. But the web container will create a new thread to handle each request. The following link explains well how the web container creates threads and use the servlet instance to serve the new client request.
http://www.datadisk.co.uk/html_docs/jsp/jsp_web_app_architecture.htm
Similarly, for each new remote client request to the EJB container, the container will create a new EJB object ( and a new thread to handle the request, I think). The EJB object will use a bean instance. But the bean instance does not need to be created everytime. EJB container uses instance pooling for the bean classes.
The following is an article that explains java RMI internal details:
http://www.developer.com/java/other/article.php/10936_3455311_1/Understanding-Java-RMI-Internals.htm
http://www.datadisk.co.uk/html_docs/jsp/jsp_web_app_architecture.htm
Similarly, for each new remote client request to the EJB container, the container will create a new EJB object ( and a new thread to handle the request, I think). The EJB object will use a bean instance. But the bean instance does not need to be created everytime. EJB container uses instance pooling for the bean classes.
The following is an article that explains java RMI internal details:
http://www.developer.com/java/other/article.php/10936_3455311_1/Understanding-Java-RMI-Internals.htm
Subscribe to:
Comments (Atom)
