Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright Google Inc.
*
* 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.
*/
package com.hextremelabs.gcm.client;
import com.hextremelabs.quickee.configuration.Config;
import com.hextremelabs.quickee.configuration.Key;
import com.hextremelabs.quickee.core.DataHelper;
import com.hextremelabs.quickee.response.BaseResponse;
import com.hextremelabs.quickee.response.DefaultResponses;
import com.hextremelabs.quickee.response.ResponseCodes;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ejb.Schedule;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.BufferedReader;
import java.io.Closeable;
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.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import static com.hextremelabs.gcm.client.Constants.GCM_SEND_ENDPOINT;
import static com.hextremelabs.gcm.client.Constants.JSON_CANONICAL_IDS;
import static com.hextremelabs.gcm.client.Constants.JSON_ERROR;
import static com.hextremelabs.gcm.client.Constants.JSON_FAILURE;
import static com.hextremelabs.gcm.client.Constants.JSON_MESSAGE_ID;
import static com.hextremelabs.gcm.client.Constants.JSON_MULTICAST_ID;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_BADGE;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_BODY;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_BODY_LOC_ARGS;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_BODY_LOC_KEY;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_CLICK_ACTION;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_COLOR;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_ICON;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_SOUND;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_TAG;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_TITLE;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_TITLE_LOC_ARGS;
import static com.hextremelabs.gcm.client.Constants.JSON_NOTIFICATION_TITLE_LOC_KEY;
import static com.hextremelabs.gcm.client.Constants.JSON_PAYLOAD;
import static com.hextremelabs.gcm.client.Constants.JSON_REGISTRATION_IDS;
import static com.hextremelabs.gcm.client.Constants.JSON_RESULTS;
import static com.hextremelabs.gcm.client.Constants.JSON_SUCCESS;
import static com.hextremelabs.gcm.client.Constants.JSON_TO;
import static com.hextremelabs.gcm.client.Constants.PARAM_COLLAPSE_KEY;
import static com.hextremelabs.gcm.client.Constants.PARAM_CONTENT_AVAILABLE;
import static com.hextremelabs.gcm.client.Constants.PARAM_DELAY_WHILE_IDLE;
import static com.hextremelabs.gcm.client.Constants.PARAM_DRY_RUN;
import static com.hextremelabs.gcm.client.Constants.PARAM_PRIORITY;
import static com.hextremelabs.gcm.client.Constants.PARAM_RESTRICTED_PACKAGE_NAME;
import static com.hextremelabs.gcm.client.Constants.PARAM_TIME_TO_LIVE;
import static com.hextremelabs.gcm.client.Constants.TOKEN_CANONICAL_REG_ID;
import static com.hextremelabs.gcm.client.Constants.TOPIC_PREFIX;
import static com.hextremelabs.quickee.response.ResponseCodes.REQUEST_SUCCESSFUL;
import static com.hextremelabs.quickee.response.ResponseCodes.TRANSACTION_FAILED;
/**
* Helper class to send messages to the GCM service using an API Key.
*/
@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class Sender {
protected static final String UTF8 = "UTF-8";
/**
* Initial delay before first retry, without jitter.
*/
protected static final int BACKOFF_INITIAL_DELAY = 1000;
/**
* Maximum delay before a retry.
*/
protected static final int MAX_BACKOFF_DELAY = 1024000;
protected static final Logger L = LoggerFactory.getLogger(Sender.class);
private static final ConcurrentLinkedQueue MESSAGE_QUEUE = new ConcurrentLinkedQueue<>();
protected final Random random = new Random();
@Inject
@Config
@Key("gcm.api.key")
private String key;
@Inject
@Config
@Key("gcm.ttl")
private Integer timeToLive;
@Inject
@Config
@Key("gcm.messages.persistent")
private Boolean persistent;
@Inject
private DefaultResponses dr;
@PersistenceContext
private EntityManager em;
private static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
L.debug("IOException closing stream", e);
}
}
}
/**
* Creates a map with just one key-value pair.
*/
protected static Map newKeyValues(String key, String value) {
Map keyValues = new HashMap<>(1);
keyValues.put(nonNull(key), nonNull(value));
return keyValues;
}
/**
* Creates a {@link StringBuilder} to be used as the body of an HTTP POST.
*
* @param name initial parameter for the POST.
* @param value initial value for that parameter.
* @return StringBuilder to be used an HTTP POST body.
*/
protected static StringBuilder newBody(String name, String value) {
return new StringBuilder(nonNull(name)).append('=').append(nonNull(value));
}
/**
* Adds a new parameter to the HTTP POST body.
*
* @param body HTTP POST body.
* @param name parameter's name.
* @param value parameter's value.
*/
protected static void addParameter(StringBuilder body, String name, String value) {
nonNull(body).append('&')
.append(nonNull(name)).append('=').append(nonNull(value));
}
/**
* Convenience method to convert an InputStream to a String.
*
* If the stream ends in a newline character, it will be stripped.
*
* If the stream is {@literal null}, returns an empty string.
*/
protected static String getString(InputStream stream) throws IOException {
if (stream == null) {
return "";
}
BufferedReader reader = new BufferedReader(new InputStreamReader(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();
}
private static String getAndClose(InputStream stream) throws IOException {
try {
return getString(stream);
} finally {
if (stream != null) {
close(stream);
}
}
}
static T nonNull(T argument) {
if (argument == null) {
throw new IllegalArgumentException("argument cannot be null");
}
return argument;
}
public BaseResponse send(String message, String to) {
Message msg = toGcmMessage(message);
BaseResponse response;
try {
GcmResult result = send(msg, to, 3);
if (result.getMessageId() == null) {
response = new BaseResponse<>(TRANSACTION_FAILED, result.getErrorCodeName(), result);
} else {
L.info("Push notification sent to {}. Result = {}", to, result);
if (persistent) {
MESSAGE_QUEUE.add(result);
}
response = new BaseResponse<>(result);
}
} catch (IOException ex) {
L.warn("Error contacting GCM.", ex);
response = new BaseResponse<>(ResponseCodes.SERVICE_ERROR, "Error contacting GCM.");
}
return response;
}
public BaseResponse send(String message, Collection to) {
if (DataHelper.hasBlank(to)) {
return new BaseResponse<>();
}
Message msg = toGcmMessage(message);
BaseResponse response;
try {
MulticastResult mr = send(msg, to, 3);
L.info("Multicast message sent. {} succeeded, {} failed.", mr.getSuccess(), mr.getFailure());
if (persistent) {
mr.getResults().forEach(MESSAGE_QUEUE::add);
}
response = new BaseResponse<>(dr.status(mr.getFailure() == 0
? REQUEST_SUCCESSFUL : mr.getSuccess() == 0
? TRANSACTION_FAILED : ResponseCodes.PARTIAL_SUCCESS), mr);
} catch (IOException ex) {
L.warn("Error contacting GCM.", ex);
response = new BaseResponse<>(ResponseCodes.SERVICE_ERROR, "Error contacting GCM.");
}
return response;
}
public BaseResponse broadcast(String message, String topic) {
return send(message, "/topics/" + topic);
}
private Message toGcmMessage(String message) {
Message msg = new Message.Builder()
//.collapseKey(messageJson)
.timeToLive(timeToLive)
//.delayWhileIdle(true)
.addData("message", message)
//.notification(new Notification.Builder("clear_grey600").title(message.messageType()).body(messageJson).build())
.build();
return msg;
}
/**
* Sends a message to one device, retrying in case of unavailability.
*
*
* Note: this method uses exponential back-off to retry in case of service unavailability and hence
* could block the calling thread for many seconds.
*
* @param message message to be sent, including the device's registration id.
* @param to registration token, notification key, or topic where the message will be sent.
* @param retries number of retries in case of service unavailability errors.
*
* @return result of the request (see its javadoc for more details).
*
* @throws IllegalArgumentException if to is {@literal null}.
* @throws InvalidRequestException if GCM didn't returned a 200 or 5xx status.
* @throws IOException if message could not be sent.
*/
public GcmResult send(Message message, String to, int retries) throws IOException {
int attempt = 0;
GcmResult result;
int backoff = BACKOFF_INITIAL_DELAY;
boolean tryAgain;
do {
attempt++;
L.debug("Attempt #{} to send message {} to regIds {}", attempt, message, to);
result = sendNoRetry(message, to);
tryAgain = result == null && attempt <= retries;
if (tryAgain) {
int sleepTime = backoff / 2 + random.nextInt(backoff);
sleep(sleepTime);
if (2 * backoff < MAX_BACKOFF_DELAY) {
backoff *= 2;
}
}
} while (tryAgain);
if (result == null) {
throw new IOException("Could not send message after " + attempt + " attempts");
}
return result;
}
/**
* Sends a message without retrying in case of service unavailability. See {@link #send(Message, String, int)} for
* more info.
*
* @return result of the post, or {@literal null} if the GCM service was unavailable or any network exception caused
* the request to fail, or if the response contains more than one result.
*
* @throws InvalidRequestException if GCM didn't returned a 200 status.
* @throws IllegalArgumentException if to is {@literal null}.
*/
public GcmResult sendNoRetry(Message message, String to) throws IOException {
nonNull(to);
Map