大重构:weixin4j精简为weixin4j-base、weixin4j-mp、weixin4j-qy、weixin4j-server四个子工程
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
|
||||
<id>bin</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<outputDirectory>/lib</outputDirectory>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>com/foxinmy/weixin4j</directory>
|
||||
<includes>
|
||||
<include>**/*.md</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>src/main</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>*.sh</include>
|
||||
<include>*.bat</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>src/main/resources</directory>
|
||||
<outputDirectory>/conf</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.foxinmy.weixin4j.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 微信账号信息
|
||||
*
|
||||
* @className WeixinAccount
|
||||
* @author jy
|
||||
* @date 2014年11月18日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class WeixinAccount implements Serializable {
|
||||
private static final long serialVersionUID = -6001008896414323534L;
|
||||
|
||||
/**
|
||||
* 唯一的身份标识
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 调用接口的密钥
|
||||
*/
|
||||
private String secret;
|
||||
private String token;
|
||||
/**
|
||||
* 安全模式下的加密密钥
|
||||
*/
|
||||
private String encodingAesKey;
|
||||
|
||||
public WeixinAccount() {
|
||||
}
|
||||
|
||||
public WeixinAccount(String id, String secret) {
|
||||
this.id = id;
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getEncodingAesKey() {
|
||||
return encodingAesKey;
|
||||
}
|
||||
|
||||
public void setEncodingAesKey(String encodingAesKey) {
|
||||
this.encodingAesKey = encodingAesKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "id=" + id + ", secret=" + secret + ", token=" + token
|
||||
+ ", encodingAesKey=" + encodingAesKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
WeixinMessageDecoder:对微信消息进行解码
|
||||
|
||||
WeixinMessageEncoder:对微信消息进行编码
|
||||
|
||||
WeixinServerHandler:微信请求处理类
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.message.HttpWeixinMessage;
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
import com.foxinmy.weixin4j.type.EncryptType;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
|
||||
/**
|
||||
* 微信消息解码类
|
||||
*
|
||||
* @className WeixinMessageDecoder
|
||||
* @author jy
|
||||
* @date 2014年11月13日
|
||||
* @since JDK 1.7
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/0/61c3a8b9d50ac74f18bdf2e54ddfc4e0.html">加密接入指引</a>
|
||||
*/
|
||||
public class WeixinMessageDecoder extends
|
||||
MessageToMessageDecoder<FullHttpRequest> {
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req,
|
||||
List<Object> out) throws Exception {
|
||||
String content = req.content().toString(Consts.UTF_8);
|
||||
HttpWeixinMessage message = new HttpWeixinMessage();
|
||||
if (!content.isEmpty()) {
|
||||
// TODO
|
||||
}
|
||||
message.setMethod(req.getMethod().name());
|
||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
|
||||
true);
|
||||
log.info("\n=================receive request=================");
|
||||
log.info("{}", req.getMethod());
|
||||
log.info("{}", req.getUri());
|
||||
log.info("{}", content);
|
||||
Map<String, List<String>> parameters = queryDecoder.parameters();
|
||||
String encryptType = parameters.containsKey("encrypt_type") ? parameters
|
||||
.get("encrypt_type").get(0) : EncryptType.RAW.name();
|
||||
message.setEncryptType(EncryptType.valueOf(encryptType.toUpperCase()));
|
||||
String echoStr = parameters.containsKey("echostr") ? parameters.get(
|
||||
"echostr").get(0) : "";
|
||||
message.setEchoStr(echoStr);
|
||||
String timeStamp = parameters.containsKey("timestamp") ? parameters
|
||||
.get("timestamp").get(0) : "";
|
||||
message.setTimeStamp(timeStamp);
|
||||
String nonce = parameters.containsKey("nonce") ? parameters
|
||||
.get("nonce").get(0) : "";
|
||||
message.setNonce(nonce);
|
||||
String signature = parameters.containsKey("signature") ? parameters
|
||||
.get("signature").get(0) : "";
|
||||
message.setSignature(signature);
|
||||
|
||||
message.setOriginalContent(content);
|
||||
if (message.getEncryptType() == EncryptType.AES) {
|
||||
WeixinAccount mpAccount = ConfigUtil.getWeixinAccount();
|
||||
message.setOriginalContent(MessageUtil.aesDecrypt(
|
||||
mpAccount.getId(), mpAccount.getEncodingAesKey(),
|
||||
message.getEncryptContent()));
|
||||
}
|
||||
out.add(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.message.HttpWeixinMessage;
|
||||
|
||||
/**
|
||||
* 微信被动消息处理类
|
||||
*
|
||||
* @className WeixinServerHandler
|
||||
* @author jy
|
||||
* @date 2014年11月16日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class WeixinMessageHandler extends
|
||||
SimpleChannelInboundHandler<HttpWeixinMessage> {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
log.error("catch the exception:{}", cause.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx,
|
||||
HttpWeixinMessage httpMessage) throws Exception {
|
||||
|
||||
String xmlContent = httpMessage.getOriginalContent();
|
||||
|
||||
|
||||
/*
|
||||
log.info("\n=================message in=================\n{}",
|
||||
httpMessage);
|
||||
boolean isGet = httpMessage.getMethod().equals(HttpMethod.GET.name());
|
||||
boolean validate = false;
|
||||
if (isGet || httpMessage.getEncryptType() == EncryptType.RAW) {
|
||||
validate = MessageUtil.signature(httpMessage.getToken(),
|
||||
httpMessage.getTimeStamp(), httpMessage.getNonce()).equals(
|
||||
httpMessage.getSignature());
|
||||
if (isGet && validate) {
|
||||
ctx.write(HttpUtil.createWeixinMessageResponse(
|
||||
httpMessage.getEchoStr(), ContentType.TEXT_PLAIN));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
validate = MessageUtil.signature(httpMessage.getToken(),
|
||||
httpMessage.getTimeStamp(), httpMessage.getNonce(),
|
||||
httpMessage.getEncryptContent()).equals(
|
||||
httpMessage.getSignature());
|
||||
}
|
||||
if (!validate) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.FORBIDDEN));
|
||||
return;
|
||||
}
|
||||
if (action == null) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
ResponseMessage response = action.execute(xmlContent);
|
||||
log.info("\n=================message out=================\n{}",
|
||||
response);
|
||||
if (response == null) {
|
||||
ctx.write(HttpUtil.createWeixinMessageResponse("",
|
||||
ContentType.TEXT_PLAIN));
|
||||
return;
|
||||
}
|
||||
if (httpMessage.getEncryptType() == EncryptType.RAW) {
|
||||
ctx.write(HttpUtil.createWeixinMessageResponse(response.toXml(),
|
||||
null));
|
||||
} else {
|
||||
ctx.write(response);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
|
||||
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel channel) throws Exception {
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||
pipeline.addLast(new WeixinMessageDecoder());
|
||||
//pipeline.addLast(new WeixinMessageEncoder());
|
||||
pipeline.addLast(new WeixinMessageHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.foxinmy.weixin4j.startup;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import com.foxinmy.weixin4j.server.WeixinServerInitializer;
|
||||
|
||||
/**
|
||||
* 微信netty服务启动程序
|
||||
*
|
||||
* @className WeixinServerBootstrap
|
||||
* @author jy
|
||||
* @date 2014年10月12日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public final class WeixinServerBootstrap {
|
||||
|
||||
private final static int port;
|
||||
private final static int workerThreads;
|
||||
static {
|
||||
ResourceBundle netty = ResourceBundle.getBundle("netty");
|
||||
port = Integer.parseInt(netty.getString("port"));
|
||||
workerThreads = Integer.parseInt(netty.getString("workerThreads"));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.option(ChannelOption.SO_BACKLOG, 1024);
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler())
|
||||
.childHandler(new WeixinServerInitializer());
|
||||
Channel ch = b.bind(port).sync().channel();
|
||||
System.err.println("weixin server startup OK:" + port);
|
||||
ch.closeFuture().sync();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
|
||||
/**
|
||||
* 商户配置工具类
|
||||
*
|
||||
* @className ConfigUtil
|
||||
* @author jy
|
||||
* @date 2014年10月31日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class ConfigUtil {
|
||||
private final static String CLASSPATH_PREFIX = "classpath:";
|
||||
private final static String CLASSPATH_VALUE;
|
||||
private final static ResourceBundle weixinBundle;
|
||||
static {
|
||||
weixinBundle = ResourceBundle.getBundle("weixin");
|
||||
Set<String> keySet = weixinBundle.keySet();
|
||||
File file = null;
|
||||
CLASSPATH_VALUE = Thread.currentThread().getContextClassLoader()
|
||||
.getResource("").getPath();
|
||||
for (String key : keySet) {
|
||||
if (!key.endsWith("_path")) {
|
||||
continue;
|
||||
}
|
||||
file = new File(getValue(key).replaceFirst(CLASSPATH_PREFIX,
|
||||
CLASSPATH_VALUE));
|
||||
if (!file.exists() && !file.mkdirs()) {
|
||||
System.err.append(String.format("%s create fail.%n",
|
||||
file.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取weixin.properties文件中的key值
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String getValue(String key) {
|
||||
return weixinBundle.getString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断属性是否存在[classpath:]如果存在则拼接项目路径后返回 一般用于文件的绝对路径获取
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String getClassPathValue(String key) {
|
||||
return new File(getValue(key).replaceFirst(CLASSPATH_PREFIX,
|
||||
CLASSPATH_VALUE)).getPath();
|
||||
}
|
||||
|
||||
public static WeixinAccount getWeixinAccount() {
|
||||
String text = getValue("account");
|
||||
return JSON.parseObject(text, WeixinAccount.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 常量类
|
||||
*
|
||||
* @className Consts
|
||||
* @author jy
|
||||
* @date 2015年4月19日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public final class Consts {
|
||||
|
||||
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
public static final Charset GBK = Charset.forName("GBK");
|
||||
public static final String SUCCESS = "SUCCESS";
|
||||
public static final String FAIL = "FAIL";
|
||||
public static final String SunX509 = "SunX509";
|
||||
public static final String JKS = "JKS";
|
||||
public static final String PKCS12 = "PKCS12";
|
||||
public static final String TLS = "TLS";
|
||||
public static final String X509 = "X.509";
|
||||
public static final String AES = "AES";
|
||||
public static final String CONTENTTYPE$APPLICATION_XML = "application/xml";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public final class DigestUtil {
|
||||
|
||||
public static String SHA1(String decript) {
|
||||
try {
|
||||
MessageDigest digest = java.security.MessageDigest
|
||||
.getInstance("SHA-1");
|
||||
digest.update(decript.getBytes());
|
||||
byte messageDigest[] = digest.digest();
|
||||
// Create Hex String
|
||||
StringBuffer hexString = new StringBuffer();
|
||||
// 字节数组转换为 十六进制 数
|
||||
for (int i = 0; i < messageDigest.length; i++) {
|
||||
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
|
||||
if (shaHex.length() < 2) {
|
||||
hexString.append(0);
|
||||
}
|
||||
hexString.append(shaHex);
|
||||
}
|
||||
return hexString.toString();
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String SHA(String decript) {
|
||||
try {
|
||||
MessageDigest digest = java.security.MessageDigest
|
||||
.getInstance("SHA");
|
||||
digest.update(decript.getBytes());
|
||||
byte messageDigest[] = digest.digest();
|
||||
// Create Hex String
|
||||
StringBuffer hexString = new StringBuffer();
|
||||
// 字节数组转换为 十六进制 数
|
||||
for (int i = 0; i < messageDigest.length; i++) {
|
||||
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
|
||||
if (shaHex.length() < 2) {
|
||||
hexString.append(0);
|
||||
}
|
||||
hexString.append(shaHex);
|
||||
}
|
||||
return hexString.toString();
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String MD5(String input) {
|
||||
try {
|
||||
// 获得MD5摘要算法的 MessageDigest 对象
|
||||
MessageDigest mdInst = MessageDigest.getInstance("MD5");
|
||||
// 使用指定的字节更新摘要
|
||||
mdInst.update(input.getBytes());
|
||||
// 获得密文
|
||||
byte[] md = mdInst.digest();
|
||||
// 把密文转换成十六进制的字符串形式
|
||||
StringBuffer hexString = new StringBuffer();
|
||||
// 字节数组转换为 十六进制 数
|
||||
for (int i = 0; i < md.length; i++) {
|
||||
String shaHex = Integer.toHexString(md[i] & 0xFF);
|
||||
if (shaHex.length() < 2) {
|
||||
hexString.append(0);
|
||||
}
|
||||
hexString.append(shaHex);
|
||||
}
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.DATE;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.SERVER;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* HTTP工具类
|
||||
*
|
||||
* @className HttpUtil
|
||||
* @author jy
|
||||
* @date 2014年11月15日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class HttpUtil {
|
||||
|
||||
public static HttpResponse createWeixinMessageResponse(String content,
|
||||
String contentType) {
|
||||
FullHttpResponse httpResponse = new DefaultFullHttpResponse(HTTP_1_1,
|
||||
OK, Unpooled.copiedBuffer(content, Consts.UTF_8));
|
||||
httpResponse.headers().set(
|
||||
CONTENT_TYPE,
|
||||
String.format("%s;encoding=%s", contentType,
|
||||
Consts.UTF_8.displayName()));
|
||||
httpResponse.headers().set(CONTENT_LENGTH,
|
||||
content.getBytes(Consts.UTF_8).length);
|
||||
httpResponse.headers().set(CONNECTION, Values.KEEP_ALIVE);
|
||||
httpResponse.headers().set(DATE, new Date());
|
||||
httpResponse.headers().set(SERVER, "netty4");
|
||||
return httpResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import io.netty.handler.codec.base64.Base64;
|
||||
import io.netty.handler.codec.base64.Base64Encoder;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* 消息工具类
|
||||
*
|
||||
* @className MessageUtil
|
||||
* @author jy
|
||||
* @date 2014年10月31日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class MessageUtil {
|
||||
|
||||
/**
|
||||
* 验证微信签名
|
||||
*
|
||||
* @param signature
|
||||
* 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数
|
||||
* @return 开发者通过检验signature对请求进行相关校验。若确认此次GET请求来自微信服务器
|
||||
* 请原样返回echostr参数内容,则接入生效 成为开发者成功,否则接入失败
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/0/61c3a8b9d50ac74f18bdf2e54ddfc4e0.html">接入指南</a>
|
||||
*/
|
||||
public static String signature(String... para) {
|
||||
Arrays.sort(para);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String str : para) {
|
||||
sb.append(str);
|
||||
}
|
||||
return DigestUtil.SHA1(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 对xml消息加密
|
||||
*
|
||||
* @param appId
|
||||
* 应用ID
|
||||
* @param encodingAesKey
|
||||
* 加密密钥
|
||||
* @param xmlContent
|
||||
* 原始消息体
|
||||
* @return aes加密后的消息体
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public static String aesEncrypt(String appId, String encodingAesKey,
|
||||
String xmlContent) throws RuntimeException {
|
||||
byte[] randomBytes = RandomUtil.generateString(16).getBytes(
|
||||
Consts.UTF_8);
|
||||
byte[] xmlBytes = xmlContent.getBytes(Consts.UTF_8);
|
||||
int xmlLength = xmlBytes.length;
|
||||
byte[] orderBytes = new byte[4];
|
||||
orderBytes[3] = (byte) (xmlLength & 0xFF);
|
||||
orderBytes[2] = (byte) (xmlLength >> 8 & 0xFF);
|
||||
orderBytes[1] = (byte) (xmlLength >> 16 & 0xFF);
|
||||
orderBytes[0] = (byte) (xmlLength >> 24 & 0xFF);
|
||||
byte[] appidBytes = appId.getBytes(Consts.UTF_8);
|
||||
int byteLength = randomBytes.length + xmlLength + orderBytes.length
|
||||
+ appidBytes.length;
|
||||
// ... + pad: 使用自定义的填充方式对明文进行补位填充
|
||||
byte[] padBytes = PKCS7Encoder.encode(byteLength);
|
||||
// random + endian + xml + appid + pad 获得最终的字节流
|
||||
byte[] unencrypted = new byte[byteLength + padBytes.length];
|
||||
byteLength = 0;
|
||||
// src:源数组;srcPos:源数组要复制的起始位置;dest:目的数组;destPos:目的数组放置的起始位置;length:复制的长度
|
||||
System.arraycopy(randomBytes, 0, unencrypted, byteLength,
|
||||
randomBytes.length);
|
||||
byteLength += randomBytes.length;
|
||||
System.arraycopy(orderBytes, 0, unencrypted, byteLength,
|
||||
orderBytes.length);
|
||||
byteLength += orderBytes.length;
|
||||
System.arraycopy(xmlBytes, 0, unencrypted, byteLength, xmlBytes.length);
|
||||
byteLength += xmlBytes.length;
|
||||
System.arraycopy(appidBytes, 0, unencrypted, byteLength,
|
||||
appidBytes.length);
|
||||
byteLength += appidBytes.length;
|
||||
System.arraycopy(padBytes, 0, unencrypted, byteLength, padBytes.length);
|
||||
try {
|
||||
byte[] aesKey = Base64.de(encodingAesKey + "=");
|
||||
// 设置加密模式为AES的CBC模式
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, Consts.AES);
|
||||
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
|
||||
// 加密
|
||||
byte[] encrypted = cipher.doFinal(unencrypted);
|
||||
// 使用BASE64对加密后的字符串进行编码
|
||||
return Base64.(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("-40006,AES加密失败:", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对AES消息解密
|
||||
*
|
||||
* @param appId
|
||||
* @param encodingAesKey
|
||||
* aes加密的密钥
|
||||
* @param encryptContent
|
||||
* 加密的消息体
|
||||
* @return 解密后的字符
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public static String aesDecrypt(String appId, String encodingAesKey,
|
||||
String encryptContent) throws RuntimeException {
|
||||
byte[] aesKey = Base64..decodeBase64(encodingAesKey + "=");
|
||||
byte[] original;
|
||||
try {
|
||||
// 设置解密模式为AES的CBC模式
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec key_spec = new SecretKeySpec(aesKey, Consts.AES);
|
||||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey,
|
||||
0, 16));
|
||||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
|
||||
// 使用BASE64对密文进行解码
|
||||
byte[] encrypted = Base64.decodeBase64(encryptContent);
|
||||
// 解密
|
||||
original = cipher.doFinal(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("-40007,AES解密失败:", e);
|
||||
}
|
||||
String xmlContent, fromAppId;
|
||||
try {
|
||||
// 去除补位字符
|
||||
byte[] bytes = PKCS7Encoder.decode(original);
|
||||
// 获取表示xml长度的字节数组
|
||||
byte[] lengthByte = Arrays.copyOfRange(bytes, 16, 20);
|
||||
// 获取xml消息主体的长度(byte[]2int)
|
||||
// http://my.oschina.net/u/169390/blog/97495
|
||||
int xmlLength = lengthByte[3] & 0xff | (lengthByte[2] & 0xff) << 8
|
||||
| (lengthByte[1] & 0xff) << 16
|
||||
| (lengthByte[0] & 0xff) << 24;
|
||||
xmlContent = new String(Arrays.copyOfRange(bytes, 20,
|
||||
20 + xmlLength), Consts.UTF_8);
|
||||
fromAppId = new String(Arrays.copyOfRange(bytes, 20 + xmlLength,
|
||||
bytes.length), Consts.UTF_8);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("-40008,公众平台发送的xml不合法:" + e.getMessage());
|
||||
}
|
||||
// 校验appId是否一致
|
||||
if (!fromAppId.trim().equals(appId)) {
|
||||
throw new RuntimeException("-40005,校验AppID失败");
|
||||
}
|
||||
return xmlContent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 对公众平台发送给公众账号的消息加解密示例代码.
|
||||
*
|
||||
* @copyright Copyright (c) 1998-2014 Tencent Inc.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 提供基于PKCS7算法的加解密接口</br>
|
||||
* 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串).
|
||||
* <ol>
|
||||
* <li>第三方回复加密消息给公众平台</li>
|
||||
* <li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li>
|
||||
* </ol>
|
||||
* 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案
|
||||
* <ol>
|
||||
* <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:
|
||||
* http://www.oracle.com/technetwork/java/javase
|
||||
* /downloads/jce-7-download-432124.html</li>
|
||||
* <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
|
||||
* <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li>
|
||||
* <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class PKCS7Encoder {
|
||||
private final static int BLOCK_SIZE = 32;
|
||||
|
||||
/**
|
||||
* 获得对明文进行补位填充的字节.
|
||||
*
|
||||
* @param count
|
||||
* 需要进行填充补位操作的明文字节个数
|
||||
* @return 补齐用的字节数组
|
||||
*/
|
||||
public static byte[] encode(int count) {
|
||||
// 计算需要填充的位数
|
||||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
|
||||
if (amountToPad == 0) {
|
||||
amountToPad = BLOCK_SIZE;
|
||||
}
|
||||
// 获得补位所用的字符
|
||||
byte target = (byte) (amountToPad & 0xFF);
|
||||
char padChr = (char) target;
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
for (int index = 0; index < amountToPad; index++) {
|
||||
tmp.append(padChr);
|
||||
}
|
||||
return tmp.toString().getBytes(Consts.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除解密后明文的补位字符
|
||||
*
|
||||
* @param decrypted
|
||||
* 解密后的明文
|
||||
* @return 删除补位字符后的明文
|
||||
*/
|
||||
public static byte[] decode(byte[] decrypted) {
|
||||
int pad = (int) decrypted[decrypted.length - 1];
|
||||
if (pad < 1 || pad > 32) {
|
||||
pad = 0;
|
||||
}
|
||||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 随机码工具类
|
||||
*
|
||||
* @className RandomUtil
|
||||
* @author jy
|
||||
* @date 2014年10月22日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class RandomUtil {
|
||||
|
||||
private static final String ALLCHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String LETTERCHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String NUMBERCHAR = "0123456789";
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机字符串(包含数字和大小写字母)
|
||||
*
|
||||
* @param length
|
||||
* 随机数的长度
|
||||
* @return
|
||||
*/
|
||||
public static String generateString(int length) {
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机纯数字字符串(只包含数字)
|
||||
*
|
||||
* @param length
|
||||
* 随机数的长度
|
||||
* @return
|
||||
*/
|
||||
public static String generateStringByNumberChar(int length) {
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(NUMBERCHAR.charAt(random.nextInt(NUMBERCHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机纯字母字符串(只包含大小写字母)
|
||||
*
|
||||
* @param length
|
||||
* 随机数的长度
|
||||
* @return
|
||||
*/
|
||||
public static String generateStringByLetterCharr(int length) {
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(LETTERCHAR.charAt(random.nextInt(LETTERCHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机纯大写字母字符串(只包含大小写字母)
|
||||
*
|
||||
* @param length
|
||||
* 随机数的长度
|
||||
* @return
|
||||
*/
|
||||
public static String generateLowerString(int length) {
|
||||
return generateStringByLetterCharr(length).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机纯小写字母字符串(只包含大小写字母)
|
||||
*
|
||||
* @param length
|
||||
* 随机数的长度
|
||||
* @return
|
||||
*/
|
||||
public static String generateUpperString(int length) {
|
||||
return generateStringByLetterCharr(length).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获取UUID字符串(无中划线)
|
||||
*
|
||||
* @return UUID字符串
|
||||
*/
|
||||
public static String getUUID() {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
return uuid.substring(0, 8) + uuid.substring(9, 13)
|
||||
+ uuid.substring(14, 18) + uuid.substring(19, 23)
|
||||
+ uuid.substring(24);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(System.nanoTime());
|
||||
System.out.println(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# \u516c\u4f17\u53f7\u4fe1\u606f
|
||||
account={"id":"appid","secret":"appsecret",\
|
||||
"token":"\u5f00\u653e\u8005\u7684token",\
|
||||
"encodingAesKey":"\u516c\u4f17\u53f7\u8bbe\u7f6e\u4e86\u52a0\u5bc6\u65b9\u5f0f\u4e14\u4e3a\u300c\u5b89\u5168\u6a21\u5f0f\u300d\u65f6\u9700\u8981\u586b\u5165",\
|
||||
"mchId":"V3.x\u7248\u672c\u4e0b\u7684\u5fae\u4fe1\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
|
||||
"version":\u6570\u5b57\u7c7b\u578b(2\u6216\u80053):\u5fae\u4fe1\u652f\u4ed8\u7684\u7248\u672c,\u5927\u6982\u57282014-09-14\u4e4b\u524d\u7533\u8bf7\u5e76\u4e14\u901a\u8fc7\u7684\u516c\u4f17\u53f7\u4e3aV2,\u5728\u8fd9\u4e4b\u540e\u5219\u4e3aV3 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165,\
|
||||
"partnerId":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
|
||||
"partnerKey":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
|
||||
"paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165"}
|
||||
|
||||
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84
|
||||
token_path=/tmp/weixin/token
|
||||
# \u4e8c\u7ef4\u7801\u4fdd\u5b58\u8def\u5f84
|
||||
qr_path=/tmp/weixin/qr
|
||||
# \u5a92\u4f53\u6587\u4ef6\u4fdd\u5b58\u8def\u5f84
|
||||
media_path=/tmp/weixin/media
|
||||
# \u5bf9\u8d26\u5355\u4fdd\u5b58\u8def\u5f84
|
||||
bill_path=/tmp/weixin/bill
|
||||
# ca\u8bc1\u4e66\u5b58\u653e\u7684\u5b8c\u6574\u8def\u5f84 (V2\u7248\u672c\u540e\u7f00\u4e3a*.pfx,V3\u7248\u672c\u540e\u7f00\u4e3a*.p12)
|
||||
ca_file=/tmp/weixin/xxxxx.p12
|
||||
# classpath\u8def\u5f84\u4e0b\u53ef\u4ee5\u8fd9\u4e48\u5199
|
||||
# ca_file=classpath:xxxxx.pfx
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- For assistance related to logback-translator or configuration -->
|
||||
<!-- files in general, please contact the logback user mailing list -->
|
||||
<!-- at http://www.qos.ch/mailman/listinfo/logback-user -->
|
||||
<!-- -->
|
||||
<!-- For professional support please see -->
|
||||
<!-- http://www.qos.ch/shop/products/professionalSupport -->
|
||||
<!-- -->
|
||||
<configuration>
|
||||
|
||||
<!-- 控制台输出日志 -->
|
||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 文件输出指定项目日志 -->
|
||||
<appender name="file"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender -->
|
||||
<!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy -->
|
||||
<!--for further documentation -->
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>/tmp/weixin/log/server/weixin.server.%d{yyyy-MM-dd}.log
|
||||
</fileNamePattern>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 异步输出指定项目日志 -->
|
||||
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
|
||||
<queueSize>512</queueSize>
|
||||
<!-- 添加附加的appender,最多只能添加一个 -->
|
||||
<appender-ref ref="file" />
|
||||
</appender>
|
||||
|
||||
<logger name="org.apache" level="INFO">
|
||||
<appender-ref ref="stdout" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework" level="INFO">
|
||||
<appender-ref ref="stdout" />
|
||||
</logger>
|
||||
|
||||
<logger name="com.foxinmy.weixin4j" level="INFO">
|
||||
<appender-ref ref="async" />
|
||||
</logger>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,4 @@
|
||||
# netty\u76d1\u542c\u7aef\u53e3
|
||||
port=8080
|
||||
# \u5de5\u4f5c\u8fdb\u7a0b\u6570
|
||||
workerThreads=20
|
||||
@@ -0,0 +1,5 @@
|
||||
# \u6d4b\u8bd5\u4e4b\u7528 \u6b63\u5f0f\u73af\u5883\u4e0bcopy\u4e00\u4efd\u5230classpath
|
||||
# \u516c\u4f17\u53f7\u4fe1\u606f
|
||||
account={"id":"wx4ab8f8de58159a57","secret":"1d4eb0f4bf556aaed539f30ed05ca795",\
|
||||
"token":"\u5f00\u653e\u8005\u7684token",\
|
||||
"encodingAesKey":"\u516c\u4f17\u53f7\u8bbe\u7f6e\u4e86\u52a0\u5bc6\u65b9\u5f0f\u4e14\u4e3a\u300c\u5b89\u5168\u6a21\u5f0f\u300d\u65f6\u9700\u8981\u586b\u5165"}
|
||||
@@ -0,0 +1,142 @@
|
||||
ulimit -n 110000
|
||||
#JDK home
|
||||
JAVA_HOME="/usr/local/java/"
|
||||
|
||||
#executing user
|
||||
RUNNING_USER=root
|
||||
|
||||
#Run home
|
||||
APP_HOME="/usr/local/weixin/weixin-mp-server"
|
||||
|
||||
#main class
|
||||
APP_MAINCLASS=com.foxinmy.weixin4j.startup.WeixinServerBootstrap
|
||||
|
||||
#classpath
|
||||
CLASSPATH=$APP_HOME/classes
|
||||
for i in "$APP_HOME"/lib/*.jar; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
|
||||
CLASSPATH="$CLASSPATH":"$APP_HOME"/conf
|
||||
|
||||
#jvm options
|
||||
JAVA_OPTS="-Xms256m -Xmx512m -Djava.awt.headless=true -XX:MaxPermSize=128m -server -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+DisableExplicitGC -Xnoclassgc -Xverify:none"
|
||||
|
||||
#psid
|
||||
psid=0
|
||||
|
||||
checkpid() {
|
||||
javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAINCLASS`
|
||||
|
||||
if [ -n "$javaps" ]; then
|
||||
psid=`echo $javaps | awk '{print $1}'`
|
||||
else
|
||||
psid=0
|
||||
fi
|
||||
}
|
||||
|
||||
###################################
|
||||
#startup
|
||||
###################################
|
||||
start() {
|
||||
checkpid
|
||||
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo "====================================================="
|
||||
echo "warn: $APP_MAINCLASS already started! (pid=$psid)"
|
||||
echo "====================================================="
|
||||
else
|
||||
echo -n "Starting $APP_MAINCLASS ..."
|
||||
# JAVA_CMD="nohup $JAVA_HOME/bin/java $JAVA_OPTS -classpath $CLASSPATH $APP_MAINCLASS >/dev/null 2>&1 &"
|
||||
JAVA_CMD="$JAVA_HOME/bin/java $JAVA_OPTS -classpath $CLASSPATH $APP_MAINCLASS &"
|
||||
su - $RUNNING_USER -c "$JAVA_CMD"
|
||||
checkpid
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo "(pid=$psid) [OK]"
|
||||
else
|
||||
echo "[Failed]"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
###################################
|
||||
#stop
|
||||
###################################
|
||||
stop() {
|
||||
checkpid
|
||||
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo -n "Stopping $APP_MAINCLASS ...(pid=$psid) "
|
||||
su - $RUNNING_USER -c "kill -9 $psid"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "[OK]"
|
||||
else
|
||||
echo "[Failed]"
|
||||
fi
|
||||
|
||||
checkpid
|
||||
if [ $psid -ne 0 ]; then
|
||||
stop
|
||||
fi
|
||||
else
|
||||
echo "====================================================="
|
||||
echo "warn: $APP_MAINCLASS is not running"
|
||||
echo "====================================================="
|
||||
fi
|
||||
}
|
||||
|
||||
###################################
|
||||
#status
|
||||
###################################
|
||||
status() {
|
||||
checkpid
|
||||
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo "$APP_MAINCLASS is running! (pid=$psid)"
|
||||
else
|
||||
echo "$APP_MAINCLASS is not running"
|
||||
fi
|
||||
}
|
||||
|
||||
###################################
|
||||
#info
|
||||
###################################
|
||||
info() {
|
||||
echo "System Information:"
|
||||
echo "****************************"
|
||||
echo `head -n 1 /etc/issue`
|
||||
echo `uname -a`
|
||||
echo
|
||||
echo "JAVA_HOME=$JAVA_HOME"
|
||||
echo `$JAVA_HOME/bin/java -version`
|
||||
echo
|
||||
echo "APP_HOME=$APP_HOME"
|
||||
echo "APP_MAINCLASS=$APP_MAINCLASS"
|
||||
echo "****************************"
|
||||
}
|
||||
|
||||
###################################
|
||||
#access only 1 argument:{start|stop|restart|status|info}
|
||||
###################################
|
||||
case "$1" in
|
||||
'start')
|
||||
start
|
||||
;;
|
||||
'stop')
|
||||
stop
|
||||
;;
|
||||
'restart')
|
||||
stop
|
||||
start
|
||||
;;
|
||||
'status')
|
||||
status
|
||||
;;
|
||||
'info')
|
||||
info
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status|info}"
|
||||
exit 1
|
||||
esac
|
||||
exit 0
|
||||
Reference in New Issue
Block a user