ModeShape with AS7 example application

In this tutorial we will show how to run ModeShape from within a Java EE application deployed on JBoss AS 7, as a back-end for a PrimeFaces GUI component (The FileUpload).

Installing ModeShape on JBoss AS 7

The first thing we will need to do is downloading the latest ModeShape AS7 kit (version 3.1.2.Final or later).

This kit is a ZIP archive that is intended to be unzipped directly into the AS7 installation. Doing so will not override any of the files in a standard AS7 installation.

 Once unzipped the ModeShape AS7 kit into your AS7 distribution you will get the following upgrades:

  • ModeShape schema (modeshape_1_0.xsd)
  • ModeShape modules (javax/jcr/, org/modeshape,org/hibernate/search-engine/4.1/,org/apache/lucene/3.5/)
  • ModeShape configuration (standalone-modeshape.xml)

Besides this, the kit also deploys the "modeshape-rest" web application as an exploded WAR file. This web application provides the RESTful API to all of the repositories and their workspaces. If you don't plan to use the RESTful API, simply undeploy it or remove it.

Finally, regarding security, one of the ModeShape modules contains two files (under modules/org/modeshape/main/conf) that define the users and roles for the default security domain used by ModeShape:

modeshape-users.properties
modeshape-roles.properties

You can edit these files to add users, or define your different security domain by defining for each user the ModeShape roles found in the modeshape-roles.properties file.

ModeShape in action !

Now let's test ModeShape with an AS7 application which uses PrimeFaces to pickup a file to be uploaded. In our case, instead of copying the file into the Server filesystem, we will upload it into the JCR repository.
Here's the sample upload.xhtml page: (Please refer to this tutorial for more information about this component)

 <html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui">

  <h:head></h:head>

  <h:body>
     <h:form id="form" >

        <p:fileUpload fileUploadListener="#{fileUploadController.upload}"
      allowTypes="/(\.|\/)(gif|jpe?g|png)$/" sizeLimit="100000" description="Select Images"/>

    </h:form>
    <h:message />
  </h:body>
</html>

And this is the FileUploadController ManagedBean which performs the Job:

package com.mastertheboss;
import java.io.*;
import java.util.*;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.jcr.*;
import org.modeshape.jcr.api.JcrTools;
import org.primefaces.event.FileUploadEvent;
 
@ManagedBean(name="fileUploadController")
public class FileUploadController {
   
   @Resource(mappedName="java:/jcr/sample")
   private Repository repository; 
   private JcrTools tools = new JcrTools();
   
    public void upload(FileUploadEvent event) {  
        String fileNamePath =  event.getFile().getFileName(); 
        String fileName = fileNamePath.substring(fileNamePath.lastIndexOf(File.separatorChar) + 1);
        
        try {
            copyFile(fileName, event.getFile().getInputstream());
        } catch (IOException e) {
            e.printStackTrace();
        }
        FacesMessage msg = new FacesMessage("Success! ", fileName + " is uploaded.");  
        FacesContext.getCurrentInstance().addMessage(null, msg);
        // Do what you want with the file      
    }  
    
    public void copyFile(String fileName, InputStream in) {
        String workspaceName = "default";
        Session session = null;
        try {
            session = repository.login(workspaceName);
            // Create the '/files' node that is an 'nt:folder' ...
            Node root = session.getRootNode();
    
            Node filesNode = root.addNode("files", "nt:folder");
                            
            InputStream stream = 
                    new BufferedInputStream(in);
            // Create an 'nt:file' node at the supplied path ...
            Node fileNode = filesNode.addNode(fileName,"nt:file");
            
 
            // Upload the file to that node ...
            Node contentNode = fileNode.addNode("jcr:content", "nt:resource");
            Binary binary = session.getValueFactory().createBinary(stream);
            contentNode.setProperty("jcr:data", binary);
            
            session.save();
                      
        } catch ( Exception e1) {
            e1.printStackTrace();
        } finally {
            // Always close the session ...
            if ( session != null ) session.logout()
        }           
    }

