The following is a list of introductory steps to understand how to create a Loom application and start working with it. It is expected to take less than 10 minutes to complete.
First, download scaffolding.zip and gradle and unzip both files in separate folders. Set a GRADLE_HOME environment variable pointing to the gradle root dir, and (just to make it easier) add the gradle and scaffolding scripts to your system PATH (on linux, creating a symbolic link in /usr/local/bin should be enough).
Now, launch the following commands:
scaffolding create foobar cd foobar gradle eclipse
Sometimes gradle timeouts while waiting for maven repositories; in that case, just wait a little and repeat the command.
This will create a 'foobar' project with some demo action and persistent classes (if you are using Eclipse, the project includes launchers to debug your application inside an embedded tomcat/jetty). Notice that there is a build.gradle file. To deploy your project to your application server, execute the following.
Tomcat (Tomcat 6 is required):
set CATALINA_HOME=/path/to/your/tomcat/home gradle deploy-tomcatJetty (Jetty 6 is required):
set JETTY_HOME=/path/to/your/jetty/home gradle deploy-jetty
Loom does not require JSP 2.1, but scaffolding creates projects optimized to take advantage of the most advanced features in the new standard.
To test the application, open a browser at http://localhost:8080/foobar and check that everything works. The created project includes the following frameworks:
The project includes a default Action and persistent classes that showcase the typical usage. To create a new CRUD interface, open Eclipse and create a new model class:
package com.foobar.model;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;
@Entity
public class BlogEntry {
@Id @GeneratedValue
private Integer id;
private String subject;
private String contents;
private Date creationDate;
// ...getters and setters omitted for clarity...
}
Once it has been created, execute the following in your project root folder:
scaffolding crud com.foobar.model.BlogEntry
It will create a BlogEntryAction mapped at http://localhost:8080/foobar/blog-entry/ with all the Resolution methods needed for a CRUD (create, read, update and delete) interface. Its content should include the following structure:
public class BlogEntryAction extends AbstractAction {
@GET @Path("/")
@Event(defaultEvent=true)
public Resolution list() { ... }
@GET @Path("/create")
public Resolution create() { ... }
@GET @Path("/{blogEntry.id}")
public Resolution edit() { ... }
@POST @Path("/{blogEntry.id?}")
public Resolution save() { ... }
@DELETE @Path("/{blogEntry.id?}")
public Resolution delete() { ... }
}
Loom applications are based on the following concepts:
The scaffolding tool creates REST interfaces where specific http methods have been bound to events. Events are bound to GET and POST, unless specified otherwise. As you can see in the example above arguments can be bound to URIs or, if not specified, they will be added as normal GET/POST parameters.
The scaffolding tool will generate a JSP 2.1 tagfiles folder at WEB-INF/tags where the general page layout will be stored (a good starting point would be layout.tag). Any tagfile that is dropped there should be immediately available.
All JSP pages have been configured at web.xml to start with the contents of WEB-INF/taglibs.jspf to save you from repeating the taglib descriptors on every JSP page.
The following is a default JSP file:
<tags:layout>
<jsp:body>
<l:url title="cancel" action="BlogEntries" event="list"/>
<l:form action="BlogEntries" event="save">
<l:button />
<l:errors/>
<l:param property="entry.id"/>
<l:inputText name="entry.name"/>
<l:inputText name="entry.creationDate" disabled="true"/>
<l:inputCheckbox name="entry.starred" labelPos="left"/>
<l:inputSelect name="entry.country" options="${action.countries}" />
<l:inputTextArea name="entry.comments" cols="40"/>
</l:form>
</jsp:body>
</tags:layout>
Links are created using <l:url> tags, which can have nested parameters specified using <l:param>. This is a constant with all loom tags, be it links, menu items, forms... Everything that can create a link can have nested params.
Actions are referenced by their unqualified class name, without the "Action" suffix, if present. In this example the framework will search for a BlogEntriesAction class with "list" and "save" events. It should also contain a "entry" attribute with all these fields. User input validation such as number constraints, minimum and maximum length or type conversion will be automatically guessed.
All form input fields create a surrounding label by default. This can be overriden or just disabled, but it's usually quite convenient to have every component correctly labeled and translated according to your resources/messages.properties file (the default i18n file location).
Open your temp folder (%TEMP% if you are on a windows machine, /tmp if linux, $CATALINA_HOME/tmp if you are using tomcat) and look for a missing-resources.properties file that should look like this:
entry.name=entry.name name=name entry.country=entry.country country=country
These are the message resources that were not found, prepared to copy and paste into your properties file. When searching for management.admin.save, Loom will look for the entire key, then admin.save, then save. If nothing is found, it will generate a "missing" label and add the message to the missing-resources file.
We usually do not need long keys, so add the following lines to your messages.properties file and refresh your browser (restarting the server is not necessary):
name=Name country=Country
Loom is designed to validate user input using model annotations. It supports JPA, Hibernate and Loom. Just annotating your persistent attribute:
@NestedAnnotations(on="save") private BlogEntry entry;
This will hint Loom that it should look for annotations inside the BlogEntry class and apply the corresponding validators to the "save" event. Now, on to the BlogEntry class:
import javax.persistence.Column;
import org.loom.annotation.validation.StringValidation;
public class BlogEntry {
@Column(nullable=false, length=20)
private String subject;
@StringValidation(minLength=10, maxLength=100)
private String contents;
// ...etc...
}
If you reboot your server you will see that the web page has changed, including attributes about the expected length of each field. The user input will be validated at the browser using javascript, and again at the server using the same error messages and displaying the same HTML error components.
Notice that where possible the standard attributes are being used, like maxlength and size. Other information about your expected input, such as if they are numbers, dates or required, are being included either as CSS classes or HTML attributes, to the benefit of your own javascript code and style sheets.
As a final step, if you set Config.development to false in your spring xml file the system will switch into production mode, disabling on-the-fly reloading of properties files and enabling automatic concatenation of javascript and CSS files.
Definitely the best place to start is the demo application where most of the features are being used. To get some insight into some of the more advanced components (which is right now a work in progress), you can check the addons demo.