OpenSwing provides a set of utility classes that allow to connect OpenSwing client-side application with the server-side application based on Spring framework.
Spring is one of the most popular server-side frameworks for developing java web applications; it offers several advantages when developing a web application, such as facilities to interconnect other technologies, like Hibernate, iBatis, JSF, Struts.
Spring framework has been designed to strongly decouple server-side layers that compose a web application, like data access layer, transaction management, presentation layer based on web pages.
It can be easily used also to connect applications having non HTML front-end with some server-side layers, such as data access layer and transaction management, without using other layers, such as presentation layer based on web pages (JSP, JSTL, Turbine, etc). Hence Spring can be connected with GUIs based on OpenSwing: in this way it is possible to inherit all advantages that Spring provides: XML based configuration, strong decoupling between server-side layers, Inversion of control, Dependency Injection, transactions management, Object Relational Mapping, Aspect oriented programming, ApplicationContext, etc.
It is possible with OpenSwing and Spring to create both two tiers client-server applications and three tiers client-server applications based on HTTP protocol. In the rest of this chapter it will be described a development use case related to a 3 tiers application.
When creating a 3 tiers application having an OpenSwing based GUI, it is possible to employ the classic DispatcherServlet servlet provided with Spring as the unique access point for all HTTP requests; these requests come from “ClientUtils.getData()” utility method provided by OpenSwing, that can be used in the client-side application to generate requests to the server-side application via HTTP protocol.
In any J2EE web application there must be defined a “ WEB-INF/web.xml ” file; with Spring this is the first file to configure too. When combining OpenSwing with Spring, a tipical web.xml content is like the one described in the following example:
?xml version="1.0" encoding="UTF-8"?> |
As you can see from the example reported above (see “demo18” sample application provided in the OpenSwing distribution), a DispatcherServlet servlet class is defined; moreover, other XML files must be defined: they are the other classical files required by Spring: applicationContext.xml and dataAccessContext-….xml
Note that in the example it is defined another servlet: JnlpDownloadServlet ; it is used to fill in the jnlp file content; this jnlp file is used by Java Web Start to launch the client-side application.
When defining the DispatcherServlet servlet, Spring requires that another XML file is created: “xxx-servlet.xml”, where “xxx” is the name of the servlet defined in web.xml (in the current example: “controller”); in the following is reported the “controller-servlet.xml” file:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <bean id="hessianObjectReceiver" class="org.openswing.swing.util.server.HessianObjectReceiver"> <bean id="handlerMapping" class=" org.openswing.springframework.web.servlet.handler.OpenSwingHandlerMapping "> <!-- ====== DEFINITIONS OF CONTROLLERS USED TO MANAGE GRID EXPORTING ====== --> <bean id="exportDataGrid" class="org.openswing.springframework.web.servlet.utils.ExportController"> <bean id="getDoc" class="org.openswing.springframework.web.servlet.utils.DocumentController"> |
OpenSwing provides four main classes that must be defined inside this XML file:
From this point the Spring framework is the only actor: it is possible to define a facade, dao objects, transactions, advices and any other Spring component. Consequently, it is possible to include any technology that Spring allows to connect: ORM layers (such as Hibernate or iBatis or TopLink or JPA), EJB, etc.
The only constraint to respect is that the value to return to the client-side must be an object that extends org.openswing.swing.message.response.java.Response. If the client request is generated by a grid control or a lookup, then the return value must be a VOListResponse; if the client request is generated by a Form, then the return value must be of type VOResponse, as with any other application based on OpenSwing and not on Spring too (independently from the number of tiers, two or three tiers).
Therefore OpenSwingViewResolver does not render a web page: it does serialize objects for the client-side application.
As default behavior, objects sent by the client side to Spring servlet are serialized by using standard java serialization; that serialization mechanism has problems when objects to send are runned with different versions of java: if the client application is running with a specific version of java and the server application with another one, then a serialization error could occour. In this scenario, it can be useful to include an alternative serialization mechanism, such as the one provided by the Hessian library; OpenSwing optionally supports this mechanism: to activate it using Spring, in “xxx-servlet.xml” must be included also instructions reported in blue color in the XML above.
On the client side the same serialization mechanism must activated, instead of the java standard serialization. To activate it on the client side the following instruction must be included before sending any message to the server, so it can be added as the first instruction of the main class (see ClientApplication classes included in all sample applications):
ClientUtils.setObjectSender(new HessianObjectSender());
It is possible to include in Spring configuration any kind of Interceptor object, as in any application based on Spring. The OpenSwingHandlerMapping class provided by OpenSwing always extracts from the request the Command serialized object and stores it as request's attribute named OpenSwingHandlerMapping.COMMAND_ATTRIBUTE_NAME. In this way the Command object is available to all Interceptor objects added to the application.
Optionally OpenSwing provides an Interceptor class named SessionCheckInterceptor that could be included in “controller-servlet.xml” file: this interceptor checks each HTTP request coming from the client-side; SessionCheckInterceptor allows the dispatching of request only if the Command object contains a session identifier previously stored in the servlet context (when the client was authenticated); if a Command object does not contain a session identifier or it contains a session identifier not stored in the servlet context, then the request is rejected.
The choice of storing session identifiers in the servlet context instead of storing them in the session context binded to the client derives from the nature of three tiers client-server applications based on Swing front-end: these applications may be started without the need of a browser (for example by directly using Java Web Start), so that the traditional means of client session identification (cookies or URL rewriting) cannot be applied outside the browser. Consequently, session infos bindable to a client must be stored in the servlet context and fetched starting from a client session identifier that must be send from client to server in each Command object.
SessionCheckInterceptor class extracts the client session identifier from the request (through Command.getSessionId method) and checks in the ServletContext if this identifier is stored. Session identifiers are stored in an HashSet whose attribute name is OpenSwingHandlerMapping.USERS_AUTHENTICATED; if the identifier is stored then the interceptor returns true, otherwise it returns false and gives back to the client a org.openswing.swing.message.response. java.ErrorResponse object.
In the following example is reported a DAO class that demostrates how to use QueryUtil utility class (provided by OpenSwing) to map value objects to tables, without the use of ORM tools:
public class EmpDao { private DataSource dataSource ;private Map attribute2dbField = new HashMap(); private Map insUpdAttribute2dbField = new HashMap(); private HashSet pkAttributes = new HashSet(); public EmpDao() { attribute2dbField.put("empCode","EMP.EMP_CODE"); attribute2dbField.put("firstName","EMP.FIRST_NAME"); attribute2dbField.put("lastName","EMP.LAST_NAME"); attribute2dbField.put("deptCode","EMP.DEPT_CODE"); attribute2dbField.put("deptDescription","DEPT.DESCRIPTION"); attribute2dbField.put("sex","EMP.SEX"); attribute2dbField.put("salary","EMP.SALARY"); attribute2dbField.put("hireDate","EMP.HIRE_DATE"); attribute2dbField.put("note","EMP.NOTE"); attribute2dbField.put("taskCode","EMP.TASK_CODE"); attribute2dbField.put("taskDescription","TASKS.DESCRIPTION"); insUpdAttribute2dbField.put("empCode","EMP.EMP_CODE"); insUpdAttribute2dbField.put("firstName","EMP.FIRST_NAME"); insUpdAttribute2dbField.put("lastName","EMP.LAST_NAME"); insUpdAttribute2dbField.put("deptCode","EMP.DEPT_CODE"); insUpdAttribute2dbField.put("sex","EMP.SEX"); insUpdAttribute2dbField.put("salary","EMP.SALARY"); insUpdAttribute2dbField.put("hireDate","EMP.HIRE_DATE"); insUpdAttribute2dbField.put("note","EMP.NOTE"); insUpdAttribute2dbField.put("taskCode","EMP.TASK_CODE"); pkAttributes.add("empCode"); }
public void setDataSource(DataSource dataSource) {
public Response getEmpsList( GridParams gridParams ) throws DataAccessException { |
As you can see from the example, QueryUtil class simplifies the query construction (for a list of value objects), by automatically combining filtering conditions and sorting conditions coming from a grid control in the client-side (these conditions are stored in GridParams argument provided by the grid control). getQuery method could be used to retrieve a list of value objects or a single value object; in the first case, it is able to fetch a specific block of records from the result set, according to pagination settings defined in the grid control (and stored in GridParams together with filtering and sorting conditions).
QueryUtil class provides utility methods useful to insert and update value objects in the database too. When it is used to update a value object, the update method is able to create a SQL instruction having WHERE conditions taken from the old value object; in fact OpenSwing is able to provide two value objects for updating operations: the old value object (that contains values previously read from the database) and the new value object to update (that contains values read from the database that have been changed by the client-side); in this way update method can check if the new value object has already been updated in the database: only if the record to update in the database is still unchanged then the SQL update operation is performed. So QueryUtil.updateRecord method ensures that a concurrent operation is performed in an secure way, without the burden of adding a TIMESTAMP field in the database table, to check data concurrent access.
Note that the example reported above describes a possible way to retrieve and store data in a database starting from value objects and other fetching conditions provided from client-side (e.g. GridParams); the example focuses on the ORM activities and integration issues with OpenSwing that could be simplified by using QueryUtil class: however you are free to choose any other ORM mechanism or other data access solutions, by including other technologies, such as Hibernate (see “demo17” sample included in OpenSwing distribution). |