Monday, May 21, 2012

ADF 11gR2 custom facelet component for image/flash preview

I have created a custom faces component for previewing the uploaded images/flash files. It is a facelet component and the implementation is very crude but i hope that the product team can come up with something better. As other frameworks like richfaces/primefaces do have this kind of component. The component does not have any renderer  registered with it. The entire thing is being handled in the component class.

The logic on which this component is based upon is to render markup based upon content type of the file. It then generates a URL to which a servlet is registered to write the data back to the stream. There are a couple of things to note 1) The values are being stored in session so for large files this will be a issue, a better implementation can store the file in temporary location and then retrieve it, The other thing is it only handles images and flash file preview but this can be extended for text,js or pdf preview.

The component class below extends UIXOutput so as to provide partialTrigger attribute for partial update of component tree.

package com.blogspot.ramannanda.components;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.myfaces.trinidad.component.UIXOutput;


/**
* @author Ramandeep Nanda
*/
public class RichPreviewOutput extends UIXOutput{
public RichPreviewOutput() {
super();
this.setRendererType(null);
}


@Override
public void encodeEnd(FacesContext facesContext) throws IOException {
super.encodeEnd(facesContext);
String elementType=(String)this.getAttributes().get("elementType");
byte[] data= (byte []) this.getAttributes().get("data");
if(elementType!=null){
if(elementType.contains("image")){
encodeImage(facesContext);
}
else
if(elementType.contains("application/x-shockwave-flash")){
encodeFlash(facesContext);
}
}

}


private void encodeImage(FacesContext facesContext) throws IOException {
ResponseWriter writer= facesContext.getResponseWriter();
writer.startElement("img", this);
String fileName=(String)getAttributes().get("filename");
String uri= generateResourceUrl(fileName);
String uriAttr=(String)this.getAttributes().get("uriAttr");
String contentType=(String)this.getAttributes().get("elementType");
writer.writeAttribute(uriAttr, uri, uriAttr);
String style=(String)getAttributes().get("style");
if(style==null){
writer.writeAttribute("style","width=100px height=100px","style");
}
else{
writer.writeAttribute("style", style, "style");
}
byte [] data=(byte[])getAttributes().get("data");
Map map=new HashMap();
map.put("data",data);
map.put("contenttype",contentType);
HttpSession session=(HttpSession) facesContext.getCurrentInstance().getExternalContext().getSession(false);
session.setAttribute(fileName, map);
writer.endElement("img");


}

private String generateResourceUrl(String fileName) {
HttpServletRequest request=(HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
String ctxtPath=FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath().toString();
String resPath=FacesContext.getCurrentInstance().getExternalContext().getInitParameter("ServletPath");
ctxtPath=request.getRequestURL().toString();
ctxtPath=ctxtPath.substring(0,ctxtPath.lastIndexOf("/faces"));
ctxtPath=ctxtPath+resPath+"?fileName="+fileName;

URI uri=null;
String returnURI=null;
try {
uri=new URI(ctxtPath);
} catch (URISyntaxException e) {
e.printStackTrace();
}
try {
System.out.println(uri.toString());
returnURI=uri.toURL().toString();

} catch (MalformedURLException e) {
System.out.println(returnURI);
e.printStackTrace();
}
return returnURI;
}


private void encodeFlash(FacesContext facesContext) throws IOException{
ResponseWriter writer= facesContext.getResponseWriter();
writer.startElement("object", this);
String fileName=(String)getAttributes().get("filename");
String uri= generateResourceUrl(fileName);
String uriAttr=(String)this.getAttributes().get("uriAttr");
String contentType=(String)this.getAttributes().get("elementType");
writer.writeAttribute(uriAttr, uri, uriAttr);
writer.writeAttribute("type",contentType , "elementType");
String style=(String)getAttributes().get("style");
if(style==null){
writer.writeAttribute("style","width=100px height=100px","style");
}
else{
writer.writeAttribute("style", style, "style");
}
writer.writeAttribute(uriAttr, uri, uriAttr);
writer.startElement("param", this);
writer.writeAttribute("name", "flash", "param name");
writer.writeAttribute("value",uri, "uri");
writer.endElement("param");
writer.startElement("a",this);
writer.writeAttribute("href","http://www.adobe.com/go/getflash", "href");
writer.writeText("Get Flash", null);
writer.endElement("a");
writer.endElement("object");
byte [] data=(byte[])getAttributes().get("data");
Map map=new HashMap();
map.put("data",data);
map.put("contenttype",contentType);
HttpSession session=(HttpSession) facesContext.getCurrentInstance().getExternalContext().getSession(false);
session.setAttribute(fileName, map);

}
}


The tag attributes and usage is shown below.

<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:preview="com.blogspot.ramannanda.components"
>
<document title="untitled1.jsf"
id="d1" xmlns="http://xmlns.oracle.com/adf/faces/rich"
>

<form id="f1" usesUpload="true">

<panelGroupLayout id="pgl1">
<inputFile value="#{pageFlowScope.TestUploadBacking.file}" label="Upload" id="if1"/>
<preview:richpreview data="#{pageFlowScope.TestUploadBacking.data.data}"
style="width:100px;height:100px" elementType="#{pageFlowScope.TestUploadBacking.data.elementType}"
filename="#{pageFlowScope.TestUploadBacking.data.fileName}"
uriAttr="#{pageFlowScope.TestUploadBacking.data.uriAttr}"
value="#{pageFlowScope.TestUploadBacking.data.value}"/>
<commandButton text="Submit"
action="#{pageFlowScope.TestUploadBacking.uploadAction}" id="cb1"/>
</panelGroupLayout>

</form>
</document>
</f:view>


Here data is a POJO class.

The attributes and their meaning for tag richpreview  is explained below
elementtype- The content type of the element. This attribute is required.
uriattr- The uri attribute. This can either be src or data and depends upon whether it is a image file or a flash file, for an image it is src and for flash it is data.This attribute is required.
data- It represents the actual data in a byte array. It is required.
filename- It is used for specifying the name of the file. It is required

Wherever you have to use the tag the registration of the resource servlet will be  required along with a init param as shown below. The ServletPath param has to be same as url pattern.

<context-param>
<param-name>ServletPath</param-name>
<param-value>/myservlet</param-value>
</context-param>

<servlet>
<servlet-name>MyResourceServlet</servlet-name>
<servlet-class>com.blogspot.ramannanda.DynamicResourceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyResourceServlet</servlet-name>
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>

The entire source code for the tag library and sample usage project can be downloaded from below links.

Tag library project
Sample usage
Tag library