Artemis/Hibernate Apollo Application
Well it has been a while that I have been promising to document this sample app. Here is the basis. ArtemisEmployee is an application that uses a Hibernate back end with an embedded hsqldb database to store the employee data. The UI is built with Apollo and the data streams between the Java back end and the Apollo UI via the Artemis Bridge.
Here is the code involved in the Java back end. It is a very simple application consisting of a single bean class and a delegate for point of connection. The delegate has 2 methods for getting and Employee object and updating an Employee object.
Here is the Employee.hbm.xml file:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated May 9, 2007 7:38:47 AM by Hibernate Tools 3.2.0.beta8 -->
<hibernate-mapping package="com.everythingflex.artemisEmployee.vo" default-lazy="false">
<class name="Employee" table="Employee">
<id name="employeeId" type="int">
<column name="EMPLOYEE_ID" />
<generator class="assigned" />
</id>
<property name="firstName" type="string">
<column name="FIRST_NAME" length="50" not-null="true" />
</property>
<property name="lastName" type="string">
<column name="LAST_NAME" length="50" not-null="true" />
</property>
<property name="email" type="string">
<column name="EMAIL" length="50" not-null="true" />
</property>
<property name="jobTitle" type="string">
<column name="JOB_TITLE" length="50" not-null="true" />
</property>
</class>
</hibernate-mapping>
Here is the corresponding Employee.java bean:
public class Employee {
private int employeeId;
private String firstName;
private String lastName;
private String email;
private String jobTitle;
/**
*
*/
public Employee() {
super();
// TODO Auto-generated constructor stub
}
/**
* @param employeeId
* @param firstName
* @param lastName
* @param email
* @param jobTitle
*/
public Employee(int employeeId, String firstName, String lastName, String email, String jobTitle) {
super();
this.employeeId = employeeId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.jobTitle = jobTitle;
}
/**
* @return the email
*/
public String getEmail() {
return email;
}
/**
* @param email the email to set
*/
public void setEmail(String email) {
this.email = email;
}
/**
* @return the employeeId
*/
public int getEmployeeId() {
return employeeId;
}
/**
* @param employeeId the employeeId to set
*/
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
/**
* @return the firstName
*/
public String getFirstName() {
return firstName;
}
/**
* @param firstName the firstName to set
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* @return the lastName
*/
public String getLastName() {
return lastName;
}
/**
* @param lastName the lastName to set
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}
/**
* @return the jobTitle
*/
public String getJobTitle() {
return jobTitle;
}
/**
* @param position the jobTitle to set
*/
public void setJobTitle(String jobTitle) {
this.jobTitle = jobTitle;
}
}
The Delegate is the point of access between Apollo and Java. Here is the Delegate class:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import com.effectiveui.artemis.ArtemisBridge;
import com.effectiveui.artemis.event.ArtemisResultEvent;
import com.everythingflex.artemisEmployee.vo.Employee;
public class Delegate {
private SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
public ArtemisResultEvent getEmployee(Integer id){
ArtemisResultEvent result = new ArtemisResultEvent();
Session session = null;
try{;
session = sessionFactory.openSession();
Employee e = (Employee) session.load(Employee.class,id);
result.setResult(e);
} catch (Exception E) {
System.out.print(E);
} finally {
session.flush();
session.close();
}
return result;
}
public ArtemisResultEvent updateEmployee(Employee employee){
ArtemisResultEvent result = new ArtemisResultEvent();
Session session = null;
Transaction tx = null;
Boolean results = true;
try{
session = sessionFactory.openSession();
tx = session.beginTransaction();
session.update(employee);
tx.commit();
} catch (Exception E) {
results = false;
System.out.print(E);
} finally {
tx = null;
session.flush();
session.close();
}
result.setResult(results);
return result;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ArtemisBridge.getInstance().bootstrap();
//Delegate d = new Delegate();
//d.getEmployee(1);
}
}
OK, now for the Apollo side of the application:
Here is the Employee ActionScript class that maps to the Java Employee class:
{
[RemoteClass(alias="com.everythingflex.artemisEmployee.vo.Employee")]
[Bindable]
public class Employee
{
public var employeeId:int;
public var firstName:String;
public var lastName:String;
public var email:String;
public var jobTitle:String;
}
}
This is the bridge class that is making the connection between Apollo and Java and handling the results.
{
import com.effectiveui.artemis.mirror.ArtemisObject;
import com.effectiveui.artemis.mirror.call.ArtemisObjectCall;
import flash.utils.getQualifiedClassName;
import mx.rpc.events.ResultEvent;
import flash.events.EventDispatcher;
import com.everythingflex.artemisEmployee.vo.Employee;
public class EmployeeArtemisObject extends ArtemisObject
{
public static const LIB_ID:String = "com.everythingflex.artemisEmployee.business.Delegate";
public function EmployeeArtemisObject(){
super();
}
public function getEmployee(employeeId:int):void{
var artemisCall:ArtemisObjectCall = new ArtemisObjectCall(LIB_ID, "getEmployee", employeeId);
artemisCall.addEventListener(ResultEvent.RESULT, getEmployeeResult);
send(artemisCall);
}
public function getEmployeeResult(e:ResultEvent):void{
EventDispatcher(e.target).removeEventListener(e.type, getEmployeeResult);
var rEvent:ResultEvent = new ResultEvent("employeeResult", false, true, e.result);
dispatchEvent(rEvent);
}
public function updateEmployee(employee:Employee):void{
var artemisCall:ArtemisObjectCall = new ArtemisObjectCall(LIB_ID, "updateEmployee", employee);
artemisCall.addEventListener(ResultEvent.RESULT, updateEmployeeResult);
send(artemisCall);
}
public function updateEmployeeResult(e:ResultEvent):void{
EventDispatcher(e.target).removeEventListener(e.type, updateEmployeeResult);
var rEvent:ResultEvent = new ResultEvent("updateEmployeeResult", false, true, e.result);
dispatchEvent(rEvent);
}
}
}
Finally the main Apollo application file which makes the connection to the bridge and the calls to get the Employee objects from the hsqldb and also update the Employee as well.
<mx:ApolloApplication xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:custom="custom.*"
creationComplete="initArtemisBridge()"
backgroundImage="@Embed('bg.png')"
backgroundColor="#FFFFFF" height="420" width="340" layout="absolute">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import com.effectiveui.artemis.mirror.call.ArtemisObjectCall;
import mx.rpc.events.ResultEvent;
import com.effectiveui.artemis.event.ArtemisServiceEvent;
import com.effectiveui.artemis.ArtemisBridge;
import com.everythingflex.artemisEmployee.vo.Employee;
import com.everythingflex.artemisEmployee.bridge.EmployeeArtemisObject;
private var bridge:ArtemisBridge = ArtemisBridge.getInstance();
private var employeeArtemisObject:EmployeeArtemisObject;
[Bindable]
private var isBridgeStarted:Boolean = false;
[Bindable]
private var updateEnabled:Boolean = false;
[Bindable]
private var employee:Employee;
private function initArtemisBridge():void{
ti.text = "Bridge Not Running \n";
bridge.startServer();
bridge.addEventListener(ArtemisServiceEvent.ARTEMIS_STARTED, bridgeStarted);
employeeArtemisObject = new EmployeeArtemisObject();
employeeArtemisObject.addEventListener("employeeResult", employeeResult);
employeeArtemisObject.addEventListener("updateEmployeeResult", updateEmployeeResult);
}
private function bridgeStarted(e:ArtemisServiceEvent):void{
ti.text += "Bridge Started \n";
isBridgeStarted = true;
}
private function getEmployee():void{
ti.text += "getEmployee(" + employeeId.value + ") \n";
ti.verticalScrollPosition += 50;
employeeArtemisObject.getEmployee(employeeId.value);
}
private function employeeResult(_employee:ResultEvent):void{
employee = Employee(_employee.result);
if(employee.employeeId > 0){
updateEnabled = true;
}
}
private function updateEmployee(employee:Employee):void{
ti.text += "updateEmployee(" + employee + ") \n";
ti.verticalScrollPosition += 75;
employee.firstName = firstName.text;
employee.lastName = lastName.text;
employee.email = email.text;
employee.jobTitle = jobTitle.text;
employeeArtemisObject.updateEmployee(employee);
}
private function updateEmployeeResult(status:ResultEvent):void{
if(status.result){
ti.text += "update successful \n";
} else {
ti.text += "update failed \n";
}
}
]]>
</mx:Script>
<mx:Style>
TextArea {
backgroundAlpha: .2;
color: #FFFFFF;
}
TextInput {
backgroundAlpha: .2;
color: #FFFFFF;
}
Label {
color: #FFFFFF;
}
</mx:Style>
<mx:TextArea id="ti" width="300" height="189" x="19" y="33"/>
<mx:NumericStepper minimum="1" maximum="9" id="employeeId" stepSize="1" x="151.5" y="230" enabled="{isBridgeStarted}" width="45"/>
<mx:Button click="getEmployee()" label="Load Employee" x="207" y="230" enabled="{isBridgeStarted}"/>
<mx:Label x="19" y="232" text="Search by EmpoyeeId:"/>
<mx:Label x="19" y="260" text="First Name:"/>
<mx:Label x="19" y="286" text="Last Name:"/>
<mx:Label x="19" y="312" text="Email:"/>
<mx:Label x="19" y="340" text="Job Title:"/>
<mx:TextInput x="89" y="284" id="lastName" text="{employee.lastName}" width="230"/>
<mx:TextInput x="89" y="258" id="firstName" text="{employee.firstName}" width="230"/>
<mx:TextInput x="89" y="310" id="email" text="{employee.email}" width="230"/>
<mx:TextInput x="89" y="338" id="jobTitle" text="{employee.jobTitle}" width="230"/>
<mx:Label x="19" y="15" text="Status Log"/>
<mx:Button x="257" y="378" label="Update" width="62" click="updateEmployee(employee)" enabled="{updateEnabled}"/>
</mx:ApolloApplication>
So, here is what actually is taking place in this example.
First, (after manually starting the Java ArtemisBridge) the Apollo application makes its connection to the listening port on the Java class.

