Flex sample application

SevenHats project has special bundle for Flex integration. It uses BlazeDS project for Flex movie-serverside collaboration. If you want to access server side from Flex movie please follow steps from this tutorial.

Let's create small application which will allow us to manage users. You can create/edit/delete user from Flex movie and from JSF page. Also you will see that all changes, made at JSF part will be updated at Flex part.
For this we need to create UserService and publish it at OSGi environment, we need to configure BlazeDS and find published services at OSGi environment. You can use org.sevenhats.flex.SevenhatsFactory class from org.sevenhats.flex bundle for this.

We will create new bundle org.sevenhats.flexsample. We need 2 parts of it: Flex client code and server side code.

Flex client

 Create file index.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                layout="vertical" verticalAlign="middle" horizontalAlign="center"
                xmlns:screen="org.sevenhats.flexsample.userlist.view.screen.*">

    <screen:UserForm/>

</mx:Application>

 Create file org/sevenhats/flexsample/userlist/view/entity/UserItem.as

package org.sevenhats.flexsample.userlist.view.entity
{
[RemoteClass(alias="org.sevenhats.flexsample.domain.UserItem")]
[Bindable]
public class UserItem
{
    public var id:Number;
    public var login:String;
}
}

 Create file org/sevenhats/flexsample/userlist/view/screen/UserForm.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
                layout="vertical" width="488" height="384" creationComplete="getList()">
 <mx:Script>
        <![CDATA[
        import mx.collections.ArrayCollection;
        import org.sevenhats.flexsample.userlist.view.entity.UserItem;
        import mx.rpc.events.ResultEvent;
        import mx.rpc.events.FaultEvent;
        import mx.controls.Alert;
        import mx.messaging.messages.IMessage;

        [Bindable]
        private var userItems:ArrayCollection;

        private var userItem:UserItem;

        public function save():void
        {
            this.userItem = new UserItem();
            this.userItem.id = new Number(idText.text);
            this.userItem.login = loginText.text;
            userService.save(userItem);
        }

        public function onResultSave(event:ResultEvent):void
        {
            status = "User was successfully saved with ID: " + UserItem(event.result).id;
            getList();
        }

        public function remove():void
        {
            if (userDatagrid.selectedItem != null) {
                userItem = userDatagrid.selectedItem as UserItem;
                userService.remove(userItem);
            } else {
                Alert.show("Select user to delete");
            }
        }

        public function onResultRemove(event:ResultEvent):void
        {
            status = "Deletion succeeded!";
            getList();
        }

        public function getList():void
        {
            userService.getList();
        }

        public function onResultGetList(event:ResultEvent):void
        {
            userItems = event.result as ArrayCollection;
        }

        public function setDefault():void
        {
            idText.text = "";
            loginText.text = "";
        }

        public function onFault(event:FaultEvent):void
        {
            Alert.show(event.fault.message);
        }

        private function messageHandler(message:IMessage):void
        {
            getList();
        }

        ]]>
    </mx:Script>

    <mx:Form width="100%" height="100%" defaultButton="{saveButton}">
        <mx:FormHeading label="User List" width="100%"/>
        <mx:FormItem label="ID:" width="127">
            <mx:TextInput width="100%" id="idText"
                          text="{UserItem(userDatagrid.selectedItem).id}" editable="false" enabled="false"/>
        </mx:FormItem>
        <mx:FormItem label="Login:" width="345">
            <mx:TextInput width="100%" id="loginText"
                          text="{UserItem(userDatagrid.selectedItem).login}"/>
        </mx:FormItem>
        <mx:DataGrid id="userDatagrid" width="100%" height="100%" dataProvider="{userItems}">
            <mx:columns>
                <mx:DataGridColumn headerText="ID" dataField="id" width="30"/>
                <mx:DataGridColumn headerText="Login" dataField="login"/>
            </mx:columns>
        </mx:DataGrid>
    </mx:Form>
    <mx:Grid>
        <mx:GridRow width="100%" height="100%">
            <mx:GridItem width="100%" height="100%">
                <mx:Button label="Subscribe for updates" id="subscribeButton" click="consumer.subscribe()" enabled="{!consumer.subscribed}"/>
            </mx:GridItem>
            <mx:GridItem width="100%" height="100%">
                <mx:Button label="Unsubscribe from updates" id="unsubscribeButton" click="consumer.unsubscribe()" enabled="{consumer.subscribed}"/>
            </mx:GridItem>
        </mx:GridRow>
    </mx:Grid>

    <mx:ControlBar horizontalAlign="center">
        <mx:Button label="New" click="setDefault()"/>
        <mx:Button label="Save" id="saveButton" click="save()"
                   textAlign="center"/>
        <mx:Button label="Delete" click="remove()"/>
    </mx:ControlBar>

    <mx:RemoteObject id="userService" showBusyCursor="true"
                     fault="onFault(event)" destination="userService">
        <mx:method name="save" result="onResultSave(event)" fault="onFault(event)"/>
        <mx:method name="remove" result="onResultRemove(event)" fault="onFault(event)"/>
        <mx:method name="getList" result="onResultGetList(event)" fault="onFault(event)"/>
    </mx:RemoteObject>

    <mx:Consumer id="consumer" destination="feed" message="messageHandler(event.message)"/>

