Sunday, August 23, 2009

File upload and download in Toplink, ADF Faces, Oracle 11g and JDeveloper 10g (10.1.3.3)


Application is simple; you upload files to Oracle database. Uploaded files are shown in a grid. To download a file, you simply click on link in the code column.




I have tried hard to write this blog-entry in detail, but still if there is anything you wanna ask then just email me at asim.ghaffar@gmail.com or simply add a comment.



Create Database

Schema

CREATE USER file_example
IDENTIFIED BY "12"
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
PROFILE DEFAULT
ACCOUNT UNLOCK;


GRANT CONNECT TO file_example;
GRANT RESOURCE TO file_example;

Table

CREATE TABLE file_example.x_files
(
file_code NUMBER(38),
file_name VARCHAR2(256 BYTE),
file_content BLOB,
file_type VARCHAR2(50 BYTE),
file_size NUMBER(38),
CONSTRAINT x_files_pk PRIMARY KEY (file_code)
);


In this example, file_code is the primary key It is basically a hash-value of uploaded file's contents calculated using Java Arrays.hashCode(Byte[]). This ensures that a same file cannot be added more than once.


Create JDeveloper Application

Basic Application

  1. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select General (if not already selected).
    2. In the Items pan on the right, select Application (if not already selected).
    3. Press Ok button.
  2. Create Application dialogue should appear. Fill it as following.
    1. Application name: Cabinet
    2. Application Package Prefix: cabinet
    3. Application Template: Web Application [JSF, EJB, Toplink]
  3. You should be seeing newly created project under Applications node in the Application Navigator window. If the window is not open press (Ctrl+Shift+A).
  4. In Application Navigator select Model project of the newly created application. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select Business Tier -> TopLink.
    2. In the Items pan on the right, select Java Objects from Tables.
    3. Press Ok button.
  5. Multistep dialogue will appear Create Java Objects from Tables
    1. Step 1 of 4: Select DB Connection.
      1. Click on the new button in row of Connection or press (Alt + w)
        1. Connection Name: file_conn, Connection Type: Oracle (JDBC)
        2. Username: file_example, Password: 12, Deploy password: [CHECKED]
        3. Driver: thin, Host Name: localhost, JDBC port: 1521, SID: ORCL
          1. This should reflect your DBMS specific values. Above should hold if you installed DB yourself on your machine with default SID.
        4. Test to verify. Else see what you missed in 2 or 3.
        5. Press Finish
      2. Click on the new button in row of Toplink Map or press (Alt + .)
        1. ToplLink Map Name: tlMap1, Connection: file_conn, Database platform: Oracle 10g
        2. Press Ok
      3. Press Next
    2. Step 2 of 4: Select Tables.
      1. Press Query or [CHECK] Auto-Query.
      2. Move table X_FILES from Available to Selected.
      3. Press Next
    3. Step 3 of 4: General Options.
      1. Press Next
    4. Step 4 of 4: Specifiy Object Details.
      1. Press Fnish.
  6. In Application Navigator select Model project of the newly created application. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select Business Tier -> EJB.
    2. In the Items pan on the right, select Session Bean (EJB 1.1/2.x/3.0).
      1. If you are using other version of JDeveloper than 10.1.3.3 and you don't have this item available then choose the one which says Session Bean and Version 3.0.
    3. Press Ok button.
  7. Multistep dialogue will appear Create Java Objects from Tables
    1. Step 1 of 4: EJB Name and Options
      1. EJB Name: SessionEJB
      2. Session Type: Stateless
      3. Transaction Type: Container
      4. Generate Session Façade Methods: [CHECKED]
      5. Entity Implementation: TopLink POJOs
      6. Press Next
    2. Step 2 or 4: Session Façade – Select Service Methods
      1. [Check All]
      2. Press Next
    3. Step 3 or 4: Class Definitions
      1. Press Next
    4. Step 4 or 4: Class Definitions
      1. Implement a Remote Interface: [UNCHECKED]
      2. Implement a Local Interface: [CHECKED]
      3. Include Web Service Endpoint Interface: [Unchecked]
      4. Press Finish
  8. In Application Navigator select ViewController project of the newly created application. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select Web Tier -> JSF.
    2. In the Items pan on the right, select JSF JSP.
    3. Press Ok button.
  9. Multistep dialogue will appear Create JSF JSP
    1. Step 1 of 4: JSP File
      1. File Name: index.jspx
      2. Type: [JSP Document (*.jspx)]]
      3. Add Mobile Support: [UNCHECKED]
      4. Press Next
    2. Step 2 or 4: Component Binding
      1. Select [Do Not Automatically Expose UI Components in a Managed Bean]
      2. Press Next
    3. Step 3 or 4: Tag Libraries
      1. Filter by Project Technologies.
      2. Move these from available libraries to selected Libraries.
        1. ADF Faces Component 10_1_3_3_0
        2. ADF Faces HTML 10_1_3_3_0
        3. JSF Core 1.0
        4. JSF HTML 1.0
      3. Press Next
    4. Step 4 or 4: HTML Options
      1. HTML Version: 4.0.1 Transitional
      2. Title: Index
      3. Press Finish

