Article by Steven.
Published: January 19, 2010 at 17:01
Category: ActionScript 3, Flex, HTML, LiveCycle Data Services
How does Flex know an HTML popup has been closed?
< back to overviewImagine a situation where you have a Flex application and you need to display a popup window in which an external application is doing something else with the same data. You don’ have control over the external application, but you know it is written in HTML and PHP. Imagine also that your Flex application needs to be locked (or at least some part of it) as long as the external popup application is still running. How do you cope with such a situation? Well, that’s what I’m going to show you.
The first system that comes to mind when doing something like this is using JavaScript and the ExternalInterface class to open the popup window. This is necessary because in the opened window you need to have a correct reference to the HTML page that actually opened it. In that popup window, you then capture the onUnload JavaScript event to quickly call a method on the opener, like so:
<body onUnload="opener.notifyFlex()">
In the HTML wrapper for the Flex SWF you then have a JavaScript function called notifyFlex() that will trigger a method call in the actual SWF file to tell it the popup window has been closed.
<script language="text/javascript">
function notifyFlex() {
${application}.closePopup();
}
</script>The ${application} will be replaced at compile time with the actual is of your Flex application. Deploying this on a webserver works perfectly and your Flex application is notified when the popup window is closed. However, when the URL for the popup is actually residing on a remote server, things are different, because now, it’s not working anymore. I’m not quite sure why this is (hey, at least I’m honest), but I assume it has something to do with the HTTP session not being the same for both applications. But anyway, that means there has to be a different and more bullet proof way of detecting this close action.
First of all, you need to know that in this particular situation (which is actually a real project) I am making use of LiveCycle Data Services. Well, it’s not that I’m using it just for this, because the project also needed some real-time collaboration as well. But since I have the technology available, I might as well take advantage of it.
Therefore, I’m going to make use of RTMP (Real-Time Messaging Protocol) that is available in LCDS. This is a way of communicating with the server on a real-time basis (instead of polling). This also opens a two-way connection so you can push data from the server to the client. And that’s exactly what I need to do what I want to achieve. So, the first step is creating a destination on the server to which I can connect my application to listen for messages. So, in the messaging-config.xml file, you create a new destination.
<destination id="popupMonitor"> <channels> <channel ref="my-rtmp"/> </channels> <adapter ref="jms"/> <properties> <jms> <connection-factory> java:comp/env/jms/flex/TopicConnectionFactory </connection-factory> <destination-type>Topic</destination-type> <destination-jndi-name> java:comp/env/jms/topic/flex/simpletopic </destination-jndi-name> <message-type>javax.jms.TextMessage</message-type> </jms> </properties> </destination>
Notice that I’m using the JMS adapter this time, because it is the JEE server that will start the transmission of the message. In the Flex code I create a new message service and start listening to that destination. That seems to be going smoothly, but there’s a catch: You are now listening to a destination, but no one is sending any messages. For that, you first need to create a new service on the back end side. But that service in not a message service class, but rather a remote object which will be accessed using RTMP. Yes, the RTMP channel is not only reserved for chat applications and Data Management Services. You can use it for all service types that you would otherwise connect using the AMF protocol. Just write a new Java Assembler class like you would with any other service.
But because the connection with this class is established using RTMP, that class can now be configured in such a way that it is notified of the fact that the client, which is the popup window, has been closed. This is possible because the RTMP connection is monitored at both ends, so if one part is dropped, the other side knows about it immediately. Now, what does this configuration look like? As you can see in the code below, it really doesn’t take that much to get notified of the connection drop.
/** * @author Steven Peeters * Adobe Flex & AIR Certified Instructor * steven@multimediacollege.be * * This class monitors the Flex session via an RTMP connection. When * a user closes the browser, this class is being notified of that drop. * This notification can be used to send a message to other clients that * are still listening to the message destination. */ public class PopupMonitor implements FlexSessionListener { private Log4JLogger log = new Log4JLogger("PopupMonitor"); protected FlexSession session; private int prop1; private String prop2; /** * The constructor */ public BookEditorMonitor() { FlexSession.addSessionCreatedListener(this); } /** * @param FlexSession The session that has been created * * This method is called when the user starts up the RTMP * connection for this service. */ @Override public void sessionCreated(FlexSession session) { log.debug("==========> Monitoring client with session " + session.getId()); this.session = session; session.addSessionDestroyedListener(this); } /** * @param FlexSession The session that has been created * * This method is called when the user closes the browser window. */ @Override public void sessionDestroyed(FlexSession session) { log.debug("==========> Client with session " + session.getId() + " has been closed. Notifying connected applications."); AsyncMessage message = new AsyncMessage(); message.setMessageId("Popup-Closing-Event-ID"); message.setDestination("popuprMonitor"); message.setBody("The popup has been closed"); message.setHeader("sender", "LCDS"); message.setHeader("prop1", prop1); message.setHeader("prop2", prop2); MessageBroker.getMessageBroker(null).routeMessageToService(message, null); } /** * @param prop1 The first property * @param prop2 The second property * @return * * This method will set some additional properties that will be sent * back to the other listening clients when the connection with this * client has been dropped. */ public void setParameters(int prop1, String prop2) { this.prop1 = prop1; this.prop2 = prop2; } }
Now you’re all set up to go, except for the fact that the application contained in the popup is not a Flex application. So how do you go about connecting to the RTMP channel when you don’t have access to the application (but you do have access to the wrapper, that’s important)? Well, you simply create a second Flex application. Although this application will never be visible in the popup html window (it resides inside a hidden <div> for example), it has to be included, because it will launch the connection with the server and maintain it. That’s all it needs to do, because when the popup is closed, the SWF is destroyed and the connection with the server is dropped, so the server gets notified and sends a message to the other connected clients.
As an addition to this system you can foresee multiple clients listening, but only one client needing to react. Therefore I’ve also opted to provide some anonymous parameters which can be set from within the invisible client, potentially through some FlashVars that come from the URL of the popup window. So yes, you can send parameters from the Flex app to the popup window and then from the popup window back to the original Flex app. Is that cool or what?
< back to overviewComments
ciprian todea on January 19, 2010 at 8:31 pm
I thing you can do it very easy using localConnection without lcds. Setting an object in popup’s hidden swf, you can check from opener (using a Timer) if object exists..etc…
Ciprian
Steven Peeters on January 20, 2010 at 7:24 am
You know, you’re absolutely right and I’ve thought about using that system as well. But then you’re back to a polling mechanism, which I really wanted to avoid. Plus there is also the fact that no one can say for sure if the opening SWF file is not closed first. A user is a very strange creature
When you’re using LocalConnection in that scenario, you can’t do anything anymore, simply because the SWF doesn’t exist anymore.
When using LCDS like I did, you can still perform certain tasks on the server (like maybe start up a batch process or setting a flag in the database or …), even if the notification to the client has failed because the client isn’t there anymore.
I hope this clarifies things a bit more in view of the advantages with this mechanism…
ciprian todea on January 20, 2010 at 1:19 pm
Idd, you’re right; but you can check if “other” file exists from both sides
using LocalConnection.
I think both methods have pros/cons, but that’s about project type/size; What I wanted to say: sometimes is more confortable and quick using localConnection
Thanks,
Cip

Tweets that mention Just blogged about a very cool RTMP communication feature: -- Topsy.com on January 19, 2010 at 5:33 pm
[...] This post was mentioned on Twitter by HowDo.us, aikisteve. aikisteve said: Just blogged about a very cool RTMP communication feature: http://bit.ly/4yJ4N7 [...]