How to keep event handlers alive in Actionscript 3 - actionscript-3

I have a Flex app that connects to a JBoss/MS-SQL back-end. Some of our customers have a proxy server in front of their JBoss with a timeout of 90 seconds. In our application there are searches that can take up to 2-3 minutes for complex criteria. Since the proxy isn't smart enough to recognize AMF's keep alive pings for what they are the proxy sends a 503 to the client, which in Flex land becomes a "Channel Call Failed" event. In searching SO and other places, this seems to be a common problem. We can't do anything about the proxy or lengthen the timeout, the application needs to handle it.
Of course the back-end continues to process and eventually ships the results to the client. But the user gets an ugly error message and assumes the app is broke.
The solution I have settled on is to consume the CCF error and have the client continue to wait. I have managed the first part, but I can't figure out how to keep the client's handlers active to receive the data (and/or consume another timeout if necessary).
Current error handler:
private function handleSearchError(event : FaultEvent) : void {
if (event.fault.faultCode == "Channel.Call.Failed") {
event.stopImmediatePropagation(); // doesn't seem to help
return;
}
if (searchProgress != null) {
PopUpManager.removePopUp(searchProgress);
searchProgress = null;
}
etc...
}
This is the setup:
<mx:Button id="btnSearch" label="
{resourceManager.getString('recon_perspective',
'ReconPerspective.ReconView.search')}" icon="{iconSearch}"
click="handleSearch()" includeIn="search, default"/>
And:
<mx:method name="search" result="event.token.resultHandler(event);"
fault="handleSearchError(event);"/>
Kicking off the call:
var token : AsyncToken = null;
token = sMSrv.search(searchType.toString(), getSearchMode(), criteria,
smartMatchParent.isArchiveMode);
searchProgress = LoadProgress(PopUpManager.createPopUp
(FlexGlobals.topLevelApplication as DisplayObject, LoadProgress, true));
searchProgress.title = resourceManager.getString('matching', 'smartmatch.loading.trans');
searchProgress.token = token;
searchProgress.showCancelButton = true;
PopUpManager.centerPopUp(searchProgress);
token.resultHandler = handleSearchResults;
token.cancelSearch = false;
So my question is how do I keep handleSearch and handleSearchError alive to consume the events from the server?
I verified that the data comes back from the server using WebDeveloper in the browser to watch the network traffic and if you cause the app to refresh that screen, the data gets displayed.

I'm very in experienced but would this help?
private function handleSearchError(event : FaultEvent) : void {
if (event.fault.faultCode == "Channel.Call.Failed") {
event.stopImmediatePropagation(); // doesn't seem to help
if(event.isImmediatePropagationStopped(true)) {
//After stopped do something here?
}
return;
}
if (searchProgress != null) {
PopUpManager.removePopUp(searchProgress);
searchProgress = null;
}
etc...
}

Related

My javax.jms.MessageListener stops receiving messages from IBM MQ Server

