Fork us on GitHub

New Push Servers

We are starting the migration to the new push servers that will be off of App Engine
Post Image

New Push Servers

We are starting the complete overhaul of our push implementation that will allow us to deliver improved push related fixes/features and provide more reliability to the push service. When we designed our push offering initially it was focused around the limitations of Google App Engine which we are finally phasing out. The new servers are no longer constrained by this can scale far more easily and efficiently for all requirements.

However, as part of this we decided to separate the push functionality into two very different capabilities: push & device registration.
Currently only push is supported and so the feature of pushing to all devices is effectively unsupported at the moment. However, once the device registration API is exposed it will allow you to perform many tasks that were often requested such as the ability to push to a cross section of devices (e.g. users in a given city).

The original push API mixed device tracking and the core push functionality in a single API which meant we had scaling issues when dealing with large volumes of push due to database issues. So the new API discards the old device id structure which is just a numeric key into our database. With the new API we have a new device key which includes the native OS push key as part of its structure e.g. cn1-ios-nativedevicekey instead of 999999.

Assuming you store device ids in your server code you can easily convert them to the new device ids, your server code can check if a device id starts with cn1- and if not convert the old numeric id to the new ID using this request (assuming 999999 is the device id):

https://codename-one.appspot.com/token?m=id&i=999999

The response to that will be something like this:

{"key":"cn1-ios-nativedevicecode"}

The response to that will be something or this:

{"error":"Unsupported device type"}

To verify that a push is being sent by your account and associate the push quotas correctly the new API requires a push token. You can see your push token in the developer console at the bottom of the account settings tab. If it doesn't appear logout and login again.

The new API is roughly identical to the old API with two major exceptions:

  1. We now need to replace usage of Push.getDeviceKey() with Push.getPushKey(). We thought about keeping the exact same API but eventually decided that creating a separate API will simplify migration and allow you to conduct it at your own pace.
  2. All push methods now require the push token as their first argument. The old methods will push to the old push servers and the new identical methods that accept a token go to the new servers.

To send a push directly to the new servers you can use very similar code to the old Java SE code we provided just changing the URL, adding the token and removing some of the unnecessary arguments. Send the push to the URL https://push.codenameone.com/push/push which accepts the following arguments:

  • token - your developer token to identify the account sending the push
  • device - one or more device keys to send the push to. You can send push to up to 500 devices with a single push.
  • type - the message type identical to the old set of supported types in the old push servers
  • body - the body of the message
  • auth - the Google push auth key
  • production - true/false whether to push to production or sandbox environment in iOS
  • certPassword - password for the push certificate in iOS push
  • cert - http or https URL containing the push certificate for an iOS push

E.g. we can send push to the new servers using something like this from Java SE/EE:

URLConnection connection = new URL("https://push.codenameone.com/push/push").openConnection();
connection.setDoOutput(true); 
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
String query = "token=TOKEN&device=" + deviceId1 + 
                    "&device=" + deviceId2 + "&device=" + deviceId3 +
                    "&type=1&auth=GOOGLE_AUTHKEY&certPassword=URL_ENCODED_CERTIFICATE_PASSWORD&" +
                    "cert=URL_ENCODED_LINK_TO_YOUR_P12_FILE&body=" + URLEncoder.encode(MESSAGE_BODY, "UTF-8") +
                    "&production=false"; 
try (OutputStream output = connection.getOutputStream()) {
    output.write(query.getBytes("UTF-8"));
}
int c = connection.getResponseCode();
... read response and parse JSON

Unlike the previous API which was completely asynchronous and decoupled the new API is mostly synchronous so we return JSON that you should inspect for results and to maintain your device list. E.g. if there is an error that isn't fatal such as quota exceeded etc. you will get an error message like this:

{"error":"Error message"}

A normal response though, will be an array with results:

[
    {"id"="deviceId","status"="error","message"="Invalid Device ID"},
    {"id"="cn1-gcm-nativegcmkey","status"="updateId" newId="cn1-gcm-newgcmkey"},
    {"id"="cn1-gcm-okgcmkey","status"="OK"},
    {"id"="cn1-gcm-errorkey","status"="error" message="Server error message"},
    {"id"="cn1-ios-iphonekey","status"="inactive" },
]

There are several things to notice in the responses above:

  • If the response contains status=updateId it means that the GCM server wants you to update the device id to a new device id. You should do that in the database and avoid sending pushes to the old key.
  • iOS doesn't acknowledge device receipt but it does send a status=inactive result which you should use to remove the device from the list of devices.

Update: It seems that APNS (Apple's push service) returns uppercase key results. So you need to query the database in a case insensitive way.

Moving Forward

Right now the legacy push system still sends to its own push destination. We will redirect the old push servers to the new servers which will effectively mean that every push sent to the old push servers will go to the new servers and it should allow us to keep them up longer. However, we'll eventually retire them as we will retire the entire App Engine infrastructure so you should try to migrate at your own pace instead of rushing thru it at the last moment.

We are aware that for some corporate requirements push is strategically important so we are working on the ability to integrate with 3rd party push providers such as Urban Airship to provide scale for higher push volumes and builtin features for cross section/analytics.

Share this Post:

Posted by Shai Almog

Shai is the co-founder of Codename One. He's been a professional programmer for over 25 years. During that time he has worked with dozens of companies including Sun Microsystems.
For more follow Shai on Twitter & github.