Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Articles
HTML5 Server-Push Technologies, Part 1   31 Mar 2010 22:16 GMT
Server-Sent Events and WebSockets explained
HTML5 Server-Push Technologies, Part 1
Wed, 2010-03-31
Server-Sent Events and WebSockets explained

{cs.r.title}



The upcoming HTML5 specification includes a lot of powerful and exiting features which turn web browsers into a fully capable ich internet application (RIA) client platform. This article takes a deeper look into two new HTML5 communication standards, Server-Sent Events and WebSockets. These new standards have the potential to become the dominant Server-push technologies for helping developers to write real-time web applications.

1. The evolution of the web

Since its beginning in the 1990s, the World Wide Web has grown rapidly and became more and more dynamic. While the first generation of web pages were static documents, later generations of web sites have been enriched by dynamic elements, followed ultimately by the development of highly-interactive browser-based rich internet applications.

The Hypertext Transfer Protocol (HTTP) was designed in the early days of the internet to transport information entities such as web pages or multimedia files between clients and servers. HTTP became a fundamental part of the World Wide Web initiative. The design of the HTTP protocol has been driven by the idea of a distributed, collaborative, hypermedia information system "to give universal access to a large universe of documents." The main design goal of the HTTP protocol was to minimize latency and network communication on the one hand, and to maximize scalability and independence of the components on the other hand.

The HTTP protocol implements a strict request-response model, which means that the client sends a HTTP request and waits until the HTTP response is received. The protocol has no support wherein the server initiates interaction with the client.

Listing 1. HTTP request on the wire


GET /html/rfc2616.html HTTP/1.1
Host: tools.ietf.org
User-Agent: xLightweb/2.11.3

For instance, a browser or other user agent will request a specific web page or other information entity by performing an HTTP GET request such as shown in Listing 1. The server returns the requested information entity by a HTTP response such as shown in Listing 2. The response consists of the HTTP response header, and the HTTP response body, which are separated by a blank line. The GET request, in contrast, includes a HTTP header only.

Listing 2. HTTP response on the wire


HTTP/1.1 200 OK
Server: Apache/2.2.14 (Debian)
Date: Tue, 09 Feb 2010 05:00:01 GMT
Content-Length: 510825
Content-Type: text/html; charset=UTF-8
Last-Modified: Fri, 25 Dec 2009 05:49:54 GMT
ETag: "302e72-7cb69-47b871ff82480"
Accept-Ranges: bytes

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
    <meta name="robots" content="index,follow" />
    <meta name="creator" content="rfcmarkup version 1.53" />
    <link rel="icon" href="/images/rfc.png" type="image/png" />
    <link rel="shortcut icon" href="/images/rfc.png" type="image/png" />
    <title>RFC 2616 Hypertext Transfer Protocol -- HTTP/1.1</title>  
  
[...]

</small></small></span>
</body></html>

To create more interactive web applications, the AJAX approach has been established as a popular solution for dynamically pulling data requests from the server. By using AJAX the browser application performs HTTP requests based on the XMLHttpRequest API. The XMLHttpRequest API enables performing the HTTP request in the background in an asynchronous way without blocking the user interface. AJAX does not define new kinds of HTTP requests or anything else. It just performs a HTTP request in the background.

In the case of a Web mail application, for instance, the web application could periodically perform an "AJAX request" to ask the server if the mailbox content is changed. This polling approach causes an event latency which depends on the polling period. Increasing the polling rate reduces event latency. The downside of this is that frequent polling wastes system resources and scales poorly. Most polling calls return empty, with no new events having occurred.

While Ajax is a popular solution for dynamically pulling data requests from the server, it does nothing to help to push data to the client. Sure, a server push channel could be emulated by an AJAX polling approach as described above, but this would waste resources. Comet, also known as reverse Ajax, enhances the Ajax communication pattern by defining architecture for pushing data from the server to the client. For instance, the Comet pattern would allow pushing a 'new mail available' event from the mail server to the WebMail client immediately.

Like AJAX, Comet is build on the top of the existing HTTP protocol without modifying it. This is a little bit tricky because the HTTP protocol is not designed to send unrequested responses from the server to the client. A HTTP response always requires a previous HTTP request initiated by the client. The Comet approach breaks this limitation by maintaining long-lived HTTP connections and "faking" notifications.

In practice, two popular strategies has been established. With long-polling, the client sends a HTTP request, waiting for a server event. If an event occurs on the server-side, the server sends the response including the event data. After receiving the response containing the event data, the client will send a request again, waiting for the next event. There is always a pending request which allows the server to send a response at any time. With Http Streaming, the server keeps the response message open. In contrast to long polling the HTTP response message (body) will not be closed after sending an event to the client. If an event occurs on the server-side, the server will write this event to the open response message body. The HTTP response message body represents a unidirectional event stream to the client.

The Comet approach allows implementing a real-time web. In contrast of the beginning of the web, many of today's web applications have to receive information as soon as it's been published on the server-side. For instance a web-based chat application will be successful only if the underlying architecture supports real-time communication.

2. HTML 5

The upcoming HTML 5 extends the HTML language to better support highly interactive web applications. HTML 5 turns web browsers into a fully capable rich internet application client platform, and is a direct competitor of other client platform environments such as Adobe Flash and Microsoft Silverlight.

For instance the HTML 5 standard includes technologies such as the WebWorkers API which allows running scripts in the background independently, the WebStorage API to store structured data on the client side or the new canvas element which supports dynamic scriptable rendering of 2D bitmap images.

2.1 Server-Sent Events

HTML5 also applies the Comet communication pattern by defining Server-Sent Events (SSE), in effect standardizing Comet for all standards-compliant web browsers. The Server-Sent Events specification "defines an API for opening an HTTP connection for receiving push notifications from a server." Server-Sent Events includes the new HTML element EventSource as well as a new mime type text/event-stream which defines an event framing format.

Listing 3. Example JavaScript using the EventSource interface


<html>
   <head>
     <script type='text/javascript'>
        source = new EventSource('Events');
        source.onmessage = function (event) {
          ev = document.getElementById('events');
          ev.innerHTML += "<br>[in] " + event.data;
        };
     </script>
  </head>
  <body>
    <div id="events"></div>
  </body>
</html>

The EventSource represents the client-side end point to receive events. The client opens an event stream by creating an EventSource, which takes an event source URL as its constructor argument. The onmessage event handler will be called each time new data is received.

In general, browsers limit the connections per server. Under some circumstances, loading multiple pages that include an EventSource from the same domain can result in each EventSource creating a dedicated connection. Often the maximum number of connections is quickly exceeded in such situations. To handle the per-server connection limitation, a shared WebWorker, which shares a single EventSource object, can be used. Furthermore, by definition the browser-specific EventSource implementation is free to reuse an existing connection if the event source absolute URL is equal to the required one. In this case, sharing connections will be managed by the EventSource implementation.

