Tuesday 14 April 2015

JSF Composite component with backing component, JavaScript, ajax, PrimeFaces and component handler

Today, I'm going to show how to create composite component with backing component and how to add event listener triggered within composite component. There is a lot of tutorials showing how to create composite component, but there is a lack of how to add event handler even in books. I've decided to write about it. By the way I show how to implement image editor based on Creative Cloud. I hope it will be useful. The goal: create composite component which enable to edit images with Creative Cloud Web SDK. The first step is registering on Adobe in order to create application and obtain appKey. The second step is to create composite component.

As always code comes from my project. In this case it is simple image editor integrated with image chooser, but you can implement your own soulution.

Now, I only show soruce code, but later I'm going to explain in details.

Composite component

simpleImage.xhtml


 Simple image editor



 
  
  
  
  
  
  
  
  
  
  
  
 
 
 
  
 
Backing component for composite component SimpleImageEditorComponent.java
/**
 * SimpleImageEditorComponent.java
 */
package aik.cms.component.composite;

import java.util.Map;

import javax.el.ELContext;
import javax.el.MethodExpression;
import javax.el.ValueExpression;

import javax.faces.component.FacesComponent;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIInput;
import javax.faces.component.UINamingContainer;

import javax.faces.context.FacesContext;

import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;

import org.apache.log4j.Logger;

import aik.cms.component.event.image.ImageChangedEvent;
import aik.cms.image.Image;


@FacesComponent("simpleImage")
public class SimpleImageEditorComponent extends UIInput implements NamingContainer
{
 /**
  * Logger object
  */
 static Logger logger = Logger.getLogger(SimpleImageEditorComponent.class);
 /**
  * 
  */
 protected Image selectedImage;
 enum PropertyKeys
 {
  imageSelectionVisibility,
  image,
  value
 }
 /**
  * Default constructor
  */
 public SimpleImageEditorComponent()
 {
  super();
  logger.debug("SimpleImageEditorComponent has been created");
  setImageSelectionVisibility(false);
 }
 
 /* (non-Javadoc)
  * @see javax.faces.component.UIInput#getFamily()
  */
 public String getFamily() 
 {
  return UINamingContainer.COMPONENT_FAMILY;
 }
 /* (non-Javadoc)
  * @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
  */
 @Override
 public void decode(FacesContext facesContext)
 {
  super.decode(facesContext);
  
  logger.debug("value: " + getAttributes().get("value"));
  
  Map params      = facesContext.getExternalContext().getRequestParameterMap();
  String      event       = params.get("aikEvent");
  
  if(event != null && event.compareTo("1") == 0) // image changed event
  {
   String imageURL = params.get("aikImageURL");
   
   if(imageURL != null)
   {
    //TODO: Create image from given URL
    Image image = getImage();
    
    if(image == null) // Image not selected from aikcms images browser. Get and set image URL
    {
     image = new Image();
    }
    
    image.setImageURL(imageURL);
    this.queueEvent(new ImageChangedEvent(this, image));
   }
   
   logger.debug("imageChangedEvent called");
  } 
 }

 /* (non-Javadoc)
  * @see javax.faces.component.UIComponentBase#broadcast(javax.faces.event.FacesEvent)
  */
 @Override
 public void broadcast(FacesEvent event) throws AbortProcessingException
 {
  super.broadcast(event);
  
  FacesContext   facesContext = FacesContext.getCurrentInstance();
  MethodExpression me     = null;
  
  if(event instanceof ImageChangedEvent) 
  {
   me = (MethodExpression) this.getAttributes().get("imageChangedListener");
  } 
  if(me != null) 
  {
   me.invoke(facesContext.getELContext(), new Object[] {event});
  }
 }
 
 /**
  * 
  */
 public String setImageChooserVisible()
 {
  setImageSelectionVisibility(true);
  return null;
 }
 /**
  * 
  */
 public void setImageChooserInvisible()
 {
  setImageSelectionVisibility(false);
  //return null;
 }
 /**
  * @param visibility
  */
 public void setImageSelectionVisibility(boolean visibility)
 {
  getStateHelper().put(PropertyKeys.imageSelectionVisibility, visibility);
 }
 /**
  * @return
  */
 public boolean getImageSelectionVisibility()
 {
  return (boolean)getStateHelper().eval(PropertyKeys.imageSelectionVisibility, null);
 }
 /**
  * @return the selectedImage
  */
 public Image getSelectedImage()
 {
  
  return selectedImage;
 }
 /**
  * @param selectedImage the selectedImage to set
  */
 public void setSelectedImage(Image selectedImage)
 {
  this.selectedImage = selectedImage;
  setImage(selectedImage);
 }
 /**
  * @param image
  */
 public void setImage(Image image)
 {
  getStateHelper().put(PropertyKeys.image, image);
 }
 /**
  * @return
  */
 public Image getImage()
 {
  return (Image)getStateHelper().eval(PropertyKeys.image, null);
 }
 /**
  * @return
  */
 public String applyImage()
 {
  //TODO: Insert cms domain
  getAttributes().put("value", "http://93.105.125.207:8080/" + getImage().getCmsPath());
  setImageSelectionVisibility(false);
  
  FacesContext    facesContext    = FacesContext.getCurrentInstance();
  ELContext       elContext       = facesContext.getELContext();
  ValueExpression valueExpression = facesContext.getApplication().getExpressionFactory().createValueExpression(elContext, "#{cc.attrs.value}", String.class);
  
  if(selectedImage == null)
  {
   logger.error("selectedImage is null");
   return null;
  }
  
  valueExpression.setValue(elContext, selectedImage.getCmsPath());
  
  return null;
 }
 public void saveImage()
 {
  
 }
} 
Component handler for composite component
/**
 * ImageHandler.java
 */
