GAE/J Channel API exception even though messages go through? - java

I open a channel during the app initialization through a series of ajax calls:
getToken = function () {
xhr = new XMLHttpRequest();
xhr.open("GET", "/game?action=getChannelToken", true);
xhr.send(null);
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status==200) {
connect(xhr.responseText);
}
};
};
Servlet:
ChannelService channelService = ChannelServiceFactory.getChannelService();
channelToken = channelService.createChannel(uid);
The token is then returned to the javascript for:
connect = function (token) {
// alert ("connect");
var channel = new goog.appengine.Channel(token);
var socket = channel.open();
socket.onopen = onOpened;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;
};
I'm getting this error:
WARNING: /_ah/channel/dev
com.google.appengine.api.channel.dev.LocalChannelFailureException:
Channel for application key null not
found.
The channel creation part is very simple, so I do not understand where is the problem.
System.out.println (channelToken); returns something like
channel--rrmk8i-100002139544068
(100002139544068 is the uid I used to create the channel), so it seems to return a real token. Moreover, channelService.sendMessage(msg); (using the same uid as before), sends the message without any problem.
Does anyone have any idea why this is happening?
I'm using eclipse 3.5.2, GAE/J 1.4.2 and ubuntu 10.10
Googling for that exception I found only one discussion here:
http://groups.google.com/group/google-appengine-java/browse_thread/thread/19f250b1ff0e4342
but changing var channel = new goog.appengine.Channel(token); to var channel = new goog.appengine.Channel(uid); did not solve anything (and, from what I understand, it shouldn't)

I can think of two reasons this could be happening:
You're restarting the dev_appserver.py while your client is still running. Because the client will be polling with an "old" token that the dev_appserver doesn't know about, it will throw this error. If this is the case, just refresh your client page after restarting the dev_appserver (or otherwise force it to request a new token).
connect() is being called with an invalid token. It sounds like you've ruled that out but if the above isn't true it might be worth double-checking.
You can see what token the client is polling with you can open up Firebug or the Chrome dev console and look for requests to a path like this:
http://localhost:8080/_ah/channel/dev?command=poll&channel=channel-1503856530-alpha-token&client=1
The channel-1503856530-alpha-token part of that URL is the token passed to "new goog.appengine.Channel()".

Related

Java Bot SDK throws 401 error for proactive message

I'm trying to implement a bot to send proactive messages. I made a proof of concept in NodeJS that works:
const { BotFrameworkAdapter } = require('botbuilder');
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
adapter.onTurnError = async (error) => {
console.error(error);
};
const cr = require('./test_cr.json');
adapter.continueConversation(cr, async (turnContext) => {
await turnContext.sendActivity('Node SDK proactive message')
});
Yet when I try to recreate this example using the Java SDK, I get com.microsoft.bot.connector.rest.ErrorResponseException: Status code 401, {"message":"Authorization has been denied for this request."}.
String appId = System.getenv("MicrosoftAppId");
String appPassword = System.getenv("MicrosoftAppPassword");
BotFrameworkAdapter bot = new BotFrameworkAdapter(new SimpleCredentialProvider(appId, appPassword));
ConversationReference cr = mapper.readValue(new File("test_cr.json"), ConversationReference.class);
CompletableFuture<Void> cf = bot.continueConversation(appId, cr, turnContext -> turnContext.sendActivity("Java SDK proactive message").thenApply(resourceResponse -> null));
cf.get();
The conversation reference and app credentials should be the same for both examples. Am I not setting up the Java bot correctly? For reference, I'm using a pretty barebones conversation reference:
{
"channelId":"msteams",
"serviceUrl":"https://smba.trafficmanager.net/amer/",
"conversation":{
"isGroup":true,
"conversationType":"channel",
"tenantId":"xxxxxx",
"id":"xxxxxx"
}
}
It sounds like it could be an issue with BotTrustServiceUrl. I can't explain it but in one of my bots, I had to add this additional code to get proactive messages to work (specifically after a restart). Not sure if this is your same issue but it helped me.
const { MicrosoftAppCredentials } = require('botframework-connector');
// Then in your proactive message section...
const conversationReference = req.body.conversationReference;
await adapter.continueConversation(conversationReference, async turnContext => {
// If you encounter permission-related errors when sending this message, see
// https://aka.ms/BotTrustServiceUrl
MicrosoftAppCredentials.trustServiceUrl(conversationReference.serviceUrl);
await turnContext.sendActivity(req.body.message);
});
Now I've also got two other bots where I didn't need to do this. The one that isn't working wihtout this code is using an older SDK so that could be it, both otherwise the proactive message functions are identical so I'm not sure.
EDIT: I misread the question and didn't notice that you were having trouble with Java SDK, not JavaScript SDK. But perhaps you could use the same method to see if this resolves your issue in Java as well.

Java websocket draft refuses handshake

I am working on android(Java) using TooTallNate's java websockets from this tutorial to consume websockets on android to connect with ws:// but I am getting error draft org.java_websocket.drafts.Draft_10#4560b1d0 refuses handshake. I tried their other draft versions but none of them worked either.
First of all, you want to use the Draft_6455, it is the current spec, the rest may or may not work on different servers reliably. There are constructors for the draft object which take a List<IProtocol>. If no protocol specified matches one offered by the server, the handshake will be refused.
public Draft_6455( List<IExtension> inputExtensions , List<IProtocol> inputProtocols )
public Draft_6455( List<IExtension> inputExtensions , List<IProtocol> inputProtocols, int inputMaxFrameSize )
I ran into a similar issue to yours with the newest version of TooTallNate's Java Websockets, my code was like so:
knownExtensions = new java.util.ArrayList();
knownProtocols = new java.util.ArrayList();
if(this._protocol){
knownProtocols.add(new org.java_websocket.protocols.Protocol(this._protocol));
}
this._socket = new _WebSocket(uri, new org.java_websocket.drafts.Draft_6455(knownExtensions, knownProtocols), toHashMap(this._headers), this._timeout);
You MUST have at least one valid protocol (even if it is a empty string), or you get the above error you referenced. So I changed my code to be:
...
if(this._protocol){
knownProtocols.add(new org.java_websocket.protocols.Protocol(this._protocol));
}
/* -=-=-=- NEW ADDED CODE -=-=-=- */
else {
knownProtocols.add(new org.java_websocket.protocols.Protocol(""));
}
/* -=-=-=- END NEW ADDED CODE -=-=-=- */
...
This is what broke, no protocol specified caused the "refuses handshake" error message for me.
Please note there are a couple of reasons for the above "refuses handshake", but in my case it was the missing empty protocol...
Did you try this on broswer? You will get a err code on the broswer.
You can write a simple js file to start and test whether this problem is on the server or is on the app.
Here is a demo,it won't take you too much time.
<script type="text/javascript">
function send() {
var url = 'ws://192.168.1.101:8080/WebSocket/echo';
var vs = new WebSocket(url);
vs.onopen = function(evt){
vs.send(te.value)
};
vs.onmessage = function(evt){
alert(evt.data);
};
}
Basically if you have for example a protocol "my-protocol"
ArrayList<IProtocol> protocols = new ArrayList<IProtocol>();
protocols.add(new Protocol("my-protocol"));
//Uncomment below if you want to have a fallback
//protocols.add(new Protocol(""));
Draft_6455 my_draft = new Draft_6455(Collections.<IExtension>emptyList(), protocols);
Taken from here

Run out of ports calling my webservice from an android app

I have an android app and a C# web service
I am hosting my web service on IIS Express - nothing fancy just set up the directories.
My android app go to the web service gets the data and then displays it on the screen and repeats this as fast as it can go.
After a while my android app will throw the following exception:
"SoapFault - faultcode: 'soap:Server' faultstring: 'Server was unable to process request. ---> No ports available in the range 10101 to 11100' faultactor: 'null' detail: org.kxml2.kdom.Node#41bdf340"
I have Googled around a bit and have not found anything that will help me.
I ran CurrPorts to watch the ports and it looks like every time the app calls the web service it is using a different port.
They all use port 80 but different remote ports.
The exception is being thrown on the following line of code
"SoapPrimitive response = (SoapPrimitive) _envelope.getResponse();"
If I run IISReset the app will go back to getting data again until sometime thereafter to get the exception mentioned above.
Here is the code for my web service which is just returning a comma separated string (it will do something more interesting just trying to get this working)
Web service code
[WebMethod]
public string GetDefaultResults()
{
return "100,1,0,125.987,124.993,117.904,116.038";
}
Here is the code that my android app uses to call the web service.
I create a new thread and have it call this method then get the string value split it on ',' then put it in a few different TextViews
public class WebService implements Runnable{
private static String NAMESPACE = "http://tempuri.org/";
private static String URL = "http://mymachine/mywebservice/service.asmx";
private static String SOAP_ACTION = "http://tempuri.org/";
private String _soapAndMethod;
private HttpTransportSE _androidHttpTransport;
private SoapSerializationEnvelope _envelope;
private String _methodName;
public WebService(String methodName) {
_methodName = methodName;
_soapAndMethod = SOAP_ACTION + _methodName;
SoapObject request = new SoapObject(NAMESPACE, methodName);
_envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
_envelope.dotNet = true;
_envelope.setOutputSoapObject(request);
_androidHttpTransport = new HttpTransportSE(URL);
}
#Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
}
public String InvokeWebService() {
String resTxt = null;
try {
_androidHttpTransport.call(_soapAndMethod, _envelope);
SoapPrimitive response = (SoapPrimitive) _envelope.getResponse();
resTxt = response.toString();
} catch (Exception e){
e.printStackTrace();
}
return resTxt;
}
}
Any help would be great. Thank you to those who do respond. Is there some setting in IIS I need to change? Is there something I missed in my code?
Additional information:
When the server is responding the webservice request IIS creates a process called w3wp.exe and it this process that is running out of ports.
Doing in a little digging and experimentation the Webservice gets newed up each call to "GetDefaultResults". Which leaves w3wp.exe in a Listening state and they never go away until the iisreset is run.
Some time later it uses the remaining ports available and starts throwing that exception.
Why is it not reusing the same webservice instead of creating a new one each time.
If that is not possible what code do I write in the Webservice to close the connection after it is finished. I put in a hack to cause it close after 5 seconds to see if that would work at least.
It does clean up w3wp.exe but after a short time I start getting this exception on the client.
"expected: START_TAG {http://schemas.xmlsoap.org/soap/envelope/}Envelope (position:START_TAG #2:7 in java.io.InputStreamReader#41beb098) "
so this is not a work around but it helped me understand what the problem is a little better.
protected override void Dispose(bool disposing)
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 5000;
timer.Elapsed += timer_Elapsed;
timer.Start();
base.Dispose(disposing);
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Environment.Exit(1);
}
Thanks for your help.
If you failed your first request, you might need to enable IIS Express to allow external connections. In your case, it looks like it only failed after running for a while.
In your question, I see that you
"go to the web service gets the data and then displays it on the screen and repeats this as fast as it can go.
did not call "close" on HttpTransportSE
You might be running out of local ports. Are you reusing the connection on all your requests? If not, try to close it when you are done.
My Webservice was referencing an api to get the values from then pass them over a webservice to the android app.
The problem was that every time the webservice was being called a new reference would be created to the api and after the request was sent it would never close out the port. As soon as I stopped referencing the api everything worked.
A cludge to get this to work is I had the webservice monitor a folder and send the data in the file that was last created. Then created another program to attach to the api and output the data to that folder. Not the most elegant solution but it works as a proof of concept for what I am doing.
Still not entirely sure why the Webservice was never closing the port and I am suspect that the Webservice could not dispose of the reference to the api but I am not really sure.