Model Project enhancement to complete example application

  1. In Application Navigator collapse tree for Model project and double click Application Sources à cabinet.model.SessionEjbBean.java to open it in the editor. Add following:

public Integer saveXFile(String fileName,byte[] fileContent,int filesize,String filetype){
int code=Arrays.hashCode(fileContent);
XFiles list=findXFiles(code);
if(list!=null) {
System.err.println("File already exist");
return code;
}


list=new XFiles();
list.setFileName(fileName);
list.setFileContent(fileContent);
list.setFileSize((double)filesize);
list.setFileType(filetype);
list.setFileCode((double)code);


try{
persistEntity(list);
} catch(Exception e){
e.printStackTrace();
return null;
}


return code;
}


public XFiles findXFiles(int code){
Session sess=getSessionFactory().acquireSession();
Expression exp=new ExpressionBuilder().get("fileCode").equal(code);
XFiles list=(XFiles)sess.readObject(XFiles.class,exp);
sess.release();
return list;
}


  1. While in SessionEjbBean.java, press (Ctrl+Shift+S) to open structure window. Collapse Sources, and double click on SessionEJBLocal.java to open it in the editor. Add following:

Integer saveXFile(String fileName,byte[] fileContent,int filesize,String filetype);


XFiles findXFiles(int code);


  1. In Application Navigator collapse tree for Model project and select Application Sources à cabinet.model.SessionEjbBean.java. Right click on the file and choose option Create Data Control.

