The one area in web development that I always struggle with is asynchronous messaging from the server to clients over HTTP. I'm pleased to announce that I got a simple application up and running.
The goal of this post is to share the example and lessons learned in order to save others some of the problems I ran into.
My project had a few main goals:
- Get a simple pub/sub example up and running
- Avoid using the "pre-configured" download. I wanted to get a project up and running "from scratch"
- Use my existing ActiveMQ broker running in it's own JVM
- Use my existing Tomcat install
- Use mxml as much as possible and avoid writing Actionscript
- Keep it simple to get a working example
Before I get into the example code, a few things I ran into:
- The BlazeDS documentation is good, but far from perfect. The messaging examples switch from mxml to actionscript, so it makes it a little difficult to piece things together. Also, it's not clear in the examples what many of the values point to--i.e. is that the JMS Topic name or the destination name???? I also found a few syntax problems in the examples which caused some "cut and paste" problems for me.
- A key piece I discovered after nothing was working at all, was the use of the "-services" flag to the mxmlc compiler. You have to compile your mxml file with the services file you plan to call.
- Don't rely on client side faults. Prior to finding out about the "-services" flag, I would call the send method in the client, but nothing happened--the faultHandler was never called on the client, and nothing appeared in the server logs. Once I compiled with the "-services" flag, things began to click.
- Make sure the JNDI names are correct. BlazeDS looks up the JMS ConnectionFactory and Topics via JNDI calls to Tomcat's JNDI. Make sure you specify the full JNDI name of the resource. i.e java:comp/env/jms/messageTopic
The example contains 5 main files:
- A standard web.xml file with a BlazeDS servlet to handle the remote calls
- A Tomcat context.xml file to add the JNDI values for the required JMS resources
- A BlazeDS services-context.xml file to set up the services and channels
- A BlazeDS messaging-config.xml that stores the messaging service(s).
- A test.mxml file which is compiled into the Flex application.
The image to the right shows the file locations in the final WAR layout.
web.xml
The web.xml file is pretty basic. The main 2 things to point out are the HttpFlexSession listener and the MessageBrokerServlet. The servlet loads up you services, so it is passed the location of the services-config.xml file inside the WAR file.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>BlazeDS</display-name>
<description>BlazeDS Application</description>
<!-- Http Flex Session attribute and binding listener support -->
<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>
<!-- MessageBroker Servlet -->
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<display-name>MessageBrokerServlet</display-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MessageBrokerServlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
context.xml
The context.xml file is for Tomcat. You can read more about it in the Tomcat documentation. The key take away is the "name" attribute is the JNDI name that has to match the jndi name used in the messaging-config.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/BlazeTest">
<Resource name="jms/flex/TopicConnectionFactory"
type="org.apache.activemq.ActiveMQConnectionFactory"
description="JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="tcp://localhost:61616"
brokerName="myBroker"/>
<Resource name="jms/messageTopic"
type="org.apache.activemq.command.ActiveMQTopic"
description="a simple topic"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
physicalName="messageTopic"/>
</Context>
services-config.xml
The services-config.xml file is used by BlazeDS. It is first used when you go to compile the mxml file. When you deploy the war file to Tomcat, this file is also loaded up by the MessageBrokerServlet.
During compilation, you pass it in like this using the relative path to it from your mxml file.
~/java_libraries/flex3/bin/mxmlc -services=WEB-INF/flex/services-config.xml test.mxml
My current file turns on logging which can help during resolve messaging problems like jndi resources not being found. This file loads in the messaging-config.xml file which is described below.
The channels determine how the client will talk to the server. The endpoint has to match the servlet-mapping url-pattern contained in the web.xml. Since the servlet is grabbing /messagebroker/* and my application is deployed under BlazeTest, the endpoint becomes:
http://localhost:8080/BlazeTest/messagebroker/amf
The "amf" on the end of the URL can be called anything--it's what distinguishes one channel url from another channel url. Here, I am only using one channel.
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service-include file-path="messaging-config.xml" />
</services>
<security/>
<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://localhost:8080/BlazeTest/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>
<logging>
<target class="flex.messaging.log.ConsoleTarget" level="Debug">
<properties>
<prefix>[BlazeDS] </prefix>
<includeDate>false</includeDate>
<includeTime>false</includeTime>
<includeLevel>false</includeLevel>
<includeCategory>false</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>
</services-config>
messaging-config.xml
This file stores the JMS services and tells BlazeDS where to get the JMS ConnectionFactory and Topics from the JNDI lookup. The part here that caused be a little problem was making sure the connection-factory and destination-jndi-name values had the correct JNDI name Tomcat expected. Also, note the destination id value. When you set up the consumer or producer in the mxml file, you don't tell it what Topic or Queue to connect to, you tell it what destination to connect to. This makes sense, since the mx:Producer and mx:Consumer attribute is called "destination", but it did throw me for a little loop.
<?xml version="1.0" encoding="UTF-8"?>
<service id="message-service" class="flex.messaging.services.MessageService">
<adapters>
<adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" />
<adapter-definition id="jms" class="flex.messaging.services.messaging.adapters.JMSAdapter"/>
</adapters>
<default-channels>
<channel ref="my-amf"/>
</default-channels>
<destination id="message-destination">
<properties>
<jms>
<destination-type>Topic</destination-type>
<message-type>javax.jms.TextMessage</message-type>
<connection-factory>java:comp/env/jms/flex/TopicConnectionFactory</connection-factory>
<destination-jndi-name>java:comp/env/jms/messageTopic</destination-jndi-name>
<delivery-mode>NON_PERSISTENT</delivery-mode>
<message-priority>DEFAULT_PRIORITY</message-priority>
<acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
<initial-context-environment>
<property>
<name>Context.INITIAL_CONTEXT_FACTORY</name>
<value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value>
</property>
<property>
<name>Context.PROVIDER_URL</name>
<value>tcp://localhost:61616</value>
</property>
</initial-context-environment>
</jms>
</properties>
<channels>
<channel ref="my-amf"/>
</channels>
<adapter ref="jms"/>
</destination>
</service>
test.mxml
The test.mxml is the final file. It's the GUI code. The file gets compiled into a swf file as I showed up above. My version uses mx:Producer and mx:Consumer mxml tags, though you can also instantiate these using Actionscript if you'd like. I prefer to write as little script as possible. The main key in this file was that I had to subscribe the consumer to it's destination before it would consume messages. Everything else is pretty simple, and you can see how easy it is to get a client sending and receiving messages to the server-side JMS broker over HTTP. This is why I like Flex!
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx='http://www.adobe.com/2006/mxml' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
backgroundGradientColors="[0xcfe4ff, 0xffffff]" paddingTop="5" paddingLeft="5"
paddingRight="5" paddingBottom="0" applicationComplete="logon()">
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.messaging.*;
import mx.messaging.messages.*;
import mx.messaging.events.*;
import mx.controls.Alert;
private function messageHandler(event:MessageEvent):void {
receiveTextArea.text = event.message.body + "\n" + receiveTextArea.text;
}
private function acknowledgeHandler(event:MessageAckEvent):void{
}
private function faultHandler(event:MessageFaultEvent):void {
Alert.show('Fault!', 'Alert Box', mx.controls.Alert.OK);
}
private function logon():void {
consumer.subscribe();
}
private function sendMessage():void {
var message:AsyncMessage = new AsyncMessage();
message.body = sendTextArea.text;
producer.send(message);
sendTextArea.text="";
}
]]>
</mx:Script>
<mx:Producer id="producer" destination="message-destination" acknowledge="acknowledgeHandler(event);" fault=" faultHandler(event)"/>
<mx:Consumer id="consumer" destination="message-destination" message="messageHandler(event)" acknowledge="acknowledgeHandler(event);" fault=" faultHandler(event)"/>
<mx:HBox>
<mx:TextInput id="sendTextArea"/>
<mx:Button label="Send" click="sendMessage();"/>
</mx:HBox>
<mx:TextArea id="receiveTextArea" width="50%" height="200"/>
</mx:Application>
The client app looks something like this:
In Conclusion, BlazeDS is an extremely simple way to use JMS with remote Flex clients over HTTP connections. The back-end configuation is somewhat complex, but mostly boilerplate and not too bad once you piece it together.
I haven't "stress" tested the messaging yet, or done very complex messaging to be able to say how robust and scalable it is, but so far, It does live up to it's claims. Best of all, I can now do asynchronous messaging to remote clients without writing code.
34 comments:
Thank you very much, very helpful explanations.
Thank you very much, very helpful explanations
Thank you for this excellent intro to BlazeDS without having to use the Turnkey distro.
I have one problem with running your current example code. In my console log I get this error message when I try to access the compiled test swf:
[BlazeDS][DEBUG] [Endpoint.FlexSession] FlexSession created with id '3796C323705E9DC2AC0BF9F4327DCCE0' for an Http-based client connection.
[BlazeDS][INFO] [Endpoint.General] Channel endpoint my-amf received request.
[BlazeDS][DEBUG] [Endpoint.AMF] Deserializing AMF/HTTP request
Version: 3
(Message #0 targetURI=null, responseURI=/1)
(Array #0)
[0] = (Typed Object #0 'flex.messaging.messages.CommandMessage')
operation = 5
correlationId = ""
body = (Object #1)
timeToLive = 0
headers = (Object #2)
DSMessagingVersion = 1
DSId = "nil"
messageId = "27370128-DE54-6E10-A2CD-E9011C6227C4"
destination = ""
clientId = null
timestamp = 0
[BlazeDS][DEBUG] [Endpoint.AMF] Serializing AMF/HTTP response
Version: 3
(Header #0 name=AppendToGatewayUrl, mustUnderstand=false)
";jsessionid=3796C323705E9DC2AC0BF9F4327DCCE0"
(Message #0 targetURI=/1/onResult, responseURI=)
(Externalizable Object #0 'DSK')
(Object #1)
DSMessagingVersion = 1.0
DSId = "B2DBB8B9-D10B-7336-AEF1-26DAAA93A9F5"
1.215089941857E12
(Byte Array #2, Length 16)
(Byte Array #3, Length 16)
(Byte Array #4, Length 16)
[BlazeDS][INFO] [Endpoint.General] Channel endpoint my-amf received request.
[BlazeDS][DEBUG] [Endpoint.AMF] Deserializing AMF/HTTP request
Version: 3
(Message #0 targetURI=null, responseURI=/2)
(Array #0)
[0] = (Typed Object #0 'flex.messaging.messages.CommandMessage')
operation = 0
correlationId = ""
body = (Object #1)
timeToLive = 0
headers = (Object #2)
DSEndpoint = "my-amf"
DSId = "B2DBB8B9-D10B-7336-AEF1-26DAAA93A9F5"
messageId = "47084F4D-EF1C-825B-CE98-E9011D81CE2A"
destination = "message-destination"
clientId = null
timestamp = 0
[BlazeDS][INFO] [Service.Message.JMS] JMS consumer for JMS destination '
java:comp/env/jms/messageTopic
' is being removed from the JMS adapter due to the following error: Cannot instantiate class:
org.apache.activemq.jndi.ActiveMQInitialContextFactory
[BlazeDS][INFO] [Service.Message.JMS] JMS consumer for JMS destination '
java:comp/env/jms/messageTopic
' is stopping.
[BlazeDS][INFO] [Service.Message.JMS] The corresponding MessageClient for JMS consumer for JMS destination '
java:comp/env/jms/messageTopic
' is being invalidated
[BlazeDS][DEBUG] [Service.Message] Warning: could not find MessageClient for clientId in pushMessageToClients: B2DBB933-E31E-06A9-8BDF-17A05AE79137 for destination: message-destination
[BlazeDS][DEBUG] [Service.Message] Warning: could not find MessageClient for clientId in pushMessageToClients: B2DBB933-E31E-06A9-8BDF-17A05AE79137 for destination: message-destination
[BlazeDS][DEBUG] [Endpoint.AMF] Serializing AMF/HTTP response
Version: 3
(Message #0 targetURI=/2/onStatus, responseURI=)
(Typed Object #0 'flex.messaging.messages.ErrorMessage')
headers = (Object #1)
rootCause = null
body = null
correlationId = "47084F4D-EF1C-825B-CE98-E9011D81CE2A"
faultDetail = null
faultString = "There was an unhandled failure on the server. MessageClient has been invalidated."
clientId = "B2DBB933-E31E-06A9-8BDF-17A05AE79137"
timeToLive = 0.0
destination = "message-destination"
timestamp = 1.215089941937E12
extendedData = null
faultCode = "Server.Processing"
messageId = "B2DBB97A-B00B-A405-A844-3B1EC16AF5C8"
It seems that this is the cause of the error:
Cannot instantiate class:
org.apache.activemq.jndi.ActiveMQInitialContextFactory
That class is available via the activemq-all-5.1.0.jar on my classpath. I am therefore having problems understanding why this is happening.
Any ideas would be highly appreciated!
Thx!
dsleeper,
Not sure what it could be. So long as activemq-all-5.1.0.jar is in WEB-INF/lib it should find it. I'm running this in Tomcat 6.0.16. You can also check the tomcat logs to see if there is any additional info.
Hi
First off, Nice work!
I'm also very interested in this code but are encountering the same problems.
I'vd tried using the exact same tomcat and the extra library you suggested.
In my opinion theres so many smaal things here that should work in perfect harmony,and with hardly no debug information it would be hard
to point out the error.
So my question would be: Is it possible to get a runnable copy of this? Like a light weight "turnkey"?
Kind Regards
The only other thing I can think of is that the xml is not forgiving of any spaces between the xml tags and the values. I'm happy to provide you with the code. I set up a temporary email account: mmbloghelp@comcast.net . You can email there your code if you want me to take a look and/or send me an email of where to send you my code.
It seems comcast secondary accounts are having problems. I changed the email to mmartinsoftware@comcast.net in an effort to fix, but it didn't work. Seems like some sync error on comcast mail server not recognizing the new account. I'll leave the mmartinsoftware@comcast.net email there. Hopefully, it will work soon.
Thx for the replies Michael. I have sent you a mail on both those addresses.
What I have been trying to do in the meantime is to start a regular ActiveMQ messagebroker. My thought is that if I can do that I must have all the required libraries on my classpath.
I have therefore added these additional jar files to the classpath of my project:
spring.jar
activeio-core-3.1.0.jar
activeio-2.1.jar (Dunno which one activeMQ uses so i've added both)
geronimo-j2ee-management_1.0_spec-1.0.jar
geronimo-jms_1.1_spec-1.1.1.jar
geronimo-jta_1.0.1B_spec-1.0.1.jar
xbean-spring-3.3.jar
To start the broker i've added a few lines to my web.xml. Unfortunately this blog post text field does not allow me to post the changes. Is there an enclosing tag I can use to post them here? You can find the web.xml in the war file I sent you
This starts a working activemq messagebroker and no errors are found in the tomcat logs. However the flash client still does not work as it gives the same error message as before.
I am therefore nearing the conclusion that this is not a missing class/jar on the classpath, but rather a configuration issue.
Looking forward to hearing from you.
Thx
I'll look for the emails--My email is broken to secondary accts. I have an open ticket now with Comcast which should be resolved in 72hrs. Once that's working, I'll take a look.
dsleeper,
After my email was "down for upgrades" today, my secondary email appears to work now. If you can resend to mmartinsoftware@comcast.net, I'll take a look.
I have resent the mail. Looking forward to hearing from you.
Hi,
Thanks for this help. I just have one request : May you share your .war file ?
Because, this doesn't work on my server, so I would like to know if my activemq configuration is wrong or not.
dsleeper (and others having issues),
The problem is probably with the xml Flex reads in. I mentioned that Flex is not forgiving of any spaces in the xml. This also includes carriage returns / line feeds between the xml tag and the value. Remove all spaces and line feeds so there is nothing between the xml-open-tag,value,xml-close-tag.
dsleeper, I did this to your messaging-config.xml and it then ran fine with the embedded activemq server. ( I did have to re-compile the test.mxmlc too )
If anyone wants my war file, shoot me an email to mmartinsoftware@comcast.net
You are absolutely right Michael! Removing all linefeeds and carriage returns in the messaging-config.xml makes this example run flawlessly.
Thanks again!
This really is a stupid issue to be having in 2008. I mean reading XML with a few linefeeds and carriage returns isn't exactly rocket science :-) Hopefully this is an issue which will be resolved in the future.
Absolutely helpful, thanks for posting this information!
ed this a little bit (using message selectors, java JMS publisher/subscriber), you can download it here: http://www.agimatec.de/blog/2008/08/extended-blazeds-and-jms-example/
Good example, very helpful, thanks a lot!
Thank you very much, this is a very helpful for me to understand blazeDs..
However, I am using weblogic9.2 and to make blazeds using JMSAdapter, can u help me to config the web.xml weblogic.xml and messaging-config.xml?
Thanks,
Thanks for sharing the useful configuration. I'm configuring blazeDS to use OpenMQ jms server. I have problem with the context.xml configuration. Could you help if you know the exact configuration. Please see below for my configuration
Resource name="jms/flex/MyTopicConnectionFactory"
type="com.sun.messaging.TopicConnectionFactory"
description="JMS Connection Factory"
factory="com.sun.jndi.fscontext.RefFSContextFactory"
brokerURL="file:///C:/Temp"
brokerName="CaafBroker"
Resource name="jms/topic/flex/CaafMINotification"
type="com.sun.messaging.Topic"
description="my Topic"
factory="com.sun.jndi.fscontext.RefFSContextFactory"
physicalName="CaafMINotification"
Thanks a lot for this!
Is it possible to specify JMS provider information AFTER compile time though? You used: "mxmlc -services=.." to specify the services-config.xml file, but this couples the application to the JMS provider at compilation time. Is there an equivalent to a "jndi.properties" file where you can change the JMS provider successfully without re-compiling the flex application?
That's a good question, Raj. I never came across a dynamic way of setting those values at runtime.
Hi, I have followed the instructions,
Alert Box error:
There was an unhandled failure on the server. MessageClient has been invalidated.
Anything I had missed out?? Any possible errors ? Please correct me.
Looking forward for your help.
Thank you.
Hi, I have followed the instructions,
Alert Box error:
There was an unhandled failure on the server. MessageClient has been invalidated.
Anything I had missed out?? Any possible errors ? Please correct me.
Looking forward for your help.
Thank you.
Since there seems to be a bud in tomcat 6.0.18 where the context.xml isn't being copied to conf/engine/host/appname.xml, is there a way to incorporate the context.xml into the apps web.xml. ANother interesting thing is if you add the context.xml file and place it in the webapps META-INF dir, tomcat wont process it if the apps warfile in there. If you remove the war it will pick it up and the app works. Your thoughts, Thanks Keith
My problem has been resolved.
Thanks a lot.
I had a comment to Raj's question about whether it's possible to specify JMS provider information after compile time.
The JMS provider information isn't actually compiled into the application. The only information that is compiled into the application is the name of the destination and the channels it uses.
You can confirm this by looking at the ServerConfig object in your compiled Flex application. ServerConfig has an xml property that contains the client side BlazeDS configuration information. This is a subset of what's in the services-config.xml file. The services-config.xml file contains both client side and server side configuration information. When the application is compiled using MXMLC only the client side configuration information from services-config.xml is included in the swf.
What this means is that you can change the JNDI provider information in the services-config.xml file (and restart the server) which will make it so the server has the correct information but as long as the destination name hasn't changed and none of yoru channels have changed you don't need to recompile the swf.
BTW, Michael, you had some good feedback on the BlazeDS documentation. I'm going to forward this post to the writer of the BlazeDS docs as I'm sure he will appreciate your feedback and suggestions.
Hello,
Thank you for the post.
I've been trying to implement your example for several days now and keep running around in circles.
Specifically it seems to be related to Tomcat not being able to bind jms/flex/TopicConnectionFactory
"Name jms is not bound in this Context"
I've looked spacing, line feeds etc in context.xml, services-config.xml, messaging-config.xml, examined multiple web sites, read the BlazeDS documentation and Tomcat documentation extensively and can't seem to get it working.
Are there any suggestions.
Regards,
John
Hello,
I knew as soon as I posted a request, I'd find a solution.
Seems I'd left the old blazeds.war file in the directory and it was re-opening each time I'd run tomcat. It didn't remove any files, but the context was incorrect in the war file implementation. And therefore ActiveMQ JNDI information wasn't right.
Thanks again.
- John
Awesome information -
I'm having a problem as well in that I my AMQ topic is showing that it's both receiving and sending a new message each time I press 'Send' on the flex UI - However, There is nothing being updated in the UI, from the message sent -
I'm pretty sure I have all the pieces in place - I used the turnkey download and modified the files there to match -
Can someone point me in the direction? i have charles running and can see the message come through amf - but my flex client is never receiving any information back it seems.
Thanks.
I retried using the binary blazeds distro then made the mods, created a war file and deployed to tomcat -
Once I compiled the app in FlexBuilder and had amq running - all works as planned!
Thanks so much - now onto building on this.
Hi Michael,
do you still have the sample code / war file available?
It Shows SLF4j version mismatch error
I get the following error (Could not connect to broker URL: tcp://localhost:61616. Reason: java.net.ConnectException: Connection refused:
connect.") when the consumer on the client calls the subscribe(). I guess the server code that listens at port 61616 and loopsback is missing?
From the Tomcat Log :-
[KAPSOrchestrationServer]Serializing AMF/HTTP response
Version: 3
(Message #0 targetURI=/2/onResult, responseURI=)
(Typed Object #0 'flex.messaging.messages.CommandMessage')
timestamp = 1.357764582772E12
headers = (Object #1)
operation = 4
body = (Array #2)
[0] = (Typed Object #3 'flex.messaging.messages.ErrorMessage')
headers = (Object #4)
rootCause = null
body = null
correlationId = null
faultDetail = null
faultString = "JMS invocation caught exception: Could not connect to broker URL: tcp://localhost:61616. Reason: java.net.ConnectException: Connection refused:
connect."
clientId = "CCC03AD1-0608-6A62-64AA-7DB0C2ECC14C"
timeToLive = 0.0
destination = "message-destination"
timestamp = 1.357764582715E12
extendedData = null
faultCode = "Server.Processing"
messageId = "CCC0469D-0B09-5C9F-D9D9-0B7E6EEA44F9"
[1] = (Typed Object #5 'flex.messaging.messages.CommandMessage')
timestamp = 1.357764582726E12
headers = (Object #6)
operation = 10
body = null
correlationId = null
messageId = "CCC046B7-E609-9BB9-EC36-5D07167C64E6"
timeToLive = 0.0
clientId = "CCC03AD1-0608-6A62-64AA-7DB0C2ECC14C"
destination = "message-destination"
correlationId = null
messageId = "CCC04728-340C-9741-1672-0CA220BC3CBB"
timeToLive = 0.0
clientId = null
destination = null
Post a Comment