Figure 1 shows the HTTP request which will be sent by a browser if an event stream is opened. The Accept header indicates the required format, text/event-stream. Although the new mime type text/event is described by the Server-Sent Events spec, the specification also allows using other formats for event framing. However, a valid Server-Sent Events implementation has to support the mime type text/event-stream at minimum.


REQUEST:
GET /Events HTTP/1.1
Host: myServer:8875
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE) 
        AppleWebKit/532+ (KHTML, like Gecko) Version/4.0.4 
        Safari/531.21.10
Accept-Encoding: gzip, deflate
Referer: http://myServer:8875/
Accept: text/event-stream
Last-Event-Id: 6
Accept-Language: de-DE
Cache-Control: no-cache
Connection: keep-alive   



RESPONSE:
HTTP/1.1 200 OK
Server: xLightweb/2.12-HTML5Preview6
Content-Type: text/event-stream
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Connection: close

: time stream
retry: 5000

id: 7
data: Thu Mar 11 07:31:30 CET 2010

id: 8
data: Thu Mar 11 07:31:35 CET 2010

[...]

Figure 1. An example event-stream

According to the mime type text/event-stream, an event consists of one or more comment lines and/or field lines on the wire level. The event is delimited by a blank line. A comment always starts with a colon (:). Fields, on the other hand, consist of a field name and field value separated by a colon.

Figure 1 also shows an example response. To avoid caching, the response header includes cache directives which disable caching of the response. Event streams should not be cached by definition.

The example response includes 3 events. The first event includes a comment and a retry field; the second and third event includes an id field and event data. The data field holds the event data, which is the current time in the example above. The example server application writes a new event on the wire every 5 seconds. If the EventSource receives the event, the onmmessage handler will be called.

In contrast to the second and third event, the first event will not trigger the onmmessage handler. The first event does not contain data. It includes an id field to track progress through the event stream, and a retry field for reconnecting purposes. The retry field defines the reconnect time in milliseconds. If such a field is received, the EventSource will update its associated reconnection time property with the received one. The reconnect time plays an important role in improving reliability in cases where network errors arise. If the event source instance detects that the connection is dropped, the connection will be re-established automatically after a delay equal to the reconnection time.

As shown in Figure 1, the HTTP request to establish the connection can be enriched by the Last-Event-Id header. This header will be set if the EventSource's last event id property is set with a non-empty string. The EventSource's last event id property will be updated each time an event is received that contains an id, or it will be updated an empty string if no id is present. Because of this, an event stream can be re-established without repeating or missing any events. This guarantees message delivery if lastEventId handling is implemented on the server side.

Listing 4 shows an example HttpServer based on the Java HTTP library xLightweb (with HTML5 preview extension), which is maintained by the author.

Listing 4. Example Java-based server delivering text/event-stream


class ServerHandler implements IHttpRequestHandler {

  private final Timer timer = new Timer(false);
            
  public void onRequest(IHttpExchange exchange) throws IOException {
    String requestURI = exchange.getRequest().getRequestURI();
                
    if (requestURI.equals("/ServerSentEventExample")) {
      sendServerSendPage(exchange, requestURI);
                    
    } else if (requestURI.equals("/Events")) {
      sendEventStream(exchange);
                    
    } else {
      exchange.sendError(404);
    }
  }
            
            
  private void sendServerSendPage(IHttpExchange exchange, 
          String uri) throws IOException {
    String page = "<html>\r\n " +
        "  <head>\r\n" + 
        "     <script type='text/javascript'>\r\n" + 
        "        var source = new EventSource('Events');\r\n" +
        "        source.onmessage = function (event) {\r\n" +
        "          ev = document.getElementById('events');\r\n" + 
        "          ev.innerHTML += \"<br>[in] \" + event.data;\r\n"+
        "        };\r\n" +
        "     </script>\r\n" +
        "  </head>\r\n" +
        "  <body>\r\n" +
        "    <div id=\"events\"></div>\r\n" + 
        "  </body>\r\n" + 
        "</html>\r\n ";
              
    exchange.send(new HttpResponse(200, "text/html", page));
  }
            

  private void sendEventStream(final IHttpExchange exchange) 
          throws IOException {

    // get the last id string
    final String idString = exchange.getRequest().getHeader(
            "Last-Event-Id", "0");

    // sending the response header
    final BodyDataSink sink = exchange.send(new 
            HttpResponseHeader(200, "text/event-stream"));

    TimerTask tt = new TimerTask() {

      public void run() {
        try {
          Event event = new Event(new Date().toString(), ++id);
          sink.write(event.toString());
        } catch (IOException ioe) {
          cancel();
          sink.destroy();
        }
      };
    };
                
    Event event = new Event();
    event.setRetry(5 * 1000);
    event.setComment("time stream");
    sink.write(event.toString());
               
    timer.schedule(tt, 3000, 3000);
  }
}

        
XHttpServer server = new XHttpServer(8875, new ServerHandler());
server.start();

The Server-Sent Events specification recommends sending a "keep-alive" comment event periodically, if no other data event is send. Proxy servers are known which drop an HTTP connection after a short period of inactivity. Such proxy servers close idling connections to avoid wasting connections to unresponsive HTTP servers. Sending a comment event disables this behaviour. Even though the EventSource will re-establish the connection automatically, sending comment events periodically avoids unnecessary reconnects.

Server-Sent Events are based on HTTP streaming. As described above, the response stays open and event data are written as they occur on the server side. Theoretically, HTTP streaming will cause trouble if network intermediaries such as Http proxies do not forward partial responses immediately. The current HTTP RFC 1.1 does not require that partial responses have to be forwarded immediately. However, a lot of popular, well-working web applications exists which are built on the HTTP Streaming approach. Furthermore, production-level intermediaries always avoid buffering large amounts of data to minimize their memory usage.

In contrast to other popular Comet protocols such as Bayeux or BOSH, Server-Sent Events support a unidirectional server-to-client channel only. The Bayeux protocol on the other side supports a bidirectional communication channel. Furthermore, Bayeux can use HTTP streaming as well as long polling. Like Bayeux, the BOSH protocol is a bidirectional protocol. BOSH is based on the long polling approach.

Although Server-Sent Events do have less functionality than Bayeux or BOSH, Server-Sent Events have the potential to be become the dominant protocol for use cases where a unidirectional server push channel is required only (which is the case in many instances). The Sever-Sent Events protocol is much simpler than Bayeux or BOSH. Furthermore Server-Sent Events will be supported natively by all HTML5-compatible browsers. In contrast, Bayeux and BOSH protocol are implemented on the top of the browser language environment.

Still to come...

Part 2 of this article series will cover the WebSocket API and protocol, and present concluding remarks.

Resources