    public void copyFile2(String fileName, InputStream in) {
        String workspaceName = "default";
        Session session = null;
        try {
            session = repository.login(workspaceName);
            
            // Most of the above code could be replaced with a single call to
            // our JcrTools utility. This will create any missing intermediate
            // nodes (such as "/files" the first time this call is made)
            // using a primary type of "nt:folder". If the latter is not ideal,
            // you can use the "findOrCreateNode(...)" method to do the same            
            // but specify the primary type for the missing nodes.
            // This utility method will update the file node if it is already
            // there (rather than always create it).
            tools.uploadFile(session,"/files/" + fileName, stream);
            
            session.save();
                      
        } catch ( Exception e1) {
            e1.printStackTrace();
        } finally {
            // Always close the session ...
            if ( session != null ) session.logout()
        }           
    }
}

Two things to notice: the first is that we are injecting the JCR Repository directly in our application which avoids us lots of headaches (starting/stopping the engine)
 
   @Resource(mappedName="java:/jcr/sample")
   private Repository repository; 

Next, as you can see, there's one more copyFile2 method which shows how to performs uploading with a single call using the JcrTools utility class.
If you are curious to know where the sample JCR repository stores its data, just have a look at the modeshape subsystem:

 <local-cache name="sample">
                    <transaction mode="NON_XA"/>
                    <file-store relative-to="jboss.server.data.dir" path="modeshape/store/sample" passivation="false" purge="false"/>
 </local-cache>


As you can see this basic repository uses as file store the folder JBOSS_HOME/standalone/data and runs in NON_XA mode.
That's all. In order to compile and package your application, we need to include in our pom.xml, the required JBoss AS (and Java EE) + ModeShape + PrimeFaces dependencies and a couple of additional dependencies such as org.apache.commons and commons-fileupload which are required for running PrimeFaces' file uploader

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mastertheboss</groupId>
    <artifactId>modeshape-webapp</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>modeshape-webapp Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <properties>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <version.org.richfaces>4.2.0.Final</version.org.richfaces>

        <version.org.jboss.bom>1.0.0.Final</version.org.jboss.bom>

        <modeshape.version>3.1.1.Final</modeshape.version>
        <!-- other plugin versions -->
        <version.compiler.plugin>2.3.1</version.compiler.plugin>
        <version.surefire.plugin>2.4.3</version.surefire.plugin>
        <version.war.plugin>2.1.1</version.war.plugin>

        <!-- maven-compiler-plugin -->
        <maven.compiler.target>1.6</maven.compiler.target>
        <maven.compiler.source>1.6</maven.compiler.source>
    </properties>

    <dependencyManagement>
        <dependencies>

            <dependency>
                <groupId>org.jboss.bom</groupId>
                <artifactId>jboss-javaee-6.0-with-tools</artifactId>
                <version>1.0.0.M11</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            
            <dependency>
                <groupId>org.modeshape.bom</groupId>
                <artifactId>modeshape-bom-jbossas</artifactId>
                <version>3.1.0.Final</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- Import the Common Annotations API (JSR-250), we use provided scope 
            as the API is included in JBoss AS 7 -->
        <dependency>
            <groupId>org.jboss.spec.javax.annotation</groupId>
            <artifactId>jboss-annotations-api_1.1_spec</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.faces</groupId>
            <artifactId>jboss-jsf-api_2.1_spec</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.jcr</groupId>
            <artifactId>jcr</artifactId>
        </dependency>
        <!-- Directly depend on ModeShape's public API -->
        <dependency>
            <groupId>org.modeshape</groupId>
            <artifactId>modeshape-jcr-api</artifactId>
        </dependency>


        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>3.5</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>modeshape-webapp</finalName>
    </build>
    <repositories>

        <repository>
            <id>prime-repo</id>
            <name>PrimeFaces Maven Repository</name>
            <url>http://repository.primefaces.org</url>
            <layout>default</layout>
        </repository>

        <repository>
            <id>JBoss-Releases</id>
            <url>https://repository.jboss.org/nexus/content/repositories/releases/</url>
        </repository>
    </repositories>
</project>



Here's the application GUI in action:

modeshape jboss as7 tutorial
As you can see, once you have gone through some uploads, the filestore's binary subfolder gets crowded with the pictures you have uploaded.

modeshape jboss as 7 primefaces tutorial