Showing posts with label SPRING FRAMEWORK. Show all posts
Showing posts with label SPRING FRAMEWORK. Show all posts

Wednesday, February 22, 2012

Sons of DHTMLX and Spring #5

Episode 5: Call of Duty
The last episode announced the availability of the DHTMLX Spring Link as an open source project. Now it is time to get involved and make the project grow.

How to get involved
As an open source project contributions will keep the project alive and updated. There are many ways to get involved in the DHTMLX Spring Link project.

Word of Mouth
The easiest way to support the project is by sharing the information with others. Feel free to create links on your website, blog or social media.

Become a Contributor
A Contributor supports the project by participating in forum discussions, testing, proving feedback for updates, bug fixing, documentation and suggestion.

Become a Committer
Committers get the write access to the code repository and participate actively to the project development. In order to become a committer a license agreement is required.

Make Donation
Individuals who do not have the time to get involved and found the project valuable can consider making a donation.The funds received will be allocated to keep the project live.
You can make a donation here.

Sponsorship
Individuals or Companies that want to provide monetary contribution the sponsorship is the most direct method.Official acknowledgement and thanks for the donation including your logo and link, will be published on this web site.

Contact
If you are interested in participating feel free to contact us at:



Thursday, February 16, 2012

Sons of DHTMLX and Spring #4

Episode 4: Fruit for the Crows
Apologies for the long wait from the last post as I've been involved in a three weeks wild goose chase.
Every company has a character who presents themselves as an expert while at the same time there are clear signals that they do not know very much on a subject.  
A customer made an agreement with one of this experts which was nicknamed Nyohkee.
I don't know very much about this guy, he can be the Greatest American Hero for all I know.  After attending his presentation which was full of colorful slides and catchy phrases such as "You have to spend money to make money" he came to my desk with a song and dance that there was the opportunity to attract new customers.
My first thought was to blow this Popsicle stand as I had a feeling that the discussion was not going to go anywhere but the customer recommended to support any request even if the solution does not meet the potential client needs. End of discussion.
In the next three weeks, including Sundays, I proceeded on the wild goose chase constantly feeling the pressure from Nyohkee. The morning before the presentation an announcement was made that the client meeting was cancelled.

Now it is time to introduce the next episode.
This post introduce another feature of the DHTMLX Spring Adapter which provides  a server side programming model using one of the nicest feature of the DHTMLX library: the ability to create the components using an XML representation. This feature gives the ability to package your  components in JAR files and reused simply including them in your web application library folder.
For example to include a DHTMLX form in a web page, you can use the code below: 
  1. <div id="bookForm" style="height:500px;"></div>

  2. <script>
  3. var bookForm;
  4. function initialize() {
  5.     myForm = new dhtmlXForm("bookForm");
  6.     myForm.loadStruct("/app/book/server");
  7. }
  8. </script>

The project has been formally renamed to DHTMLX Spring Link and it is available on Google Code at: https://code.google.com/p/dhtmlx-spring-link.

Cheers!

Wednesday, January 18, 2012

Sons of DHTMLX and Spring #3

Episode 3: Turning and Turning

DHTMLX Spring Adapter
In the picture below you will find  the DHTMLX Spring Adapter class diagram for the dhtmlxGrid component.


click to enlarge


Each adapter implements the Adapter interface, which consists of the single method toXML. The AbstractAdapter class, is the base class of all the adapters and encapsulates  the methods common to all the subclasses. 
The DhtmlxHttpMessageConveter, is the class which integrates the adapters with the Spring framework. 


Spring Message Converters
The Spring MVC allows to handle any format of HTTP request and responses using a HttpMessageConverter implementation. Spring registers a set of default converters, but it is  also possible write your own and register it in the configuration file: 

  1. <mvc:annotation-driven>
  2.   <mvc:message-converters>
  3.     <bean class="com.mylaensys.dhtmlx.adapter.DhtmlxHttpMessageConverter"/>
  4.   </mvc:message-converters>
  5. </mvc:annotation-driven>
Below the code of the message converter.

