XmlBlaster Logo

REQUIREMENT

client.activex

XmlBlaster Logo


Type NEW
Priority MEDIUM
Status DEPRECATED
Topic XmlBlaster provides access from C# and VisualBasic with ActiveX.
Des
cription

Overview

With the free ActiveX bridge from SUN (Java JDK 1.5) we can export the Java client library into an ActiveX control (Windows only). With this control easy access to xmlBlaster is possible from C# or Visual Basic .net

With this control you can access xmlBlaster from C# or VisualBasic which calls a client side java bean which again connects to the xmlBlaster server. All invocations are done with Micheles xml scripting client, see client.script requirement and ActiveX bridge howto by calling sendRequest().

Alternatively you can use the methods like connect() or subscribe() directly. These methods have the advantage to return a ready parsed object to the ActiveX component, for example Visual Basic can directly call all methods of SubscribeReturnQos which is returned by subscribe(). You can lookup all supported methods in the Javadoc API of XmlScriptAccess bean.

Limitations

It seems to work only with the ActiveX bridge from JDK 1.5 or higher. JDK 1.4.2 failed on my Windows XP. Probably we need to create a ActiveX VisualBasic 6.0 proxy control which delegates the calls from VisualBasic .net for JDK 1.4.2

If you compile the dll bridge with xmlBlaster/build.xml you need a MS Visual C++ installed otherwise the IDL compiler midl.exe is missing. In the runtime environment you don't need VC++.

It was tested for example with Microsoft .net Framework 1.1.4322 on Windows XP Professional 2002 Service Pack 1.

Installation

First you need to create a dll and register it, theses steps are described in the above mentioned howto. We have added these steps into a new ant task called activex, the call below will generate everything and register the ActiveX bridge on the fly:

   build  -DJRE_HOME=C:\PROGRA~1\Java\j2re1.5.0  activex

Please adjust the JRE_HOME setting to point to your Java runtime installation.

Now you find all generated files under

   C:\PROGRA~1\Java\j2re1.5.0\activex

and the control is registered in the registry under

   regedit -> HKEY_CLASSES_ROOT
           -> CLSID
           -> D824B185-AE3C-11D6-ABF5-00B0D07B8581
           -> XmlScriptAccess Bean Control

Demonstration clients

A functional C# demo client and a VisualBasic .net client you find in directory

xmlBlaster\demo\activex 

Here are the steps to use the demonstration clients from Visual Studio:

  Visual Studio:
    -> Solution Explorer
    -> References
    -> (right mouse): Add Reference
    -> COM -> XmlScriptAccess

  Task bar: Java Icon:
    -> Open Console

Don't forget to start the server first:

    java -jar lib\xmlBlaster.jar

API usage

The Java bean XmlScriptAccess exports all needed functionality to activeX aware languages. All native data types are directly available from C#/VisualBasic, xmlBlaster specific java objects returned by XmlScriptAccess methods are automatically available in C#/VisualBasic, so you simply use the returned objects as you would in java. Here is an example of the subscribe method declaration in Java (XmlSriptAccess bean)

public SubscribeReturnQos subscribe(String xmlKey, String xmlQos)
                                    throws XmlBlasterException
   

In Visual Basic you use the returned java object like

Try
   ...

   Dim subscribeReturnQos As Object

   subscribeReturnQos = xmlBlaster.subscribe("<key oid='HelloWorld3'/>", "<qos/>")
   
   ' Access examples:
   ' subscribeReturnQos.getSubcriptionId()
   ' subscribeReturnQos.getStateInfo()
   ' ...

   ...
Catch e As SystemException
   Console.WriteLine("Exception:" & e.ToString())
   

You just lookup all methods of the returned java object in the Javadoc API and call the methods as you would do in Java

Callback usage

As events into ActiveX can't have a return value and can't throw an exception back to us we handle it here as a callback, for example Visual Basic needs to call sendUpdateReturn() or sendUpdateException() after processing a message received by update(). Our update thread blocks until one of those two methods is called, however the blocking times out after 10 minutes which is adjustable with the property client/activex/responseWaitTime given in milli seconds.

The 'msg' received in the callback method is of type UpdateEvent, the usage is described in the UpdateEvent Javadoc.

Accessing byte[] with ActiveX bridge