I am building a service that continuously consumes messages from a IBM MQ Queue. This service runs in a private cloud with 80 replicas (containers or PODs) listening to the queue. Observing the application logs, I can see about 20 PODs that are not consuming any messages, even though I can check the queue PUT metric counter which shows about 200 req/s (rush hour). Most of the messages stay in the queue for a while (20 seconds) and get expired.
During the non rush hour, the application receives about 40 req/s and can process all the messages without any being expired.
I wonder if there is a limit of active listeners that the IBM MQ Server support? If so, is there any way to increase this limit?
I create a JMSContext using this method:
public JMSContext buildContext() {
if (this.context == null) {
try {
JmsConnectionFactory connection = JmsFactoryFactory.getInstance(JmsConstants.WMQ_PROVIDER).createConnectionFactory();
String hostname = System.getenv("HOSTNAME");
String appName = application.get();
if (hostname != null && !hostname.trim().isEmpty()) {
if (hostname.length() > 28) { // Tamanho máximo da propriedade WMQ_APPLICATIONNAME
appName = hostname.substring(hostname.length() - 28);
} else {
appName = hostname;
}
}
connection.setStringProperty(CommonConstants.WMQ_HOST_NAME, host.get());
connection.setIntProperty(CommonConstants.WMQ_PORT, port.get());
connection.setStringProperty(CommonConstants.WMQ_CHANNEL, channel.get());
connection.setStringProperty(CommonConstants.WMQ_QUEUE_MANAGER, queueManager);
connection.setStringProperty(JmsConstants.USERID, userid);
connection.setStringProperty(JmsConstants.PASSWORD, password);
connection.setBooleanProperty(JmsConstants.USER_AUTHENTICATION_MQCSP, isUserAuthenticationMqcsp);
connection.setIntProperty(CommonConstants.WMQ_CONNECTION_MODE, CommonConstants.WMQ_CM_CLIENT);
connection.setStringProperty(CommonConstants.WMQ_APPLICATIONNAME, appName);
connection.setIntProperty(CommonConstants.WMQ_CLIENT_RECONNECT_OPTIONS, CommonConstants.WMQ_CLIENT_RECONNECT);
LOGGER.info(String.format("buildContext - Parametros da fila a alta [%s],[%s],[%s],[%s],[%s]",
host.get(), channel.get(), application.get(), queueManager, userid));
this.context = connection.createContext();
this.context.start();
this.connectionFactory = connection;
} catch (JMSException e) {
LOGGER.error(String.format("buildContext - error - code: [%s], message: [%s].", e.getErrorCode(), e.getMessage()));
}
}
return this.context;
}
I also configure my message listener using a separate thread as following:
#Override
public void run() {
JMSContext context = factory.buildContext();
this.creditoQueueConsumer = context.createConsumer(context.createQueue(Constantes.PREFIXO_QUEUE.concat(creditoQueue)));
this.creditoQueueConsumer.setMessageListener(mqAltaPlataformaCreditoListener);
// mqAltaPlataformaCreditoListener is an implementation of MessageListener
context.start();
}
The application seems to work fine, however there is a problem when some listeners suddenly stop receiving messages.
I assume the MQ server works as a proxy and then distributes equally messages to all configured listeners, but for some reason, some of the listeners stop receiving messages. The MQ server team mentioned that some connection timeouts are occurring in the server side, as shown in the following picture.
Is there any configuration that can be done in the client to avoid those timeouts ou to avoid the service to stop receiving messages?

MassTransit servicebuse optimize consumer

I am trying to use MassTransit for Request/Response communication through Azure service bus queue. Sender is an Azure WebApp, Consumer is a windows service installed at on-premise machine.
Everything works fine when it is about small volumes of messages. However as soon as I start sending more than ~20 msg/sec i see severe(1-2 sec) delays in responses from consumer. My telemetry tells me that delay is happening at point when consumer needs to grab messages from queue.
One strange, but I think important part of behavior: I can see that with current load amount of unread messages in queue is on avg constant and its 25. If I send 2x more messages, than I see on avg 50messages in queue. With delays on consumption side i would expect queue to GROW, but it is constant, so it is definitely something inside code that throttles the connection.
Quick info:
There are no problems with hardware on the machine. CPU/Mem not high.
I tried playing with the UseConcurrencyLimit, MaxConcurrentCalls, PrefetchCount configs on consuner side. It did not help
My solution code of sender and consumer are next to classic examples.
Consumer: .Net framework 4.7.2 and MassTransit.Azure.ServiceBus.Core 5.5.2
Here's my listener class with all business logic removed:
public class QueueListener
{
private IBusControl Bus { get; set; }
public QueueListener()
{
Bus = MassTransit.Bus.Factory.CreateUsingAzureServiceBus(serviceBusFactoryConfigurator =>
{
var host = serviceBusFactoryConfigurator.Host(SettingsHelper.AzureServiceBusConnectionString,
(config) =>
{
config.OperationTimeout = TimeSpan.FromSeconds(60);
config.TransportType = TransportType.AmqpWebSockets;
});
serviceBusFactoryConfigurator.ReceiveEndpoint(host, SettingsHelper.CouponQueryQueueName, e =>
{
e.Handler<JToken>(HandleMessage);
e.UseConcurrencyLimit(16);
e.MaxConcurrentCalls = 16;
e.PrefetchCount = 32;
});
serviceBusFactoryConfigurator.EnableBatchedOperations = true;
serviceBusFactoryConfigurator.DefaultMessageTimeToLive = TimeSpan.FromSeconds(60);
});
}
private async Task HandleMessage(ConsumeContext context)
{
await Task.Delay(800);
if (context.ExpirationTime > SystemDateTime.Now)
{
await context.RespondAsync(new CouponUsedList { CouponsUsed = new List<CouponCurrentUsed>() });
}
}
public Task LaunchAsync()
{
return Bus.StartAsync();
}
public Task StopAsync()
{
return Bus.StopAsync();
}
}
Seems that here, once again, it was just missing one config. All code that you write inside ReceiveEndpoint configurates the consumer listener queue and configurations that you provide in CreateUsingAzureServiceBus are configurations for a consumer response queue.
All I needed was to add one line inside consumer configuration. Without this config all prefetched messages are handled gradually
e.EnableBatchedOperations = true;

