mirror of
https://gitee.com/xchao/j-im.git
synced 2024-12-01 19:37:37 +08:00
调整项目工程结构, 更加友好的支持idea maven多模块开发
This commit is contained in:
parent
6c22c9a30e
commit
7c0b79a0fe
6
jim-client/.gitignore
vendored
6
jim-client/.gitignore
vendored
@ -1,6 +0,0 @@
|
||||
.settings/
|
||||
/target/
|
||||
.idea/
|
||||
*.iml
|
||||
*.classpath
|
||||
*.project
|
@ -1,32 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jim-client</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>jim-client</name>
|
||||
<parent>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-parent</artifactId>
|
||||
<version>3.0.0.v20200101-RELEASE</version>
|
||||
<relativePath>../jim-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<source>${jdk.version}</source>
|
||||
<target>${jdk.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -1,69 +0,0 @@
|
||||
package org.jim.client;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.jim.common.ImConst;
|
||||
import org.tio.client.intf.ClientAioHandler;
|
||||
import org.tio.core.ChannelContext;
|
||||
import org.tio.core.GroupContext;
|
||||
import org.tio.core.exception.ImDecodeException;
|
||||
import org.tio.core.intf.AioHandler;
|
||||
import org.tio.core.intf.Packet;
|
||||
import org.jim.common.packets.Command;
|
||||
import org.jim.common.tcp.TcpPacket;
|
||||
import org.jim.common.tcp.TcpServerDecoder;
|
||||
import org.jim.common.tcp.TcpServerEncoder;
|
||||
/**
|
||||
*
|
||||
* 版本: [1.0]
|
||||
* 功能说明:
|
||||
* 作者: WChao 创建时间: 2017年8月30日 下午1:10:28
|
||||
*/
|
||||
public class HelloClientAioHandler implements AioHandler,ClientAioHandler
|
||||
{
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(Packet packet, ChannelContext channelContext) throws Exception
|
||||
{
|
||||
TcpPacket helloPacket = (TcpPacket)packet;
|
||||
byte[] body = helloPacket.getBody();
|
||||
if (body != null)
|
||||
{
|
||||
String str = new String(body, ImConst.CHARSET);
|
||||
System.out.println("收到消息:" + str);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* 编码:把业务消息包编码为可以发送的ByteBuffer
|
||||
* 总的消息结构:消息头 + 消息体
|
||||
* 消息头结构: 4个字节,存储消息体的长度
|
||||
* 消息体结构: 对象的json串的byte[]
|
||||
*/
|
||||
@Override
|
||||
public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext)
|
||||
{
|
||||
TcpPacket tcpPacket = (TcpPacket)packet;
|
||||
return TcpServerEncoder.encode(tcpPacket, groupContext, channelContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TcpPacket decode(ByteBuffer buffer,int limit, int position, int readableLength,ChannelContext channelContext) throws ImDecodeException {
|
||||
TcpPacket tcpPacket = TcpServerDecoder.decode(buffer, channelContext);
|
||||
return tcpPacket;
|
||||
}
|
||||
|
||||
private static TcpPacket heartbeatPacket = new TcpPacket(Command.COMMAND_HEARTBEAT_REQ,new byte[]{Protocol.HEARTBEAT_BYTE});
|
||||
|
||||
/**
|
||||
* 此方法如果返回null,框架层面则不会发心跳;如果返回非null,框架层面会定时发本方法返回的消息包
|
||||
*/
|
||||
@Override
|
||||
public TcpPacket heartbeatPacket()
|
||||
{
|
||||
return heartbeatPacket;
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package org.jim.client;
|
||||
|
||||
import org.jim.common.ImConst;
|
||||
import org.jim.common.Jim;
|
||||
import org.jim.common.packets.ChatBody;
|
||||
import org.jim.common.packets.Command;
|
||||
import org.jim.common.packets.LoginReqBody;
|
||||
import org.jim.common.tcp.TcpPacket;
|
||||
import org.tio.client.AioClient;
|
||||
import org.tio.client.ClientChannelContext;
|
||||
import org.tio.client.ClientGroupContext;
|
||||
import org.tio.client.ReconnConf;
|
||||
import org.tio.client.intf.ClientAioHandler;
|
||||
import org.tio.client.intf.ClientAioListener;
|
||||
import org.tio.core.Node;
|
||||
/**
|
||||
*
|
||||
* 版本: [1.0]
|
||||
* 功能说明:
|
||||
* 作者: WChao 创建时间: 2017年8月30日 下午1:05:17
|
||||
*/
|
||||
public class HelloClientStarter {
|
||||
/**
|
||||
* 服务器节点
|
||||
*/
|
||||
public static Node serverNode = new Node("localhost", ImConst.SERVER_PORT);
|
||||
/**
|
||||
* handler, 包括编码、解码、消息处理
|
||||
*/
|
||||
public static ClientAioHandler aioClientHandler = new HelloClientAioHandler();
|
||||
|
||||
/**
|
||||
* 事件监听器,可以为null,但建议自己实现该接口,可以参考showcase了解些接口
|
||||
*/
|
||||
public static ClientAioListener aioListener = null;
|
||||
|
||||
/**
|
||||
* 断链后自动连接的,不想自动连接请设为null
|
||||
*/
|
||||
private static ReconnConf reconnConf = new ReconnConf(5000L);
|
||||
|
||||
/**
|
||||
* 一组连接共用的上下文对象
|
||||
*/
|
||||
public static ClientGroupContext clientGroupContext = new ClientGroupContext(aioClientHandler, aioListener, reconnConf);
|
||||
|
||||
public static AioClient aioClient = null;
|
||||
public static ClientChannelContext clientChannelContext = null;
|
||||
|
||||
/**
|
||||
* 启动程序入口
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
//clientGroupContext.setHeartbeatTimeout(org.tio.examples.helloworld.common.Const.TIMEOUT);
|
||||
clientGroupContext.setHeartbeatTimeout(0);
|
||||
aioClient = new AioClient(clientGroupContext);
|
||||
clientChannelContext = aioClient.connect(serverNode);
|
||||
//连上后,发条消息玩玩
|
||||
send();
|
||||
}
|
||||
|
||||
private static void send() throws Exception {
|
||||
byte[] loginBody = new LoginReqBody("hello_client","123").toByte();
|
||||
TcpPacket loginPacket = new TcpPacket(Command.COMMAND_LOGIN_REQ,loginBody);
|
||||
//先登录;
|
||||
Jim.send(clientChannelContext, loginPacket);
|
||||
ChatBody chatBody = ChatBody.newBuilder()
|
||||
.setFrom("hello_client")
|
||||
.setTo("admin")
|
||||
.setMsgType(0)
|
||||
.setChatType(1)
|
||||
.setGroup_id("100")
|
||||
.setContent("Socket普通客户端消息测试!").build();
|
||||
TcpPacket chatPacket = new TcpPacket(Command.COMMAND_CHAT_REQ,chatBody.toByte());
|
||||
Jim.send(clientChannelContext, chatPacket);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package org.jim.client;
|
||||
|
||||
import org.jim.common.ImHandler;
|
||||
import org.jim.common.config.ImConfig;
|
||||
import org.jim.common.listener.ImListener;
|
||||
|
||||
/**
|
||||
* @ClassName ImClientConfig
|
||||
* @Description TODO
|
||||
* @Author WChao
|
||||
* @Date 2020/1/4 10:42
|
||||
* @Version 1.0
|
||||
**/
|
||||
public class ImClientConfig extends ImConfig {
|
||||
|
||||
@Override
|
||||
public ImHandler getImHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImListener getImListener() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
package org.jim.client.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* An SSL/TLS client that connects to a server using its IP address and port.
|
||||
* <p/>
|
||||
* After initialization of a {@link NioSslClient} object, {@link NioSslClient#connect()} should be called,
|
||||
* in order to establish connection with the server.
|
||||
* <p/>
|
||||
* When the connection between the client and the object is established, {@link NioSslClient} provides
|
||||
* a public write and read method, in order to communicate with its peer.
|
||||
*
|
||||
* @author <a href="mailto:alex.a.karnezis@gmail.com">Alex Karnezis</a>
|
||||
*/
|
||||
public class NioSslClient extends NioSslPeer {
|
||||
|
||||
/**
|
||||
* The remote address of the server this client is configured to connect to.
|
||||
*/
|
||||
private String remoteAddress;
|
||||
|
||||
/**
|
||||
* The port of the server this client is configured to connect to.
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* The engine that will be used to encrypt/decrypt data between this client and the server.
|
||||
*/
|
||||
private SSLEngine engine;
|
||||
|
||||
/**
|
||||
* The socket channel that will be used as the transport link between this client and the server.
|
||||
*/
|
||||
private SocketChannel socketChannel;
|
||||
|
||||
|
||||
/**
|
||||
* Initiates the engine to run as a client using peer information, and allocates space for the
|
||||
* buffers that will be used by the engine.
|
||||
*
|
||||
* @param protocol The SSL/TLS protocol to be used. Java 1.6 will only run with up to TLSv1 protocol. Java 1.7 or higher also supports TLSv1.1 and TLSv1.2 protocols.
|
||||
* @param remoteAddress The IP address of the peer.
|
||||
* @param port The peer's port that will be used.
|
||||
* @throws Exception
|
||||
*/
|
||||
public NioSslClient(String protocol, String remoteAddress, int port, String keyStorePath , String keyStorePassword) throws Exception {
|
||||
this.remoteAddress = remoteAddress;
|
||||
this.port = port;
|
||||
|
||||
SSLContext context = SSLContext.getInstance(protocol);
|
||||
//context.init(createKeyManagers("./src/main/resources/client.jks", "storepass", "keypass"), createTrustManagers("./src/main/resources/trustedCerts.jks", "storepass"), new SecureRandom());
|
||||
context.init(createKeyManagers(keyStorePath,keyStorePassword,keyStorePassword), createTrustManagers(keyStorePath,keyStorePassword), new SecureRandom());
|
||||
engine = context.createSSLEngine(remoteAddress, port);
|
||||
engine.setUseClientMode(true);
|
||||
|
||||
SSLSession session = engine.getSession();
|
||||
myAppData = ByteBuffer.allocate(1024);
|
||||
myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
|
||||
peerAppData = ByteBuffer.allocate(1024);
|
||||
peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a socket channel to communicate with the configured server and tries to complete the handshake protocol.
|
||||
*
|
||||
* @return True if client established a connection with the server, false otherwise.
|
||||
* @throws Exception
|
||||
*/
|
||||
public boolean connect() throws Exception {
|
||||
socketChannel = SocketChannel.open();
|
||||
socketChannel.configureBlocking(false);
|
||||
socketChannel.connect(new InetSocketAddress(remoteAddress, port));
|
||||
while (!socketChannel.finishConnect()) {
|
||||
// can do something here...
|
||||
}
|
||||
|
||||
engine.beginHandshake();
|
||||
return doHandshake(socketChannel, engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method to send a message to the server.
|
||||
*
|
||||
* @param message - message to be sent to the server.
|
||||
* @throws IOException if an I/O error occurs to the socket channel.
|
||||
*/
|
||||
public void write(String message) throws IOException {
|
||||
write(socketChannel, engine, message.getBytes());
|
||||
}
|
||||
public void write(byte[] data) throws IOException {
|
||||
write(socketChannel, engine, data);
|
||||
}
|
||||
/**
|
||||
* Implements the write method that sends a message to the server the client is connected to,
|
||||
* but should not be called by the user, since socket channel and engine are inner class' variables.
|
||||
* {@link NioSslClient#write(String)} should be called instead.
|
||||
*
|
||||
* @param message - message to be sent to the server.
|
||||
* @param engine - the engine used for encryption/decryption of the data exchanged between the two peers.
|
||||
* @throws IOException if an I/O error occurs to the socket channel.
|
||||
*/
|
||||
@Override
|
||||
protected void write(SocketChannel socketChannel, SSLEngine engine, byte[] data) throws IOException {
|
||||
|
||||
log.debug("About to write to the server...");
|
||||
|
||||
myAppData.clear();
|
||||
myAppData.put(data);
|
||||
myAppData.flip();
|
||||
while (myAppData.hasRemaining()) {
|
||||
// The loop has a meaning for (outgoing) messages larger than 16KB.
|
||||
// Every wrap call will remove 16KB from the original message and send it to the remote peer.
|
||||
myNetData.clear();
|
||||
SSLEngineResult result = engine.wrap(myAppData, myNetData);
|
||||
switch (result.getStatus()) {
|
||||
case OK:
|
||||
myNetData.flip();
|
||||
while (myNetData.hasRemaining()) {
|
||||
socketChannel.write(myNetData);
|
||||
}
|
||||
log.debug("Message sent to the server: " + data);
|
||||
break;
|
||||
case BUFFER_OVERFLOW:
|
||||
myNetData = enlargePacketBuffer(engine, myNetData);
|
||||
break;
|
||||
case BUFFER_UNDERFLOW:
|
||||
throw new SSLException("Buffer underflow occured after a wrap. I don't think we should ever get here.");
|
||||
case CLOSED:
|
||||
closeConnection(socketChannel, engine);
|
||||
return;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method to try to read from the server.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void read() throws Exception {
|
||||
read(socketChannel, engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will wait for response from the remote peer, until it actually gets something.
|
||||
* Uses {@link SocketChannel#read(ByteBuffer)}, which is non-blocking, and if
|
||||
* it gets nothing from the peer, waits for {@code waitToReadMillis} and tries again.
|
||||
* <p/>
|
||||
* Just like {@link NioSslClient#read(SocketChannel, SSLEngine)} it uses inner class' socket channel
|
||||
* and engine and should not be used by the client. {@link NioSslClient#read()} should be called instead.
|
||||
*
|
||||
* @param message - message to be sent to the server.
|
||||
* @param engine - the engine used for encryption/decryption of the data exchanged between the two peers.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected void read(SocketChannel socketChannel, SSLEngine engine) throws Exception {
|
||||
|
||||
log.debug("About to read from the server...");
|
||||
|
||||
peerNetData.clear();
|
||||
int waitToReadMillis = 50;
|
||||
boolean exitReadLoop = false;
|
||||
while (!exitReadLoop) {
|
||||
int bytesRead = socketChannel.read(peerNetData);
|
||||
if (bytesRead > 0) {
|
||||
peerNetData.flip();
|
||||
while (peerNetData.hasRemaining()) {
|
||||
peerAppData.clear();
|
||||
SSLEngineResult result = engine.unwrap(peerNetData, peerAppData);
|
||||
switch (result.getStatus()) {
|
||||
case OK:
|
||||
peerAppData.flip();
|
||||
log.debug("Server response: " + new String(peerAppData.array()));
|
||||
exitReadLoop = true;
|
||||
break;
|
||||
case BUFFER_OVERFLOW:
|
||||
peerAppData = enlargeApplicationBuffer(engine, peerAppData);
|
||||
break;
|
||||
case BUFFER_UNDERFLOW:
|
||||
peerNetData = handleBufferUnderflow(engine, peerNetData);
|
||||
break;
|
||||
case CLOSED:
|
||||
closeConnection(socketChannel, engine);
|
||||
return;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
|
||||
}
|
||||
}
|
||||
} else if (bytesRead < 0) {
|
||||
handleEndOfStream(socketChannel, engine);
|
||||
return;
|
||||
}
|
||||
Thread.sleep(waitToReadMillis);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called when the client wants to explicitly close the connection to the server.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs to the socket channel.
|
||||
*/
|
||||
public void shutdown() throws IOException {
|
||||
log.debug("About to close connection with the server...");
|
||||
closeConnection(socketChannel, engine);
|
||||
executor.shutdown();
|
||||
log.debug("Goodbye!");
|
||||
}
|
||||
|
||||
}
|
@ -1,380 +0,0 @@
|
||||
package org.jim.client.ssl;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.security.KeyStore;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* A class that represents an SSL/TLS peer, and can be extended to create a client or a server.
|
||||
* <p/>
|
||||
* It makes use of the JSSE framework, and specifically the {@link SSLEngine} logic, which
|
||||
* is described by Oracle as "an advanced API, not appropriate for casual use", since
|
||||
* it requires the user to implement much of the communication establishment procedure himself.
|
||||
* More information about it can be found here: http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SSLEngine
|
||||
* <p/>
|
||||
* {@link NioSslPeer} implements the handshake protocol, required to establish a connection between two peers,
|
||||
* which is common for both client and server and provides the abstract {@link NioSslPeer#read(SocketChannel, SSLEngine)} and
|
||||
* {@link NioSslPeer#write(SocketChannel, SSLEngine, String)} methods, that need to be implemented by the specific SSL/TLS peer
|
||||
* that is going to extend this class.
|
||||
*
|
||||
* @author <a href="mailto:alex.a.karnezis@gmail.com">Alex Karnezis</a>
|
||||
*/
|
||||
public abstract class NioSslPeer {
|
||||
|
||||
/**
|
||||
* Class' logger.
|
||||
*/
|
||||
protected final Logger log = Logger.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* Will contain this peer's application data in plaintext, that will be later encrypted
|
||||
* using {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)} and sent to the other peer. This buffer can typically
|
||||
* be of any size, as long as it is large enough to contain this peer's outgoing messages.
|
||||
* If this peer tries to send a message bigger than buffer's capacity a {@link BufferOverflowException}
|
||||
* will be thrown.
|
||||
*/
|
||||
protected ByteBuffer myAppData;
|
||||
|
||||
/**
|
||||
* Will contain this peer's encrypted data, that will be generated after {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)}
|
||||
* is applied on {@link NioSslPeer#myAppData}. It should be initialized using {@link SSLSession#getPacketBufferSize()},
|
||||
* which returns the size up to which, SSL/TLS packets will be generated from the engine under a session.
|
||||
* All SSLEngine network buffers should be sized at least this large to avoid insufficient space problems when performing wrap and unwrap calls.
|
||||
*/
|
||||
protected ByteBuffer myNetData;
|
||||
|
||||
/**
|
||||
* Will contain the other peer's (decrypted) application data. It must be large enough to hold the application data
|
||||
* from any peer. Can be initialized with {@link SSLSession#getApplicationBufferSize()} for an estimation
|
||||
* of the other peer's application data and should be enlarged if this size is not enough.
|
||||
*/
|
||||
protected ByteBuffer peerAppData;
|
||||
|
||||
/**
|
||||
* Will contain the other peer's encrypted data. The SSL/TLS protocols specify that implementations should produce packets containing at most 16 KB of plaintext,
|
||||
* so a buffer sized to this value should normally cause no capacity problems. However, some implementations violate the specification and generate large records up to 32 KB.
|
||||
* If the {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer)} detects large inbound packets, the buffer sizes returned by SSLSession will be updated dynamically, so the this peer
|
||||
* should check for overflow conditions and enlarge the buffer using the session's (updated) buffer size.
|
||||
*/
|
||||
protected ByteBuffer peerNetData;
|
||||
|
||||
/**
|
||||
* Will be used to execute tasks that may emerge during handshake in parallel with the server's main thread.
|
||||
*/
|
||||
protected ExecutorService executor = new ThreadPoolExecutor(1,1,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
|
||||
|
||||
protected abstract void read(SocketChannel socketChannel, SSLEngine engine) throws Exception;
|
||||
|
||||
protected abstract void write(SocketChannel socketChannel, SSLEngine engine, byte[] data) throws Exception;
|
||||
|
||||
/**
|
||||
* Implements the handshake protocol between two peers, required for the establishment of the SSL/TLS connection.
|
||||
* During the handshake, encryption configuration information - such as the list of available cipher suites - will be exchanged
|
||||
* and if the handshake is successful will lead to an established SSL/TLS session.
|
||||
*
|
||||
* <p/>
|
||||
* A typical handshake will usually contain the following steps:
|
||||
*
|
||||
* <ul>
|
||||
* <li>1. wrap: ClientHello</li>
|
||||
* <li>2. unwrap: ServerHello/Cert/ServerHelloDone</li>
|
||||
* <li>3. wrap: ClientKeyExchange</li>
|
||||
* <li>4. wrap: ChangeCipherSpec</li>
|
||||
* <li>5. wrap: Finished</li>
|
||||
* <li>6. unwrap: ChangeCipherSpec</li>
|
||||
* <li>7. unwrap: Finished</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* Handshake is also used during the end of the session, in order to properly close the connection between the two peers.
|
||||
* A proper connection close will typically include the one peer sending a CLOSE message to another, and then wait for
|
||||
* the other's CLOSE message to close the transport link. The other peer from his perspective would read a CLOSE message
|
||||
* from his peer and then enter the handshake procedure to send his own CLOSE message as well.
|
||||
*
|
||||
* @param socketChannel - the socket channel that connects the two peers.
|
||||
* @param engine - the engine that will be used for encryption/decryption of the data exchanged with the other peer.
|
||||
* @return True if the connection handshake was successful or false if an error occurred.
|
||||
* @throws IOException - if an error occurs during read/write to the socket channel.
|
||||
*/
|
||||
protected boolean doHandshake(SocketChannel socketChannel, SSLEngine engine) throws IOException {
|
||||
|
||||
log.debug("About to do handshake...");
|
||||
|
||||
SSLEngineResult result;
|
||||
HandshakeStatus handshakeStatus;
|
||||
|
||||
// NioSslPeer's fields myAppData and peerAppData are supposed to be large enough to hold all message data the peer
|
||||
// will send and expects to receive from the other peer respectively. Since the messages to be exchanged will usually be less
|
||||
// than 16KB long the capacity of these fields should also be smaller. Here we initialize these two local buffers
|
||||
// to be used for the handshake, while keeping client's buffers at the same size.
|
||||
int appBufferSize = engine.getSession().getApplicationBufferSize();
|
||||
ByteBuffer myAppData = ByteBuffer.allocate(appBufferSize);
|
||||
ByteBuffer peerAppData = ByteBuffer.allocate(appBufferSize);
|
||||
myNetData.clear();
|
||||
peerNetData.clear();
|
||||
|
||||
handshakeStatus = engine.getHandshakeStatus();
|
||||
while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
|
||||
switch (handshakeStatus) {
|
||||
case NEED_UNWRAP:
|
||||
if (socketChannel.read(peerNetData) < 0) {
|
||||
if (engine.isInboundDone() && engine.isOutboundDone()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
engine.closeInbound();
|
||||
} catch (SSLException e) {
|
||||
log.error("This engine was forced to close inbound, without having received the proper SSL/TLS close notification message from the peer, due to end of stream.");
|
||||
}
|
||||
engine.closeOutbound();
|
||||
// After closeOutbound the engine will be set to WRAP state, in order to try to send a close message to the client.
|
||||
handshakeStatus = engine.getHandshakeStatus();
|
||||
break;
|
||||
}
|
||||
peerNetData.flip();
|
||||
try {
|
||||
result = engine.unwrap(peerNetData, peerAppData);
|
||||
peerNetData.compact();
|
||||
handshakeStatus = result.getHandshakeStatus();
|
||||
} catch (SSLException sslException) {
|
||||
log.error("A problem was encountered while processing the data that caused the SSLEngine to abort. Will try to properly close connection...");
|
||||
engine.closeOutbound();
|
||||
handshakeStatus = engine.getHandshakeStatus();
|
||||
break;
|
||||
}
|
||||
switch (result.getStatus()) {
|
||||
case OK:
|
||||
break;
|
||||
case BUFFER_OVERFLOW:
|
||||
// Will occur when peerAppData's capacity is smaller than the data derived from peerNetData's unwrap.
|
||||
peerAppData = enlargeApplicationBuffer(engine, peerAppData);
|
||||
break;
|
||||
case BUFFER_UNDERFLOW:
|
||||
// Will occur either when no data was read from the peer or when the peerNetData buffer was too small to hold all peer's data.
|
||||
peerNetData = handleBufferUnderflow(engine, peerNetData);
|
||||
break;
|
||||
case CLOSED:
|
||||
if (engine.isOutboundDone()) {
|
||||
return false;
|
||||
} else {
|
||||
engine.closeOutbound();
|
||||
handshakeStatus = engine.getHandshakeStatus();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
|
||||
}
|
||||
break;
|
||||
case NEED_WRAP:
|
||||
myNetData.clear();
|
||||
try {
|
||||
result = engine.wrap(myAppData, myNetData);
|
||||
handshakeStatus = result.getHandshakeStatus();
|
||||
} catch (SSLException sslException) {
|
||||
log.error("A problem was encountered while processing the data that caused the SSLEngine to abort. Will try to properly close connection...");
|
||||
engine.closeOutbound();
|
||||
handshakeStatus = engine.getHandshakeStatus();
|
||||
break;
|
||||
}
|
||||
switch (result.getStatus()) {
|
||||
case OK :
|
||||
myNetData.flip();
|
||||
while (myNetData.hasRemaining()) {
|
||||
socketChannel.write(myNetData);
|
||||
}
|
||||
break;
|
||||
case BUFFER_OVERFLOW:
|
||||
// Will occur if there is not enough space in myNetData buffer to write all the data that would be generated by the method wrap.
|
||||
// Since myNetData is set to session's packet size we should not get to this point because SSLEngine is supposed
|
||||
// to produce messages smaller or equal to that, but a general handling would be the following:
|
||||
myNetData = enlargePacketBuffer(engine, myNetData);
|
||||
break;
|
||||
case BUFFER_UNDERFLOW:
|
||||
throw new SSLException("Buffer underflow occured after a wrap. I don't think we should ever get here.");
|
||||
case CLOSED:
|
||||
try {
|
||||
myNetData.flip();
|
||||
while (myNetData.hasRemaining()) {
|
||||
socketChannel.write(myNetData);
|
||||
}
|
||||
// At this point the handshake status will probably be NEED_UNWRAP so we make sure that peerNetData is clear to read.
|
||||
peerNetData.clear();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to send server's CLOSE message due to socket channel's failure.");
|
||||
handshakeStatus = engine.getHandshakeStatus();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
|
||||
}
|
||||
break;
|
||||
case NEED_TASK:
|
||||
Runnable task;
|
||||
while ((task = engine.getDelegatedTask()) != null) {
|
||||
executor.execute(task);
|
||||
}
|
||||
handshakeStatus = engine.getHandshakeStatus();
|
||||
break;
|
||||
case FINISHED:
|
||||
break;
|
||||
case NOT_HANDSHAKING:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SSL status: " + handshakeStatus);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
protected ByteBuffer enlargePacketBuffer(SSLEngine engine, ByteBuffer buffer) {
|
||||
return enlargeBuffer(buffer, engine.getSession().getPacketBufferSize());
|
||||
}
|
||||
|
||||
protected ByteBuffer enlargeApplicationBuffer(SSLEngine engine, ByteBuffer buffer) {
|
||||
return enlargeBuffer(buffer, engine.getSession().getApplicationBufferSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares <code>sessionProposedCapacity<code> with buffer's capacity. If buffer's capacity is smaller,
|
||||
* returns a buffer with the proposed capacity. If it's equal or larger, returns a buffer
|
||||
* with capacity twice the size of the initial one.
|
||||
*
|
||||
* @param buffer - the buffer to be enlarged.
|
||||
* @param sessionProposedCapacity - the minimum size of the new buffer, proposed by {@link SSLSession}.
|
||||
* @return A new buffer with a larger capacity.
|
||||
*/
|
||||
protected ByteBuffer enlargeBuffer(ByteBuffer buffer, int sessionProposedCapacity) {
|
||||
if (sessionProposedCapacity > buffer.capacity()) {
|
||||
buffer = ByteBuffer.allocate(sessionProposedCapacity);
|
||||
} else {
|
||||
buffer = ByteBuffer.allocate(buffer.capacity() * 2);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles {@link SSLEngineResult.Status#BUFFER_UNDERFLOW}. Will check if the buffer is already filled, and if there is no space problem
|
||||
* will return the same buffer, so the client tries to read again. If the buffer is already filled will try to enlarge the buffer either to
|
||||
* session's proposed size or to a larger capacity. A buffer underflow can happen only after an unwrap, so the buffer will always be a
|
||||
* peerNetData buffer.
|
||||
*
|
||||
* @param buffer - will always be peerNetData buffer.
|
||||
* @param engine - the engine used for encryption/decryption of the data exchanged between the two peers.
|
||||
* @return The same buffer if there is no space problem or a new buffer with the same data but more space.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected ByteBuffer handleBufferUnderflow(SSLEngine engine, ByteBuffer buffer) {
|
||||
if (engine.getSession().getPacketBufferSize() < buffer.limit()) {
|
||||
return buffer;
|
||||
} else {
|
||||
ByteBuffer replaceBuffer = enlargePacketBuffer(engine, buffer);
|
||||
buffer.flip();
|
||||
replaceBuffer.put(buffer);
|
||||
return replaceBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be called when this peer wants to explicitly close the connection
|
||||
* or when a close message has arrived from the other peer, in order to provide an orderly shutdown.
|
||||
* <p/>
|
||||
* It first calls {@link SSLEngine#closeOutbound()} which prepares this peer to send its own close message and
|
||||
* sets {@link SSLEngine} to the <code>NEED_WRAP</code> state. Then, it delegates the exchange of close messages
|
||||
* to the handshake method and finally, it closes socket channel.
|
||||
*
|
||||
* @param socketChannel - the transport link used between the two peers.
|
||||
* @param engine - the engine used for encryption/decryption of the data exchanged between the two peers.
|
||||
* @throws IOException if an I/O error occurs to the socket channel.
|
||||
*/
|
||||
protected void closeConnection(SocketChannel socketChannel, SSLEngine engine) throws IOException {
|
||||
engine.closeOutbound();
|
||||
doHandshake(socketChannel, engine);
|
||||
socketChannel.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* In addition to orderly shutdowns, an unorderly shutdown may occur, when the transport link (socket channel)
|
||||
* is severed before close messages are exchanged. This may happen by getting an -1 or {@link IOException}
|
||||
* when trying to read from the socket channel, or an {@link IOException} when trying to write to it.
|
||||
* In both cases {@link SSLEngine#closeInbound()} should be called and then try to follow the standard procedure.
|
||||
*
|
||||
* @param socketChannel - the transport link used between the two peers.
|
||||
* @param engine - the engine used for encryption/decryption of the data exchanged between the two peers.
|
||||
* @throws IOException if an I/O error occurs to the socket channel.
|
||||
*/
|
||||
protected void handleEndOfStream(SocketChannel socketChannel, SSLEngine engine) throws IOException {
|
||||
try {
|
||||
engine.closeInbound();
|
||||
} catch (Exception e) {
|
||||
log.error("This engine was forced to close inbound, without having received the proper SSL/TLS close notification message from the peer, due to end of stream.");
|
||||
}
|
||||
closeConnection(socketChannel, engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the key managers required to initiate the {@link SSLContext}, using a JKS keystore as an input.
|
||||
*
|
||||
* @param filepath - the path to the JKS keystore.
|
||||
* @param keystorePassword - the keystore's password.
|
||||
* @param keyPassword - the key's passsword.
|
||||
* @return {@link KeyManager} array that will be used to initiate the {@link SSLContext}.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected KeyManager[] createKeyManagers(String filepath, String keystorePassword, String keyPassword) throws Exception {
|
||||
KeyStore keyStore = KeyStore.getInstance("JKS");
|
||||
InputStream keyStoreIS = new FileInputStream(filepath);
|
||||
try {
|
||||
keyStore.load(keyStoreIS, keystorePassword.toCharArray());
|
||||
} finally {
|
||||
if (keyStoreIS != null) {
|
||||
keyStoreIS.close();
|
||||
}
|
||||
}
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(keyStore, keyPassword.toCharArray());
|
||||
return kmf.getKeyManagers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the trust managers required to initiate the {@link SSLContext}, using a JKS keystore as an input.
|
||||
*
|
||||
* @param filepath - the path to the JKS keystore.
|
||||
* @param keystorePassword - the keystore's password.
|
||||
* @return {@link TrustManager} array, that will be used to initiate the {@link SSLContext}.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected TrustManager[] createTrustManagers(String filepath, String keystorePassword) throws Exception {
|
||||
KeyStore trustStore = KeyStore.getInstance("JKS");
|
||||
InputStream trustStoreIS = new FileInputStream(filepath);
|
||||
try {
|
||||
trustStore.load(trustStoreIS, keystorePassword.toCharArray());
|
||||
} finally {
|
||||
if (trustStoreIS != null) {
|
||||
trustStoreIS.close();
|
||||
}
|
||||
}
|
||||
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustFactory.init(trustStore);
|
||||
return trustFactory.getTrustManagers();
|
||||
}
|
||||
|
||||
}
|
@ -1,257 +0,0 @@
|
||||
package org.jim.client.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* An SSL/TLS server, that will listen to a specific address and port and serve SSL/TLS connections
|
||||
* compatible with the protocol it applies.
|
||||
* <p/>
|
||||
* After initialization {@link NioSslServer#start()} should be called so the server starts to listen to
|
||||
* new connection requests. At this point, start is blocking, so, in order to be able to gracefully stop
|
||||
* the server, a {@link Runnable} containing a server object should be created. This runnable should
|
||||
* start the server in its run method and also provide a stop method, which will call {@link NioSslServer#stop()}.
|
||||
* </p>
|
||||
* NioSslServer makes use of Java NIO, and specifically listens to new connection requests with a {@link ServerSocketChannel}, which will
|
||||
* create new {@link SocketChannel}s and a {@link Selector} which serves all the connections in one thread.
|
||||
*
|
||||
* @author <a href="mailto:alex.a.karnezis@gmail.com">Alex Karnezis</a>
|
||||
*/
|
||||
public class NioSslServer extends NioSslPeer {
|
||||
|
||||
/**
|
||||
* Declares if the server is active to serve and create new connections.
|
||||
*/
|
||||
private boolean active;
|
||||
|
||||
/**
|
||||
* The context will be initialized with a specific SSL/TLS protocol and will then be used
|
||||
* to create {@link SSLEngine} classes for each new connection that arrives to the server.
|
||||
*/
|
||||
private SSLContext context;
|
||||
|
||||
/**
|
||||
* A part of Java NIO that will be used to serve all connections to the server in one thread.
|
||||
*/
|
||||
private Selector selector;
|
||||
|
||||
|
||||
/**
|
||||
* Server is designed to apply an SSL/TLS protocol and listen to an IP address and port.
|
||||
*
|
||||
* @param protocol - the SSL/TLS protocol that this server will be configured to apply.
|
||||
* @param hostAddress - the IP address this server will listen to.
|
||||
* @param port - the port this server will listen to.
|
||||
* @throws Exception
|
||||
*/
|
||||
public NioSslServer(String protocol, String hostAddress, int port, String keyStorePath , String keyStorePassword) throws Exception {
|
||||
|
||||
context = SSLContext.getInstance(protocol);
|
||||
//context.init(createKeyManagers("./src/main/resources/server.jks", "storepass", "keypass"), createTrustManagers("./src/main/resources/trustedCerts.jks", "storepass"), new SecureRandom());
|
||||
context.init(createKeyManagers(keyStorePath, keyStorePassword, keyStorePassword), createTrustManagers(keyStorePath, keyStorePassword), new SecureRandom());
|
||||
|
||||
SSLSession dummySession = context.createSSLEngine().getSession();
|
||||
myAppData = ByteBuffer.allocate(dummySession.getApplicationBufferSize());
|
||||
myNetData = ByteBuffer.allocate(dummySession.getPacketBufferSize());
|
||||
peerAppData = ByteBuffer.allocate(dummySession.getApplicationBufferSize());
|
||||
peerNetData = ByteBuffer.allocate(dummySession.getPacketBufferSize());
|
||||
dummySession.invalidate();
|
||||
|
||||
selector = SelectorProvider.provider().openSelector();
|
||||
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
|
||||
serverSocketChannel.configureBlocking(false);
|
||||
serverSocketChannel.socket().bind(new InetSocketAddress(hostAddress, port));
|
||||
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
|
||||
|
||||
active = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called in order the server to start listening to new connections.
|
||||
* This method will run in a loop as long as the server is active. In order to stop the server
|
||||
* you should use {@link NioSslServer#stop()} which will set it to inactive state
|
||||
* and also wake up the listener, which may be in blocking select() state.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void start() throws Exception {
|
||||
|
||||
log.debug("Initialized and waiting for new connections...");
|
||||
|
||||
while (isActive()) {
|
||||
selector.select();
|
||||
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
|
||||
while (selectedKeys.hasNext()) {
|
||||
SelectionKey key = selectedKeys.next();
|
||||
selectedKeys.remove();
|
||||
if (!key.isValid()) {
|
||||
continue;
|
||||
}
|
||||
if (key.isAcceptable()) {
|
||||
accept(key);
|
||||
} else if (key.isReadable()) {
|
||||
read((SocketChannel) key.channel(), (SSLEngine) key.attachment());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Goodbye!");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server to an inactive state, in order to exit the reading loop in {@link NioSslServer#start()}
|
||||
* and also wakes up the selector, which may be in select() blocking state.
|
||||
*/
|
||||
public void stop() {
|
||||
log.debug("Will now close server...");
|
||||
active = false;
|
||||
executor.shutdown();
|
||||
selector.wakeup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called after a new connection request arrives to the server. Creates the {@link SocketChannel} that will
|
||||
* be used as the network layer link, and the {@link SSLEngine} that will encrypt and decrypt all the data
|
||||
* that will be exchanged during the session with this specific client.
|
||||
*
|
||||
* @param key - the key dedicated to the {@link ServerSocketChannel} used by the server to listen to new connection requests.
|
||||
* @throws Exception
|
||||
*/
|
||||
private void accept(SelectionKey key) throws Exception {
|
||||
|
||||
log.debug("New connection request!");
|
||||
|
||||
SocketChannel socketChannel = ((ServerSocketChannel) key.channel()).accept();
|
||||
socketChannel.configureBlocking(false);
|
||||
|
||||
SSLEngine engine = context.createSSLEngine();
|
||||
engine.setUseClientMode(false);
|
||||
engine.beginHandshake();
|
||||
|
||||
if (doHandshake(socketChannel, engine)) {
|
||||
socketChannel.register(selector, SelectionKey.OP_READ, engine);
|
||||
} else {
|
||||
socketChannel.close();
|
||||
log.debug("Connection closed due to handshake failure.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called by the selector when the specific socket channel has data to be read.
|
||||
* As soon as the server reads these data, it will call {@link NioSslServer#write(SocketChannel, SSLEngine, String)}
|
||||
* to send back a trivial response.
|
||||
*
|
||||
* @param socketChannel - the transport link used between the two peers.
|
||||
* @param engine - the engine used for encryption/decryption of the data exchanged between the two peers.
|
||||
* @throws IOException if an I/O error occurs to the socket channel.
|
||||
*/
|
||||
@Override
|
||||
protected void read(SocketChannel socketChannel, SSLEngine engine) throws IOException {
|
||||
|
||||
log.debug("About to read from a client...");
|
||||
|
||||
peerNetData.clear();
|
||||
int bytesRead = socketChannel.read(peerNetData);
|
||||
if (bytesRead > 0) {
|
||||
peerNetData.flip();
|
||||
while (peerNetData.hasRemaining()) {
|
||||
peerAppData.clear();
|
||||
SSLEngineResult result = engine.unwrap(peerNetData, peerAppData);
|
||||
switch (result.getStatus()) {
|
||||
case OK:
|
||||
peerAppData.flip();
|
||||
log.debug("Incoming message: " + new String(peerAppData.array()));
|
||||
break;
|
||||
case BUFFER_OVERFLOW:
|
||||
peerAppData = enlargeApplicationBuffer(engine, peerAppData);
|
||||
break;
|
||||
case BUFFER_UNDERFLOW:
|
||||
peerNetData = handleBufferUnderflow(engine, peerNetData);
|
||||
break;
|
||||
case CLOSED:
|
||||
log.debug("Client wants to close connection...");
|
||||
closeConnection(socketChannel, engine);
|
||||
log.debug("Goodbye client!");
|
||||
return;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
write(socketChannel, engine, "Hello! I am your server!".getBytes());
|
||||
|
||||
} else if (bytesRead < 0) {
|
||||
log.error("Received end of stream. Will try to close connection with client...");
|
||||
handleEndOfStream(socketChannel, engine);
|
||||
log.debug("Goodbye client!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will send a message back to a client.
|
||||
*
|
||||
* @param key - the key dedicated to the socket channel that will be used to write to the client.
|
||||
* @param message - the message to be sent.
|
||||
* @throws IOException if an I/O error occurs to the socket channel.
|
||||
*/
|
||||
@Override
|
||||
protected void write(SocketChannel socketChannel, SSLEngine engine, byte[] data) throws IOException {
|
||||
|
||||
log.debug("About to write to a client...");
|
||||
|
||||
myAppData.clear();
|
||||
myAppData.put(data);
|
||||
myAppData.flip();
|
||||
while (myAppData.hasRemaining()) {
|
||||
// The loop has a meaning for (outgoing) messages larger than 16KB.
|
||||
// Every wrap call will remove 16KB from the original message and send it to the remote peer.
|
||||
myNetData.clear();
|
||||
SSLEngineResult result = engine.wrap(myAppData, myNetData);
|
||||
switch (result.getStatus()) {
|
||||
case OK:
|
||||
myNetData.flip();
|
||||
while (myNetData.hasRemaining()) {
|
||||
socketChannel.write(myNetData);
|
||||
}
|
||||
log.debug("Message sent to the client: " + data);
|
||||
break;
|
||||
case BUFFER_OVERFLOW:
|
||||
myNetData = enlargePacketBuffer(engine, myNetData);
|
||||
break;
|
||||
case BUFFER_UNDERFLOW:
|
||||
throw new SSLException("Buffer underflow occured after a wrap. I don't think we should ever get here.");
|
||||
case CLOSED:
|
||||
closeConnection(socketChannel, engine);
|
||||
return;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the the server is active or not.
|
||||
*
|
||||
* @return if the server is active or not.
|
||||
*/
|
||||
private boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
}
|
@ -1 +0,0 @@
|
||||
本包代码摘自https://github.com/alkarn/sslengine.example改造而来,谢谢源作者的付出!
|
Binary file not shown.
Binary file not shown.
@ -1,16 +0,0 @@
|
||||
#http://logback.qos.ch/manual/configuration.html
|
||||
#<include resource="includedConfig.xml"/> resource, file, url (被包含的文件需要满足一定格式)
|
||||
|
||||
context.name=jim-client
|
||||
|
||||
log.dir=/logs/jim-client
|
||||
|
||||
rolling.policy.file.name.pattern=yyyy-MM-dd HH
|
||||
max.file.size=100MB
|
||||
max.history=50
|
||||
|
||||
conversion.pattern=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{30}[%line]: %m%n
|
||||
root.level=debug
|
||||
|
||||
|
||||
|
@ -1,129 +0,0 @@
|
||||
<configuration scan="true" scanPeriod="10 seconds" debug="true">
|
||||
<property resource="logback.properties" />
|
||||
|
||||
<contextName>${context.name}</contextName> <!-- 本项目的名字 -->
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${conversion.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- root file 日志 -->
|
||||
<appender name="root-file-error"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.dir}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.dir}/error.%d{${rolling.policy.file.name.pattern}}%d{mmss}.%i.log.zip</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>${max.file.size}</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>${max.history}</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${conversion.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
<appender name="root-file-warn"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.dir}/warn.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.dir}/warn.%d{${rolling.policy.file.name.pattern}}%d{mmss}.%i.log.zip</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>${max.file.size}</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>${max.history}</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${conversion.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>warn</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
<appender name="root-file-info"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.dir}/info.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.dir}/info.%d{${rolling.policy.file.name.pattern}}%d{mmss}.%i.log.zip</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>${max.file.size}</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>${max.history}</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${conversion.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>INFO</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
<appender name="root-file-debug"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.dir}/debug.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.dir}/debug.%d{${rolling.policy.file.name.pattern}}%d{mmss}.%i.log.zip</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>${max.file.size}</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>${max.history}</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${conversion.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>debug</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
<root level="${root.level}">
|
||||
<appender-ref ref="console" />
|
||||
<appender-ref ref="root-file-error"/>
|
||||
<appender-ref ref="root-file-warn"/>
|
||||
<appender-ref ref="root-file-info"/>
|
||||
<appender-ref ref="root-file-debug"/>
|
||||
</root>
|
||||
|
||||
|
||||
|
||||
<!-- 跟踪客户端行为 -->
|
||||
<appender name="tio-client-trace-log-appender" class="ch.qos.logback.classic.sift.SiftingAppender">
|
||||
<discriminator>
|
||||
<Key>tio_client</Key>
|
||||
<DefaultValue>unknown</DefaultValue>
|
||||
</discriminator>
|
||||
|
||||
<sift>
|
||||
<appender name="${tio_client}" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.dir}/tio/client-trace/${tio_client}_%d{yyyyMMdd}.log</fileNamePattern>
|
||||
<maxFileSize>20MB</maxFileSize>
|
||||
</rollingPolicy>
|
||||
<Append>false</Append>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>%m%n</pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
</sift>
|
||||
</appender>
|
||||
|
||||
<logger name="tio-client-trace-log" additivity="false">
|
||||
<level value="info"/>
|
||||
<appender-ref ref="tio-client-trace-log-appender"/>
|
||||
</logger>
|
||||
|
||||
</configuration>
|
Binary file not shown.
Binary file not shown.
@ -1,41 +0,0 @@
|
||||
package org.jim.client.example.ssl;
|
||||
|
||||
import org.jim.client.ssl.NioSslServer;
|
||||
|
||||
/**
|
||||
* This class provides a runnable that can be used to initialize a {@link NioSslServer} thread.
|
||||
* <p/>
|
||||
* Run starts the server, which will start listening to the configured IP address and port for
|
||||
* new SSL/TLS connections and serve the ones already connected to it.
|
||||
* <p/>
|
||||
* Also a stop method is provided in order to gracefully close the server and stop the thread.
|
||||
*
|
||||
* @author <a href="mailto:alex.a.karnezis@gmail.com">Alex Karnezis</a>
|
||||
*/
|
||||
public class ServerRunnable implements Runnable {
|
||||
|
||||
NioSslServer server;
|
||||
public ServerRunnable(String protocol, String hostAddress, int port, String keyStorePath , String keyStorePassword) {
|
||||
try {
|
||||
server = new NioSslServer(protocol,hostAddress, port,keyStorePath,keyStorePassword);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
server.start();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called in order to gracefully stop the server.
|
||||
*/
|
||||
public void stop() {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package org.jim.client.example.ssl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.jim.client.ssl.NioSslClient;
|
||||
import org.jim.common.packets.ChatBody;
|
||||
import org.jim.common.packets.Command;
|
||||
import org.jim.common.packets.LoginReqBody;
|
||||
import org.jim.common.tcp.TcpPacket;
|
||||
import org.jim.common.tcp.TcpServerEncoder;
|
||||
|
||||
public class SslDemoStarter {
|
||||
|
||||
ServerRunnable serverRunnable;
|
||||
|
||||
public SslDemoStarter() {
|
||||
serverRunnable = new ServerRunnable("TLSv1.2", "localhost", 9222,"./src/main/resources/keystore.jks","214323428310224");
|
||||
Thread server = new Thread(serverRunnable);
|
||||
server.start();
|
||||
}
|
||||
|
||||
public void runDemo() throws Exception {
|
||||
|
||||
NioSslClient client = new NioSslClient("TLSv1.2", "localhost", 9222,"./src/main/resources/keystore.jks","214323428310224");
|
||||
client.connect();
|
||||
byte[] loginBody = new LoginReqBody("hello_client","123").toByte();
|
||||
TcpPacket loginPacket = new TcpPacket(Command.COMMAND_LOGIN_REQ,loginBody);
|
||||
ByteBuffer loginByteBuffer = TcpServerEncoder.encode(loginPacket, null, null);
|
||||
client.write(loginByteBuffer.array());
|
||||
ChatBody chatBody = ChatBody.newBuilder()
|
||||
.setFrom("hello_client")
|
||||
.setTo("admin")
|
||||
.setMsgType(0)
|
||||
.setChatType(1)
|
||||
.setGroup_id("100")
|
||||
.setContent("Socket普通客户端消息测试!").build();
|
||||
TcpPacket chatPacket = new TcpPacket(Command.COMMAND_CHAT_REQ,chatBody.toByte());
|
||||
ByteBuffer chatByteBuffer = TcpServerEncoder.encode(chatPacket,null, null);
|
||||
client.write(chatByteBuffer.array());
|
||||
client.read();
|
||||
client.shutdown();
|
||||
|
||||
/*NioSslClient client2 = new NioSslClient("TLSv1.2", "localhost", 9222);
|
||||
NioSslClient client3 = new NioSslClient("TLSv1.2", "localhost", 9222);
|
||||
NioSslClient client4 = new NioSslClient("TLSv1.2", "localhost", 9222);
|
||||
|
||||
client2.connect();
|
||||
client2.write("Hello! I am another client!");
|
||||
client2.read();
|
||||
client2.shutdown();
|
||||
|
||||
client3.connect();
|
||||
client4.connect();
|
||||
client3.write("Hello from client3!!!");
|
||||
client4.write("Hello from client4!!!");
|
||||
client3.read();
|
||||
client4.read();
|
||||
client3.shutdown();
|
||||
client4.shutdown();*/
|
||||
|
||||
serverRunnable.stop();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SslDemoStarter demo = new SslDemoStarter();
|
||||
Thread.sleep(1000); // Give the server some time to start.
|
||||
demo.runDemo();
|
||||
}
|
||||
|
||||
}
|
@ -8,10 +8,8 @@
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-parent</artifactId>
|
||||
<version>3.0.0.v20200101-RELEASE</version>
|
||||
<relativePath>../jim-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.t-io</groupId>
|
||||
<artifactId>tio-core</artifactId>
|
||||
|
6
jim-parent/.gitignore
vendored
6
jim-parent/.gitignore
vendored
@ -1,6 +0,0 @@
|
||||
.settings/
|
||||
/target/
|
||||
.idea/
|
||||
*.iml
|
||||
*.classpath
|
||||
*.project
|
@ -1,359 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>3.0.0.v20200101-RELEASE</version>
|
||||
<description>Jim-parent is the dependent parent project of J-IM communication establishment.</description>
|
||||
<url>http://www.j-im.org/</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>wchao</id>
|
||||
<name>wchao</name>
|
||||
<email>wchaojava@163.com</email>
|
||||
<url>http://git.oschina.net/xchao/j-im</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git@gitee.com:xchao/j-im.git</connection>
|
||||
<developerConnection>scm:git:git@gitee.com:xchao/j-im.git</developerConnection>
|
||||
<url>git@gitee.com:xchao/j-im.git</url>
|
||||
</scm>
|
||||
<modules>
|
||||
<module>../jim-client</module>
|
||||
<module>../jim-common</module>
|
||||
<module>../jim-server</module>
|
||||
<module>../jim-server-demo</module>
|
||||
</modules>
|
||||
<properties>
|
||||
<jim.version>3.0.0.v20200101-RELEASE</jim.version>
|
||||
<tio-core.version>3.5.8.v20191228-RELEASE</tio-core.version>
|
||||
<tio-utils.version>3.5.8.v20191228-RELEASE</tio-utils.version>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.test.skip>true</maven.test.skip>
|
||||
<maven.version>3.5.0</maven.version>
|
||||
|
||||
<mysql.driver.version>5.1.40</mysql.driver.version>
|
||||
<druid.version>1.1.2</druid.version>
|
||||
<logback.version>1.2.3</logback.version>
|
||||
<slf4j.version>1.7.25</slf4j.version>
|
||||
<jdk.version>1.8</jdk.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<commons-collections4.version>4.1</commons-collections4.version>
|
||||
<commons-io.version>2.5</commons-io.version>
|
||||
<commons-lang3.version>3.6</commons-lang3.version>
|
||||
<commons-codec.version>1.10</commons-codec.version>
|
||||
<commons-compress.version>1.14</commons-compress.version>
|
||||
<fastjson.version>1.2.35</fastjson.version>
|
||||
<testng.version>6.11</testng.version>
|
||||
<hutool.version>4.0.10</hutool.version>
|
||||
<commons-beanutils.version>1.9.3</commons-beanutils.version>
|
||||
<jedis.version>2.7.3</jedis.version>
|
||||
<redisson.version>3.7.0</redisson.version>
|
||||
<j2cache.version>2.3.21-release</j2cache.version>
|
||||
<guava.version>23.0</guava.version>
|
||||
</properties>
|
||||
<repositories>
|
||||
|
||||
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
|
||||
</pluginRepositories>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.t-io</groupId>
|
||||
<artifactId>tio-utils</artifactId>
|
||||
<version>${tio-utils.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.t-io</groupId>
|
||||
<artifactId>tio-core</artifactId>
|
||||
<version>${tio-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-common</artifactId>
|
||||
<version>${jim.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-server</artifactId>
|
||||
<version>${jim.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-client</artifactId>
|
||||
<version>${jim.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>${jedis.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>${commons-beanutils.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql.driver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache-core</artifactId>
|
||||
<version>2.6.11</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- log framework start -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-ext</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<!-- redirect apache commons logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<!-- redirect jdk util logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<!-- redirect log4j -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<!-- log framework end -->
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- logback-access访问模块与Servlet容器集成提供通过Http来访问日记的功能 -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-access</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>${commons-collections4.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${commons-codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>${testng.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>${commons-compress.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.10.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springside</groupId>
|
||||
<artifactId>springside-utils</artifactId>
|
||||
<version>5.0.0-RC1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nl.basjes.parse.useragent</groupId>
|
||||
<artifactId>yauaa</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.oschina.j2cache</groupId>
|
||||
<artifactId>j2cache-core</artifactId>
|
||||
<version>${j2cache.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<!-- 插件配置 -->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
<!-- 适配idea下生成不了配置文件问题 -->
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>oss</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>oss</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Source -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<configuration><additionalparam>-Xdoclint:none</additionalparam></configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- GPG -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
@ -7,7 +7,6 @@
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-parent</artifactId>
|
||||
<version>3.0.0.v20200101-RELEASE</version>
|
||||
<relativePath>../jim-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
@ -8,7 +8,6 @@
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-parent</artifactId>
|
||||
<version>3.0.0.v20200101-RELEASE</version>
|
||||
<relativePath>../jim-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
355
pom.xml
Normal file
355
pom.xml
Normal file
@ -0,0 +1,355 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>3.0.0.v20200101-RELEASE</version>
|
||||
<description>Jim-parent is the dependent parent project of J-IM communication establishment.</description>
|
||||
<url>http://www.j-im.org/</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>wchao</id>
|
||||
<name>wchao</name>
|
||||
<email>wchaojava@163.com</email>
|
||||
<url>http://git.oschina.net/xchao/j-im</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git@gitee.com:xchao/j-im.git</connection>
|
||||
<developerConnection>scm:git:git@gitee.com:xchao/j-im.git</developerConnection>
|
||||
<url>git@gitee.com:xchao/j-im.git</url>
|
||||
</scm>
|
||||
<modules>
|
||||
<module>jim-common</module>
|
||||
<module>jim-server</module>
|
||||
<module>jim-server-demo</module>
|
||||
</modules>
|
||||
<properties>
|
||||
<jim.version>3.0.0.v20200101-RELEASE</jim.version>
|
||||
<tio-core.version>3.5.8.v20191228-RELEASE</tio-core.version>
|
||||
<tio-utils.version>3.5.8.v20191228-RELEASE</tio-utils.version>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.test.skip>true</maven.test.skip>
|
||||
<maven.version>3.5.0</maven.version>
|
||||
|
||||
<mysql.driver.version>5.1.40</mysql.driver.version>
|
||||
<druid.version>1.1.2</druid.version>
|
||||
<logback.version>1.2.3</logback.version>
|
||||
<slf4j.version>1.7.25</slf4j.version>
|
||||
<jdk.version>1.8</jdk.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<commons-collections4.version>4.1</commons-collections4.version>
|
||||
<commons-io.version>2.5</commons-io.version>
|
||||
<commons-lang3.version>3.6</commons-lang3.version>
|
||||
<commons-codec.version>1.10</commons-codec.version>
|
||||
<commons-compress.version>1.14</commons-compress.version>
|
||||
<fastjson.version>1.2.35</fastjson.version>
|
||||
<testng.version>6.11</testng.version>
|
||||
<hutool.version>4.0.10</hutool.version>
|
||||
<commons-beanutils.version>1.9.3</commons-beanutils.version>
|
||||
<jedis.version>2.7.3</jedis.version>
|
||||
<redisson.version>3.7.0</redisson.version>
|
||||
<j2cache.version>2.3.21-release</j2cache.version>
|
||||
<guava.version>23.0</guava.version>
|
||||
</properties>
|
||||
<repositories>
|
||||
|
||||
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
|
||||
</pluginRepositories>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.t-io</groupId>
|
||||
<artifactId>tio-utils</artifactId>
|
||||
<version>${tio-utils.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.t-io</groupId>
|
||||
<artifactId>tio-core</artifactId>
|
||||
<version>${tio-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-common</artifactId>
|
||||
<version>${jim.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.j-im</groupId>
|
||||
<artifactId>jim-server</artifactId>
|
||||
<version>${jim.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>${jedis.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>${commons-beanutils.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql.driver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache-core</artifactId>
|
||||
<version>2.6.11</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- log framework start -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-ext</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<!-- redirect apache commons logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<!-- redirect jdk util logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<!-- redirect log4j -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<!-- log framework end -->
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- logback-access访问模块与Servlet容器集成提供通过Http来访问日记的功能 -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-access</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>${commons-collections4.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${commons-codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>${testng.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>${commons-compress.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.10.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springside</groupId>
|
||||
<artifactId>springside-utils</artifactId>
|
||||
<version>5.0.0-RC1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nl.basjes.parse.useragent</groupId>
|
||||
<artifactId>yauaa</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.oschina.j2cache</groupId>
|
||||
<artifactId>j2cache-core</artifactId>
|
||||
<version>${j2cache.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<!-- 插件配置 -->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
<!-- 适配idea下生成不了配置文件问题 -->
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>oss</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>oss</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Source -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<configuration>
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- GPG -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
Loading…
Reference in New Issue
Block a user