Private Sub XmlScriptAccess_update(ByVal msg As Object) _
               Handles xmlBlaster.XmlScriptAccessSource_Event_update
   Try
      ' Access the raw content bytes (by Robert Dupuy) ...
      Dim len As Int32 = msg.getContent().length
      Dim str As String
      Dim contentSBytes As SByte()
      contentSBytes = msg.getContent()
      Dim contentBytes As Byte() = New Byte(contentSBytes.Length)
      Buffer.BlockCopy(contentSBytes, 0, contentBytes, 0, contentSBytes.Length)
      str = System.Text.Encoding.ASCII.GetString(contentBytes)
      MsgBox("Success, message arrived:" & str) 

      ...
   

If the ActiveX bridge has problems to convert Java byte[] as described in bug_id=4887461 you can use the Base64 workaround:

We offer in the callback a method msg.getContentBase64() if you need to transfer binary data. If your content contains only strings (for example a xml string) you can use the convenience method msg.getContentStr().

Here is an example for the callback method update(), str and decoded contain the same result:

Private Sub XmlScriptAccess_update(ByVal msg As Object) _
               Handles xmlBlaster.XmlScriptAccessSource_Event_update
   Try
      Dim str As String
      Dim len As Int32
      Dim encoded As String
      Dim decoded As Byte()

      str = msg.getContentStr()
      len = msg.getContentLength()
      encoded = msg.getContentBase64()
      decoded = Convert.FromBase64String(encoded)

      ...
   
Example
Csharp

C# example



// HelloWorld.cs
using System;
namespace demo
{
   class XmlBlasterCSharp
   {
      [STAThread]
      static void Main(string[] args)
      {
         // See registry: 
         //   regedit -> HKEY_CLASSES_ROOT
         //           -> CLSID
         //           -> D824B185-AE3C-11D6-ABF5-00B0D07B8581
         //           -> XmlScriptAccess Bean Control
         // org.xmlBlaster.client.activex.XmlScriptAccess

         XmlScriptAccess.XmlScriptAccessClass xmlBlaster;
         xmlBlaster = new XmlScriptAccess.XmlScriptAccessClass();

         string[] argArr = { "-protocol", "SOCKET" };
         xmlBlaster.initArgs(argArr);

         string request = "<xmlBlaster>" +
                          "   <connect/>" +
                          "   <wait delay='1000' />" +
                          "   <publish>" +
                          "      <key oid='test'><airport name='london' /></key>" +
                          "      <content>This is a simple script test</content>" +
                          "      <qos/>" +
                          "   </publish>" +
                          "</xmlBlaster>";
         string response = xmlBlaster.sendRequest(request);
         Console.WriteLine("Got response from xmlBlaster: " + response);
      }
   }
}
   

Tim Heilig posted how to retrieve the MsgUnit from callback calls in the update() method

While I don't have time to update the examples on the website,
I figured I'd send a code snippet to the list of a working C# client with the activex bridge.

this code should reside in a try/catch so you can appropriately set the update return if problems occur,
I just included the success case.


public void update(object o)
{
 string key;
 string content;
 string qos;
 object k;
 object q;

 Type eventType = o.GetType();
 k = eventType.InvokeMember("getKey", System.Reflection.BindingFlags.InvokeMethod,
                            null, o, null);
 q = eventType.InvokeMember("getQos", System.Reflection.BindingFlags.InvokeMethod,
                            null, o,null);
 content = (string)eventType.InvokeMember("getContentStr",
                                          System.Reflection.BindingFlags.InvokeMethod,
                                          null, o, null);
 eventType = k.GetType();
 key = (string)eventType.InvokeMember("toXml",
                                      System.Reflection.BindingFlags.InvokeMethod,
                                      null, k, null);
 eventType = q.GetType();
 qos = (string)eventType.InvokeMember("toXml",
                                      System.Reflection.BindingFlags.InvokeMethod,
                                      null, k, null);
           MemoryStream memoryStream = new MemoryStream(new UTF8Encoding().GetBytes(content));
 XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);

 XmlDocument keyDoc = new XmlDocument();
 keyDoc.LoadXml(key);

 xmlBlaster.setUpdateReturn("

");
 return;
}
Example
VisualBasic

VisualBasic.net example



' Simple HelloWorld example how to access xmlBlaster
' We connect to xmlBlaster and query the free memory of the server
' @author Marcel Ruff
Module HelloWorld
    Sub Main()
        Dim request, response As String
        Dim xmlBlaster As XmlScriptAccess.XmlScriptAccessClass

        xmlBlaster = New XmlScriptAccess.XmlScriptAccessClass

        ' Configure using the SOCKET protocol
        Dim argArr(1) As String
        argArr(0) = "-protocol"
        argArr(1) = "SOCKET"
        xmlBlaster.initArgs(argArr)

        ' Connect to the server
        response = xmlBlaster.sendRequest("<xmlBlaster><connect/></xmlBlaster>")

        ' Query the free memory
        request = "<xmlBlaster><get><key oid='__cmd:?freeMem'/></get></xmlBlaster>"
        response = xmlBlaster.sendRequest(request)
        Console.WriteLine("Got response:" & response)

        ' Leave the server
        response = xmlBlaster.sendRequest("<xmlBlaster><disconnect/></xmlBlaster>")
    End Sub
