October 13, 2009 8:23 PM
Lisp is a language that many developers shy away from. But I urge everyone to try it out at least once, because it offers the possibility of opening up doors in your mind that you know didn't exist. Clojure is a modern Lisp dialect that runs on the JVM and is designed for concurrency. In this article, I will build an end-to-end CRUD application in Clojure which connects to a mysql database and has a Flex UI as the front-end.

The complete source is available for download: clojure source file and Flex MXML file.

Clojure

Clojure compiles source into Java bytecode and can therefore leverage existing Java code while superimposing a functional programming paradigm on top of the mature, optimized Java virtual machine. Data is immutable and there are software transactional memory and agent systems to make developing parallel programs painless.

Setting up Clojure

You need clojure and compojure (a web framework). You can get them from their respective sites or do it the easy way:

1) Grab the following JARs from github repository of programming-clojure:

clojure-contrib.jar, clojure.jar, commons-*.jar,
compojure.jar, jetty-*.jar, servlet-api-*.jar

2) Get the mysql connector JAR.

3) Create a Java project in Eclipse (if you are an IDE guy), add all the above jars to the build path.

4) Create a Java class known as MainApp in your source folder (src):

public class MainApp 
{   
    public static void main(String[] args) 
    {
        try
        {
            clojure.main.main(args);
        }
        catch (Exception e)
        {
            System.err.println("Err: " + e);
        }
    }   

5) If you run the application now, you will get the Clojure REPL. Create a file called user.clj in your source folder (src) and put this in:

(use 'compojure)

(defroutes my-app
  (GET "/"
    (html [:h1 "Hello World"]))
  (ANY "*"
    (page-not-found)))

(run-server {:port 8080}
  "/*" (servlet my-app))

6) Save user.clj, re-run the application and navigate to http://localhost:8080 and you should see a hello world. Congratulations, you have clojure and compojure set up and running the embedded Jetty server.

Writing the CRUD App in Clojure

Clojure has some nice libraries that will ease development and offload common tasks such as HTML/XML parsing and generation, MySQL access, etc. We will use the sql library in clojure-contrib for our MySQL access.

Our database schema is simple:

CREATE TABLE Books
(
   BOOK_ID SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
   BOOK_NAME VARCHAR(400) NOT NULL,
   BOOK_PRICE FLOAT NOT NULL,
   BOOK_AUTHOR VARCHAR(400) NOT NULL,
   PRIMARY KEY (BOOK_ID)
);

Jumping ahead, this is how our final CRUD functions look like:

(defn get-all-books []
 (db-run crud-read "SELECT * FROM books" format-book nil))

(defn add-book [name author price]
  (db-run crud-add [:Books [:BOOK_NAME :BOOK_AUTHOR :BOOK_PRICE]] 
	  format-book [name author price] ))

(defn delete-book [id]
  (db-run crud-delete [:Books ["BOOK_ID=?" id]] format-book nil))

(defn update-book [id name author price]  
  (db-run crud-update [:Books ["BOOK_ID=?" id]] format-book 
	  {:BOOK_NAME name :BOOK_AUTHOR author 
	       :BOOK_PRICE price} ))

(defn format-book [res] 
  (html [:books (map (fn [x] [:book x]) res)]))

Some simple things to help you understand the code above: defn defines a function, square brackets define vectors or maps, a colon prefix indicates a key name for a map.

In seventeen lines of code, the high level CRUD is done. I find this beautiful. The above functions are the only ones that know our schema, the rest are generic.

The following four functions define each CRUD operation generically:

(defn crud-read [sql resfn args]
  (with-query-results rs [sql]
    ;; pass result to resfn
    (resfn rs)))

(defn crud-add [sql resfn args]
   (resfn
    (insert-values
     ;; table name and fields
     (first sql) (last sql)
     ;; values to insert
     args)))

(defn crud-delete [sql resfn args]  
  (resfn
   (delete-rows 
    ;; table name and condition
     (first sql) (last sql))))

(defn crud-update [sql resfn args]
  (resfn
   (update-values 
    ;; table name, condition
    (first sql) (last sql) 
    ;; values to update with
    args)))

These functions are then passed (according to the CRUD operation desired) to our generic function to access the database:

; Don't forget to substitute your database name, 
; username and password in the above code.
(defn db-run
  [crudop sql resfn args]
  (let [db-host "localhost"
        db-port 3306
        db-name "dcd"]
    (def db {:classname "com.mysql.jdbc.Driver"
           :subprotocol "mysql"
           :subname (str "//" db-host ":" db-port "/" db-name)
           :user "root"
           :password "root"})
    (with-connection db
      ;; perform CRUD
      (crudop sql resfn args))))

The above function takes four parameters - the operation to perform with the database connection, the SQL to execute, the function to call on the result and arguments for the SQL.

The flow is as follows: add-book, get-books, etc. call db-run with crud-add, crud-read, etc. along with the schema details and the GET parameters.

The servlet configuration for compojure is:

(defroutes webservice
  (GET "/get" 
    [{:headers {"Content-Type" "text/xml"}}
     (get-all-books)]) 
  (GET "/add" 
    [{:headers {"Content-Type" "text/xml"}}
     (add-book (params :name) (params :author) (params :price) )]) 
  (GET "/delete" 
    [{:headers {"Content-Type" "text/xml"}}
     (delete-book (params :id) )]) 
  (GET "/update" 
    [{:headers {"Content-Type" "text/xml"}}
     (update-book (params :id) (params :name) (params :author) (params :price) )]) 
  ) 

;; run integrated jetty
(run-server {:port 8080} 
  "/*" (servlet webservice))

