|
Introduction
Virtual Collaboration for Lotus Sametime is a new asset in the Lotus portfolio enabling small-team collaboration in virtual worlds. A platform for virtual worlds is shipped with the asset that enables developers to build their own applications for virtual worlds in an enterprise context. The existing API tackles the basic infrastructure and management requirements when an integrated virtual world application is built.
The goal of this white paper is to demonstrate to developers who are already familiar with developing JavaTM 2 Platform, Enterprise Edition (J2EE) applications how to write their own virtual world applications. We do this by introducing the architecture of the virtual worlds platform and then providing a tutorial to write an example application in the area of building automation.
The sample application connects a smart doll house (equipped with lights and sensors) to the virtual world (containing a 3D representation of that house), enabling you to control the lights and to monitor the sensors from within the virtual world as well as from a Web page.
Architecture
A virtual world application typically consists of two parts: the “in-world” (inside the virtual world) part, containing 3D objects, textures, and scripts; and the enterprise application part, usually a J2EE component, which integrates the application into an existing enterprise infrastructure (for example, using some services provided by other enterprise applications).
The Virtual Collaboration platform contains different infrastructure services for applications. The three most important services do the following:
- manage space in virtual worlds, for example, create spaces the user can access
- manage content that should be accessible by the virtual world application, for example, by uploading presentations
- integrate enterprise user repositories, for example, use LDAP information to create an in-world avatar (an avatar is the representation of a user in a virtual world)
Figure 1 shows an overview of the architecture, containing a prospective virtual world application and the existing Virtual Collaboration platform.
Figure 1. Virtual World Application architecture

As stated above, a virtual world application consists of two parts: the in-world part that runs on a region on the OpenSim virtual world server (a space, in our terminology), and the J2EE part that runs on the application server and can access the services through a Representational State Transfer (REST) interface as well as access the services' Enterprise JavaBeans (EJBs) 3.0.
Usually these two parts of the application interact with each other to fulfill a given use case. For example, using the application scenario we will build later, when a temperature alarm is sent to the J2EE application, it can modify the status of an object in-world to display the emergency status.
Overview and prerequisites
In this section we provide an overview of how build a sample virtual world application that connects a smart doll house (equipped with lights and sensors) to the virtual world (containing a 3-D representation of that house). From within the virtual world as well as from a Web page you can then control the lights and monitor the sensors.
The application uses two services of the Virtual Collaboration platform: (1) the space service, to create and manage spaces and space templates; and (2) the content service, to upload and manage the template files.
The overall goal of the application is to automatically provide a space in the virtual world that contains a predefined doll house and then provide the capabilities to control the devices.
A high-level flow of the first step, the provisioning of the space, is shown in figure 2. First, the application determines whether a virtual space with the doll house already exists (query to the space service). If yes, all is set up correctly; if no, it checks whether the building automation template is already available (query to the space service). If yes, then the application creates a virtual space from this space template (also using the space service); if not, the application creates a new space template.
Figure 2. Virtual space provisioning basic flow

This part of the application is a two-step process: (1) the template file, which is packaged with the application, is uploaded to the Virtual Collaboration platform (using the content service), and then (2) a new space template is created that's linked to the just-uploaded template file (the template is created with a call to the space service). After a template is created, the application can then use the template to create a virtual space instance from it.
The second part of the application—the capabilities to control the devices—is a simple JavaServer Pages (JSP) page that uses a JavaScript framework to make asynchronous calls to control the devices, such as turning the lights on and off. Since it is deployed directly to the Virtual Collaboration server, the Web interface is available on that server.
But now the deep dive into how to realize that!
Prerequisites
Here's what you need to have installed before proceeding:
Also, you must have access to a Virtual Collaboration for Lotus Sametime / Sametime 3D server.
The overall steps in the procedure will be as follows:
- Start Eclipse with a new workspace.
- Create an Enterprise JavaBean (EJB) project with data and bean classes.
- Create a Web project with servlets that access the data from the bean project.
- Create an Enterprise ARchive (EAR) project that bundles the EJB and Web projects.
- Deploy it on the Virtual Collaboration / Sametime 3D server.
Creating an EJB project with data and bean classes
Now for the detailed steps:
- After you've started Eclipse with a new workspace, select the Workbench icon in the upper-right-hand corner, and navigate to the Project Explorer.
- Create a new EJB project (named “BuildingAutomationEJB”) by right-clicking in the white area and selecting New > EJB Project (see figure 3).
Figure 3. Creating an EJB project

3. In the next window (see figure 4), fill in the Project name as “BuildingAutomationEJB,” and select “3.0” for the EJB Module version.
4. Click in the Target Runtime section and then click the New button, to add the locally installed IBM WebSphere Application Server Community Edition (WASCE).
Figure 4. New EJB Project window

5. Select the correct server adapter “IBM WASCE v2.1”, as shown in figure 5 (if not listed, click the Download additional server adapters link and click Next).
Figure 5. New Server Runtime Environment window

6. Specify the correct location (directory) of your local WASCE installation and click Finish (see figure 6).
Figure 6. New IBM WASCE v2.1 Runtime window

7. Finally, make sure the New EJB Project window looks similar to that shown in figure 7, in which the Target Runtime field is set to “IBM WASCE v2.1”, the EJB Module version field is set to “3.0,” and the Configuration field is “Default Configuration for IBM WASCE v2.1”. Click Finish.
Figure 7. Review the New EJB Project window fields

