com.ibm.btt.sample.SampleFileHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.btt.sample.SampleFileHandler.java

Source

/*_
 * Licensed Materials - Property of IBM
 * Restricted Materials of IBM
 * 5724-H82
 * (C) Copyright IBM Corp. 2010 All Rights Reserved.
 * US Government Users Restricted Rights - Use, duplication or disclosure
 * restricted by GSA ADP Schedule Contract with IBM Corp
 */
package com.ibm.btt.sample;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.ibm.btt.base.BTTLog;
import com.ibm.btt.base.BTTLogFactory;
import com.ibm.btt.base.Context;
import com.ibm.btt.base.DSEException;
import com.ibm.btt.base.DSEInvalidArgumentException;
import com.ibm.btt.base.DSEObjectNotFoundException;
import com.ibm.btt.base.KeyedCollection;
import com.ibm.btt.cs.ajax.fileupload.AbstractFileHandler;
import com.ibm.btt.cs.ajax.fileupload.FileUploadUtil;
import com.ibm.btt.sm.BTTSMException;

/**
 * <p>
 * This class extends HttpServlet class used to handle file updload scenario.
 * It will save the uploaded file in file system or configured DB
 *  
 * @copyright(c) Copyright IBM Corporation 2010
 */
public class SampleFileHandler extends AbstractFileHandler {

    private static final BTTLog LOG = BTTLogFactory.getLog(SampleFileHandler.class.getName());

    /**
     * name of paramters that will be configured in btt.xml file for this file handler
     * make sure the name are exactly the same with the ones in btt.xml file
     */
    public static final String PARAM_CACHE_PATH = "cachePath";
    public static final String PARAM_FILE_PATH = "filepath";
    public static final String PARAM_MEM_CACHE = "memCacheSize";
    public static final String PARAM_TIME_OUT = "timeout";
    public static final String PARAM_MAX_SIZE = "maxSize";

    /**
     * cache size, if file is bigger than the size, it will be saved in file system
     * or else if will be hold in memory. 
     */
    protected int memCacheSize = 1024 * 4;
    /**
     * real system cache folder path
     */
    protected String cachePath = null;
    /**
     * real system upload file folder 
     */
    protected String filePath = null;
    /**
     * maximum allowed size of a file 
     */
    protected long maxSize = 1024 * 1024 * 4;
    /**
     * fileId maybe a encryped, meaning-less string, maybe a key to 
     * real file system path of the uploaded file or a key by which the file
     * can be accessed from DB. 
     * In this sample, it just use the file fullpath as the id 
     */
    private String fileId = null;
    /**
     * record the current tmp file, it will be used to remove the tmp file 
     * if the session expired. 
     */
    private FileItem tmpfile = null;
    /**
     * name of the file with ext. 
     */
    private String filename = null;

    /**
     * initial parameters, this method should be customized based on the handler's implementaion
     * 
     */
    @Override
    public void initConfig(KeyedCollection config) throws DSEException {
        Enumeration keys = config.getElements().keys();
        while (keys.hasMoreElements()) {
            String key = (String) keys.nextElement();
            Object value = config.getValueAt(key);
            if (PARAM_CACHE_PATH.equals(key)) {
                cachePath = value.toString();
            } else if (PARAM_FILE_PATH.equals(key)) {
                filePath = value.toString();
            } else if (PARAM_MEM_CACHE.equals(key)) {
                memCacheSize = Integer.parseInt(value.toString());
            } else if (PARAM_TIME_OUT.equals(key)) {
                this.setTimeout(Long.parseLong(value.toString()));
            } else if (PARAM_MAX_SIZE.equals(key)) {
                maxSize = Long.parseLong(value.toString());
                maxSize = maxSize < 0 ? -1 : maxSize;
            }
        }
        // formate file path based on OS, DOS or unix.  
        this.cachePath = formatFilePath(this.cachePath);
        this.filePath = formatFilePath(this.filePath);

        StringBuilder sb = new StringBuilder();
        if (cachePath == null || cachePath.trim().length() < 1) {
            sb.append("SampleFileHandler : Error, cachePath is null. \n");
        }
        if (filePath == null || filePath.trim().length() < 1) {
            sb.append("SampleFileHandler : Error, filePath is null. \n");
        }
        if (memCacheSize < 0) {
            sb.append("SampleFileHandler : Error, memory cache size must bigger than 0 \n");
        }
        if (getTimeout() < 0) {
            sb.append("SampleFileHandler : Error, timeout value must bigger than 0 \n");
        }
        if (sb.length() > 0) {
            throw new DSEException(sb.toString());
        }
        // create relative folders if not existed 
        initFolders();
    }

