Showing posts with label Prettyfaces. Show all posts
Showing posts with label Prettyfaces. Show all posts

Thursday, July 18, 2013

JSF 2, Spring, Spring Security, Hibernate/JPA, Jasypt, application sample available at Github

If you have followed my previous articles series about JSF 2 and different frameworks integration, then you can find the complete application source code on Github here.
Please feel free to post your comments, suggestions and ameliorations

Wednesday, July 10, 2013

JSF 2, display PDF files stored at Amazon S3 for cloud storage

Lately I have been working on a personal project using JSF2. In that project I had to display PDF files to users on demand. I wanted to deploy the project in the cloud and I chose CloudBees. The only problem I encountered with CloudBees is that you cannot upload files via your application to their servers.
The solution was to store my files in an external storage provider. I chose Amazon Simple Storage Service (S3).
Now here is the challenge: I have my files stored at Amazon servers, and I need to remotely access these files and display them back to users via Google Docs Viewer. You may say that I could generate direct URL to these files (as discussed here) and then simply display them. But, what if you don't want them to be publicly accessible, or if you want to make them accessible under some conditions ?
In this article we will see how to do this. But first let's begin with uploading files to Amazon S3 via our JSF2 application.

1) The JetS3t Toolkit

The JetS3t is an open source application suite for Amazon S3, Amazon CloudFront content delivery network and Google storage for developers. It provides a very simple  API for interacting with these storage services. Here is POM dependencies for JetS3t:




 net.java.dev.jets3t
 jets3t
 0.9.0

2) Uploading files in JSF2

To upload files, we will use Primefaces uploader. To use it, you should define PrimeFaces FileUpload Filter in your web.xml descriptor:



 PrimeFaces FileUpload Filter
 org.primefaces.webapp.filter.FileUploadFilter


 PrimeFaces FileUpload Filter
 Faces Servlet
 
 FORWARD

Please notice that we set dispatcher to FORWARD. This because we are using Prettyfaces (it's filter dispatcher is also set to FORWARD). Without doing this, you may encounter some problems.
You also must add dependencies of two additional APIs: commons-io and commons-fileupload. It's not mentioned in Primefaces docs, but without doing this, you will get many ClassNotFoundExceptions related to theses libraries:



 commons-fileupload
 commons-fileupload
 1.3


 commons-io
 commons-io
 2.4

Now everything is set alright. Let's begin with implementation. The JSF upload part is very simple. You simply define a managed bean containing an UploadedFile property and two methods:
package com.raissi.managedbeans;
import java.io.IOException;
import java.io.Serializable;
import javax.faces.event.ActionEvent;
import javax.inject.Inject;
import javax.inject.Named;

import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.raissi.domain.Resume;
import com.raissi.service.ResumeService;

@Component
@Scope("view")
public class HomeManagedBean implements Serializable{
 private static final long serialVersionUID = 5426154702541976181L;
 @Inject
 private ResumeService resumeService;
        private Resume resume = new Resume();
 private UploadedFile file;

 public UploadedFile getFile() {
          return file;
 }

  public void setFile(UploadedFile file) {
          this.file = file;
 }
    
 public void fileUploadListener(FileUploadEvent event){
       file = event.getFile();
 }
        public void upload() {
          if(file != null) {
      try {
       resume.setDocumentName(file.getFileName());
    resumeService.persistCvContent(file.getInputstream(), resume);
    loggedInUser.getUser().setResume(resume);
    FacesMessage msg = new FacesMessage("Succesful", file.getFileName() + " is uploaded.");
             FacesContext.getCurrentInstance().addMessage(null, msg);
  } catch (IOException e) {
    FacesMessage msg = new FacesMessage("File ", file.getFileName() + " couldn't be uploaded. Please contact admins");
             FacesContext.getCurrentInstance().addMessage(null, msg);
    e.printStackTrace();
  }
          }
        }
}
And here is our xhtml code:

      
      
     
      
       
       
       
       
       
        
                
                
                
                
      
     
    
     
The Resume class referenced in the managed bean, is just a domain class that we defined in last article. It will contain data related to user's resume and will be persisted in DB. The key class here is the ResumeService bean which is responsible for Resume persistence in both DB via ResumeDao and in remote Amazon S3 store. Before we go through document persistence in Amazon S3, please make sure you got your your Amazon S3 credentials: AWSAccessKeyId (access key ID) and AWSSecretKey (Secret Key) since we will use them to store/retrieve data from S3.

3) Persisting files to Amazon S3 

Now we go to examine the ResumeService class:

package com.raissi.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.security.NoSuchAlgorithmException;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.io.IOUtils;
import org.jets3t.service.S3Service;
import org.jets3t.service.S3ServiceException;
import org.jets3t.service.ServiceException;
import org.jets3t.service.impl.rest.httpclient.RestS3Service;
import org.jets3t.service.model.S3Bucket;
import org.jets3t.service.model.S3Object;
import org.jets3t.service.security.AWSCredentials;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;

import com.raissi.dao.ResumeDao;
import com.raissi.domain.Resume;
import com.raissi.service.ResumeService;

@Named("resumeService")
@Transactional
public class ResumeServiceImpl implements ResumeService, Serializable{
 private static final long serialVersionUID = 1L;
 
 @Inject
 private ResumeDao resumeDao;
 @Value("${s3.accessKeyId}")
 private String amazonAccessKeyId;
 
 @Value("${s3.secretKey}")
 private String amazonSecretKey;
 
 @Value("${s3.bucketName}")
 private String bucketName;
 
