struts-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Reinhard Nägele <reinhard.naeg...@mgm-edv.de>
Subject Re: RFC: BLOBAction
Date Mon, 20 Sep 2004 07:57:00 GMT
Frank,

I don't really like your approach and don't want to see such an Action 
added to a Framework like Struts. Here's a few comments why:

    * Struts is database-independent. People don't necessarily use plain
      JDBC.
    * You are getting anything you need for accessing your DB from the
      request, including SQL, username, password, i. e. it would
      probably have to be included plain text in some JSP. Bad thing...
    * I don't like JDBC in Actions. What about separating controller
      from business logic and database access?
    * Your approach is not very flexible. You may not always want to
      read the data completely into memory before writing it to the
      response.
    * BTW, it's a good idea to call response.reset() and, if possible,
      to set the content length before writing to the OutputStream.

Streaming files back to the reponse is really nothing special, and it's 
not exactly a Struts-specific thing. A simple Booch utitlity class would 
serve the purpose.

Reinhard


Frank W. Zammetti (MLists) wrote:

>One of the things I see asked very frequently on the Users mailing list is
>how to return PDF's and other BLOB fields from a database (or from a file
>system).  People either have trouble figuring out how to do it and require
>help, or have trouble making it work.
>
>Please find attached source for a new Action called BLOBAction that I
>submit to you all for comments and, perhaps eventually, inclusion in
>Struts as a built-in Action like ForwardAction and the like.
>
>Consider this proof-of-concept code, quality-wise... You can read my
>rather verbose comments on this, but in short... Right now it works by
>accepting a bunch of parameters submitted with the request.  This I feel
>is NOT the right approach.  I was hoping that Struts would read in unknown
>attributes for <action> mappings from struts-config.xml, but that does not
>seem to be the case.  I believe most, if not all, of these parameters
>should become attributes of the action mappings themselves, but this is
>one of the things I'm looking for feedback on.
>
>I am using this code (essentially this code... this is more generic
>though) in a production app here at work, so I know it works (aside from
>my tests here on this particular code which of course work).  I'm sure
>it's not ready as-is, but I don't think it's too bad either.
>
>I look forward to any comments you may have (first and foremost: is this
>even worth it?  I think it is, but I may be wrong on even that basic
>point!)
>
>  
>
>------------------------------------------------------------------------
>
>/*
> * ====================================================================
> *
> * The Apache Software License, Version 1.1
> *
> * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
> * reserved.
> *
> * Redistribution and use in source and binary forms, with or without
> * modification, are permitted provided that the following conditions
> * are met:
> *
> * 1. Redistributions of source code must retain the above copyright
> *    notice, this list of conditions and the following disclaimer.
> *
> * 2. Redistributions in binary form must reproduce the above copyright
> *    notice, this list of conditions and the following disclaimer in
> *    the documentation and/or other materials provided with the
> *    distribution.
> *
> * 3. The end-user documentation included with the redistribution, if
> *    any, must include the following acknowlegement:
> *       "This product includes software developed by the
> *        Apache Software Foundation (http://www.apache.org/)."
> *    Alternately, this acknowlegement may appear in the software itself,
> *    if and wherever such third-party acknowlegements normally appear.
> *
> * 4. The names "The Jakarta Project", "Struts", and "Apache Software
> *    Foundation" must not be used to endorse or promote products derived
> *    from this software without prior written permission. For written
> *    permission, please contact apache@apache.org.
> *
> * 5. Products derived from this software may not be called "Apache"
> *    nor may "Apache" appear in their names without prior written
> *    permission of the Apache Group.
> *
> * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
> * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
> * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
> * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
> * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> * SUCH DAMAGE.
> * ====================================================================
> *
> * This software consists of voluntary contributions made by many
> * individuals on behalf of the Apache Software Foundation.  For more
> * information on the Apache Software Foundation, please see
> * <http://www.apache.org/>.
> *
> */
>
>
>package org.apache.struts.action;
>
>
>
>import java.io.BufferedInputStream;
>import java.io.ByteArrayOutputStream;
>import java.io.File;
>import java.io.FileInputStream;
>import java.sql.Blob;
>import java.sql.Connection;
>import java.sql.DriverManager;
>import java.sql.ResultSet;
>import java.sql.Statement;
>import javax.naming.Context;
>import javax.naming.InitialContext;
>import javax.servlet.http.HttpServletRequest;
>import javax.servlet.http.HttpServletResponse;
>import javax.servlet.ServletOutputStream;
>import javax.sql.DataSource;
>
>
>/**
> * A BLOBAction can be used generically to return a BLOB, i.e., an image or a
> * PDF or anything else.  It can use a database or the file system as the
> * source of the BLOB.  Note that this version, which is basically a more
> * generic proof-of-concept of an Action I use in a productiona app already,
> * gets all its parameters from request.  This, I think, is *NOT* how it
> * should work, at least not exclusively.  I believe most, if not all, of the
> * parameters used here should actually be attributes of the ActionMapping.
> * Ideally, if the parameters are found in request them they would override
> * what's in the mapping, but having them JUST be request parameters I don't
> * think is the right answer.  I was hoping Struts would grab any attributes
> * it found for mappings whether it recognized them or not, but that doesn't
> * seem to be the case, hence I can't fully and properly implement this without
> * input from others.  So, for now, everything is request parameters.
> *
> * The following parameters are accepted (remember, most or maybe even all of
> * these I think should be optional attributes of <action>...
> *
> * contentType .. This is the content type of the returned response.  Values
> * like "image/gif", "image/jpg" or "application/pdf" are typical.
> *
> * dataSource .......... Either the value "database" or "file".  Determines
> *                       whether the BLOB is served from a database or the file
> *                       system (probably the local file system, but could be a
> *                       mapped drive or remotely mounted volume too.)
> * dbConnectionSource .. Only applicable when dataSource="database".  Either
> *                       the value "create" or "jndi".  Use "create" when you
> *                       want the database connection to be created with each
> *                       invocation of this Action.  Use "jndi" to get the
> *                       connection from JNDI (probably a connection pool).
> * dbDriverClass ....... Only applicable when dataSource="database".  This is
> *                       the JDBC driver class to use to access the database.
> * dbURL ............... Only applicable when dataSource="database".  This is
> *                       the connection string used to connect to the database.
> *                       This is vendor-specific!
> * dbUsername .......... Only applicable when dataSource="database".  Username
> *                       used to connect to the database.
> * dbPassword .......... Only applicable when dataSource="database".  Password
> *                       for the user specified by username, used to access the
> *                       database.
> * dbTable ............. Only applicable when dataSource="database".  This is
> *                       the name of the table in the database that the BLOB is
> *                       coming from.
> * dbField ............. Only applicable when dataSource="database".  This is
> *                       the field in the table specified by dbTable that the
> *                       BLOB is coming from.
> * dbQuery ............. Only applicable when dataSource="database".  This is
> *                       the SQL in the WHERE clause used to retrieve the BLOB
> *                       from the database.
> * flFullPath .......... Only applicable when dataSource="file".  This is a
> *                       fully-qualified filename, including path.  Note that
> *                       this is NOT relative to the container, which might be
> *                       a security concern (although I view it as a powerful
> *                       capability... I think it's definitely a security
> *                       concern when all these parameters are passed on the
> *                       URL, but it shouldn't be an issue when they are
> *                       included in the <action> mappings).
> *
> * Sample usages:
> *
> * >> Serve from a database:
> * Assume you have an app server running on the local machine on port 8181,
> * and a test app installed called "blobtest" using an extension of .bt.
> * Further assume you have an action mapping in struts-config.xml pointing to
> * this action under the name BLOBServer.bt.  Further still, assume
> * you have an Oracle database on a remote machine called
> * "dbsrvr" on listening on port 1521 with a schema named "schm1".  Even
> * further assume you have a user "theusr" set up in the database with a
> * password of "mypw123".  Lastly, assume you have a table named "logos" with
> * two fields, "logo" and "logoid" and you want to return the image stored in
> * the BLOB field "logo" where logoid=153.  You are NOT using JNDI to get a
> * connection to the database, you want to create a connection on each
> * invocation of this Action.  All that assumed, invoke this
> * action with the following URL to test it (i.e., as the src of an <img>
> * tag perhaps)...
> *
> * http://localhost:8181/toa/BLOBServer.bt?dataSource=database&
> * dbConnectionSource=create&contentType=image/gif&dbTable=logos&dbField=logo&
> * dbQuery=logoid=153&dbDriverClass=oracle.jdbc.driver.OracleDriver&
> * dbURL=jdbc:oracle:thin:@cdp03d:1521:utoa&dbUsername=utoa&dbPassword=utoatest
> *
> *
> * >> Serve from the file system:
> * Assume you have an image named test.gif in the directory c:\temp (on a
> * Windows system obviously)...
> *
> * http://localhost:8181/toa/BLOBServer.toa?dataSource=file&
> * flFullPath=c:\temp\test.gif
> *
> *
> * @author    <a href="mailto:frank@zammetti.com">Frank W. Zammetti</a>
> * @version   .1
> * @date      September 17, 2004
> */
>public class BLOBAction extends Action {
>
>
>  /**
>   * This usual execute method
>   *
>   * @param  mapping   Action mapping
>   * @param  form      ActionForm
>   * @param  request   HTTP Request as passed in to execute
>   * @param  response  HTTP Response as passed in to execute
>   * @return null      No return, response is rendered fully from here
>   * @throws Exception Exception
>   */
>  public ActionForward execute(ActionMapping mapping, ActionForm form,
>                               HttpServletRequest request,
>                               HttpServletResponse response) throws Exception {
>
>    // Get the parameter telling us the source of our BLOB as well as what
>    // the content type should be
>    String dataSource  = (String)request.getParameter("dataSource");
>    String contentType = (String)request.getParameter("contentType");
>
>    // Determine whether the BLOB is coming from a database or the file
>    // system and call the appropriate method, which returns an array of
>    // bytes that we'll then render to the output stream
>    byte[] bytes = null;
>    if (dataSource.equalsIgnoreCase("database")) {
>      bytes = serveFromDatabase(request);
>    }
>    if (dataSource.equalsIgnoreCase("file")) {
>      bytes = serveFromFileSystem(request);
>    }
>
>    // Get ready to output
>    ServletOutputStream out = response.getOutputStream();
>
>    // Set content type and output to stream
>    response.setContentType(contentType);
>    ByteArrayOutputStream ba = new ByteArrayOutputStream();
>    ba.write(bytes, 0, bytes.length);
>    ba.writeTo(out);
>    out.flush();
>
>    // Not forwarding anywhere
>    return null;
>
>  } // End process()
>
>
>  /**
>   * This method is called to return the BLOB as a byte array from a database
>   *
>   * @param  request   HTTP Request as passed in to execute
>   * @return byte[]    Byte array of the BLOB data
>   * @throws Exception Exception
>   */
>  private byte[] serveFromDatabase(HttpServletRequest request)
>                                   throws Exception {
>
>   // Get the parameters from request specific to a database source
>    String dbTable = (String)request.getParameter("dbTable");
>    String dbField = (String)request.getParameter("dbField");
>    String dbQuery = (String)request.getParameter("dbQuery");
>
>    // Set up for the query
>    String     sql  = "select " + dbField + " from " + dbTable +
>                      " where " + dbQuery;
>    Connection conn = getDBConnection(request);
>
>    // Do the query and get results
>    Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
>                                          ResultSet.CONCUR_UPDATABLE);
>    ResultSet rs   = stmt.executeQuery(sql);
>    Blob      blob = null;
>    while (rs.next()) {
>      blob = rs.getBlob(dbField);
>    }
>
>   // Clean up after ourselves
>    rs.close();
>    conn.close();
>
>    // Return our BLOB
>    return blob.getBytes(1, (int)(blob.length()));
>
>  } // End serveFromDatabase
>
>
>  /**
>   * This method will return a Connection object to a database when a
>   * database is the BLOB data source
>   *
>   * @param  request    HTTP Request as passed in to execute
>   * @return Connection Database connection
>   * @throws Exception  Exception
>   */
>  private Connection getDBConnection(HttpServletRequest request)
>                                     throws Exception {
>
>    // Determine whether we're creating a connection manually or getting one
>    // from a JNDI lookup
>    String dbConnSource = (String)request.getParameter("dbConnectionSource");
>
>    // Create the connection, or get it from JNDI
>    Connection conn = null;
>
>    if (dbConnSource.equalsIgnoreCase("create")) {
>
>      // We're creating the database connection here, so get the required
>      // parameters from request and do it
>      String dbDriverClass = (String)request.getParameter("dbDriverClass");
>      String dbURL         = (String)request.getParameter("dbURL");
>      String dbUsername    = (String)request.getParameter("dbUsername");
>      String dbPassword    = (String)request.getParameter("dbPassword");
>      Class.forName(dbDriverClass);
>      conn = DriverManager.getConnection(dbURL, dbUsername, dbPassword);
>
>    } else if (dbConnSource.equalsIgnoreCase("jndi")) {
>
>      // We're getting the database connection from JNDI, easy enough!  The
>      // only extra thing we need is the JNDI context to use.. I found that
>      // under WebSphere you have to look up in InitialContext, while under
>      // Tomcat it's under java:comp/env.  I very much assume I either don't
>      // know something about JNDI or am doing something wrong, so I look
>      // forward to anyone that can give me the right answer!  For now though,
>      // this code seems to do the trick.
>      String jndiContext = (String)request.getParameter("jndiContext");
>      String jndiIdentifier = (String)request.getParameter("jndiIdentifier");
>      InitialContext jndiCntx = new InitialContext();
>      Context ctx = null;
>      if (jndiContext == null || jndiContext.equalsIgnoreCase("")) {
>        ctx = jndiCntx;
>      } else {
>        ctx = (Context)jndiCntx.lookup(jndiContext);
>      }
>      DataSource ds = (DataSource)ctx.lookup(jndiIdentifier);
>      conn =  ds.getConnection();
>
>    } // End dbConnSource if
>
>    return conn;
>
>  } // End getDBConnection()
>
>
>  /**
>   * This method is called to return the BLOB as a byte array from the
>   * file system
>   *
>   * @param  request   HTTP Request as passed in to execute
>   * @return byte[]    Byte array of the BLOB data
>   * @throws Exception Exception
>   */
>  private byte[] serveFromFileSystem(HttpServletRequest request)
>                                     throws Exception {
>
>    // Get the parameters from request specific to a database source
>    String flFullPath = (String)request.getParameter("flFullPath");
>
>    // Read in the file to a byte array
>    File                f    = new File(flFullPath);
>    FileInputStream     istr = new FileInputStream(f);
>    BufferedInputStream bstr = new BufferedInputStream(istr);
>    int                 size = (int)f.length();
>    byte[]              data = new byte[size];
>    bstr.read(data, 0, size);
>    bstr.close();
>
>    // Return the BLOB data
>    return data;
>
>  } // End serveFromDatabase
>
>
>} // End class
>  
>
>------------------------------------------------------------------------
>
>---------------------------------------------------------------------
>To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
>For additional commands, e-mail: dev-help@struts.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Mime
View raw message