@SuppressWarnings("rawtypes") @Override public void run() { System.out.println("开始查询是否有短信要发送。"); while (true) { try { // 本服务需要被监控,更新当前服务的时间戳 // new URL(this.backgroundMonitorUrl + // this.getClass().getCanonicalName()).openConnection().getInputStream().close(); if (this.backgroundMonitorUrl.endsWith("=")) { // 默认传递空白监控参数,由服务器产生监控参数(客户端来访IP) InputStream is = new URL(this.backgroundMonitorUrl).openConnection().getInputStream(); byte[] b = new byte[1024 * 1]; int length = is.read(b); is.close(); // 组合新的监控链接(加上监控参数) String s = new String(b, 0, length); this.backgroundMonitorUrl += s + "--" + this.getLocalIPForJava() + "--" + this.getClass().getSimpleName(); } // 访问监控链接 new URL(this.backgroundMonitorUrl).openConnection().getInputStream().close(); } catch (MalformedURLException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } // 以下代码调试用 // try // { // Thread.sleep(this.smsSendInterval*1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } // // if (true) // continue; // 从数据库查询待发短信 // 判断号码是否正确 // 如果是非系统主动发出的短信(如考勤短信),则添加发送者编号,用于处理短信接收者回复短信 // 每次查询50条待发短信 // MHJ 2013-10-15 新增定时发送短信功能,即短信sendtime时间需要小于当前时间,才发送 String queryString = "select * from t_smsend t where (flag=0) "; // MHJ 2013.11.14 因上海莫名/浙江移动 可能封杀了往移动号码发送短信,故增加对发送状态为-3的短信,用集信通进行重发 // 当使用上海莫名通道进行短信发送时,如果失败,则将状态码设置为-3,等待使用 // 2013.11.15 浙江省联通要求,如果超过10分钟,收不到来自异网状态通知,则重发该条短信 // String queryString = "select * from t_smsend t where (flag=0 or flag=-3 or // ((statustime-finaldate)*60*24>10 and flag=1 and not regexp_like (t.destnbr, // '861(30|31|32|45|55|56|85|86)[0-9]{8}') ) ) "; // 集信通什么短信都发 // 这里只发送非联通的短信 // queryString += " and not regexp_like (t.destnbr, '861(30|31|32|45|55|56|85|86)[0-9]{8}') // "; // 这里只发送非电信的短信 // queryString += " and not regexp_like (t.destnbr, '861(33|80|81|89|53)[0-9]{8}') "; // 这里只发送MHJ和喻元的电信号码,用于检测浙江电信通道是否恢复 // queryString += " or (t.destnbr='8613392893853' or t.destnbr='8618098903528') ) "; // queryString += " or (t.destnbr='8613392893853') ) "; // 这里只发送联通的短信 queryString += " and regexp_like (t.destnbr, '861(30|31|32|45|55|56|85|86)[0-9]{8}') "; // 这里只发送电信的短信 // queryString += " and regexp_like (t.destnbr, '861(33|80|81|89|53)[0-9]{8}') "; // 这里只发送5分钟内的短信(正常情况下,不会有短信超过5分钟仍发送不出去) // 超过10分钟的短信,用备用通道发送,如集信通 queryString += " and ((sendtime >= SYSDATE - 5 / 24 / 60 AND sendtime <= SYSDATE) or sendtime is null)"; // queryString += " and (sysdate-sendtime)*60*24>10 "; // 如果指定地区,则追加限制条件 if (null != this.areaIds && 0 < this.areaIds.length()) { queryString += "and t.school_id in (select school_id from t_jcxx_school t where t.area in (" + this.areaIds + "))"; } // 每次查出最多50条待发短信 queryString += " and rownum<50 "; queryString += " order by center_id nulls first, id desc "; // 定义此变量,用于传入 final String sql = queryString; @SuppressWarnings("unchecked") // List <TSmsend> smsList = this.tSmsendDao.getHibernateTemplate().find(queryString); // 直接使用原始SQL查询 // MHJ 2014.7.31 // 因贵州服务器内网偶尔会出现网络异常,导致无法顺利从5.2连接到2.2(数据库),故增加异常判断,当执行本查询时恰好出现网络异常,则重新执行一遍 List<TSmsend> smsList = null; try { smsList = this.tSmsendDao .getHibernateTemplate() .executeFind( new HibernateCallback() { public Object doInHibernate(Session session) throws SQLException, HibernateException { SQLQuery query = session.createSQLQuery(sql).addEntity(TSmsend.class); return query.list(); } }); } catch (UncategorizedSQLException e) { System.out.println("网络异常,重新查询数据库!"); // 暂停一分钟 try { Thread.sleep(60 * 1000); } catch (InterruptedException e1) { e1.printStackTrace(); } smsList = this.tSmsendDao .getHibernateTemplate() .executeFind( new HibernateCallback() { public Object doInHibernate(Session session) throws SQLException, HibernateException { SQLQuery query = session.createSQLQuery(sql).addEntity(TSmsend.class); return query.list(); } }); } for (TSmsend tSmsend : smsList) { // 保存开始提交短信处理的时间。 tSmsend.setUptime(new Timestamp(System.currentTimeMillis())); // TODO 这里用来动态改变短信接收号码 // tSmsend.setDestnbr("8613157191037"); // 使用正则表达式检查号码是否合法 if (tSmsend.getDestnbr().matches("861[0-9]{10}")) { // 如果号码不属于联通号段,则通过代理商发送短信 // TODO 目前未检测号码是否属于浙江联通号段 // 如果采用了集信通(tdType=6)来发送短信,则不区分联通或者非联通(主要用于处理队列超时),都统一用集信通来发送短信 if (tSmsend.getDestnbr().matches("861(30|31|32|45|55|56|85|86)[0-9]{8}") && !"6".equalsIgnoreCase(this.tdType)) // if (false) { // 通过浙江联通发送短信 if (null == this.client) { this.initSGIP(); } // 初始化金鹏SGIP短信发送类 if (null == this.mtClient) { this.mtClient = MTClient.getInstance(); // 获取单例的MTClienter } // try { // MHJ 因2013.11.7日仍出现卡死而无法缓解的现象,初步判断和流控不起作用有关,故暂时不使用流控 // 而直接每条短信等待50毫秒 /* // 启用联通短信行业网关发送速度流量控制 if (speedCount == 0) { timeFlag = System.currentTimeMillis(); } */ // 因 2013-10-29 日出现浙江联通短信行业网关异常超时,故增加超时检测机制 // 如果超过30秒没有执行完,则中止,重新启动,重发短信 SGIPSmsSendTask SgipSmsSendTask = new SGIPSmsSendTask(); SgipSmsSendTask.setTSmsend(tSmsend); Boolean taskResult = false; String failReason = null; // 短信发送超时检测 ExecutorService execService = Executors.newCachedThreadPool(); Future<Boolean> future = execService.submit(SgipSmsSendTask); try { // 等待计算结果,最长等待 30 秒,30 秒后中止任务 // 因联通本网不应该会出现超时,所以将超时设置为10秒,而不是之前的30秒 taskResult = future.get(10, TimeUnit.SECONDS); } catch (InterruptedException e) { failReason = "主线程等待浙江联通行业网关提交短信时被中断!"; } catch (ExecutionException e) { failReason = "主线程等待浙江联通行业网关提交短信,但抛出异常!"; } catch (TimeoutException e) { failReason = "主线程等待浙江联通行业网关提交短信(" + tSmsend.getId() + "/" + tSmsend.getDestnbr() + ")任务超时,因此中断任务线程!" + new Date(); } finally { execService.shutdownNow(); execService = null; if (null != failReason) { System.out.println(failReason); // MHJ 2013.11.04 因发现近期浙江联通短信行业网关,发送超时后,中止进程,但是仍无法正常继续发送,所以增加等待90秒 try { System.out.println( "发现浙江联通短信行业网关卡死,再人为等待90秒钟。(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ")"); Thread.sleep(1000 * 90); } catch (InterruptedException e) { e.printStackTrace(); } // MHJ 2013.11.05 发现增加人为等待,仍无法缓解,故重新初始化 SGIPClient // MHJ 2013.11.08 发现没什么效果,故取消 // this.client = null; } System.out.println( "通过联通短信网关发送短信,时间戳:" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ",接收号码=" + tSmsend.getDestnbr()); // MHJ 因2013.11.7日仍出现卡死而无法缓解的现象,初步判断和流控不起作用有关,故暂时不使用流控 // MHJ 2013.11.10 将等待时间从50毫秒增加到100毫秒 // 而直接每条短信等待40毫秒,即每秒最多发送25条短信 MHJ 2013.11.15 try { Thread.sleep(40); } catch (InterruptedException e) { e.printStackTrace(); } } // MHJ 因2013.11.7日仍出现卡死而无法缓解的现象,初步判断和流控不起作用有关,故暂时不使用流控 // 而直接每条短信等待50毫秒 /* // 已发短信计数加一 speedCount++; // 进行常规短信发送间隔停顿(含流量控制) calSendSpeed(); */ // this.sendSmsBySGIP(tSmsend); // } catch (IOException e) { // e.printStackTrace(); // continue; // } // System.out.println(tSmsend.getDestnbr()); } else { // 短信发送是否成功标识位 int sendFlag = -1; // -1表示发送失败 // 如果配置了异网短信代理商调用地址,则通过代理商发送短信(同时验证用户名和Key是否为空) if (null != this.smsProxyUrl && 0 < this.smsProxyUrl.length()) { if (null != this.smsProxyUsername && 0 < this.smsProxyUsername.length()) { if (null != this.smsProxyKey && 0 < this.smsProxyKey.length()) { try { // 短信末尾签名 String signature = "【平安校园】"; // 通过代理商发送短信,不需要86前缀 String destNbr = tSmsend.getDestnbr().startsWith("86") ? tSmsend.getDestnbr().substring(2) : tSmsend.getDestnbr(); // destNbr = "13590132493"; String content = tSmsend.getMsgcontent(); content = null == content ? "" : content; // content = // "60个字符60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符不超过60个字符【平园】"; // 如果短信内容已经包含签名,则提取签名 if (content.indexOf("【") < content.indexOf("】")) { // 提取签名 signature = content.substring(content.lastIndexOf("【"), content.lastIndexOf("】") + 1); } List<String> tmpList = new ArrayList(); while (content.length() > 200) { tmpList.add(content.substring(0, 200)); content = content.substring(200); } tmpList.add(content); if (1 < tmpList.size()) { int i_Count = tmpList.size(); int i_Index = 0; for (String strSms : tmpList) { i_Index++; // 拆分后的短信 content = "[" + i_Index + "/" + i_Count + "]" + strSms; // 发送短信 sendFlag = sendSmsByProxy( tSmsend.getId().longValue(), destNbr, content, signature); } System.out.println("短信拆分条数:" + i_Count); } else { // 短信不需要拆分 // 发送短信 sendFlag = sendSmsByProxy(tSmsend.getId().longValue(), destNbr, content, signature); } // 短信通过上海莫名网络科技发展有限公司发送 tdType=7 if (null != this.tdType && 0 < this.tdType.length()) tSmsend.setTdtype(this.tdType); tmpList.clear(); tmpList = null; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } // 保存短信发送状态:通过代理商发送短信 tSmsend.setFlag(new Integer(sendFlag)); // 保存提交短信成功的时间。 tSmsend.setFinaldate(new Timestamp(System.currentTimeMillis())); // 保存短信发送状态 this.tSmsendDao.getHibernateTemplate().update(tSmsend); } } else { // 将所有错误的短信接收号码,设置发送状态为-1(即发送失败) tSmsend.setFlag(-1); this.tSmsendDao.getHibernateTemplate().saveOrUpdate(tSmsend); } } try { Thread.sleep(this.smsSendInterval * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
private void sendSmsBySGIP(TSmsend tSmsend) throws IOException { // 该短信发起者的标识号,默认为空,表示是系统短信,如考勤短信 String srcNo = (null == tSmsend.getSrcnbr()) ? "" : tSmsend.getSrcnbr(); // 如果长度不足11位,也将转换为系统短信,即设置为默认 // substring(1) 表示取手机号后10位,用户回复时,再前补1,拼成完整手机号 srcNo = (11 != srcNo.length()) ? "" : srcNo.substring(1); // 如果不是系统短信(如考勤短信),比如老师发送家庭作业等,则追加短信发起者的编号到106xxx后,用于处理用户回复短信 (t_jcxx_accout.id) if ("".equalsIgnoreCase(srcNo) && null != tSmsend.getCenterId()) { List<TJcxxTeacherinfo> tJcxxTeacherinfoList = this.tJcxxTeacherinfoDao.findByTeacherId(tSmsend.getCenterId()); if (0 < tJcxxTeacherinfoList.size()) { TJcxxTeacherinfo tJcxxTeacherinfo = tJcxxTeacherinfoList.get(0); if (null != tJcxxTeacherinfo.getMobile()) { srcNo = tJcxxTeacherinfo.getMobile().substring(1); } } } // 贵州联通不支持通道扩展号 srcNo = ""; String content = tSmsend.getMsgcontent(); content = null == content ? "" : content; if (true) { // MHJ 2013.11.11 // 因使用 ZTE 提供的 SGIP 短信发送包<br> // 在浙江联通出现僵死现象,返回-3<br> // 在广东联通,出现无法连接,返回-5现象<br> // 故联系广东联通,获取金鹏SGIP短信开发包<br> // 以下是使用金鹏开发包发送短信 // 以下是发送短信的代码(可多线程调用) // System.out.println("检测浙江联通短信行业网关是否提交短信超时 sendMessage (" + tSmsend.getId() + "/" + // tSmsend.getDestnbr() + ")(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) // + ")"); SendResult[] results = this.mtClient.sendMessage(srcNo, tSmsend.getDestnbr(), content, null); // if ("8618606709366".equalsIgnoreCase(tSmsend.getDestnbr())) // this.mtClient.sendMessage(srcNo, tSmsend.getDestnbr(), content, null); // else // this.mtClient.sendMessage(tSmsend.getDestnbr(), content); // System.out.println("检测浙江联通短信行业网关是否提交短信超时 sendMessage (" + tSmsend.getId() + "/" + // tSmsend.getDestnbr() + ")(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) // + ")"); for (SendResult reslut : results) { // log.info("发送短信,短信序号为:"+ Arrays.toString(reslut.getFlowId()) + // "发送结果:"+reslut.getResult()); // 保存短信发送状态:SGIP返回0,表示短信已成功发送,需将状态设置为1,保存到数据库备查。 tSmsend.setFlag(new Integer(0 == reslut.getResult() ? 1 : reslut.getResult())); } } else { // 以下是使用中兴开发包发送短信 SGIPRsp rspHandler = new SGIPRsp(); SGIPSubmitResp rsp = null; this.submit.getHead().setSequenceNo(new SGIPSequenceNo()); submit.getBody().setSPNumber(this.spNumber + srcNo); this.submit.getBody().setUserNumber(tSmsend.getDestnbr()); // 如果短信内容长度不超过127个字符,则使用标准短信方式发送短信 if (127 >= content.length()) { this.submit.getBody().setTP_udhi(0); // 启用发标准短信 this.submit.getBody().setMessageContent(content.getBytes("UnicodeBigUnmarked")); // 要发送的短信内容 System.out.println( "检测浙江联通短信行业网关是否提交短信超时 普通短信 sendSubmit (" + tSmsend.getId() + "/" + tSmsend.getDestnbr() + ")(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ")"); this.client.sendSubmit(submit, rspHandler); System.out.println( "检测浙江联通短信行业网关是否提交短信超时 普通短信 waitForSGIPSubmitResp (" + tSmsend.getId() + "/" + tSmsend.getDestnbr() + ")(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ")"); rsp = rspHandler.waitForSGIPSubmitResp(); System.out.println( "检测浙江联通短信行业网关是否提交短信超时 普通短信 over (" + tSmsend.getId() + "/" + tSmsend.getDestnbr() + ")(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ")"); } else { // 短信内容超过127个字符,使用长短信方式,发送短信 this.submit.getBody().setTP_udhi(1); // 启用发标准短信 // 因使用长短信方式时,需将原短信,拆分成多条小短信,每条小短信,长度不超过67个字符, // 因采用 UnicodeBigUnmarked 编码格式,每个字符,不管英文还是中文,都占用2个字节, // 故每小条短信的长度均为67个字符(在Java里),加上短信头6个字节,一共等于140个字节。 List msgList = new ArrayList(); while (content.length() > 67) { msgList.add(content.substring(0, 67)); content = content.substring(67); } msgList.add(content); // 计算总条数 byte totalMsg = (byte) msgList.size(); // 本条长短信的(随机)序列号 byte by_seq = (byte) (new Random().nextInt(254) + 1); for (int i = 0; i < msgList.size(); i++) { String msg = (String) msgList.get(i); byte[] byArr = {0x05, 0x00, 0x03, by_seq, totalMsg, (byte) (i + 1)}; byte[] bb = msg.getBytes("UnicodeBigUnmarked"); byte[] bb_arr = new byte[bb.length + 6]; System.arraycopy(byArr, 0, bb_arr, 0, 6); System.arraycopy(bb, 0, bb_arr, 6, bb_arr.length - 6); this.submit.getBody().setMessageContent(bb_arr); // 每小条要发送的短信内容 System.out.println( "检测浙江联通短信行业网关是否提交短信超时 sendSubmit 长短信 (" + tSmsend.getId() + "/" + tSmsend.getDestnbr() + ")(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ")"); this.client.sendSubmit(submit, rspHandler); System.out.println( "检测浙江联通短信行业网关是否提交短信超时 waitForSGIPSubmitResp 长短信 (" + tSmsend.getId() + "/" + tSmsend.getDestnbr() + ")(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ")"); rsp = rspHandler.waitForSGIPSubmitResp(); System.out.println( "检测浙江联通短信行业网关是否提交短信超时 over 长短信 (" + tSmsend.getId() + "/" + tSmsend.getDestnbr() + ")(" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ")"); } } // 保存短信发送状态:SGIP返回0,表示短信已成功发送,需将状态设置为1,保存到数据库备查。 tSmsend.setFlag(new Integer(0 == rsp.getBody().getResult() ? 1 : rsp.getBody().getResult())); } // 保存提交短信发送成功的时间。 tSmsend.setFinaldate(new Timestamp(System.currentTimeMillis())); // System.out.println(new String(this.submit.getBody().getMessageContent(), // "UnicodeBigUnmarked")); // 保存短信发送状态 this.tSmsendDao.getHibernateTemplate().update(tSmsend); }