|
package com.ekexiu.push.service;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.jfw.apt.web.annotation.Path;
import org.jfw.apt.web.annotation.operate.Get;
import org.jfw.apt.web.annotation.operate.Post;
import org.jfw.apt.web.annotation.param.RequestHeader;
import org.jfw.util.ConstData;
import org.jfw.util.DateUtil;
import org.jfw.util.io.IoUtil;
import org.jfw.util.json.JsonService;
import org.jfw.util.log.LogFactory;
import org.jfw.util.log.Logger;
import org.jfw.util.reflect.TypeReference;
import com.ekexiu.operation.push.getui.Message;
import com.ekexiu.operation.push.getui.MsgCondition;
import com.ekexiu.operation.push.getui.MsgStyle;
import com.ekexiu.operation.push.getui.NotificationMessage;
@Path("/push")
public class PushService {
private static final Logger log = LogFactory.getLog(PushService.class);
private static Type RES_TYPE = new TypeReference<Map<String, Object>>() {
}.getType();
private static final MsgCondition android = new MsgCondition("phonetype", new String[] { "ANDROID" }, 1);
private static final MsgCondition ios = new MsgCondition("phonetype", new String[] { "IOS" }, 1);
private static final MsgCondition[] onlyAndroid = new MsgCondition[] { android };
private static final MsgCondition[] onlyIos = new MsgCondition[] { ios };
private static Map<String, String> CONTENT_TYPE = new HashMap<String, String>();
private static Map<String, String> TOKEN_MAP = new ConcurrentHashMap<String, String>();
private static Map<String, String> ERR_MAP = new HashMap<String, String>();
final static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
private String secret = "U96j677DHoA1Pv5X0mxMU9";
private String appKey = "48nq7d7yHu8dgbzVHuisp6";
private String appId = "TUdvbnxu1c97r6Fb6cUy57";
private int timeout = 0;
private boolean enable = false;
private String tokenUrl = "http://192.168.3.233/ajax/push/token";
private String lastLoadToken = null;
private String token = null;
private long lastBuildTime = 0;
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getAppKey() {
return appKey;
}
public void setAppKey(String appKey) {
this.appKey = appKey;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
private String buildSign(long flag) {
String p = this.appKey + flag + this.secret;
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(p.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : messageDigest.digest()) {
sb.append(digits[(b & 0xf0) >> 4]);
sb.append(digits[b & 0xf]);
}
return sb.toString();
} catch (Exception e) {
throw new RuntimeException("jdk unsupported sh256???????????", e);
}
}
private void raiseGeTuiError(Object obj) {
String errMsg = null;
errMsg = ERR_MAP.get(obj);
if (errMsg == null)
errMsg = "其他错误";
throw new RuntimeException("调用用个推restApi,返回结果错误:" + obj + ":" + errMsg);
}
private void checkResult(Map<String, Object> ret) {
Object obj = ret.get("result");
if (!"ok".equals(obj)) {
raiseGeTuiError(obj);
}
}
public String allocateRequesstId() {
UUID uuid = UUID.randomUUID();
return "A" + Long.toString(uuid.getLeastSignificantBits(), 36) + "_" + Long.toString(uuid.getMostSignificantBits(), 36);
}
private void buildToken() {
long time = System.currentTimeMillis();
String sign = this.buildSign(time);
StringBuilder sb = new StringBuilder();
sb.append("{\"sign\":\"").append(sign).append("\",\"timestamp\":\"").append(time).append("\",\"appkey\":\"").append(this.appKey).append("\"}");
try {
Map<String, Object> ret = post("https://restapi.getui.com/v1/" + this.appId + "/auth_sign", CONTENT_TYPE, sb.toString().getBytes(ConstData.UTF8));
this.checkResult(ret);
String otoken = (String) ret.get("auth_token");
if (otoken == null) {
throw new RuntimeException("返回token is null");
}
this.token = otoken;
this.lastBuildTime = System.currentTimeMillis();
TOKEN_MAP.put("authtoken", this.token);
} catch (Exception e) {
this.token = null;
log.error("build token error", e);
}
}
public void refresh() {
if (this.enable) {
if ((this.token != null) && ((System.currentTimeMillis() - this.lastBuildTime) < (24 * 60 * 60 * 1000 - 5000))) {
return;
}
this.buildToken();
} else {
this.loadToken();
}
}
private void loadToken() {
if (this.tokenUrl != null) {
try {
HttpURLConnection connection = getConnection(this.tokenUrl);
connection.setRequestMethod("GET");
connection.setUseCaches(false);
int code = connection.getResponseCode();
if (code == 200) {
InputStream in = null;
byte[] resData = null;
try {
in = connection.getInputStream();
resData = IoUtil.readStream(in, false);
} finally {
if (null != in)
IoUtil.close(in);
}
Map<String, Object> map = JsonService.fromJson(new String(resData, ConstData.UTF8), RES_TYPE);
Object obj = map.get("data");
if (obj != null && obj instanceof String) {
if (obj.equals(this.lastLoadToken)) {
return;
}
String tokenResult = (String) obj;
int idx = tokenResult.indexOf("_");
if (idx > 0) {
this.updateToken(tokenResult.substring(0, idx), Integer.parseInt(tokenResult.substring(idx + 1)));
} else {
TOKEN_MAP.put("authtoken", tokenResult);
}
this.lastLoadToken = tokenResult;
}
}
} catch (Exception e) {
log.error("load token error", e);
}
}
}
private void updateToken(String tk, int idx) {
int len = tk.length();
idx = len - idx;
TOKEN_MAP.put("authtoken", tk.substring(idx) + tk.substring(0, idx));
}
public Map<String, Object> post(String url, Map<String, String> header, byte[] data) throws IOException {
HttpURLConnection connection = getConnection(url);
try {
connection.setRequestMethod("POST");
connection.setUseCaches(false);
for (Map.Entry<String, String> entry : header.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
connection.setDoOutput(true);
connection.setDoInput(true);
if (this.timeout > 0) {
connection.setConnectTimeout(this.timeout);
connection.setReadTimeout(this.timeout);
}
connection.setFixedLengthStreamingMode(data.length);
OutputStream out = connection.getOutputStream();
try {
out.write(data);
} finally {
out.close();
}
int code = connection.getResponseCode();
if (code == 200) {
InputStream in = null;
byte[] resData = null;
try {
in = connection.getInputStream();
resData = IoUtil.readStream(in, false);
} catch (IOException e) {
log.error("Error read http response stream,url:" + url, e);
throw new IOException("Error read http response stream");
} finally {
if (null != in)
IoUtil.close(in);
}
try {
return JsonService.fromJson(new String(resData, ConstData.UTF8), RES_TYPE);
} catch (Exception e) {
log.error("invalid response data,url:" + url, e);
throw new IOException("invalid response data", e);
}
}
log.error("Error http reponse status:" + code + ",url:" + url);
throw new IOException("Error http reponse status:" + code);
} finally {
connection.disconnect();
}
}
@Path("/bindAlias")
@Post
public boolean bindAlias(String alias, String cid) {
String url = "https://restapi.getui.com/v1/" + this.appId + "/bind_alias";
StringBuilder sb = new StringBuilder();
sb.append("{\"alias_list\" : [{\"cid\":\"").append(cid).append("\",\"alias\":\"").append(alias).append("\" }]}");
try {
Map<String, Object> ret = post(url, TOKEN_MAP, sb.toString().getBytes(ConstData.UTF8));
this.checkResult(ret);
return true;
} catch (IOException e) {
log.error("bind alias error[cid:" + cid + ",alias:" + alias + "]", e);
return false;
}
}
@Get
@Path("/token")
public String getToken() {
if (this.enable) {
String ret = this.token;
if (ret != null) {
int len = ret.length();
Random random = new Random();
int idx = random.nextInt(len - 1);
while (idx < 1 || idx >= len) {
idx = random.nextInt(len - 1);
}
return ret.substring(idx) + ret.substring(0, idx) + "_" + idx;
}
}
return null;
}
@Get
@Path("/testCtrl")
public void ctrl(@RequestHeader("EKEXIU-DEV") String dev, boolean enable) {
String time = DateUtil.formatDateTime(System.currentTimeMillis());
time = time.substring(7, 8);
int i = Integer.parseInt(time) * 2;
int j = i / 2 + 9;
time = time + i + j;
if (time.equals(dev)) {
this.enable = enable;
if (this.enable) {
this.token = null;
this.lastBuildTime = 0;
}
this.refresh();
}
}
@Path("/bindTags")
@Post
public boolean bindTags(String[] tags, String cid) {
String url = "https://restapi.getui.com/v1/" + this.appId + "/set_tags";
Map<String, Object> msg = new HashMap<String, Object>();
msg.put("cid", cid);
msg.put("tag_list", tags);
try {
Map<String, Object> ret = post(url, TOKEN_MAP, JsonService.toJson(msg).getBytes(ConstData.UTF8));
this.checkResult(ret);
return true;
} catch (IOException e) {
log.error("bind tags error:" + JsonService.toJson(msg), e);
return false;
}
}
private Map<String, Object> createIosMessage(String title, String content, String payload) {
Map<String, Object> ret = new HashMap<String, Object>();
Map<String, Object> message = new HashMap<String, Object>();
ret.put("message", message);
message.put("appkey", this.appKey);
message.put("is_offline", true);
message.put("msgtype", "transmission");
Map<String, Object> transmission = new HashMap<String, Object>();
ret.put("transmission", transmission);
transmission.put("transmission_type", false);
transmission.put("transmission_content", payload);
Map<String, Object> pushInfo = new HashMap<String, Object>();
ret.put("push_info", pushInfo);
Map<String, Object> aps = new HashMap<String, Object>();
pushInfo.put("aps", aps);
Map<String, Object> alert = new HashMap<String, Object>();
aps.put("alert", alert);
alert.put("title", title);
alert.put("body", content);
aps.put("autoBadge", "+1");
aps.put("content-available", 1);
aps.put("sound", "default");
pushInfo.put("payload", payload);
ret.put("condition", onlyIos);
return ret;
}
private Map<String, Object> createAndriodMessage(String title, String content, String payload) {
Map<String, Object> ret = new HashMap<String, Object>();
Message message = new Message();
ret.put("message", message);
message.setAppKey(this.appKey);
message.setMsgtype("notification");
message.setOffline(true);
message.setOfflineExpireTime(2 * 60 * 1000);
message.setPushNetworkType(0);
NotificationMessage notification = new NotificationMessage();
ret.put("notification", notification);
notification.setTransmissionType(true);
notification.setTransmissionContent(payload);
MsgStyle style = new MsgStyle();
notification.setStyle(style);
style.setType(0);
style.setClearable(true);
style.setRing(true);
style.setText(content);
style.setTitle(title);
style.setVibrate(true);
ret.put("condition", onlyAndroid);
return ret;
}
public void toApp(String title, String content, Object payload) {
String url = "https://restapi.getui.com/v1/" + this.appId + "/push_app";
String pl_str = JsonService.toJson(payload);
Map<String, Object> andriodMessage = this.createAndriodMessage(title, content, pl_str);
Map<String, Object> iosMessage = this.createIosMessage(title, content, pl_str);
andriodMessage.put("requestid", this.allocateRequesstId());
iosMessage.put("requestid", this.allocateRequesstId());
try {
Map<String, Object> ret = post(url, TOKEN_MAP, JsonService.toJson(andriodMessage).getBytes(ConstData.UTF8));
this.checkResult(ret);
} catch (Exception e) {
log.error("send app message error", e);
}
try {
Map<String, Object> ret = post(url, TOKEN_MAP, JsonService.toJson(iosMessage).getBytes(ConstData.UTF8));
this.checkResult(ret);
} catch (Exception e) {
log.error("send app message error", e);
}
}
private MsgCondition createTagCondition(String[] tags) {
return new MsgCondition("tag", tags, 0);
}
private void addTag(Map<String, Object> msg, MsgCondition tagCondition) {
MsgCondition[] mcs = (MsgCondition[]) msg.get("condition");
MsgCondition[] mcsn = new MsgCondition[mcs.length + 1];
System.arraycopy(mcs, 0, mcsn, 0, mcs.length);
mcsn[mcs.length] = tagCondition;
msg.put("condition", mcsn);
}
public void pushWithTag(String title, String content, Object payload, String[] tags) {
String url = "https://restapi.getui.com/v1/" + this.appId + "/push_app";
String pl_str = JsonService.toJson(payload);
Map<String, Object> andriodMessage = this.createAndriodMessage(title, content, pl_str);
Map<String, Object> iosMessage = this.createIosMessage(title, content, pl_str);
andriodMessage.put("requestid", this.allocateRequesstId());
iosMessage.put("requestid", this.allocateRequesstId());
MsgCondition tagC = this.createTagCondition(tags);
addTag(andriodMessage, tagC);
addTag(iosMessage, tagC);
try {
Map<String, Object> ret = post(url, TOKEN_MAP, JsonService.toJson(andriodMessage).getBytes(ConstData.UTF8));
this.checkResult(ret);
} catch (Exception e) {
log.error("send app message error", e);
}
try {
Map<String, Object> ret = post(url, TOKEN_MAP, JsonService.toJson(iosMessage).getBytes(ConstData.UTF8));
this.checkResult(ret);
} catch (Exception e) {
log.error("send app message error", e);
}
}
public void pushWithCid(String title, String content, Object payload, String cid) {
String url = "https://restapi.getui.com/v1/" + this.appId + "/push_single";
String pl_str = JsonService.toJson(payload);
Map<String, Object> androidMessage = this.createAndriodMessage(title, content, pl_str);
Map<String, Object> iosMessage = this.createIosMessage(title, content, pl_str);
androidMessage.put("requestid", this.allocateRequesstId());
androidMessage.put("cid", cid);
iosMessage.put("requestid", this.allocateRequesstId());
iosMessage.put("cid", cid);
try {
Map<String, Object> ret = post(url, TOKEN_MAP, JsonService.toJson(androidMessage).getBytes(ConstData.UTF8));
this.checkResult(ret);
} catch (Exception e) {
log.error("send app message error", e);
}
try {
Map<String, Object> ret = post(url, TOKEN_MAP, JsonService.toJson(iosMessage).getBytes(ConstData.UTF8));
this.checkResult(ret);
} catch (Exception e) {
log.error("send app message error", e);
}
}
public void pushWithAlias(String title, String content, Object payload, String alias) {
String url = "https://restapi.getui.com/v1/" + this.appId + "/push_single";
String pl_str = JsonService.toJson(payload);
Map<String, Object> androidMessage = this.createAndriodMessage(title, content, pl_str);
Map<String, Object> iosMessage = this.createIosMessage(title, content, pl_str);
androidMessage.put("requestid", this.allocateRequesstId());
androidMessage.put("alias", "A_" + alias);
iosMessage.put("requestid", this.allocateRequesstId());
iosMessage.put("alias", "I_" + alias);
boolean sended = false;
Object result = null;
Map<String, Object> ret = null;
try {
ret = post(url, TOKEN_MAP, JsonService.toJson(androidMessage).getBytes(ConstData.UTF8));
} catch (Exception e) {
log.error("send app message error", e);
}
result = ret.get("result");
if ("ok".equals(result)) {
sended = true;
} else if (!"alias_notbind".equals(result)) {
raiseGeTuiError(result);
}
try {
ret = post(url, TOKEN_MAP, JsonService.toJson(iosMessage).getBytes(ConstData.UTF8));
} catch (Exception e) {
log.error("send app message error", e);
}
result = ret.get("result");
if ("ok".equals(result)) {
return;
} else if ("alias_notbind".equals(result)) {
raiseGeTuiError("alias_notbind");
} else {
if (!sended) {
raiseGeTuiError(result);
}
}
}
public static HttpURLConnection getConnection(String urlString) throws IOException {
URL url = new URL(urlString);
HttpURLConnection conn = null;
conn = (HttpURLConnection) url.openConnection();
String protocal = url.getProtocol();
if ((protocal != null) && (protocal.equalsIgnoreCase("https"))) {
try {
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
httpsConn.setSSLSocketFactory(getTrustAllSSLContext().getSocketFactory());
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
httpsConn.setHostnameVerifier(hv);
} catch (Exception e) {
log.warn("init httpmanager error", e);
throw new RuntimeException("init httpmanager error", e);
}
}
return conn;
}
public static SSLContext getTrustAllSSLContext() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager trust = new CustomTrustManager();
trustAllCerts[0] = trust;
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
return sc;
}
static class CustomTrustManager implements TrustManager, X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(X509Certificate[] certs) {
return true;
}
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
}
static {
CONTENT_TYPE.put("Content-Type", "application/json");
TOKEN_MAP.putAll(CONTENT_TYPE);
TOKEN_MAP.put("authtoken", "ef60d2aa2bc3193fc99a99ef43906cee70658156dfb41156acc35f7e7b7fb3dd");
ERR_MAP.put("no_msg", "没有消息体");
ERR_MAP.put("alias_error", "找不到别名");
ERR_MAP.put("black_ip", "黑名单ip");
ERR_MAP.put("sign_error", "鉴权失败");
ERR_MAP.put("pushnum_overlimit", "推送次数超限");
ERR_MAP.put("no_appid", "找不到appid");
ERR_MAP.put("no_user", "找不到对应用户");
ERR_MAP.put("too_frequent", "推送过于频繁");
ERR_MAP.put("sensitive_word", "有敏感词出现");
ERR_MAP.put("appid_notmatch", "appid与cid或者appkey不匹配");
ERR_MAP.put("not_auth", "用户没有鉴权");
ERR_MAP.put("black_appid", "黑名单app");
ERR_MAP.put("invalid_param", "参数检验不通过");
ERR_MAP.put("alias_notbind", "别名没有绑定cid");
ERR_MAP.put("tag_over_limit", "tag个数超限");
ERR_MAP.put("successed_online", "在线下发");
ERR_MAP.put("successed_offline", "离线下发");
ERR_MAP.put("taginvalid_or_noauth", "tag无效或者没有使用权限");
ERR_MAP.put("no_valid_push", "没有有效下发");
ERR_MAP.put("successed_ignore", "忽略非活跃用户");
ERR_MAP.put("no_taskid", "找不到taskid");
}
public static void testCtrl() throws UnknownHostException, IOException, InterruptedException {
String time = DateUtil.formatDateTime(System.currentTimeMillis());
time = time.substring(7, 8);
int i = Integer.parseInt(time) * 2;
int j = i / 2 + 9;
time = time + i + j;
StringBuilder sb = new StringBuilder();
sb.append("GET /ajax/push/testCtrl?enable=1 HTTP/1.1\r\nEKEXIU-DEV:").append(time).append("\r\nContent-Length:0\r\n");
sb.append("Accept:text/html,application/xhtml+xm…plication/xml;q=0.9,*/*;q=0.8\r\n"
+ "Accept-Encoding:gzip, deflate\r\n" + "Accept-Language:zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n"
+ "Cache-Control:max-age=0\r\n" + "Host:192.168.3.233\r\n" + "User-Agent:Mozilla/5.0 (Windows NT 6.1; W…) Gecko/20100101 Firefox/57.0\r\n"
+ "\r\n");
final long startTime = System.currentTimeMillis();
byte[] buf = sb.toString().getBytes("UTF-8");
Socket socket = new Socket("192.168.3.233", 80);
try {
final CountDownLatch lock = new CountDownLatch(1);
int idx = 1;
OutputStream out = socket.getOutputStream();
out.write(buf[0]);
final InputStream in = socket.getInputStream();
(new Thread() {
@Override
public void run() {
// byte[] buffer = new byte[4096];
for (;;) {
int i = 0;
try {
i = in.read();
} catch (IOException e) {
System.out.println("");
System.out.println("===============================");
e.printStackTrace();
System.out.println("===============================");
break;
}
if (i < 0) {
System.out.print("success close" + (System.currentTimeMillis() - startTime));
break;
}
System.out.print((char) i);
}
lock.countDown();
}
}).start();
while (idx < buf.length) {
out.write(buf[idx++]);
}
out.flush();
// socket.shutdownOutput();
lock.await();
} finally {
socket.close();
}
}
public static void main(String[] args) throws Exception {
testCtrl();
if( 1==1) return;
PushService s = new PushService();
// s.buildToken();
// s.checkToken();
String t = "44f123977f387165a8c2b77f83ce30ccad8be50a922a21a96976700dad3a5f61";
int idx = 51;
int len = t.length();
idx = len - idx;
System.out.println();
TOKEN_MAP.put("authtoken", t.substring(idx) + t.substring(0, idx));
long l = System.currentTimeMillis();
// s.toApp("title_" + l, "content_" + l, "payload_" + l);
s.pushWithAlias("title_" + l, "content_" + l, "payload_" + l, "1A21F6D7BD4C549A1A2ABC415DE7701BD");
// s.applyBadge(10, "f293af1f1cc043185ee9d921b63c4807",
// "9982595B6AAAE1EFABD0B40E215A76507E74D12477573491AF023B89807A01AA");
System.out.println(l);
// s.applyBadge(1, "f293af1f1cc043185ee9d921b63c4807",
// "D380AD6865B61F4D179DBB208EE3C7268A672AB9821385050596608A186F6023");
// for(int i = 0 ;i < 100;++i){
// UUID uuid= UUID.randomUUID();
// System.out.println( "A"+Long.toString(
// uuid.getLeastSignificantBits(),36)+"_"+Long.toString(uuid.getMostSignificantBits(),
// 36));
// }
}
}
|