CarrierWave: Getting Started Guide

About this guide

For the sake of brevity and simplicity, this guide won't walk through building an application. But will simply discuss the major components of CarrierWave and some of the tasks and decisions necessary to successfully deploy your own application.

Understanding CarrierWave

CarrierWave, out of the box, provides the following components:

You are responsible for the developing or generating these components:

When using one a persistence provider (besides the default InMemoryPersistenceSession), you will need to create schema files and mappings as required by the persistence tool.

Property Files

CarrierWave uses the PropertiesRoot class as the source for all runtime property values. PropertiesRoot by default scans all the .property files in the CARRIERWAVE_HOME/properties directory.

The default.properties file provides a means to configure the persistence session class name, repository root directory and default handler, the ClientSession class implementation, and JNDI context factory.

The InMemoryPersistenceSession provides a dirt simple persistence, with finder support. By default, persistence is turned off, but can be enabled by the inmemory.* properties.

The default repository handler is the FSRepositoryHandler. This handler provides a means to persist repository artifacts in the given directory.

ClientSession implementations provide the glue from client applications to the CarrierWave server. The EJBClientSession forwards all requests over the CommonClientSession bean. The optional LocalClientSession provides support for client and server components to live in the same process space (when deploying under Jetty, without EJB). Web service and JMS implementations are also possible, but not yet implemented.

Lastly, the JNDI context factory can be specified here.

The CommonClientSession Bean

When using the EJBClientSession implementation, the CommonClientSession bean needs to be registered with the EJB server. This bean can be found in the carrierwave-ejb.jar file found in the build directory. Or in the carrierwave.jar file found under the dist directory.

When using the carrierwave-ejb.jar, the rest of the CarrierWave class files need to be added to you CLASSPATH since the jar only holds the bean and bean home interfaces.

Tool Library

The tools for use during build-time are located in the build/tools directory, or the carrierwave-tools.jar file under the dist directory.

Currently there are two ant taskdefs defined, the first generates Image subclasses based on the source Imageable types. The second generates a type map, for mapping between Image and Imageable types during runtime.

The following ant build file snippet should be used as as prototype:

<target name="generated" depends="prepare,tools,compile-testobjects" unless="present.generated">

    <mkdir dir="${build}/generated"/>

    <codegen destDir="${build}/generated"
             applicationPackageRoot="com.vinculumtech.carrierwave"
             generatedPackageRoot="com.vinculumtech.carrierwave"
             aliasCommonInterface=""
             identifiableCommonClass="">
      <srcpath>
        <pathelement location="src/vendor"/>
        <pathelement location="src/server"/>
        <pathelement location="src/shared"/>
      </srcpath>
      <classpath>
        <pathelement location="${build}/tools"/>
        <path refid="classpath.compile.server"/>
      </classpath>
      <patternset>
        <include name="com/vinculumtech/carrierwave/finder/**/*.java"/>
        <exclude name="**/jdo/**" unless="enabled.jdo"/>
      </patternset>

    </codegen>

    <codegen destDir="${build}/generated"
             applicationPackageRoot="test.objects"
             generatedPackageRoot="test.generated"
             aliasCommonInterface="test.objects.BaseObject"
             identifiableCommonClass="test.objects.BaseObject">
      <srcpath>
        <pathelement location="src/server"/>
        <pathelement location="src/shared"/>
        <pathelement location="src/test"/>
      </srcpath>
      <classpath>
        <pathelement location="${build}/tools"/>
        <path refid="classpath.compile.server"/>
      </classpath>
      <patternset refid="patterns.codegen"/>
    </codegen>

  </target>

  <target name="compile-generated" depends="generated">

    <javac srcdir="${build}/generated"
           destdir="${build}/generated"
           debug="${debug}"
           deprecation="${deprecation}"
           optimize="${optimize}">
      <classpath>
        <pathelement location="${build}/shared"/>
	      <pathelement location="${build}/server"/>
        <pathelement location="${build}/test"/>
        <path refid="classpath.compile.server"/>
      </classpath>
    </javac>

  </target>

  <target name="typemap" depends="compile-generated">

    <typemap destDir="${build}/generated/"
             identifiableCommonClass="test.objects.BaseObject">
      <images dir="${build}/generated">
        <include name="**/*Image.class"/>
      </images>
    </typemap>

  </target>

Note that the Imageable types do not need to be compiled before the Image classes are generated, but the Codegen parser will fail in the same places javac will fail.

Creating Business Objects

Probably the simplest approach to integrating an existing application or creating a new one for use with CarrierWave, is to have all the "first class" business object types inherit from a common base type. For this example, we will call it BusinessObject. 

All that really needs to be done is have BusinessObject implement the ImgeableIdentifiable interface. This tells CarrierWave that all instances of BusinessObject have identity, and are durable until explicitly deleted by the system. Currently identity is assigned by the underlying persistence mechanism (PersistenceSession implementations), but can be created the object by implementing the PersistenceIdentifiable and PersistenceNamed interfaces.

For some real Imageable types in practice, see the JUnit tests under the src/tests directory.

Client Applications

A remote CarrierWave server is accessed via the ClientSession class. It acts as a factory returnnig specific versions of itself that know how to reach the CarrierWave server over RMI/EJB/JMS/etc. In this guide we are assuming the default EJBClientSession was specified in the default.properties file. And that we are using JBoss, and the JNDI context factory config in the default.properties file is also correct.

In your client code, to "open" a connection to the server, invoke the following code:

ClientSession clientSession = ClientSession.createClientSession( null );

Note that createClientSession() could take a Properties object with JNDI authentication values or whatever is required by the ClientSession implementation being used.

ClientSession is where a reference to the QueryClient and RepositoryClient are retrieved.

If we had created an ImageableIdentifiable type called BusinessObject, then ant would have spit out a BusinessObjectImage class.

To create a new BusinessObject on the CarrierWave server, simply invoke the following code:

QueryClient queryClient = clientSession.getQueryClient();
BusinessObjectImage image = new BusinessObjectImage();
Icon icon = queryClient.modifyImageableGraphFor( image );

This code sends the given Image and its namespace (ImageGraph) to the CarrierWave server and recieves in return the Icon instance referencing the new BusinessObject instance created by CarrierWave.

To retrieve a reflected version of our new BusinessObject as an Image, simply invoke the following code:

GraphPlan graphPlan = new GraphPlan( 1, true ); // depth, isFuzzy
BusinessObjectImage image = (BusinessObjectImage) queryClient.selectImageGraphFor( graphPlan, icon );

This code finds the BusinessObject identified by the returned Icon instance, and returns it and its children (if any) at a depth of 1 (one edge is traversed), and with the leaves as Icons not as images (isFuzzy).

Last Modified: 09/22/2003