End Module
   
Example
VisualBasic

VisualBasic.net example with asynchronous callbacks



'------------------------------------------------------------------------------
' XmlBlaster access with asynchronous callbacks from Visual Basic .net
' Calls are routed over ActiveX encapsulating a Java client bean which
' connects to xmlBlaster
' @file xmlBlaster/demo/activex/VisualBasic3.vb
' @author Marcel Ruff, xmlBlaster@marcelruff.info (2004-03-17)
' @see http://www.xmlBlaster.org/xmlBlaster/doc/requirements/client.activex.html
' @see org.xmlBlaster.client.activex.XmlScriptAccess
'------------------------------------------------------------------------------
Imports System

Module HelloWorld3
   Private WithEvents xmlBlaster As XmlScriptAccess.XmlScriptAccessClass
   Dim ascii As System.Text.ASCIIEncoding = New System.Text.ASCIIEncoding

   Sub Main()
      Call HelloWorld3()
   End Sub

   '---------------------------------------------------------------------------
   ' This method is called asynchronously from java delivering a message. 
   ' As events from java into ActiveX can't deliver a return value
   ' or an exception back we need to call either
   '    setUpdateReturn()        -> passes a return value to the server
   ' or
   '    setUpdateException()     -> throws an XmlBlasterException
   ' If you forget this the update thread of the java bean will block forever
   ' @param msg See
   ' http://www.xmlblaster.org/xmlBlaster/doc/client-api/org/xmlBlaster/client/activex/UpdateEvent.html
   '---------------------------------------------------------------------------
   Private Sub XmlScriptAccess_update(ByVal msg As Object) _
               Handles xmlBlaster.XmlScriptAccessSource_Event_update
      Try
         Dim age As String
         age = msg.getQos().getClientProperty("myAge").getStringValue()
         Console.WriteLine("SUCCESS: Update arrived: " & msg.getCbSessionId() & _
                 ", oid=" & msg.getKey().getOid() & _
                 ", content=" & msg.getContentStr() & _
                 ", myAge=" & age)
         xmlBlaster.setUpdateReturn("<qos><state id='OK'/></qos>")
      Catch e As SystemException
         Console.WriteLine("Exception in update:" & e.ToString())
         xmlBlaster.setUpdateException("user.update.internalError", e.ToString())
      End Try
   End Sub

   '---------------------------------------------------------------------------
   ' Connect to xmlBlaster and try all possible methods
   '---------------------------------------------------------------------------
   Sub HelloWorld3()
      Dim key, qos As String

      xmlBlaster = New XmlScriptAccess.XmlScriptAccessClass

      Dim prop As Object = xmlBlaster.createPropertiesInstance()
      prop.setProperty("protocol", "SOCKET")
      prop.setProperty("trace", "false")
      xmlBlaster.initialize(prop)

      Try
         ' Connect to the server
         qos = "<qos>" & _
               "  <securityService type='htpasswd' version='1.0'>" & _
               "   <![CDATA[" & _
               "   <user>HelloWorld3</user>" & _
               "   <passwd>secret</passwd>" & _
               "   ]]>" & _
               "  </securityService>" & _
               "</qos>"
         Dim connectReturnQos As Object
         connectReturnQos = xmlBlaster.connect(qos)
         Console.WriteLine("Connected to xmlBlaster, sessionId=" & _
                           connectReturnQos.getSecretSessionId())

         ' Publish a message
         key = "<key oid='HelloWorld3' contentMime='text/xml'>" & _
               "  <org.xmlBlaster><demo/></org.xmlBlaster>" & _
               "</key>"
         Dim content As Byte() = ascii.GetBytes("Hi")
         qos = "<qos>" & _
               "<clientProperty name='myAge' type='int'>18</clientProperty>" & _
               "</qos>"
         Dim publishReturnQos As Object
         publishReturnQos = xmlBlaster.publishBlob(key, content, qos)
         Console.WriteLine("Published message id=" & _
                       publishReturnQos.getRcvTimestamp().toXml("", True))

         ' Get synchronous the above message
         Dim getMsgArr As Object()
         getMsgArr = xmlBlaster.get("<key oid='HelloWorld3'/>", "<qos/>")
         Dim msg As Object
         For Each msg In getMsgArr
            Console.WriteLine("Get returned:" & msg.toXml())
         Next

         ' Subscribe
         Dim subscribeReturnQos As Object
         subscribeReturnQos = xmlBlaster.subscribe("<key oid='HelloWorld3'/>", "<qos/>")
         Console.WriteLine("Got subscribe response:" & _
                           subscribeReturnQos.getSubscriptionId())

         ' Give control to the main loop to receive the update event
         System.Windows.Forms.Application.DoEvents()

         ' Publish again, message arrives asynchronously in
         ' Sub XmlScriptAccess_update() (see above)
         publishReturnQos = xmlBlaster.publishStr(key, "Ho", qos)
         Console.WriteLine("Got publish response:" & publishReturnQos.toXml())

         ' Give control to the main loop to receive the update event
         System.Windows.Forms.Application.DoEvents()

         ' UnSubscribe
         Dim k As String = "<key oid='" & subscribeReturnQos.getSubscriptionId() & "'/>"
         xmlBlaster.unSubscribe(k, "<qos/>")

         ' Destroy the topic "HelloWorld3"
         xmlBlaster.erase("<key oid='HelloWorld3'/>", "<qos/>")

         ' Leave the server, cleanup resources
         xmlBlaster.disconnect("<qos/>")

         ' Pass control to eventLoop ...
         MsgBox("Click me to finish ...")

      Catch e As SystemException
         Console.WriteLine("Exception:" & e.ToString())
      End Try
   End Sub