 private S3Service s3Service;
 // To store data in S3 you must first create a bucket, a container for objects.
 private S3Bucket bucket;
 @PostConstruct
 public void init(){
  try {
   //amazon S3 storage credentials:
   AWSCredentials awsCredentials = 
       new AWSCredentials(amazonAccessKeyId, amazonSecretKey);
   //To communicate with S3, create a class that implements an S3Service. 
   //We will use the REST/HTTP implementation based on HttpClient, 
   //as this is the most robust implementation provided with JetS3t.
   s3Service = new RestS3Service(awsCredentials);
   
   bucket = s3Service.getBucket(bucketName);
   if(bucket == null){
    bucket = s3Service.createBucket(bucketName);
   }
  } catch (S3ServiceException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

        @Override
 public void persistCvContent(InputStream content, Resume resume) {
  long currentTime = System.currentTimeMillis();
  String extension = resume.getDocumentName().substring(resume.getDocumentName().lastIndexOf("."));
  String fileName = currentTime+extension;
  
  try {
   byte[] contentArray = IOUtils.toByteArray(content);
   S3Object cvS3Object = new S3Object(fileName, contentArray);
   cvS3Object.setContentLength(contentArray.length);
   cvS3Object.setContentType("application/pdf");
   s3Service.putObject(bucket, cvS3Object);
   resume.setContentUrl(bucketName+"/"+fileName);
  } catch (S3ServiceException e1) {
   // TODO Auto-generated catch block
   e1.printStackTrace();
  } catch (NoSuchAlgorithmException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}
A bucket in Amazon S3 is a file container in which every uploaded file will be stored. A bucket name must be unique through all S3 users. Buckets cannot be nested. And, each bucket can contain an unlimited number of files. I decided bucket name to be configurable with accessKeyId and secretKey. That's why I am injecting their values in my ResumeService, so application admin can chose whatever he wants. (Remember the property-placeholder defined in Spring application context in last article, well these String values should be defined in the file referenced by the properties holder).
Also notice that we are saving the distant file name and the bucket name in a persistent entity "Resume". In this way, we are able to use different bucket names in the future.

4) Displaying PDF files in JSF2

To display PDF files, we can simply use 


From Primefaces. But this is for static and public files (accessible by simple URLs). For dynamic files which need some server logic before rendering we can use a custom servlet with the above primefaces component.
Use case: Assume we have a datatable displaying a list of users (User object from last article). For each row (User) we will have a button to view CV. When clicked, the button opens a popup dialog displaying the PDF file. So here is the XHTML code:



     
                 Registered users
                 
               
           
   
           
               
            
           
           
            
               
           
                  
               
              
                        
               
        
       
              
             
As you can see, the command button used to display the dialog has an action listener to call a server side method: generateUserCV(User user); the method code is:
public void generateUserCV(User user){
  setSelectedUser(user);
  if(user != null){
   InputStream file = resumeService.getCvByUser(user.getUserId());
   if(file != null){
    try {
     byte[] bytes = IOUtils.toByteArray(file);
     Map session = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
     //We are using userId when storing cv content to be possible to display multiple files 
     session.put(ResumeService.ATTR_RESUME+user.getUserId(), bytes);
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    
   }
  }
}
As for the generateUserCV method, it calls the resumeService to get user's CV file as java.io.InputStream. Then, we use org.apache.commons.io.IOUtils#toByteArray(InputStream input) to convert file content a byte array. After that we save the array in the session map under a constant String (ResumeService.ATTR_RESUME) of our choice concatenated with the user id. This will be used by the mentioned custom servlet later. Now here is the code of resumeService.getCvByUser:
public InputStream getCvByUser(Long userId){
  Resume resume = resumeDao.getResumeByUser(userId);
  String fullS3Name = resume.getContentUrl();
                //Remember, we set the bucket name and file name in the contentUrl property of Resume
  String bucketNameForResume = fullS3Name.substring(0, fullS3Name.indexOf("/"));
  String fileName = fullS3Name.substring(fullS3Name.indexOf("/")+1);
  try {

   S3Object cvS3Object = s3Service.getObject(bucketNameForResume, fileName);
   if(cvS3Object != null){
    InputStream stream = cvS3Object.getDataInputStream();    
    return stream;
   }
  } catch (S3ServiceException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (ServiceException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  return null;
 }
In the dialog to be shown, you may have noticed that we are using p:media with a stream value: /file/cv?id=#{adminHomeManagedBean.selectedUser.userId}. Well, here we are referring to a GET call to a servlet with a param named "id". And here is the servlet implementation:
package com.raissi.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.raissi.service.ResumeService;

@WebServlet("/file/cv")
public class ResumeServlet  extends HttpServlet {
 private static final long serialVersionUID = -221600603615879137L;

 @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  String userId = request.getParameter("id");
  if(userId != null && !userId.equals("")){
         byte[] content = (byte[]) request.getSession().getAttribute(ResumeService.ATTR_RESUME+userId);
            if(content != null){
          response.setContentType("application/pdf");
          response.setContentLength(content.length);
          response.getOutputStream().write(content);
            }
  }
    }

}
The code is very obvious. At first, we fetch the user id (user of whom we are displaying CV) from request parameters. After that, we access the HTTPSession to retrieve the stored content and we set it into response output. Finally, we need to clean our session when dialog is closed. Otherwise, session will be encumbered with content. That's why I added a listener to be called on dialog close event: . And here is the listener code:
public void removeUserCVFromSession(CloseEvent event){
  if(selectedUser != null){
   Map session = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
   session.remove(ResumeService.ATTR_RESUME+selectedUser.getUserId());
  }
 }
And that's all you need.

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