Archive for the ‘Hibernate’ Category

 

Posted by : Bigyan Basnet on | Related Categories: BlazeDS, Flex, Hibernate, Spring

When I started my current job, I was asked to look into implementing data push using BlazeDS. To implement Data Push, I used the Spring BlazeDS Integration 1.0 integration just released last month. It integrates BlazeDS remoting and messaging facilities within the Spring IOC framework. Below is a quick proof of concept application I made using Spring, BlazeDS and Hibernate showing off some data push features with the aforementioned combination. If you open multiple windows or tabs on your browser, you’ll notice the changes are pushed to all clients.


I’ve included all the Flex, Java, XML config files, just click “View Source” and you can even get the project archive and play around with it locally. I’ll just go over some key points for and those interested.

  1. Since you’re reading this, I am assuming you are familiar and working with BlazeDS. You’ll also need to download and install the Spring BlazeDS integration jars from Spring Source: http://www.springsource.org/spring-flex
  2. Instead of the BlazeDS MessageBrokerServlet, configure you web.xml so that the Spring MVC Dispatcher Servlet handles all the communication for the “/messagebroker/*” AMF endpoint:
        <!-- Spring Flex Integration 1.0  -->
    	<servlet>
    		<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<init-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>/WEB-INF/applicationContext.xml</param-value>
    		</init-param>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    
        <!-- Spring Flex Integration 1.0  -->
    	<!-- Map all /messagbroker requests to the DispatcherServlet for handling -->
    	<servlet-mapping>
    		<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
    		<url-pattern>/messagebroker/*</url-pattern>
    	</servlet-mapping>
    
  3. The Spring ApplicationContext.xml looks like this:
    	<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    
    	<flex:message-broker>
    		<flex:message-service
    	        default-channels="my-amf, my-polling-amf"/>
    	</flex:message-broker>	
    
    	<bean id="defaultMessageTemplate"
    		class="org.springframework.flex.messaging.MessageTemplate" />		
    
    	<bean id="dataSource"
    		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    		<property name="url" value="jdbc:mysql://localhost:3306/datapushtest"/>
    		<property name="username" value="XXXX"/>
    		<property name="password" value="XXXX"/>
    	</bean>
    
    	<bean id="sessionFactory"
    		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    
    		<property name="dataSource" ref="dataSource"/>
    
    		<property name="mappingResources">
    			<list>
    				<value>com/datapushtest/entities/Contact.hbm.xml</value>
    			</list>
    		</property>
    
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
    				<prop key="hibernate.show_sql">true</prop>
    				<prop key="hibernate.hbm2ddl.auto">update</prop>
    			</props>
    		</property>
    	</bean>
    
    	<bean id="contactsService"
    		class="com.datapushtest.dao.ContactDAO">
    		<property name="sessionFactory" ref="sessionFactory"/>
    		<flex:remoting-destination/>
    	</bean>
    
    	<flex:message-destination id="contactsMessaging"
    		channels="my-amf, my-polling-amf"/>
    

    Important here is that I am registering the MessageBroker in the Spring Container:

    	<flex:message-broker>
    		<flex:message-service
    	        default-channels="my-amf, my-polling-amf"/>
    	</flex:message-broker>
    

    Also I am registering the Spring MessageTemplate here. The MessageTemplate seems to wrap up the BlazeDS message broker and really simplifies adding messages to the queue from Java code and pushing these messages to all connected clients:

    	<bean id="defaultMessageTemplate"
    		class="org.springframework.flex.messaging.MessageTemplate" />
    

    Here I am registering the class for remoting instead of in remoting-config.xml. Spring is going to manage this and provide all the things it needs like connection to a database, the message template mentioned above, etc.

    	<bean id="contactsService"
    		class="com.datapushtest.dao.ContactDAO">
    		<property name="sessionFactory" ref="sessionFactory"/>
    		<flex:remoting-destination/>
    	</bean>
    

    I am also registering a AMF messaging destination with the Spring Container to handle incoming messages. Note above, I am calling remote methods of java classes mapped on the client, here I am setting a destination which is like a bus where messages are sent and pushed to clients:

    	<flex:message-destination id="contactsMessaging"
    		channels="my-amf, my-polling-amf"/>
    
  4. Now, whenever CRUD methods are called on the Hibernate DAOs, we want to push this to the message bus. I’ve implemented it like this. We have a class that implements this functionality and all hibernate DAOs that you want implementing this functionality can just extend this class.
    public class MessagingDAO {
    
    	private MessageTemplate template;
    
    	public MessageTemplate getTemplate() {
    		return template;
    	}
    
    	@Autowired
    	public void setTemplate(MessageTemplate template) {
    		this.template = template;
    	}
    
    	public void sendMessage(String destination, String headerName,
    			String headerValue, Object body) {
    		AsyncMessage message = template.createMessage();
            message.setDestination(destination);
            message.setBody(body);
            message.setHeader(headerName, headerValue);
            template.getMessageBroker().routeMessageToService(message, null);
    	}
    }
    

    Notice that Spring will (@autowired) inject the MessageTemplate we register and push the message with the sendMessage() method where you have args to specify the destination (we also registered this in the applicationContext), a header name, a header value, and a body (any object that is mapped over on the Flex client). Now a DAO will inherit the sendMessage() method and call it when doing a CRUD method:

    	@Override
    	public void remove(int contactId) {
    		Session session = sessionFactory.openSession();
    		Transaction tx = session.getTransaction();
    		try {
    			tx.begin();
    			Contact Contact = (Contact) session.get(Contact.class, contactId);
    			session.delete(Contact);
    			tx.commit();
    			super.sendMessage("contactsMessaging", "method", "delete", contactId);
    		}
    		catch (RuntimeException e) {
    			tx.rollback();
    			throw e;
    		}
    		finally {
    			session.close();
    		}
    	}
    
  5. Flex Client Side Now, I’ll just briefly highlight some of the main Flex client side code required to get things working. First you need to have remote objects somewhere pointing to the “contactsService” destination declared in the Spring applicationContext.xml and some event handlers to notify the user about successful or failure when calling the service:
        <mx:RemoteObject id="contactsService" destination="contactsService" fault="onFault(event)">
            <mx:method name="findAll" result="queryResult(event)" />
            <mx:method name="create" result="crudResult(event)"/>
            <mx:method name="remove" result="crudResult(event)"/>
            <mx:method name="update" result="crudResult(event)"/>
        </mx:RemoteObject>
    

    Next, the corresponding service is called based on a user action. We pass the data that is wrapped in the event and we also set some values to a asynchronous token when calling the service. Since the asynchronous token is a dynamic class, we set some of our own values like identifying the method that was called. This will help us know for what remote service call the a result event is returning for later.

                private function contactEventHandler(event:ContactEvent) : void {
                    var method : int = event.action;
                    switch (method) {
                        case (ContactEvent.CREATE_CONTACT):
                            asynToken = contactsService.create(event.contact);
                            asynToken.method = "add";
                            break;
                        case (ContactEvent.EDIT_CONTACT):
                            asynToken = contactsService.update(event.contact);
                            asynToken.method = "update";
                            break;
                        default:
                            break;
                    }
                }
    

    Now when the result returns, we can inform the user:

                private function crudResult(event:ResultEvent) : void {
    
                    var action:String = event.token.method;
                    switch (action) {
                        case "add":
                            Alert.show("New Contact added to database", "Contact Added");
                            break;
                        case "delete":
                            Alert.show("Contact deleted from database", "Contact Deleted");
                            break;
                        case "update":
                            Alert.show("Contact updated in database", "Contact Updated");
                            break;
                        default:
                            break;
                    }
                }
    

    Now the client side implementation of the data push part. Remember, on all CRUD operations on hibernate, using the Spring MessageTemplete, we were pushing the message to the message bus. Now on the client, we need to listen in on the channel. First we need a ChannelSet on the client pointing to a amfpolling endpoint on the server (the amf endpoints are defined in services-config.xml):

        <mx:ChannelSet id="cs">
            <mx:AMFChannel url="/DataPushTest/messagebroker/amfpolling"/>
        </mx:ChannelSet>
    

    Next you need to create a consumer that uses that ChannelSet and is connected to the contactsMessaging destination.

       <mx:Consumer id="contactsConsumer" destination="contactsMessaging" channelSet="{cs}"
            message="messageHandler(event)"/>
    

    This consumer will also have to explicitly subscribe to the messaging destination. In the sample app, I have done it in the init() method which is called after creationComplete():

                private function init() : void {
                    contactsConsumer.subscribe();
                    ............
                    ....................
                }
    

    FINALLY….some logic to mange the data that has been pushed through consumer. Remember the header name and head value set by the hibernate DAO, we use that to determine what action to take. The actual hibernate entity, the contact, comes back in the as the message body. We have an array collection of contacts and we either add a contact to it, update a contact in the collection by looping through it and finding it or delete it using the same logic. If you’ve declared your contacts arrayCollection using the [Binding] tag, databinding will ensure that the changes to the arrayCollection are seen by the user.

                private function messageHandler(event:MessageEvent) : void {
    
                    var action:String = event.message.headers.method;
                    var contact : Contact;
                    var i:int;
                    switch (action) {
                        case "create":
                            contact = event.message.body as Contact;
                            acContacts.addItem(contact);
                            break;
                        case "update":
                            contact = event.message.body as Contact;
                            for (i=0; i < acContacts.length; i++) {
                                if (acContacts[i].id == contact.id) {
                                    acContacts.setItemAt(contact, i);
                                }
                            }
                            break;
                        case "delete":
                            var id : int = event.message.body as int;
                            for (i =0; i < acContacts.length; i++) {
                                if (acContacts[i].id == id){
                                    acContacts.removeItemAt(i);
                                }
                            }
                            break;
                        defaut:
                            break;
                        }
                    }
    

    Note, you should have the ActionScript ContactVO mapped to the hibernate Contact:

        [RemoteClass(alias="com.datapushtest.entities.Contact")]
        [Bindable]
        public class Contact {
        .............
        ..................................
    




Tags: > > > > > > > > > > > > > >

June 3, 2009 at 12:40 am by Bigyan Basnet