Package org.gomba

Source Code of org.gomba.LOBUpdateServlet

package org.gomba;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

/**
* Write a LOB to a JDBC data store. the SQL in the <code>query</code>
* init-param should be a SELECT that selects a LOB field. Currently LOBs are
* overwritten but not cleared, so previous data in the LOB that exceeds the
* length of the new data will not be overwritten. This servlet inherits the
* init-params of {@link org.gomba.AbstractServlet}, plus:
* <dl>
* <dt>http-method</dt>
* <dd>The value can be POST or PUT. (Required)</dd>
* <dt>column</dt>
* <dd>The result set column containing the BLOB or CLOB value. This init-param
* is required only if the result set contains more than one column. (Optional)
* </dd>
* <dt>update-query</dt>
* <dd>An SQL statement that writes the BLOB or CLOB to the db. The statement
* must contain one parameter of BLOB or CLOB type using the
* <code>${blob.myColumn}</code> syntax. This is required only if the JDBC
* driver does not support LOB in-place modification. (Optional)</dd>
* </dl>
*
* Note about HTTP method usage. The POST method is normally used for creation
* (INSERT in SQL) operations. The PUT method is normally used for update
* (UPDATE in SQL) operations.
*
* @author Flavio Tordini
* @version $Id: LOBUpdateServlet.java,v 1.8 2005/07/21 09:12:22 flaviotordini Exp $
*/
public class LOBUpdateServlet extends SingleQueryServlet {

    /**
     * The result set column name to render. May be null.
     */
    private String columnName;

    /**
     * <code>true</code> if this servlet supports the POST HTTP method.
     */
    private boolean supportPost;

    /**
     * <code>true</code> if this servlet supports the PUT HTTP method.
     */
    private boolean supportPut;

    /**
     * The parsed update query definition, if any.
     */
    private QueryDefinition updateQueryDefinition;