ViewController Project enhancement to complete example application

  1. In Application Navigator collapse tree for ViewController project and double click web Content à index.jspx to open it in the editor. Choose Design view.
  2. From Data Control Palette, drag and drop SessionEJBLocal à findAllXFiles() à XFiles at center of open page. Use option Tables à ADF Read-only Table…
    1. You will get a notification Client Project Libraries Added, if this is the first page on which you are dropping a data control.
  3. Click anywhere on page and press (Ctrl + Shift + S).
  4. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form à af:table and then double click on it to open Table Properties dialogue.
    1. On Column Summary tab, rename Column headers
      1. #{bindings.findAllXFiles1.labels.fileCode} à Code
      2. #{bindings.findAllXFiles1.labels.fileName} à Name
      3. #{bindings.findAllXFiles1.labels.fileSize} à File Size
      4. #{bindings.findAllXFiles1.labels.fileType} à File Type
    2. On same tab, change Component of first row (value=#{row.fileCode})
      1. af:outputText à af:commandLink
    3. Press OK
  5. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form à af:table à af:column – Code à af:commandLink - #{row.fileCode} and then press (Ctrl + Shift + I) to open Property Inspector.
    1. In Action property write #{backing_index.download}
  6. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form and then right click on it and choose Convert option. Convert Form dialogue will open.
    1. From ADF Faces Core category choose Form.
    2. Press OK
  7. With af:form still selected press (Ctrl + Shift + I) to open Property Inspector.
    1. In UsesUpload choose true. (IMPORTANT)
  8. Click anywhere on page. Press (Ctrl + Shift + P) to open Component Palette.
  9. From Component Palette, drag and drop ADF Faces Core à InputFile at center of open page. New Component will appear below existing Table.
  10. With newly added component selected press (Ctrl + Shift + I) to open Property Inspector.
    1. In Label property remove existing text i.e. Label 1
    2. In Value property enter #{backing_index.uploadedFile}
  11. Click anywhere on page. Press (Ctrl + Shift + P) to open Component Palette.
  12. From Component Palette, drag and drop ADF Faces Core à CommandButton at center of open page. New Component will appear below existing File Input Compoent.
  13. With newly added component selected press (Ctrl + Shift + I) to open Property Inspector.
    1. In Text property replace existing text with Upload
  14. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form and then (Ctrl) select af:inputFile and af:commandButton - Upload. Right click on the selection and choose Surround With. Surround With dialogue will open.
    1. From ADF Faces Core category choose PanelHorizontal.
    2. Press OK
  15. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form à af:table à af:column – Code à af:commandLink - #{row.fileCode} and then right click and choose Insert inside of af:commandLink - #{row.fileCode} à ADF Faces Core. Insert ADF Faces Core Item dialogue will appear.
    1. From ADF Faces Core category choose SetActionListener.
    2. Press OK.
    3. Insert SetActionaListener dialogue will appear. Add following values
      1. From: #{row.fileCode}
      2. To: #{backing_index.fileCode}

Now we need a backing bean. For this we will create a file and then add relevant information in faces-config.

  1. In Application Navigator select ViewController project of the newly created application. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select General.
    2. In the Items pan on the right, select Java Class.
    3. Copy paste Index.java code from next section: Complete Source
    4. Press Ok.
  2. Create Java Class dialogue will appear
    1. Set following values
      1. Name: Index
      2. Package: cabinet.view
      3. Public: [CHECKED]
      4. Generate Default Constructor: [UNCHECKED]
      5. Generate Main Method: [UNCHECKED]
    2. Press OK
  3. In Application Navigator collapse tree for ViewController project and double click web Content à faces-config.xml to open it in the editor. Choose Source view.
    1. Before last tag </faces-config> add these lines

    <managed-bean>
    <managed-bean-name>backing_index</managed-bean-name>
    <managed-bean-class>cabinet.view.Index</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
    <property-name>bindings</property-name>
    <value>#{bindings}</value>
    </managed-property>
    </managed-bean>

Complete Source

index.jspx

<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0" xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces" xmlns:afh="http://xmlns.oracle.com/adf/faces/html">
<jsp:output omit-xml-declaration="true" doctype-root-element="HTML" doctype-system="http://www.w3.org/TR/html4/loose.dtd"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<afh:html>
<afh:head title="Index">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
</afh:head>
<afh:body>
<af:messages/>
<af:form usesUpload="true">
<af:table value="#{bindings.findAllXFiles1.collectionModel}" var="row" rows="#{bindings.findAllXFiles1.rangeSize}" first="#{bindings.findAllXFiles1.rangeStart}"
emptyText="#{bindings.findAllXFiles1.viewable ? 'No rows yet.' : 'Access Denied.'}">
<af:column sortProperty="fileCode" sortable="false" headerText="Code">
<af:commandLink text="#{row.fileCode}" action="#{backing_index.download}">
<af:setActionListener from="#{row.fileCode}" to="#{backing_index.fileCode}"/>
</af:commandLink>
</af:column>
<af:column sortProperty="fileName" sortable="false" headerText="Name">
<af:outputText value="#{row.fileName}"/>
</af:column>
<af:column sortProperty="fileSize" sortable="false" headerText="File Size">
<af:outputText value="#{row.fileSize}">
<f:convertNumber groupingUsed="false" pattern="#{bindings.findAllXFiles1.formats.fileSize}"/>
</af:outputText>
</af:column>
<af:column sortProperty="fileType" sortable="false" headerText="File Type">
<af:outputText value="#{row.fileType}"/>
</af:column>
</af:table>
<af:panelHorizontal>
<af:inputFile value="#{backing_index.uploadedFile}"/>
<af:commandButton text="Upload"/>
</af:panelHorizontal>
</af:form>
</afh:body>
</afh:html>
</f:view>
</jsp:root>

faces-config.xml

<?xml version="1.0" encoding="windows-1252"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config xmlns="http://java.sun.com/JSF/Configuration">
<application>
<default-render-kit-id>oracle.adf.core</default-render-kit-id>
</application>
<lifecycle>
<phase-listener>oracle.adf.controller.faces.lifecycle.ADFPhaseListener</phase-listener>
</lifecycle>
<managed-bean>
<managed-bean-name>backing_index</managed-bean-name>
<managed-bean-class>cabinet.view.Index</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>bindings</property-name>
<value>#{bindings}</value>
</managed-property>
</managed-bean>
</faces-config>

Index.java

package cabinet.view;


import cabinet.model.SessionEJBLocal;
import cabinet.model.XFiles;
import java.io.IOException;
import java.io.InputStream;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import oracle.adf.view.faces.model.UploadedFile;
import oracle.binding.BindingContainer;


public class Index{
private BindingContainer bindings;
private UploadedFile uploadedFile;
private Double fileCode;


public BindingContainer getBindings(){
return this.bindings;
}


public void setBindings(BindingContainer bindings){
this.bindings=bindings;
}


public void setFileCode(Double fileCode){
this.fileCode=fileCode;
}


public Double getFileCode(){
return fileCode;
}


public void setUploadedFile(UploadedFile uploadedFile){
this.uploadedFile=uploadedFile;
try{
Integer code=uploadFile(uploadedFile);


// To refresh table
bindings.getOperationBinding("findAllXFiles").execute();
} catch(IOException e){
e.printStackTrace();
}
}


public UploadedFile getUploadedFile(){
return uploadedFile;
}


public String download(){
if(fileCode!=null)
try{
XFiles atchmn=getEJBLocal().findXFiles(fileCode.intValue());
if(atchmn==null)
return null;
downloadFile(atchmn);
} catch(Exception ex){
ex.printStackTrace();
}
return null;
}


public static Integer uploadFile(UploadedFile file) throws IOException{
byte[] array=new byte[(int)file.getLength()];
InputStream in=file.getInputStream();
int offset=0;
int numRead=0;
do{
numRead=in.read(array,offset,array.length-offset);
offset+=2048;
} while(numRead!=-1);
Integer code=getEJBLocal().saveXFile(file.getFilename(),array,(int)file.getLength(),file.getContentType());
return code;
}


public static void downloadFile(XFiles atchmn){
String fname=atchmn.getFileName();
byte[] pdf=atchmn.getFileContent();
FacesContext faces=FacesContext.getCurrentInstance();
HttpServletResponse response=(HttpServletResponse)faces.getExternalContext().getResponse();
response.setContentType(atchmn.getFileType());
response.setContentLength(atchmn.getFileSize().intValue());
response.setHeader("Cache-Control","must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma","public");
response.setHeader("Content-disposition","attachment; filename=\""+fname+"\"");
try{
ServletOutputStream out;
out=response.getOutputStream();
out.write(pdf);
} catch(IOException ex){
ex.printStackTrace();
}
faces.responseComplete();
}


public static Object getELObj(String s){
FacesContext fc=FacesContext.getCurrentInstance();
ValueBinding vb=fc.getApplication().createValueBinding(s);
return vb.getValue(fc);
}


public static SessionEJBLocal getEJBLocal(){
try{
SessionEJBLocal ejb=(SessionEJBLocal)getELObj("#{data.SessionEJBLocal.dataProvider}");
return ejb;
} catch(Exception ex){
ex.printStackTrace();
}
return null;
}
}

Note:

If in Index.java you get error on oracle.binding.BindingContainer then this means certain required libraries are not added to your project. When you drag and drop control from Data Control Palette these libraries are automatically added.