Gregor Roth works as a software architect at United Internet group, a leading European internet service provider.

Source: Java.net
Integrate your Cloud Computing Apps with Salesforce.com, Java   25 Mar 2010 20:02 GMT
New integration patterns have emerged to enable many applications to migrate to the cloud while leveraging existing infrastructure.
Source: DevX
Flexible Swing Reporting Using JIDE Aggregate and Pivot Tables   22 Mar 2010 22:55 GMT
Learn about a Swing report alternative that provides 90% of the solution with 10% of the effort.
Flexible Swing Reporting Using JIDE Aggregate and Pivot Tables
Mon, 2010-03-22
Learn about a Swing report alternative that provides 90% of the solution with 10% of the effort.

{cs.r.title}



Most classical report writers don't fit well in Swing. Reporting solutions can be adapted to Swing, but the adaptation is time consuming to implement, and often provides a less than desirable solution. This article explores an alternative to the classical reporting that provides the end user with more power for less development effort.

Knowledge of Swing and JTable is required to use the techniques described in the article. The discussion covers:

  • Report: Creating and using JIDE Aggregate and Pivot tables.
  • Formula: Adding a new column based on a formula.
  • Memorize: Saving and restoring user report changes.
  • Export: Exporting the tables to Excel

Swing's JTable solution

For a Java developer, the JTable presents the first reporting solution for Swing. Data is read and translated for a data source, the translated data is then wrapped in a TableModel, and finally the TableModel is used with a JTable. The following figure shows a simple JTable flow:

missing image
Figure 1. JTable application flow

