15 lovely features you don't get from other J2EE frameworks

Besides the type (Java or config files)-and-click (refresh button) development style, Active Record, and routes, here are a list of features developers love in Scooter framework.

  1. Ajax
  2. Flexible rendering APIs
  3. One-click routes list
  4. SQL Window
  5. Data browser
  6. File browser
  7. Named SQL
  8. Code generator
  9. Code stats
  10. Friendly time in your locale
  11. Automcatically logging who did what in your web app
  12. Wiki support
  13. Built-in methods: cycle(), flash()
  14. Pagination
  15. HTTP request priority for I18N

Ajax

Generate an Ajax-backed CRUD post module of the Blog application:

> java -jar tools/generate.jar blog scaffold-ajax post

Browse the below URL to add/show/edit/delete a post or even pagination without leaving the page:

http://localhost:8080/blog/posts

Converting a non-Ajax HTTP link to an Ajax link by using data-ajax="true":

<a href="http://localhost:8080/blog/time/current">Show Time</a>

Here is the same link backed by Ajax:

<a href="http://localhost:8080/blog/time/current" data-ajax="true" data-target="#time">Show Time</a>
<div id="time">show time here</div>

Converting a non-Ajax HTTP form to an Ajax form by using data-ajax="true":

<form action="http://localhost:8080/blog/time/current" method="GET">
...
</form>

Here is the same form backed by Ajax:

<form action="http://localhost:8080/blog/time/current" method="GET" data-ajax="true" data-target="#time">
...
</form>
<div id="time">show time here</div>

For more details, see Ajax.

[top]

Flexible rendering APIs

Render APIs are automatically available when static import the ActionControl class:

import static com.scooterframework.web.controller.ActionControl.*;

Now you can render result in an action method of a controller in multiple ways:

public class PostsController {
    public String show() {
        ActiveRecord post = Post.findById(p("id"));

        //
        //Use one of the return type below to render the post object:
        //

        //render object post, and let requester to define return format.
        //  /posts/10      => return post content as html
        //  /posts/10.json => return post content as json
        //  /posts/10.text => return post content as text
        //  /posts/10.txt  => return post content as text
        //  /posts/10.xml  => return post content as xml
        return render(post);

        //render object post and always return result in html format
        //Here the request extension has no impact on response format
        return render(post, "html");

        //render object post and always return result in text format
        //Here the request extension has no impact on response format
        return render(post, "text");

        //render object post and always return result in xml format
        //Here the request extension has no impact on response format
        return render(post, "xml");


        setViewData("post", post);

        //
        //Use one of the return type below to render a view for the post:
        //

        //render view file views/posts/show.jsp
        return renderView("show");

        //render view file .../WEB-INF/views/posts/show.jsp
        return renderView("posts/show");

        //render view file /home/foo/templates/show.st with StringTemplate engine
        return renderView("/home/foo/templates/show.st");

        //render view file file .../WEB-INF/views/posts/show.st and return xml
        return renderView("show.st", "xml");

        //render view file show.jsp and return result in text format
        return renderView("show", "text");

        //return a view coded in String Template as html
        return renderView("paged_list.st", "html", map);

        //return a view coded in FreeMarker Template as text
        return renderView("paged_list.ftl", "text", map);
    }
}

For more details, see Render A View.

[top]

One-click routes list

You can easily view all routes currently supported by your application by clicking on the Routes link on your Home page or using the link such as below:

http://localhost:8080/blog/routes

The priority of a route is linked with its position on the routes list. The higher the priority, the higher it appears in the list.

Developers can immediately view changes in the routes when updating the config/routes.properties file.

For example, if you want to use /blabla instead of /posts as url style in your blog web site, you just add the following in the config/routes.properties file:

resources.name.posts=path_alias:blabla

Save your changes, and then refresh the routes link page. You will get a complete new set of routes for posts.

[top]

SQL Window

