HostingProgrammersGuide PDF
HostingProgrammersGuide PDF
HostingProgrammersGuide PDF
Active Workspace
Hosting
Programmers Guide
AW • 2.4
Active Workspace Hosting Programmers Guide – V2.4
1. Introduction .......................................................................................................................................... 3
Terms and Definitions ............................................................................................................................... 3
Exported Libraries ..................................................................................................................................... 4
Java (Platform Neutral) ......................................................................................................................... 4
C++ (Win32/Win64) .............................................................................................................................. 4
C++ (Linux/gcc) ...................................................................................................................................... 4
C++ (Mac/clang) .................................................................................................................................... 4
C# (Win32/Win64) ................................................................................................................................ 4
JavaScript .............................................................................................................................................. 4
External Dependencies ............................................................................................................................. 5
Java........................................................................................................................................................ 5
C# .......................................................................................................................................................... 5
Javascript............................................................................................................................................... 5
C++ ........................................................................................................................................................ 5
Message Path Overview............................................................................................................................ 6
2. How to Host Active Workspace ............................................................................................................ 8
Java............................................................................................................................................................ 9
C++ ............................................................................................................................................................ 9
JavaScript ................................................................................................................................................ 10
3. Existing Hosting Services ..................................................................................................................... 11
Host-Side Services ................................................................................................................................... 12
SOA Related ‘Infrastructure’ Services ................................................................................................. 12
General ‘Infrastructure’ Services ........................................................................................................ 12
General ‘Solutions’ Services ................................................................................................................ 13
Client-Side Services ................................................................................................................................. 14
SOA Related ‘Infrastructure’ Services ................................................................................................. 14
General ‘Infrastructure’ Services ........................................................................................................ 14
General ‘Solutions’ Services ................................................................................................................ 14
1. Introduction
Use the APIs described in this document to host the Active Workspace (AW) client, which includes the
following independent players:
Definition and use of the ‘Interop Libraries’ middleware is the focus of this document.
Service – Implementation of functionality that extends across the host/client boundary. A service is
called by a proxy from the other side. Services can exist on either side of the boundary.
Fully Qualified Name (FQN) – The name of a service. This name must be known to both the host and
client, and is used to connect proxies to services. For services that exist on the host-side, the suffix
“.host” is used. For services that exist on the client the suffix “.client” is used.
Target Service Descriptor – Combination of the Fully Qualified Name and a version number.
Host Content State – The current state of the host instance. Used to track whether the client is capable
of communication and, if it is, what type of communication it’s capable of.
Component – A functional unit of Active Workspace that can be hosted as a standalone web application.
An example is the ObjectInfo component which displays summary data about an object.
Active Workspace (AW) - A client application that is the focus of many services and examples in this
programmer guide.
Exported Libraries
Hosting APIs are provided via the following language bindings:
Java
C++
C# (.net)
JavaScript
The specific libraries for the various language bindings and operating systems are as follows.
C++ (Win32/Win64)
libweb_hosting.dll
libweb_hosting.lib
libweb_hostingservices.dll
libweb_hostingservices.lib
C++ (Linux/gcc)
libweb_hosting.so
libweb_hostingservices.so
C++ (Mac/clang)
libweb_hosting.dylib
libweb_hostingservices.dylib
C# (Win32/Win64)
HostingServices.dll
HostingServices.pdb
HostingSetvices.xml
WpfWebContentHosting.dll
WpfWebContentHosting.pdb
WpfWebContentHosting.xml
JavaScript
splmBrowserInterOp.js
splmBrowserInterOpMin.js (same, only 'minified')
External Dependencies
This is a short list of the external third party libraries the hosting libraries are dependent on.
Java
Google GSON – 2.2.2 - gson2.2.2.jar - Toolbox
SWT - 3.8 – org.eclipse.* - Toolbox
Log4j - 1.2.13 - log4j.jar - Toolbox
C#
Log4j - 1.2.10.0 - log4net.dll - Toolbox
Javascript
None
C++
Log4j - 1.2.10.0 - Toolbox
Setup Instructions
This section identifies the general setup instructions for each type of host application. Setting up a host
to encapsulate Active Workspace content typically follows these general steps:
ActiveWorkspaceHosting.NX.URL=http://111.222.333.444:7001/awc
ActiveWorkspaceHosting.Office.URL=http://111.222.333.444:7001/awc
ActiveWorkspaceHosting.RAC.URL=http://111.222.333.444:7001/awc
ActiveWorkspaceHosting.WorkflowEmail.URL=http://111.222.333.444:7001/awc
ActiveWorkspaceHosting.Vis.URL=<not correct today>
Or you can turn on AW client in all host applications by setting the following option:
ActiveWorkspaceHosting.URL=http://111.222.333.444:7001/awc
The Rich Application Client (RAC) has additional options that can be set to allow the Active Workspace UI
to be used instead of the RAC's native UI. Specifically, you can replace the 'My Worklist' (a.k.a. 'Inbox')
and 'Summary' views with the Active Workspace UI by setting the following options:
TC_Use_ActiveWorkspace_Inbox=true
TC_Use_ActiveWorkspace_Summary=true
1) Add the needed libraries to the host application based on its language binding.
2) Initialize the HostManager singleton instance.
3) Create the WebHostControl object (Java, C++ and C# only).
4) Set the URI of the client into the WebHostControl (or related <iframe>, in the case of
JavaScript)
1) Link to the libraries for your language. All libraries are published to the toolbox. See Exported
Libraries for language-specific library details.
2) Initialize the HostManager. This is done by getting an instance of the HostManager singleton
and registering the host-side services you're interested in exposing to the client.
Note: For the Java, C++, and C# bindings the host-side StartupNotificationSvc is required to
assure proper asynchronous operation between the host and client during the 'handshake' phase
that takes place during later steps. For Javascript, this service is not required since it is assumed the
host and client are running inside the same web browser.
3) Create the WebHostControl object. This will contain the embedded browser window and
instantiate the HostControlInstance object used to handle all host-client communication.
4) Pass the URI that contains the location of the client application you wish to host. The URI must
include the ah=true query. This query indicates to the Active Workspace client that it is being
hosted and will cause it to attempt a 'handshake' with the host application before displaying any
of its UI.
Java
import com.siemens.splm.browserinterop.infrastructure.interop.IHostManager;
import com.siemens.splm.browserinterop.infrastructure.interop.IHostServiceDescriptor;
import com.siemens.splm.browserinterop.infrastructure.interop.HostManager;
import com.siemens.splm.browserinterop.infrastructure.services.core._2014_02.StartupNotificationSvc;
import com.siemens.splm.browserinterop.infrastructure.services.logging._2014_02.LoggerForwardSvc;
import com.siemens.splm.browserinterop.solutions.services.selection._2014_02.SelectionProviderSvc;
...
// Step 2
IHostManager hostMgr = HostManager.getTheInstance();
List<IHostServiceDescriptor> hostServices = hostMgr.getKnownServices();
if( hostServices == null || hostServices.isEmpty() )
{
try
{
hostMgr.registerService( new StartupNotificationSvc() );
hostMgr.registerService( new LoggerForwardSvc( new LogEntryHandler() ) );
hostMgr.registerService( new
com.siemens.splm.browserinterop.infrastructure.services.core._2014_07.HostConfigurationSvc(
new sampleawapp._2014_07.HostConfigurationHandler() ) );
hostMgr.registerService( new
com.siemens.splm.browserinterop.infrastructure.services.core._2014_02.HostConfigurationSvc(
new sampleawapp._2014_02.HostConfigurationHandler() ) );
hostMgr.registerService( new SelectionProviderSvc() );
}
catch( Exception ex )
{
}
}
// Step 3
m_webHostControl = new WebHostControl( parentComposite, SWT.NONE );
// Step 4
final TargetConfig uri = new TargetConfig("", "", new URI(http://myhostwebsite.com/tc.html?ah=true));
final IHostControlInstance hostControl = m_webHostControl.getHostInstanceData();
hostControl.launchToTarget(uri);
C++
/* Step 2 */
BROWSERINTEROP_INTEROP_NS::HostManager* hostMgr = BROWSERINTEROP_INTEROP_NS::HostManager::TheInstance();
BROWSERINTEROP_INTEROP_NS::HostServiceList* hostServices = hostMgr->KnownServices();
if( hostServices == NULL || hostServices.GetSize() == 0 )
{
hostMgr->RegisterService(new BROWSERINTEROP_SERVICES_NS::Core::_2014_02::StartupNotificationSvc());
hostMgr->RegisterService(new
BROWSERINTEROP_SERVICES_NS::Logging::_2014_02::LoggerForwardSvc(&SampleWebApp::LogUtilities::logCallback));
hostMgr->RegisterService(new BROWSERINTEROP_SERVICES_NS::Core::_2014_07::HostConfigurationSvc(
new SampleWebApp::2014_07::HostConfigurationHandler());
hostMgr->RegisterService(new BROWSERINTEROP_SERVICES_NS::Core::_2014_02::HostConfigurationSvc(
new SampleWebApp::2014_02::HostConfigurationHandler());
hostMgr->RegisterService(new BROWSERINTEROP_SOLUTIONS_SERVICES_NS::Selection::_2014_07::SelectionProviderSvc());
}
/* Step 3 */
(Creation of the C++ WebControl is Specific to each host)
/* Step 4 */
const std::string& uri("http://myhostwebsite.com/tc.html?ah=true");
BROWSERINTEROP_INTEROP_NS::TargetConfig* targetConfig("","",uri);
m_hostControlInstance->LaunchToTarget(targetConfig);
JavaScript
var clientIFrame = document.getElementById("MyClientIFrame");
// Step 2
var hostManager = INF_INTEROP_HOST_MANAGER.getTheInstance();
var hostServices = hostManager.getKnownServices();
if( hostServices || hostServices.length == 0 ) {
var hostControl = hostManager.initializeHostIntegration(clientIFrame, function (hostManager) {
hostManager.registerService(new INF_SERVICES_LOGGING_2014_02.LoggerForwardSvc( new MyLogEntryHandler());
hostManager.registerService(new INF_SERVICES_CORE_2014_07.HostConfigurationSvc(
new MyHostConfigHandler2014_07());
hostManager.registerService(new INF_SERVICES_CORE_2014_02.HostConfigurationSvc(
new MyHostConfigHandler2014_02());
hostManager.registerService(new SOL_SERVICES_SELECTION_2014_02.SelectionProviderSvc();
}
}
// Step 3 - N/A
// Step 4
clientIFrame.src = "http://myhostwebsite.com/tc.html?ah=true"
Infrastructure - lower level services, handling communication and other basic API ‘plumbing’.
Solutions - more application-level functionality.
Further, all services have both a name and a version. It's possible for there to be multiple versions with
the same service name, performing similar tasks.
Note: A brief description of the currently available host-side and client-side services can be displayed
by the Active Workspace server by navigating with your browser to the following relative address:
http://<aw_server>/thinclient/lib/host_integration/hostServices.html
Host-Side Services
The following sections provide a brief description of the existing services a host can choose to provide
implementations for. The implementations will process client-side requests to these services. All of
these services are optional.
This service is called by the client to have the host perform an SOA operation.
This service is called when the client needs information about the current SOA session.
This service is called when the client wishes to asynchronously start a session for the given user.
Client Status
FQN: splm.browserinterop.infrastructure.services.core.ClientStatus.host / _2014_07
Logger Forwarder
FQN: splm.browserinterop.infrastructure.services.LoggerForward.host / _2014_02
This service is called when the client wants the host to log some 'routine' message.
Refresh
FQN: splm.browserinterop.infrastructure.services.refresh.Refresh.host / _2014_07
This service is called by the client to have the host perform a refresh of its data model based on the
object references it supplies.
Session
FQN: splm.browserinterop.infrastructure.services.session.Session.host / _2014_07
This service is called by the client to have the host react to various types of session changes.
Host Configuration
FQN: splm.browserinterop.infrastructure.services.core.HostConfiguration.host / 2014_02 & 2014_07
This service is called by the client to get the host's configuration for it.
Host Open
FQN: splm.browserinterop.solutions.services.open.HostOpen.host / _2014_02
This service is called when the client wants the host to open one or more components in some host
appropriate view(s).
Remote Clipboard
FQN splm.browserinterop.services.clipboard.RemoteClipboard.host / _2014_07
This service is called by the client to have the host add or remove items from its clipboard.
Selection Provider
FQN: splm.browserinterop.solutions.services.selection.SelectionProvider.host / _2014_02
This service is called when the client wishes to have one or more objects selected in the host
application.
Client-Side Services
The following sections provide a brief description of the existing services a client can choose to provide
implementation for. The implementations will process host-side requests to these services. All of these
services are optional.
This service is called by the host to complete the client's asynchronous request for user session
authorization.
JSON Request
FQN: splm.browserinterop.infrastructure.services.soa.SoaJsonRequest.client / _2014_02
This service is called by the host to return data from an asynchronous SOA call.
This service is called by the host to complete the client's asynchronous request for user session
authorization.
Open Location
FQN: splm.browserinterop.infrastructure.services.openlocation.OpenLocation.client / _2014_02
This service is called by the host to have the client go to a specific location within the hosted
application.
Refresh
FQN: splm.browserinterop.infrastructure.services.refresh.Refresh.client / _2014_07
This service is called by the host to have the client perform a refresh of its data model based on the
object references it supplies.
Session
FQN: splm.browserinterop.infrastructure.services.session.Session.client / _2014_07
This service is called by the host to have the client react to various types of session changes.
Component Config
FQN splm.browserinterop.services.component.ComponentConfig.client / _2014_07
This service is called by the host to get the names of currently available styling themes.
This service is called by the host to have the client generate a URL to upload/download a file.
Initiate Search
FQN: splm.browserinterop.solutions.services.search.InitiateSearch.client / _2014_02
This service is called by the host to have the client perform a search with the given string and to
display the results.
This service is called by the host to set input for the object info component
Remote Clipboard
FQN: splm.browserinterop.services.clipboard.RemoteClipboard.client / 2014_07
This service is called by the host to have the client add or remove items from its clipboard
Selection Listener
FQN: splm.browserinterop.solutions.services.selection.SelectionListener.client / 2014_02
This service is called by the host to pass objects to the client to be selected.
Set Theme
FQN: splm.browserinterop.solutions.services.theme.SetTheme.client / _2014_02
This service is called by the host to set the current styling theme.
4. Invoking Proxies
A proxy is the host-side object used to invoke a client-side service. If you want to invoke proxies from
the host application, you must create an instance of that proxy, and then call methods on it.
For example, let's call the HelloWorldProxy explained later in this document.
Java
HelloWorldProxy proxy = new HelloWorldProxy( webHostControl.getHostInstanceData() );
proxy.SendHelloWorld();
C++
HelloWorldProxy proxy( hostControlInstance );
proxy.SendHelloWorld();
JavaScript
var proxy = new HelloWorldProxy( hostControl );
proxy.SendHelloWorld();
Java
package com.siemens.splm.browserinterop.solutions.services.helloWorld._2014_07;
import com.google.gson.Gson;
import com.siemens.splm.browserinterop.infrastructure.interop.IHostControlInstance;
import com.siemens.splm.browserinterop.infrastructure.services.BaseServiceProxy;
// Step 1
public class HelloWorldProxy
extends BaseServiceProxy
{
// Step 2
public HelloWorldProxy( IHostControlInstance hostcontrol )
{
super( hostcontrol, "com.sample.HelloWorld.client", "_2014_07" );
}
// Step 3
public void SendHelloWorld() throws Exception
{
// Step 4
HelloWorldMessage msg = new HelloWorldMessage( "Hello World", "_2014_07" );
// Step 5
String payload = new Gson().toJson( msg );
try
{
if( _hostControl != null )
{
// Step 6
_hostControl.invokeWebEvent( getTargetServiceDescriptor(), payload );
}
}
catch( Exception ex )
{
// some error invoking the service - likely not able to communicate with client-side.
BaseServiceProxy.logError( "Cannot call client-side for method invocation: "
+ ex.getLocalizedMessage(), ex );
throw ex;
}
}
}
C++
#include <infrastructure/services/BaseServiceProxy.hxx>
#include <infrastructure/interop/HostlibExports.hxx>
#include <infrastructure/interop/IHostControlInstance.hxx>
namespace Com { namespace Siemens { namespace SPLM { namespace BrowserInterop { namespace Solutions { namespace Services
{ namespace HelloWorld { namespace _2014_07 {
/* Step 1 */
class HelloWorldProxy : public BROWSERINTEROP_SERVICES_NS::BaseServiceProxy
{
public:
/* Step 2 */
HelloWorldProxy(BROWSERINTEROP_INTEROP_NS::IHostControlInstance* hostControl)
: BROWSERINTEROP_SERVICES_NS::BaseServiceProxy(hostControl, "com.sample.HelloWorld.client", "_2014_07")
{
}
void SendHelloWorld()
{
/* Step 4 */
HelloWorldMessage msg("Hello World", "_2014_07");
/* Step 5 */
std::string payload = msg.ToJsonString();
if( m_hostControl != NULL )
{
/* Step 6 */
m_hostControl->InvokeWebEvent(GetBaseServiceDescriptor(), payload);
}
}
}; } } } } } } } }
JavaScript
// Step 1
var HelloWorldProxy = function (hostControl) {
// Step 2
INF_SERVICES.BaseServiceProxy.call(this, hostControl,
“com.sample.HelloWorld.client”, “_2014_07”);
};
HelloWorldProxy.prototype = Object.create(INF_SERVICES.BaseServiceProxy.prototype);
// Step 3
HelloWorldProxy.prototype.SendHelloWorld = function(){
// Step 4
var msg = new HelloWorldMessage( "Hello World", "_2014_07" );
// Step 5
String payload = JSON.stringify( msg );
try{
if(this.hostControl){
// Step 6
this.hostControl.invokeWebEvent( this.getTargetServiceDescriptor(), payload );
}
}catch(ex){
// some error invoking the service - likely not able to communicate with client-side.
Console.log ( "Cannot call client-side for method invocation: " + ex );
throw ex;
}
}
Proxy Messages
Now let's take a look at the HelloWorldMessage class. Message classes are used to hold the
information that's being passed across the inter-process boundary to the service on the other side.
Java
package com.siemens.splm.browserinterop.solutions.services.helloWorld._2014_07;
import com.siemens.splm.browserinterop.infrastructure.services.BaseDataContractImpl;
C++
#include <infrastructure/interop/HostlibExports.hxx>
#include <infrastructure/services/BaseMessage.hxx>
namespace Com { namespace Siemens { namespace SPLM { namespace BrowserInterop { namespace Solutions { namespace Services
{ namespace HelloWorld { namespace _2014_07 {
JavaScript
var HelloWorldMessage = function ( message, version ) {
INF_SERVICES.BaseDataContractImpl.call(this, version);
this.FormatMessage = message;
};
HelloWorldMessage.prototype = Object.create(INF_SERVICES.BaseDataContractImpl.prototype);
HelloWorldMessage.prototype.getMessage = function(){
return this.FormatMessage;
}
1) Within the service's constructor, we'll pass the name of the service
“com.sample.HelloWorld.client”, and the version “_2014_07” to the base class
BaseCallableService. If the name and version numbers don't match the ones used in the
proxy, the host manager will be unable to find the service called by the proxy, and the proxy will
fail.
2) Override the handleIncomingEvent function to handle the call from the proxy.
3) Change the simple text of the JSON message into a class.
4) Call into the call to get the message that was sent to us from the proxy.
5) Print out the message. In this case, "Hello World!" is seen.
Java
package com.siemens.splm.client.hosted.services.helloworld._2014_07;
import com.siemens.splm.clientfx.bootstrap.hosted.published.services.BaseCallableService;
public class HelloWorldSvc
extends BaseCallableService
{
/**
* Constructor
*/
public HelloWorldSvc()
{
// Step 1
super( "com.sample.HelloWorld.client", "_2014_07" );
}
// Step 2
@Override
public void handleIncomingEvent( String jsondata )
throws Exception
{
String message = null;
// Step 3
HelloWorldMsg msg = HelloWorldMsg.getResponseFromJson( jsondata );
if( msg != null )
{
// Step 4
message = msg.getMessage();
}
// Step 5
System.out.println( message );
}
}
JavaScript
// Step 1
var HelloWorldSvc = function() {
INF_SERVICES.BaseHostingService.call(this, "com.sample.HelloWorld.client", "_2014_07" );
};
HelloWorldSvc.prototype = Object.create(INF_SERVICES.BaseHostingService.prototype);
// Step 2
HelloWorldSvc.prototype.handleIncomingEvent = function( jsondata ){
var message = null;
// Step 3
var msg = JSON.parse(jsondata);
if( msg ){
// Step 4
message = msg.getMessage();
}
// Step 5
Console.log( message );
};
7. Hosting a Component
Components are functional pieces of Active Workspace that can be hosted as a stand-alone application.
Typically, a component will either allow a user to perform a task or show information to the user.
Components are hosted in the same way as the full Active Workspace client. They can utilize common
services for soa, authentication, session, etc. While a component is only a small piece of Active
Workspace from a programming perspective it is the same as the full client and requires its own
HostControlInstance and its own authentication. This means a hosted component cannot share them
with another hosted instance of Active Workspace.
When hosting a component the URL used is the standard Active Workspace URL with component
location appended by adding
#com.siemens.splm.clientfx.ui.components.ComponentsPresenter to the end of the URL.
Every component in Active Workspace is identified with a unique string identifier; to host a particular
component it is necessary to know its identifier. The component identifier can be set either by adding it
to the URL with the parameter componentId or by setting it through the
ComponentConfigurationProxy described in Service Details.
A component can both accept input and send output. Input can be sent either through aparameter in
the URL or by invoking a proxy in the host-side code. Output can be received by implementing a service
on the host-side code that will be called by the client component. Input proxies and output services are
specific to a particular component. An example of an input proxy is shown in the ObjectInfo
Component Input section under Service Details.
Service Details
Client Info
For logging purposes it may be useful for the host to know information about the Active Workspace
client it is connected to. The ClientInfoProxy provides a way for the host to query this information.
It can be called by the host any time after communication has been established between host and client.
The return value is a list of key / value string pairs. Currently Active Workspace provides information
about its version and URL.
Example code
C++
BROWSERINTEROP_SERVICES_NS::ClientInfo::_2014_07::ClientInfoProxy clientInfoProxy(GetHostControlInstance()-
>GetHostControlInstance());
Component Configuration
The component configuration sets up the configuration of the component system; it does not set
options specific to a component. Currently, the following fields are available in ComponentConfigMsg:
C++
class HOSTLIBEXPORT ComponentConfigMsg : public BROWSERINTEROP_SERVICES_NS::BaseMessage
{
public:
ComponentConfigMsg();
/// <summary>
/// Component id.
/// </summary>
std::string ComponentId;
/// <summary>
/// Should the client display in the embedded location view?
/// </summary>
bool UseEmbeddedLocationView;
};
The component configuration can be set by using the ComponentConfigProxy:
C++
class HOSTLIBEXPORT ComponentConfigProxy : public BROWSERINTEROP_SERVICES_NS::BaseServiceProxy
{
public:
/// <summary>
/// ctor - associate this with a particular web container.
/// </summary>
/// <param name="hostcontrol">the host control instance.</param>
ComponentConfigProxy(BROWSERINTEROP_INTEROP_NS::IHostControlInstance* hostControl);
~ComponentConfigProxy();
UseEmbeddedLocationView – Should the page be displayed using the embedded location view? For
example, should the header, footer, command bars, search box, etc. be hidden? This affects all pages
and does not require ComponentId to be set in order to take effect.
To use the ObjectInfo component it is necessary to first invoke the ComponentConfigProxy and set
the ComponentId field in the ComponentConfigMsg to
com.siemens.splm.clientfx.tcui.xrt.published.ObjectInfo
C++
class HOSTLIBEXPORT ObjectInfoComponentInputMsg : public BROWSERINTEROP_SERVICES_NS::BaseMessage
{
public:
ObjectInfoComponentInputMsg();
/// <summary>
/// Object to display.
/// </summary>
BROWSERINTEROP_SERVICES_NS::Core::_2014_02::InteropObjRef ObjectRef;
};
Currently, the following fields are available:
The object that the ObjectInfo component is rendering can be updated by using the
ObjectInfoComponentInputProxy and calling UpdateObject
C++
class HOSTLIBEXPORT ObjectInfoComponentInputProxy : public BROWSERINTEROP_SERVICES_NS::BaseServiceProxy
{
public:
/// <summary>
/// ctor - associate this with a particular web container.
/// </summary>
/// <param name="hostcontrol">the host control instance.</param>
ObjectInfoComponentInputProxy(BROWSERINTEROP_INTEROP_NS::IHostControlInstance* hostControl);
~ObjectInfoComponentInputProxy();
Refresh
When hosting AW, both the host and AW have independent local data models. If AW triggers a change
to a model object, the change may need to be reflected in the host's client-side data model. Likewise, if
the host triggers a change to a model object, the change may need to be reflected in the AW client-side
data model. Without a mechanism to synchronize the two client-side data models, both the host and
AW may be presenting different information about the same loaded object. For example, the host may
show an object as checked out and AW may show it as checked in.
To provide the necessary client-side data synchronization between the host and AW, we can use a
client-side BIO service (Refresh) to send across the relevant object modifications. When the host makes
a change to an object loaded in its client-side data model, it will send the affected list of objects to AW.
The list of impacted objects can be created in the host from information returned in the response to the
SOA call modifying the objects. SOA provides a list of added, removed, and updated objects for each
SOA call (a.k.a. service data). This list of objects is sent to AW, and AW uses it to refresh its client-side
data model accordingly. Since AW also uses SOA, this mechanism becomes bi-directional. If AW modifies
an object, the corresponding SOA call's service data can be used to update the host.
For the host to communicate object-level modifications to AW, it needs to use the Refresh service.
Specifically, it must invoke RefreshProxy::sendRefresh and pass along the object UIDs
representing the created, updated, and deleted objects. AW will use this list to update its client-side
data model and refresh the UI if required.
For the host to receive object level modifications from AW, it needs to register the RefreshSvc and
implement a handler (IRefreshHandler). When AW makes a server call resulting in a created,
updated, or deleted object, it will pass this information to the host within a RefreshMsg. The
RefreshMsg contains a list of created, updated, and deleted objects. It is up to the host to decide what
to do, if anything, after it receives this information. In general, however, the host will trigger an SOA call
to refresh these objects so that they are updated and/or loaded within its client-side data model.
Below is the handler that is invoked in the host after AW makes changes to a set of objects.
Java
/**
* Interface implemented on the host-side tasked with handling client data model updates initiated from the client-side.
*/
public interface IRefreshHandler
{
/**
* Update the host-side client data model based on the given object references.
*
* @param createdObjects List of {@link InteropObjectRef} to objects that have been created.
* @param deletedObjects List of {@link InteropObjectRef} to objects that have been deleted.
* @param updatedObjects List of {@link InteropObjectRef} to objects that have been updated.
*/
void refreshHost( List<InteropObjectRef> createdObjects, List<InteropObjectRef> deletedObjects,
List<InteropObjectRef> updatedObjects );
}
Session
Similar to how model object changes can be synchronized using the Refresh service; session level
changes can be synchronized using the Session service. Session level objects such as GroupMember,
RevisionRule, Project, and Preferences (future) can all be synchronized using the Session
service.
For the host to communicate session changes to AW, it needs to use the Session service. The
SessionProxy::updateSession method should be invoked, passing along the changed session
objects (GroupMember, RevisionRule, default Project). The API has a placeholder for preferences
which are currently not implemented. When AW receives the list of changed session objects, it will
update its internal session settings and update the UI, if required.
For the host to receive session level changes from AW, it needs to register the SessionSvc and
implement a handler (ISessionHandler). AW will trigger this handler after session changes have been
made within AW (currently only RevisionRule is supported). It is up to the host to decide what to do, if
anything, after receiving the session information.
Below is the host-side handler that is invoked after AW makes a session change.
Java
/**
* Interface implemented on the host-side tasked with handling session updates initiated from the client-side.
*/
public interface ISessionHandler
{
/**
* Update the host-side session based on the given object references.
*
* @param sessionObjects List of {@link InteropObjectRef} to session objects that have been updated
* (group member, rev rule, project).
*
* @param properties List of {@link StringArrayProperty} to preferences (name/value pairs)
* that have been updated.
*/
void handleSessionRequest( List<InteropObjectRef> sessionObjects, List<StringArrayProperty> properties );
}
A simplified overview of the 'handshake' phase steps after basic host-side initialization is:
1) HCI tells a web browser to load a URL that specifies the client-side to be hosted.
2) HCI waits till client invokes webSideStartHandShake method in the HCI.
3) HCI 'Pings' the client to assure bi-directional communications are working.
4) HCI pushes the list of supported host-side services to the client.
5) HCI requests the client to return a list of its supported client-side services.
6) HCI invokes the StartupNotification.client service on the client. The client is now
responsible for completing any login/authorization.
7) HCI waits till the client invokes the StartupNotification.host service on the host.
At this point, the 'handshake' phase is largely complete and normal bi-directional communication can
now continue. The next step is optional, but common, and allows the host to configure some specific
options in the client's UI and its operation:
Client invokes HostConfigurationSvc.host which returns a set of name/value pairs. These define
the host's desired options for the client to honor.
For a more complete state diagram of these 'handshake' phase steps (and some other common host-
client iterations), follow this link:
BIO_2.2.3.20140926_HandShakeStates_Full.png
Note: This example assumes you have deployed the Active Workspace client and this example code
at: http://localhost:8080/. If this is not true you will need to update the URL in the
index.html file.
Deployment Structure
Deploy the example files to your web server using the following structure.
<webserver root>/
tests/
index.html
js/
helloHosting.js
splmBrowserInterOp.js (from hosting deployment)
Once this is done, you can run the test by loading the following URL:
http://<webserver>/tests/index.html
Html (tests/index.html)
<!DOCTYPE html>
<html>
<head>
<title>Hello Hosting</title>
<script type="text/javascript" src="js/splmBrowserInterOp.js"></script>
<script type="text/javascript" src="js/helloHosting.js"></script>
</head>
<body onload="HELLO_HOSTING.initializeHosting('http://localhost:8080/?ah=true')">
<iframe src="" width="100%" height="100%" id="clientIFrame"></iframe>
</body>
</html>
Javascript (tests/js/helloHosting.js)
/*global
INF_INTEROP,
INF_SERVICES_LOGGING_2014_02,
INF_UTILS,
window
*/
/**
* The Classes and Methods in this namespace implement a simple 'hello world' example of hosting a client.
* <P>
* It provides an example of:
* <ul>
* <li>Define a local handler for the LoggerForwardSvc that extends the ILogEntryHandler interface class.</li>
* <li>Locate the <iframe> (defined in the index.html) that AW client will be hosted within.</li>
* <li>Get the INF_INTEROP.HostManager singlton.</li>
* <li>Register the local handler for the LoggerForwardSvc with the INF_INTEROP.HostManager</li>
* <li>Set the URL to the Active Workspace client provided to the 'HELLO_HOSTING.initializeHosting' method</li>
* </ul>
* When the URL is set into the <iframe>, the Active Workspace client will be launched and the 'handshake' phase between
* this host and that client will performed. After this, all logging events generated in client will be sent to the
* host-side logging service and logged by the MyLogEntryHandler class.
*
* @namespace HELLO_HOSTING
* @ApiVisibility( maturity = MaturityEnum.Experimental, publishScope = Scope.Public )
*/
(function (HELLO_HOSTING) {
/**
* Implements (extends) the {@link INF_SERVICES_LOGGING_2014_02.ILogEntryHandler|ILogEntryHandler} interface.
*
* @constructor MyLogEntryHandler
*
* @augments INF_SERVICES_LOGGING_2014_02.ILogEntryHandler
*/
function MyLogEntryHandler () {
INF_SERVICES_LOGGING_2014_02.ILogEntryHandler.call(this);
}
MyLogEntryHandler.prototype = Object.create(INF_SERVICES_LOGGING_2014_02.ILogEntryHandler.prototype);
/**
* Simply lists out the properties of the arriving log entry message.
*
* @see {@link INF_SERVICES_LOGGING_2014_02.ILogEntryHandler}
*/
MyLogEntryHandler.prototype.handleEntry = function (msg) {
INF_UTILS.logMessage( //
"vvvvv [From client-side logging:]\n" + //
"Version: " + msg.getVersion() + " " + //
"Level: " + msg.getLevel() + "\n" + //
msg.getFormatMessage() + "\n" + //
"^^^^^");
};
/**
* This callback method is invoked by the INF_INTEROP.HostManager as part of the initialization of the
* INF_INTEROP.HostControlInstance. Any services and their handlers provided this host should be registered here.
*
* @param {INF_INTEROP.HostManager}
/**
* Initialize INF_INTEROP.HostManager with the required host-side services and create the
* INF_INTEROP.HostControlInstance used for all future interaction between the host and client.
*
* @param {String}
* url - URL to the Active Workspace client (assumed to have the ah=true query already added to it).
*/
HELLO_HOSTING.initializeHosting = function (url) {
var clientIFrame = document.getElementById("clientIFrame");
if (hostManager) {
hostManager.initializeHostIntegration(clientIFrame, registerHostServices);
}
clientIFrame.src = url;
};
Headquarters
Europe
Granite Park One
Stephenson House
5800 Granite Parkway
Sir William Siemens Square
Suite 600
Frimley, Camberley
Plano, TX 75024
Surrey, GU16 8QD
USA
+44 (0) 1276 413200
+1 972 987 3000
Asia-Pacific
Americas
Suites 4301-4302, 43/F
Granite Park One
AIA Kowloon Tower, Landmark East
5800 Granite Parkway
100 How Ming Street
Suite 600
Kwun Tong, Kowloon
Plano, TX 75024
Hong Kong
USA
+852 2230 3308
+1 314 264 8499