BlazeDS StreamingAMF: How to detect when flex client closes the connection? - java

I have a Flex application that connects to a BlazeDS server using the StreamingAMF channel.
On the server-side the logic is handled by a custom adapter that extends ActionScriptAdapter and implements FlexSessionListener and FlexClientListener interfaces.
I am asking how can I detect which "flex-client" has closed a connection when for example the user is closing the browser? (so I can clean some infos inside the database)
I tried using the following:
1. To manually manage the command messages:
#Override
public Object manage(final CommandMessage commandMessage) {
switch (commandMessage.getOperation()) {
case CommandMessage.SUBSCRIBE_OPERATION:
System.out.println("SUBSCRIBE_OPERATION = " + commandMessage.getHeaders());
break;
case CommandMessage.UNSUBSCRIBE_OPERATION:
System.out.println("UNSUBSCRIBE_OPERATION = " + commandMessage.getHeaders());
break;
}
return super.manage(commandMessage);
}
But the clientID's are always different from the ones that came.
2. Listening for sessionDestroyed and clientDestroyed events
#Override
public void clientCreated(final FlexClient client) {
client.addClientDestroyedListener(this);
System.out.println("clientCreated = " + client.getId());
}
#Override
public void clientDestroyed(final FlexClient client) {
System.out.println("clientDestroyed = " + client.getId());
}
#Override
public void sessionCreated(final FlexSession session) {
System.out.println("sessionCreated = " + session.getId());
session.addSessionDestroyedListener(this);
}
#Override
public void sessionDestroyed(final FlexSession session) {
System.out.println("sessionDestroyed = " + session.getId());
}
But those sessionDestroyed and clientDestroyed methods are never called. :(

I made it the following way, by using a custom adapter and a static class with a list of connected clients.
#Override
public Object manage(final CommandMessage commandMessage) {
switch (commandMessage.getOperation()) {
case CommandMessage.SUBSCRIBE_OPERATION:
// add user info
// be aware - each time the selector changes this method is called. So when you add user info check to see if you are not duplicating the clients.
addInfoAboutUser(commandMessage.getHeader("DSId").toString(), commandMessage.getClientId().toString());
break;
case CommandMessage.UNSUBSCRIBE_OPERATION:
clearUserInfo(commandMessage.getClientId().toString());
break;
}
return null;
}
-
Code INFO:
addInfoAboutUser() and clearUserinfo() are private methods in my class that manage the static list of connected clients.
-
NOTE: when a selector is changed from the flex client side the manage() method will be called twice: 1st to unsubscribe and 2nd to subscribe with the new selector.

You need to catch the event onbeforeunload and call a method on server which will cleanup all the client related data. Otherwise there is no way for the Flex client to automatically detect that it is unloaded.
The session should be destroyed when the maximum inactivity interval is exceeded...if the web.xml is properly configured.

Had the same problem as you ... solved it by a "hack" of BlazeDS ... I documented it on my confluence. Perhaps it helps with your problem:
http://dev.c-ware.de/confluence/display/PUBLIC/Litening+for+BlazeDS+client+logins+and+logouts

Related

Pubnub V4 Migration Callbacks

I am trying to update my Code from pubnub sdk v3 to v4 and I am stuck at callbacks.
I have following function which I would like to update:
void transmitMessage(String toID, JSONObject packet){
if (this.id==null){ .
mRtcListener.onDebug(new PnRTCMessage("Cannot transmit before calling Client.connect"));
}
try {
JSONObject message = new JSONObject();
message.put(PnRTCMessage.JSON_PACKET, packet);
message.put(PnRTCMessage.JSON_ID, "");
message.put(PnRTCMessage.JSON_NUMBER, this.id);
this.mPubNub.publish(toID, message, new Callback() {
#Override
public void successCallback(String channel, Object message, String timetoken) {
mRtcListener.onDebug(new PnRTCMessage((JSONObject)message));
}
#Override
public void errorCallback(String channel, PubNubError error) {
mRtcListener.onDebug(new PnRTCMessage(error.errorObject));
}
});
} catch (JSONException e){
e.printStackTrace();
}
}
The docs say one does not Need to instantiate com.pubnub.api.Callback and one should use the new SubscribeCallback class. I am not sure how to handle it, the SubscribeCallback contains These Methods: Status, message and presence, currently I have a successCallback method and a errorCallback.
The code at https://www.pubnub.com/docs/android-java/api-reference-publish-and-subscribe#listeners should help you with this.
You can create listeners using the code below:
pubnub.addListener(new SubscribeCallback() {
#Override
public void status(PubNub pubnub, PNStatus status) {
switch (status.getOperation()) {
// let's combine unsubscribe and subscribe handling for ease of use
case PNSubscribeOperation:
case PNUnsubscribeOperation:
// note: subscribe statuses never have traditional
// errors, they just have categories to represent the
// different issues or successes that occur as part of subscribe
switch (status.getCategory()) {
case PNConnectedCategory:
// this is expected for a subscribe, this means there is no error or issue whatsoever
case PNReconnectedCategory:
// this usually occurs if subscribe temporarily fails but reconnects. This means
// there was an error but there is no longer any issue
case PNDisconnectedCategory:
// this is the expected category for an unsubscribe. This means there
// was no error in unsubscribing from everything
case PNUnexpectedDisconnectCategory:
// this is usually an issue with the internet connection, this is an error, handle appropriately
case PNAccessDeniedCategory:
// this means that PAM does allow this client to subscribe to this
// channel and channel group configuration. This is another explicit error
default:
// More errors can be directly specified by creating explicit cases for other
// error categories of `PNStatusCategory` such as `PNTimeoutCategory` or `PNMalformedFilterExpressionCategory` or `PNDecryptionErrorCategory`
}
case PNHeartbeatOperation:
// heartbeat operations can in fact have errors, so it is important to check first for an error.
// For more information on how to configure heartbeat notifications through the status
// PNObjectEventListener callback, consult <link to the PNCONFIGURATION heartbeart config>
if (status.isError()) {
// There was an error with the heartbeat operation, handle here
} else {
// heartbeat operation was successful
}
default: {
// Encountered unknown status type
}
}
}
#Override
public void message(PubNub pubnub, PNMessageResult message) {
String messagePublisher = message.getPublisher();
System.out.println("Message publisher: " + messagePublisher);
System.out.println("Message Payload: " + message.getMessage());
System.out.println("Message Subscription: " + message.getSubscription());
System.out.println("Message Channel: " + message.getChannel());
System.out.println("Message timetoken: " + message.getTimetoken());
}
#Override
public void presence(PubNub pubnub, PNPresenceEventResult presence) {
}
});
Once you've subscribed to a channel like below, when a message or presence event is received the above listeners will be called.
pubnub.subscribe()
.channels(Arrays.asList("my_channel")) // subscribe to channels
.withPresence() // also subscribe to related presence information
.execute();
Please note that we have recently launched new features with new types of listeners as well, all of which are listed in the link above.

Junit and Spring 5 : All my classes should be multithreading tests expect one class, how can I achieve this? SSL handshake with concurrent

I'm working on a Spring 5 project and have some very special expectations with junit. Spring 5 now support junit multithreading and that definitely works very well, I'm now running my hundreds of tests into method parrallel multithreading. But I just setup recently my whole automatic mailing system which works like a charm but that's where it start to be problematic : I run a class that send all my mails to test them, and so they are being sent concurently. But as I just tried right now to test it with not only one email at a time but several, I get a strange SSL handshake error which I related to the fact that concurrent mail sending is not supported by most mail clients.
That's where goes my interrogation: how can I run all my test classes with parallel methods execution except for that email batch sending class?
Maybe I should think about a mail queue to avoid this kind of problem in live? Anyone has an idea?
By the way, in case you wonder I'm yet using gmail client to send mail as I didn't configured it yet for our live mail sending but it will be achieved using dedicated 1and1.fr smtp email client.
Thanks for your patience!
For those who feels interested about the solution, here is how I solved it:
I created a new Singleton class which would handle the queue :
public class EmailQueueHandler {
/** private Constructor */
private EmailQueueHandler() {}
/** Holder */
private static class EmailQueueHandlerHolder
{
/** unique instance non preinitialized */
private final static EmailQueueHandler INSTANCE = new EmailQueueHandler();
}
/** access point for unique instanciation of the singleton **/
public static EmailQueueHandler getInstance()
{
return EmailQueueHandlerHolder.INSTANCE;
}
private List<EmailPreparator> queue = new ArrayList<>();
public void queue(EmailPreparator email) {
waitForQueueHandlerToBeAvailable();
queue.add(email);
}
public List<EmailPreparator> getQueue()
{
waitForQueueHandlerToBeAvailable();
List<EmailPreparator> preparators = queue;
queue = new ArrayList<>();
return preparators;
}
// This method is used to make this handler thread safe
private synchronized void waitForQueueHandlerToBeAvailable(){}
}
I then created a CRON task using #Schedule annotation in my Scheduler bean in which I would correctly handle any mail sending fail.
#Scheduled(fixedRate = 30 * SECOND)
public void sendMailsInQueue()
{
List<EmailPreparator> queue = emailQueueHandler.getQueue();
int mailsSent = queue.size();
int mailsFailed = 0;
for(EmailPreparator preparator : queue)
{
try {
// And finally send the mail
emailSenderService.sendMail(preparator);
}
// If mail sending is not activated, mail sending function will throw an exception,
// Therefore we have to catch it and only throw it back if the email was really supposed to be sent
catch(Exception e)
{
mailsSent --;
// If we are not in test Env
if(!SpringConfiguration.isTestEnv())
{
mailsFailed ++;
preparator.getEmail().setTriggeredExceptionName(e.getMessage()).update();
// This will log the error into the database and eventually
// print it to the console if in LOCAL env
new Error()
.setTriggeredException(e)
.setErrorMessage(e.getClass().getName());
}
else if(SpringConfiguration.SEND_MAIL_ANYWAY_IN_TEST_ENV || preparator.isForceSend())
{
mailsFailed ++;
throw new EmailException(e);
}
}
}
log.info("CRON Task - " + mailsSent + " were successfuly sent ");
if(mailsFailed > 0)
log.warn("CRON Task - But " + mailsFailed + " could not be sent");
}
And then I called this mail queue emptyer methods at the end of each unit test in my #After annotated method to make sure it's called before I unit test the mail resulted. This way I'm aware of any mail sending fail even if it appear in PROD env and I'm also aware of any mail content creation failure when testing.
#After
public void downUp() throws Exception
{
proceedMailQueueManuallyIfNotAlreadySent();
logger.debug("After Test");
RequestHolder requestHolder = securityContextBuilder.getSecurityContextHolder().getRequestHolder();
// We check mails sending if some were sent
if(requestHolder.isExtResultsSent())
{
for(ExtResults results : requestHolder.getExtResults())
{
ExtSenderVerificationResolver resolver =
new ExtSenderVerificationResolver(
results,
notificationParserService
);
resolver.assertExtSending();
}
}
// Some code
}
protected void proceedMailQueueManuallyIfNotAlreadySent()
{
if(!mailQueueProceeded)
{
mailQueueProceeded = true;
scheduler.sendMailsInQueue();
}
}

How to add offline event handling in Openfire plugin?

I am java newbie . I need to handle offline sessions in Openfire plugin. From plugin development doc I am able to understand the basics , but I am not getting how can I handle offline event of openfire.
There is a class "SessionEventDispatcher" in package org.jivesoftware.openfire.event
There we have following predefined events:
session_created
session_destroyed
anonymous_session_created
anonymous_session_destroyed
resource_bound
These events then have listener , which is implemented in the Presence plugin.
Please help me understand how to proceed if I need to add offline event as well.
I am saving online users in redis but some how user sessions in openfire is less than users in redis, which means I am not handling some offline event in my plugin due to which user session is created and get added in redis , but user session is not closed explicitly which is handled to get remove from redis, and I face this discrepancy.
please correct me if I don`t understand your question clearly.
Do you examine interface org.jivesoftware.openfire.user.PresenceEventListener?
For example I implement that interface :
public class CustomPresenceEventListener implements PresenceEventListener {
private static final Logger LOG = LoggerFactory.getLogger(CustomPresenceEventListener.class);
#Override
public void availableSession(ClientSession session, Presence presence) {
LOG.info("\n\n=======SESSION AVAILABLE=========\n");
try {
LOG.info("USER : {}", session.getUsername());
} catch (UserNotFoundException e) {
LOG.info(e.getMessage(), e);
}
LOG.info("\n================\n\n");
}
#Override
public void unavailableSession(ClientSession session, Presence presence) {
LOG.info("\n\n=======SESSION UNAVAILABLE=========\n");
try {
LOG.info("USER : {}", session.getUsername());
} catch (UserNotFoundException e) {
LOG.info(e.getMessage(), e);
}
LOG.info("\n================\n\n");
}
#Override
public void presenceChanged(ClientSession session, Presence presence) {
}
#Override
public void subscribedToPresence(JID subscriberJID, JID authorizerJID) {
}
#Override
public void unsubscribedToPresence(JID unsubscriberJID, JID recipientJID) {
}
And add/remove this listener with :
PresenceEventDispatcher.addListener(presenceEventListener);
PresenceEventDispatcher.removeListener(presenceEventListener);
And when I connect to Openfire I will see in the file info.log something like that :
=======SESSION AVAILABLE=========
2017.03.29 14:27:01 .CustomPresenceEventListener - USER : 25
2017.03.29 14:27:01 .CustomPresenceEventListener -
And when I leave Openfire logs will be :
=======SESSION UNAVAILABLE=========
2017.03.29 14:27:34 .CustomPresenceEventListener - USER : 25
2017.03.29 14:27:34 .CustomPresenceEventListener -
So you could add any action you want when user enter/leave Openfire.

Apache Camel creating Consumer component

I'm newbie to Apache Camel. In hp nonstop there is a Receiver that receives events generated by event manager assume like a stream. My goal is to setup a consumer end point which receives the incoming message and process it through Camel.
Another end point I simply need to write it in logs. From my study I understood that for Consumer end point I need to create own component and configuration would be like
from("myComp:receive").to("log:net.javaforge.blog.camel?level=INFO")
Here is my code snippet which receives message from event system.
Receive receive = com.tandem.ext.guardian.Receive.getInstance();
byte[] maxMsg = new byte[500]; // holds largest possible request
short errorReturn = 0;
do { // read messages from $receive until last close
try {
countRead = receive.read(maxMsg, maxMsg.length);
String receivedMessage=new String(maxMsg, "UTF-8");
//Here I need to handover receivedMessage to camel
} catch (ReceiveNoOpeners ex) {
moreOpeners = false;
} catch(Exception e) {
moreOpeners = false;
}
} while (moreOpeners);
Can someone guide with some hints how to make this as a Consumer.
The 10'000 feet view is this:
You need to start out with implementing a component. The easiest way to get started is to extend org.apache.camel.impl.DefaultComponent. The only thing you have to do is override DefaultComponent::createEndpoint(..). Quite obviously what it does is create your endpoint.
So the next thing you need is to implement your endpoint. Extend org.apache.camel.impl.DefaultEndpoint for this. Override at the minimum DefaultEndpoint::createConsumer(Processor) to create your own consumer.
Last but not least you need to implement the consumer. Again, best ist to extend org.apache.camel.impl.DefaultConsumer. The consumer is where your code has to go that generates your messages. Through the constructor you receive a reference to your endpoint. Use the endpoint reference to create a new Exchange, populate it and send it on its way along the route. Something along the lines of
Exchange ex = endpoint.createExchange(ExchangePattern.InOnly);
setMyMessageHeaders(ex.getIn(), myMessagemetaData);
setMyMessageBody(ex.getIn(), myMessage);
getAsyncProcessor().process(ex, new AsyncCallback() {
#Override
public void done(boolean doneSync) {
LOG.debug("Mssage was processed " + (doneSync ? "synchronously" : "asynchronously"));
}
});
I recommend you pick a simple component (DirectComponent ?) as an example to follow.
Herewith adding my own consumer component may help someone.
public class MessageConsumer extends DefaultConsumer {
private final MessageEndpoint endpoint;
private boolean moreOpeners = true;
public MessageConsumer(MessageEndpoint endpoint, Processor processor) {
super(endpoint, processor);
this.endpoint = endpoint;
}
#Override
protected void doStart() throws Exception {
int countRead=0; // number of bytes read
do {
countRead++;
String msg = String.valueOf(countRead)+" "+System.currentTimeMillis();
Exchange ex = endpoint.createExchange(ExchangePattern.InOnly);
ex.getIn().setBody(msg);
getAsyncProcessor().process(ex, new AsyncCallback() {
#Override
public void done(boolean doneSync) {
log.info("Mssage was processed " + (doneSync ? "synchronously" : "asynchronously"));
}
});
// This is an echo server so echo request back to requester
} while (moreOpeners);
}
#Override
protected void doStop() throws Exception {
moreOpeners = false;
log.debug("Message processor is shutdown");
}
}

connection to a red5 remote shared object from a flex app

I'm writing a Flex application.
The server side application is Java application that is on top of Red5 1.0.
that connection is made throught RTMPE protcol. (the results are the same with RTMP or RTMPE).
I have am able to properly connect to the server and enter and leave rooms, but I am not able to connect to a shared object in a room. using a non-persistent shared object.
I create a shared object in a room in the server side using the following code:
private void createSharedObject (IScope scope, String soName, boolean persistent) {
ISharedObjectService service= (ISharedObjectService) ScopeUtils
.getScopeService(scope,
ISharedObjectService.class,
false);
service.createSharedObject(scope, soName, persistent);
}
private ISharedObject getSharedObject(IScope scope, String soName,boolean persistent) {
ISharedObjectService service = (ISharedObjectService) ScopeUtils
.getScopeService(scope,
ISharedObjectService.class,
false);
return service.getSharedObject(scope, soName,persistent);
}
public ISharedObject getRoomSharedObject() {
final String soName = "room_" + this._scope.getName();
log.debug("application found. creating shared object");
log.debug("shared object: {} not found for scope: {}. creating one",new Object[]{soName,this._scope.getContextPath()});
createSharedObject(this._scope, soName, XpoConstants.persistentSharedObjects);
ISharedObject so = getSharedObject(this._scope,soName, XpoConstants.persistentSharedObjects);
so.clear();
return so;
}
then the user enters the proper room I get the connction, and invoke a function at his end and provide the Shared Object name so he will know what to to connect to using the following code:
IConnection conn = Red5.getConnectionLocal();
IServiceCapableConnection sc = (IServiceCapableConnection) conn;
sc.invoke("<function name>", new Object[]{this._so.getName(),...});
now in the client side,
I use the following code in the flex application to get the remote shared object:
var roomSharedObject:SharedObject = SharedObject.getRemote(soName, SharedUtils.getNetConnection().uri, Finals.persistentSharedObject);
roomSharedObject.addEventListener(NetStatusEvent.NET_STATUS, this._parse);
roomSharedObject.addEventListener(SyncEvent.SYNC,this._parse2);
roomSharedObject.connect(SharedUtils.getNetConnection());
...
private function _parse(e:NetStatusEvent):void {
trace("NETSTATUSEVENT");
trace("########## EVENT INFO CODE: " + e.info.code);
}
private function _parse2(e:SyncEvent):void {
trace("SYNCEVENT");
}
the only message that I get from red5 is the following:
[INFO] [pool-8-thread-12] org.red5.server.so.SharedObject - Deleting shared object room_3963 because all clients disconnected and it is no longer acquired.
I would guess that NetStatusEvent should be triggered would some kind of error message
but I get no trace messages at all! and the only thing that I get from red5 is that the shared object is deleted which means that that client tried to connect for a sec and disconnected.
I tired to google, tired the red5 google group. i'm lost!
any information regarding the issue would be greatly appreciated.
thank you!
update
ok I created a smaller client and server..
at the server, the appConnect function just return true.
the client is a flex 4.6 client with the following code:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" creationComplete="init()"
minHeight="600">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private var nc:NetConnection = new NetConnection();
private function init():void {
nc.proxyType = "best";
nc.objectEncoding = ObjectEncoding.AMF0;
nc.addEventListener (NetStatusEvent.NET_STATUS,checkConnect);
nc.connect("rtmpe://127.0.0.1/ufktest");
}
private function checkConnect (event:NetStatusEvent):void
{
switch (event.info['code']) {
case 'NetConnection.Connect.Success':
{
_red5Connected();
trace('connected succesfully');
break;
}
case 'NetConnection.Connect.Closed':
{
_red5Disconnected();
trace('connection closed');
break;
}
case 'NetConnection.Connect.Rejected':
{
_red5Disconnected();
trace('connection rejected' );
break;
}
case 'NetConnection.Connect.Failed':
{
_red5Disconnected();
trace('connection failed');
break;
}
case 'NetConnection.Connect.AppShutDown':
{
trace('app shut down');
_red5Disconnected();
break;
}
default:
{
trace("unknown netconenction status: " + event.info['code']);
break;
}
}
}
private function _red5Connected():void {
trace("red5 connection was successful");
var roomSharedObject:SharedObject = SharedObject.getRemote("moshe",nc.uri,false);
roomSharedObject.addEventListener(NetStatusEvent.NET_STATUS, _parse);
roomSharedObject.addEventListener(SyncEvent.SYNC,_parse2);
roomSharedObject.connect(nc);
}
private function _parse(e:NetStatusEvent):void {
trace("NETSTATUSEVENT");
trace("########## EVENT INFO CODE: " + e.info.code);
}
private function _parse2(e:SyncEvent):void {
trace("SYNCEVENT");
}
private static function _red5Disconnected():void {
trace("disconnected from red5");
Alert.show("could not connect to server, please try again later");
}
]]>
</fx:Script>
and still when I run the client application it connects to the server and give me the following error:
[INFO] [pool-8-thread-15] org.red5.server.so.SharedObject - Deleting shared object moshe because all clients disconnected and it is no longer acquired.
Any more logs? However your log suggests that the shared object was created successfully on the server.Try using acquire() method on creation of the shared object inside roomStart() or wherever you are creating it(roomStart() should be the right place though). Server by default would destroy any shared object if no client is connected.
This seems like an overly complex way to do this. If you use the Flex method SharedObject::getRemote all of this will be taken care of for you. If the object exists the client will get a copy. If it doesn't exist the object will be created. So first person in 'the room' creates the object and anyone that shows up later will get a copy.

Categories

Resources