DhtmlxHttpMessageConverter.java

  1. public class DhtmlxHttpMessageConverter
  2.   extends AbstractHttpMessageConverter<Object> {
  3.     public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
  4.     public DhtmlxHttpMessageConverter() {
  5.         super(new MediaType("text", "xml", DEFAULT_CHARSET));
  6.     }
  7.     @Override
  8.     protected boolean supports(Class<?> clazz) {
  9.         Class[] theInterfaces = clazz.getInterfaces();
  10.         for (int i = 0; i < theInterfaces.length; i++) {
  11.             if( theInterfaces[i].getName().equalsIgnoreCase( Adapter.class.getName() ) ) {
  12.                 return true;
  13.             }
  14.         }
  15.         return false;
  16.     }
  17.     @Override
  18.     protected void writeInternal(Object object, HttpOutputMessage outputMessage)
  19.   throws IOException, HttpMessageNotWritableException {
  20.         Adapter adapter = (Adapter)object;
  21.         outputMessage.getBody().write( adapter.toXML().getBytes() );
  22.     }
  23. }
The supports method detects whether the given class is supported by the converter. In this case the class must implement the Adapter interface. When a class is supported, the HttpMessageConverter invokes the writeInternal method to write the object to the Http response body. 
The writeInternal method of the DhtmlxHttpMessageConverter obtains the representation of the object in XML, invoking the toXML method of the Adapter interface. In the example below, the BookController class returns an instance of the DefaultGridAdapter to the DhtmlxHttpMessageConverter.
BookController.java
  1. @Controller
  2. public class BookController {
  3.     @Autowired
  4.     private BookService bookService;
  5.     @RequestMapping(value = "/books", method = RequestMethod.GET)
  6.     public @ResponseBody DefaultGridAdapter getBooks(@RequestParam("c") String c) {
  7.         DefaultGridAdapter adapter = new DefaultGridAdapter(c,Book.class);
  8.         adapter.setData( bookService.getBooks() );
  9.         return adapter;
  10.     }
  11. }
Another important element is the @ResponseBody which indicates that the return type of a controller method should be written to the HTTP response body, and not placed in a Model, or interpreted as a view name as standard behavior of Spring MVC.


Grid Adapter
The DefaulGridAdapter is a basic adapter for the dhtmlxGrid component. The grid adapter constructor accepts as parameters a string containing the attributes to render, and  the class of the object. The setData method allows to set the collection of data. 
DefaultGridAdapter.java
  1. public class DefaultGridAdapter extends AbstractAdapter implements Adapter {
  2.     private List data;
  3.     private List<String> columnList = new ArrayList<String>();
  4.     private GridInterceptor interceptor = new GridInterceptorImpl();
  5.     public DefaultGridAdapter(String columnList,Class clazz) {
  6.         this.fieldList = getObjectFields(clazz);
  7.         StringTokenizer st = new StringTokenizer(columnList,",");
  8.         while(st.hasMoreTokens()) {
  9.               this.columnList.add(st.nextToken());
  10.         }
  11.     }
  12.     public List getData() {
  13.         return data;
  14.     }
  15.     public void setData(List data) {
  16.         this.data = data;
  17.     }
  18.     public void setInterceptor(GridInterceptor interceptor) {
  19.         this.interceptor = interceptor;
  20.     }
  21.     @Override
  22.     public String toXML() {
  23.         StringBuffer buffer = new StringBuffer();
  24.         buffer.append("<?xml version='1.0' encoding='UTF-8'?>");
  25.         interceptor.onHeader(this,data,buffer);
  26.         interceptor.onStartRows(this, data, buffer);
  27.         for(Object object : data) {
  28.             interceptor.onStartRow(this, object, buffer);
  29.             for(String column : columnList ) {
  30.                 if( fieldList.contains( column ) ) {
  31.                     interceptor.onRenderCell(this, object, column, buffer);
  32.                 }
  33.             }
  34.             interceptor.onEndRow(buffer);
  35.         }
  36.         interceptor.onEndRows(buffer);
  37.         interceptor.onOutput(buffer);
  38.         return buffer.toString();
  39.     }
  40. }
The toXML method invokes the interceptor which contains the logic for the XML generation. Below the default implementation of the GridInteceptor.
GridInterceptorImpl.java
  1. public class GridInterceptorImpl implements GridInterceptor {
  2.     public void onHeader(AbstractAdapter adapter, List list, StringBuffer buffer) {
  3.     }
  4.     public void onStartRows(AbstractAdapter adapter, List list, StringBuffer buffer) {
  5.         buffer.append("<rows>");
  6.     }
  7.     public void onEndRows(StringBuffer buffer) {
  8.         buffer.append("</rows>");
  9.     }
  10.     public void onStartRow(AbstractAdapter adapter, Object object, StringBuffer buffer) {
  11.         buffer.append("<row id='").append( adapter.getPrimaryKey(object).toString() ).append("'>");
  12.     }
  13.     public void onEndRow(StringBuffer buffer) {
  14.         buffer.append("</row>");
  15.     }
  16.     public void onRenderCell(AbstractAdapter adapter, Object object, String column, StringBuffer buffer) {
  17.         Object value = adapter.getObjectValue(object, column);
  18.         buffer.append("<cell><![CDATA[").append( value.toString() ).append("]]></cell>");
  19.     }
  20.     public void onOutput(StringBuffer buffer) {
  21.     }
  22. }
