Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

The following article describes and shows code for the basics of uploading attachments with Seam, and downloading those attachments.

To start with go ahead and 'seam create-entity' or with JBoss Tools right click on the project and "New -> Seam Entity" and name the Entity "Attachment". We'll only use part of the generated code, mainly only to create the files in the correct locations so you don't need to worry about it.

Next, lets modify the Attachment.java Entity and change it to:

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;

@Entity
public class Attachment implements java.io.Serializable {

	@Id
	@GeneratedValue
	private Long id;
	public Long getId() { return this.id; }
	public void setId(Long id) { this.id = id; }
	
	private String name;
	public String getName() { return this.name;	}
	public void setName(String name) { this.name = name; }
	
	private long size;
	public long getSize() { return this.size; }
	public void setSize(long size) { this.size = size; }
	
	private String contentType;
	public String getContentType() { return this.contentType; }
	public void setContentType(String contentType) { this.contentType = contentType; }
	
	@Lob
	@Column(length = 2147483647)
	@Basic(fetch = FetchType.LAZY)
	private byte[] data;
	public byte[] getData() { return this.data; }
	public void setData(byte[] data) { this.data = data; }
	
}

Now it will contain all of the basic fields to correspond with s:fileUpload.

Open attachment.xhtml and paste in:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:s="http://jboss.com/products/seam/taglib"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:rich="http://richfaces.org/rich" template="layout/template.xhtml">

	<ui:define name="body">

		<h:messages globalOnly="true" styleClass="message" />

		<h:form enctype="multipart/form-data">

			<rich:panel>
				<f:facet name="header">Upload Attachment</f:facet>

				<s:decorate id="fileUploadDecoration" template="layout/edit.xhtml">
					<ui:define name="label">Attachment</ui:define>
					<s:fileUpload id="file" 
						      data="#{attachmentHome.instance.data}"
						      contentType="#{attachmentHome.instance.contentType}"
						      fileName="#{attachmentHome.instance.name}"
						      fileSize="#{attachmentHome.instance.size}" />
				</s:decorate>
				
				<s:decorate id="nameDecoration" template="layout/display.xhtml">
					<ui:define name="label">Name</ui:define>
					<h:outputText value="#{attachmentHome.instance.name}"/>
				</s:decorate>
				
				<s:decorate id="contentTypeDecoration" template="layout/display.xhtml">
					<ui:define name="label">Content Type</ui:define>
					<h:outputText value="#{attachmentHome.instance.contentType}"/>
				</s:decorate>
				
				<s:decorate id="sizeDecoration" template="layout/display.xhtml">
					<ui:define name="label">Size</ui:define>
					<h:outputText value="#{attachmentHome.instance.size}"/>
				</s:decorate>
				
				<div style="clear: both" />
			</rich:panel>
			
			<div class="actionButtons">
				<h:commandButton value="Upload" 
						 action="#{attachmentHome.persist}"  
						 rendered="#{!attachmentHome.managed}"/>
				<h:commandButton value="Delete" 
						 action="#{attachmentHome.remove}"
						 immediate="true"  
						 rendered="#{attachmentHome.managed}">
					<s:conversationPropagation type="end" />
				</h:commandButton>
				<s:button propagation="end" 
                          id="done" 
                          value="Done"
                          view="/attachmentList.xhtml"/>
			</div>

		</h:form>

	</ui:define>

</ui:composition>

The main difference between the above code and what you would expect with a standard CRUD page is that the fields are outputText instead of inputText because the values are automatically determined by the s:fileUpload component. No code I have here is set in stone, feel free to modify as heavily as you need to, this is just an outline really.