    /**
     * Just handle one file upload in this handler, developer can extend to support 
     * multi-files upload 
     */
    @Override
    public int saveFile(HttpServletRequest request) {
        int code = SAVE_FAILED;

        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (isMultipart) {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //config the memory cache, if above the cache, the file data will be 
            // saved on cache folder 
            factory.setSizeThreshold(memCacheSize);
            // set up cache folder, when uploading, the tmp file 
            // will be saved in cache folder with a internal managed name. 
            factory.setRepository(new File(cachePath));

            ServletFileUpload upload = new ServletFileUpload(factory);
            // max size of a file 
            upload.setSizeMax(maxSize);

            try {
                if (isExpired()) {
                    return REQ_TIMEOUT;
                }
                List<FileItem> fileItems = upload.parseRequest(request);
                // save file from request 
                code = uploadFilesFromReq(request, fileItems);
            } catch (SizeLimitExceededException e) {
                code = FILE_SIZE_EXCEED;
            } catch (FileUploadException e) {
                if (LOG.doDebug()) {
                    LOG.debug("SampleFileHandler:saveFile() -- upload file stream was been cancelled", e);
                }
            }
        }
        return code;
    }

    /**
     * 
     * @param request
     * @param fileItems
     * @throws IOException
     */
    private int uploadFilesFromReq(HttpServletRequest request, List<FileItem> fileItems) {
        int code = SAVE_FAILED;
        // get the first file item 
        FileItem fileItem = getTheFileItem(request, fileItems);
        this.tmpfile = fileItem;
        String sessionId = request.getParameter("sessionId");
        if (fileItem == null || sessionId == null) {
            return SAVE_FAILED;
        }
        // save file and remove the temp file in cache folder 
        try {
            code = writeFiles(fileItem, request, sessionId);

            // remove temp file         
            fileItem.delete();
            this.tmpfile = null;
        } catch (IOException e) {
            return SAVE_FAILED;
        }
        // NOTES: just handle the firest file in the request, and save the first file 
        // in the file system. developer can extend to support multi-files upload 
        return code;
    }

    /**
     * NOTES: In this sample, we just handle the first file item in the upload request. 
     * 
     * @param request
     * @param fileItems upload file items
     * @return the first file item or null if errors 
     */
    private FileItem getTheFileItem(HttpServletRequest request, List<FileItem> fileItems) {
        String name = "";
        for (FileItem fileItem : fileItems) {
            //common form item name
            name = fileItem.getFieldName();
            if (fileItem.isFormField()) {
                // common form item value 
                String value = fileItem.getString();
                if (LOG.doDebug()) {
                    LOG.debug(new StringBuilder().append("Request item -- ").append(name).append(": ").append(value)
                            .toString());
                }
            } else {
                if (name == null || "".equals(name.trim())) {
                    continue;
                }
                return fileItem;
            }
        }
        return null;
    }

    /** 
      * write the files into file sytem. upload files are seperated by sessionId on the 
      * server side. 
      * notes that for performance, here we do not use the FileItem.get() method. 
      * 
     * @param fileItem
     * @param request
     * @param sessionId
     * @return whether save file success. 
     * @throws IOException
     */
    private int writeFiles(FileItem fileItem, HttpServletRequest request, String sessionId) throws IOException {
        // get the target file folder where to save the uploaded file 
        // Here use the sessionId to seperate each request files.    
        StringBuilder spath = new StringBuilder();
        spath.append(filePath).append("\\").append(sessionId);
        File sFolder = new File(spath.toString());

        if (!sFolder.isDirectory()) {
            boolean r1 = sFolder.mkdir();
            if (r1 == false && LOG.doError()) {
                LOG.error(new StringBuilder().append("Create folder error: ").append(spath).toString());
                throw new IOException(
                        new StringBuilder().append("FileUploadServelet: session folder create error").toString());
            }
        }

        // handle the file path for various browsers 
        String fileNameString = fileItem.getName();
        int start = fileNameString.lastIndexOf("\\");
        this.filename = fileNameString.substring(start + 1);
        // save file 
        File file = new File(spath.toString(), filename);
        byte[] buffer = new byte[1024];
        int length = 0;
        InputStream is = fileItem.getInputStream();
        FileOutputStream fos = new FileOutputStream(file);

        while ((length = is.read(buffer)) > 0 && !isExpired()) {
            fos.write(buffer, 0, length);
        }
        // colse stream 
        fos.close();
        is.close();

        // remove file if request expired. 
        if (isExpired()) {
            if (file.exists()) {
                file.delete();
            }
            return REQ_TIMEOUT;
        }
        return SAVE_SUCCESS;
    }

    @Override
    public int deleteFile(String fileId) {
        if (fileId == null || fileId.length() == 0) {
            return DELETE_SUCCESS;
        }
        if (isExpired()) {
            return REQ_TIMEOUT;
        }
        File file = retrieveFile(fileId);
        boolean r = false;
        if (file != null && file.exists()) {
            r = file.delete();
            this.filename = null;
        }
        // clean file Id 
        this.fileId = null;

        return r ? DELETE_SUCCESS : DELETE_FAILED;
    }