End Module

   
Example
VisualBasic

VisualBasic 6 example

Note: The synchronous invocations like connect or publish work fine. The asynchronous callbacks into the function XmlScriptAccess_update don't arrive, the reason is not tracked down. We can see that the Activex Java bean pushes the message into the ActiveX layer, probably the VB function name used (XmlScriptAccess_update()) is not correct? Note that the VisualBasic.net example works fine with exactly the same ActiveX component




'---------------------------------------------------------------------------
' This method is called asynchronously from java delivering a message.
' As events from java into ActiveX can't deliver a return value
' or an exception back we need to call either
'    setUpdateReturn()        -> passes a return value to the server
' or
'    setUpdateException()     -> throws an XmlBlasterException
' If you forget this the update thread of the java bean will block forever
'---------------------------------------------------------------------------
Public Sub XmlScriptAccess_update(ByVal msg As Object)
   On Error GoTo UpdateErrorHandler
      Dim age As String
      age = msg.getQos().getClientProperty("myAge").getStringValue()
      log ("SUCCESS: Update arrived: " & msg.getCbSessionId() & _
              ", oid=" & msg.getKey().getOid() & _
              ", content=" & msg.getContentStr() & _
              ", myAge=" & age)
      MsgBox ("Success, message arrived:" & Str)
      xmlBlaster.setUpdateReturn ("<qos><state id='OK'/></qos>")
   Exit Sub

UpdateErrorHandler:
   log (Err.Number & ": " & Err.Description)
   xmlBlaster.setUpdateException "user.update.internalError", Err.Description
   Exit Sub
End Sub

