最近一段业余时间,学围棋、五子棋、黑白棋、2048,完事之后,学一点就学不进去了——年龄大了,学什么都困难了。
此为题外话,想来想去,也许业余时间只有读读代码,才学得进去吧。
hsqldb是一个开放源代码的JAVA数据库,其具有标准的SQL语法和JAVA接口。当然为作源码阅读的主要原因是代码量比较小,作为首次阅读源码,这很重要。
于是就从 http://hsqldb.org/ 下载了源码。
源码是下载下来了,src目录下,这么多文件以及文件夹,之前也没有阅读一个完整源代码的经验,从何开始呢?

原本想一个个文件的看吧,突然灵光一闪,应该从程序入口开始。
hsqldb可以使用命令行方式 java -cp xxxx org.hsqldb.server.Server 或者 java -cp xxxx org.hsqldb.server.WebServer的方式,作为服务器启动。
就从Server/WebServer的main方法开始。
-
public static void main(String[] args) {
-
-
HsqlProperties argProps = null;
-
-
argProps = HsqlProperties.argArrayToProps(args,
-
ServerProperties.sc_key_prefix);
-
-
String[] errors = argProps.getErrorKeys();
-
-
if (errors.length != 0) {
-
System.out.println("no value for argument:" + errors[0]);
-
printHelp("server.help");
-
-
return;
-
}
-
-
String propsPath = argProps.getProperty(ServerProperties.sc_key_props);
-
String propsExtension = "";
-
-
if (propsPath == null) {
-
propsPath = "server";
-
propsExtension = ".properties";
-
} else {
-
argProps.removeProperty(ServerProperties.sc_key_props);
-
}
-
-
propsPath = FileUtil.getFileUtil().canonicalOrAbsolutePath(propsPath);
-
-
ServerProperties fileProps = ServerConfiguration.getPropertiesFromFile(
-
ServerConstants.SC_PROTOCOL_HSQL, propsPath, propsExtension);
-
ServerProperties props =
-
fileProps == null
-
? new ServerProperties(ServerConstants.SC_PROTOCOL_HSQL)
-
: fileProps;
-
-
props.addProperties(argProps);
-
ServerConfiguration.translateDefaultDatabaseProperty(props);
-
-
// Standard behaviour when started from the command line
-
// is to halt the VM when the server shuts down. This may, of
-
// course, be overridden by whatever, if any, security policy
-
// is in place.
-
ServerConfiguration.translateDefaultNoSystemExitProperty(props);
-
ServerConfiguration.translateAddressProperty(props);
-
-
// finished setting up properties;
-
Server server = new Server();
-
-
try {
-
server.setProperties(props);
-
} catch (Exception e) {
-
server.printError("Failed to set properties");
-
server.printStackTrace(e);
-
-
return;
-
}
-
-
// now messages go to the channel specified in properties
-
server.print("Startup sequence initiated from main() method");
-
-
if (fileProps != null) {
-
server.print("Loaded properties from [" + propsPath
-
+ propsExtension + "]");
-
} else {
-
server.print("Could not load properties from file");
-
server.print("Using cli/default properties only");
-
}
-
-
server.start();
-
}
在main方法中,主要完成参数的读取、new Server对象设置参数后调用start方法。
在此方法中,声明一个HsqlProperties类来读取、解析参数是一个不错的代码方式,可以将配置项都体现在该类中——如果代码都在Server(也即main所在的类)实现的话,那就显得Server职责过多。
下面再看server.start方法
-
public int start() {
-
-
printWithThread("start() entered");
-
-
int previousState = getState();
-
-
if (serverThread != null) {
-
printWithThread("start(): serverThread != null; no action taken");
-
-
return previousState;
-
}
-
-
setState(ServerConstants.SERVER_STATE_OPENING);
-
-
serverThread = new ServerThread("HSQLDB Server ");
-
-
if (isDaemon) {
-
serverThread.setDaemon(true);
-
}
-
-
serverThread.start();
-
-
// call synchronized getState() to become owner of the Server Object‘s monitor
-
while (getState() == ServerConstants.SERVER_STATE_OPENING) {
-
try {
-
Thread.sleep(100);
-
} catch (InterruptedException e) {}
-
}
-
-
printWithThread("start() exiting");
-
-
return previousState;
-
}
这里开启了一个后台线程,去处理启动数据库,然后开启端口监听,而主线程,则在这里每隔100毫秒检查一下状态,一直等到启动结束或者关闭等状态。
这里要开一个新的线程,因为当前线程可能是别的地方调用了方法,需要返回值的。
那我们再来看看程序的主体工作又是在做什么,先是ServerThread
-
private class ServerThread extends Thread {
-
-
/**
-
* Constructs a new thread in which to execute the run method
-
* of this server.
-
*
-
* @param name The thread name
-
*/
-
ServerThread(String name) {
-
-
super(name);
-
-
setName(name + ‘@‘ + Integer.toString(Server.this.hashCode(), 16));
-
}
-
-
/**
-
* Executes the run() method of this server
-
*/
-
public void run() {
-
Server.this.run();
-
printWithThread("ServerThread.run() exited");
-
}
-
}
这里代码很简单,这是Server的一个内部类,然后又回到Server.run方法去了。
-
private void run() {
-
-
StopWatch sw;
-
ThreadGroup tg;
-
String tgName;
-
-
printWithThread("run() entered");
-
print("Initiating startup sequence...");
-
printProperties();
-
-
sw = new StopWatch();
-
-
setServerError(null);
-
-
try {
-
-
// Faster init first:
-
// It is huge waste to fully open the databases, only
-
// to find that the socket address is already in use
-
openServerSocket();
-
} catch (Exception e) {
-
setServerError(e);
-
printError("run()/openServerSocket(): ");
-
printStackTrace(e);
-
shutdown(true);
-
-
return;
-
}
-
-
tgName = "HSQLDB Connections @"
-
+ Integer.toString(this.hashCode(), 16);
-
tg = new ThreadGroup(tgName);
-
-
tg.setDaemon(false);
-
-
serverConnectionThreadGroup = tg;
-
-
// Mount the databases this server is supposed to host.
-
// This may take some time if the databases are not all
-
// already open.
-
if (!openDatabases()) {
-
setServerError(null);
-
printError("Shutting down because there are no open databases");
-
shutdown(true);
-
-
return;
-
}
-
-
// At this point, we have a valid server socket and
-
// a valid hosted database set, so its OK to start
-
// listening for connections.
-
setState(ServerConstants.SERVER_STATE_ONLINE);
-
print(sw.elapsedTimeToMessage("Startup sequence completed"));
-
printServerOnlineMessage();
-
-
// isShuttingDown is only read after socket connections are ‘accept‘ed.
-
isShuttingDown = false; // In case shutdown was aborted previously.
-
-
try {
-
/*
-
* This loop is necessary for UNIX w/ Sun Java 1.3 because
-
* in that case the socket.close() elsewhere will not
-
* interrupt this accept().
-
*/
-
while (socket != null) {
-
try {
-
handleConnection(socket.accept());
-
} catch (java.io.InterruptedIOException iioe) {}
-
}
-
} catch (IOException ioe) {
-
if (getState() == ServerConstants.SERVER_STATE_ONLINE) {
-
setServerError(ioe);
-
printError(this + ".run()/handleConnection(): ");
-
printStackTrace(ioe);
-
}
-
} catch (Throwable t) {
-
printWithThread(t.toString());
-
} finally {
-
shutdown(false); // or maybe getServerError() != null?
-
}
-
}
写过tcp服务端的人可能比较熟悉,就是绑定socket端口,然后while循环一直监听到有客户端进行连接,有连接就调用handleConnection进行处理。
-
public void handleConnection(Socket s) {
-
-
Thread t;
-
Runnable r;
-
String ctn;
-
-
printWithThread("handleConnection(" + s + ") entered");
-
-
if (!allowConnection(s)) {
-
try {
-
s.close();
-
} catch (Exception e) {}
-
-
printWithThread("allowConnection(): connection refused");
-
printWithThread("handleConnection() exited");
-
-
return;
-
}
-
-
// Maybe set up socket options, SSL
-
// Session tracing/callbacks, etc.
-
if (socketFactory != null) {
-
socketFactory.configureSocket(s);
-
}
-
-
if (serverProtocol == ServerConstants.SC_PROTOCOL_HSQL) {
-
r = new ServerConnection(s, this);
-
ctn = ((ServerConnection) r).getConnectionThreadName();
-
} else {
-
r = new WebServerConnection(s, (WebServer) this);
-
ctn = ((WebServerConnection) r).getConnectionThreadName();
-
}
-
-
t = new Thread(serverConnectionThreadGroup, r, ctn);
-
-
t.start();
-
printWithThread("handleConnection() exited");
-
}
在这里,为每一个客户端新建一个线程进行处理(线程放入ThreadGroup中),处理函数转接到ServerConnection/WebServerConnection——分别对应不同的客户端协议处理。
至此,Server这个入口基本上清晰了。
1. 解析配置(含命令行的、配置文件的)
2. 声明一个服务监听线程,打开socket端口,进行监听
3. 对于每一个连接而来的客户端,声明一个线程,进行处理
4. 下一步客户端协议的请求响应处理,进入ServerConnection/WebServerConnection中
WebServer继承自Server,内容基本一样。
[相关源码文件]
Server.java
WebServer.java
HsqlProperties.java
ServerProperties.java
ServerConfiguration.java
HsqlSocketFactory.java
HSQLDB源码阅读 (一)从程序入口开始
原文:http://blog.chinaunix.net/uid-29966746-id-4561171.html