It is possible to write a custom  GridIntercepter and set it via the setInterceptor method. For example to highlight the rows of the grid which match a condition, it is possible extending the DefaultGridInterceptor and overriding the onStartRow method. The code example below highlights in red the books with a price greater than 10. 
  1.  public class GridInterceptorHighLight extends DefaultGridInterceptor {
  2.     public void onStartRow(AbstractAdapter adapter, Object object, StringBuffer buffer) {
  3.         if( object instanceof Book) {
  4.             Book book = (Book)object;
  5.             if( book.getPrice() > 10 ) {
  6.                 buffer.append("<row id='")
  7.       .append( adapter.getPrimaryKey(object).toString() )
  8.       .append("' style='color:red;'>");
  9.             } else {
  10.                 buffer.append("<row id='")
  11.       .append( adapter.getPrimaryKey(object).toString() )
  12.       .append("'>");
  13.             }
  14.         }
  15.     }
  16. }

The same concept applies to the header (onHeader), to a single cell (onRenderCell) and to the end of the XML processing (onOutput). For more information about the grid XML format refer to  DHTMLX documentation.


Form Adapter
The DefaulFormAdapter is a basic adapter for the dhtmlxForm component. The adapter supports the basic operation: read, write and delete. 
GridInterceptorImpl.java
  1. public class DefaultFormAdapter extends AbstractAdapter implements Adapter {
  2.     public static final String Insert = "inserted";
  3.     public static final String Update = "updated";
  4.     public static final String Delete = "deleted";
  5.     private Object data;
  6.     private String operation;
  7.     public DefaultFormAdapter(Object data) {
  8.         initialize( data );
  9.         this.operation = "";
  10.     }
  11.     public DefaultFormAdapter(Object data,String operation) {
  12.         initialize( data );
  13.         this.operation = operation;
  14.     }
  15.     public DefaultFormAdapter(Object data,BindingResult binding) {
  16.          initialize(data);
  17.          if( binding.hasErrors() ) {
  18.              for( FieldError e : binding.getFieldErrors() ) {
  19.                 this.errorList.add(new ErrorMessage(e.getField(), e.getDefaultMessage()));
  20.             }
  21.          }
  22.     }
  23.     private void initialize(Object data) {
  24.         this.fieldList = getObjectFields(data.getClass());
  25.         Object id = getPrimaryKey( data );
  26.         if( id == null ) {
  27.             this.operation = Insert;
  28.         } else {
  29.             this.operation = Update;
  30.         }
  31.         this.data = data;
  32.     }
  33.     public Object getData() {
  34.         return data;
  35.     }
  36.     public void setData(Object data) {
  37.         this.data = data;
  38.     }
  39.     private boolean isWriting() {
  40.         return Insert.equalsIgnoreCase( this.operation ) || Update.equalsIgnoreCase( this.operation ) || Delete.equalsIgnoreCase( this.operation ) ;
  41.     }
  42.     public boolean hasValidData() {
  43.         return errorList.size() == 0;
  44.     }
  45.     public String toErrorXML() {
  46.         StringBuffer buffer = new StringBuffer();
  47.         String id = getPrimaryKey( data ).toString();
  48.         buffer.append("<data>");
  49.         for( ErrorMessage e : errorList ) {
  50.                 buffer.append("<action sid='").append( id == null ? "" : id.toString() ).append( "' ");
  51.                 buffer.append("type='invalid' ");
  52.                 buffer.append("field='" + e.getField() + "' ");
  53.                 buffer.append("message='" ).append( e.getMessage() ).append("'/>");
  54.         }
  55.         buffer.append("</data>");
  56.         return buffer.toString();
  57.     }
  58.     @Override
  59.     public String toXML() {
  60.         StringBuffer buffer = new StringBuffer();
  61.         if( isWriting() ) {
  62.             if( this.errorList.size() == 0 ) {
  63.                 buffer.append( toStoreXML() );
  64.             } else {
  65.                 buffer.append( toErrorXML() );
  66.             }
  67.         } else {
  68.             buffer.append(toDataXML());
  69.         }
  70.         return buffer.toString();
  71.     }
  72.     private String toDataXML() {
  73.         StringBuffer buffer = new StringBuffer();
  74.         buffer.append("<?xml version='1.0' encoding='UTF-8'?>");
  75.         buffer.append("<data>");
  76.         try {
  77.             if( data != null ) {
  78.                 for(String field : fieldList) {
  79.                     Object value = getObjectValue(data, field);
  80.                     buffer.append("<").append(field).append("><![CDATA[").append( value.toString() ).append(  "]]></").append(field).append(">");
  81.                 }
  82.             }
  83.         } catch (Exception e) {
  84.             log.severe(e.getMessage());
  85.         }
  86.         buffer.append("</data>");
  87.         return buffer.toString();
  88.     }
  89.     public String toStoreXML() {
  90.         StringBuffer buffer = new StringBuffer();
  91.         buffer.append("<data>");
  92.         buffer.append("<action type='").append( this.operation ).append("' ");
  93.         if( data != null ) {
  94.             String id = getPrimaryKey( data ).toString();
  95.             buffer.append("sid='").append( id ).append("' ");
  96.             buffer.append("tid='").append( id ).append("'/>");
  97.         } else {
  98.             buffer.append("field='id' ");
  99.             buffer.append("sid='").append( "0" ).append("' ");
  100.             buffer.append("tid='").append( "0" ).append("' ");
  101.             buffer.append("message='" ).append( "invalid data " ).append("'/>");
  102.         }
  103.         buffer.append("</data>");
  104.         return buffer.toString();
  105.     }
  106. }
