远程服务器的编写与部署
远程服务器功能与原理
远程服务器相当于客户端和服务器之间的一个中间层,这个中间层避免了客户端直接访问服务器,通过该中间层实现用户权限的控制。
远程服务器例子代码介绍
例子代码共包括四个类:RemoteManager, RemoteServer, UserInfo, UserManager
其中UserInfo为装载个人会话信息的类, UserManager为管理UserInfo的类 RemoteServer为远程服务器,接受服务请求,返回服务结果 RemoteManager供RemoteServer调用,实现了所有服务请求的方法
实现思路大致为:
在RemoteServer类的初始化方法里,读取了用户信息配置文件里的所有用户信息,用UserInfo装载,并由UserManager管理。用户信息包括用户密码、权限、会话变量、无权限访问的语义视图和语义字段等等;
RemoteServer类的初始化方法里,还读取了缺省语义层文件名和远程数据源配置参数,并把这些信息存储在RemoteManager里
在RemoteServer类的service方法里,接受了客户端的登录、注销、访问文件、保存文件请求,并调用RemoteManager处理这些请求,将请求结果以Response的形式返回给客户端
在RemoteManager类里,分别实现了登录、注销、打开文件、保存文件四种方法,供RemoteServer调用,在这四个方法里进行权限的控制
其中:登录的时候不仅仅校验了用户密码,还把用户的会话信息传递给了客户端response,同时还把缺省的语义层文件名、远程数据源连接参数传递给客户端;此处润乾公司提供了LoginResponse类作为返回的接口。因此客户端登录成功后,会自动发送打开缺省语义层文件的请求,该语义层文件经过权限过滤后会在客户端自动打开;而且客户端登录成功后,还会在系统/数据源菜单下,看到新增加了几个远程数据源配置,这几个都是登录时由远程服务器传递过来的。
打开文件和保存文件方法,会根据用户的会话信息里的权限信息,对访问权限进行过滤,之后执行,分别以OpenResponse和SaveResponse类作为返回的接口
注销方法以LogoutResponse作为返回的接口
返回接口是润乾报表规定的接口,不可随意改动,否则客户端不能识别。
接口说明
下面详细介绍一下上面提到的远程服务器实际和远程设计器打交的全部接口,掌握了这几个接口,就可以最大灵活的写出自定义的远程服务器。下面举例用的代码,在上面提到的远程服务器例子代码中都可以找到具体的实现。
接收命令
远程设计器是通过发送“com.runqian.report4.remote.Request ”的子类来向远程服务器发出命令。远程服务器需要接收到并解析这个命令。在servlet中经典的接收命令的代码是:
Java.io.InputStream is = request.getInputStream();//request是HttpServletRequest类型Java.io.ObjectInputStream ois = new ObjectInputStream( is );com.runqian.report4.remote.Request req = ( Request ) ois.readObject();
远程设计器一共只会发出四种命令:请求在远程服务器登录、请求从远程服务器注销、请求打开远程服务器上的文件、请求把文件保存在远程服务器,分别对应com.runqian.report4.remote.Request的四个子类:
-
LoginRequest
-
LogoutRequest
-
OpenRequest
-
SaveRequest
显然接收到命令后通过instanceof方法就可以判断出req是这四种命令中的哪种了。
返回应答
而远程服务器总是通过返回“com.runqian.report4.remote.Response”类的子类来应答这些命令。Servlet里经典的应答代码如下:
com.runqian.report4.remote.Response res=null;res=………………….//分析接收到的req命令,处理后给res赋值java.io.OutputStream os = response.getOutputStream();//response是HttpServletResponse类型java.io.ObjectOutputStream oos = new ObjectOutputStream( os );oos.writeObject( res );
针对设计器的4种命令,远程服务器会对应回答:返回登陆结果、返回注销结果、返回请求的文件、返回保存文件后的结果.他们分别对应com.runqian.report4.remote.Response的四个子类:
-
LoginResponse
-
LogoutResponse
-
OpenResponse
-
SaveResponse
-
命令: LoginRequest
全面的类说明请参阅API说明文档,这里只给出常用的说明,下同。 java.lang.Object
java.lang.Objectcom.runqian.report4.remote.Requestcom.runqian.report4.remote.LoginRequestAll Implemented Interfaces: java.io.Externalizable, java.io.Serializable
-
命令: LogoutRequest
java.lang.Objectcom.runqian.report4.remote.Requestcom.runqian.report4.remote.LogoutRequestAll Implemented Interfaces: java.io.Externalizable, java.io.Serializable
-
命令:OpenRequest
java.lang.Objectcom.runqian.report4.remote.Requestcom.runqian.report4.remote.OpenRequestAll Implemented Interfaces: java.io.Externalizable, java.io.Serializable
-
命令:SaveRequest
java.lang.Objectcom.runqian.report4.remote.Requestcom.runqian.report4.remote.SaveRequestAll Implemented Interfaces: java.io.Externalizable, java.io.Serializable
-
应答:LoginResponse
java.lang.Objectcom.runqian.report4.remote.Requestcom.runqian.report4.remote.LoginRequestAll Implemented Interfaces: java.io.Externalizable, java.io.Serializable
-
应答:LogoutResponse
java.lang.Objectcom.runqian.report4.remote.Responsecom.runqian.report4.remote.LogoutResponseAll Implemented Interfaces: java.io.Externalizable, java.io.Serializable
-
应答: OpenResponse
java.lang.Objectcom.runqian.report4.remote.Responsecom.runqian.report4.remote.OpenResponseAll Implemented Interfaces: java.io.Externalizable, java.io.Serializable
-
应答:SaveResponse
java.lang.Objectcom.runqian.report4.remote.Responsecom.runqian.report4.remote.SaveResponseAll Implemented Interfaces: java.io.Externalizable, java.io.Serializable
-
远程数据源DataSourceDefine
java.lang.Objectcom.runqian.report4.usermodel.DataSourceConfigcom.runqian.report4.remote.DataSourceDefineAll Implemented Interfaces: java.io.Serializable Direct Known Subclasses: DataSource
远程服务器的例子部署与使用介绍
web.xml文件配置
在此文件中配置一个为远程报表设计器提供服务的Servlet,如上述例子代码介绍中的RemoteServer,配置如下:
<servlet><servlet-name>remoteServer</servlet-name><servlet-class>com.runqian.report4.remote.example.RemoteServer</servlet-class><init-param><param-name>usersConfig</param-name><param-value>/WEB-INF/users.properties</param-value></init-param><init-param><param-name>semanticsFile</param-name><param-value>demo.xml</param-value></init-param><init-param><param-name>dsConfig</param-name><param-value>/WEB-INF/remoteDataSource.xml</param-value></init-param><load-on-startup>10</load-on-startup></servlet><servlet-mapping><servlet-name>remoteServer</servlet-name><url-pattern>/remoteServer</url-pattern></servlet-mapping>
这是润乾提供的一个实现实例,启动参数的含义是:
用户信息文件users.properties
本示例中用户信息文件放置在WEB-INF目录中,格式如下:
root=管理员,root,1111111111,-1,month=2,岗位;职称=职称名称;部门表=部门名称:上级部门user1=报表读取者,user1,0010001001,1000,userName=报表读取者;userId=user1;dept=salesuser2=报表设计者,user2,1010001111,5000,userName=报表设计者;userId=user2;dept=salesuser3=语义层编辑者,user3,1011110001,300, userName=语义层编辑者;userId=user3;dept=IT
每一行代表一个用户,等号左边的是用户登录ID,右边六项分别是用户名、密码、权限、记录条数报警值、会话变量、无权访问的语义视图和字段,权限是一个二进制表示的串,每一位的含义如下(从右往左为1,2......10位):
1位 是否能打开远程报表
2位 是否能保存远程报表
3位 是否能另存为远程报表
4位 是否能保存报表到本地
5位 是否能保存语义
6位 是否能另存语义
7位 是否能保存语义到本地
8位 是否能获取指定名称的语义层
9位 是否可以修改会话变量
10位 是否可以直接使用数据库对象,目前只控制到各种SQL及存储过程编辑
记录条数报警值为-1表示无限制,否则到达指定数量将报警。 会话变量的格式如下:arg1=value1;arg2=value2;……;argn=valuen
无权限的语义视图和字段的定义如下:view1;view2=col1:col2……:coln;……viewn 如果整个视图没有权限访问,则紧紧写出视图名即可,多个之间分号分隔;如果仅仅是某个视图中的部分字段没有权限访问,则用view=col1:col2:……:coln;的格式描述,多个字段之间冒号分隔,视图间分号分隔
远程数据源信息文件remoteDataSource.xml
本示例中远程数据源信息文件放置在WEB-INF目录中,格式如下:
<?xml version="1.0" encoding="GBK" ?><dataSource><ds name="remote-sjr"dbType="oracle"user="system"password="manager"driverClass="oracle.jdbc.driver.OracleDriver"url="jdbc:oracle:thin:@192.168.0.27:1521:demo"transCharset="0"dbCharset="GBK"ideCharset="GBK"transSQLCharset="0"CaseSensitive="0"useSchema="0"/></dataSource>
每个ds节点代表一个数据源,各属性含义如下:
远程服务器的部署
如果直接采用润乾提供的例子远程服务器,那么不需要进行任何部署,因为润乾提供的report4.jar已经包含了相关的类;
如果自己重写远程服务器,那么需要把自己写的相关类部署在应用的类路径下,一般是WEB-INF\classes,参照本小节3.1的介绍进行相关servlet的配置。主要需要修改两个地方:
1. web.xml
web.xml中要修改远程服务器类名和servlet路径,以下是例子带的远程服务器RemoteServe的配置,需要修改成自己的:
<servlet> <servlet-name>RemoteServer</servlet-name> <servlet-class>com.runqian.report4.remote.example.RemoteServer</servlet-class> <init-param> <param-name>usersConfig</param-name> <param-value>/WEB-INF/users.properties</param-value> </init-param> <init-param> <param-name>semanticsFile</param-name> <param-value>yy0718.xml</param-value> </init-param> <init-param> <param-name>dsConfig</param-name> <param-value>/WEB-INF/remoteDataSource.xml</param-value> </init-param> <load-on-startup>10</load-on-startup> </servlet> <servlet-mapping> <servlet-name>RemoteServer</servlet-name> <url-pattern>/RemoteServer</url-pattern> </servlet-mapping>
2、远程设计器登录界面
登录远程设计器时,URL参数需要修改,例子中远程服务器是RemoteServe时URL型如:http://192.168.0.173:8001/report/RemoteServer,需要修改成web.xml中配置的servlet路径。
启发
前面介绍的例子里,是把用户信息存在配置文件中,而远程数据源的连接参数也存在配置文件中,且和用户无关。
实际应用中可能更加复杂,例如用户信息往往和用户表挂钩,存在数据库里,远程数据源的连接参数往往也和角色或者用户组等关联,不同的用户连接参数可能不同。
因此,实际编写远程服务器的时候,可以不在初始化的时候读取用户信息,因为这样还占用内存;建议在用户登录的时候从数据库里读取用户信息,返回给客户端,用户信息除了包括会话变量、权限、还可以包括数据库连接参数等。这样一来,UserManager类基本就没用了。