Tuesday, June 25, 2013

JSF 2 tutorial. Part II: Spring integration and Prettyfaces usage

In the first part of this JSF 2 series, we prepared our development environment: Eclipse, Maven, Tomcat 7 and JSF 2. In this second part, we will walk through Spring integration with JSF 2. We will also see how to use the Prettyfaces library. And finally we will use Spring security to secure the access to our application.

1) Spring integration

First we need to add Spring dependencies to our application (POM file):
3.2.3.RELEASE



 javax.inject
 javax.inject
 1



  org.springframework
  spring-core
  ${org.springframework.version}



  org.springframework
  spring-expression
  ${org.springframework.version}

 


  org.springframework
  spring-beans
  ${org.springframework.version}



  org.springframework
  spring-aop
  ${org.springframework.version}

 


  org.springframework
  spring-context
  ${org.springframework.version}

 


  org.springframework
  spring-context-support
  ${org.springframework.version}



  org.springframework
  spring-tx
  ${org.springframework.version}



  org.springframework
  spring-web
  ${org.springframework.version}



 org.springframework
 spring-orm
 ${org.springframework.version}


You can refer to this blog entry from Spring source for detailed explanation about every dependency.
Next step is to create the famous Spring's applicationContext.xml file:


 
 
 
 
 

  
Now we need to register Spring listeners in the web deployment descriptor:




 org.springframework.web.context.ContextLoaderListener



 org.springframework.web.context.request.RequestContextListener

Last config to be done, is registering the SpringBeanFacesELResolver in the faces-config.xml file:

   
   
  
          org.springframework.web.jsf.el.SpringBeanFacesELResolver
  
   

The SpringBeanFacesELResolver is an ELResolver that delegates to the Spring root WebApplicationContext, resolving name references to Spring-defined beans.
Now we can use the Spring framework for dependency injection (DI) in our application. If you are not familiar with DI and Inversion of Control (IoC) please refer to this excellent article.
In the first part of this series, we annotated our user managed bean with:
@ManagedBean(name="userManagedBean")
@SessionScoped
to make JSF aware of it. Now we want Spring to be responsible for missions like this: registering all components and instantiating them when needed. For this, Spring offers multiple class annotations: @Component, @Service, @Repository.
So we change our UserManagedBean to be annotated with:
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("session")
public class UserManagedBean implements Serializable
By now you can start your server and see that the bean is instantiated.

2) JSF2 View scope with Spring

JSF 2 introduced a big enhancement by introducing new scopes to Managed beans. One of the most interesting scopes, is the View scope. In fact, if you used JSF 1.2 without Richfaces (or Seam), then you would have found yourself obliged to define most of your managed beans as Session beans, otherwise you would encounter plenty of problems with Ajax requests sent along with request scoped beans. In fact, with every Ajax call, request bean is re-instantiated again.
With the View scope, your bean will keep alive (this remembers me the a4j:keepAlive in Richfaces) until view is completely refreshed.
The problem here with Spring, is that you can not define a bean with @Scope("view") by default. An excellent workaround was proposed by Cagatay Civici (Founder of Primefaces) here.
The main idea, is to define the View scope our self and make Spring aware of it and add it to its list of scopes.
So first thing to do is to define the ViewScope class that implements the Scope interfaces:

package com.raissi.spring.customscope;

import java.util.Map;

import javax.faces.context.FacesContext;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

/**
 * Implements the JSF View Scope for use by Spring. 
 * This class is registered as a Spring bean with the CustomScopeConfigurer.
*/
public class ViewScope implements Scope {

 public Object get(String name, ObjectFactory objectFactory) {
  if (FacesContext.getCurrentInstance().getViewRoot() != null) {
   Map viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
   if (viewMap.containsKey(name)) {
    return viewMap.get(name);
   } else {
    Object object = objectFactory.getObject();
    viewMap.put(name, object);
    return object;
   }
  } else {
   return null;
  }
 }