</mx:TitleWindow>

We use Consumer/Producer communication model to receive events from server side.
To configure Flex server side you have to create file services-config.xml at some package (for example, org.sevenhats.flexsample.flex.config) and to export this package using Manifest.mf.

Export-Package: org.sevenhats.flexsample.flex.config

You should expose a flex configuration service (org.sevenhats.flex.FlexConfiguration) to let know flex servlet about service-config.xml location.

<bean id="flexConfiguration"
		class="org.sevenhats.flex.FlexConfigurationBean">
		<property name="serviceConfigUrl"
			value="/org/sevenhats/flexsample/flex/config/services-config.xml" />
	</bean>
<osgi:service ref="flexConfiguration"
		interface="org.sevenhats.flex.FlexConfiguration" />

The bundle that exposes this service should import packages org.sevenhats.flex and org.sevenhats.flexsample.flex.config.

Import-Package:
...
 org.sevenhats.flex,
 org.sevenhats.flexsample.flex.config
...

File services-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
	<services>
		<service-include file-path="remoting-config.xml" />
		<service id="message-service" class="flex.messaging.services.MessageService">
			<adapters>
				<adapter-definition id="actionscript"
					class="flex.messaging.services.messaging.adapters.ActionScriptAdapter"
					default="true" />
			</adapters>
			<default-channels>
				<channel ref="my-streaming-amf" />
				<channel ref="my-polling-amf" />
			</default-channels>
			<destination id="feed">
				<!--
					Destination specific channel configuration can be defined if needed
					<channels> <channel ref="my-streaming-amf"/> </channels>
				-->
			</destination>
		</service>
	</services>
    <!-- Sevenhats factory registration -->
	<factories>
		<factory id="sevenhats" class="org.sevenhats.flex.SevenhatsFactory" />
	</factories>
	<channels>
		<channel-definition id="channel-amf"
			class="mx.messaging.channels.AMFChannel">
			<endpoint
				url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
				class="flex.messaging.endpoints.AMFEndpoint" />
			<properties>
				<polling-enabled>false</polling-enabled>
			</properties>
		</channel-definition>
		<channel-definition id="my-streaming-amf"
			class="mx.messaging.channels.StreamingAMFChannel">
			<endpoint
				url="http://{server.name}:{server.port}/{context.root}/messagebroker/streamingamf"
				class="flex.messaging.endpoints.StreamingAMFEndpoint" />
		</channel-definition>
		<channel-definition id="my-polling-amf"
			class="mx.messaging.channels.AMFChannel">
			<endpoint
				url="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling"
				class="flex.messaging.endpoints.AMFEndpoint" />
			<properties>
				<polling-enabled>true</polling-enabled>
				<polling-interval-seconds>4</polling-interval-seconds>
			</properties>
		</channel-definition>
	</channels>
</services-config>

Please pay attention that endpoints at SevenHats framework should match pattern '/messagebroker/*' for endpoints as in example above.

Create file remoting-config.xml at the same package org.sevenhats.flexsample.flex.config.