Tools like GlazedLists (http://publicobject.com/glazedlists) provide filtering and sorting, and resolve real-time concurrency issues. Commercial products like JDataGrid provide Excel worksheet look and feel, the ability to read Excel spreadsheets into the TableModel as well as utilities to translate the data from common data file types into TableModels.

From a Swing developer's perspective, the JTable solution is easy to implement, and with the addition of third party libraries, further functionality can be provided to the end user. However, the user still sees the data as a flat grid that can only be manipulated by moving and sorting columns.

JIDE Aggregate and Pivot solution

JIDE is a third party Swing library vendor. Among JIDE's array of components are JTable alternatives called Aggregate and Pivot (see Pivot example at http://www.jidesoft.com/products/pivot.htm). The Aggregate and Pivot tables are proxies that provide a higher level abstraction implemented with multiple JTables. Aggregate and Pivot are the components that will be demonstrated as a quick reporting alternative for desktop clients. Figure 2 shows a modified JTable flow, using the JIDE model.

missing image
Figure 2. Modified application flow with JIDE

Getting started

The following are links to the libraries and code for this article.

Note: OpenCSV and Common Lang are used, but can easily be replaced with other libraries.

Sample classes

The sample code is decomposed into the following sections:

  • SwingReportDemo: Starting point of the application. Contains the GUI code and acts as the moderator for all the other classes.
  • Data: SalesDetail.txt is sample sales data that exists in a CSV (comma-separated values) formatted text file.
  • Report: A Report interface provides a service interface to the header, data types, and data. CsvReport and NullReport are implementations of the Report Interface.
  • ReportTableModel: The table model used to wrap the Report.
  • CalculatedTableModel (JIDE): Provides calculated columns behavior to the table model.
  • JideView: Common interface points shared between Aggregate and Pivot, which allows the SwingReportDemo to treat the Aggregate and Pivot views the same.
  • AggregateView: Controls the look, feel, and behavior of the Aggregate table.
  • PivotView: Controls the look, feel, and behavior of the Pivot table.

Figure 3 presents the code in diagram form.

missing image
Figure 3. SwingReportDemo class layout

Table data

The table data involves:

  • Data: The data for the sample is stored in SalesDetail.txt. The first row is the default table header, the second row is the corresponding Java type, and the remaining rows are the report data. The following is a small portion of header, class, and line data:
    
    
    
    Date,Desc,Qty,....
    
    java.util.Date,java.lang.String,java.lang.Double,....
    
    Thu Dec 03 19:00:00 CDT 2009,Cabinet Pulls,4.0,....
    
    
    
    
  • Loading and translation: MainAppAggregate.getReport() is the starting point for loading and translation. The file is loaded as a resource from /jideReport/SalesDetail.txt. CsvReport contains services to translate CSV data to a Report.
  • Table model: ReportTableModel wraps a Report, and also extends JIDE's DefaultContextSensitiveTableModel. JIDE provides more than 2 dozen table model classes that extend Java's AbstractTableModel. JIDE models can be used together by piping one into another to supply additionally functionality. JIDE provides models for caching, filtering, directly reading database data, and adding calculations.

    Note: Be careful with the chaining of models. Understanding the sequence of chaining, and how the different models impact each other is essential. In the following chaining, the CachedTableModel is piped into FilterableTableModel rather than the other way around. If FilterableTableModel were piped into CachedTableModel, as the filtering is updated the CachedTableModel will be invalidated, which is less than optimal.

    
    PivotDataModel(
    
         new FilterableTableModel(
    
            new CachedTableModel(
    
                new CalculatedTableModel(
    
                    new ContextSensitiveTableModel(data, columnNames))))
    
    
    
    
  • Calculation: A significant part of the report is the ability to use expressions to extend the data model. JIDE's CalculatedTableModel allows for the addition of new columns to a data model based on small expressions (or scripts). Expressions can utilize values in other columns and other expressions. The CalculatedTableModel is created by passing in another TableModel. In the sample, ReportTableModel is passed into the CalculatedTableModel constructor. (Note: The expression in the cell is based on cell values on the same row level. Expressions have no knowledge of previous or post row values.) The following demonstrates the data model:

    Data model with calculations:

    ReportTableModelCalculatedTableModel
    Column 1Column 2Computed Column 1

    Sample data:

    ReportTableModelCalculatedTableModel
    Sales PriceQuantity[Sales Price]*[Quantity]
    5.0022110.00

Table creation

AggregateTable is created simply by passing in a TableModel. Additional functionality can be added to AggregateTable. AggregateView.getAggregateTable() in the sample code demonstrates the addition of column header features that allow for auto resizing, modifying the display state of columns, filtering and grouping.

Table functionality

The initial AggregateTable look and feel is similar to a JTable. The following are features of interest.

  • Sort: Click on table header to sort.
  • Filter: Click on the pull down next to the column to filter on values in the column. In Figure 4, the "Item Type" column's filter choice is displayed.

    missing image
    Figure 4. Filter

  • Group, hide, and more: Right-clicking on the column header allows the user to group on the column, hide the column, reset the view, and other things (which options are displayed to the user is controlled in AggregateView.getAggregateTable()). Figure 5 illustrates this.

    missing image
    Figure 5. Group

  • Subtotals and grand totals: Once a column is grouped, the user can select the desired subtotals and grand totals. Figure 6 illustrates this.

    missing image
    Figure 6. Subtotals and grand totals

Initial view

Figure 7 shows a partial view of the initial report.

missing image
Figure 7. Initial view

Modified view

Figure 8 shows a partial view after the user right-clicked on the "Date Month" header and selected "Group this Column"; then did this again with "Item Type"; and then selected to create a "Date Month" subtotal.

missing image
Figure 8. Modified view

Application features

A toolbar was added to the demo application to allow the user to:

  • Add a formula
  • Export to Excel
  • Save and restore user layout changes
  • Switch between the detail aggregate view and the summary pivot view

Add a formula

As of this writing, JIDE does not provide an interface for users to enter a formula; therefore you will need to provide your own GUI if you want to allow users to enter formulas dynamically. The example in the article only applies a simple expression.

To add a computed column, create a new ExpressionCalculatedColumn and add the column to the CalculatedTableModel. The following is the code from the sample:




final ExpressionCalculatedColumn commission = 

    new ExpressionCalculatedColumn(getAggregateView().getTableModel(),

         "Commission", "([Amount])*.01");

getCalculatedModel().addColumn(commission);

getCalculatedModel().fireTableStructureChanged();



Note: There are numerous reasons to add computed columns. The SwingReportDemo.getCalculatedModel() decomposes each Date column into Year, Quarter, and Month columns. The additional columns allow the user more flexibility in grouping and subtotaling. The user can right-click on the header and hide columns; therefore you needn't be too concerned about making too much data available.

Export to Excel

The SwingReportDemo.uiExport() function demonstrates the use of JIDE Excel export feature. Since Aggregate and Pivot tables have separate export functions, the export task is delegated to the appropriate View classes (AggregateView or PivotView).

Note: If your focus is true Excel functionality, products like JDataGrid from http://www.zfqjava.com/ do a great job of both importing and exporting Excel formatted files. JDataGrid tries to reproduce the Excel environment in Java without utilizing the OLE solution space.

Save and restore user layout changes

Once the user has modified the report layout, the layout can be saved and reused. JIDE does the layout persistence to XML. The sample code uses a file chooser, but persistence to a data store is more practical.

Try: Play around with the report by grouping, subtotaling, and hiding columns, then save the report layout. Restart the application and load the saved report layout.

Important: The layout will not save filters. The reason filters are not saved is that filters are customizable and difficult to generalize for XML.

Switch views

The Switch button allows the user to swap between Aggregate and Pivot views of the same data. The pivot view replicates much of the same behavior that exists in Excel pivot grid. Merely drag and drop columns to change the report layout, and right-click on the headers to see more Pivot column features. getPivotView() returns the Pivot view that is displayed:




PivotView getPivotView() {

    if (m_pivotView == null) {

        final String[] header = { "Date Month" };

        final String[] row = { "Item Type", "Item Name" };

        final String[] data = { "Amount" };

        m_pivotView = PivotView.newUi(getCalculatedModel(), 
                header, row, data);

    }

    return m_pivotView;

}



Figure 9 displays the Pivot view on switch.

missing image
Figure 9. Pivot report on switch

The good, the bad, and the ugly

JIDE is no panacea for reporting. The following are the things I like, and issues to be aware of when developing with JIDE.

Good
  • Knowledge reuse: Re-use Java Swing knowledge, technology and techniques.
  • Integration: Allows the user more flexibility at the report application level, without all the integration issues of the classical report writer.
  • Now, not tomorrow: Quick solution for many problems without engagement of a report writer.
  • Reuse of techniques between reports: Copy and pasting is rampant in classical report writing. If you see someone write reports over time, the style and approach changes drastically. With limited or no refactoring, classical reporting becomes a mess to maintain.
  • Support: JIDE response to bugs and requests is exceptional. JIDEs' goal is a release every other week, which reduces the wait time for an enhancement and fix. The following is a link to JIDE's enhancements and bug fix history: http://www.jidesoft.com/history/
Bad
  • Expressions: JIDE uses an expression parser from an open source project called prefuse (http://prefuse.org). Even though prefuse has low memory overhead, and is very responsive, prefuse is also very limited. Prefuse only deals with primitive types in numeric calculations (hence forget about specialized numeric types like Amount). Aggregate and Pivot will fail to respond if a divide by zero issue occurs.
Ugly
  • Lack of aggregated functions: Aggregated functions like 'running totals' or 'balance' require two-pass calculations. Two-pass aggregations are commonplace in reporting. If your users can't live without the second pass calculations, JIDE won't work for you.
  • Summary Calculator: From a developer's perspective, much of JIDE's extensibility seems to revolve around context and catalog patterns. However, SummaryCalculator, used in Pivot tables, does not follow the catalog and context design patterns, and hence has limited usage. JIDE's Pivot table ships with numerous built in summary calculations. However, SummaryCalculator limitations will become a problem if more than a single additional summary calculation is required.
  • Printable objects: Classical report writers are developed from a print page perspective, while Swing is geared to the UI. Therefore don't have high expectations when trying to print JIDE tables. Nice looking printable objects are not only a problem with JIDE, but a problem with every Swing object I have ever used. Some products like DataGrid provide a printable version of their Swing objects, yet DataGrid printing is still less than perfect. Printing is a non-trivial problem to solve. Even popular products like Eclipse IDE started off doing a horrible job of printing, and have only gotten marginally better over time. Hence, do not expect the world when trying to print JIDE tables, it's a Java problem, and a problem that third party print libraries do not and cannot readily resolve.

Final thoughts

A more complete Swing based report solution could be developed by completing and generalizing the report code. Furthermore, as the JIDE components are improved, those new capabilities can be utilized to improve the example application. For now, the sample provides a great start on providing end users more flexibility inside a Swing application, while reducing headaches associated with incorporating a standard report writer.

Resources

Malcolm Davis has been developing commercial software since 1992.
AttachmentSize
SwingJideReportCode.zip12.38 KB
aggtable.png125.72 KB
aggtable-modified.png94.76 KB
jide_flow.png3.15 KB
jtable_flow.png3.53 KB
pivot.png15.15 KB
revised_swing_report.png65.09 KB
swing-demo-filter.png9.18 KB
swing-demo-group.png17.8 KB
swing-demo-grouped.png19.19 KB
swing-demo-pivot.png15.15 KB

Source: Java.net
Getting Started with Java and SQLite on Blackberry OS 5.0   17 Mar 2010 19:52 GMT
Get started in creating applications that utilize the SQLite database engine on Blackbery OS 5.0.
Getting Started with Java and SQLite on Blackberry OS 5.0
Wed, 2010-03-17
Get started in creating applications that utilize the SQLite database engine on Blackbery OS 5.0.

{cs.r.title}



The Blackberry OS is one of the most widely deployed Java ME implementations worldwide, with millions of business users that run mobile devices that use Java within the operating system. Released in 2009, the Blackberry OS 5.0 includes several new features that are of interest to today’s mobile Java developers, such as JSR-135 video recording and 3G graphics support with JSR-239 OpenGL ES API. Most notably for enterprise developers, the Blackberry OS 5.0 is the first mobile operating system that will include database support for Java ME developers who wish to store relational data.

The purpose of this article is to show developers how to get started in creating applications that utilize the SQLite database engine for Blackberry OS 5.0 applications. SQLite is an extremely popular database library that is used for wireless, embedded, and seamless-install application scenarios where a full scale database is not feasible or suitable. Since SQLite is an embedded database library, you (the developer) will have less visibility into the database itself. You won’t be able to use a db viewing tool to maintain the database from another computer, since there’s no way to connect you to SQLite db over a network. This article provides you with a simple tool, DBUtil.java, that will allow you to verify the existence of your db, and monitor its size.

No JDBC, but the Concepts are Still the Same

If you’re wondering if the Blackberry SQLite Java API is JDBC compliant, let me clarify that it’s not. The JSR-169 API (JDBC API for Java ME) has been standardized for years, but unfortunately, there are no commercially available mobile phones that support database development with it. Please note, however, that although the Blackberry SQLite Java API doesn’t support the JSR-169 API, the basic concepts relating to programmatic access to the database are still the same:

  • As an embedded database library, you need to create your own database instance before you can start creating tables.
  • You’ll need to execute INSERT, SELECT, UPDATE, and DELETE to handle the respective CRUD operations within the db instance.
  • If you want to group some commands together in an atomic operation, then be sure to leverage the database’s capability for transactions so that you can roll back changes if a failure occurs midstream in the process.

So, with all the formalities out of the way, let’s look at the code needed to create your first SQLite database.

Prerequisites

In order to run the example code provided in this article, you will need to download the Blackberry JDE 5.0 SDK.

Creating Your First Database

Of course, since SQLite is an embedded database engine that’s optimized for mobile environments, the overhead to create a database is extremely simple. Listing 1, located below, shows the two lines of code necessary to physically create an instance of the SQLite database on Blackberry OS 5.0 devices.

Listing 1. The Code Required to Create a SQLite Database

URI uri = URI.create("file:///SDCard/Databases/database1.db");
sqliteDB = DatabaseFactory.create(uri);

As you can see, it’s extremely trivial to create a db instance with SQLite! Please note that although you may have the capability to create your database instance in various locations on the filesystem, it’s recommended that you create your database on external media, such as SD cards (especially if you intend to store large amounts of data). Listing 2, located below, is the source code for FirstSQLiteApp.java, which is a complete working example of a Java application that creates a database.

Listing 2. Full Source Code for FirstSQLiteApp.java.


import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.database.*;
import net.rim.device.api.io.*;


public class FirstSQLiteApp extends UiApplication {
    
    public FirstSQLiteApp() {    
        pushScreen(new InnerClassScreen());
    }
    
    public static void main(String[] args){
        FirstSQLiteApp firstSQLiteApp = new FirstSQLiteApp();
        firstSQLiteApp.enterEventDispatcher();
    }
    
    class InnerClassScreen extends MainScreen {
     
        public Database sqliteDB;
        public InnerClassScreen() {
            
            LabelField title = new LabelField("Create DB Application",
                LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH);
            setTitle(title);
            add(new RichTextField("Initializing db create process..."));
            
            try{
                URI uri = URI.create(
                    "file:///SDCard/Databases/database1.db");
                sqliteDB = DatabaseFactory.create(uri);
                add(new RichTextField(
                    "Status: Database was successfully created."));
            } catch (Exception e){
                System.out.println(e.getMessage());
                add(new RichTextField(
                    "Status: Database was not created."));
                add(new RichTextField(e.getMessage()));
                e.printStackTrace();
            }
        }  
    }
}

Now, after we run the application within the Blackberry Simulator, we’ll get the following result as shown in Figure 1 below.


Figure 1. The Results of Executing FirstSQLiteApp.java

Please note that by default, the Blackberry OS 5.0 simulator doesn’t have a simulated SD loaded within the environment, so you’ll have to load one in order for the code in Listing 2 to work properly.

Loading a Simulated SD Card

In order to load a simulated smart card into the Blackberry OS 5.0 simulator, you need to access the “Simulate > Change SD Card…” menu item while the simulator is running. Additionally, you need to be prepared to load a DMP file in order to finish configuration of the simulated smart card. You can find several sample DMP files in the Research In Motion\BlackBerry JDE 5.0.0\simulator folder. If the simulator provides an error message stating that the card needs to be formatted, then proceed with the formatting procedure.

Performing CRUD Operations with SQLite

Programmatically retrieving, storing and updating data within the SQLite database is also a very trivial task. The code in Listings 3-6 demonstrates how to create tables, insert, and select data from the database.

Listing 2. Creating Tables in the Database

try {
    URI uri = URI.create("file:///SDCard/Databases/database1.db");
    sqliteDB = DatabaseFactory.open(myURI);
    Statement st = sqliteDB.createStatement( "CREATE TABLE 'Employee' ( " +
                                              "'Name' TEXT, " +
                                              "'Age' INTEGER )" );
            
    st.prepare();
    st.execute();
}
catch ( Exception e ) {         
    System.out.println( e.getMessage() );
    e.printStackTrace();
}

Listing 3. Inserting Data into the Database

 try {
    URI uri = URI.create("file:///SDCard/Databases/database1.db");
    sqliteDB = DatabaseFactory.open(myURI);
    Statement st = sqliteDB.createStatement(
        "INSERT INTO Employee(Name,Age) " +
        "VALUES ('Ralph',47)");
            
    st.prepare();
    st.execute();
}
catch ( Exception e ) {         
    System.out.println( e.getMessage() );
    e.printStackTrace();
}


Listing 4. Selecting Data from the Database

try {
    URI uri = URI.create("file:///SDCard/Databases/database1.db");
    sqliteDB = DatabaseFactory.open(myURI);
    Statement st = sqliteDB.createStatement("SELECT * FROM Employee");
    
    st.prepare();
    Cursor c = st.getCursor(); 

    Row r;
    while(c.next()) {
        r = c.getRow();
        String name =  r.getString(0) ;
        Integer age =  r.getInteger(1);
    }

}
catch ( Exception e ) {         
    System.out.println( e.getMessage() );
    e.printStackTrace();
}

Creating and Using the DBUtility Application

One of the downsides of working with embedded databases is the lack of visibility. A traditional database runs on its own process on the CPU, binds to its own port on the network adapter, and can easily be remotely managed and configured. Conversely, embedded databases such as SQLite run in the process of your application, have no connection to the network, and must be managed and configured by applications that are resident on the host machine. Therefore, I created a simple JSR-75 (FileConnection API) application that allows developers to have more visibility into their embedded databases. When you run the DBUtility.java MIDlet, you can navigate the filesystem of your device to debug database creation issues, as well as to easily determine the file size of your embedded database. Figure 2, located below, shows how the DBUtility can browse your mobile device’s filesystem, and Figure 3 shows the file size of an individual file. Listings 5 and 6 contain all the source code necessary to build the DBUtility MIDlet.


Figure 2. Browsing the Filesystem Using DBUtility.java



Figure 3. Viewing the File Size of a File Using DBUtility.java


Listing 5. DButility.java

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import javax.microedition.io.file.*;
import javax.microedition.lcdui.*;

public class DBUtility extends MIDlet implements CommandListener {

    private Command exitCommand; // The exit command
    private Display display;     // The display for this MIDlet
    private FileNavigator fileNavigator = null;

    public DBUtility() {
        display = Display.getDisplay(this);
        exitCommand = new Command("Exit", Command.EXIT, 0);
        fileNavigator = new FileNavigator(this);
    }

    public void startApp() {

        Runnable r = new Runnable() {
            public void run() {
                display.setCurrent(fileNavigator.getListofFolder(
                    "",true)); //fileViewer.
            }
        };
        new Thread(r).start();

    }

    public void pauseApp() {
    }

    public void destroyApp(boolean unconditional) {
    }

    public void commandAction(Command c, Displayable s) {
        if (c == exitCommand) {
            destroyApp(false);
            notifyDestroyed();
        } 
    }
}

Listing 6. FileNavigator.java

import javax.microedition.midlet.*;
import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import javax.microedition.io.file.*;
import javax.microedition.lcdui.*;



    /**
     * This class traverses a filesystem and displays the folders
     *and files within the filesystem in a List. This class creates the
     * List, directoryList, to show its contents
     *
     * This class may also be renamed fileUtilty
     */
    class FileNavigator implements CommandListener{

        private Command backCommand1;
        private Command selectCommand;
        private Image folder_closed;
        private Image folder_open;
        private Image plain_file;

        private MIDlet midlet;


        //
        // this is the main connection to the filesystem
        // we'll use this object over and over
        //
        private FileConnection fileConn = null;

        // These are vectors for the file/directory names,
        // images, and URLs for the directory/files
        // the boolean is used to determine if the item is
        // a folder or file

        private Vector fileNames = null;
        private Vector images = null;
        private Vector curr_dir_urls = null;  // the urls for 
                   // the files/folders in the current directory
        private Vector fileTypes = null;  // used to determine 
                   //whether the element is a file or directory

        // I'm assuming the fact that I'll need a class level variable
        // that has the current "level" that we're at
        // the folder with all the system roots is level '0'
        int level = 0;

        // I'm also assuming that I'm going to need a class level Vector
        // where I'll store all my folder level urls
        Vector level_urls = new Vector();



        public FileNavigator(MIDlet _midlet){

            midlet = _midlet;

            selectCommand = new Command("Select", "Select", Command.OK, 1);
            backCommand1 = new Command("Back", Command.BACK, 1);
 


            try {
                folder_closed = Image.createImage("/folder_closed.png");
                plain_file = Image.createImage("/plain_file.png");
                folder_open = Image.createImage("/folder_open.png");
            } catch (Exception e) {

            }

        }

        // this method returns a javax.lcd.List 
        // representing the files
        // located in the folder found at the url 
        // passed in (if useRoots is false)
        // if the useRoots variable is true, then the 
        //method will ignore the url string
        // and return a List of the system roots
        public List getListofFolder(String folder_url, boolean useRoots){

            List directoryList = null;  // this is the list that we'll return
            Enumeration folder_contents = null;  // the enumeration 
                       //will contain all the contents of the current dir

            // create the vectors, which will eventually be turned
            // into arrays
            fileNames = new Vector();
            images = new Vector();
            curr_dir_urls = new Vector();
            fileTypes = new Vector();


            // if useRoots is set to true, then obtain an enumeration of all
            // the system roots (System roots may need to be explained)
            if (useRoots == true){
                folder_contents = FileSystemRegistry.listRoots();

            } else {
                // if we're in the else clause, then we need to use the
                // passed in url to obtain a enumeration of the current dir
                try{
                    fileConn = (FileConnection) Connector.open(folder_url);

                    // be sure to explain how to get hidden files
                    folder_contents = fileConn.list();

                } catch (Exception e){
                    System.out.println(e);
                    new Alert(e.toString());
                }

            }

            // we now need to provide a way to navigate 
            // out up of the folder (ie. cd ..)
            // of course, we should provide this functionality for every
            // folder EXCEPT the root
            if (useRoots == true){
                // set the level to "0"
                level = 0;
            } else {
                fileNames.addElement("..");
                images.addElement(folder_open);
                curr_dir_urls.addElement("");  // this is an empty 
                          // string null since that var isn't 
                          // used for navigation
                fileTypes.addElement(new Boolean(true));


                // store the connection url in the level vector
                // this will be used when we have to navigate ".."
                level_urls.insertElementAt(folder_url, level);

                // increment the level
                level++;


            }

            // this is the rest of the old traverse method
            while (folder_contents.hasMoreElements()) {

                // 8-5, this looks like the items that will go in the vectors
                // if so, then some additional comments may be needed
                String item_name = (String)folder_contents.nextElement();
                String item_url = null;
                Image icon = null;
                boolean isDirectory = false;

                // since the enumeration only contains the filename, 
                // and not
                // the full URL to the file, then we need set the full url
                // in order to obtain the full information about 
                // each item in the folder

                if (useRoots == true){
                    item_url = "file:///" + item_name;
                } else {
                    item_url = fileConn.getURL() + item_name;
                }

                // here's the logic to set the proper image
                // in the 'icon' variable

                try {
                    FileConnection conn = (FileConnection) 
                        Connector.open(item_url);
                    if(conn.isDirectory()){
                        icon = folder_closed;
                        isDirectory = true;
                    } else {
                        icon = plain_file;
                        isDirectory = false;
                    }
                } catch (Exception e) {
                    System.out.println(e);
                    new Alert(e.toString());
                }

                // add the items to the vectors

                fileNames.addElement(item_name);
                images.addElement(icon);
                curr_dir_urls.addElement(item_url);
                fileTypes.addElement(new Boolean(isDirectory));
            }

            // initialize the arrays

            String fileNameArray[] = new String[fileNames.size()];
            Image imageArray[] = new Image[images.size()];
            String urlArray[] = new String[curr_dir_urls.size()]; // this 
                        // may not be needed since the vectors are global
            Boolean fileTypeArray[] = new Boolean[fileTypes.size()];  // same
                        // for this one

            // convert to arrays
            fileNames.copyInto(fileNameArray);
            images.copyInto(imageArray);
            curr_dir_urls.copyInto(urlArray);
            fileTypes.copyInto(fileTypeArray);

            //create the List
            directoryList = new List("Choose a folder or select a file",
                Choice.IMPLICIT, fileNameArray, imageArray);

            // add the navigation buttons

            //here's the back button
            directoryList.addCommand(backCommand1);

            //here's the select button
            directoryList.addCommand(selectCommand);

            // add the inner class as the commandListener
            directoryList.setCommandListener(this);

            return directoryList;
            
        }



        public void commandAction(Command command, Displayable displayable) {

            if(command == backCommand1){
                // go to a previous screen
                //Display.getDisplay(midlet).setCurrent(get_homeForm());

                // test to see of the user hit the 
                // "select" button or the command action button 

            } else {
                // get the selected index, which will be used shortly
                int selectedIndex = ((List)displayable).getSelectedIndex();

                if (((List)displayable).getString(selectedIndex).equals("..")){
                    // the user has selected the ".." directory
                    // so let's run some special logic to handle this

                    if(level > 1){
                        // this means that we should use the level_urls vector
                        // 8-6 perhaps we need to put the level-- right here
                        level = level - 2;
                        Display.getDisplay(midlet).setCurrent(
                            getListofFolder((String)
                            level_urls.elementAt(level), false));
                    } else{
                        // this means that we're at a low directory, 
                        // so we need to show the roots
                        Display.getDisplay(midlet).setCurrent(
                            getListofFolder("", true));
                        level = 0;
                    }
                    // now decrement the level
                    //level--;

                } else {
                    // otherwise, let's use the selected 
                    // index to get the proper
                    // item in the List to navigate in the selected folder

                    // so, we've reached this area of code because 
                    // we've come to
                    // the point where we will go further down a directory
                    // or select a file to see the size

                    // so obviously, we need to know if the user 
                    // selected a file
                    // or a directory

                    boolean isFolderSelected = (
                        (Boolean)fileTypes.elementAt(selectedIndex)).
                        booleanValue();
                    if(isFolderSelected == true){
                        // the user selected a folder, so navigate down it
                        String folderUrl = 
                            (String)curr_dir_urls.elementAt(selectedIndex);
                        Display.getDisplay(midlet).setCurrent(
                            getListofFolder(folderUrl, false));

                    } else {
                        // the user selected a file, so don't navigate
                        // start the process to discover Bluetooth devices
                        //**Display.getDisplay(midlet).
                        //  setCurrent(get_fileSelectedAlert(), displayable);

                        // the user has obviously selected a file, 
                        // so let's read it in
                        // this probably should be done in a new thread'
                        String file_url = (String)
                            curr_dir_urls.elementAt(selectedIndex);
                        //here
                        try{
                            fileConn = (FileConnection) 
                                Connector.open(file_url);
                            long fileSize = fileConn.fileSize();
                            String stringFileSize = fileSize / 1024 + " kB";
                            Alert fileSelectedAlert = new Alert("", 
                                "File Size: " + stringFileSize, null,
                                AlertType.CONFIRMATION);
                            fileSelectedAlert.setTimeout(Alert.FOREVER);
                            Display.getDisplay(midlet).
                                setCurrent(fileSelectedAlert);   
                            System.out.println("File Size: " + stringFileSize);

                        } catch (Exception e){

                        }
                    }
                }
            }
        }
    }

Conclusion

For mobile Java developers, the Blackberry OS 5.0 platform provides a compelling development environment to enable developers to create exciting and full-featured applications. One of the most compelling features is the ability to create and manage a local relational database for persistent storage for your mobile application. This feature enables Java developers to bridge the gap between enterprise and mobile application development by incorporating a traditional paradigm for persistent data storage.

Acknowledgements

Thanks to the Blackberry OS 5.0 development team for the extensive documentation and example code on the Blackberry OS 5.0 and SQLite Java APIs.

Bruce Hopkins author of Bluetooth for Java, is an enthusiast for mobile, embedded, and wireless application development. He's currently working for the startup BlogRadio. Bruce is also a Java Champion.
AttachmentSize
Figure1_sml.png66.61 KB
Figure2_sml.png65 KB
Figure3_sml.png59.13 KB

Source: Java.net
Rethinking Multi-Threaded Design Priniciples   04 Mar 2010 03:29 GMT
Designing and improvising a thread based application is a challenge. But by following certain design principles and guidance, this can be easily overcome.
Rethinking Multi-Threaded Design Priniciples
Wed, 2010-03-03
Designing and improvising a thread based application is a challenge. But by following certain design principles and guidance, this can be easily overcome.



Introduction

For many years, we have been using Java Threads for developing numerous Client-Server based applications. While it's always desirable to have a highly responsive Client application, handling a high volume of client requests has always been a prime goal of any Server application. However, designing a highly-scalable server requires lots of analysis. Without careful design and adherence to a well thought out underlying policy, a thread based system can fail to produce the desired outcome.

In this article, we'll discuss certain design principles that should be followed when the objective is to build an efficient, thread-safe application.

Thread Safety lies in Domain

As a design principle of thread safety, the vital point to look into is "Domain First." After all, the whole structure of an application lies on the Domain itself. If you get your domain model right, most of the problems are automatically taken care of. A model represents certain aspects of business logic consisting of various constraints and invariants. Understanding the pre and post conditions of a model are much needed in order to make the realized application thread-safe.

Let's imagine an travel agency that arranges budgeted cars for their traveler guests. They need an interactive car rental application that allows them to book multiple cars at different tourist locations on different dates within a specified price range. As they keep making multiple rental requests, at the same time if the system fails to find a car at particular location within price range, they want to be notified with suggested locations on the same screen.

We can think of this as a booking Requester taking the renal requests and putting them in a thread safe collection CarBookings that would be read concurrently by an actual CarFinder process to provide suggested locations. If CarFinder succeeds, it forwards this via the booking engine to the booking rental location. If CarFinder does not succeed, it tries to find the cars based on the price range and nearest location. Once found, it adds the info to another thread safe collection CarBookingAdvices.

There are many thread safe data structures available, but if you're sure about the Thread Safety policy that you want to adhere to, it's best to create a domain model on your own. For example:

 class CarBookings{     private final Set bookings = new HashSet();

public synchronized void putCar(CarBooking booking){
bookings.add(booking);
}

public synchronized Set getBookings(){
return Collections.unmodifiableSet(bookings);
}
}

Since the methods putCar() and getBookings() provide a secure way of accessing the non-thread-safe instance variable bookings, the CarBookings can be used safely by both the BookingRequester and CarFinder process running in different threads. This meets our simple thread safety requirement.

 //Defining BookingAdvice class BookingAdvice{     private final String bookingId="";     private final BigDecimal rate=new BigDecimal("");     private final String location=""; //Getter and Setter ommited }  class BookingAdvices{     private final Set<BookingAdvice> advices = new HashSet<BookingAdvice>();      public synchronized void putAdvice(BookingAdvice advice){         advices.add(advice);     }      public synchronized Set<BookingAdvice> getAdvices(){         return Collections.unmodifiableSet(advices);     } }  //Defining Requester class BookingRequester{     private CarBookings bookings;     private BookingAdvices advices;      public void requestBooking(CarBooking booking){         bookings.putCar(booking);                     //Line 1         displayAdvice(advices.getAdvices());      //Line 2     }      private void displayAdvices(Set<BookingAdvice> advices){         //Displays the advices on the same screen     } }  //Defining Finder class CarFinder{     private BookingAdvices advices;     private CarBookings bookings;      public void processAdvice(){         List<BookingAdvice> adviceList = getGeneratedAdvices();           for(BookingAdvice advice: adviceList)             advices.putAdvice(advice);                //Line 3          findAndProcessBookings(bookings.getBookings()); //Line 4     }      private List<BookingAdvice> getGeneratedAdvices(){         //Generate advice based on requests     }      private void findAndProcessBookings(Set<CarBooking> bookings){         //Finds advice for the booking if required     } }  

Hiding Model State

But, there is a caveat: if the CarBooking class is mutable, then there is a possibility of modifying each CarBooking instance in the set returned by getBookings(). Does it make our model brittle? Well, as long as we can make sure that it won't be modified later on, we'll be fine with that. But, sometimes it's easier get thread safety by hiding the model state and blocking the access to it thereafter. Here, we make CarBooking immutable and add a copy constructor:

 class CarBooking{     private final String bookingId;     private final String location;     private final BigDecimal rate;      //Copy constructor     public CarBooking(CarBooking booking){         this.bookingId= booking.getBookingId();         this.location= booking.getLocation();         this.rate= booking.getRate();     }     //public Getter methods below }  class CarBookings{     //Modified version     public synchronized Set getBookings(){
Set newBookings = new HashSet();
for(CarBooking b : bookings){
CarBooking newBooking = new CarBooking(b);
newBookings.add(newBooking);
}
return Collections.unmodifiableSet(newBookings);
}
}

So, when getBookings() returns, it returns a copy of the CarBooking, thus not allowing modification of the original state--which makes it purely thread safe.

Do Thread Delegation if you can

Sometimes you might find yourself being overburdened with handling the thread safety in your domain model; in that case you can simply delegate it to a range of Java built-in data structures that are already proven and tested to be thread safe. Like in our example, if we had to use bookings as HashMap<String, CarBooking>, where String could represent bookingId, we could have easily replaced that with ConcurrentHashMap--which is a thread safe HashMap. Likewise there are many other data structures available (under the java.util.concurrent package) like CopyOnWriteArrayList and ConcurrentLinkedQueue, to name a few.

Figuring Single Thread Confinement

To coordinate multiple threads in a lock-based application, the OS and JVM have to burn fair amount of CPU cycles for context switching and thread scheduling--making the system less responsive sometimes.

This can be avoided by allowing only one thread to gain access to work on a unit model (either CarBooking or BookingAdvice) at a time, and also making sure that same unit model should not be re-worked by the same thread. To make this happen, we can employ bounded BlockingQueues (BookingQ and AdviceQ) that would act as a mediator between Requester and CarFinder. So, for a rental request, the Requester would just create a CarBooking and put it in the BookingQ queue, and on the other hand, CarFinder would take that off of the queue. The same thing will be done by the CarFinder to return a BookingAdvice in the AdviceQ queue. This way, multiple Requesters and CarFinders can work asynchronously, making the system highly responsive.

Use synchronized lock wisely

While synchronized lock provides an easy way to work with multiple threads, improper usage of the lock may cause unpredictable results. This becomes obvious when multiple locks are acquired to perform an atomic operation where the first lock is held until last lock operation is performed. Let's consider our BookingRequester and CarFinder that are using simple java Sets instead of CarBookings and BookingAdvices. Here two operations: putting request and getting advices for Requester (vice versa for CarFinder, lines 5,6,7 and 8) become implicitly atomic under the same lock:

 class BookingRequester{     private final Set bookings = new HashSet();
private final CarFinder finder;

public BookingRequester(CarFinder cf){
finder = cf;
}
public synchronized void requestBooking(CarBooking booking){
bookings.add(booking); //Line 5

displayAdvice(finder.getAdvices()); //Line 6
}

public synchronized Set getBookings(){
return bookings;
}
}
class CarFinder{
private final Set advices = new HashSet();
private BookingRequester bookings;

public CarFinder(BookingRequester br){
bookings = br;
}
public synchronized void processAdvice(){
List adviceList = getGeneratedAdvices();
for(Advice advice: adviceList
advices.add(advice); //Line 7

findAndProcessBookings(bookings.getBookings()); //Line 8
}

public synchronized Set getAdvices(){
return advices;
}

}

Now, a problem starts to creep in when two threads T1 and T2 try to call Requester requestBooking() and CarFinder processAdvice() respectively at the same time. During requestBooking, before T1 calls getAdvices() on CarFinder (Line 6), it has to wait for T2 to complete processAdvice. But to complete processAdvice, T2 needs to call getBookings() on Requester (Line 8), which won't happen until T1 completes requestBooking()--leading to a deadlock situation.

In our previous implementation of BookingRequester and CarFinder, we have already delegated the two operations to be handled independently under different locks (one for CarBookings and another for BookingAdvices, lines 1, 2, 3 and 4) to avoid any deadlock.

But, here we can simply resolve it by moving synchronized from the entire method to only the time required to put the booking (Line 5) and advice (line 7).

Another important point regarding deadlocks would be the order in which the resources are accessed in an atomic operation. If two threads work on an operation with two resource locks being held in a different order, it can lead to a deadlock situation too. So, make sure all threads calling an atomic operation get all resource locks in the same order.

Conclusion

Designing and improvising a thread based application is a challenge. But by following certain design principles and guidance, this can be easily overcome. At the same time, clear understanding of thread safety policy is also essential as it helps you simply the design. There are many other techniques available that we couldn't cover here. However, the principles presented here will always assist you in overcoming some of the thread safety related hurdles you might be facing as you develop thread-safe applications intended for operation on modern multicore and multiprocessor computers.

Dibyendu Roy has more than ten years of design and development experience in various domains including Banking and Financial Systems, Business Intelligence tools and ERP products.

Source: Java.net
Taking a Java-Based App Offline Using Flash Builder, LiveCycle Data Services   02 Mar 2010 20:03 GMT
This article builds a sample application that can seamlessly transition between online and offline connectivity status.
Source: DevX
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage




©2010 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.