 public Object remove(String name) {
  if (FacesContext.getCurrentInstance().getViewRoot() != null) {
   return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
  } else {
   return null;
  }
 }

 public void registerDestructionCallback(String name, Runnable callback) {
  // Do nothing
 }

 public Object resolveContextualObject(String key) {
  return null;
 }

 public String getConversationId() {
  return null;
 }

}
Once done, we need to register our newly defined scope to the Spring CustomScopeConfigurer in applicationContext.xml:


 
  
   
    
   
  
 

And that's all you need to make Spring aware of ViewScope. Notice that possible enhancements to this workaround can be found here for possible memory leaks. Et voilà!!! vous avez Spring et JSF 2 ready to go. Next step is to prettify our URLs.

3) Using Prettyfaces

Prettyfaces is an open source URL-rewriting library used essentially with JSF (1.1, 1.2 and 2). It's a very powerful library to re-write your JSF app URLs with many customization features. It's also very easy to configure and use. First thing, add Prettyfaces dependencies in your POM file: 

 com.ocpsoft
 prettyfaces-jsf2
 3.3.3

Next, configure your web.xml to use the Prettyfaces filter:

  Pretty Filter
  com.ocpsoft.pretty.PrettyFilter
  true


 
  Pretty Filter 
  /* 
  FORWARD 
  REQUEST 
  ERROR
  ASYNC

Last thing is to map your views with Prettyfaces. So under WEB-INF, create a file pretty-config.xml:


 
  
  
 
 
  
  
  #{userManagedBean.logout}
 
 
  
  
 


As you can see in this mapping file, to prettify your URLs you need to create a url-mapping for each JSF view. For example, if we want the URL to login page to be just: http://domainname.ex/login instead of http://domainname.ex/pages/login.jsf then we create a url-mapping, defining "/login" as pattern and /pages/login.jsf as correspondent view. So whenever there is a call to /login URL the "/login/login.jsf" view will be rendered. Now, we want that whenever the "/logout" URL is called by browser, user is a) redirected to login.jsf and b) also user session is destroyed. This can be easily achieved by defining the pattern /logout pointing to login.jsf view and also we define an action element. This action, is just a method of a defined managed bean that will get executed once the specified URL is called. And here is our method:
public void logout(){
     FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
}
Other beautiful features can be found in the Prettyfaces user guide.
In next part of the series, we will add some security to our application

Friday, June 21, 2013

Creating datatable in an Alfresco activiti workflow task form using YUI and Web scripts

As mentioned in the title, in this article we will learn an advanced  Alfresco workflow feature: implementing datatables to display data in a workflow form.
This article assumes you have some knowledge of Alfresco and Activiti workflows. If this is not the case, you can refer to Alfresco docs. Good tutorials can be found here, especially the article concerning advanced workflows (PDF available here) .

1) Modeling the workflow

When developing workflows, first thing after reading and analyzing requirements is to model workflow. So if you already didn't you may install the Activiti BPMN 2.0 Designer plug-in for eclipse (install URL is here).
For the simplicity of this article, we will assume we have a simple workflow containing only one task that displays a datatable filled by dynamic data (remember, this is our main goal).
So here you can find the process definition:

    

    
 
       
    
 
      
        
          
            
    var jsonData ;
    var tableSize = 10;
    for(var i = 0; tableSize >i; i++){     
     if(i != 0){
      jsonData = jsonData+',{ "user": user"'+i+'","data1":data1_"'+i+'","data2":data2_"'+i+'","data3":data3_"'+i+'" }';
     }else{
      jsonData = '[{ "user": user"'+i+'","data1":data1_"'+i+'","data2":data2_"'+i+'","data3":data3_"'+i+'" }';
     }     
    }
    jsonData += ']';
    execution.setVariable('lrwf_userData',jsonData);                
   
          
                
      
    
 
 



 
The important part here is the activiti:taskListener event="create" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener" element. It's in the create event that we prepare data to be displayed in the simpleTask form.
The data that will be sent to the client is in JSON format.
Another point to mention, is that we defined a custom formKey  for our task. The custom type lrwf:simpleTask will be defined in next step.
Now save the above code in "simple-process-datatable.bpmn" under "alfresco/module/custom/workflows/"

2) Workflow content model

Now that the workflow has been defined, next step is to integrate the process with the Alfresco UI by defining the content model and updating the client configuration (Alfresco Share). 
To do that, open the file: Repository/config/alfresco/application-context.xml (this is the starting point of the Spring configs of Alfresco). Then add the following line:
<import resource="classpath*:alfresco/module/custommodel/workflow-context.xml"/>
In workflow-context.xml we declare paths and names of our custom workflow model and process definitions:

 
  
   
    
     activiti
     alfresco/module/custom/workflows/simple-process-datatable.bpmn
     
     text/xml
     true
        
   
  
  
            
                alfresco/module/custom/model/workflowModel.xml
            
        
        
            
                alfresco.module.custom.messages.customWorkflow
            
        
 

In this config file, we define a)workflow definitions: engineId (in Alfresco 4 and above you can use both activiti and jBPM), path to process definition and otherproperties, b)workflow content model, i.e.: types of custom tasks, aspects etc... and c) path to message bundle. Next we define our content model, so in "workflowModel.xml":
 




 
 Custom Workflow Model
 Laâbidi RAISSI
 1.0

 
 
  
  
  
  
     
        
        
        
  
 

 
 
  
 
 
 
  
            bpm:activitiOutcomeTask
            
                
                    d:text
                    DefaultValue
                    
                        
                            
                                
                                    Reject
                                    DefaultValue
                                    Approve
                                
                            
                        
                    
                                
            
            
                
                    edit_package_item_actions
                
                
                    {http://www.telec.com/model/workflow/1.0}customOutcome
                
            
                         
             lrwf:withUserDataTable             
          
        
  
 
 
  
   Data that will be displayed in a datatable
   
    
     d:text
     false
     true
    
   
  
 


If you are using Eclipse than these files should go under Repository project. If not then, root directory is; TOMCAT_HOME/webapps/alfresco/WEB-INF/classes and there create the paths I already mentioned for every file.

3) Client configuration

Now we go to configure the client part (Share: share webapp under Tomcat). If you didn't already, go to "Slingshot project /config/alfresco/slingshot-application-context.xml" and declare path to Share Custom Config file to define forms contents:

classpath:alfresco/web-extension/share-config-custom.xml
jar:*!/META-INF/share-config-custom.xml
In file: "share-config-custom.xml" define forms config, in particular fields to display and their control templates:

 
      
         
You can see here, that we defined a config element, having "task-type" as evaluator and the name of our task as condition. In this element, we define properties that will be displayed to client. Also in the "appearance" sub-element, we attached to every field (property) its label, the "set" where it will be displayed and the "control" that will be responsible for its rendering. The control that matters for us is the control part (we declared one named "userDataTable.ftl" to render our datatable. This is a Freemarker template.
So in Slingshot project, under the defined path, create a file named "userDataTable.ftl":
<#assign controlId = fieldHtmlId + "-cntrl">



Here we just defined html elements that will contain the datatable, and we call a new Javascript function. We will use the YUI library (included by defualt in Alfresco and used to render almost everything in Share). So create a Javascript file "userDataTable.js" to handle data and build datatable. You can download the file from here.

Finally, in "Web Framework Commons/config/alfresco/site-webscripts/org/alfresco/components/form/form.get.head.ftl"
include "userDataTable.js" and eventually "userDataTable.css" if you don't like the default looking of YUI:
<@link rel="stylesheet" type="text/css" href="${page.url.context}/res/components/form/userDataTable.css" />
<@script type="text/javascript" src="${page.url.context}/res/components/form/userDataTable.js">
Et voilà!!! You have a pretty datatable to display info in your workflow tasks.