AMS doesn't receive unpublish command SOMETIMES over rtmpt

This one has had me going for a week at least. I am trying to record a video file to AMS. It works great almost all of the time, except about 1 in 10 or 15 recording sessions, I never receive 'NetStream.Unpublish.Success' on my netstream from AMS when I close the stream. I am connecting to AMS using rtmpt when this happens, it seems to work fine over rtmp. Also, it seems like this only happens in safari on mac, but since its so intermittent I don't really trust that. Here is my basic flow:
// just a way to use promises with netStatusEvents
private function netListener(code:String, netObject:*):Promise {
var deferred:Deferred = new Deferred();
var netStatusHandler:Function = function (event:NetStatusEvent):void {
if (event.info.level == 'error') {
deferred.reject(event);
} else if (event.info.code == code) {
deferred.resolve(netObject);
// we want this to be a one time listener since the connection can swap between record/playback
netObject.removeEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
}
};
netObject.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
return deferred.promise;
}
// set up for recording
private function initRecord():void {
Settings.recordFile = Settings.uniquePrefix + (new Date()).getTime();
// detach any existing NetStream from the video
_view.video.attachNetStream(null);
// dispose of existing NetStream
if (_videoStream) {
_videoStream.dispose();
_videoStream = null;
}
// disconnect before connecting anew
(_nc.connected ? netListener('NetConnection.Connect.Closed', _nc) : Promise.when(_nc))
.then(function (nc:NetConnection):void {
netListener('NetConnection.Connect.Success', _nc)
.then(function (nc:NetConnection):void {
_view.video.attachCamera(_webcam);
// get new NetStream
_videoStream = getNetStream(_nc);
ExternalInterface.call("CTplayer." + Settings.instanceName + ".onRecordReady", true);
}, function(error:NetStatusEvent):void {
ExternalInterface.call("CTplayer." + Settings.instanceName + ".onError", error.info);
});
_nc.connect(Settings.recordServer);
}); // end ncClose
if (_nc.connected) _nc.close();
}
// stop recording
private function stop():void {
netListener('NetStream.Unpublish.Success', _videoStream)
.then(function (ns:NetStream):void {
ExternalInterface.call("CTplayer." + Settings.instanceName + ".onRecordStop", Settings.recordFile);
});
_videoStream.attachCamera(null);
_videoStream.attachAudio(null);
_videoStream.close();
}
// start recording
private function record():void {
netListener('NetStream.Publish.Start', _videoStream)
.then(function (ns:NetStream):void {
ExternalInterface.call("CTplayer." + Settings.instanceName + ".onRecording");
});
_videoStream.attachCamera(_webcam);
_videoStream.attachAudio(_microphone);
_videoStream.publish(Settings.recordFile, "record"); // fires NetStream.Publish.Success
}
Update
I am now using a new NetConnection per connection attempt and also not forcing port 80 (see my 'answer' below). This has not solved my connection woes, only made the instances more infrequent. Now like every week or so I still have some random failure of ams or flash. Most recently someone made a recording and then flash player was unable to load the video for playback. The ams logs show a connection attempt and then nothing. There should at least be a play event logged for when i load the metadata. This is quite frustrating and impossible to debug.
I would try 2 distinct NetConnection objects, one for record and one for replay. This will remove your complexities around listeners adding/removing and connect/reconnect/disconnect logic and would IMO be cleaner.
NetConnections are cheap, and I've always used one per task at hand. The other advantage is that you can connect both at startup so the replay connection is ready instantly.
I've not seen a Promise used here before, but I'm not qualified to comment if that may cause a problem or not.
I think my issue was connecting over port 80. I originally thought I had to use port 80 with rtmpt, so I set my Settings.recordServer variable to rtmpt://myamsserver.net:80/app. I'm now using a shotgun approach where I try a bunch of port/protocol combos at once and pick the first one to connect. It is almost always picking port 443 over rtmpt, which seems much faster and more stable all around than 80, and I haven't had this issue since. It could also be due to not reusing the same NetConnection object like Stefan suggested, its hard to say.