Now, let's edit attachmentList.xhtml to display the properties of the uploaded file and allow for downloading.

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:s="http://jboss.com/products/seam/taglib"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:rich="http://richfaces.org/rich"
                template="layout/template.xhtml">
                       
	<ui:define name="body">
	    
	    <h:messages globalOnly="true" styleClass="message"/>
	    
	    <rich:panel>
	        <f:facet name="header">Files Uploaded</f:facet>
	        
	        <div class="results">
	        
	            <h:outputText value="No files exists" rendered="#{empty attachmentList.resultList}"/>
	                   
	            <rich:dataTable id="attachmentList" 
	            			 var="attachment"
	                      	 value="#{attachmentList.resultList}" 
	                   		 rendered="#{not empty attachmentList.resultList}">
	                <rich:column>
	                    <f:facet name="header">File Name</f:facet>
	                    <s:link value="#{attachment.name}" view="/attachment.xhtml">
	                        <f:param name="attachmentId" value="#{attachment.id}" />
	                    </s:link>
	                </rich:column>
	                <rich:column>
	                    <f:facet name="header">Content Type</f:facet>
	                    #{attachment.contentType}
	                </rich:column>
	                <rich:column>
	                    <f:facet name="header">Size(bytes)</f:facet>
	                    #{attachment.size}
	                </rich:column>
	                <rich:column>
	                    <f:facet name="header">Action</f:facet>
	                    <s:link value="Download" action="#{downloadAttachment.download}">
		                    <f:param name="attachmentId" value="${attachment.id}" />
		                </s:link>
	                </rich:column>
	            </rich:dataTable>
	            
	        </div>
	        
	    </rich:panel>
	    
	    <div class="actionButtons">
	        <s:button id="done" 
	               value="Create attachment"
	                view="/attachment.xhtml"/>			  
	    </div>
	    
	</ui:define>

</ui:composition>

As you can see there is a Component called 'downloadAttachment' referenced that we haven't created yet so lets do that. You can make this an EJB3 or a Seam pojo, the latter is easier so we'll do that. The DownloadAttachment component is the real meat and potatoes of this whole article, so pay attention:

@Name("downloadAttachment")
public class DownloadAttachment {
	
	@Logger
	private Log log;
	
	@In
	private EntityManager entityManager;
	
	@In(value="#{facesContext.externalContext}")
	private ExternalContext extCtx;
	
	@In(value="#{facesContext}")
	FacesContext facesContext;
	
	@RequestParameter
	private Long attachmentId;
	
	public String download() {
		Attachment attachment = entityManager.find(Attachment.class, attachmentId);
		HttpServletResponse response = (HttpServletResponse)extCtx.getResponse();
		response.setContentType(attachment.getContentType());
                response.addHeader("Content-disposition", "attachment; filename=\"" + attachment.getName() +"\"");
		try {
			ServletOutputStream os = response.getOutputStream();
			os.write(attachment.getData());
			os.flush();
			os.close();
			facesContext.responseComplete();
		} catch(Exception e) {
			log.error("\nFailure : " + e.toString() + "\n");
		}

		return null;
	}
}

And for the sake of completeness, here is the AttachmentHome.java and AttachmentList.java which required no changes:

import org.jboss.seam.annotations.Name;
import org.jboss.seam.framework.EntityQuery;

@Name("attachmentList")
public class AttachmentList extends EntityQuery
{
    @Override
    public String getEjbql() 
    { 
        return "select attachment from Attachment attachment";
    }
}
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.framework.EntityHome;

import org.domain.knowledgebase.entity.Attachment;

@Name("attachmentHome")
public class AttachmentHome extends EntityHome<Attachment> {

    @RequestParameter 
    Long attachmentId;
    
    @Override
    public Object getId() { 
        if (attachmentId==null) {
            return super.getId();
        } else {
            return attachmentId;
        }
    }
    
    @Override @Begin
    public void create() {
        super.create();
    }
 	
}

Now the only thing left to do is add exception handling for FileUploadExceptions and define the multipart filter in components.xml.

Add the following to pages.xml:

<exception class="org.jboss.seam.web.FileUploadException">
  <redirect view-id="/error.xhtml">
    <message>#{org.jboss.seam.handledException.message}</message>
  </redirect>
</exception>

And to components.xml:

xmlns:web="http://jboss.com/products/seam/web"
...
http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.0.xsd
...
<web:multipart-filter create-temp-files="true"
                      max-request-size="1000000" 
                      url-pattern="*.seam" />

Incorporating this into your own Webapp

If you want to make this seamless with your your own app you can rip out the form in the attachment.xhtml page and the dataTable in the attachmentList.xhtml page and embed that xhtml in any page you need, it should 'just work'.