That's it, our server side is done.

Flash Builder Goodness

Accessing the Clojure methods in Flash Builder 4 to build a Flex application is easy.

1) Get Adobe Flash Builder 4 Beta 2 from Adobe Labs if you haven't already.

2) Create a new Flex Project, type in a project name and hit Finish.

3) In the bottom part of Flash Builder, choose the Data/Services tab and click on "Connect to Data/Service".

New Project Wizard

4) Pick "HTTP", hit Next.

Connect to Data Service Wizard

5) Select "Yes" to pick a common base URL: http://localhost:8080/. Add the four operations: getBooks, addBook, updateBook, deleteBook. The first doesn't take any parameter, second takes in three parameters: name, author and price. updateBook takes in the same parameters along with an "id" parameter. The deleteBook operation only takes in an "id" parameter of type int.

Connect to Data Service Wizard

6) Type in a service name "BookService". Hit Finish.

7) Your Data/Services tab should be populated with the service and methods. Right click the getBooks() method and choose "Configure Return Type."

Connect to Data Service Wizard
Connect to Data Service Wizard

8) Hit Next twice and Flash Builder should have automatically connected to your clojure backend and detected your return type. Select the "root" as "book."

Connect to Data Service Wizard

9) Hit Finish. Now right click the addBook() method and choose "Configure return type".

10) Hit Next. Enter a sample name, author and price. Hit Next and then Finish.

Connect to Data Service Wizard

11) Repeat steps 9 - 10 for update and delete while providing the appropriate sample input values.

Wiring it to a Flex UI

This section wires the services you set up earlier to the Flex UI. This is similar to the "Wiring to a Flex UI" section in "Building a CRUD application using Java 6 Web Services and Flash Builder 4."

1) Start off by replacing your main MXML file with the following code:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
	       xmlns:s="library://ns.adobe.com/flex/spark" 
	       xmlns:mx="library://ns.adobe.com/flex/halo" 
               minWidth="1024" minHeight="768">
<s:Panel title="Books" x="61" y="78" width="124" height="387">
  <s:List id="list" x="0" y="10" width="100%" height="100%" 
	  borderVisible="false"></s:List>
</s:Panel>
<s:Panel title="Book Info" x="193" y="78" width="379" height="387">
  <mx:Form x="0" y="10" width="377" height="300">
  </mx:Form>
  <s:HGroup x="66" y="309"  
	    height="46" 
	    verticalAlign="middle"  
	    contentBackgroundColor="#938F8F">
    <s:Button label="Add" id="button"  />
    <s:Button label="Update" id="button2" />
    <s:Button label="Delete" id="button3" />
    <s:Button label="Get" id="button4"  />
  </s:HGroup>
</s:Panel>
</s:Application>

You should now have a UI that looks this in design view:

Flash Builder 4

2) Switch to design view. Select the list in the "Books" panel. Right click the list and choose "Bind to Data".

Flash Builder 4

3) Choose "New service call" and select the operation as getBooks(). Choose "Bind to field" as name.

Flash Builder 4

4) Now select the form in the "Book Info" panel, right click it and choose "Bind to Data." Choose "Data type" in the "Generate form for" drop down. Hit Next.

Connect to Data Service Wizard

You can choose the ordering of values in the wizard.

Connect to Data Service Wizard

The generated form by default is bound to a value object with name "book."

5) Right click the list in the "Books" panel and choose "Generate Change Handler." The view will shift to source code and type in the following code into the change event handler:

/* point book to the selected item */
book = list.selectedItem as Book;

This is done so that every time the selected changes in the list, the form on the right is updated. If you run the application now, it should get the list of books from the server.

6) To make sure the first item in the list is selected every time the list is retrieved from the server, add a "result" event handler to the CallResponder that fetches the data in your main MXML file.

<s:CallResponder id="getBooksResult" result="list.selectedIndex=0"/>

CRUD

Getting the Add/Update/Delete/Get buttons to work is painless:

1) Select the Add button in design view.

2) Right click, choose "Generate Service Call" and choose the addBook operation.

3) The IDE automatically switches to source view so that you can type in the parameter to the addBook operation. Simply type in book.book_name , book.book_author, and book.book_price.

protected function button_clickHandler(event:MouseEvent):void
{
    addBookResult.token = bookService.addBook(book.book_name, 
					      book.book_author, 
					      book.book_price);
}
CRUD App with Clojure and Flash Builder 4

4) Repeat steps 1 - 3 for the update and delete buttons. The delete button uses an integer parameter, book.bookid instead and updateBook needs that along with the rest.

5) Right click the "Get" button and choose "Generate Click Handler." In the event handler, call the list's creation complete method.

protected function button4_clickHandler(event:MouseEvent):void
{
    list_creationCompleteHandler(null);
}

6) Optionally you could call list_creationCompleteHandler(null) after add, update and delete so that the list on the left is refreshed.

That's it, you have a CRUD app working! You can download the complete MXML file here.

CRUD App with Clojure and Flash Builder 4

Conclusion

Clojure is a powerful, modern Lisp dialect that is designed to be parallel and has a symbiotic relationship with the JVM. Coupled with compojure, web application development becomes easy via the embedded Jetty server. Hooking up the clojure backend to a Flex UI is a matter of mere minutes.

Together, clojure and Flash Builder 4 can help you easily build succinct, functional and parallel web applications.

For more Flash Builder 4 articles, visit Sujit's blog.

CategoryFlex Comment(s)

Copyright © 2004-2011 Anirudh Sasikumar. All rights reserved.
Last Updated: October 13, 2009 10:23 PM