'---------------------------------------------------------------------------
' Connect to xmlBlaster and try all possible methods
'---------------------------------------------------------------------------
Private Sub xmlBlasterDemo()
   On Error GoTo ErrorHandler
      Set xmlBlaster = CreateObject("XmlScriptAccess.Bean")
      
      Set prop = xmlBlaster.createPropertiesInstance()
      Rem CallByName(prop, "setProperty", vbLet, "protocol","SOCKET")
      Rem prop.setProperty("protocol", "SOCKET")
      Rem prop.setProperty("trace", "false")
      xmlBlaster.Initialize (prop)
      
      Dim argArr(3) As String
      argArr(0) = "-protocol"
      argArr(1) = "SOCKET"
      argArr(2) = "-trace"
      argArr(3) = "false"
      xmlBlaster.initArgs (argArr)
      
      ' Connect to the server
      qos = "<qos>" & _
            "  <securityService type='htpasswd' version='1.0'>" & _
            "   <user>HelloWorld3</user>" & _
            "   <passwd>secret</passwd>" & _
            "  </securityService>" & _
            "</qos>"
      Dim connectReturnQos As Object
      
      Set connectReturnQos = xmlBlaster.Connect(qos)
            
      sessionId = connectReturnQos.getSecretSessionId()
      log ("Connected to xmlBlaster, sessionId=" & sessionId)
      
      ' Publish a message
      Key = "<key oid='HelloWorld3' contentMime='text/xml'>" & _
            "  <org.xmlBlaster><demo/></org.xmlBlaster>" & _
            "</key>"
      contentStr = "Hi"
      qos = "<qos>" & _
            "<clientProperty name='myAge' type='int'>18</clientProperty>" & _
            "</qos>"
      Set publishReturnQos = xmlBlaster.publishStr(Key, contentStr, qos)
      log ("Published message id=" & publishReturnQos.getRcvTimestamp().toXml("", True))
   
      ' Get synchronous the above message
      getMsgArr = xmlBlaster.get("<key oid='HelloWorld3'/>", "<qos/>")
      For Each msg In getMsgArr
         log ("Get returned:" & msg.toXml())
      Next
   
      ' Subscribe
      Set subscribeReturnQos = xmlBlaster.subscribe("<key oid='HelloWorld3'/>", "<qos/>")
      log ("Got subscribe response:" & subscribeReturnQos.getSubscriptionId())
   
      Call loopEvents
      
      ' Publish again, message arrives asynchronously in
      ' Sub XmlScriptAccess_update() (see above)
      Set publishReturnQos = xmlBlaster.publishStr(Key, "Ho", qos)
      log ("Got publish response:" & publishReturnQos.toXml())
      
      Call loopEvents
            
      ' UnSubscribe
      k = "<key oid='" & subscribeReturnQos.getSubscriptionId() & "'/>"
      unSubscribeReturnQos = xmlBlaster.unSubscribe(k, "<qos/>")
   
      ' Destroy the topic "HelloWorld3"
      eraseReturnQos = xmlBlaster.erase("<key oid='HelloWorld3'/>", "<qos/>")
   
      
      Call loopEvents
      
      xmlBlaster.disconnect ("<qos/>")
      Set xmlBlaster = Nothing
      Rem MsgBox ("Hit a key to continue ...")
   Exit Sub
   
ErrorHandler:
   log (Err.Number & ": " & Err.Description)
   MsgBox ("Error, giving up: " & Err.Description)
   Exit Sub
End Sub

Private Sub loopEvents()
   ' Give control to the main loop to receive the update event
   'System.Windows.Forms.Application.DoEvents()
   MsgBox ("Hit a key to continue ...")
End Sub

' Log into the GUI text area
Private Sub log(text)
   Logger.text = Logger.text & vbCrLf & text
End Sub

' GUI button to start the demo
Private Sub Start_Click()
    Call xmlBlasterDemo
End Sub
   
Configure

Set JRE_HOME to point to your java runtime environment, for example
set JRE_HOME=C:\PROGRA~1\Java\j2re1.5.0

NOTE: Configuration parameters are specified on command line (-someValue 17) or in the xmlBlaster.properties file (someValue=17). See requirement "util.property" for details.
Columns named Impl tells you if the feature is implemented.
Columns named Hot tells you if the configuration is changeable in hot operation.

Todo
  1. For VisualBasic.net we provide a complete example with asynchronous callbacks. For C# and VisualBasic6 the callback example is missing.
  2. Fix HSQLDB (JDBC database) driver loading problem. There seems to be a CLASSLOADER problem which we need to solve.
    A work around is to copy xmlBlaster\lib\hsqldb.jar to %JRE_HOME%\lib\ext. Do the same with any other JDBC driver you want to use.
  3. Fix CORBA driver loading problem. There seems to be a CLASSLOADER problem which we need to solve.
    Copying xmlBlaster\lib\jacorb\*.jar to %JRE_HOME%\lib\ext doe not seem to solve the problem entirely, so please stick to the SOCKET protocol until this issue is resolved.
  4. The ActiveX control has all features of an ordinary xmlBlaster Java client. As we have not found a way yet to send arbitrary events back to C#/VB the subscribe() variant with a specific callback implementation is not supported. Any idea concerning this problem is welcome, please mail it to xmlBlaster@marcelruff.info
See REQ interface
See REQ client.script
See API org.xmlBlaster.client.activex.XmlScriptAccess
See Sun ActiveX bridge guide
See http://forum.java.sun.com/forum.jsp?forum=40
See VisualBasic 6 hints
See Bean implementation
See http://www.xmlblaster.org/xmlBlaster/demo/activex

This page is generated from the requirement XML file xmlBlaster/doc/requirements/client.activex.xml

Back to overview