package cn.jpush.api.push; import cn.jiguang.common.ClientConfig; import cn.jiguang.common.ServiceHelper; import cn.jiguang.common.connection.*; import cn.jiguang.common.resp.*; import cn.jiguang.common.utils.Base64; import cn.jiguang.common.utils.Preconditions; import cn.jiguang.common.utils.StringUtils; import cn.jiguang.common.utils.sm2.SM2Util; import cn.jpush.api.push.model.*; import cn.jpush.api.push.model.audience.Audience; import com.google.gson.*; import java.util.List; /** * Entrance for sending Push. *

* For the following parameters, you can set them by instance creation. * This action will override setting in PushPayload Optional. * * apnsProduction If not present, the default is true. * * timeToLive If not present, the default is 86400(s) (one day). *

* Can be used directly. */ public class PushClient { private IHttpClient _httpClient; private String _baseUrl; private String _pushPath; private String _pushValidatePath; private String batchRegidPushPath; private String batchAliasPushPath; private String _filePushPath; private JsonParser _jsonParser = new JsonParser(); // If not present, true by default. private int _apnsProduction; // If not present, the default value is 86400(s) (one day) private long _timeToLive; // encrypt type, the default value is empty private String _encryptType; public PushClient() {} /** * Create a Push Client. * * @param masterSecret API access secret of the appKey. * @param appKey The KEY of one application on JPush. */ public PushClient(String masterSecret, String appKey) { this(masterSecret, appKey, null, ClientConfig.getInstance()); } /** * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig} instead of this constructor. * * @param masterSecret API access secret of the appKey. * @param appKey The KEY of one application on JPush. * @param maxRetryTimes The max retry times. */ @Deprecated public PushClient(String masterSecret, String appKey, int maxRetryTimes) { this(masterSecret, appKey, maxRetryTimes, null); } /** * Create a Push Client with max retry times. * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig} instead of this constructor. * * @param masterSecret API access secret of the appKey. * @param appKey The KEY of one application on JPush. * @param maxRetryTimes max retry times * @param proxy The max retry times. */ @Deprecated public PushClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy) { ServiceHelper.checkBasic(appKey, masterSecret); ClientConfig conf = ClientConfig.getInstance(); conf.setMaxRetryTimes(maxRetryTimes); this._baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME); this._pushPath = (String) conf.get(ClientConfig.PUSH_PATH); this._pushValidatePath = (String) conf.get(ClientConfig.PUSH_VALIDATE_PATH); this._filePushPath = (String) conf.get(ClientConfig.FILE_PUSH_PATH); this._apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION); this._timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE); String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret); this._httpClient = new NativeHttpClient(authCode, proxy, conf); } public PushClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) { ServiceHelper.checkBasic(appKey, masterSecret); this._baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME); this._pushPath = (String) conf.get(ClientConfig.PUSH_PATH); this._pushValidatePath = (String) conf.get(ClientConfig.PUSH_VALIDATE_PATH); this._filePushPath = (String) conf.get(ClientConfig.FILE_PUSH_PATH); this.batchAliasPushPath = (String) conf.get(ClientConfig.BATCH_ALIAS_PUSH_PATH); this.batchRegidPushPath = (String) conf.get(ClientConfig.BATCH_REGID_PUSH_PATH); this._apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION); this._timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE); this._encryptType = (String) conf.get(ClientConfig.ENCRYPT_TYPE); String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret); this._httpClient = new NativeHttpClient(authCode, proxy, conf); } /** * Create a Push Client with global settings. *

* If you want different settings from default globally, this constructor is what you needed. * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig} instead of this constructor. * * @param masterSecret API access secret of the appKey. * @param appKey The KEY of one application on JPush. * @param apnsProduction Global APNs environment setting. It will override PushPayload Options. * @param timeToLive Global time_to_live setting. It will override PushPayload Options. */ @Deprecated public PushClient(String masterSecret, String appKey, boolean apnsProduction, long timeToLive) { this(masterSecret, appKey); if (apnsProduction) { _apnsProduction = 1; } else { _apnsProduction = 0; } this._timeToLive = timeToLive; } /** * This will be removed in the future. Please use ClientConfig{jiguang-common cn.jiguang.common.ClientConfig#setGlobalPushSetting} instead of this method. * * @param apnsProduction Global APNs environment setting. It will override PushPayload Options. * @param timeToLive Global time_to_live setting. It will override PushPayload Options. */ @Deprecated public void setDefaults(boolean apnsProduction, long timeToLive) { if (apnsProduction) { _apnsProduction = 1; } else { _apnsProduction = 0; } this._timeToLive = timeToLive; } public void setBaseUrl(String baseUrl) { this._baseUrl = baseUrl; } public PushResult sendPush(PushPayload pushPayload) throws APIConnectionException, APIRequestException { checkPushPayload(pushPayload); ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushPath, getEncryptData(pushPayload)); return BaseResult.fromResponse(response, PushResult.class); } public PushResult sendPushValidate(PushPayload pushPayload) throws APIConnectionException, APIRequestException { checkPushPayload(pushPayload); ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushValidatePath, getEncryptData(pushPayload)); return BaseResult.fromResponse(response, PushResult.class); } public PushResult sendPush(String payloadString) throws APIConnectionException, APIRequestException { Preconditions.checkArgument(StringUtils.isNotEmpty(payloadString), "pushPayload should not be empty"); try { _jsonParser.parse(payloadString); } catch (JsonParseException e) { Preconditions.checkArgument(false, "payloadString should be a valid JSON string."); } ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushPath, getEncryptData(payloadString)); return BaseResult.fromResponse(response, PushResult.class); } public PushResult sendFilePush(PushPayload pushPayload) throws APIConnectionException, APIRequestException { checkPushPayload(pushPayload); ResponseWrapper response = _httpClient.sendPost(_baseUrl + _filePushPath, getEncryptData(pushPayload)); return BaseResult.fromResponse(response, PushResult.class); } public PushResult sendPushValidate(String payloadString) throws APIConnectionException, APIRequestException { Preconditions.checkArgument(StringUtils.isNotEmpty(payloadString), "pushPayload should not be empty"); try { _jsonParser.parse(payloadString); } catch (JsonParseException e) { Preconditions.checkArgument(false, "payloadString should be a valid JSON string."); } ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushValidatePath, getEncryptData(payloadString)); return BaseResult.fromResponse(response, PushResult.class); } public BatchPushResult batchSendPushByRegId(List pushPayloadList) throws APIConnectionException, APIRequestException { return batchSendPush(_baseUrl + batchRegidPushPath, pushPayloadList); } public BatchPushResult batchSendPushByAlias(List pushPayloadList) throws APIConnectionException, APIRequestException { return batchSendPush(_baseUrl + batchAliasPushPath, pushPayloadList); } public BatchPushResult batchSendPush(String url, List pushPayloadList) throws APIConnectionException, APIRequestException { Preconditions.checkArgument((null != pushPayloadList), "param should not be null"); Preconditions.checkArgument((!pushPayloadList.isEmpty()), "pushPayloadList should not be empty"); Gson gson = new Gson(); JsonObject contentJson = new JsonObject(); CIDResult cidResult = getCidList(pushPayloadList.size(), "push"); int i = 0; JsonObject pushPayLoadList = new JsonObject(); // setting cid for (PushPayload payload : pushPayloadList) { String cid = payload.getCid(); if (cid != null && !cid.trim().isEmpty()) { payload.setCid(null); } else { cid = cidResult.cidlist.get(i++); } pushPayLoadList.add(cid, payload.toJSON()); } contentJson.add("pushlist", pushPayLoadList); ResponseWrapper response = _httpClient.sendPost(url, getEncryptData(gson.toJson(contentJson))); return BatchPushResult.fromResponse(response); } /** * Get cid list, the data form of cid is appKey-uuid. * * @param count the count of cid list, from 1 to 1000. default is 1. * @param type default is "push", option: "schedule" * @return CIDResult, an array of cid * @throws APIConnectionException connect exception * @throws APIRequestException request exception */ public CIDResult getCidList(int count, String type) throws APIConnectionException, APIRequestException { Preconditions.checkArgument(count >= 1 && count <= 1000, "count should not less than 1 or larger than 1000"); Preconditions.checkArgument(type == null || type.equals("push") || type.equals("schedule"), "type should be \"push\" or \"schedule\""); ResponseWrapper responseWrapper; if (type != null) { responseWrapper = _httpClient.sendGet(_baseUrl + _pushPath + "/cid?count=" + count + "&type=" + type); } else { responseWrapper = _httpClient.sendGet(_baseUrl + _pushPath + "/cid?count=" + count); } return BaseResult.fromResponse(responseWrapper, CIDResult.class); } /** * Delete a push by msgId. * * @param msgId The message id * @return delete result * @throws APIConnectionException connect exception * @throws APIRequestException request exception */ public DefaultResult deletePush(String msgId) throws APIConnectionException, APIRequestException { Preconditions.checkArgument(StringUtils.isNotEmpty(msgId), "msgId should not be empty"); ResponseWrapper responseWrapper = _httpClient.sendDelete(_baseUrl + _pushPath + "/" + msgId); return DefaultResult.fromResponse(responseWrapper); } public void setHttpClient(IHttpClient client) { this._httpClient = client; } // 如果使用 NettyHttpClient,在发送请求后需要手动调用 close 方法 public void close() { if (_httpClient != null && _httpClient instanceof NettyHttpClient) { ((NettyHttpClient) _httpClient).close(); } else if (_httpClient != null && _httpClient instanceof ApacheHttpClient) { ((ApacheHttpClient) _httpClient).close(); } } /** * 获取加密的payload数据 * * @param payloadData * @return */ private String getEncryptData(String payloadData) { JsonElement payloadElement = _jsonParser.parse(payloadData); JsonObject jsonObject = payloadElement.getAsJsonObject(); Audience audience = Audience.fromJsonElement(jsonObject.get("audience")); return getEncryptData(payloadData, audience); } /** * 获取加密的payload数据 * * @param pushPayload * @return */ private String getEncryptData(PushPayload pushPayload) { if (StringUtils.isEmpty(_encryptType)) { return pushPayload.toString(); } if (EncryptKeys.ENCRYPT_SMS2_TYPE.equals(_encryptType)) { EncryptPushPayload encryptPushPayload = new EncryptPushPayload(); try { encryptPushPayload.setPayload(String.valueOf(Base64.encode(SM2Util.encrypt(pushPayload.toString(), EncryptKeys.DEFAULT_SM2_ENCRYPT_KEY)))); } catch (Exception e) { throw new RuntimeException("encrypt word exception", e); } encryptPushPayload.setAudience(pushPayload.getAudience()); return encryptPushPayload.toString(); } // 不支持的加密默认不加密 return pushPayload.toString(); } /** * 获取加密的payload数据 * * @param pushPayload * @return */ private String getEncryptData(String pushPayload, Audience audience) { if (StringUtils.isEmpty(_encryptType)) { return pushPayload; } if (EncryptKeys.ENCRYPT_SMS2_TYPE.equals(_encryptType)) { EncryptPushPayload encryptPushPayload = new EncryptPushPayload(); try { encryptPushPayload.setPayload(String.valueOf(Base64.encode(SM2Util.encrypt(pushPayload, EncryptKeys.DEFAULT_SM2_ENCRYPT_KEY)))); } catch (Exception e) { throw new RuntimeException("encrypt word exception", e); } encryptPushPayload.setAudience(audience); return encryptPushPayload.toString(); } // 不支持的加密默认不加密 return pushPayload; } private void checkPushPayload(PushPayload pushPayload) { Preconditions.checkArgument(!(null == pushPayload), "pushPayload should not be null"); if (_apnsProduction > 0) { pushPayload.resetOptionsApnsProduction(true); } else if (_apnsProduction == 0) { pushPayload.resetOptionsApnsProduction(false); } if (_timeToLive >= 0) { pushPayload.resetOptionsTimeToLive(_timeToLive); } } }