/*
* <p>
* 版权: ©2011
* </p>
*/
package org.young.isocket.server;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import org.glassfish.grizzly.filterchain.FilterChainBuilder;
import org.glassfish.grizzly.filterchain.TransportFilter;
import org.glassfish.grizzly.monitoring.jmx.AbstractJmxMonitoringConfig;
import org.glassfish.grizzly.monitoring.jmx.GrizzlyJmxManager;
import org.glassfish.grizzly.monitoring.jmx.JmxObject;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder;
import org.glassfish.grizzly.ssl.SSLContextConfigurator;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.ssl.SSLFilter;
import org.glassfish.grizzly.strategies.SameThreadIOStrategy;
import org.glassfish.grizzly.utils.StringFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.young.icore.annotation.PropertiesAnnotation;
import org.young.icore.util.ClassLoaderUtils;
import org.young.icore.util.PropertiesLoaderUtils;
import org.young.isocket.client.NIOSocketClient;
import org.young.isocket.filter.ServerAuthFilter;
import org.young.isocket.filter.ServerStringDataFilter;
import org.young.isocket.filter.ThreadPoolFilter;
import org.young.isocket.jmx.SocketServerJMX;
import org.young.isocket.jmx.SocketServerProbe;
import org.young.isocket.service.ServiceRequest;
import org.young.isocket.service.ServiceResponse;
import org.young.isocket.threadpool.JobDispatcher;
import org.young.isocket.util.SocketKeys;
/**
* <p>
* 描述:非阻塞的Socket Server
* </p>
*
* @see
* @author yangjun2
* @email yangjun1120@gmail.com
*
*/
public class NIOSocketServer extends AbstractLifeCycle {
private static final Logger logger = LoggerFactory.getLogger(NIOSocketServer.class);
private final Object _joinLock = new Object();
//TODO 修改为properties文件读取.
@PropertiesAnnotation(name = "serverhost", resource = "isocket-server.properties")
private String host = "localhost";
@PropertiesAnnotation(name = "serverport", resource = "isocket-server.properties")
private int port = 7777;
@PropertiesAnnotation(name = "serverkeepalive", resource = "isocket-server.properties")
private boolean keepAlive;
@PropertiesAnnotation(name = "serverlinger", resource = "isocket-server.properties")
private int linger = 0;
@PropertiesAnnotation(name = "serverreuseaddress", resource = "isocket-server.properties")
private boolean reuseAddress;
@PropertiesAnnotation(name = "serverbacklog", resource = "isocket-server.properties")
private int backLog = 0;
@PropertiesAnnotation(name = "servertimeout", resource = "isocket-server.properties")
private int serverSocketTimeout;
@PropertiesAnnotation(name = "servertcpnodelay", resource = "isocket-server.properties")
private boolean tcpNoDelay = false;
@PropertiesAnnotation(name = "needauth", resource = "isocket-server.properties")
private boolean needAuth = true;
@PropertiesAnnotation(name = "needssl", resource = "isocket-server.properties")
private boolean needSSL = false;
@PropertiesAnnotation(name = "ssltruststorefile", resource = "isocket-server.properties")
private String trustStoreFile;
@PropertiesAnnotation(name = "ssltruststorepassword", resource = "isocket-server.properties")
private String trustStorePassword;
@PropertiesAnnotation(name = "sslkeystorefile", resource = "isocket-server.properties")
private String keyStoreFile;
@PropertiesAnnotation(name = "sslkeystorepassword", resource = "isocket-server.properties")
private String keyStorePassword;
@PropertiesAnnotation(name = "sslneedclientmode", resource = "isocket-server.properties")
private boolean needClientMode = false;
@PropertiesAnnotation(name = "sslwantclientmode", resource = "isocket-server.properties")
private boolean wantClientMode = false;
@PropertiesAnnotation(name = "serverreadbuffer", resource = "isocket-server.properties")
private int readBufferSize;
@PropertiesAnnotation(name = "serverwritebuffer", resource = "isocket-server.properties")
private int writeBufferSize;
private final static NIOSocketServer nioSocketServer = new NIOSocketServer();
private TCPNIOTransport transport;
private JobDispatcher jobDispatcher;
//private FilterChainBuilder filterChainBuilder;
private TCPNIOTransportBuilder tcpNIOTransportBuilder;
public boolean isNeedClientMode() {
return needClientMode;
}
public void setNeedClientMode(boolean needClientMode) {
this.needClientMode = needClientMode;
}
public boolean isWantClientMode() {
return wantClientMode;
}
public void setWantClientMode(boolean wantClientMode) {
this.wantClientMode = wantClientMode;
}
public String getTrustStoreFile() {
return trustStoreFile;
}
public void setTrustStoreFile(String trustStoreFile) {
this.trustStoreFile = trustStoreFile;
}
public String getTrustStorePassword() {
return trustStorePassword;
}
public void setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
}
public String getKeyStoreFile() {
return keyStoreFile;
}
public void setKeyStoreFile(String keyStoreFile) {
this.keyStoreFile = keyStoreFile;
}
public String getKeyStorePassword() {
return keyStorePassword;
}
public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}
public boolean isNeedSSL() {
return needSSL;
}
public void setNeedSSL(boolean needSSL) {
this.needSSL = needSSL;
}
public boolean isNeedAuth() {
return needAuth;
}
public void setNeedAuth(boolean needAuth) {
this.needAuth = needAuth;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public boolean isKeepAlive() {
return keepAlive;
}
public void setKeepAlive(boolean keepAlive) {
this.keepAlive = keepAlive;
}
public int getLinger() {
return linger;
}
public void setLinger(int linger) {
this.linger = linger;
}
public boolean isReuseAddress() {
return reuseAddress;
}
public void setReuseAddress(boolean reuseAddress) {
this.reuseAddress = reuseAddress;
}
public int getBackLog() {
return backLog;
}
public void setBackLog(int backLog) {
this.backLog = backLog;
}
public int getServerSocketTimeout() {
return serverSocketTimeout;
}
public void setServerSocketTimeout(int serverSocketTimeout) {
this.serverSocketTimeout = serverSocketTimeout;
}
public boolean isTcpNoDelay() {
return tcpNoDelay;
}
public void setTcpNoDelay(boolean tcpNoDelay) {
this.tcpNoDelay = tcpNoDelay;
}
public int getReadBufferSize() {
return readBufferSize;
}
public void setReadBufferSize(int readBufferSize) {
this.readBufferSize = readBufferSize;
}
public int getWriteBufferSize() {
return writeBufferSize;
}
public void setWriteBufferSize(int writeBufferSize) {
this.writeBufferSize = writeBufferSize;
}
public static NIOSocketServer getInstance() {
return nioSocketServer;
}
private NIOSocketServer() {
//init attribute from properties
PropertiesLoaderUtils.setPropertiesFields(this);
// Create TCP transport
this.tcpNIOTransportBuilder = TCPNIOTransportBuilder.newInstance();
this.tcpNIOTransportBuilder.setKeepAlive(keepAlive);
if (this.linger > 0)
this.tcpNIOTransportBuilder.setLinger(this.linger);
this.tcpNIOTransportBuilder.setReuseAddress(reuseAddress);
if (this.backLog > 0)
this.tcpNIOTransportBuilder.setServerConnectionBackLog(this.backLog);
if (this.serverSocketTimeout > 0)
this.tcpNIOTransportBuilder.setServerSocketSoTimeout(this.serverSocketTimeout);
this.tcpNIOTransportBuilder.setTcpNoDelay(tcpNoDelay);
if (readBufferSize > 0)
this.tcpNIOTransportBuilder.setReadBufferSize(readBufferSize);
if (writeBufferSize > 0)
this.tcpNIOTransportBuilder.setReadBufferSize(writeBufferSize);
}
@Override
public void doStart() throws Exception {
if (transport != null && isRunning()) {
logger.error("Server is running now!");
return;
}
try {
initLog();
transport = buildTCPNIOTransport();
transport.setWorkerThreadPoolConfig(null);//custom work thread pool
// start the transport
transport.start();
//register jmx
registerJMX();
//add shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
if (nioSocketServer.isStopping() || nioSocketServer.isStopped()) {
//have shutdown by mbean
} else {
logger.warn("server is interrupted!");
nioSocketServer.stop();
nioSocketServer.notifyServer();
}
} catch (Exception e) {
logger.error("shut down server error.", e);
}
}
});
} catch (Exception e) {
e.printStackTrace();
logger.error("Server start error!", e);
throw e;
}
}
private void initLog() throws IOException {
java.util.logging.LogManager logManager = java.util.logging.LogManager.getLogManager();
InputStream is = ClassLoaderUtils.getDefaultClassLoader().getResourceAsStream("log4j.properties");
logManager.readConfiguration(is);
}
private TCPNIOTransport buildTCPNIOTransport() throws IOException {
final TCPNIOTransport transport = tcpNIOTransportBuilder.build();
transport.configureBlocking(false);
// Create a FilterChain using FilterChainBuilder
FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();
// Add TransportFilter, which is responsible
// for reading and writing data to the connection
filterChainBuilder.add(new TransportFilter());
if (needSSL) {
// Initialize and add SSLFilter
final SSLEngineConfigurator serverConfig = initializeSSL();
final SSLEngineConfigurator clientConfig = serverConfig.copy().setClientMode(true);
filterChainBuilder.add(new SSLFilter(serverConfig, clientConfig));
} else {
logger.warn("server need not ssl!");
}
// StringFilter is responsible for Buffer <-> String conversion
filterChainBuilder.add(new StringFilter(Charset.forName("UTF-8"), SocketKeys.STRING_TERMINATING_SYMB));
filterChainBuilder.add(new ServerStringDataFilter());
if (needAuth) {
filterChainBuilder.add(new ServerAuthFilter());
} else {
logger.warn("server need not authentication!");
}
this.jobDispatcher = new JobDispatcher();
// EchoFilter is responsible for echoing received messages
filterChainBuilder.add(new ThreadPoolFilter(this.jobDispatcher));
transport.setProcessor(filterChainBuilder.build());
//不使用grizzly默认的线程池,而是使用与应用相关的线程池,
//优点:根据业务信息来使用线程池
//缺点:SelectorRunner的生命周期变长.造成处理能力下降,可以通过区分accept和read/write来
//降低这个影响程度。
transport.setIOStrategy(SameThreadIOStrategy.getInstance());
// binding transport to start listen on certain host and port
logger.debug("server host:{},port:{}", new Object[] { host, port });
transport.bind(host, port);
return transport;
}
public void joinServer() throws InterruptedException {
synchronized (_joinLock) {
while (isRunning())
_joinLock.wait();
}
while (isStopping())
Thread.sleep(1);
}
public void notifyServer() {
synchronized (_joinLock) {
_joinLock.notifyAll();
}
}
@Override
protected void doStop() throws Exception {
try {
if (this.transport != null) {
this.transport.stop();
} else {
logger.error("transport is null, Server is " + getState());
return;
}
if (this.jobDispatcher != null) {
this.jobDispatcher.stopDispatcher();
}
// synchronized (_joinLock) {
// _joinLock.notifyAll();
// }
} catch (Exception e) {
e.printStackTrace();
logger.error("Server stop error!", e);
}
}
/**
* Initialize server side SSL configuration.
*
* @return server side {@link SSLEngineConfigurator}.
*/
private SSLEngineConfigurator initializeSSL() {
// Initialize SSLContext configuration
SSLContextConfigurator sslContextConfig = new SSLContextConfigurator();
// Set key store
ClassLoader cl = ClassLoaderUtils.getDefaultClassLoader();
URL cacertsUrl = cl.getResource(getTrustStoreFile());
if (cacertsUrl != null) {
sslContextConfig.setTrustStoreFile(cacertsUrl.getFile());
sslContextConfig.setTrustStorePass(getTrustStorePassword());
}
// Set trust store
URL keystoreUrl = cl.getResource(getKeyStoreFile());
if (keystoreUrl != null) {
sslContextConfig.setKeyStoreFile(keystoreUrl.getFile());
sslContextConfig.setKeyStorePass(getKeyStorePassword());
}
// Create SSLEngine configurator
return new SSLEngineConfigurator(sslContextConfig.createSSLContext(), false, isNeedClientMode(),
isWantClientMode());
}
private void registerJMX() {
AbstractJmxMonitoringConfig<SocketServerProbe> monitoringConfig = new AbstractJmxMonitoringConfig<SocketServerProbe>(
SocketServerProbe.class) {
@Override
public JmxObject createManagementObject() {
return new SocketServerJMX();
}
};
final GrizzlyJmxManager manager = GrizzlyJmxManager.instance();
JmxObject jmxTransportObject = monitoringConfig.createManagementObject();
manager.registerAtRoot(jmxTransportObject, "SocketServerJMX");
}
public void stopServer(String host, int port, String user, String password, String trustStoreFile,
String trustStorePassword, String keyStoreFile, String keyStorePassword) throws IOException {
NIOSocketClient client = new NIOSocketClient(user, password);//("EAUSER123", "aaaaa888");
client.setSoTimeout(1);
client.setKeepAlive(true);
client.setLinger(30);
client.setTcpNoDelay(false);
if (trustStoreFile != null || keyStoreFile != null) {
client.setNeedSSL(true);
if (trustStoreFile != null) {
client.setTrustStoreFile(trustStoreFile);//("ssltest-cacerts.jks");
client.setTrustStorePassword(trustStorePassword);//("changeit");
}
if (keyStoreFile != null) {
client.setKeyStoreFile(keyStoreFile);//("ssltest-keystore.jks");
client.setKeyStorePassword(keyStorePassword);//("changeit");
}
}
client.connect(host, port);//("localhost", 7777);
ServiceRequest message1 = new ServiceRequest();
message1.setServiceId("0000000002");
message1.setTransformType(SocketKeys.TRANSFORM_JSON);
client.write(message1);
ServiceResponse rcvMessage = client.getServiceResponse(10, TimeUnit.SECONDS);
if (rcvMessage != null)
logger.info(rcvMessage.toString());
else
logger.info("receiveMessage is null!");
client.close();
}
public static void main(String[] args) throws Exception {
if (args.length < 1) {
System.out.println("=======================start server==============================");
System.out
.println("start server: java -cp ${classpath\\\\lib\\\\*.jar};isocket.jar org.young.isokcet.server.NIOSocketServer start");
System.out.println("");
System.out
.println("start server(with jmx): java -Dcom.sun.management.jmxremote.port=7778 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -cp ${classpath\\\\lib\\\\*.jar};isocket.jar org.young.isokcet.server.NIOSocketServer start");
System.out.println("================================================================");
System.out.println("=======================stop server==============================");
System.out
.println("stop server(disable ssl): java -cp ${classpath\\\\lib\\\\*.jar};isocket.jar org.young.isokcet.server.NIOSocketServer stop host port username password ");
System.out.println("");
System.out
.println("stop server(enable ssl): java -cp ${classpath\\\\lib\\\\*.jar};isocket.jar org.young.isokcet.server.NIOSocketServer stop host port username password trustStoreFile trustStorePassword keyStoreFile keyStorePasswored");
System.out.println("================================================================");
System.exit(1);
}
if (args[0].equalsIgnoreCase("start")) {
if (logger.isDebugEnabled()) {
System.setProperty("javax.net.debug", "ssl,handshake");
}
NIOSocketServer server = NIOSocketServer.getInstance();
server.start();
server.joinServer();//不退出jvm
logger.info("server exit!!!");
} else if (args[0].equalsIgnoreCase("stop")) {
if (args.length != 5 && args.length != 9) {
System.out.println("=======================stop server==============================");
System.out
.println("stop server(disable ssl): java -cp ${classpath\\\\lib\\\\*.jar};isocket.jar org.young.isokcet.server.NIOSocketServer stop host port username password ");
System.out.println("");
System.out
.println("stop server(enable ssl): java -cp ${classpath\\\\lib\\\\*.jar};isocket.jar org.young.isokcet.server.NIOSocketServer stop host port username password trustStoreFile trustStorePassword keyStoreFile keyStorePasswored");
System.out.println("================================================================");
} else if (args.length == 5) {
NIOSocketServer.getInstance().stopServer(args[1], Integer.parseInt(args[2]), args[3], args[4], null,
null, null, null);
} else if (args.length == 9) {
NIOSocketServer.getInstance().stopServer(args[1], Integer.parseInt(args[2]), args[3], args[4], args[5],
args[6], args[7], args[8]);
}
} else {
logger.error("command parameter error.");
System.out.println("start server: java -cp isocket.jar org.young.isokcet.server.NIOSocketServer start");
System.out.println("stop server: java -cp isocket.jar org.young.isokcet.server.NIOSocketServer stop");
}
System.exit(0);
}
}