Then the user loads an Employee object by id using the Hibernate load method. The Java Employee object is retrieved from the hsqldb and passed into the Apollo application.

The user then may edit the Employee data and send it back to Java where it is updated using the Hibernate update method.

The ArtemisBridge will obviously need to started automatically by the Apollo application and this will be possible in future versions of the Apollo runtime.



May 30th, 2007 at 12:37 pm
Seriously, this is awesome, Rich. Thanks a lot for pushing the technology!
May 30th, 2007 at 12:54 pm
No problem Tony, I think Artemis is a great contribution to the community. The only issue I am having is that if I try to return a List of Employee objects through the Bridge, it fails. Any ideas on what the issue is? I can pass primitive data types or Objects (like the Employee object, but not Lists of Objects.
May 30th, 2007 at 1:32 pm
Very good example, but the hibernate implementation needs to be more efficient.
Regards
May 30th, 2007 at 1:40 pm
Please ellaborate on “the hibernate implementation needs to be more efficient.”.
May 31st, 2007 at 12:59 am
ok, sorry.
In each method of the delegate you build a new SessionFactory. The SessionFactory is a heavy object (i.e. proxy classes generation by cglib). In the example you only have a mapped object, but in a real world application the SessionFactory is a singleton (with many mapped objects).
Regards
May 31st, 2007 at 12:37 pm
Duh, sorry about that. That is not my general practice :-).
I updated the code above moving the SessionFactory configuration to a Class variable.
July 31st, 2007 at 10:22 pm
なんとしてでも、地球を死の惑星にはしたくない。未来に向かって、
地球上のすべての生物との共存をめざし、むしろこれからが、
人類のほんとうの“あけぼの”なのかもしれないとも思うのです。