这段时间由于一个项目,需要为GeoServer添加自定义的空间数据源,因此对GeoServer以及GeoTools作了点的学习。
由于GeoServer使用的是GeoTools体系。因此,增加自定义数据源本质上就是按照GeoTools的数据源plugin规范写一个plugin。
I. 数据源的创建
空间数据源的创建采用了工厂方法,GeoTools的工厂类派生结构如下(下图中的类仅为将涉及到的部分):
因此,第一步工作就是要为数据源写一个工厂类,在这里我们需要决定应该从哪个类派生。由于我的数据源是以数据库为基础的空间数据,因此,这里选择从JDBCDataStoreFactory类派生。
并不是所有的空间数据库一定要从该类派生,比如PostgisDataStoreFactory就是从AbstractDataStoreFactory派生出来。
另外ShapefileDataStoreFactory则是从DataStoreFactorySpi派生出来。
在创建了自己的工厂类之后,GeoTools是如何找到该工厂的?GeoTools其实是通过org.geotools.data.DataStoreFactorySpi配置文件得到工厂信息。因此,需要为自己的工厂类建立该文件。该文件应存放在jar包的META-INF/services目录下,文件内容如下:
org.geotools.data.zengjie.SqlServerDataStoreFactory
(也就是工厂类的全名)
I. 实现数据源工厂类
接下来,我们就需要实现该类,该工厂类中几个必需要实现的方法如下:
l createSQLDialect
l getDatabaseID
l getDriverClassName
l getValidationQuery
l getDescription
其中createSQLDialect方法是其中最重要的(并不意味着复杂)。该方法声明如下:
SQLDialect createSQLDialect(JDBCDataStore dataStore)
该方法使用JDBCDataStore创建一个SQLDialect对象。SQLDialect对象用于屏蔽各个数据库之间的差异,比如:decodeGeometryValue方法,该方法用于从二进制流中得到Geometry对象。
看到以上的方法,你也许会产生疑惑,对于一个已经创建好的数据源,GeoTools在初始化时是如何确定使用哪一个工厂?
GeoTools得到所有可用的Factory。然后调用每个Factory的canProcess,将params作为参数传到该方法中,由每个Factory来判断是否可以处理该params。如果可以处理就用该Factory创建DataStore。对于JDBC类的canProcess方法来说,
public boolean canProcess(Map params) {
if (!super.canProcess(params)) {
return false; // was not in agreement with getParametersInfo
}
return checkDBType(params);
}
首先调用了基类(AbstractDataStoreFactory)的canProcess,在该方法中得到所有该数据源的参数(Param),然后循环判断每个参数是否在params中存在,如果不存在且该参数又是必需的则返回false。
在执行完了基类的canProcess后,又调用了checkDBType(params)。因为作为JDBC类的数据源,参数大多一样,因此仅仅检查了参数并不能保证该数据源必定能被处理。这里JDBC的数据源工厂利用getDatabaseID方法返回的ID来进行判断。因此我们在实现getDatabaseID方法时一定要注意,返回的字符串不要太简单。
除了以上必须要实现的函数外,还可以根据需要重载基类的方法。我这里由于需要自己生成数据源的URL,因此还重载了
String getJDBCUrl(Map param)函数,如下:
protected String getJDBCUrl(Map params) throws IOException {
// jdbc url
String host = (String) HOST.lookUp(params);
Integer port = (Integer) PORT.lookUp(params);
String db = (String) DATABASE.lookUp(params);
String url = URL + host;
if ( port != null ) {
url += ":" + port;
}
url += ";DatabaseName=" + db;
return url;
}