Appengine Java & GWT - Cannot recreate and open a channel

I'm using App engine Channel API with gwt-gae-channel library (v. 0.4) in GWT and i'm trying to test the recreation of a channel, after expiration (i.e. onError is called with code 401).
The test is:
- use GWT RPC method to create a channel (clientID= and expiration = 1 minute) and get the token needed: createTestChannel(useremail).
- call ChannelFactory.createChannel(token, new ChannelCreatedCallback() { ... }) and channel.open(new MySocketListener(){...})
- when onError(...) is called (channel expires) I try to call createTestChannel(useremail) again (to get a new token for the same clientID) and open a channel, but I always get onError() and onClose() called .
If I refresh the page or open a new tab with the same code, the first channel creation works ok, but after each channel expiration, I cannot recreate them. I also tried another clientID, but it wont work.
This is a known issue:
https://groups.google.com/forum/?fromgroups#!searchin/google-appengine-java/channel/google-appengine-java/kD3H6BWNYuA/NivXiDrqW7QJ
You must create a new channel on the server and get the new token. From the docs :
Tokens expire in two hours. If a client remains connected to a channel for longer than two hours, the socket’s onerror() and onclose() callbacks are called. At this point the client can make an XHR request to the application to request a new token.
http://code.google.com/appengine/docs/java/channel/overview.html#Tokens_and_Security

