Custom editor - FileUpload example

Custom editor - FileUpload example

Adding an entry to the editors map

First, we add an entry to the editorMap in the configuration of the editorService bean. We Add the following entry:

<entry>
    <key><value>propertyType.name == "[B"</value></key>
    <bean class="org.apache.tapestry.util.ComponentAddress">
        <constructor-arg index="0">
            <value>func:Editors</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>fileUploadEditor</value>
        </constructor-arg>
    </bean>
</entry>





The entry's key is <code>propertyType.name == "[B"</code>, which is an OGNL expression that evaluates to true if the property's type is a =byte[]=. NOTE: there might be better ways to do this, like using annotations to 'trigger' an editor. Adding an isBinary method to the trails PropertyDescriptor would be another possiblity, allowing us to just use binary as the key in the editorMap.

The entry's value is a ComponentAddress object, that specifies where the custom editor can be found. In this case, it's defined in the Editors component in the funcnamespace, which we'll define later. The id of the editor is =fileUploadEditor=. For more information on creating Tapestry components and component libraries, please consult the Tapestry users' guide.

Adding our custom library

Open the .application file in the WEB-INF directory of your Trails application and add a custom Tapestry library specification:

<library id="func" specification-path="/nl/func/component/func.library"/>





Define the func component library

Since we just added a reference to a custom library to the .application file, we should ofcourse actually create this library. Create the file func.library in the nl.func.component package. The contents look like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library-specification
      PUBLIC "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
      "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<library-specification>
    <description>The func library</description>
</library-specification>





As you see, this is an empty library: no custom components are created. To add our custom editor, we need a number of components: A custom Editors page holding our editor, and our custom FileUpload component. Change the library-specification in the file func.library like this:

<library-specification>
    <description>The func library</description>
    <library id="contrib" specification-path="/org/apache/tapestry/contrib/Contrib.library"/>
    <library id="trails" specification-path="/org/trails/component/trails.library"/>
    <component-type type="FileUpload" specification-path="FileUpload.jwc"/>
    <page name="Editors" specification-path="/nl/func/page/Editors.page" />
</library-specification>





We include references to the contrib and trails libraries here so our components can use any component from these libraries. Furthermore, we specify a FileUpload component and an Editors page.

Define the Editors component

Like many other Tapestry components, the Editors component consists of three parts: a java file, an HTML file and a page specification. The Editors components provides a central place to store our custom editors. The code for this component is copied from the Trails sources and adapted (i.e. stripped) to suit our needs. The HTML file (Editors.html in the package nl.func.page) defines a Tapestry Block component that is referenced by the editorMap in the applicationContext.xml:

<td jwcid="fileUploadEditor@Block" >
  <label><span jwcid="@Insert" value="ognl:descriptor.displayName" /></label>
  <span class="editor">
    <span jwcid="fileUpload"  />
  </span>
  <br/>
</td>





The page specification (nl/func/page/Editors.page) looks like this:

<!DOCTYPE page-specification PUBLIC
  "-//Apache Software Foundation//Tapestry Specification 4.0//EN"
  "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">
<page-specification class="org.trails.page.EditorBlockPage">
   <property name="model" />
   <property name="descriptor" />
   <property name="editPageName" />

    <component id="fileUpload" type="FileUpload" inherit-informal-parameters="no">
        <binding name="data">model[descriptor.name]</binding>
    </component>

</page-specification>





Finally, the Editors' java class is org.trails.page.EditorBlockPage, we don't need a custom class here.

Create the FileUpload component

Now we have all the building blocks in place to use our custom editor, the only thing that remains is actually defining our editor component. Again, this is a Tapestry component consisting of three parts: java, html and component specification. The nl/func/component/FileUpload.html looks like this:

<span jwcid="$content$">
    <input jwcid="upload@Upload" file="ognl:uploadFile" type="file" displayName="File"/>
    <input jwcid="@Submit" value="Upload" listener="listener:formSubmit"/>
</span>





nl/func/component/FileUpload.jwc contains the component specification:

<!DOCTYPE component-specification PUBLIC "-//Apache Software Foundation//Tapestry Specification 4.0//EN"
          "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">
<component-specification class="nl.func.component.FileUpload" allow-body="yes" allow-informal-parameters="yes" deprecated="no">
   <description>Dit wordt een mooie upload component, maar is nu nog een text input</description>

    <parameter name="data" required="yes" cache="yes" deprecated="no"/>
    <parameter name="model" default-value="container.model" required="no" cache="yes" deprecated="no"/>
</component-specification>





Finally, our java file is very simple; it only contains a listener method that is responsible for storing the uploaded file into the byte[] property:

public abstract class FileUpload extends PropertyEditor {

    public abstract IPropertyDescriptor getDescriptor();
    public abstract void setDescriptor(IPropertyDescriptor Descriptor);
    public abstract Object getModel();
    public abstract void setModel(Object Model);
    public abstract byte[] getData();
    public abstract void setData(byte[] Data);

    public abstract IUploadFile getUploadFile();


    public void formSubmit(IRequestCycle cycle) {
        if (getUploadFile() == null) {
            return;
        }

        InputStream inputStream = getUploadFile().getStream();
        try {
            byte[] data = IOUtils.toByteArray(inputStream);
            setData(data);
        } catch (IOException e) {
            // nuthin' ?
        }
    }
}





Labels

 
(None)