package aik.cms.component.event.image;

import javax.faces.view.facelets.ComponentConfig;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.MetaRuleset;

import org.apache.log4j.Logger;

import com.sun.faces.facelets.tag.MethodRule;


public class ImageHandler extends ComponentHandler
{
	/**
	 * Logger object
	 */
	static Logger logger = Logger.getLogger(ImageHandler.class);

	/**
	 * @param config
	 */
	public ImageHandler(ComponentConfig config)
	{
		super(config);
		// TODO Auto-generated constructor stub
		logger.debug("ImageHandler has been created");
	}

	/* (non-Javadoc)
	 * @see javax.faces.view.facelets.DelegatingMetaTagHandler#createMetaRuleset(java.lang.Class)
	 */
	@Override
	protected MetaRuleset createMetaRuleset(Class type)
	{
		// TODO Auto-generated method stub
		MetaRuleset metaRuleset 	   	   = super.createMetaRuleset(type);
		Class[] 	changeEventClasses 	   = new Class[]{ ImageChangedEvent.class };
		
		metaRuleset.addRule(new MethodRule("imageChangedListener", Void.class, changeEventClasses));
		
		return metaRuleset;
	}
}
Event handler
/**
 * ImageChangedEvent.java
 */
package aik.cms.component.event.image;

import javax.faces.component.UIComponent;

import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;

import org.apache.log4j.Logger;

import aik.cms.image.Image;


public class ImageChangedEvent extends FacesEvent
{
	/**
	 * Serial version UID
	 */
	private static final long serialVersionUID = -4854070727246981410L;
	/**
	 * Logger object
	 */
	static Logger logger = Logger.getLogger(ImageChangedEvent.class);
	/**
	 * Image :)
	 */
	protected Image image;
	/**
	 * @param component
	 */
	public ImageChangedEvent(UIComponent component, Image image)
	{
		super(component);
		this.image = image;
		logger.debug("ImageChangedEvent called");
		// TODO Auto-generated constructor stub
	}
	/* (non-Javadoc)
	 * @see javax.faces.event.FacesEvent#isAppropriateListener(javax.faces.event.FacesListener)
	 */
	@Override
	public boolean isAppropriateListener(FacesListener arg0)
	{
		// TODO Auto-generated method stub
		return false;
	}
	/* (non-Javadoc)
	 * @see javax.faces.event.FacesEvent#processListener(javax.faces.event.FacesListener)
	 */
	@Override
	public void processListener(FacesListener arg0)
	{
		// TODO Auto-generated method stub
		
	}
	/**
	 * @return the image
	 */
	public Image getImage()
	{
		return image;
	}
	/**
	 * @param image the image to set
	 */
	public void setImage(Image image)
	{
		this.image = image;
	}

}

aik.taglib.xml
   
       simpleImage
       
           aik.cms.component.composite.SimpleImageEditorComponent
           aik.cms.component.composite.SimpleImageEditorComponent
           aik.cms.component.event.image.ImageHandler
       
   

Usage. Method from managed bean
	public void imageChangedListener(ImageChangedEvent event)
	{
		logger.debug("ImageChangedEvent called. URL of saved image: " + event.getImage().getImageURL());
	}

			

Thursday 12 February 2015

PrimeFaces theme converter

I've created PrimeFaces theme converter library as an aik-theme-converter.jar.

Dependencies:

- org.apache.commons.io.FileUtils;
- org.apache.log4j.Logger;

Usage. Create custom theme on JQuery web page. Download theme to local folder, for instance: c:\test. Create ThemeConverter object and call convert(...) method.  This method creates PrimeFaces theme jar in given folder.
 ThemeConverter converter = new ThemeConverter();
  
  try
  {
   converter.convert("c:\test\jquery-ui-1.11.2.custom.zip", "theme_name", "c:\test");
  }
  catch (IOException e)
  {
   logger.error(e.getMessage());
  }
theme_name.jar will be created in test directory.