    @Override
    public File retrieveFile(String fileId) {
        if (fileId == null || fileId.length() == 0 || isExpired()) {
            return null;
        }
        File folder = new File(filePath + "\\" + getSessionId());
        if (folder.exists() && folder.isDirectory()) {
            int value = Integer.parseInt(fileId);
            File[] files = folder.listFiles();
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                if (value == file.getName().hashCode()) {
                    return file;
                }
            }
        }
        return null;
    }

    @Override
    public String getFileId() {
        if (fileId == null && filename != null) {
            fileId = new Integer(filename.hashCode()).toString();
        }

        return fileId;
    }

    @Override
    public String getFileName(String fileId) {
        return this.filename;
    }

    @Override
    public int upldateContext(String fileId) {
        if (isExpired()) {
            return REQ_TIMEOUT;
        }
        Context ctx = null;
        try {
            ctx = FileUploadUtil.getProperContext(getSessionId(), getProcessorId(), getDataname());
        } catch (BTTSMException e) {
            if (LOG.doError()) {
                LOG.error("SampleFileHandler : updateContext() -- ", e);
            }
        }
        return doUpdateContext(ctx, fileId);
    }

    private int doUpdateContext(Context ctx, String fileId) {
        int code = UPDATE_CTX_FAILED;
        if (ctx != null) {
            try {
                KeyedCollection kcoll = (KeyedCollection) ctx.tryGetElementAt(getDataname());
                // note that the a kcoll with id=file is mandatory for file upload 
                // 
                KeyedCollection fkc = (KeyedCollection) kcoll.tryGetElementAt(FILE);
                if (fkc != null) {
                    //upldated the new file info with the file kcoll. 
                    fkc.setValueAt(FILE_ID, fileId);
                    fkc.setValueAt(FILE_NAME, this.filename);
                    File file = this.retrieveFile(fileId);
                    long size = file == null ? 0 : file.length();
                    fkc.setValueAt("size", size);

                    code = UPDATE_CTX_SUCCESS;

                    //TODO this can be extend for multi-file upload scenario with a single fileupload
                    // widget. 
                    // developer can add the existed file info into the iColl with 
                    // id=receivedFiles. and then update the file kcoll with the latest
                    // uploaded file info               
                }
            } catch (DSEInvalidArgumentException e) {
                if (LOG.doError()) {
                    LOG.error("SampleFileHandler: update context error", e);
                }
            } catch (DSEObjectNotFoundException e) {
                if (LOG.doError()) {
                    LOG.error("SampleFileHandler: update context error", e);
                }
            }
        }
        return code;
    }

    @Override
    public int cleanContext() {
        return upldateContext("");
    }

    @Override
    protected int doRequestValidation(HttpServletRequest request) {
        int size = request.getContentLength();
        // notifiy the browser the file size is exceeded. 
        if (size > maxSize) {
            return FILE_SIZE_EXCEED;
        }
        return REQ_VALID;
    }

    @Override
    public String getFileInfo(int code) {
        StringBuilder sb = new StringBuilder();
        String fId = getFileId();
        fId = fId == null ? "" : fId;

        String fname = getFileName(fId);
        fname = fname == null ? "" : fname;

        sb.append("{\"").append(FILE_NAME).append("\":\"").append(fname).append("\",\"").append(FILE_ID)
                .append("\":\"").append(fId);
        if (code != UPDATE_CTX_SUCCESS) {
            sb.append("\",\"errorCode\":\"").append(code).append("\"}");
        } else {
            sb.append("\"}");
        }

        return sb.toString();
    }

    /**
     * create cache file folders and real file folders 
     * 
     */
    private void initFolders() {
        File fFolder = new File(filePath);
        File tFolder = new File(cachePath);
        boolean r1 = true;
        boolean r2 = true;
        if (!fFolder.isDirectory()) {
            r1 = fFolder.mkdirs();
            if (r1 == false && LOG.doError()) {
                LOG.error(new StringBuilder().append("Create folder error: ").append(filePath).toString());
            }
        }
        if (!tFolder.isDirectory()) {
            r2 = tFolder.mkdirs();
            if (r2 == false && LOG.doError()) {
                LOG.error(new StringBuilder().append("Create folder error: ").append(cachePath).toString());
            }
        }
    }

    /**
     * format the file path associated with the OS 
     * @param path
     * @return file path associated with the os
     */
    private String formatFilePath(String path) {
        // We test if the entitiesPath is a complete adress for a directory (ex: "d:\er\wee" for DOS, or "/d/er/wee" for UNIX)
        char sep = System.getProperty("file.separator").charAt(0);
        String p = path.replace('/', sep);
        p = path.replace('\\', sep);
        if ((p.startsWith(System.getProperty("file.separator"))) || ((p.length() > 1) && (p.charAt(1) == ':'))) {
            return p;
        }
        return null;
    }

    @Override
    public AbstractFileHandler clone() {
        AbstractFileHandler handler = new SampleFileHandler();
        handler.setConfig(getConfig());
        handler.setDataname(this.getDataname());
        handler.setProcessorId(this.getProcessorId());
        handler.setSessionId(this.getSessionId());
        try {
            handler.initConfig(this.getConfig());
        } catch (DSEException e) {
            return null;
        }
        return handler;
    }

    /**
     * 
     */
    @Override
    public void onRequestExpired(String fileId) {
        // delte tmp file if have 
        if (this.tmpfile != null) {
            this.tmpfile.delete();
            this.tmpfile = null;
        }
        // delete file if have 
        this.deleteFile(getFileId());
        // clean ctx if have 
        this.cleanContext();
    }
}