Tomcat clustering configuration

The following steps assume that you have installed a Tomcat 5.5.x bundle or latest, i only tested on 5.5.27 but is should work for other configuration as well. The network configuration apply to Linux and may vary with the distribution. It should work as is for distributions based on Red Hat.

For Tomcat clustering we have two main things to configure:

Configure the network support for cluster

Opening Specific HTTP Ports (e.g. Port 45564, 4001)

The cluster class will start up a membership service (multicast) and a replication service (tcp unicast). See also http://www.cyberciti.biz/faq/howto-rhel-linux-open-port-using-iptables/ for a brief article on this. You will need to have root access as noted above to complete this.

Your server may or may not already have this entry. Open iptables:

> vi /etc/sysconfig/iptables

Add the following entries:

-A RH-Firewall-1-INPUT -p udp -m udp --dport 45564 -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m tcp --dport 45564 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 4001 -j ACCEPT

Save and close the above file and after restart the iptables

  > /etc/init.d/iptables restart

Configure the multicast address and routes.

Clustering membership is established using very simple multicast pings. Each Tomcat instance will periodically send out a multicast ping, in the ping message the instance will broad cast its IP and TCP listen port for replication. If an instance has not received such a ping within a given timeframe, the member is considered dead.

Add route  (the server’s ip address)

sudo /sbin/route add 228.0.0.4 gw 10.72.10.1 dev bond0

Edit rc.local to make the change persistent through restarts.

sudo vim /etc/rc.d/rc.local

Add this line at the end (the server’s ip address)

/sbin/route add 228.0.0.4 gw 10.72.10.1 dev bond0

Configure Tomcat to support clustering.

Application clustering with Tomcat has two steps:

Enable Tomcat clustering support

You need to enable the cluster support in Tomcat by editing the server.xml file. Open server.xml

sudo vim /usr/local/tomcat-5.5.27/conf/server.xml

Enable clustering configuration in the configuration file, notice that the default configuration is using the DeltaManager which will replicate only the session’s changes and not the entire object:

<Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
managerClassName="org.apache.catalina.cluster.session.DeltaManager"
	expireSessionsOnShutdown="false"
	useDirtyFlag="true"
	notifyListenersOnReplication="true">
<Membership className="org.apache.catalina.cluster.mcast.McastService"
	mcastAddr="228.0.0.4"
	mcastPort="45564"
	mcastFrequency="500"
	mcastDropTime="3000"/>
<Receiver className="org.apache.catalina.cluster.tcp.ReplicationListener"
	tcpListenAddress="10.72.10.1"
	tcpListenPort="4001"
	tcpSelectorTimeout="100"
	tcpThreadCount="6"/>
<Sender className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
	replicationMode="pooled"
	ackTimeout="15000"
	waitForAck="true"/>
<Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
  filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
<Deployer className="org.apache.catalina.cluster.deploy.FarmWarDeployer"
	tempDir="/tmp/war-temp/"
	deployDir="/tmp/war-deploy/"
	watchDir="/tmp/war-listen/"
	watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.cluster.session.ClusterSessionListener"/>
  </Cluster>

One main condition for replication to work is that your session content is serializable. Add a _jvmRoute_ to your Tomcat Engine section From

  <Engine name="Catalina" defaultHost="localhost">

To

  <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

jvmRoute identifies unique a Tomcat instance in a cluster. If multiple servers are used I recommend you to use descriptive names.

Make your application clusterizable

Configuring Tomcat clustering is not enough to cluster your application. For that you need to tell Tomcat which application you want to be clusterizable. This is achieved in two ways:

Enable application clustering by ROOT.xml

Edit ROOT.xml file

 sudo vim /usr/local/tomcat-5.5.27/conf/Catalina/localhost/ROOT.xml

Look for

 <Context path="" cookies="true" distributable="true" crossContext="true">

Change it to

 <Context path="" debug="0" reloadable="true"
cookies="true" crossContext="false" privileged="false" >

Enable application clustering by editing the web.xml

Edit the web.xml file

 sudo vim /usr/local/tomcat-5.5.27/webapps/ROOT/WEB-INF/web.xml

