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]
(resfn rs)))
(defn crud-add [sql resfn args]
(resfn
(insert-values
(first sql) (last sql)
args)))
(defn crud-delete [sql resfn args]
(resfn
(delete-rows
(first sql) (last sql))))
(defn crud-update [sql resfn args]
(resfn
(update-values
(first sql) (last sql)
args)))
These functions are then passed (according to the CRUD operation
desired) to our generic function to access the database:
(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
(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-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".
4) Pick "HTTP", hit Next.
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.
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."
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."
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.
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:
2) Switch to design view. Select the list in the "Books" panel. Right
click the list and choose "Bind to Data".
3) Choose "New service call" and select the operation as getBooks().
Choose "Bind to field" as name.
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.
You can choose the ordering of values in the 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:
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);
}
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.
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)