The form adapter toXML method generates  the appropriate XML message depending on the operation. The toDataXML returns the XML for the load method of the dhtmlxForm, toStoreXML and toErrorXML handle the send method. Below an example of the BookController using the form adapter.     
BookController.java
  1. public class BookController {
  2.     @Autowired
  3.     private BookService bookService;
  4.     @RequestMapping(value = "/books/{id}", method = RequestMethod.GET)
  5.     public @ResponseBody DefaultFormAdapter getBook(@PathVariable("id") final String id) {
  6.         Book book = bookService.getBook(id);
  7.         DefaultFormAdapter adapter = new DefaultFormAdapter( book );
  8.         return adapter;
  9.     }
  10.     @RequestMapping(value = "/book", method = RequestMethod.POST)
  11.     public @ResponseBody DefaultFormAdapter storeBook(@Valid @ModelAttribute Book book, BindingResult binding) {
  12.         DefaultFormAdapter adapter = new DefaultFormAdapter(book,binding);
  13.         if( adapter.hasValidData() ) {
  14.             bookService.store(book);
  15.         }
  16.         return adapter;
  17.     }
  18.     @RequestMapping(value = "/book/{id}", method = RequestMethod.DELETE)
  19.     public @ResponseBody DefaultFormAdapter deleteBook(@PathVariable("id") final String id) {
  20.         Book book = bookService.getBook(id);
  21.         DefaultFormAdapter adapter = new DefaultFormAdapter(book,DefaultFormAdapter.Delete);
  22.         bookService.remove(id);
  23.         return adapter;
  24.     }
  25. }
The getBook method implements the read operation, while storeBook creates or updates  the object. The DefaultFormAdapter constructor used in the storeBook method takes two parameters: the first one is the model object, the second (BindingResult) holds the result of the validation. When the object contains valid data (hasValidData) the controller can perform write operation to the datastore.    


While waiting for the next episode, don't forget to join the virtual strike against Internet censorship.

Stay Tuned!

Thursday, January 5, 2012

Sons of DHTMLX and Spring Series

Episode 1: Home
After a long Christmas break, traveling, reading, playing with kids and eating my favorite  Roberto's burritos (carne asada with the works: pico di gallo, guacamole, cheese and sour cream), it is time to return to the blog and give my best wishes of a happy New Year to  all readers.  

In the early days of the DHTMLX Java Tag library some developers of a large financial company asked me to give code examples with the Spring Framework. I have also received requests from blog readers to provide examples of using DHTMLX and Spring, so I am taking the opportunity to write a series (see also episode 2 , episode 3 and episode 4) of posts on the subject. 
I have dug up my old examples, updated them to run with Spring 3.1, DHTMLX 3.0 and Google App Engine and packaged in a simple web application. Part of the DHTMLX Spring Adapter code presented comes from an open source library that will be included in the upcoming release (sometime in 2012) of the DHTMLX Java Tag Library.