Look for:

 <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
	<context-param>
	<param-name>contextClass</param-name>
	.............

Change it to:

 <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
	<distributable/>
	<context-param>
	<param-name>contextClass</param-name>
	.............

Restart Tomcat

 cd /usr/local/tomcat-5.5.27/bin/
sudo ./shutdown.sh
sudo ./startup.sh
or if you have a init script
sudo /etc/init.d/tomcat5 restart

You need to configure all the nodes in the cluster as detailed above. Every node should have unique name provided by “jvmRoute” attribute.

Further reading

Cluster-howto | http://tomcat.apache.org/tomcat-5.5-doc/cluster-howto.html

Put together Struts2, JPA, Hibernate and Spring

This weekend I played a bit with Struts2. Obviously my first thought was to put together “fellowship of the ring” ie Struts, Spring and Hibernate. Now, we have two ways for integrating Hibernate and I’ve chosen the Java Persistence Architecture approach, because is the latest trend in terms of persistence in the J2EE landscape.

Create your domain model

For the sake of simplicity I’ll take  a simple example that contains Books and Authors. A book has one or more authors while one author wrote one or more books. This looks like a many to many relationship and we want to be able to get, from one shoot, the book and its authors and for an author his entire list of books.

Book.java

package org.bserban.shs.model;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "book")
public class Book {
    @Id
    @GeneratedValue
    private Integer id;
    private String title;
    private String publisher;
    private String keywords;
    private String ISBN13;
    private String ISBN10;
    @ManyToMany(fetch = FetchType.EAGER)
    private List<Author> authors;
........................................

Author. java

package org.bserban.shs.model;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "author")
public class Author {
    @Id
    @GeneratedValue
    private Integer id;
    private String firstName;
    private String lastName;
    private String blog;
    @ManyToMany(mappedBy="authors",fetch = FetchType.EAGER)
    private List<Book> books;
.......................................

As you see in the code snippets above, the many to many relationship was marked with @ManyToMany annotation. We specified that the many to many relationship is mapped by Author entity too. No need to create a persistence.xml or hibernate.cfg.xml files at this moment because the whole configuration will take place in the Spring files.

Create a DAOs

First of all create a generic DAO object to keep the reference to the EntityManager and avoid duplicate code in other DAOs.

package org.bserban.shs.dao;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public abstract class GenericJPADAO<T, ID extends Serializable> {

private Class<T> persistentClass;

protected EntityManager em;

@PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}

public EntityManager getEntityManager() {
return em;
}

public void save(T entity) {
getEntityManager().persist(entity);
}

public void delete(T entity) {
getEntityManager().remove(entity);
}
public T getById(Object id) {
return getEntityManager().find(persistentClass, id);
}

By making a generic DAO class using java generics we are avoiding casting and using the Object class as parameter, in this way our methods will always have the proper type for their operation. While this is very easy with simple application it can become more complicated, but don’t forget that now we are building bricks that will help us to build our house, for more complicated logic we just simply create a facade class that will use our bricks.
BookDAO.java and AuthorDAO.java

package org.bserban.shs.dao;
import org.bserban.shs.model.Author;
public class AuthorDAO extends GenericJPADAO<Author,Integer> {
}
package org.bserban.shs.dao;
import org.bserban.shs.model.Book;
public class BookDAO extends GenericJPADAO<Book,Integer>{
}

These classes are empty so far but specific methods will probably be added.

Create your service layer

I am going to build also a generic service similar with the generic DAO class. For this example I only need basic operations that can be easily made generic. All I need is to create a generic interface called GenericService and ask for a type. Note that while GenericService is an interface for the DAOs I’ve done an abstract class because I wanted to add code to methods.

package org.bserban.shs.service;
import java.util.List;
public interface GenericService<T> {
    T create(T entity);
    void delete(T entity);
    T update(T entity);
    List<T> list(int page, int size);
}

Now, in the Services classes we need to have also a reference to the specific DAO object for that type of service. I am going to create a dao field in each class and I am going to assume that somebody will set the dao to the correct value when the service will be used.

BookService.java and AuthorService.java, their structure is identically so i will list only one here:

package org.bserban.shs.service;
import org.bserban.shs.dao.AuthorDAO;
import org.bserban.shs.model.Author;
import java.util.List;
public class AuthorService implements GenericService<Author> {
    private AuthorDAO dao;
    public Author create(Author entity) {
        dao.save(entity);
        return entity;
    }
    public void delete(Author entity) {
        dao.delete(entity);
    }
    public Author update(Author entity) {
        dao.save(entity);
        return entity;
    }
    public List<Author> list(int page, int size) {
        return dao.list(page,size);
    }
    public AuthorDAO getDao() {
        return dao;
    }
    public void setDao(AuthorDAO dao) {
        this.dao = dao;
    }
}

For the listing feature I simply added a new method in the DAOs for list, and it looks like this:

................
public List<Author> list(int page, int size) {
    Query query = this.em.createQuery("from Author order by lastName,firstName");
    query.setFirstResult((page - 1) * size);
    query.setMaxResults(size);
    return query.getResultList();
}
.................
public List<Book> list(int page, int size) {
    Query query = this.em.createQuery("from Book order by title");
    query.setFirstResult((page - 1) * size);
    query.setMaxResults(size);
    return query.getResultList();
}
..................

Create the Actions

The actions are part of the controller and they need to validate data and to provide error handling before calling the service layer. One characteristic of putting together the Spring and Struts2 is that the dependency injection is done via a constructor argument. You could do it also with setters and getters but this would expose your service class to the exterior. One could call your action via HTTP by providing a parameter with the same name of your service class which will result into an unpredictable behavior, most probably an error.

In the actions I am going to have three fields, the first is the Service reference, the second is the list of model object and the third is a solo entity used for create, update, delete.

I will only list one Action class because they are pretty similar:

public class AuthorAction extends FrontendAction {
    private GenericService<Author> service;
    private List<Author> authors;
    private Author author;
    public AuthorAction(GenericService<Author> service) {
        this.service = service;
    }
    public String list() {
        authors=service.list(page, size);
        return SUCCESS;
    }
    public String update() {
        service.update(author);
        return SUCCESS;
    }
    public String create() {
        service.create(author);
        return SUCCESS;
    }
    public String delete() {
        service.delete(author);
        return SUCCESS;
    }
......................................

I have created a FrontendAction to put the page and size parameters, since they are a common feature of both actions.  Similar, you can put here other attributes that are common for the all actions.

Now lets add our actions into struts.xml file and start to configure the Spring support. I have chosen the extension of our actions to be *.html. I also want to set the Struts2 into dev mode to debug the application if necessary. For IOC Struts2 has a property called objectFactory which let’s you to specify what is your IOC container, i set this to “spring” values. The views will be written as Freemarker templates which I consider to be a powerful template engine.

<constant name="struts.devMode" value="true"/>
<constant name="struts.action.extension" value="html"/>
<constant name="struts.objectFactory" value="spring"/>
<constant name="struts.ui.theme" value="simple"/>
<constant name="struts.ui.templateSuffix" value="ftl"/>
<constant name="struts.configuration.xml.reload" value="true"/>

Now we need to declare our actions, I’ve decided to create different name space for each action which means that the http calls will look like this: /books/…..   /authors/…..

<package name="books" namespace="/books" extends="struts-default">
    <action name="list" class="bookAction" method="list">
        <result name="success" type="freemarker">/WEB-INF/pages/books/list.ftl</result>
    </action>
</package>
<package name="authors" namespace="/authors" extends="struts-default">
    <action name="list" class="authorAction" method="list">
        <result name="success" type="freemarker">/WEB-INF/pages/authors/list.ftl</result>
    </action>
</package>

For the purpose of demonstration I am providing only the listing capability, I let you code the rest. So I need to create two templates to list the entities, they are done in the same way so i am pasting only the author’s list page:

[#ftl]
<link media="screen" type="text/css" href="${base}/resources/css/style.css" rel="stylesheet">
<div>
<table class="listing">
    <tr><td class="hed" colspan="3">List of authors</td></tr>
    <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Books</th>
    </tr>
    [#list authors as author]
    <tr [#if author_index%2 = 0]class="bg"[/#if]>
        <td>${author.firstName!''}</td>
        <td>${author.lastName!''}</td>
        <td>[#if author.books??][#list author.books as book] ${book.title}[#if book_has_next],[/#if][/#list][/#if]</td>
    </tr>
    [/#list]
</table>
</div>

Glue the code with Spring

This example uses Spring 2.5.5 version. I am going to separate the configuration files in two, one is for generic configuration for JPA and transactions and the second will contain our classes glued together.

My applicationContext.xml

Declare the JPA support

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

Now declare the EntityManager bean

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="database" value="MYSQL" />
            <property name="showSql" value="true" />
        </bean>
    </property>
</bean>

The data source used by EntityManager bean

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/example" />
    <property name="username" value="example" />
    <property name="password" value="example" />
</bean>

Now I need to configure the transaction manager for my classes and obviously I am going to use the one that supports annotations. The era for declarative transaction configuration has long gone.

<bean id="transactionManager"
    class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
<tx:annotation-driven transaction-manager="transactionManager" />

Let’s not forgot our objects, let’s tell Spring where is our application specific configuration file

<import resource="exampleContext.xml"/>
Now, all I need is to glue my classes together, basically our DAOs need the JPA beans, the Services need our DAOs and Struts Actions need our Services.
exampleContext.xml will contain the followings:
<!-- daos -->
 <bean id="authorDao" class="org.bserban.shs.dao.AuthorDAO"/>
 <bean id="bookDAO" class="org.bserban.shs.dao.BookDAO"/>
 <!-- services -->
 <bean id="authorService" class="org.bserban.shs.service.AuthorService">
     <property name="dao" ref="authorDao"/>
 </bean>
 <bean id="bookService" class="org.bserban.shs.service.BookService">
     <property name="dao" ref="bookDAO"/>
 </bean>
 <!-- action -->
 <bean id="authorAction" scope="prototype" class="org.bserban.shs.action.AuthorAction">
     <constructor-arg ref="authorService"/>
 </bean>
 <bean id="bookAction" scope="prototype" class="org.bserban.shs.action.BookAction">
     <constructor-arg ref="bookService"/>
 </bean>

Now add the transaction support to our service classes. I simply need to put the @Transactional annotation at class level which means that all method from that class support transactions.

import org.springframework.transaction.annotation.Transactional;
@Transactional
public class BookService implements GenericService<Book> {
.........................

I am going to configure also the web.xml file, where I’ll put the configuration for Struts and for Spring.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Now all we need is to hit run and see if it is working. Also we are going to need some testing data.

Database tables

Putting this line <property name=”generateDdl” value=”true” /> into EntintyManagerFactory bean configuration will cause the generation of our database tables.

tables

I just need to insert manually some sample data to be able to list them in the “list” pages.

Sample data:

INSERT INTO `author` (`id`,`firstName`,`lastName`,`blog`) VALUES  (1,'John','Doe','no blog'), (2,'Sam','Dune','no blog');
INSERT INTO `book` (`id`,`title`,`publisher`,`keywords`,`ISBN13`,`ISBN10`) VALUES  (1,'Some Random Title','The plublisher','random','0596517726','978-0596517724');
INSERT INTO `book_author` (`books_id`,`authors_id`) VALUES  (1,1);

Html Views

Let’s add a simple css file to give a decent look to our pages, all we need is to customize the table look&feel.

table.listing {
    text-align: center;
    font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
    font-weight: normal;
    font-size: 11px;
    color: #fff;
    width: 280px;
    background-color: #666;
    border: 0px;
    border-collapse: collapse;
    border-spacing: 0px;
}
table.listing td {
    background-color: #CCC;
    color: #000;
    padding: 4px;
    text-align: left;
    border: 1px #fff solid;
}
table.listing td.hed {
    background-color: #666;
    color: #fff;
    padding: 4px;
    text-align: left;
    border-bottom: 2px #fff solid;
    font-size: 12px;
    font-weight: bold;

End the result:

http://localhost:8080/books/list.html

listBooks

http://localhost:8080/authors/list.html

listAuthors

I hope I’ll have the time to continue this apps and introduce jQuery for simple actions like add/edit/delete/filter books and authors.

Cheers,

Resources

I’ll list here all the needed jars/frameworks for this sample application:

Source code sample, you can downloaded from here Struts2, Spring, JPA, Hibernate

Key-Value Storage using MemcacheDB

What is Entity-Attribute-Value model (aka key-value storage)

This is also know as Entity-Attribute-Value model, and it is used in circumstances where the number of attributes (properties) that can be used to describe an entity  is very vast but the number of attributes that will actually be used is modest.

Let’s think in terms of a database how an Entity-Attribute-Value model would look like for storing an user profile.

id user_id key value
1 101 screen_name john
2 101 first_name John
3 101 last_name Smith

The table has one row for each Attribute-Value pair. In practice, we prefer to separate values based on data type to let the database to perform type validation checks and to support proper indexing. So programmers tend to create separate EAV tables for strings, real and integer numbers, dates, long text and BLOBS.

The benefits of such structure are:

  1. Flexibility, there is no limit on attributes used to describe an entity. No schema redesign.
  2. The storage is efficient on sparse data.
  3. Easy to put the data into an XML format for interchange.

There are also some important drawbacks:

  1. No real use of data types
  2. Awkward use of database constraints
  3. There are several problems in querying such a structure.

What is MemcacheDB

Memcachedb is a distributed key-value storage system designed for persistence. It is a very fast an reliable distributed storage. It includes transaction and replication. It is using Berkeley DB as persistence storage.

Why is better than a database?

  1. Faster, no SQL engine on top of MemcacheDB
  2. Designed for concurrency, design for millions of requests
  3. Optimized for small data

Memcachedb is suitable for Messaging, metadata storage, Identity Management (Accounts, Profiles, Preferences, etc), index, counters, flags, etc.

The main features for Memcachedb are:

Storage, replication and recovery

Berkeley DB stores data quickly and easily without the overhead found in other databases. Read more about Berkeley DB here

MemcacheDB supports replication using Masters and Slaves nodes. The exact deployment design must chosen according with your application needs. A MemcacheDB environment consists intro three things:

One problem could be spot in Log files, that record you transaction, over time they will contain a lot of data making the recovery a pain moment. For this Memcache DB has a Checkpoint. The checkpoint empties the in-memory cache, writes a checkpoint record, flushes the logs and writes a list of open database files.

Berkeley DB also allows hot backups and uses gzip and tar to compress the backup.

Monitoring

Memcache DB has a lot of built in commands for monitoring, such as:

What i liked most at Memcached is that you can use telnet to log on the running process and issue commands from command prompt. The same thing is valid also for MemcacheDB.

Besides memcached built function the Berkeley DB engine comes with his own stats command:

db_stats, –c locking statistics, –l logging statistics, –m cache statistics, –r replication statistics, –t transaction statistics.

Overall i liked what i saw about this alternative and i think that this is the most suitable solution for storing user profiles and user data that don’t need to be queried. When you need to scale this is for sure a very reliable solution. Have fun!

Further reading

Homepage: http://memcachedb.org

Mailing list: http://groups.google.com/group/memcachedb

Facebook temporarily lost data.

Last Sunday Facebook reported a data loss. We are talking about approximately 15% of users’ photos. Loosing your client’s data is the worst thing that could happen to you and reminded me what a guy said once in a tech talk: “The main rules in running an online community service are: Never lose data and never go to jail.”

Facebook has not yet made public the details of what happened but only assured users that their photos will be restored using a backup. The official report states that we are talking about a hardware failure at storage level.

First of some key facts about Facebook

Based on the above numbers it means that they lost approximately 1.5 billion of pictures. Waw!

How is Facebook handling user’s images? Last year Jason Sobel, Manager of the Facebook Infrastructure Group, presented some insights about the current Facebook storage solution and the future one. We don’t know right now whether the new storage solution failed or the old one is to blame.

Writing files using the old way

They were using upload servers and stored images via NFS into a NetApp storage (last year they were planning to replace it). Each image is stored 4 times. This solution experienced heavy workload when processing metadata.

Reading files using the old way

Here all resumes to speed.

The main concerns with the above architecture are:  Netapp storage is overwhelmed, they rely too much on CDNs.

Obviously when your app grows like hell, you start to think that is better to make your own toys, fully customized and optimized for your particular problem. So did Amazon back in 2001 and  Google too.  This is how the Facebook storage was born: Haystack

Haystack

The answer was to develop in house a distributed file system like GFS (Google File System). Haystack should run on inexpensive commodity hardware, and it should deliver high aggregate performance to a large number of clients.

Haystack is file based and stores arbitrary data in files. For 1Gb disk data file they create 1M in memory index. In this way they have one disk seek which is much better than NetApp which had 3.

The Haystack format is rather simple and efficient, Version number, Magic number (supplied by the client to prevent brute force attack), length, data, checksum.  The index simply stores the Version, Photo key, Photo size, start, length.

Using a Haystack server

To write uses POST
/write/[pvid]_[key]_[magic]_[size].jpg
- writes data on disk haystack
- writes data on in memory index
To read uses GET
/[pvid]_[key]_[magic]_[size].jpg
- uses the in memory index to retrieve the offset
- reads data from the on-disk file

This simple approach allows Facebook to easily balance the reads and writes using Haystack clusters but to speed up the reads they still plan to use CDNs in areas where they don’t have datacenters and Cachr for profiles. This is their first step to create their own CDN network.

Additional readings

Needle in a haystack: efficient storage of billions of photos

Engineering[at]Facebook’s Notes

Simple MVC for Flex and AIR

This weekend I tried to make an “Air” client for search using “Adobe Community Help. I had no prior experience with Flex and AIR. My first attempt ended as a simple project for college, a spaghetti code. All examples found on the web or official documentation are based on massive use mxml code. I ended up having a glorious mxml file. Huge :-).

Some smart guys are telling you to split your mxml using an ActionScript file but is like putting the trash under the carpet. It is still spaghetti code but in two dishes.

I tried to remember how I would have programmed it in Java Swing.  9 years have passed since then but I remembered. TableModel, DefaultListModel, ButtonModel etc. they all came out from a dark corner of my memory. Yes ! I remembered, Swing MVC.

A typical sample from documentation…..

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
   3:     <mx:Panel x="10" y="10" width="350" height="200" title="Rate Customer Service">
   4:         <mx:ComboBox id="cbxRating" x="20" y="20" width="100">
   5:             <mx:dataProvider>
   6:                 <mx:Array>
   7:                     <mx:String>Satisfied</mx:String>
   8:                     <mx:String>Neutral</mx:String>
   9:                     <mx:String>Dissatisfied</mx:String>
  10:                 </mx:Array>
  11:             </mx:dataProvider>
  12:         </mx:ComboBox>
  13:         <mx:Button x="140" y="20" label="Send"/>
  14:     </mx:Panel>
  15: </mx:Application>

Who hard-codes his application in this way? In a typical application you get this stuff from files or database. But let’s see the good part, at least is giving you a hint, a ComboBox and List are accepting an Array as input.

MVC applied to Flex

First of all a briefing about MVC

  1. Model, is the data. Manipulates the internal state and fire events when the internal state changed.
  2. View, the visual representation for the Model’s data (controls on the screen)
  3. Controller, is responsible for interpreting the user actions on the view and make changes to the model. (usually an event handler in flex)

In reality there is no 100% demarcation between these three layers. Is not that easy to make them completely decoupled and usually we end up making some tradeoffs.

The controller will always know about the view and the view about the controller. Controller also knows about the model. In the end I could say that the model is the only piece of the MVC that can be “100% decoupled”.

Let’s try to put a simple screen like this into MVC.

image

We fill in some text and then press Add. After we press add the text box is cleared and the text is added into de the list. In the end wee look into it and try to make a reusable component.

Model

The model contains the actual data, and for our example it should a class that hold list values. As I pointed out earlier we could use an ArrayCollection to store the list. For this we make an actionscript class named ListModel

The view should display model’s changes and for this we mark the model class as [Bindable]. This is nice feature of ActionScript, no such thing in Java Swing.

Now, to add a new item in the list we simply nee an addElement function. In this example we make the model a singleton. This means that if we make more than one list they will share the same data.

   1: package org.bserban.flex.simplemvc.model
   2: {
   3:     import mx.collections.ArrayCollection;
   4:     /**
   5:     * Model class used to store list data and to
   6:     * provide access to the list content.
   7:     *
   8:     * For demo purposes, we have only addElement.
   9:      */
  10:     [Bindable]
  11:     public class ListModel
  12:     {
  13:         public var items :ArrayCollection = new ArrayCollection();
  14:         private static var instance:ListModel;
  15:         public function ListModel()
  16:         {
  17:             instance=this;
  18:         }
  19:         /**
  20:          * For this demo we treat this class as singletone.
  21:          * If list is reused the component that reuse it
  22:          * must keep reference to model and controler.
  23:          */
  24:         public static function getInstance():ListModel{
  25:             if(instance == null){
  26:                 instance = new ListModel();
  27:             }
  28:             return instance;
  29:         }
  30:         /**
  31:          * Add a new element to the list, the model is bindable,
  32:          * no further actions are required, the view
  33:          * is notified automatically.
  34:          */
  35:         public function addElement(itm:String): void{
  36:             items.addItem(itm);
  37:             trace(" item added, now we have:"+items.length);
  38:         }
  39:     }
  40: }

Controller

The controller is responsible for the interaction between view and model. In our case will make the validation of the text, it doesn’t allow the view to add empty text into the list. Let’s make a new class named Controller. As an exercise add also a sort function.

The controller holds a reference to the model and provides functions to the view. In a more advanced implementation the Controller would listen for events from the view and the decides what action should do.

   1: package org.bserban.flex.simplemvc.controller
   2: {
   3:     import org.bserban.flex.simplemvc.model.ListModel
   4:     import mx.utils.StringUtil;
   5:     public class Controller
   6:     {
   7:        private var model:ListModel = ListModel.getInstance();
   8:         /**
   9:          * Glue the interaction between view and model.
  10:          */
  11:         public function Controller(){
  12:         }
  13:         /**
  14:          * add a new item into the list model.
  15:          */
  16:         public function addPerson(name:String):void
  17:         {
  18:             if(name==null || StringUtil.trim(name).length==0){
  19:                 trace("empty name");
  20:             }
  21:             else{
  22:                 model.addElement(name);
  23:            }
  24:         }
  25:     }
  26: }

View

The view is the graphical representation of the component. Usually it is a an mxml file but it can be also an actions script file for advanced programmers. It binds his data to the model and use the controller to process the view events. Let’s name it SimpleAirMVC.mxml. I added Air to its name because i decided to make an air project in Flex Builder.

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" height="226" width="235">
   3:     <mx:Script>
   4:         <![CDATA[
   5:         import mx.controls.List;
   6:         import org.bserban.flex.simplemvc.model.ListModel;
   7:         import org.bserban.flex.simplemvc.controller.Controller;
   8:
   9:         [Bindable]
  10:          private var model:ListModel = ListModel.getInstance();
  11:         private function clickHandler():void
  12:             {
  13:                 var controler:Controller = new Controller();
  14:                 controler.addPerson(personTxt.text);
  15:                 personTxt.text="";
  16:             }
  17:              ]]>
  18:     </mx:Script>
  19:
  20:     <mx:TextInput x="10" y="10" id="personTxt" text="" width="145"/>
  21:     <mx:Button x="163" y="10" label="Add" id="btnAdd" click="clickHandler()"/>
  22:     <mx:List x="10" y="40" width="218" dataProvider="{model.items}"></mx:List>
  23: </mx:WindowedApplication>

This a  simple way to implement a MVC in a small application. Things can become more complicated if the application is big. If this is the case then you should look over Pure MVC framework or Cairngorm. They eliminate the dependencies between MVC layer by using events. It is an event driven approach.

You may download the source code here SimpleAirMVC

Further reading

I recommend you to read the books ActionScript 3.0 Design Patterns and Advanced ActionScript 3 with Design Patterns

← Previous PageNext Page →