Use Apple Push Notification Service through Java

Am trying to implement a Java program which sends an Apple Push Notification to an iPhone client app... Found the following library: Java APNs
Provider code:
Created the following code (from Javapns) to use in my app:
try {
PayLoad payLoad = new PayLoad();
payLoad.addAlert("My alert message");
payLoad.addBadge(45);
payLoad.addSound("default");
PushNotificationManager pushManager = PushNotificationManager.getInstance();
pushManager.addDevice("iPhone", "f4201f5d8278fe39545349d0868a24a3b60ed732");
log.warn("Initializing connectiong with APNS...");
// Connect to APNs
pushManager.initializeConnection(HOST, PORT,
"/etc/Certificates.p12", "password",
SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);
Device client = pushManager.getDevice("Lambo");
// Send Push
log.warn("Sending push notification...");
PushNotificationManager.getInstance().sendNotification(client, payLoad);
}
catch (Exception e) {
throw new ApnsPushNotificationException("Unable to send push " + e);
}
When I run this app (as you can see through the Log4j statements) there's no exceptions which occur:
WARN [MyCode] Initializing connectiong with APNS...
WARN [MyCode] Sending push notification...
But my client app doesn't receive any notifications!
IDPP Registration Process:
Also, did the following on the iPhone Developer Program Portal (IDPP):
Created the APNS based SSL Certificate and Keys
Created and installed the provisioning profile
Installed the SSL Certificate and Key on the server.
Have read over the Apple Push Notification Service Guide several times and noticed a few things:
(1) On page 15, it states that the device token is not the same as the device UDID (which I am currently incorrectly passing in as the second parameter inside the PushNotificationManager.addDevice() method (see above)).
On page 17, it states:
"APNs generates a device token using information contained in the unique device certificate. The device token contains an identifier of the device. It then encrypts the device token with a token key and returns it to the device. The device returns the device token to the requesting application as an NSData object. The application then must deliver the device token to its provider in either binary or hexidecimal format."
iPhone OS Client Implementation
(2) After reading pages 33 - 34, I discovered that I didn't include the Objective-C code to have the app register with APNs.
Am not an Objective-C developer, so is this where I can recover the device code or do I have to get it from the certificate?
Where do I obtain the device token (sorry, someone else wrote the Objective-C client app and I am a Java Developer)?
Question(s):
(1) With the exception of not knowing where to get the device token and the mobile client code registration, is there anything else that I have not looked over or missed?
(2) Am I using the Javapns library the right way?
Thank you for taking the time to read this...
As a shameful self-advertising, I encourage to use java-apns library. Your code will look like:
ApnsService service =
APNS.newService()
.withCert("/etc/Certificates.p12", "password")
.withSandboxDestination() // or .withProductionDestination()
.build();
String payload =
APNS.newPayload()
.alertBody("My alert message")
.badge(45)
.sound("default")
.build();
String deviceToken = "f4201f5d8278fe39545349d0868a24a3b60ed732";
log.warn("Sending push notification...");
service.push(deviceToken, payload);
Just a little tip, in order to convert your received token into a format suitable for registration with javapns, this code will do the trick:
- (NSString *)convertTokenToDeviceID:(NSData *)token {
NSMutableString *deviceID = [NSMutableString string];
// iterate through the bytes and convert to hex
unsigned char *ptr = (unsigned char *)[token bytes];
for (NSInteger i=0; i < 32; ++i) {
[deviceID appendString:[NSString stringWithFormat:#"%02x", ptr[i]]];
}
return deviceID;
}
I tried this and I keep getting hanged when sending the notification, and nothing gets sent.
The issue stems from the following function:
public void sendNotification(Device device, PayLoad payload)
It seems that the bufferedreader has NULL
BufferedReader in =
new BufferedReader(new InputStreamReader(this.socket.getInputStream() ) );
So when this portion of the code gets hit it just hangs there in endless loop
logger.debug( "In: [" + in.readLine() + "]" );
This output is [null]
So then right after then the loops get executed:
while ( ! this.socket.isInputShutdown() ) {
while( in.ready() ) {
logger.debug("ready now");
logger.debug(in.readLine());
System.out.println( this.socket.getInputStream().read() );
}
}
The code enters the first while loop and waits for the BufferedReader in to be ready
and just keeps waiting..... ad that is your hanging
Your Java code looks solid! However, don't forget to close the connection, through PushNotificationManager.closeConnection(). It's important to cleanup after yourself.
As a side comment, I notice that you are adding the device 'iPhone' but querying for 'Lambo' afterwards. This is an indication of a bug.
The device token shown in the code is incorrect. Device tokens, currently, as 32-bit long value, which gets hexed into 64 characters. I assume that the server is failing silently when pushing the notification to invalid token!
The only way to get the device token is from the app itself. As provided by the Push Notification guide suggests, the iPhone app needs to register for notification upon launch. In the application:didRegisterForRemoteNotificationsWithDeviceToken:, the iPhone needs to send the device token to your java provider server. (For debugging purposes, you can just NSLog the device token and use it; it never changes across runs).
I would recommend that you create a server in your java provider server to receive device tokens. Set up a ServerSocket to receive connections from the iPhone and their device token (and any additional info you need) and insert the tokens in the database.
JavaPNS was recently updated to 2.0, and fixed ALL reported issues up to the release date. It does fix the issue you are describing, and using the library is MUCH simpler than it ever was (you can push a notification with a single line of code now).
You seem to be missing the token
pushManager.addDevice("iPhone", "f4201f5d8278fe39545349d0868a24a3b60ed732");
Takes id and token check:
https://github.com/o-sam-o/javapns/blob/master/src/javapns/notification/PushNotificationManager.java#L501
The only way to get a token is from the iphone app. A valid token looks something like this:
1d2d6f34 c5028bca c50df5f9 1992c912 ce7deae8 3bbe7da5 447f6a68 cfecdc0e
Regarding the comment for notnoop here:
If you are landing on this post in 2022, you'll find that the java-apns library doesn't work since 2021. Instead they recommend using pushy library.
I have tried this one just by following their example in the README file and it works really well. They have added examples for both authorisation types: by certificate or by token.

Categories