<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
         class="flex.messaging.services.RemotingService">

    <adapters>
        <adapter-definition id="java-object"
                            class="flex.messaging.services.remoting.adapters.JavaAdapter"
                            default="true"/>
    </adapters>

    <default-channels>
        <channel ref="channel-amf"/>
    </default-channels>

    <destination id="userService">
        <properties>
            <factory>sevenhats</factory>
            <source>org.sevenhats.flexsample.api.UserService</source>
        </properties>
    </destination>
</service>

org.sevenhats.flexsample.api.UserService is service interface at OSGi environment. You can see how we define this service and its implementation below in flexsample-context.xml and flexsample-context-osgi.xml.

Compile it to swf with Ant

<mxmlc file="org.sevenhats.flexsample/src-flash/index.mxml" keep-generated-actionscript="false"
  output="org.sevenhats.flexsample/src/org/sevenhats/flexsample/flash/index.swf">
       <compiler.services>org.sevenhats.flexsample.flex.config/src/org/sevenhats/flex/services-config.xml</compiler.services>
       <compiler.context-root>/</compiler.context-root>
       <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml"/>
       <source-path path-element="${FLEX_HOME}/frameworks"/>
</mxmlc>

Server side

Create Transfer object for User

package org.sevenhats.flexsample.domain;

/**
 * Transfer object.
 * @author Alexey Chystoprudov
 *
 */
public class UserItem {

    private long id;
    private String login;

    public UserItem(Long id2, String lastName) {
    	this.id = id2;
    	this.login = lastName;
    }

    public UserItem() {
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UserItem todoItem = (UserItem) o;
        if (id != todoItem.id) return false;
        return true;
    }

    @Override
    public int hashCode() {
        return (int) (id ^ (id >>> 32));
    }
}

Create UserService interface

package org.sevenhats.flexsample.api;

import org.sevenhats.flexsample.domain.UserItem;
import java.util.List;

/**
 * Service for TodoItem operations.
 * @author Alexey Chystoprudov
 *
 */
public interface UserService {
    void remove(UserItem todoItem) throws Exception;
    UserItem save(UserItem todoItem) throws Exception;
    UserItem findById(UserItem todoItem) throws Exception;
    List<UserItem> getList() throws Exception;
}

And implementation for it

package org.sevenhats.flexsample;

import org.sevenhats.flexsample.api.UserService;
import org.sevenhats.flexsample.domain.UserItemRepository;

/**
 * Service implementation.
 * @author Alexey Chystoprudov
 *
 */
public class UserServiceImpl implements UserService {
    private UserItemRepository userItemRepository;
    public void setUserItemRepository(UserItemRepository userItemRepository) {
        this.userItemRepository = userItemRepository;
    }

    ...delegate calls to userItemRepository...
}

Create UserItemRepository interface

package org.sevenhats.flexsample.domain;

import java.util.List;


/**
 * Repository interface.
 * @author Alexey Chystoprudov
 *
 */
public interface UserItemRepository {
    void remove(UserItem todoItem);
    UserItem save(UserItem todoItem);
    UserItem findById(UserItem todoItem) throws Exception;
    List<UserItem> getList();
}

Create implementation for it

package org.sevenhats.flexsample.domain;

import java.util.*;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.sevenhats.persistence.api.EntityFactory;
import org.sevenhats.persistence.api.PersistentEntity;
import org.sevenhats.um.api.User;

/**
 * Stores items in memory.
 * @author Alexey Chystoprudov
 *
 */
public class UserItemMemoryDao implements
        UserItemRepository {
    private EntityManager entityManager;
    private EntityFactory factory;

    public void setEntityManager(EntityManager entityManager) {
	this.entityManager = entityManager;
    }

    public void setEntityFactory(EntityFactory factory) {
	this.factory = factory;
    }

    public UserItem save(UserItem todoItem) {
        User user = null;
        if(todoItem.getId() != 0) {
            user = entityManager.find(User.class, todoItem.getId());
        } else {
            user = factory.createEntity(User.class);
        }
        user.setLastName(todoItem.getLogin());
        user.setFirstName(todoItem.getLogin());
        entityManager.persist(user);
        return todoItem;
    }

    public void remove(UserItem todoItem) {
    	entityManager.remove(entityManager.find(User.class, todoItem.getId()));
    }

    public UserItem findById(UserItem todoItem) throws Exception {
        long id = todoItem.getId();
        User user = entityManager.find(User.class, id);
        if (user == null) {
            throw new Exception("Could not find an item with id " + id);
        }
        todoItem = new UserItem(user.getId(), user.getLastName());
        return todoItem;
    }

    @SuppressWarnings("unchecked")
    public List<UserItem> getList() {
    	Query query = entityManager.createQuery("from "
			+ User.class.getName() + " entities");
	List<PersistentEntity> resultList = query.getResultList();
	List<UserItem> result = new LinkedList<UserItem>();
	for(PersistentEntity user: resultList) {
	    result.add(new UserItem(user.getId(), ((User) user).getLastName()));
	}
        return result;
    }
}