HttpClient timeouts before the timeout specified

I'm using HttpClient from WP8 and do a Post request. I know the call may take long time as I'm actually simulating slow network scenarios. Therefore I set the HttpClient.Timeout accordingly to 5 minutes.
However, I get a Timeout at around 60s. I believe the Timeout is not working.
I believe there is an issue with this for WP as stated in this question:
HttpClient Portable returns 404 notfound on WP8.
They use a workaround but that does not applies to my scenario. I do actually want to wait for long time.
My questions:
1) Is it a bug/issue of HttpClient for WP8 or I'm not setting it properly?
2) Do you think of a workaround still using HttpClient?
I've read that maybe HttpWebRequest is an option. However, I believe HttpClient should be ideal for this 'simple' scenario.
My code is simple:
private static async Task<HttpResponseMessage> PostAsync(Uri serverUri, HttpContent httpContent)
{
var client = new HttpClient();
client.Timeout = TimeSpan.FromMinutes(5);
return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);
}
The server receives the request and while is processing it, the client aborts.
UPDATE: The HttpResponseMessage returned by HttpClient.PostAsyn is this "{StatusCode: 404, ReasonPhrase: '', Version: 0.0, Content: System.Net.Http.StreamContent, Headers: { Content-Length: 0 }}". As I said, the server is found and is receiving the data and processing it.
After some search and some tests I've came to the conclusion that the problem is Windows Phone itself and that it has a 60 seconds timeout (irrespective of the HttpClient) and that cannot be changed to my knowledge. See http://social.msdn.microsoft.com/Forums/en-US/faf00a04-8a2e-4a64-b1c1-74c52cf685d3/httpwebrequest-60-seconds-timeout.
As I'm programming the server as well, I will try the advice by Darin Rousseau in the link provided above, specifically to send an OK and then do some more processing.
UPDATE: The problem seems to be the Windows Phone emulator as stated here:
http://social.msdn.microsoft.com/forums/wpapps/en-us/6c114ae9-4dc1-4e1f-afb2-a6b9004bf0c6/httpclient-doesnt-work-on-windows-phone?forum=wpdevelop. In my experience the tcp connection times-out if it doesn't hear anything for 60s.
Therefore my solution is to use the Http header characters as a way of keep alive. The first line Http header response always starts with HTTP/1.0. So I send the characters one by one with a delay <60s between them. Of course, if the response gets ready, everything that is left is sent right away. This buys some time, for instance if using a delay of 50s per 9 character we get about 450s.
This is a project for my degree so I wouldn't recommend it for production.
By the way, I also tried with other characters instead the sub string of the header, for instance space character, but that results in a http protocol violation.
This is the main part of the code:
private const string Header1 = #"HTTP/1.0 ";
private int _keepAliveCounter = 0;
private readonly object _sendingLock = new object();
private bool _keepAliveDone = true;
private void StartKeepAlive()
{
Task.Run(() => KeepAlive());
}
/// <summary>
/// Keeps the connection alive sending the first characters of the http response with an interval.
/// This is a hack for Windows Phone 8 that need reponses within 60s interval.
/// </summary>
private void KeepAlive()
{
try
{
_keepAliveDone = false;
_keepAliveCounter = 0;
while (!_keepAliveDone && _keepAliveCounter < Header1.Length)
{
Task.Delay(TimeSpan.FromSeconds(50)).Wait();
lock (_sendingLock)
{
if (!_keepAliveDone)
{
var sw = new StreamWriter(OutputStream);
sw.Write(Header1[_keepAliveCounter]);
Console.Out.WriteLine("Wrote keep alive char '{0}'", Header1[_keepAliveCounter]);
_keepAliveCounter++;
sw.Flush();
}
}
}
_keepAliveCounter = 0;
_keepAliveDone = true;
}
catch (Exception e)
{
// log the exception
Console.Out.WriteLine("Error while sending keepalive: " + e.Message);
}
}
Then, the actual processing happens in a different thread.
Comments and critics are appreciated.
It is possible that you are hitting the timeout of the network stream. You can change this by doing,
var handler = new WebRequestHandler();
handler.ReadWriteTimeout= 5 * 60 * 1000;
var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromMinutes(5);
return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);
The default on the desktop OS is already 5mins. However, it is possible that on Windows Phone it has been reduced by default.

Chromecast Launch, what's with the DIAL parameters? Aren't I runing MY application, not some registered standard?

The following code displays a proper list of available chromecast devices on my network. But when I click on the links, the application never launches. There are a couple of things that I'm quite confused about that may or may not be related to this question:
If I'm making my own custom application, what's with the DIAL parameters and why do I have to pass them? I don't want to write an app for the DIAL standard... this is MY app.
Again related to the DIAL parameters, if I search for devices with any other query other than "YouTube" (a DIAL parameter), the list always comes up blank. I suppose I shouldn't care, as long as the device is listed... but again... the app won't launch.
It should be noted that my sender app is a chrome webpage.
I'm a bit confused as to where my "appid" goes int he launch parameters,'
<html data-cast-api-enabled="true">
<body>
hi!<BR/>
<script>
var cast_api, cv_activity;
if (window.cast && window.cast.isAvailable) {
// Cast is known to be available
initializeApi();
} else {
// Wait for API to post a message to us
window.addEventListener("message", function(event) {
if (event.source == window && event.data &&
event.data.source == "CastApi" &&
event.data.event == "Hello")
{
//document.write("Initialize via message.<br/>");
initializeApi();
//document.write("Api initialized via message.");
};
});
};
initializeApi = function() {
cast_api = new cast.Api();
cast_api.addReceiverListener("YouTube", onReceiverList);
};
var g_list;
onReceiverList = function(list) {
g_list = list;
// If the list is non-empty, show a widget with
// the friendly names of receivers.
// When a receiver is picked, invoke doLaunch with the receiver.
document.write("Receivers: "+list.length+"<br/>");
var t;
for(t=0;t<list.length;t++)
document.write('found:'+list[t].name+' ' +list[t].id+'<br/>');
};
onLaunch = function(activity) {
if (activity.status == "running") {
cv_activity = activity;
// update UI to reflect that the receiver has received the
// launch command and should start video playback.
} else if (activity.status == "error") {
cv_activity = null;
}
};
function launchy(idx)
{
doLaunch(g_list[idx]);
}
doLaunch = function(receiver) {
var request = new window.cast.LaunchRequest(">>>>>what REALLY goes here?<<<<<<< ", receiver);
request.parameters = "v=abcdefg";
request.description = new window.cast.LaunchDescription();
request.description.text = "My Cat Video";
request.description.url = "http://my.website.get.your.own/chomecast/test.php";
cast_api.launch(request, onLaunch);
};
stopPlayback = function() {
if (cv_activity) {
cast_api.stopActivity(cv_activity.activityId);
}
};
</script>
</body>
</html>
The part marked "what really goes here?" is the part that I THINK is wrong... I couldn't be completely wrong. My device is white listed, I have an appid (which I thought might go in that slot)... The documentation merely says ActivityType DIAL Parmeters are valid, mandatory.
The first argument to the LaunchRequest is your App ID, the one that you have received in an email as part of whitelisting process. Also, the "YouTube" in the initialize method should also be replaced with the same App ID.
I strongly suggest you look at the sample that is on GitHub for chrome sender to see how you can send a request to load a media on a cast device.