DHTMLX Server Side
As extension of the JavaScript component library DHTMLX delivers a free open source Java package (dhtmlxConnector) for server side integration.  The connector provides  all the functionality needed to integrate the major database servers. Below is a Java code sample to connect a dhtmlxGrid to a database table: 
  1. /**
  2.  * DHTMLX Grid Connector Example
  3.  */
  4. public class BasicConnector extends ConnectorServlet {
  5.    @Override
  6.    protected void configure() {
  7.       Connection conn= ( new DataBaseConnection()).getConnection();
  8.      
  9.       GridConnector c = new GridConnector(conn);
  10.       c.dynamic_loading(100);
  11.       c.render_table("table", "id", "field1,field2");
  12.    }
  13. }
With the dhtmlxConnector you can have a working dhtmlxGrid in minutes if you have direct access to the database; however, the design of the connector does not allow you to get the same result in a environment where the persistence layer is based on JDO/JPA.  

Spring MVC
In order to get the advantage of the many features that the framework offers, Spring developers adopt a well defined architecture. A typical basic Spring Web Application consists of a  Model, Controller, Service and Repository.
Book.java
  1. /**
  2.  * Model
  3.  */
  4. public class Book {
  5.     private Long id;
  6.     private String author;
  7.     private String title;
  8.     private Integer sales;
  9.     private Integer price;
  10. }
Controller.java
  1. /**
  2.  * Controller
  3.  */
  4. @Controller
  5. public class BookController {
  6.     @Autowired
  7.     private BookService bookService;
  8.     @RequestMapping(value = "/books", method = RequestMethod.GET)
  9.     public void getBooks() {
  10.         List<Book> books = bookService.getBooks();
  11.         ...
  12.     }
  13. }
BookService.java
  1. /**
  2.  * Service
  3.  */
  4. public interface BookService  {
  5.     List<Book> getBooks();
  6.     Book getBook(String id);
  7.     Book store(Book book);
  8. }
BookDao.java
  1. /**
  2.  * Repository
  3.  */
  4. public interface BookDao {
  5.     Book find(String id);
  6.     List<Book> findByTitle(String title);
  7.     Book store(Book book);
  8.     List<Book> findAll();
  9. }

Request Mapping
Spring provides a powerful and flexible Java annotation for request mapping. There are many schools on the request mapping design. The table below highlight the URL mapping of the example application presented in the series:

URL
GET
PUT
POST
DELETE
/books
List books
NA
Create/Update book
NA
/books/id
Get book (id)
NA
NA
NA

DHTMLX Spring Adapter
The sample application of the series illustrates an approach how to integrate a Grid and a Form component with the Spring Framework. Below is the code example for the Controller class using the DHTMLX Spring Adapter.
BookController.java
  1. /**
  2.  * DHTMLX Spring Adapter Example
  3.  */
  4. @Controller
  5. public class BookController {
  6.   @Autowired
  7.   private BookService bookService;
  8.   @RequestMapping(value = "/books", method = RequestMethod.GET)
  9.   public GridAdapter getBooks(@RequestParam("c") String c) {
  10.     GridAdapter adapter = new GridAdapter(c,Book.class);
  11.     adapter.setData( bookService.getBooks() );
  12.     return adapter;
  13.   }
  14.   @RequestMapping(value = "/books/{id}", method = RequestMethod.GET)
  15.   public FormAdapter getBook(@PathVariable("id") final String id) {
  16.     Book book = bookService.getBook(id);
  17.     FormAdapter adapter = new FormAdapter( book );
  18.     return adapter;
  19.   }
  20.   @RequestMapping(value = "/book", method = RequestMethod.POST)
  21.   public FormAdapter storeBook(@Valid @ModelAttribute Book book, ...) {
  22.     FormAdapter adapter = new FormAdapter(book,binding);
  23.     if( adapter.hasValidData() ) {
  24.       bookService.store(book);
  25.     }
  26.     return adapter;
  27.   }
  28. }
Looks familiar, huh?
The series will cover many aspects of the integration of Spring and DHTMLX trying to get the best of both worlds, including client and server site validation, MVC architecture, and URL mapping best practice.

In the upcoming episodes I will go deep in the code, illustrating  the design of the DHTMLX Spring Adapter. Below is the list of  Sons of DHTMLX and SPRING  episodes: 

Episode 1: Home
Introduction to the DHTMLX Spring Adapater (this post)

Episode 2: Caregiver
Take care of your objects with client and server side validation  (released 01/11/12)

Episode 3: Turning and Turning
Spring MVC annotation driven configuration (released 01/18/12)

Episode 4: Fruit for the Crows
Full Source code delivery (released 02/16/12)


Episode 5: Call of Duty 
Get involved and contribute to the open source development (released 02/22/2012)

Stay Tuned!