Wednesday, September 9, 2009

External File Upload into Apps

There are indeed a lot of programs/processes that need some external file as input. There are different ways to make this file available in Oracle Apps environment.

1. You can use some FTP UIs like SFTP, which users can login to server and post the files. You can control the folders to which what users have access to. There are obvious drawbacks to this. You need to maintain separate server accounts for users, maintain those and for users they have one more login/password information to track. Again you would need to create the same accounts on test servers also.

2. You can write simple JSP page and expose this JSP function to users

3. Or you can write OA Framework File Upload Page. I feel this approach is simpler.

Let us see how we can do file upload using Framework (11510)
All you need to do is use messageFileUpload bean, see the page XML.




Next is the controller where you do the actual reading and writing of the file.
processFormRequest is where the logic would reside.

public void processFormRequest(OAPageContext pageContext, OAWebBean webBean)
{
  super.processFormRequest(pageContext, webBean);
  String event = (String) pageContext.getParameter(EVENT_PARAM);
  OAApplicationModule AM = (OAApplicationModule) pageContext.getApplicationModule(webBean);
  DataObject fileUploadData = (DataObject)pageContext.getNamedDataObject("fileUpload");
  String productName  = pageContext.getParameter("productName");
  String applcsfPath  = pageContext.getProfile("APPLCSF_ABS_PATH");

  /*this is the profile where you can put the actual physical location of the folder
    where you intend to store the files. This is more like a basepath. you can have this
    basepath in the profile and then for each upload function, you can pass different arguments
    say for sub-floders etc.*/

  if ("uploadFile".equals(event))
  {
    if (fileUploadData == null)
    {
      throw new OAException("APP", "NO_FILE_TO_LOAD");
    }

    String uFileName = (String)fileUploadData.selectValue(null, "UPLOAD_FILE_NAME");
    BlobDomain byteStream = (BlobDomain)fileUploadData.selectValue(null, uFileName);

    try
    {

      String filePath = applcsfPath+"/";
      System.out.println("filePath  : " + filePath);
      System.out.println("uFileName : " + uFileName);

      FileOutputStream fileOp = new FileOutputStream(filePath+uFileName);
      fileOp.write(byteStream.getBytes(0,(int)byteStream.getLength()));
      fileOp.close();

      fileUploadData = null;
      pageContext.removeNamedDataObject("fileUpload");       

      OAException confirmMessage = new OAException("APP", "FILE_UPLOAD_SUCCESS", null,
                                                   OAException.CONFIRMATION, null);
      pageContext.putDialogMessage(confirmMessage);
   }
   catch(Exception e)
   {
     System.out.println("File Upload Error " + e.toString());
     e.printStackTrace();
     throw new OAException(e.getMessage(), OAException.ERROR);
   }
  }
}

That is all to upload a file, simple and straightforward. You would need following imports apart from the regular ones.

import oracle.cabo.ui.data.DataObject;
import oracle.jbo.domain.BlobDomain;
import java.io.FileOutputStream;
Now, let us say we want to build a download function. There is a download bean, but more about it later. You can expose specific files in httpd.conf making aliases for folders, but this approach does not provide much security.

Here is what I think should be done for download, I have not tried these steps yet, would do that sometime later.

First you need to browse through folder and generate a file-list. Java would be slower to browse if your folder has huge number of files. Better approach is to use a simple shell script that creates a flat file, it can be a one line code such as
grep -ir -v -l 'cantfindthis' $APPLCSF > 1.txt
Then in java code, you can loop through the file (1.txt) and insert the data into a Global Temp table[or the grep output could be redirected directly to java which you can loop through] . Inserting this file list into table provides a good means to have search UI. Next is build search UI and a results tables. Now comes the actual download part. You can add a single-select and a button or link the filename to a submit action. This submit action should can throw open a new page and the file can be streamed out.
Following code snippet is for streaming out from postFormsRequest..
.
  void  throwZip(String pDocList, String pFileName,OAPageContext pageContext)
  {
      //System.out.println("Throwing Docs!");
      URLConnection connURL            = null;
      URL url                          = null;
      InputStream inStream             = null;
      HttpServletResponse httpResponse = null;
      ZipOutputStream outStream        = null;
      DataObject sessionDictionary     = pageContext.getNamedDataObject("_SessionParameters");
      StringTokenizer stURLToken = new StringTokenizer(pDocList,",");
      int urlCount = stURLToken.countTokens();
      int i = 0;
      boolean closeConnection = false;

      while(stURLToken.hasMoreTokens())
      {
        i = i + 1;
        String stURL          = stURLToken.nextToken();
        closeConnection = false;
        try
        {
          if (i == 1)
          {
            httpResponse = (HttpServletResponse)sessionDictionary.selectValue(null, "HttpServletResponse");
            httpResponse.setContentType("application/x-zip-compressed");
            httpResponse.setHeader("Content-Disposition", "Attachment; Filename=" + pFileName);
            outStream = new ZipOutputStream(httpResponse.getOutputStream());
          }
          url  = new URL(stURL);
          connURL  = url.openConnection();
          inStream = connURL.getInputStream();
          outStream.putNextEntry(new ZipEntry(stURL.substring(stURL.lastIndexOf("/"))));
          byte bytes[]                     = new byte[1024];
          for(int bytesRead = inStream.read(bytes); bytesRead > 0;)
          {
           outStream.write(bytes, 0, bytesRead);
           bytesRead = inStream.read(bytes);
           outStream.flush();
          }
          outStream.closeEntry();
        }
        catch(Exception e)
        {
          System.out.println("Exception 1 " + e.toString());
          closeConnection = true;
          throw OAException.wrapperException(e);
        }
        finally
        {
          try
          {
              if (closeConnection || (i==urlCount))
              {
                System.out.println("Closing Connections ");
                if(inStream != null)
                    inStream.close();
                if(outStream != null)
                    outStream.close();
              }
          }
          catch(Exception e)
          {
            System.out.println("Exception 2 " + e.toString());
            throw OAException.wrapperException(e);
          }
        }
      }
  }
.
For standard concurrent requests, FND_WEBFILE.GET_URL generates a temp URL. (Tools --> Copy on the Concurrent Request Log/Output file)

Cheers!

No comments: