libPusher pod issues - Disconnection during channel subscription - rubymotion

I'm using the libPusher pod in a Ruby Motion project but running into an issue where my code works when used in the REPL but not in the app itself.
When I try this code in a viewDidAppear method it connects successfully and then disconnects during the channel subscription call.
When I try it in the console, it connects and subscribes perfectly. (same code)
I'm trying to figure out:
Why this is happening
What should I change to alleviate the issue?
I'm using v 1.5 of the pod v2.31 of Ruby Motion
For reference, I'm also using ProMotion framework but I doubt that has anything to do with the issue.
Here's my code:
client = PTPusher.pusherWithKey("my_pusher_key_here", delegate:self, encrypted:true)
client.connect
channel = client.subscribeToChannelNamed("test_channel_1")
channel.bindToEventNamed('status', target: self, action: 'test_method:')

Well I got it working by separating the connection and subscription calls into separate lifecycle methods.
I put:
client = PTPusher.pusherWithKey("my_pusher_key_here", delegate:self, encrypted:true)
client.connect
into the viewDidLoad method
and:
channel = client.subscribeToChannelNamed("test_channel_1")
channel.bindToEventNamed('status', target: self, action: 'test_method:')
into the viewDidAppear method.
I can't say I know exactly why this worked but I assume it has to do with the time between the calls. The connection process must need a little time to complete.

Related

Google App Engine deferred.defer task not getting executed

I have a Google App Engine Standard Environment application that has been working fine for a year or more, that, quite suddenly, refuses to enqueue new deferred tasks using deferred.defer.
Here's the Python 2.7 code that is making the deferred call:
# Find any inventory items that reference the product, and change them too.
# because this could take some time, we'll do it as a deferred task, and only
# if needed.
if upd:
updater = deferredtasks.InvUpdate()
deferred.defer(updater.run, product_key)
My app.yaml file has the necessary bits to support deferred.defer:
- url: /_ah/queue/deferred
script: google.appengine.ext.deferred.deferred.application
login: admin
builtins:
- deferred: on
And my deferred task has logging in it so I should see it running when it does:
#-------------------------------------------------------------------------------
# DEFERRED routine that updates the inventory items for a particular product. Should be callecd
# when ANY changes are made to the product, because it should trigger a re-download of the
# inventory record for that product to the iPad.
#-------------------------------------------------------------------------------
class InvUpdate(object):
def __init__(self):
self.to_put = []
self.product_key = None
self.updcount = 0
def run(self, product_key, batch_size=100):
updproduct = product_key.get()
if not updproduct:
logging.error("DEFERRED ERROR: Product key passed in does not exist")
return
logging.info(u"DEFERRED BEGIN: beginning inventory update for: {}".format(updproduct.name))
self.product_key = product_key
self._continue(None, batch_size)
...
When I run this in the development environment on my development box, everything works fine. Once I deploy it to the App Engine server, the inventory updates never get done (i.e. the deferred task is not executed), and there are no errors (and no other logging from the deferred task in fact) in the log files on the server. I know that with the sudden move to get everybody on Python 3 as quickly as possible, the deferred.defer library has been marked as not recommended because it only works with the 2.7 Python environment, and I planned on moving to task queues for this, but I wasn't expecting deferred.defer to suddenly stop working in the existing python environment.
Any insight would be greatly appreciated!
I'm pretty sure you cant pass the method of an instance to appengine taskqueue, because that instance will not get exist when your task runs since it will be running in a different process. I actually dont understand how your task ever worked when running remotely in the first place (and running locally is not an accurate representation of how things will run remotely)
Try changing your code to this:
if upd:
deferred.defer(deferredtasks.InvUpdate.run_cls, product_key)
and then InvUpdate is the same but has a new function run_cls:
class InvUpdate(object):
#classmethod
def run_cls(cls, product_key):
cls().run(product_key)
And I'm still on the process of migrating to cloud tasks and my deferred tasks still work

Widevine Session Update endless Loop

I am using libwidevinecdm.so from chrome to handle DRM protected data. I am currently successfully setting the widevine server certificate I get from the license server. I can also create a session with the pssh box of the media im trying to decode. So far everything is successful (all promises resolve fine).
(session is created like this: _cdm->CreateSessionAndGenerateRequest(promise_id, cdm::SessionType::kTemporary, cdm::InitDataType::kCenc, pssh_box.data(), static_cast<uint32_t>(pssh_box.size()));)
I am then getting a session message of type kLicenseRequest which I am forwarding to the respective license server. The license server responds with a valid response and the same amount of data as I can see in the browser when using Chrome. I am then passing this to my session like this:
_cdm->UpdateSession(promise_id, session_id.data(), static_cast<uint32_t>(session_id.size()),
license_response.data(), static_cast<uint32_t>(license_response.size()));
The problem now is that this promise never resolves. It keeps posting the kLicenseRequest message over and over again to my session without ever returning. Does this mean my response is wrong? Or is this something else?
Br
Yanick
The issue is caused by the fact, that everything in CreateSessionAndGenerateRequest is done synchronous - that means by the time CreateSessionAndGenerateRequest returns your promise will always be resolved.
The CDM will emit the kLicenseRequest inside CreateSessionAndGenerateRequest and it doesn't do so in a "fire & forget" fashion, but the function waits there until you have returned from the cdm::Host_10::OnSessionMessage. Since my implementation of OnSessionMessage was creating a synchronous HTTP Request to the license server before - also synchronously - calling the UpdateSession the entire chain ended up to be blocking.
So ultimately I was calling UpdateSession while still being inside CreateSessionAndGenerateRequest and I assume the CDM cannot handle this and reacts by creating a new session with the given ID and generating a request again, which of course triggered another UpdateSession and so on.
Ultimately the simplest way to break the cycle was to make something asynchronous. I decided to launch a separate thread when receiving kLicenseRequest, wait for a few milliseconds to make sure that CreateSessionAndGenerateRequest has time to finish (not sure if that is really required) and then issue the request to the license server.
The only change I had to do was adding the surrounding std::thread:
void WidevineSession::forward_license_request(const std::vector<uint8_t> &data) {
std::thread{
[=]() {
std::this_thread::sleep_for(std::chrono::milliseconds{100});
net::HttpRequest request{"POST", _license_server_url};
request.add_header("Authorization", fmt::format("Bearer {}", _access_token))
.byte_body(data);
const auto response = _client.execute(request);
if (response.status_code() != 200) {
log->error("Widevine license request not accepted by license server: {} {} ({})", response.status_code(), response.status_text(), utils::bytes_to_utf8(response.body()));
throw std::runtime_error{"Error requesting widevine license"};
}
log->info("Successfully requested widevine license from license server");
_adapter->update_session(this, _session_id, response.body());
}
}.detach();
}

Multipeer Connectivity Not Connecting Programmatically

I am creating an iOS/macOS app that uses remote control functionality via the Multipeer Connectivity Framework. Since the device to be remotely monitored and controlled will run over an extended period of time, it's not viable to use the automatic view controller methods since the monitoring device may be locked or go to sleep and then disconnect the connection. So I'm using the programatic approach so that when the monitoring devices lose connection, they will automatically pair up when they are unlocked/woken up and the app is started again. My connection works fine using the ViewController method but not the programatic delegate approach. The advertising, browsing and inviting works fine, but when the invitation is accepted on the remote side I get several errors and then a failed connection. What's weird is that several of the errors are GCKSession errors.
So why is it trying to use the GameCenter framework? And why is it failing after accepting the invitation? Could it just be a bug in the Xcode 8 / Swift 3 /iOS 10 / macOS Sierra Beta SDKs?
[ViceroyTrace] [ICE][ERROR] ICEStopConnectivityCheck() found no ICE check with call id (2008493930)
[GCKSession] Wrong connection data. Participant ID from remote connection data = 6FBBAE66, local participant ID = 3A4C626C
[MCSession] GCKSessionEstablishConnection failed (FFFFFFFF801A0020)
Peer Changing
Failed
[GCKSession] Not in connected state, so giving up for participant [77B72F6A] on channel [0]
Here is the code from my connection class
func startAdvertisingWithoutUI () {
if advertiserService == nil {
advertiserService = MCNearbyServiceAdvertiser (peer: LMConnectivity.peerID, discoveryInfo: nil, serviceType: "mlm-timers")
advertiserService?.delegate = self
session.delegate = self
}
advertiserService?.startAdvertisingPeer()
}
func browserForNearbyDevices () {
if browserService == nil {
browserService = MCNearbyServiceBrowser (peer: LMConnectivity.peerID, serviceType: "mlm-timers")
browserService?.delegate = self
session.delegate = self
}
browserService?.startBrowsingForPeers()
}
func sendInvitation(to peer: MCPeerID) {
browserService?.invitePeer(peer, to: session, withContext: nil, timeout: 60)
}
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: (Bool, MCSession?) -> Void) {
let trustedNames = GetPreferences.trustedRemoteDevices
for name in trustedNames {
if name == peerID.displayName {
invitationHandler(true,session)
return
}
}
invitationHandler (false, session)
}
None has worked for me.
I've resolved only disabling encryption...
let session = MCSession(peer:myPeerId, securityIdentity: nil, encryptionPreference: MCEncryptionPreference.none)
When the peerID used to make the session and the peerID used to make the advertiser or browser do not match, I get this part of the error.
[GCKSession] Wrong connection data. Participant ID from remote connection data = 6FBBAE66, local participant ID = 3A4C626C
Once peerIDs match, that part of the error goes away.
There might still be some other connection problems though.
I found out what was wrong. The MCPeerID object that I was passing into the MCSession instances, I was vending it as a Computed Class Property instead of storing it as a Stored Property. So I changed it to a Stored Instance Property and everything started working! Thanks Tanya for pointing me in the direction of the MCPeerID object.
Old Code
// Class Properties
static var localPeer : MCPeerID { return MCPeerID(displayName: GetPreferences.deviceName!) }
New Code
// Instance Properties
let localPeer = MCPeerID (displayName: GetPreferences.deviceName!)
The problem for me was that I never set the delegate of MCSession. I got all the same error messages that the OP mentioned, which made me think the connection was broken, but really I just forgot to set the delegate. After setting the delegate, all the error messages still printed, but otherwise my delegate methods got called normally upon receiving a message!
I've inflicted this problem on myself twice. Hopefully this helps someone reading along!
I got to work with TViOS 10.0 beta with this ...
peerID = MCPeerID(displayName: UIDevice.current.name)
Although I am still seeing this error...
2016-09-08 10:13:43.016600 PeerCodeATV[208:51135] [ViceroyTrace] [ICE][ERROR] ICEStopConnectivityCheck() found no ICE check with call id (847172408)
2016-09-08 10:13:47.577645 PeerCodeATV[208:51155] [GCKSession] SSLHandshake returned with error [-9819].
Same problem for me with an app I've had on the itunes store for years.
The latest 10.1 beta update now seems to fix the bluetooth issue with my app without any change to my code.

Can Amazon Simple Workflow (SWF) be made to work with jRuby?