    /**
     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        // optional UPDATE query
        String updateQuery = config.getInitParameter("update-query");

        // check db/driver capabilities
        try {
            if (updateQuery == null && checkLocatorsUpdateCopy(getDataSource())) {
                throw new ServletException(
                        "The database does not allow in-place LOB modification. "
                                + "Updates made to a LOB are made on a copy and not directly to the LOB. "
                                + "The 'update-query' init-param must be specified.");
            }
        } catch (SQLException sqle) {
            throw new ServletException(
                    "Error checking if locators update copies.", sqle);
        }

        // parse the update query definition
        if (updateQuery != null) {
            try {
                this.updateQueryDefinition = new QueryDefinition(updateQuery);
            } catch (Exception e) {
                throw new ServletException(
                        "Error parsing update query definition.", e);
            }
        }

        // name of the column to write the LOB to
        this.columnName = config.getInitParameter("column");

        // supported HTTP method
        String httpMethod = config.getInitParameter("http-method");
        if (httpMethod == null) {
            throw new ServletException("Missing init-param: http-method");
        }
        if (httpMethod.equals("POST")) {
            this.supportPost = true;
        } else if (httpMethod.equals("PUT")) {
            this.supportPut = true;
        } else {
            throw new ServletException("Unsupported HTTP method: " + httpMethod);
        }
    }

    /**
     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
     *           javax.servlet.http.HttpServletResponse)
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        if (this.supportPost) {
            processRequest(request, response, false);
        } else {
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
    }

    /**
     * @see javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest,
     *           javax.servlet.http.HttpServletResponse)
     */
    protected void doPut(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        if (this.supportPut) {
            processRequest(request, response, false);
        } else {
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
    }

    /**
     * Get a reference to the LOB object and write the content of the request
     * body to it.
     *
     * @see org.gomba.AbstractServlet#doInput(java.sql.ResultSet,
     *           javax.servlet.http.HttpServletRequest, org.gomba.ParameterResolver)
     */
    protected void doInput(ResultSet resultSet, HttpServletRequest request,
            ParameterResolver parameterResolver, Connection connection) throws Exception {
       
        if (resultSet == null) {
            throw new Exception("Resultset is null.");
        }
       
        ResultSetMetaData rsmd = resultSet.getMetaData();
        if (rsmd.getColumnCount() != 1 && this.columnName == null) {
            throw new Exception("The resultset contains more than one column. "
                    + "You must set the 'column' init-param.");
        }

        // find out the column index
        final int columnIndex;
        if (this.columnName != null) {
            columnIndex = DatumServlet.getColumnIndex(rsmd, this.columnName);
        } else {
            columnIndex = 1;
        }

        final int columnType = rsmd.getColumnType(columnIndex);

        // write!
        switch (columnType) {

        case Types.BLOB:
        case Types.LONGVARBINARY:

            if (this.updateQueryDefinition == null) {

                // in-place modification
                // Get a reference to the blob
                Blob blob = resultSet.getBlob(columnIndex);
                if (blob == null) {
                    throw new Exception("BLOB value is null.");
                }

                // clear the BLOB. If we don't do this, previous data in the
                // BLOB that exceeds the length of the new data will not be
                // overwritten.
                // FIXME Not supported by MySQL
                // blob.truncate(0);

                // copy the request body to the BLOB
                writeBytes(request.getInputStream(), blob.setBinaryStream(1));

            } else {

                // use the update query (this is the most common situation:
                // MySQL, Oracle)
                Query updateQuery = new Query(this.updateQueryDefinition,
                        parameterResolver);

                // The blob object should be already in the parameters via the
                // BlobColumnDomain
                Blob blob = getBlob(updateQuery.getStatementParameters());
                if (blob == null) {
                    throw new Exception("Unable to get reference to a BLOB. "
                            + "Maybe the BLOB field is null on the db "
                            + "or the wrong column name has been specified.");
                }

                // clear the BLOB. If we don't do this, previous data in the
                // BLOB that exceeds the length of the new data will not be
                // overwritten.
                // FIXME Not supported by MySQL
                // blob.truncate(0);

                // write the request body to the blob
                writeBytes(request.getInputStream(), blob.setBinaryStream(1));
                // perform the update
                updateQuery.execute(connection);

            }
            break;

        case Types.CLOB:
        case Types.LONGVARCHAR:

            if (this.updateQueryDefinition == null) {

                // in-place modification
                // Get a reference to the CLOB
                Clob clob = resultSet.getClob(columnIndex);
                if (clob == null) {
                    throw new Exception("CLOB value is null.");
                }

                // clear the CLOB. If we don't do this, previous data in the
                // CLOB that exceeds the length of the new data will not be
                // overwritten.
                // FIXME Not supported by MySQL
                // blob.truncate(0);

                // copy the request body to the CLOB
                writeCharacters(request.getReader(), clob.setCharacterStream(1));

            } else {

                // use the update query (this is the most common situation:
                // MySQL, Oracle)
                Query updateQuery = new Query(this.updateQueryDefinition,
                        parameterResolver);

                // The blob object should be already in the parameters via the
                // BlobColumnDomain
                Clob clob = getClob(updateQuery.getStatementParameters());
                if (clob == null) {
                    throw new Exception("Unable to get reference to a CLOB. "
                            + "Maybe the CLOB field is null on the db "
                            + "or the wrong column name has been specified.");
                }

                // clear the CLOB. If we don't do this, previous data in the
                // CLOB that exceeds the length of the new data will not be
                // overwritten.
                // FIXME Not supported by MySQL
                // clob.truncate(0);

                // write the request body to the blob
                writeCharacters(request.getReader(), clob.setCharacterStream(1));
                // perform the update
                updateQuery.execute(connection);

            }
            break;

        default:
            throw new ServletException("Invalid SQL data type: " + columnType);
        }
    }

    /**
     * Write characters from a Reader to a Writer.
     */
    private static void writeCharacters(Reader reader, Writer writer)
            throws IOException {
        try {
            char[] buffer = new char[4096];
            int length;
            while ((length = reader.read(buffer)) >= 0) {
                writer.write(buffer, 0, length);
            }
        } finally {
            writer.close();
            writer.close();
        }
    }

    /**
     * Write bytes from an InputStream to an OutputStream.
     */
    private static void writeBytes(InputStream is, OutputStream os)
            throws IOException {
        try {
            byte[] buffer = new byte[4096];
            int length;
            while ((length = is.read(buffer)) >= 0) {
                os.write(buffer, 0, length);
            }
        } finally {
            is.close();
            os.close();
        }
    }

    /**
     * Check if the JDBC driver supports in-place modification of LOB locators.
     */
    private static boolean checkLocatorsUpdateCopy(DataSource dataSource)
            throws SQLException {
        Connection connection = dataSource.getConnection();
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            return metaData.locatorsUpdateCopy();
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }

    /**
     * Iterate thorugh the statement parameters and find a Blob. TODO make sure
     * there is only one Blob
     */
    private static Blob getBlob(List parameters) throws Exception {
        for (Iterator i = parameters.iterator(); i.hasNext();) {
            Object parameter = i.next();
            if (parameter instanceof Blob) {
                return (Blob) parameter;
            }
        }
        return null;
    }

    /**
     * Iterate thorugh the statement parameters and find a Clob. TODO make sure
     * there is only one Clob
     */
    private static Clob getClob(List parameters) throws Exception {
        for (Iterator i = parameters.iterator(); i.hasNext();) {
            Object parameter = i.next();
            if (parameter instanceof Clob) {
                return (Clob) parameter;
            }
        }
        return null;
    }

}
TOP

Related Classes of org.gomba.LOBUpdateServlet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.