Bug 38462

Summary: Allow direct File Upload in mailer2
Product: Taglibs Reporter: Karl-Heinz Sergel <serkom>
Component: Sandbox TaglibsAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED WONTFIX    
Severity: normal    
Priority: P2    
Version: 1.0   
Target Milestone: ---   
Hardware: All   
OS: All   

Description Karl-Heinz Sergel 2006-01-31 15:30:04 UTC
This nearly addresses the same issue as report #31869.
We 'd like to have the possibility to upload files and directly send them with 
mailer2, without including scriptlets.
We build a solution and ask if it would be a benefit to others.
What we've done is:
Expose the content of a multipart upload to the jsp via a tag 'upload' using 
the commons-fileupload library, wrapping the 'FileItem' to a DataSource.
Adding a new attribute 'upload' to the append-tag, which accepts a DataSource.
So sending a mail with an uploaded file is done by the lines:

<mt:upload var="upl"/>
<c:if test="${upl.file1Ok}"> 
  <mt:attach upload="${upl.file1Data}" />
</c:if>	

We've tested the build with the sandbox mailer2 build and it works.

I add the new classes, changes, example-jsp and requirements here:
1. Two new classes
------------------
UploadTag.java
------------------
package org.apache.taglibs.mailer2;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

import javax.activation.DataSource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;

/**
 * JSP tag <b>mt:upload</b> is used to construct the list of the FileItems from
 * the multipart response to be used for preparing the mail.
 * 
 * @author Karl-Heinz Sergel
 * @version 2.0
 * 
 * @jsp.tag name="upload" body-content="JSP" display-name="mt:upload"
 *          description="JSP tag <mt:pload> is used to extract uploaded file
 *          information to be included in an e-mail message. It is a standalone
 *          element.
 */

public class UploadTag extends BodyTagSupport {
  private static final String UPLOAD = "upload";
  private Logger  log       = Logger.getLogger(this.getClass().getName());
  
  /**
   * Default attributename for exposing upload multipart-values to the jsp.
   */
  private String  var       = UPLOAD;


  /**
   * indicates kind of upload
   */
  private boolean multipart = false;

  public UploadTag() {
    super();
    init();
  }

  public void init() {
    super.release();
    var = UPLOAD;
    multipart = false;
  }

  public int doEndTag() throws JspException {
    HashMap map = new HashMap();
    try {
      ServletRequestContext psrc = new ServletRequestContext(
          (HttpServletRequest) pageContext.getRequest());
      boolean isMultipart = FileUpload.isMultipartContent(psrc);
      if (isMultipart) {
        setMultipart(true);
        FileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        List items = upload.parseRequest(psrc);
        for (Iterator it = items.iterator(); it.hasNext();) {
          FileItem item = (FileItem) it.next();
          if (item.isFormField()) {
            map.put(item.getFieldName(), item.getString());
          } else {
            String baseName = item.getFieldName();
            map.put(baseName, item.getName());
            if (item.getSize() > 0) {
              DataSource wds = new WrappedFileItem(item);
              map.put(baseName + "Data", wds);
              map.put(baseName + "Ok", new Boolean(true));
              map.put(baseName + "Size", new Long(item.getSize()));
            } else {
              map.put(baseName + "Ok", new Boolean(false));
            }
          }
        }
      }
      if (var == null)
        var = UPLOAD;
      map.put("isMultipart", new Boolean(isMultipart));
      pageContext.setAttribute(var, map, PageContext.PAGE_SCOPE);
    } catch (Exception ex) {
      throw new JspException("ml:upload: error processing upload: ", ex);
    }
    return EVAL_PAGE;
  }

  public boolean isMultipart() {
    return multipart;
  }

  public void setMultipart(boolean multipart) {
    this.multipart = multipart;
  }

  public String getVar() {
    return var;
  }

  public void setVar(String var) {
    this.var = var;
  }
}

------------------
WrappedFileItem.java
------------------
package org.apache.taglibs.mailer2;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.activation.DataSource;

import org.apache.commons.fileupload.FileItem;

public class WrappedFileItem implements DataSource {
 
    private FileItem fileItem;

    public WrappedFileItem(FileItem fileItem) {
      this.fileItem = fileItem;
    }
    
    public String getContentType() {
      return fileItem.getContentType();
    }

    public InputStream getInputStream() throws IOException {
      return fileItem.getInputStream();
    }

