Spring RooThe article Saving/Retrieving BLOB object in Spring Roo assumes the Reader is a Developer with some familiarity with Spring Roo. For introductory information about Spring Roo vist http://www.ibm.com/developerworks/java/library/os-springroo1/index.html?ca=drs-.

Project setup

Spring Roo brings alternatives for setting up a project: a) Entering project using a script file or b) Creating the database first and reverse engineering it using Roo DBRE commands.

For this article I am using option a), via an script file. Option b) is specially convenient for projects with an database already in place. I’ll vist Roo DBRE in a coming article in the near future.

Script File

project --topLevelPackage org.pragmatikroo.roodocman
persistence setup --provider HIBERNATE --database MYSQL --databaseName roodocman --userName <username> --password <password>
entity      --class ~.domain.Document
field string    --fieldName name        --notNull --sizeMax 30
field string    --fieldName description     --sizeMax 500
field string    --fieldName filename        --notNull
//field blob    --fieldName content     --notNull //Type not supported by Roo
field string    --fieldName contentType     --notNull
field numbert   --fieldName size -
controller all  --package ~.web
perform clean
perform eclipse

Notice content field is of type blob. This field would have to be entered manually into the entity class. You can use the IDE of your choice. I use STS. Either you use STS or the native Roo shell, the project would need to be synced by Roo after the change. More on this later.

Generate Entity Class

package org.pragmatikroo.roodocman;

@RooJavaBean
@RooToString
@RooEntity
public class Document {

    @NotNull
    @Size(max = 30)
    private java.lang.String name;

    @NotNull
    @Size(max = 500)
    private java.lang.String description;

    private java.lang.String filename;

    @NotNull
    @Lob
    @Basic(fetch = FetchType.LAZY)
    private byte[] content;

    private java.lang.String contentType;

    private java.lang.Long size;

    @Transient
    @Size(max = 100)
    private String url ;
}

Please review the seminal article for more information about the example entity fields. All highlighted code is manually added to the original file created by Roo. Why I added field size and url directly into the entity class?. I could have entered referred fields using the shell too. Right?. I did it in purpose to mentioned that Spring Roo is a round-trip tool. There are two more fields not shown in the entity class. They are the id and version fields, Roo automatically handle them using an ITD. Details about round-trip capabilities and how Roo uses ITDs, please visit Spring Roo website at http://www.springsource.org/roo. One more thing about the blob field. DBMS have different type of blobs. Verify that your project database is using the right one for your purposes.

As mention before: always make sure that Roo synced your latest changes. That would save you a lot of confusion.

It is a good time to deploy the application and verify that everything is working find to this point.

Required Source code changes

Well, to this point Spring Roo has done a lot for us. Properly setup the project. Created a Maven pom.xml with all required dependencies -well, almost; more on this below-. Generate all the code necessary for having a working web app. If this not enough, Roo is there in the background monitoring the project against any possible change.

With exception of the blob field -content- all the other fields are ready to go. Roo took care of them. In order to save/retrieve data from the blob field, we need to modify code in the client and in the server side for allowing file uploads.

Server Side Changes

Basically we need code that allows us to save/retrieve documents that include a blob field. The documents are entered using a web form. Typically Roo by default, generates client and server side code for having CRUD operations for every entity. I am not allowing updates in this version of the application. So, I disabled it, by setting the update=false in the @RooWebScaffold annotation. Please make sure next code is included in the DocumentController project file.

@RooWebScaffold(path = "documents", formBackingObject = Document.class, update=false)
@InitBinder
protected void initBinder(HttpServletRequest request,
                       ServletRequestDataBinder binder)
                   throws ServletException {
    binder.registerCustomEditor(byte[].class,       newByteArrayMultipartFileEditor());
    }
@RequestMapping(value="savedoc",  method = RequestMethod.POST)
public String createdoc(@Valid Document document,
                    BindingResult result,
                    Model model,
                    @RequestParam("content") MultipartFile content,
                            HttpServletRequest request) {
         
        document.setContentType(content.getContentType());
        document.setFilename(content.getOriginalFilename());
        document.setSize(content.getSize());
 
        log.debug("Document: ");
        log.debug("Name: "+content.getOriginalFilename());
        log.debug("Description: "+document.getDescription());
        log.debug("File: " +content.getName());
        log.debug("Type: "+content.getContentType());
        log.debug("Size: "+content.getSize());
        if (result.hasErrors()) {
            model.addAttribute("document", document);
            return "documents/create";
        }
        document.persist();
         
        return "redirect:/documents?page=1&amp;size=10" +             encodeUrlPathSegment(document.getId().toString(), request);
    }
     
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String show(@PathVariable("id") Long id, Model model) {
        Document doc = Document.findDocument(id);
        doc.setU("/documents/showdoc/"+id);
        model.addAttribute("document", Document.findDocument(id));
        model.addAttribute("itemId", id);
        return "documents/show";
    }
     
