com.slamd.admin.JobPack.java Source code

Java tutorial

Introduction

Here is the source code for com.slamd.admin.JobPack.java

Source

/*
 *                             Sun Public License
 *
 * The contents of this file are subject to the Sun Public License Version
 * 1.0 (the "License").  You may not use this file except in compliance with
 * the License.  A copy of the License is available at http://www.sun.com/
 *
 * The Original Code is the SLAMD Distributed Load Generation Engine.
 * The Initial Developer of the Original Code is Neil A. Wilson.
 * Portions created by Neil A. Wilson are Copyright (C) 2004-2010.
 * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc.
 * All Rights Reserved.
 *
 * Contributor(s):  Neil A. Wilson
 */
package com.slamd.admin;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;

import com.slamd.common.Constants;
import com.slamd.job.JobClass;
import com.slamd.server.SLAMDServer;
import com.slamd.server.SLAMDServerException;

/**
 * This class defines methods for dealing with SLAMD job packs.  A job pack is a
 * JAR file containing one or more SLAMD jobs and supporting files that may be
 * uploaded as a group and automatically registered with the SLAMD server.
 *
 *
 * @author   Neil A. Wilson
 */
public class JobPack {
    // The list of fields in the multipart form content.
    private List fieldList;

    // Information about the request from the client, including the file data.
    private RequestInfo requestInfo;

    // The SLAMD server with which to register the jobs.
    private SLAMDServer slamdServer;

    // The path to the directory in which the job classes should be placed.
    private String jobClassDirectory;

    // The path to the job pack file on the server's filesystem.
    private String filePath;

    /**
     * Creates a new job pack definition with the provided request.
     *
     * @param  requestInfo  Information about the request from the client,
     *                      including the file data.
     */
    public JobPack(RequestInfo requestInfo) {
        this.requestInfo = requestInfo;

        filePath = null;
        fieldList = requestInfo.multipartFieldList;
        slamdServer = AdminServlet.slamdServer;
        jobClassDirectory = AdminServlet.classPath;
    }

    /**
     * Creates a new job pack definition from a file on the server's filesystem.
     *
     * @param  requestInfo  Information about the request from the client.
     * @param  filePath     The path to the job pack file on the server's
     *                      filesystem.
     */
    public JobPack(RequestInfo requestInfo, String filePath) {
        this.requestInfo = requestInfo;
        this.filePath = filePath;

        slamdServer = AdminServlet.slamdServer;
        jobClassDirectory = AdminServlet.classPath;
    }