Now you can create /META-INF/spring/flexsample-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <bean id="userService" class="org.sevenhats.flexsample.UserServiceImpl">
        <property name="userItemRepository" ref="userItemRepository"/>
    </bean>

    <bean id="userItemRepository" class="org.sevenhats.flexsample.domain.UserItemMemoryDao">
    	<property name="entityManager" ref="entityManager"></property>
    	<property name="entityFactory" ref="entityFactory"></property>
    </bean>

     <bean id="userListBean" class="org.sevenhats.flexsample.UserListBeanImpl">
     	<property name="userService" ref="userService"/>
    </bean>
</beans>

And /META-INF/spring/flexsample-context-osgi.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:osgi="http://www.springframework.org/schema/osgi"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <osgi:service ref="userService"
        interface="org.sevenhats.flexsample.api.UserService" />

   <osgi:service ref="userListBean"
        interface="org.sevenhats.flexsample.api.UserListBean" />

    <osgi:reference id="entityManager"
	interface="javax.persistence.EntityManager" />

    <osgi:reference id="entityFactory"
	interface="org.sevenhats.persistence.api.EntityFactory" />

</beans>

And MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Sample application with Flex 
Bundle-SymbolicName: org.sevenhats.flexsample
Bundle-Version: 1.0.0
Bundle-Vendor: TeamDev Ltd.
Import-Package: flex.messaging,
 flex.messaging.messages,
 javax.el,
 javax.faces.context,
 javax.faces.event,
 javax.persistence,
 javax.servlet,
 javax.servlet.http,
 javax.servlet.jsp,
 org.apache.commons.logging,
 org.eclipse.equinox.http.helper,
 org.osgi.framework;version="1.3.0",
 org.osgi.service.http;version="1.2.0",
 org.osgi.service.packageadmin;version="1.2.0",
 org.osgi.util.tracker;version="1.3.3",
 org.sevenhats.bizcommons.api,
 org.sevenhats.flex,
 org.sevenhats.flexsample.flex.config,
 org.sevenhats.jsf.api,
 org.sevenhats.jsf.flow,
 org.sevenhats.persistence.api,
 org.sevenhats.um.api,
 org.sevenhats.util,
 org.sevenhats.util.servlet,
 org.sevenhats.web.api,
 org.sevenhats.web.beans,
 org.springframework.beans,
 org.springframework.beans.factory,
 org.springframework.beans.factory.config,
 org.springframework.beans.factory.support,
 org.springframework.beans.factory.xml,
 org.springframework.context,
 org.springframework.context.i18n,
 org.springframework.context.support,
 org.springframework.core,
 org.springframework.core.enums,
 org.springframework.core.io,
 org.springframework.core.style,
 org.springframework.osgi.context,
 org.springframework.osgi.context.support,
 org.springframework.osgi.util,
 org.springframework.util,
 org.springframework.util.xml,
 org.springframework.web.context,
 org.springframework.web.context.request,
 org.springframework.web.context.support,
 org.springframework.web.filter
Export-Package: org.sevenhats.flexsample.api,
 org.sevenhats.flexsample.flash,
 org.sevenhats.flexsample.web
Require-Bundle: org.sevenhats.flex;version="1.0.0" 
Eclipse-RegisterBuddy: org.sevenhats.flex

Last line of this file is grant access from org.sevenhats.flex bundle to the classes of org.sevenhats.flexsample bundle.

You can see results of your work if you start bundle and open page http://localhost:8080/org/sevenhats/flexsample/flash/index.swf and http://localhost:8080/org/sevenhats/flexsample/web/index.jsf

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.