SQL Window allows you to run ad-hoc SQL queries.

Just select a database, choose a retrieval limit, enter a SQL query, and click on Run button.

SQL Window

SQL Window is built on top of Scooter's AJAX and SQL Data Express (SDE).

[top]

Data browser

Data browser is another lovely link on the default home page of a Scooter web application. It allows you to CRUD your database records in a RESTful way.

Url for listing all databases in a web app named twitterdemo:

http://localhost:8080/twitterdemo/databases

Url for listing all tables in a database named twitterdemo_development:

http://localhost:8080/twitterdemo/databases/twitterdemo_development

Url for listing all records in a table named tweets:

http://localhost:8080/twitterdemo/databases/twitterdemo_development/tables/tweets/records

Url for listing one single record in a table named tweets:

http://localhost:8080/twitterdemo/databases/twitterdemo_development/tables/tweets/records/2

Url for editing a record:

http://localhost:8080/twitterdemo/databases/twitterdemo_development/tables/tweets/records/2/edit

It is so powerful and cheap (free!) that some developers feel that they don't need TOAD anymore.

[top]

File browser

File Browser allows you to browse/copy/delete/edit/rename/replace files and directories on the web server.

Display content of a Java source file WEB-INF/src/blog/models/Post.java:

http://localhost:8080/blog/admin/files/show?f=/WEB-INF/src/blog/models/Post.java

With File Browser, developers can remotely modify Java source files or other properties files, css files or Javascript files with a browser.

Post model

[top]

Named SQL

Sometimes there is nothing more happy than to be able to use sql queries directly.

You simply put them in config/sql.properties file. Give each query a unique name and then invoke it by using the name in your Java code.

    //
    // retrieve records based on named sql: sqlKey maps to a sql statement in sql.properties file.
    //

    //in model class Pet.java:
    public static TableData getAllPets() {
        //maps to "getAllPets=select * from pets"
        return SqlServiceClient.retrieveTableDataBySQLKey("getAllPets");
    }

    public static TableData getWholeWorld() {
        //maps to "getWorldKey=select a.*, b.*, c.*, d.* from a, b, c, d where ..."
        return SqlServiceClient.retrieveTableDataBySQLKey("getWorldKey");
    }

Due to the dynamic nature of Scooter framework, you can edit/save your sql query and refresh browser to see results immediately.

See SQL Data Express for more details.

[top]

Code generators

You can use code generator to generate controller, model or even a complete scaffold CRUD code.

java -jar tools/generate.jar scaffold post

You can use code generator to generate a complete login module:

java -jar tools/generate-signon.jar

[top]

Code stats

Sometimes you want to know how many lines of code you have written.

You can easily do so by using this command:

java -jar tools/codestats.jar src

It will output something like this:

C:\projects\twitterdemo>java -jar tools/codestats.jar src
-------------------------------------
                code    total   files
-------------------------------------
java            163     266     7
-------------------------------------
summary         163     266     7

Then you know that it only takes 163 lines of Java code to write Twitter sample application.

[top]

Friendly time in your locale

You don't need to confuse your users with this kind of time anymore: 10-08-09 13:54:57.861. Does it mean year 2010 or 2008 or 2009? Which month, October, August or September?

If you use the following method, you can display time in a more friendly way:

<%=D.message(date)%>

Here are some output examples:

  • moments from now
  • 5 minutes from now
  • 1 hour ago
  • 2 weeks ago

Notice that plural nouns are properly handled.

If you want the display in your own locale, just update the friendlytime block in a message properties file under config/locales and refresh browser.

[top]

Automcatically logging who did what in your web app

How many times were you asked by your boss who crashed your web server?

This could be very hard to figure out in many situations. Not anymore with Scooter.

For every login user, Scooter automatically add his/her username in the log message surrounded by brackets such as follows:

2010/01/23 01:42:15,554  INFO  ScooterRequestFilter - [John] "POST /petclinic/pets": 20 ms