For uninteresting reasons, I have to use jRuby on a particular project where we also want to use Amazon Simple Workflow (SWF). I don't have a choice in the jRuby department, so please don't say "use MRI".
The first problem I ran into is that jRuby doesn't support forking and SWF activity workers love to fork. After hacking through the SWF ruby libraries, I was able to figure out how to attach a logger and also figure out how to prevent forking, which was tremendously helpful:
AWS::Flow::ActivityWorker.new(
swf.client, domain,"my_tasklist", MyActivities
) do |options|
options.logger= Logger.new("logs/swf_logger.log")
options.use_forking = false
end
This prevented forking, but now I'm hitting more exceptions deep in the SWF source code having to do with Fibers and the context not existing:
Error in the poller, exception:
AWS::Flow::Core::NoContextException: AWS::Flow::Core::NoContextException stacktrace:
"aws-flow-2.4.0/lib/aws/flow/implementation.rb:38:in 'task'",
"aws-flow-2.4.0/lib/aws/decider/task_poller.rb:292:in 'respond_activity_task_failed'",
"aws-flow-2.4.0/lib/aws/decider/task_poller.rb:204:in 'respond_activity_task_failed_with_retry'",
"aws-flow-2.4.0/lib/aws/decider/task_poller.rb:335:in 'process_single_task'",
"aws-flow-2.4.0/lib/aws/decider/task_poller.rb:388:in 'poll_and_process_single_task'",
"aws-flow-2.4.0/lib/aws/decider/worker.rb:447:in 'run_once'",
"aws-flow-2.4.0/lib/aws/decider/worker.rb:419:in 'start'",
"org/jruby/RubyKernel.java:1501:in `loop'",
"aws-flow-2.4.0/lib/aws/decider/worker.rb:417:in 'start'",
"/Users/trcull/dev/etl/flow/etl_runner.rb:28:in 'start_workers'"
This is the SWF code at that line:
# #param [Future] future
# Unused; defaults to **nil**.
#
# #param block
# The block of code to be executed when the task is run.
#
# #raise [NoContextException]
# If the current fiber does not respond to `Fiber.__context__`.
#
# #return [Future]
# The tasks result, which is a {Future}.
#
def task(future = nil, &block)
fiber = ::Fiber.current
raise NoContextException unless fiber.respond_to? :__context__
context = fiber.__context__
t = Task.new(nil, &block)
task_context = TaskContext.new(:parent => context.get_closest_containing_scope, :task => t)
context << t
t.result
end
I fear this is another flavor of the same forking problem and also fear that I'm facing a long road of slogging through SWF source code and working around problems until I finally hit a wall I can't work around.
So, my question is, has anyone actually gotten jRuby and SWF to work together? If so, is there a list of steps and workarounds somewhere I can be pointed to? Googling for "SWF and jRuby" hasn't turned up anything so far and I'm already 1 1/2 days into this task.
I think the issue might be that aws-flow-ruby doesn't support Ruby 2.0. I found this PDF dated Jan 22, 2015.
1.2.1
Tested Ruby Runtimes The AWS Flow Framework for Ruby has been tested
with the official Ruby 1.9 runtime, also known as YARV. Other versions
of the Ruby runtime may work, but are unsupported.
I have a partial answer to my own question. The answer to "Can SWF be made to work on jRuby" is "Yes...ish."
I was, indeed, able to get a workflow working end-to-end (and even make calls to a database via JDBC, the original reason I had to do this). So, that's the "yes" part of the answer. Yes, SWF can be made to work on jRuby.
Here's the "ish" part of the answer.
The stack trace I posted above is the result of SWF trying to raise an ActivityTaskFailedException due to a problem in some of my activity code. That part is my fault. What's not my fault is that the superclass of ActivityTaskFailedException has this code in it:
def initialize(reason = "Something went wrong in Flow",
details = "But this indicates that it got corrupted getting out")
super(reason)
#reason = reason
#details = details
details = details.message if details.is_a? Exception
self.set_backtrace(details)
end
When your activity throws an exception, the "details" variable you see above is filled with a String. MRI is perfectly happy to take a String as an argument to set_backtrace(), but jRuby is not, and jRuby throws an exception saying that "details" must be an Array of Strings. This exception blows through all the nice error catching logic of the SWF library and into this code that's trying to do incompatible things with the Fiber library. That code then throws a follow-on exception and kills the activity worker thread entirely.
So, you can run SWF on jRuby as long as your activity and workflow code never, ever throws exceptions because otherwise those exceptions will kill your worker threads (which is not the intended behavior of SWF workers). What they are designed to do instead is communicate the exception back to SWF in a nice, trackable, recoverable fashion. But, the SWF code that does the communicating back to SWF has, itself, code that's incompatible with jRuby.
To get past this problem, I monkey-patched AWS::Flow::FlowException like so:
def initialize(reason = "Something went wrong in Flow",
details = "But this indicates that it got corrupted getting out")
super(reason)
#reason = reason
#details = details
details = details.message if details.is_a? Exception
details = [details] if details.is_a? String
self.set_backtrace(details)
end
Hope that helps someone in the same situation as me.
I'm using JFlow, it lets you start SWF flow activity workers with JRuby.

XSockets.NET 4.0 - new controller instance created on method call

After upgrading my .NET server and client projects to 4.0 RC
I get NullReference exceptions because my custom State object is null.
I instantiate the state property in OnOpen event handler, but inside the method body of the first call it is already null.
I have checked in debugger and see that this.GetHashCode() returns different values
in OnOpen event handler and in method, which means it is a different instance.
Is it a known issue? I assume it is very basic behavior and probably I have missed something during upgrade to new version.
Thanks in advance.
I managed to understand the problem. It happens when using PluginAlias.
[XSocketMetadata(PluginAlias =
When attribute is removed and client uses full controller name everything works as expected
and GetHashCode returns same id.
I pushed the replication code to GitHub:
https://github.com/amichel/PlayWithXSockets/tree/ReproduceBugs
When using alias there is a bug (as you have found out).
The workaround is to either use the class name of the controller or only have alias in lower casing.
In your case using
[XSocketMetadata(PluginAlias = "test")]
would work.
Regards
Uffe