@RequestMapping(value = "/showdoc/{id}", method = RequestMethod.GET)
public String showdoc(  @PathVariable("id") Long id,
                    HttpServletResponse response,
                    Model model) {
   Document doc = Document.findDocument(id);
                 
   try {
          response.setHeader("Content-Disposition", "inline;filename="" +doc.getFilename()+ """);
 
          OutputStream out = response.getOutputStream();
          response.setContentType(doc.getContentType());
          IOUtils.copy( new ByteArrayInputStream(doc.getContent()),out);
            out.flush();
          
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

The code above allows CRUD operations with all the document fields including the blob. Very-very important: all this source code must be placed in the DocumentControler.java file to avoid been removed by Roo when the project is synced for changes.

Client Side Changes

On client side, I created a create custom form. Please replace the content of the create.jspx, with:

Create.jpsx Form

<form:multi id="fc_org_pragmatikroo_roodocman_domain_Document" modelAttribute="document" path="/documents/savedoc" render="${empty dependencies}" z="O7jiRGyhKQOUnBT8yJ9W4XU6VrQ=">
      <field:input field="name" id="c_org_pragmatikroo_roodocman_domain_Document_name" max="30" required="true" z="K3YncHn7F+HtBX/zgCZMYM6VO7k="/>
      <field:textarea field="description" id="c_org_pragmatikroo_roodocman_domain_Document_description" required="true" z="/RLpJWAf9zVjHtRapWIFve8JB8Y="/>
      <field:input field="filename" id="c_org_pragmatikroo_roodocman_domain_Document_filename" render="false" z="u4EclOEjQnID4g34VPv9zu9+qPo="/>
      <field:file field="content" id="c_org_pragmatikroo_roodocman_domain_Document_content" required="true" z="GZHlfc+o4h7EBA/SZ8/yXMVenOw="/>
      <field:input field="contentType" id="c_org_pragmatikroo_roodocman_domain_Document_contentType" render="false" z="jtq5/DHhBTgNImjac/d4AIfpCzA="/>
      <field:input field="size" id="c_org_pragmatikroo_roodocman_domain_Document_size" render="false" validationMessageCode="field_invalid_integer" z="ONDmhU5Eg5pw1j8LgTz9sXjOm6E="/>
      <field:textarea field="url" id="c_org_pragmatikroo_roodocman_domain_Document_url" render="false" z="bQ3FUVGL01nfWselK7WDPUD65Rw="/>
</form:multi>

As you can see, the form depends on two custom tagx files -multi.tagx and file.tagx- not included in the Spring Roo tag library. These two tags handle the blob field input.

spring-roo-blob-object

Show.jpx Form

<page:show id="ps_org_pragmatikroo_roodocman_domain_Document" object="${document}" path="/documents" update="false" z="J8X2O2T06x6jYb3WAyaNiTPGVZ0=">
      <field:display field="name" id="s_org_pragmatikroo_roodocman_domain_Document_name" object="${document}" z="VZEPJgXqYkqaHpDDPTcYr6DAjsM="/>
      <field:display field="description" id="s_org_pragmatikroo_roodocman_domain_Document_description" object="${document}" z="CeXQhSyEDeLn1Iu2hblanNbVc+A="/>
      <field:display field="filename" id="s_org_pragmatikroo_roodocman_domain_Document_filename" object="${document}" z="2HT3+zT1+1ft5kN4KhvXFWT9QnM="/>
      <field:display field="contentType" id="s_org_pragmatikroo_roodocman_domain_Document_contentType" object="${document}" z="FgCNUQ2KCygzKpUbP06Nxyjyfd8="/>
      <field:display field="size" id="s_org_pragmatikroo_roodocman_domain_Document_size" object="${document}" z="WdQ4wob2LThzMpucODZRa275TBc="/>
      <field:frame field="url" id="s_org_pragmatikroo_roodocman_domain_Document_url" object="${document}" render="true" z="user-managed"/>
</page:show>

This form depends on a custom tag frame.tagx. Basically is a iframe html for rendering images.

spring-roo-blob-show-form

spring-roo-blob-list-form

Little issue with a missed dependency: When you deploy the app -after the file upload changes- you will find a missing dependence error.

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.0.1</version>
</dependency>

Download Source

Click here to download source code (ZIP, 94kb).

Reference: viralpatel

Leave a Reply

Your email address will not be published. Required fields are marked *