Apparently user John just added a new pet in our RESTful petclinic application.

You don't need extra effort to achieve this. Just declare a log field in your class and log a message as you normally would do with Log4J:

import com.scooterframework.common.logging.LogUtil;

public class DataController {
    protected LogUtil log = LogUtil.getLogger(getClass().getName());

	public String status() {
	    log.debug("invoking status()");
	    return ...
	}
}

If the status() action is only available for logged-in users, you may see something like the following in your log file.

2009/08/20 14:02:04,068 DEBUG DataController - [joe] "invoking status()": 1 ms

Here joe is the username for the logged-in user. Now you can monitor who has called the status action.

By the way, if your controller class extends ActionControl class, which is a good practice, you can use the log field directly without declaring it.

[top]

Wiki support

Scooter includes MarkDown as engine of wiki.

Displaying a message in wiki is easy. For example, if your wiki is like this: ## Header 2 ##, you can use the following to parse the message:

<%=W.markdown("## Header 2 ##")%>

and you will see result as follows:

<h2>Header 2</h2>

[top]

Built-in methods

cycle()

This is a nice feature adopted from Ruby on Rails.

cycle() method allows you to assign a different css class to a row in a loop.

For example, the following is what we use when displaying a list of routes.

<tr class="<%=W.cycle("odd, even")%>">

Here both odd and even are tags which define different colors in style sheet file.

You can actually use more colors as follows:

<tr class="<%=W.cycle("odd, even, neutral")%>">

[top]

flash(), flashNotice(), flashError()

This is another nice feature adopted from Ruby on Rails.

flash() method allows you to pass information to the next request.

To use it, you simply make your Controller class be a subclass of ActionControl class which comes with many helper methods for a controller.

Then you can use it in your own controller like the following:

flash("notice", "Successfully created a record.");
flashNotice("Successfully created a record.");

This will display a message on the next screen as follows:

Successfully created a record.

[top]

Pagination

Scooter comes with handy support of pagination link display. It supports both Yahoo Mail Inbox style pagination and Digg style pagination.

Here is how you can use them.

In your controller class, PostsController for example, add the following:

    public String index() {
        if ("true".equals(params(Constants.PAGED))) {
            Paginator page = jdbcPaginator(Post.class, params());
            storeToRequest("post_page", page);
            return forwardTo(viewPath("paged_list"));
        }
        storeToRequest("posts", findAll(Entry.class));
        return null;
    }

The index() action prepares a Paginator instance when there is a &paged=true element in the url.

The paginator instance is retrieved in a JSP page as follows:

<%
Paginator paginator = (Paginator)request.getAttribute("post_page");
%>

Then you will get a pagination link as follows:

Yahoo Mail Inbox style:

<%=W.yahooStylePageLinks(paginator, R.resourcePath("posts"))%>

The above code will display a paged posts list as follows:

Showing 31 - 45 of 63 First | Previous | Next | Last

The above display shows that the current page is for records 31 to 45 among a total of 63 records. There are links on First, Previous, Next, and Last, so that users can navigate.

Digg style:

<%=W.diggStylePageLinks(paginator, R.resourcePath("posts"), 4, 11)%>

The above code will display a paged posts list as follows:

« Previous 1 2 3 4 …. 9 10 11 12 13 14 15 16 17 18 19 …. 29 30 31 32 Next »

As you can see, the current displayed page is page 14. The middle band has 11 pages. While the two side bands each has 4 pages.

[top]

HTTP request priority for I18N

You probably have noticed that the welcome message on the home page of a web application built by Scooter is displayed in your browser's current locale language.

You can make the home page to display the welcome message in a different language simply by changing your browser's language option.

In Firefox, you change the language by going to Tools->Options->Content->Languages.

In IE, you go to Tools->Internet Options->General->Languages.

Just change a language, and refresh your browser.

[top]