Now we want to add the Java Persistence API (JPA) capabilities and add the basic data classes (entities, in the terminology of JPA) to the project. To do that:
1. Right-click the EJB project and open the Properties.
2. Select Project Facets and check the Java Persistence box; click OK (see figure 8).
Figure 8. Properties for BuildingAutomationEJB window

The JPA capabilities for the EJB project are now enabled, and an an additional JPA Content entry is visible in the project tree.
Adding the data classes
Let's now add three data classes, Building, Light, and Sensor, to the “com.ibm.vw.apps.demo.buildingautomation.model” package:
1. Right-click the EJB project and select New > Class; a window displays in which you can fill in the Package and class Name fields (see figure 9).
Figure 9. New Java Class window

2. Copy/paste the code in listing 1 into the Building class.
Listing 1. Code for Building class
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation.model;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import com.ibm.vw.platform.ObjectID;
import com.ibm.vw.platform.Space;
import com.ibm.vw.platform.impl.ObjectIDImpl;
@Entity
public class Building
{
@Id
@GeneratedValue
Long id;
@OneToMany(targetEntity=Light.class, cascade={CascadeType.PERSIST, CascadeType.MERGE})
Set<Light> lights;
@OneToMany(targetEntity=Sensor.class, cascade={CascadeType.PERSIST, CascadeType.MERGE})
Set<Sensor> sensors;
@OneToOne(targetEntity=ObjectIDImpl.class, cascade={CascadeType.PERSIST, CascadeType.MERGE})
ObjectID<Space> spaceID;
String name;
public Building()
{
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public Set<Light> getLights()
{
return lights;
}
public void setLights(Set<Light> lights)
{
this.lights = lights;
}
public Set<Sensor> getSensors()
{
return sensors;
}
public void setSensors(Set<Sensor> sensors)
{
this.sensors = sensors;
}
public ObjectID<Space> getSpaceID()
{
return spaceID;
}
public void setSpaceID(ObjectID<Space> spaceID)
{
this.spaceID = spaceID;
}
public String getName()
{
return this.name;
}
public void setName(String name)
{
this.name = name;
}
}
3. Copy/paste the code in listing 2 into the Light class.
Listing 2. Code for the Light class
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Light
{
@Id
@GeneratedValue
Long id;
String name;
Boolean state;
public Light()
{
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Boolean getState()
{
return state;
}
public void setState(Boolean state)
{
this.state = state;
}
}
4. Copy/paste the code in listing 3 into the Sensor class.
Listing 3. Code for Sensor class
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Sensor
{
@Id
@GeneratedValue
Long id;
Integer value;
String name;
public Sensor()
{
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Integer getValue()
{
return value;
}
public void setValue(Integer value)
{
this.value = value;
}
}
Don't worry if red problem markers show up. We can resolve them one by one.
Resolving the problem markers
In these classes you can see that there are basic entities denoted with the @Entity annotation and they contain a few properties (with their respective getters and setters). The interesting part is the connection of the building to a virtual world space, done with the property spaceID of the type ObjectID. This references an existing virtual space in the Virtual Collaboration platform.
To be able to reference this type, we must add the Virtual Collaboration API bundle to the Java library path of the EJB project. These two .jar files are found in the Virtual Collaboration server installation and are called something similar to platformbase-0.11.jar and platformapp-0.11.jar.
To reference this type:
- Right-click on the EJB project and open Properties.
- Select Java Build Path and then click the Libraries tab.
- Click the Add External JAR button and add the above-cited files (see figure 10).
Figure 10. Add Virtual Collaboration libraries

4. Synchronize the entity classes with the persistence.xml configuration file by opening the JPA Content, right-clicking on persistence.xml, and selecting Synchronize Classes. All problem markers should now be resolved.
5. Finally, select the Order and Export tab and select the two library files as exported (see figure 11).
Figure 11. Order and Export tab

As there is a different database schema used, this information must be added to the persistence.xml configuration file. Also, the indirectly referenced class “com.ibm.vw.platform.impl.ObjectIDImpl” will be added to the managed class files.
That leads to the persistence.xml shown in listing 4 (open the file in an editor window and replace the content).
Listing 4. Code for persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="BuildingAutomationEJB"
transaction-type="JTA">
<jta-data-source>buildingautomationJTA
</jta-data-source>
<non-jta-data-source>buildingautomationNonJTA
</non-jta-data-source>
<class>com.ibm.vw.apps.demo.buildingautomation.model.Building
</class>
<class>com.ibm.vw.apps.demo.buildingautomation.model.Light
</class>
<class>com.ibm.vw.apps.demo.buildingautomation.model.Sensor
</class>
<class>com.ibm.vw.platform.impl.ObjectIDImpl
</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name='openjpa.Compatibility' value='strictIdentityValues=false' />
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)" />
<property name="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true, PrettyPrintLineLength=72" />
<property name="openjpa.jdbc.Schema" value="APP" />
<property name="openjpa.TransactionMode" value="managed" />
</properties>
</persistence-unit>
</persistence>
Adding a controller bean
Now we need to add a controller bean that provides access to the persistent entities. To add a bean:
- Right-click on the EJB project and select New > Session bean.
- In the Java package field, enter “com.ibm.vw.apps.demo.buildingautomation”, and for the Class name, use “BuildingController” (see figure 12).
- In the Create business interface section, select both the Remote and Local bean options.
Figure 12. Create Controller Session Bean

Now we use the content in listings 5—7 for the two bean interfaces and the implementation.
To do this, first paste the code of listing 5 into the local business interface (the file specified in the Local field in figure 11), then paste the code of listing 6 into the remote business interface (file specified in the Remote field in figure 11) and, finally, paste the code of listing 7 into the session bean class (file specified in the Class name field in figure 11).
The local interface defines a few operations to access the devices and sensors (see listing 5).
Listing 5. Local interface code
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation;
import java.util.List;
import java.util.Set;
import javax.ejb.Local;
import com.ibm.vw.apps.demo.buildingautomation.model.Building;
import com.ibm.vw.apps.demo.buildingautomation.model.Light;
import com.ibm.vw.apps.demo.buildingautomation.model.Sensor;
import com.ibm.vw.platform.ObjectID;
import com.ibm.vw.platform.Space;
@Local
public interface BuildingControllerLocal {
public ObjectID<Space> createBuilding();
public List<Building> getBuildings();
public Set<Light> getLights();
public Set<Sensor> getSensors();
public String getName();
public Boolean switchLight(Light l);
public Integer getSensorData(Sensor s);
}
The remote interface merely extends the local interface (see listing 6).
Listing 6. Remote interface code
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation;
import javax.ejb.Remote;
@Remote
public interface BuildingControllerRemote extends BuildingControllerLocal
{
}
The implementation implements both the local and the remote interfaces (see listing 7).
Listing 7. Implementation of both interfaces
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import com.ibm.vw.apps.demo.buildingautomation.model.Building;
import com.ibm.vw.apps.demo.buildingautomation.model.Light;
import com.ibm.vw.apps.demo.buildingautomation.model.Sensor;
import com.ibm.vw.helper.GlobalConstants;
import com.ibm.vw.helper.LocalizedString;
import com.ibm.vw.platform.Configuration;
import com.ibm.vw.platform.ObjectID;
import com.ibm.vw.platform.ObjectLink;
import com.ibm.vw.platform.PlatformService;
import com.ibm.vw.platform.Space;
import com.ibm.vw.platform.SpaceTemplate;
import com.ibm.vw.platform.SpaceType;
import com.ibm.vw.platform.api.ConfigurationServiceRemote;
import com.ibm.vw.platform.api.ContentServiceRemote;
import com.ibm.vw.platform.api.SpaceServiceRemote;
import com.ibm.vw.platform.exception.ServiceException;
import com.ibm.vw.platform.impl.ConfigurationImpl;
import com.ibm.vw.platform.spi.ObjectRepositoryHandler;
@Stateless
public class BuildingController implements BuildingControllerLocal, BuildingControllerRemote
{
@PersistenceUnit(unitName = "BuildingAutomationEJB")
protected EntityManagerFactory emf = null;
@EJB(name = "SpaceServiceImplRemote")
protected SpaceServiceRemote spaceService = null;
@EJB(name = "ContentServiceImplRemote")
protected ContentServiceRemote contentService = null;
@EJB(name = "ConfigurationServiceImplRemote")
protected ConfigurationServiceRemote configurationService = null;
private static final String CLASS_NAME = BuildingController.class.getName();
private static final Logger logger = Logger.getLogger(CLASS_NAME);
private static final String TEMPLATE_NAME = "Building Automation Template";
private static final String SPACE_NAME = "Building Automation Space";
private static final String BUILDINGAUTOMATIONTEMPLATEFILE_NAME = "dollhouse.oar";
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public Boolean switchLight(Light l)
{
if (l == null)
{
logger.severe("No light given!");
return null;
}
EntityManager em = emf.createEntityManager();
// load from db
Light loadedLight = em.find(Light.class, l.getId());
if (loadedLight == null)
{
logger.severe("Given light (id = " + l.getId() + ") not found in the database!");
return null;
}
loadedLight.setState(!loadedLight.getState());
// update in db
em.merge(loadedLight);
return loadedLight.getState();
}
public Integer getSensorData(Sensor s)
{
if (s == null)
{
logger.severe("No sensor given!");
return null;
}
EntityManager em = emf.createEntityManager();
// load from db
Sensor loadedSensor = em.find(Sensor.class, s.getId());
if (loadedSensor == null)
{
logger.severe("Given sensor (id = " + s.getId() + ") not found in the database!");
return null;
}
return loadedSensor.getValue();
}
public ObjectID<Space> createBuilding()
{
if (this.spaceService == null)
{
logger.severe("Space Service Bean not available!");
return null;
}
try
{
// first check if the building automation space is already available
Set<ObjectID<Space>> spaces = this.spaceService.query(null, null);
if (spaces != null)
{
for (ObjectID<Space> s : spaces)
{
LocalizedString name = this.spaceService.getName(null, s);
if (name != null && SPACE_NAME.equals(name.getSingleString()))
{
// found, check if a building object is in the database
EntityManager em = emf.createEntityManager();
List<Building> buildings = getBuildings();
if (buildings != null && buildings.size() > 0)
{
logger.finest("Found " + buildings.size() + " buildings, will now check if they are connected to the space.");
Building b = buildings.iterator().next();
// check if the space is connected to the building
if (!s.equals(b.getSpaceID()))
{
b.setSpaceID(s);
em.merge(b);
}
return s;
}
// no buildings found, create on
Building dollhouse = DollhouseBuilding.build();
dollhouse.setSpaceID(s);
em.persist(dollhouse);
this.spaceService.setType(null, s, SpaceType.PUBLIC);
logger.finest("Set space type to " + this.spaceService.getType(null, s) + " (should be PUBLIC)");
return s;
}
}
}
// not found, try to create one
Set<ObjectID<SpaceTemplate>> templates = this.spaceService.getSpaceTemplates(null);
ObjectID<SpaceTemplate> buildingTemplate = null;
if (templates != null)
{
for (ObjectID<SpaceTemplate> t : templates)
{
LocalizedString name = this.spaceService.getSpaceTemplateName(null, t);
if (name != null && TEMPLATE_NAME.equals(name.getSingleString()))
{
logger.finest("Found existing building automation template with id = " + t);
buildingTemplate = t;
}
}
}
if (buildingTemplate == null)
{
logger.info("No space template with the name \"" + TEMPLATE_NAME + "\" found, will create one!");
LocalizedString spaceNameString = new LocalizedString(SPACE_NAME);
logger.finest("Uploading new template file");
ObjectID<ObjectRepositoryHandler> repositoryID = null;
for (PlatformService<?> h : this.configurationService.getServicesByType(ObjectRepositoryHandler.class.getName()))
{
// use the first one
repositoryID = ((ObjectRepositoryHandler) h).getID();
break;
}
if (repositoryID == null)
{
logger.severe("Cannot find a repository handler on the server!");
return null;
}
logger.finest("Using bundled template file");
InputStream stream = BuildingController.class.getClassLoader().getResourceAsStream(BUILDINGAUTOMATIONTEMPLATEFILE_NAME);
if (stream == null)
{
logger.severe("Could not find template file!");
return null;
}
ObjectID<ObjectLink> templateFileID = this.contentService.create(repositoryID, null,
new URI("buildingautomationdemotemplate-dollhouse.oar"), GlobalConstants.MIME_TYPE_XOAR, null, stream);
Set<ObjectID<ObjectLink>> objectLinks = new HashSet<ObjectID<ObjectLink>>();
objectLinks.add(templateFileID);
Map<String, Object> settings = new HashMap<String, Object>();
String url = "http://" + configurationService.getValue(GlobalConstants.SERVER_ADDRESS_PARAMETER) + ":"
+ configurationService.getValue(GlobalConstants.REST_PORT_PARAMETER) + "/PlatformService/content/"
+ templateFileID.getID().toString();
settings.put("oar-file-url", url);
logger.finest("Now creating a template with following settings = " + Arrays.toString(settings.entrySet().toArray()));
buildingTemplate = this.spaceService.createSpaceTemplate(null, spaceNameString, objectLinks, SpaceType.PUBLIC, true, null,
settings);
if (buildingTemplate == null)
{
logger.severe("Could not create building automation demo application template!");
return null;
}
}
Configuration c = new ConfigurationImpl();
c.setValue("space-name", String.class, SPACE_NAME);
ObjectID<Space> space = this.spaceService.create(null, buildingTemplate, c);
if (space == null)
{
logger.severe("Could not create a building automation space!");
return null;
}
// delete temporary template
this.spaceService.removeSpaceTemplate(null, buildingTemplate);
this.spaceService.setName(null, space, new LocalizedString(SPACE_NAME));
logger.finest("Set space name to " + this.spaceService.getName(null, space) + " (should be " + SPACE_NAME + ")");
this.spaceService.setType(null, space, SpaceType.PUBLIC);
logger.finest("Set space type to " + this.spaceService.getType(null, space) + " (should be PUBLIC)");
// create the building with the connected space
EntityManager em = emf.createEntityManager();
List<Building> buildings = getBuildings();
if (buildings != null && buildings.size() > 0)
{
Building b = buildings.iterator().next();
// check if the space is connected to the building
if (!space.equals(b.getSpaceID()))
{
b.setSpaceID(space);
em.merge(b);
return space;
}
}
// no buildings found or building is not connected to the space
Building dollhouse = DollhouseBuilding.build();
dollhouse.setSpaceID(space);
em.persist(dollhouse);
return space;
}
catch (ServiceException e)
{
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
catch (URISyntaxException e)
{
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
return null;
}
public Set<Light> getLights()
{
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("select a from Light a");
List<Light> results = query.getResultList();
return new HashSet<Light>(results);
}
public Set<Sensor> getSensors()
{
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("select a from Sensor a");
List<Sensor> results = query.getResultList();
return new HashSet<Sensor>(results);
}
public String getName()
{
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("select a from Building a");
List<Building> results = query.getResultList();
if (results == null || results.size() == 0)
{
return null;
}
return results.iterator().next().getName();
}
public List<Building> getBuildings()
{
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("select a from Building a");
List<Building> results = query.getResultList();
return results;
}
}
The most interesting method in the controller bean is “createBuilding()”, in which you can see how to use the Virtual Collaboration platform services, for example, how to create a space from a given template, which is done with these lines:
Configuration c = new ConfigurationImpl();
c.setValue("space-name", String.class, SPACE_NAME);
ObjectID<Space> space = this.spaceService.create(buildingTemplate, c);
Review this method carefully to understand how to use the platform services, specifically the:
- space service (to create a space and a space template)
- content service (to upload a given file which is later used as template).
NOTE: The persistence unit name (which defaults to “BuildingAutomationEJB”) must be the same as in the “persistence.xml” (expand “JPA Content”, right-click on the persistence.xml file, and then select open). Also, it must be the same in the annotation “PersistenceUnit” (and there “unitName”) of the protected member “EntityManagerFactory emf”, which is the first member of the BuildingController class.
Let's also add a simple example building, called “DollhouseBuilding.” Add this as a class to the same package “com.ibm.vw.apps.demo.buildingautomation” with the implementation in listing 8.
Listing 8. Implementation for DollhouseBuilding
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation;
import java.util.HashSet;
import java.util.Set;
import com.ibm.vw.apps.demo.buildingautomation.model.Building;
import com.ibm.vw.apps.demo.buildingautomation.model.Light;
import com.ibm.vw.apps.demo.buildingautomation.model.Sensor;
public class DollhouseBuilding
{
public static Building build()
{
// the dollhouse has three lights
Light light1 = new Light();
light1.setName("Light_1");
light1.setState(false);
Light light2 = new Light();
light2.setName("Light_2");
light2.setState(false);
Light light3 = new Light();
light3.setName("Light_3");
light3.setState(false);
Set<Light> lights = new HashSet<Light>();
lights.add(light1);
lights.add(light2);
lights.add(light3);
Sensor sensor1 = new Sensor();
sensor1.setName("Sensor_Temperature");
sensor1.setValue(new Integer(20));
Set<Sensor> sensors = new HashSet<Sensor>();
sensors.add(sensor1);
Building dollhouse = new Building();
dollhouse.setLights(lights);
dollhouse.setSensors(sensors);
dollhouse.setName("Dollhouse");
return dollhouse;
}
}
Finally, we add the building automation template file (called “dollhouse.oar”, in the Attachments section) to the ejbModule directory.
For compiling the classes, we want to enhance them at build time, so we need a small Ant script that runs the OpenJPA enhancer. You can use the code in listing 9 as a sample for your file.
Listing 9. Sample Ant script code
<project name="BuildingAutomationEJB" default="enhance" basedir=".">
<target name="enhance" depends="init">
<java failonerror="true" fork="true" classname="org.apache.openjpa.enhance.PCEnhancer">
<arg value="-propertiesFile" />
<arg value="${basedir}/ejbModule/META-INF/persistence.xml" />
<classpath>
<path refid="compile.classpath" />
<path location="${build.location}" />
</classpath>
</java>
</target>
<target name="init" description="Initialises the build system">
<echo message="you are running java version ${ant.java.version}" />
<path id="source.classpath">
<pathelement path="${basedir}/build/classes" />
</path>
<path id="library.classpath">
<pathelement path="${basedir}/openjpa-1.2.1.jar" />
<pathelement path="${basedir}/serp-1.11.0.jar" />
<pathelement path="${basedir}/commons-collections-3.2.jar" />
<pathelement path="${basedir}/commons-lang-2.3.jar" />
<pathelement path="${basedir}/j2ee.jar" />
<pathelement path="${basedir}/geronimo-jpa_3.0_spec-1.1.1.jar" />
<pathelement path="${basedir}/platformapp-0.11.jar" />
<pathelement path="${basedir}/platformbase-0.11.jar" />
</path>
<path id="compile.classpath">
<path refid="source.classpath" />
<path refid="library.classpath" />
</path>
</target>
</project>
Now the EJB project is complete, and we can move on to implement the Web project.
Creating a Web project with servlets that access data from the bean project
To create a new dynamic Web project:
- Select File > New > Other from the menu.
- Expand Web and select Dynamic Web Project (see figure 13); click Next.
Figure 13. Create a Dynamic Web project

3. In the next window (see figure 14), specify “BuildingAutomationWeb” for the Project name field.
4. Select “IBM WASCE v2.1” in the Target Runtime field; click Next.
Figure 14. Dynamic Web project window

5. In the Web Module window (see figure 15), click Next to accept the default settings.
Figure 15. Web Module window

6. In the next window (see figure 16), enter “buildingautomation-web” in the Artifact Id field and click Finish. This creates a dynamic Web project with a deployment descriptor.
Figure 16. Geronimo Deployment Plan window

7. After successful creation of the project, the EJB project must be added as a dependent project. To do this:
- Right-click on the Web project and open Properties.
- Select Java Build Path and then click the Projects tab.
- Click Add, and select the EJB project.
- Verify the screen looks like that shown in figure 17.
Figure 17. Projects tab

Creating a servlet
Upon startup we need a servlet verifying that a building has been created and is available. This realizes the “createBuilding()” method in the “BuildingController”, discussed above. To create a servlet:
1. Right-click on the Web project in the Project Explorer and select New > Servlet (see figure 18).
Figure 18. Create a new servlet

2. Enter the value “com.ibm.vw.apps.demo.buildingautomation” in the Java package field, and enter the value “InitializerServlet” in the Class name field (see figure 19). Click Next.
Figure 19. Specify class file destination

3. Accept the default values in the next window (see figure 20) and click Finish.
Figure 20. Deployment descriptor information defaults

4. A servlet is created and inserted into the Web configuration file (web.xml). Paste in the code in listing 10 to implement the servlet.
Listing 10. Servlet implementation code
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import com.ibm.vw.platform.ObjectID;
import com.ibm.vw.platform.Space;
/**
* Servlet implementation class InitializerServlet
*/
public class InitializerServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@EJB
BuildingControllerLocal buildingController = null;
private static final String CLASS_NAME = InitializerServlet.class.getName();
private static final Logger logger = Logger.getLogger(CLASS_NAME);
/**
* @see HttpServlet#HttpServlet()
*/
public InitializerServlet()
{
super();
}
/**
* @see Servlet#init(ServletConfig)
*/
public void init(ServletConfig config) throws ServletException
{
super.init();
if (buildingController != null)
{
ObjectID<Space> space = buildingController.createBuilding();
if (space == null)
{
logger.severe("No building space found!");
}
}
else
{
logger.severe("No building controller EJB found!");
}
}
}
Creating action servlets
We still need a few small action servlets, namely, the “building status,” “space link,” and “switch” servlets:
- Create a servlet for each of these, using the above steps (right-click on the Web project, select New > Servlet, use the same Java package name, and use the following Class names: BuildingStatus, SpaceLink, and Switch).
- Since we want to load the InitializerServlet on startup, add the “1” line to the existing servlet definition (see listing 11).
Listing 11. Load InitializerServlet on startup
<servlet>
<description></description>
<display-name>InitializerServlet</display-name>
<servlet-name>InitializerServlet</servlet-name>
<servlet-class>com.ibm.vw.apps.demo.buildingautomation.InitializerServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
NOTE: The BuildingStatus servlet requires a JavaScript Object Notation (JSON) library, which should be downloaded. Also, the Java files in the archive should be copied into the source folder into a new “org.json” package (or if you have a JAR file, you can place it directly in the WEB-INF/lib folder).
3. Paste in the content in listing 12 for the BuildingStatus servlet.
Listing 12. Code for BuildingStatus servlet
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation;
import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONObject;
import com.ibm.vw.apps.demo.buildingautomation.model.Building;
/**
* Servlet implementation class BuildingStatus
*/
public class BuildingStatus extends HttpServlet
{
private static final long serialVersionUID = 1L;
@EJB
BuildingControllerLocal buildingController = null;
/**
* @see HttpServlet#HttpServlet()
*/
public BuildingStatus()
{
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setCharacterEncoding("UTF-8");
ServletOutputStream out = response.getOutputStream();
if (buildingController != null && buildingController.getBuildings() != null && buildingController.getBuildings().size() > 0)
{
Building building = buildingController.getBuildings().iterator().next();
out.println(new JSONObject(building).toString());
}
else
{
out.println(new JSONObject().toString());
}
}
}
4. Paste in the content in listing 13 for the Switch servlet.
Listing 13. Code for Switch servlet
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation;
import java.io.IOException;
import java.util.Set;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ibm.vw.apps.demo.buildingautomation.model.Light;
/**
* Servlet implementation class Switch
*/
public class Switch extends HttpServlet
{
private static final long serialVersionUID = 1L;
@EJB
BuildingControllerLocal buildingController = null;
/**
* @see HttpServlet#HttpServlet()
*/
public Switch()
{
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String idString = request.getParameter("id");
Long id = idString != null ? Long.parseLong(idString) : null;
String stateString = request.getParameter("state");
Boolean state = stateString != null ? Boolean.parseBoolean(stateString) : null;
if (buildingController != null && id != null && state != null)
{
Set<Light> lights = buildingController.getLights();
if (lights != null)
{
for (Light l : lights)
{
if (l.getId().equals(id))
{
Boolean newState = buildingController.switchLight(l);
response.getOutputStream().println(newState.toString().toLowerCase());
return ;
}
}
}
}
response.getOutputStream().println("failure (id = " + id + ", state = " + state + ")");
}
}
5. Paste in the content in listing 14 for the SpaceLink servlet.
Listing 14. Code for SpaceLink servlet
/*
********************************************************************
* *
* (C) Copyright IBM Corp. 2009 *
* *
* DISCLAIMER OF WARRANTIES *
* *
* This information contains sample application programs in source *
* language, which illustrate programming techniques on various *
* operating platforms. You may copy, modify, and distribute these *
* sample programs in any form without payment to IBM, for the *
* purposes of developing, using, marketing or distributing *
* application programs conforming to the application programming *
* interface for the operating platform for which the sample *
* programs are written. These examples have not been thoroughly *
* tested under all conditions. IBM, therefore, cannot guarantee or *
* imply reliability, serviceability, or function of these *
* programs. *
* *
* The sample programs are provided "AS IS", without warranty of *
* any kind. IBM shall not be liable for any damages arising out of *
* your use of the sample programs. *
* *
********************************************************************
*/
package com.ibm.vw.apps.demo.buildingautomation;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ibm.vw.apps.demo.buildingautomation.model.Building;
import com.ibm.vw.platform.api.SpaceServiceRemote;
import com.ibm.vw.platform.exception.ServiceException;
/**
* Servlet implementation class SpaceLink
*/
public class SpaceLink extends HttpServlet
{
private static final long serialVersionUID = 1L;
private static final String CLASS_NAME = SpaceLink.class.getName();
private static final Logger logger = Logger.getLogger(CLASS_NAME);
@EJB
BuildingControllerLocal buildingController = null;
@EJB(name = "SpaceServiceImplRemote")
protected SpaceServiceRemote spaceService = null;
/**
* @see HttpServlet#HttpServlet()
*/
public SpaceLink() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setCharacterEncoding("UTF-8");
ServletOutputStream out = response.getOutputStream();
URI spaceURI = null;
if (buildingController != null && buildingController.getBuildings() != null && buildingController.getBuildings().size() > 0)
{
Building b = buildingController.getBuildings().iterator().next();
try
{
spaceURI = spaceService.getURI(null, b.getSpaceID());
}
catch (ServiceException e)
{
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
}
out.println("{ \"url\" : \"" + (spaceURI != null ? spaceURI.toString() : "") + "\" }");
}
}
Now that the servlets are completed, all that's left to create is the main JSP, which will asynchronously access the different servlets to get the status and to control the devices. To do this:
- Create a file called “index.jsp” in the WebContent folder (right-click on WebContent and select New > JSP).
- Paste the content in listing 15 into the file.
Listing 15. Code for index.jsp file
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Building Automation Demo Application</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css" media="all">
h1, h2, h3 { font-family: Arial, Sans Serif; }
body { font-family: Georgia, Serif; }
table { padding:2px; margin:0px; border: 1px solid #bbbbbb; background-color: #eeeeff; }
tr { margin:2px; padding:0px; }
td { margin:2px; padding:4px; }
div.portlet { float: left; margin-right:20px; }
</style>
<script type="text/javascript" src="inc-scripts/prototype-1.6.1.js"></script>
<script type="text/javascript" src="inc-scripts/base.js"></script>
<script type="text/javascript">
function sortByName(first, second) {
a = first.name;
b = second.name;
return a == b ? 0 : (a < b ? -1 : 1)
}
function stateToName(state)
{
return state ? "on" : "off";
}
var CurrentPage = Class.create(Page, {
initialize: function($super)
{
$super();
new Ajax.Request("BuildingStatus",
{
method:"get",
parameters: null,
onSuccess: function(transport)
{
var data = transport.responseText.evalJSON(true);
page.setValues(data);
},
onFailure: function(transport)
{
alert("Could not connect to the building status service!");
}
});
},
setValues: function(data)
{
// set the name
$('building-name').innerHTML = "Here you can see a list of lights and sensors and also control the lights of the registered building (named "" + data.name + "")";
var jsonLights = data.lights;
jsonLights.sort(sortByName);
// get the lights table and fill in the lights
var lights = new String();
lights = "<tr><td>ID</td><td>Name</td><td>State</td><td>Action</td></tr>";
for (var i = 0; i < jsonLights.length; i++)
{
lights += "<tr><td>" + data.lights[i].id + "</td><td>" + data.lights[i].name + "</td><td>" + stateToName(data.lights[i].state) + "</td><td><a href=\"#\" onclick=\"javascript:page.switchLight(" + data.lights[i].id + "," + !data.lights[i].state + ");\">Switch</a></td></tr>";
}
$('lights-table').innerHTML = lights;
// get the sensors table and fill in the sensors
var jsonSensors = data.sensors;
jsonSensors.sort(sortByName);
var sensors = new String();
sensors = "<tr><td>ID</td><td>Name</td><td>Value</td></tr>";
for (i = 0; i < jsonSensors.length; i++)
{
sensors += "<tr><td>" + data.sensors[i].id + "</td><td>" + data.sensors[i].name + "</td><td>" + data.sensors[i].value + "</td></tr>";
}
$('sensors-table').innerHTML = sensors;
},
switchLight: function(id, state)
{
var parameters = {};
parameters['id'] = id;
parameters['state'] = state;
new Ajax.Request("Switch",
{
method:"get",
parameters: parameters,
onSuccess: function(transport)
{
var data = transport.responseText.evalJSON(true);
page.initialize();
},
onFailure: function(transport)
{
alert("Could not connect to the switch service!");
}
});
},
getSpaceLink: function()
{
new Ajax.Request("SpaceLink",
{
method:"get",
parameters: null,
onSuccess: function(transport)
{
var data = transport.responseText.evalJSON(true);
window.location.href = data.url;
},
onFailure: function(transport)
{
alert("Could not connect to the switch service!");
}
});
}
});
var page = new CurrentPage();
</script>
</head>
<body>
<h1>Building Automation Demo Application</h1>
<div id="building-name"></div>
<div class="portlet">
<h2>Lights</h2>
<table id="lights-table">
</table>
</div>
<div class="portlet">
<h2>Sensors</h2>
<table id="sensors-table">
</table>
</div>
<div class="portlet">
<h2>Actions</h2>
<a href="#" onclick="javascript:page.getSpaceLink();">Visit space in 3D world</a><br/>
<a href="#" onclick="javascript:page.initialize();">Reload page</a>
</div>
</body>
</html>
3. The JSP file uses the Prototype JavaScript Framework library, so you need to download it and place it as “prototype-1.6.1.js” in a new folder “inc-scripts” below the WebContent folder.
4. Additionally, create a “base.js” file in the same directory with content in listing 16.
Listing 16. Code for base.js file
/*
* The Page class helps to address all the tasks that are triggered
* by a web page. It collects all global functions that are the same
* on all pages and makes it possible to easily integrate more
* functions as they are needed.
*/
var Page = Class.create({
initialize: function()
{
}
});
var inits = [];
function runInits(){
for (var i = 0; i < inits.length; i++) {
inits[i].run();
}
}
function addToInits(runimpl){
inits.push(runimpl);
}
window.onload = runInits;
Verify that your project structure looks similar to that in figure 21. The EJB module should have two source packages, “com.ibm.vw.apps.demo.buildingautomation” and “com.ibm.vw.apps.demo.buildingautomation.model”.
Also, the dollhouse.oar must be directly below the source folder as there are dependencies with two files (platformbase and platformapp) from the Virtual Collaboration server.
Figure 21. Project structure

Create an EAR project bundling the EJB and Web projects
Similar to creating a Web project (as in Section 5 above), we create an Enterprise Application project:
- Select File > New > Enterprise Application Project, and name it BuildingAutomationEAR.
- Verify that the Target Runtime field is set to “IBM WASCE v2.1” (see figure 22); click Next.
Figure 22. EAR Application Project window

3. In the next window (see figure 23) select both the projects as Java EE module dependencies; click Next.
Figure 23. Enter Application window

4. In the next window, specify “buildingautomation-ear” for the Artifact Id field (see figure 22); click Finish.
Figure 22. Geronimo Deployment Plan window

Since a database is used in the application, a resource adapter must be specified that contains information regarding the database connection. For an easy deployment, the integrated Derby database provided by WASCE is used, for which two files must exist:
- the resource adapter implementation (this is shipped with WASCE and can be found in the repository packages, for example, at {WAS_CE_DIRECTORY}/repository/org/tranql/tranql-connector-derby-client-xa/1.4/tranql-connector-derby-client-xa-1.4.rar on version 2.1.1.2)
- connection plan (Attachments file “derby-connection-plan.xml”)
The databases are automatically created when starting the enterprise application. These two files must be placed in the content directory of the enterprise application; the Eclipse default name is EarContent.
These database configuration files are referenced from the application-server specific deployment descriptor, a file called “geronimo-application.xml,” which is found in the “EarContent/META-INF” directory.
Additionally, the Virtual Collaboration platform must be referenced as a dependency, that is, as a reference to an application already deployed on the application server (see listing 17).
Listing 17. Code for referencing the geronimo-application.xml file
Finally, we must add the new module to application.xml (the default deployment descriptor) at the end of the file before “”:
<module>
<connector>tranql-connector-derby-client-xa-1.4.rar</connector>
</module>
Now you can export the application by right-clicking the EAR project and selecting Export > EAR File.
Deploying on WebSphere Application Server Community Edition
To see the application running, you need to deploy it on the Virtual Collaboration server. To do that:
- First, verify that the Virtual Collaboration service is started.
- Within the administration console of WASCE at http://serverip:8080/BuildingAutomationDemo/ (the Virtual Collaboration administrator knows the log-in credentials for the administration console), select Deploy New under the Applications section (see figure 25).
- Select the exported EAR file as Archive, leave the Plan empty (as the plan was already packaged within the EAR file), and verify that "Start app after install" is checked and "Redeploy application" is not checked.
- Finally, click Install to install the application.
Figure 25. Deploying the application to the Virtual Collaboration WASCE

Now the application can be accessed at http://serverip:port/BuildingAutomationWeb/ (see figure 26).
Figure 26. Building Automation Web interface

After you click the “go in-world” button, an OpenSim Viewer (usually the standard Second Life Client, but other viewers like the Hippo Viewer can also be used) will be started via the RezzMe launcher. You are then in the region with the doll house, and you can control the lights by clicking the light objects (see figures 27 and 28).
Figure 27. Virtual World doll house

Figure 28. Virtual World Doll house at night

Conclusion
In this tutorial you've been introduced to the Virtual World Application platform, which contains different services to manage spaces, content, and users in virtual worlds. On top of these services, applications can be built to realize a specific use case.
We created a sample building automation application that connects a 3D representation in-world to a backend, realized as a J2EE application. The status of the devices in the building (for example, lights and temperature sensors) can be queried and toggled from a Web application. The same is also true for the in-world representation.
In a future article, you can see how to connect the backend to a real home automation bridge called Shared Spaces Shaspa, and you'll see how to instantly add new devices in-world and connect them to the backend. The whole demonstration is already available in the IBM Forum, Ehningen, Germany.
Resources
Lotus Sametime documentation, Version 8.5:
http://www.ibm.com/developerworks/lotus/documentation/sametime/
developerWorks® Sametime product page:
http://www.ibm.com/developerworks/lotus/products/instantmessaging/
Lotus Sametime blog:
https://www-950.ibm.com/blogs/SametimeBlog/?lang=en_us
Lotus Sametime wiki article, “Virtual Collaboration for Lotus Sametime:”
http://www-10.lotus.com/ldd/stwiki.nsf/dx/Virtual_Collaboration_for_Lotus_Sametime
ISSL Virtual Collaboration for Lotus Sametime page:
http://www-01.ibm.com/software/lotus/services/vc4sametime.html
Attachments
- Complete Eclipse Project Interchange (containing Enterprise Applications and the doll house template but not the third-party libraries like the JSON API and the prototype JavaScript framework)
- Dollhouse template file
- API documentation
- derby-connection-plan.xml
About the author
Matthias Hub is an IT specialist at IBM's Boeblingen, Germany, Lab, where he is one of the core developers of of the virtual collaboration asset. As a member of the Lab Services team, he provides technical consultancy on collaboration solutions within enterprises on the Web and in virtual worlds, and his expertise also includes Lotus Connections. Matthias holds a Diploma (Master's) degree in computer science from the University of Stuttgart. You can reach him at matthias.hub@de.ibm.com. |