调整项目工程结构, 更加友好的支持idea maven多模块开发

This commit is contained in:
Taylor Swift 2020-04-07 11:16:00 +08:00
parent 6c22c9a30e
commit 7c0b79a0fe
25 changed files with 355 additions and 1697 deletions

View File

@ -1,6 +0,0 @@
.settings/
/target/
.idea/
*.iml
*.classpath
*.project

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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!");
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -1 +0,0 @@
本包代码摘自https://github.com/alkarn/sslengine.example改造而来谢谢源作者的付出

View File

@ -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

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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>

View File

@ -1,6 +0,0 @@
.settings/
/target/
.idea/
*.iml
*.classpath
*.project

View File

@ -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>

View File

@ -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>

View File

@ -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
View 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>