    /**
     * Extracts the contents of the job pack and registers the included jobs with
     * the SLAMD server.
     *
     * @throws  SLAMDServerException  If a problem occurs while processing the job
     *                                pack JAR file.
     */
    public void processJobPack() throws SLAMDServerException {
        byte[] fileData = null;
        File tempFile = null;
        String fileName = null;
        String separator = System.getProperty("file.separator");

        if (filePath == null) {
            // First, get the request and ensure it is multipart content.
            HttpServletRequest request = requestInfo.request;
            if (!FileUpload.isMultipartContent(request)) {
                throw new SLAMDServerException("Request does not contain multipart " + "content");
            }

            // Iterate through the request fields to get to the file data.
            Iterator iterator = fieldList.iterator();
            while (iterator.hasNext()) {
                FileItem fileItem = (FileItem) iterator.next();
                String fieldName = fileItem.getFieldName();

                if (fieldName.equals(Constants.SERVLET_PARAM_JOB_PACK_FILE)) {
                    fileData = fileItem.get();
                    fileName = fileItem.getName();
                }
            }

            // Make sure that a file was actually uploaded.
            if (fileData == null) {
                throw new SLAMDServerException("No file data was found in the " + "request.");
            }

            // Write the JAR file data to a temp file, since that's the only way we
            // can parse it.
            if (separator == null) {
                separator = "/";
            }

            tempFile = new File(jobClassDirectory + separator + fileName);
            try {
                FileOutputStream outputStream = new FileOutputStream(tempFile);
                outputStream.write(fileData);
                outputStream.flush();
                outputStream.close();
            } catch (IOException ioe) {
                try {
                    tempFile.delete();
                } catch (Exception e) {
                }

                ioe.printStackTrace();
                slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(ioe));
                throw new SLAMDServerException("I/O error writing temporary JAR " + "file:  " + ioe, ioe);
            }
        } else {
            tempFile = new File(filePath);
            if ((!tempFile.exists()) || (!tempFile.isFile())) {
                throw new SLAMDServerException("Specified job pack file \"" + filePath + "\" does not exist");
            }

            try {
                fileName = tempFile.getName();
                int fileLength = (int) tempFile.length();
                fileData = new byte[fileLength];

                FileInputStream inputStream = new FileInputStream(tempFile);
                int bytesRead = 0;
                while (bytesRead < fileLength) {
                    bytesRead += inputStream.read(fileData, bytesRead, fileLength - bytesRead);
                }
                inputStream.close();
            } catch (Exception e) {
                slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(e));
                throw new SLAMDServerException("Error reading job pack file \"" + filePath + "\" -- " + e, e);
            }
        }

        StringBuilder htmlBody = requestInfo.htmlBody;

        // Parse the jar file
        JarFile jarFile = null;
        Manifest manifest = null;
        Enumeration jarEntries = null;
        try {
            jarFile = new JarFile(tempFile, true);
            manifest = jarFile.getManifest();
            jarEntries = jarFile.entries();
        } catch (IOException ioe) {
            try {
                if (filePath == null) {
                    tempFile.delete();
                }
            } catch (Exception e) {
            }

            ioe.printStackTrace();
            slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(ioe));
            throw new SLAMDServerException("Unable to parse the JAR file:  " + ioe, ioe);
        }

        ArrayList<String> dirList = new ArrayList<String>();
        ArrayList<String> fileNameList = new ArrayList<String>();
        ArrayList<byte[]> fileDataList = new ArrayList<byte[]>();
        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
            String entryName = jarEntry.getName();
            if (jarEntry.isDirectory()) {
                dirList.add(entryName);
            } else {
                try {
                    int entrySize = (int) jarEntry.getSize();
                    byte[] entryData = new byte[entrySize];
                    InputStream inputStream = jarFile.getInputStream(jarEntry);
                    extractFileData(inputStream, entryData);
                    fileNameList.add(entryName);
                    fileDataList.add(entryData);
                } catch (IOException ioe) {
                    try {
                        jarFile.close();
                        if (filePath == null) {
                            tempFile.delete();
                        }
                    } catch (Exception e) {
                    }

                    ioe.printStackTrace();
                    slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(ioe));
                    throw new SLAMDServerException("I/O error parsing JAR entry " + entryName + " -- " + ioe, ioe);
                } catch (SLAMDServerException sse) {
                    try {
                        jarFile.close();
                        if (filePath == null) {
                            tempFile.delete();
                        }
                    } catch (Exception e) {
                    }

                    sse.printStackTrace();
                    throw sse;
                }
            }
        }

        // If we have gotten here, then we have read all the data from the JAR file.
        // Delete the temporary file to prevent possible (although unlikely)
        // conflicts with data contained in the JAR.
        try {
            jarFile.close();
            if (filePath == null) {
                tempFile.delete();
            }
        } catch (Exception e) {
        }

        // Create the directory structure specified in the JAR file.
        if (!dirList.isEmpty()) {
            htmlBody.append("<B>Created the following directories</B>" + Constants.EOL);
            htmlBody.append("<BR>" + Constants.EOL);
            htmlBody.append("<UL>" + Constants.EOL);

            for (int i = 0; i < dirList.size(); i++) {
                File dirFile = new File(jobClassDirectory + separator + dirList.get(i));
                try {
                    dirFile.mkdirs();
                    htmlBody.append("  <LI>" + dirFile.getAbsolutePath() + "</LI>" + Constants.EOL);
                } catch (Exception e) {
                    htmlBody.append("</UL>" + Constants.EOL);
                    e.printStackTrace();
                    slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(e));
                    throw new SLAMDServerException(
                            "Unable to create directory \"" + dirFile.getAbsolutePath() + " -- " + e, e);
                }
            }

            htmlBody.append("</UL>" + Constants.EOL);
            htmlBody.append("<BR><BR>" + Constants.EOL);
        }

        // Write all the files to disk.  If we have gotten this far, then there
        // should not be any failures, but if there are, then we will have to
        // leave things in a "dirty" state.
        if (!fileNameList.isEmpty()) {
            htmlBody.append("<B>Created the following files</B>" + Constants.EOL);
            htmlBody.append("<BR>" + Constants.EOL);
            htmlBody.append("<UL>" + Constants.EOL);

            for (int i = 0; i < fileNameList.size(); i++) {
                File dataFile = new File(jobClassDirectory + separator + fileNameList.get(i));

                try {
                    // Make sure the parent directory exists.
                    dataFile.getParentFile().mkdirs();
                } catch (Exception e) {
                }

                try {
                    FileOutputStream outputStream = new FileOutputStream(dataFile);
                    outputStream.write(fileDataList.get(i));
                    outputStream.flush();
                    outputStream.close();
                    htmlBody.append("  <LI>" + dataFile.getAbsolutePath() + "</LI>" + Constants.EOL);
                } catch (IOException ioe) {
                    htmlBody.append("</UL>" + Constants.EOL);
                    ioe.printStackTrace();
                    slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(ioe));
                    throw new SLAMDServerException("Unable to write file " + dataFile.getAbsolutePath() + ioe, ioe);
                }
            }

            htmlBody.append("</UL>" + Constants.EOL);
            htmlBody.append("<BR><BR>" + Constants.EOL);
        }

        // Finally, parse the manifest to get the names of the classes that should
        // be registered with the SLAMD server.
        Attributes manifestAttributes = manifest.getMainAttributes();
        Attributes.Name key = new Attributes.Name(Constants.JOB_PACK_MANIFEST_REGISTER_JOBS_ATTR);
        String registerClassesStr = (String) manifestAttributes.get(key);
        if ((registerClassesStr == null) || (registerClassesStr.length() == 0)) {
            htmlBody.append("<B>No job classes registered</B>" + Constants.EOL);
        } else {
            ArrayList<String> successList = new ArrayList<String>();
            ArrayList<String> failureList = new ArrayList<String>();

            StringTokenizer tokenizer = new StringTokenizer(registerClassesStr, ", \t\r\n");
            while (tokenizer.hasMoreTokens()) {
                String className = tokenizer.nextToken();

                try {
                    JobClass jobClass = slamdServer.loadJobClass(className);
                    slamdServer.addJobClass(jobClass);
                    successList.add(className);
                } catch (Exception e) {
                    failureList.add(className + ":  " + e);
                }
            }

            if (!successList.isEmpty()) {
                htmlBody.append("<B>Registered Job Classes</B>" + Constants.EOL);
                htmlBody.append("<UL>" + Constants.EOL);
                for (int i = 0; i < successList.size(); i++) {
                    htmlBody.append("  <LI>" + successList.get(i) + "</LI>" + Constants.EOL);
                }
                htmlBody.append("</UL>" + Constants.EOL);
                htmlBody.append("<BR><BR>" + Constants.EOL);
            }

            if (!failureList.isEmpty()) {
                htmlBody.append("<B>Unable to Register Job Classes</B>" + Constants.EOL);
                htmlBody.append("<UL>" + Constants.EOL);
                for (int i = 0; i < failureList.size(); i++) {
                    htmlBody.append("  <LI>" + failureList.get(i) + "</LI>" + Constants.EOL);
                }
                htmlBody.append("</UL>" + Constants.EOL);
                htmlBody.append("<BR><BR>" + Constants.EOL);
            }
        }
    }

    /**
     * Reads the contents of the provided input stream into the given data array.
     * It will continue reading until the data array has been filled.
     *
     * @param  inputStream  The input stream from which to read the data.
     * @param  dataArray    The array into which the data should be placed after
     *                      it is read.
     *
     * @throws  IOException           If an I/O problem occurs while reading the
     *                                data from the input stream.
     * @throws  SLAMDServerException  If a problem occurs while trying to fill the
     *                                array with the data.
     */
    private void extractFileData(InputStream inputStream, byte[] dataArray)
            throws IOException, SLAMDServerException {
        int bytesRead = inputStream.read(dataArray);
        if (bytesRead == dataArray.length) {
            return;
        } else if (bytesRead < 0) {
            throw new SLAMDServerException("Unexpectedly reached the end of the " + "JAR entry input stream");
        }

        while (bytesRead < dataArray.length) {
            int bytesRemaining = dataArray.length - bytesRead;
            int moreBytesRead = inputStream.read(dataArray, bytesRead, bytesRemaining);
            if (moreBytesRead < 0) {
                throw new SLAMDServerException("Unexpectedly reached the end of the " + "JAR entry input stream");
            }

            bytesRead += moreBytesRead;
        }
    }
}