Java tutorial
package org.jboss.aerogear.unifiedpush.message.sender; /** * JBoss, Home of Professional Open Source * Copyright Red Hat, Inc., and individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.jboss.aerogear.unifiedpush.api.ChromePackagedAppVariant; import org.jboss.aerogear.unifiedpush.api.Variant; import org.jboss.aerogear.unifiedpush.message.helper.ChromePackagedAppTokenCache; import org.jboss.aerogear.unifiedpush.message.UnifiedPushMessage; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.Charset; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @SenderType(ChromePackagedAppVariant.class) public class GCMForChromePushNotificationSender implements PushNotificationSender { private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final String MESSAGE_URL = "https://www.googleapis.com/gcm_for_chrome/v1/messages"; private static final String ACCESS_TOKEN_URL = "https://accounts.google.com/o/oauth2/token"; private final Logger logger = Logger.getLogger(GCMForChromePushNotificationSender.class.getName()); // We need a place to hold the current access token/expire time for each GCM for Chrome application. Not good practice to always get a new access token private Map<String, ChromePackagedAppTokenCache> accessTokenMap = new HashMap<String, ChromePackagedAppTokenCache>(); public void sendPushMessage(Variant variant, Collection<String> tokens, UnifiedPushMessage pushMessage, NotificationSenderCallback callback) { // no need to send empty list if (tokens.isEmpty()) { return; } final ChromePackagedAppVariant chromePackagedAppVariant = (ChromePackagedAppVariant) variant; String accessToken = fetchAccessToken(chromePackagedAppVariant); // iterate over all the given channelIDs for (String channelID : tokens) { HttpURLConnection conn = null; try { final String clientURL = MESSAGE_URL; // POST the payload to the GCM For Chrome server conn = post(clientURL, "{'channelId': '" + channelID + "', 'subchannelId': '0', 'payload': '" + pushMessage.getAlert() + "'}", accessToken); int chromePackagedAppStatusCode = conn.getResponseCode(); // did we get a 'good' status code? if (chromePackagedAppStatusCode < 400) { callback.onSuccess(); } else { logger.log(Level.SEVERE, "Error during Post execution to GCM for Chrome Network, status code was: " + chromePackagedAppStatusCode); callback.onError("Error delivering GCM/Chrome payload"); } } catch (IOException e) { logger.log(Level.SEVERE, "Error during Post execution to GCM for Chrome Network", e); callback.onError("Error delivering GCM/Chrome payload"); } finally { // tear down if (conn != null) { conn.disconnect(); } } } } /** * Returns HttpURLConnection that 'posts' the given body to the given URL. */ protected HttpURLConnection post(String url, String body, String accessToken) throws IOException { if (url == null || body == null) { throw new IllegalArgumentException("arguments cannot be null"); } byte[] bytes = body.getBytes(UTF_8); HttpURLConnection conn = getConnection(url); conn.setDoOutput(true); conn.setUseCaches(false); conn.setFixedLengthStreamingMode(bytes.length); conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Authorization", "Bearer " + accessToken); conn.setRequestMethod("POST"); OutputStream out = null; try { out = conn.getOutputStream(); out.write(bytes); } finally { // in case something blows up, while writing // the payload, we wanna close the stream: if (out != null) { out.close(); } } return conn; } protected HttpURLConnection refreshAccessToken(ChromePackagedAppVariant chromePackagedAppVariant) throws IOException { String body = "client_secret=" + chromePackagedAppVariant.getClientSecret() + "&grant_type=refresh_token&refresh_token=" + chromePackagedAppVariant.getRefreshToken() + "&client_id=" + chromePackagedAppVariant.getClientId(); byte[] bytes = body.getBytes(UTF_8); HttpURLConnection conn = getConnection(ACCESS_TOKEN_URL); conn.setDoOutput(true); conn.setUseCaches(false); conn.setFixedLengthStreamingMode(bytes.length); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); OutputStream out = null; try { out = conn.getOutputStream(); out.write(bytes); } finally { // in case something blows up, while writing // the payload, we wanna close the stream: if (out != null) { out.close(); } } return conn; } /** * * @param chromePackagedAppVariant * @return a valid access token */ protected String fetchAccessToken(ChromePackagedAppVariant chromePackagedAppVariant) { HttpURLConnection accessTokenConn = null; JSONParser jsonParser = new JSONParser(); ChromePackagedAppTokenCache accessTokenObject = null; String accessToken = null; long expireTime = 0; // in milliseconds try { // Not good practice to always get a new access token. so only get one if it is expired or null accessTokenObject = accessTokenMap.get(chromePackagedAppVariant.getClientId()); if (accessTokenObject != null) { accessToken = accessTokenObject.getAccessToken(); expireTime = accessTokenObject.getExpiresIn(); } if (accessToken == null || expireTime < new Date().getTime()) { accessTokenConn = refreshAccessToken(chromePackagedAppVariant); String stringResponse = getString(accessTokenConn.getInputStream()); accessToken = ((JSONObject) jsonParser.parse(stringResponse)).get("access_token").toString(); String expiresIn = ((JSONObject) jsonParser.parse(stringResponse)).get("expires_in").toString(); // Convert to millis long ex = Long.parseLong(expiresIn); expireTime = new Date().getTime() + (ex * 1000); if (accessTokenObject == null) { accessTokenObject = new ChromePackagedAppTokenCache(); } accessTokenObject.setAccessToken(accessToken); accessTokenObject.setExpiresIn(expireTime); accessTokenMap.put(chromePackagedAppVariant.getClientId(), accessTokenObject); } } catch (IOException e) { logger.log(Level.SEVERE, "Error during Post execution to GCM for Chrome Network For access token refresh", e); } catch (ParseException e) { logger.log(Level.SEVERE, "Error during Parse of Response ", e); } finally { // tear down if (accessTokenConn != null) { accessTokenConn.disconnect(); } } return accessToken; } /** * Convenience method to open/establish a HttpURLConnection. */ protected HttpURLConnection getConnection(String url) throws IOException { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); return conn; } /** * Convenience method to convert an InputStream to a String. * * <p> * If the stream ends in a newline character, it will be stripped. */ protected static String getString(InputStream stream) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(nonNull(stream))); StringBuilder content = new StringBuilder(); String newLine; do { newLine = reader.readLine(); if (newLine != null) { content.append(newLine).append('\n'); } } while (newLine != null); if (content.length() > 0) { // strip last newline content.setLength(content.length() - 1); } return content.toString(); } static <T> T nonNull(T argument) { if (argument == null) { throw new IllegalArgumentException("argument cannot be null"); } return argument; } }