    /**
     * this is nessessary because of the different behavier of the brousers. IE
     * returns the full pathname as filename, whereas firefox returns the
     * filename only, as needed.
     * 
     * 
     */
    public String getName() {
      File file = new File(fileItem.getName());
      return file.getName();
    }

    public OutputStream getOutputStream() throws IOException {
      return fileItem.getOutputStream();
    }
}

----------------------
2. Changes to AttachTag
----------------------
add Attribute
-------------
    private DataSource upload     = null;

-----------------
add Getter/Setter
-----------------
   public DataSource getUpload() {
        return upload;
    }
    
    /**
     * Set the DataSource for this  attachment.
     * @param DataSource of uploaded file to attach
     */
    public void setUpload(DataSource upload) {
        this.upload = upload;
    }

-------------------------
insert into doEndTag.java
-------------------------
...
        try {
          // This is to support the uploaded files khs 2006/01/31
          if (upload != null) {
            mbp.setDataHandler(new DataHandler(upload));
            localName = upload.getName();
            mbp.setHeader("Content-Type", upload.getContentType());
          }
          else if (filename != null) {
...

-----------------------------------
insert new tag into xml/mailer2.xml
-----------------------------------
            <tag>
                <description><![CDATA[JSP tag <mt:upload> is used to expose the 
uploaded content of the mulripart message to the jsp-page. <mt:upload> does not 
have any child tags.]]></description>
                <display-name>mt:upload</display-name>
                <name>upload</name>
                <tag-class>org.apache.taglibs.mailer2.UploadTag</tag-class>
                <body-content>empty</body-content>
                <availability>1.1</availability>
                <restrictions>None</restrictions>
                <attribute>
                    <description>The prefix of the exposed content. Defaults 
to 'upload'</description>
                    <name>var</name>
                    <required>no</required>
                    <rtexprvalue>yes</rtexprvalue>
                    <availability>1.1</availability>
                </attribute>
								<variables>
								
								</variables>
                <example>
                    <usage>
                        <comment>Example usage of the &lt;mt:upload&gt; 
tag.</comment>
                        <code><![CDATA[
The multipart content is exposed in two differnt forms.
${upl.upl.isMultipart} return true, if multipart content is exposed.
Ff false is returned, the fields are to be accessed via param.<filedname>

The content of normal form-fields can be accessed with
(given <mt:upload var="upl"/>)
${upl.<name of the field>}
The content of a file-filed be accessed with
${upl.<name of the field>} = browser retured name of the uploaded file,
${upl.<name of the field>Ok} = the DataSource is ready,
${upl.<name of the field>Size} = size in byte of the uploaded file,
${upl.<name of the field>Data} = a DataSource representation of the uploaded 
file,
${upl.<name of the field>Data.name} = the filename of the uploaded file,
${upl.<name of the field>Data.contentType} = the contentType of the uploaded 
file.

<mt:upload var="upl"/>
<c:if test="${upl.isMultipart == true}">
	<mt:attach upload="${upl.file1Data}"/>
</c:if>

]]></code>
                    </usage>
                </example>
            </tag>

-----------------------------------------------------
add new attribute for tag append into xml/mailer2.xml
-----------------------------------------------------
                <attribute>
                    <description>A upload file exposed with a previous 
&lt;mt:upload var="..."&gt;</description>
                    <name>upload</name>
                    <required>no</required>
                    <rtexprvalue>yes</rtexprvalue>
                    <type>javax.activation.DataSource</type>
                    <availability>1.1</availability>
                </attribute>

-----------------------------------
add sendUpload.jsp to examples
-----------------------------------
<!doctype html PUBLIC "-//W3C//DTD XHTML 1.0 
Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<%@ taglib prefix="mt" uri="http://jakarta.apache.org/taglibs/mailer2" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!-- ## expose the multipart content -->
<mt:upload var="upl"/>

<html>
<head>
<title>simple mail with upload file example using mt:send and mt:upload</title>
<link rel="stylesheet" type="text/css" href="mailer2.css"/>
<link rel="home" href="index.jsp"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>


<body>
<h1>simple mail with Upload example using mt:send and mt:upload</h1>
sending to "${email}" via "${server}".<br>


<!-- ## if we have an multipart upload, all must be set from var defined in the 
upload-tag -->
<c:choose>
	<c:when test="${upl.isMultipart == true}">
		<c:set var="feld1"  value="${upl.feld1}" scope="session"/>
		<c:set var="feld2"  value="${upl.feld2}" scope="session"/>
		<c:set var="email"  value="${upl.email}" scope="session"/>	
		<c:set var="server" value="${upl.server}" scope="session"/>
		<c:if test="${upl.file1Ok == true}" >
			<c:set var="file1" value="${upl.file1}" scope="page"/>
		</c:if>
	</c:when>
	<c:otherwise>
		<c:set var="feld1"  value="${param.feld1}" scope="session"/>
		<c:set var="feld2"  value="${param.feld2}" scope="session"/>
		<c:set var="email"  value="${param.email}" scope="session"/>	
		<c:set var="server" value="${param.server}" scope="session"/>
	</c:otherwise>
</c:choose>

<!-- ## for test purpose only -->
feld1 = ${feld1} <br>
feld2 = ${feld2} <br>
file1 = ${upl.file1} <br>
content = ${upl.file1Data} <br>
size = ${upl.file1Size} Byte <br>

<!-- ## test whatever seems nessessary -->
<c:if test="${not empty feld1}">
    <mt:mail 	
	var="message" from="${email}" 
	subject="upload File example using mt:send and mt:upload" 
	server="${server}" 
	background="false">
        <mt:addrecipient type="to" name="The User">${email}</mt:addrecipient>
        Hello World,
	<daten>
		<feld1>${feld1}</feld1>
		<feld2>${feld2}</feld2>
	</daten>
	<c:if test="${not empty file1}"> 
		<!-- access the name of the DataSource direct -->
		Der Inhalt des Uploads "${upl.file1Data.name}" als attachement 
(via DataSource)!<br>
        	<mt:attach upload="${upl.file1Data}" />
	</c:if>	

    </mt:mail>
    <mt:send message="${message}"/>

    <p>Mail sent to ${email}
	<c:if test="${not empty file1}">
		, attaching ${upl.file1}
	</c:if>

	</p>
</c:if>




<form method="post" action="#" enctype="multipart/form-data">
	<p>
	to be send:</br>	
    Email to:<input type="text" name="email" value="${sessionScope.email}" 
size="30"/><br>
    local Server: <input type="text" name="server" 
value="${sessionScope.server}" size="30"/><br>
    Feld1:: <input type="text" name="feld1" value="${feld1}" size="30"/></br>
    Feld2:: <input type="text" name="feld2" value="${feld2}" size="30"/></br>
    File1:: <input type="file" name="file1" value=""/></br>
    <p>
    <input class="button" type="submit" value="Send"/>
    </p>
</form>

<p>
<a href="index.jsp">Back</a>
</p>

</body>
</html>

-----------------------------------------
don't forget, we need new libs.
so add to build.properties at sandbox level
-----------------------------------------
# commons-upload required by mailer2
fileupload.jar=${basedir}/commons-fileupload-1.1.jar
# commons-io required by mailer2
commonsio.jar=${basedir}/commons-io-1.1.jar

-----------------------------------------
and check that at mailer2-level
add in build.xml at mailer2-level
first the classpath
-----------------------------------------
  <property name="classpath" 
	value="${servlet.jar}:${mail.jar}:${activation.jar}:${bcmail.jar}:${bcpr
ov.jar}:${fileupload.jar}"/>

-----------------------------------------
then at target 'examples.pre'
-----------------------------------------
    <copy todir="${build.examples}/WEB-INF/lib" file="${fileupload.jar}"/>
    <copy todir="${build.examples}/WEB-INF/lib" file="${commonsio.jar}"/>

-----------------------------------------
then at target 'checkRequirements.pre' add
-----------------------------------------

    <antcall target="checkRequiredFile">
       <param name="file" value="${fileupload.jar}"/>
       <param name="fail.message" value="Property fileupload.jar is not set in 
build.properties. This library is required to send upload file via mail 
attachments. get fileupload.jar from 
http://jakarta.apache.org/commons/fileupload/"/>
    </antcall>
    <antcall target="checkRequiredFile">
       <param name="file" value="${commonsio.jar}"/>
       <param name="fail.message" value="Property commonsio.jar is not set in 
build.properties. This library is required to send upload file via mail 
attachments. get commonsio.jar from http://jakarta.apache.org/commons/io/"/>
    </antcall>
Comment 1 Henri Yandell 2009-07-10 00:41:12 UTC
This taglib has been deprecated, so I don't expect to see this worked on.