My favorites | Sign in
Logo
                
Search
for
Updated Oct 19, 2009 by ff...@gmx.de
Labels: Featured
HoodGuide  
A guide to the hood example application

The project contains eclipse project files . So the easiest way to get an overview over hood is to just import it into Eclipse with "File / Import.. -> Existing Projects into Workspace".

Here is a little overview over the files and directories in the project. if you don't want to use eclipse you might like the ant build script provided.

WebContent
    WEB-INF
        config
            couchdb-config.xml      CouchDB server and database config
            couchdb-services.xml    CouchDBUpdater config that pushes the source views to
                                    the CouchDB server
            security-config.xml     Spring security
            services-config.xml     svenson JSON config, object services
            webmvc-config.xml       Spring WebMVC config. 
 
        designdocs                  source for CouchDB views
            ...
        lib                         runtime dependencies as jar files
            ...
        tags
            hood-fn.tld     Some JSP EL functions for hood
            link.tag        A link tag to avoid <c:url value=""/> for simple links
            page.tag        general page layout as JSP tag file
        views               contains the JSP views
            delObject.jsp   Delete Object?
            hood.jsp        main view
            login.jsp       login view
            newObject.jsp   new object view
            objects.jsp     non-JS view for objects
        web.xml             Servlet API web.xml file 
    image                   Images
        ... 
    index.jsp               redirects to the hood view
    script
        firebug-emu.js      emulates firebug if not present
        hood.js             main app script
        jquery-1.3.2.js     jquery 1.3.2
        json2.js            JSON api by Douglas Crockford (for those browser who don't do it natively)
    style                   CSS files
        ...
build               
    ...
build.properties        contains the version number
build.xml               ant build file
lib                     Testing and non-eclipse dependencies as jars
    ...
misc                    SVG source images for the Marker icons
    ...
src                     source code
    log4j.properties    Log4J configuration
    ...                 
test                    test source code
    ...
                                    

The project uses Spring WebMVC and Spring Security. The latter is currently not really useful, but was left in to show how it can be easily done.

Users not being interested in doing web applications might just want to look at the spring configs couchdb-config.xml, couchdb-services.xml and services-config.xml if they're interested in the non-web spring configuration. couchdb-services contains a spring configuration for the CouchDBUpdater that can sync view sources with CouchDB. See /hood/WebContent/WEB-INF/designdocs for an example of how to do it.

All other couchdb related code is is /hood/src/org/hood/HoodServiceImpl.java, /hood/src/org/hood/LocationServiceImpl.java and /hood/src/org/hood/auth/AppUserDetailsService.java

An important aspect of hood is the global JSON config. So far, the different parse path mappings required made this highly impractical, but with sub type mapping we can actually create a useful global JSONConfig as it is done by the class org.hood.JSONConfigFactory included in hood.

package org.hood;
import org.hood.domain.AppDocument;
import org.hood.domain.LatLongConverter;
import org.svenson.ClassNameBasedTypeMapper;
import org.svenson.JSON;
import org.svenson.JSONConfig;
import org.svenson.JSONParser;
import org.svenson.converter.DateConverter;
import org.svenson.converter.DefaultTypeConverterRepository;
import org.svenson.matcher.SubtypeMatcher;

/**
 * Creates a project global JSON config.
 * 
 * @author shelmberger
 *
 */
public class JSONConfigFactory
{
    public JSONConfig createJSONConfig()
    {
        JSONParser parser = new JSONParser();

        DefaultTypeConverterRepository typeConverterRepository = new DefaultTypeConverterRepository();
        typeConverterRepository.addTypeConverter(new DateConverter());
        typeConverterRepository.addTypeConverter(new LatLongConverter());
   
        // we use the new sub type matcher  
        ClassNameBasedTypeMapper typeMapper = new ClassNameBasedTypeMapper();
        typeMapper.setBasePackage(AppDocument.class.getPackage().getName());
        // we only want to have AppDocument instances
        typeMapper.setEnforcedBaseType(AppDocument.class);
        // we use the docType property of the AppDocument 
        typeMapper.setDiscriminatorField("docType");        
        // we only want to do the expensive look ahead if we're being told to
        // deliver AppDocument instances.        
        typeMapper.setPathMatcher(new SubtypeMatcher(AppDocument.class));

        parser.setTypeMapper(typeMapper);
        parser.setTypeConverterRepository(typeConverterRepository);

        JSON json = new JSON();
        json.setTypeConverterRepository(typeConverterRepository);

        return new JSONConfig(json, parser);
    }
}

The ClassNameBasedTypeMapper is using the new SubtypeMatcher that then triggers the decision process of the ClassNameBasedTypeMapper based on the previously mapped type hint. In Hood's case this is our application-specific base type org.hood.domain.AppDocument

package org.hood.domain;

import org.hood.JSONConfigFactory;
import org.jcouchdb.document.BaseDocument;
import org.svenson.JSONProperty;

/**
 * Base class for the documents in our app. Contains a read-only documentType / docType property on
 * which the {@link JSONConfigFactory} used to discriminate types.
 * 
 * @author shelmberger
 *
 */
public class AppDocument extends BaseDocument
{
    /**
     * Returns the simple name of the class as doc type.
     * 
     * The annotation makes it a read-only property and also shortens the JSON name a little.
     * @return
     */
    @JSONProperty(value = "docType", readOnly = true)
    public String getDocumentType()
    {
        return this.getClass().getSimpleName();
    }
}

The code above shows AppDocument. It implements a read-only JSON-Property that automatically prints the current instance's simple name as "docType" property. Since this is readonly, it only influences the JSON generation of AppDocument instances.

On parsing documents with our globally configured JSONParser decide the actual type to parse into based on exactly the same read-only property. When using the global config, we now can just convert AppDocuments into JSON and can automatically convert that JSON back into the correct AppDocument derived type (which we also enforce).

Full circle configs are useful in situations where you have objects that you need to represent on both the java side as well as the client side or in CouchDB. For other scenarios it might make sense to only support one direction of conversion, or it might be useful to define a JSONifier or implement JSONable and let it generate a JSON structure that is a convenient transformation from the Java side. Larger applications might even require multiple JSON/Java circles etc. It's all your choice, your design.

The JSONConfigFactory also registers two type converters that can be used by annotating JSON Bean methods with @JSONConverter

package org.hood.domain;

import org.svenson.JSONProperty;
import org.svenson.converter.JSONConverter;

public class PositionedDocument
    extends AppDocument
{
    private LatLon location;

    private String name;

    private String description;

    /** {@inheritDoc} */
    public LatLon getLocation()
    {
        return location;
    }

    /** {@inheritDoc} */
    @JSONProperty("loc")
    @JSONConverter(type = LatLongConverter.class)
    public void setLocation(LatLon location)
    {
        this.location = location;
    }


    /** {@inheritDoc} */
    public String getName()
    {
        return name;
    }

    /** {@inheritDoc} */
    public void setName(String name)
    {
        this.name = name;
    }


    public String getDescription()
    {
        return description;
    }


    public void setDescription(String description)
    {
        this.description = description;
    }

}

Here we see PositionedDocument, the base class for all Hood objects with location. the @JSONProperty renames the LatLong typed property to "loc" and the @JSONConverter method effects the LatLong objects being converted into arrays and arrays into LatLong objects.


Sign in to add a comment
Hosted by Google Code