最近几天一直忙于 SCORM 2004 3rd Edition Sample Run-Time Environment 的二次开发。今天基本结束了。明天会提交到另外一个项目组做一下应用测试。现在顺便记下开发过程中的一些经验
首先:解决adl的跨平台
adl给出的这个开发平台采用了access数据库。而对于具体的learner学习sco的状态信息则都是以java序列化对象的形式保存在文件里,所以要想跨平台只需要更换掉access。目前我采用的hsqldb。第一:他是一个基于java的数据库,属于平台无关。第二:因为adl的开发平台对于数据库的交互并不频繁。而且在数据库存储的相关数据也不多。所以hsqldb完全可以支持adl的基本需求。第三:hsqldb非常小巧。并不需要单独安装。只需要拷贝一个jar包即可。所以对于应用的发布非常方便。在web应用里加一个监听器用来部署hsql即可。可以做到直接拷贝war包就可以运行。下面给出部署hsqldb和启动hsqldb的监听器类(在网上搜到的稍作修改)
package
org.adl.hsqllistener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.hsqldb.Server;
/** */ /**
* 该类的职责是在WebApp启动时自动开启HSQL服务. 依然使用Server方式,不受AppServer的影响.
*
* @author frank
* @author calvin
*/
public class HsqlListener implements ServletContextListener ... {
public void contextInitialized(ServletContextEvent sce) ...{
String dbName = sce.getServletContext().getInitParameter("hsql.dbName");
String path = sce.getServletContext().getInitParameter("hsql.dbPath");
path = sce.getServletContext().getRealPath("/") + path;
System.out.println("path:" + path);
int port = -1;
try ...{
port = Integer.parseInt(sce.getServletContext().getInitParameter(
"hsql.port"));
} catch (Exception e) ...{
port = 9001;
}
if (dbName == null || dbName.equals("")) ...{
System.out
.println("Cant' get hsqldb.dbName from web.xml Context Param");
return;
}
String scriptFile = path + File.separator + dbName + ".script";// 源hsqldb
// *.script文件
String propertiesFile = path + File.separator + dbName + ".properties";// 源hsqldb
// *.properties文件
String dsqlFilePath = File.separator + "scorm" + File.separator
+ "hsqldb";
File dsqlFileDir = new File(dsqlFilePath);
String dsqlFileScript = dsqlFilePath + File.separator + dbName
+ ".script";// 目标hsqldb *.script文件(最终使用的hsql数据库)
String dsqlFileProperties = dsqlFilePath + File.separator + dbName
+ ".properties";// 目标hsqldb *.properties文件(最终使用的hsql数据库)
File dsqlFileS = new File(dsqlFileScript);
File dsqlFileP = new File(dsqlFileProperties);
System.out.println(" 源Hsql文件:" + scriptFile + "," + propertiesFile
+ " ");
System.out.println(" 目标Hsql文件:" + dsqlFileScript + ","
+ dsqlFileProperties);
try ...{
if (dsqlFileP.exists() && dsqlFileS.exists()) ...{// 判断数据文件是否存在
System.out.println("数据库文件已存在");
this.startServer(dsqlFilePath + File.separator, dbName, port);
} else ...{
System.out.println("创建数据库文件");
if (!dsqlFileDir.exists()) ...{// 判断数据库目录是否存在
dsqlFileDir.mkdirs();
}
if (HsqlListener.copy(scriptFile, dsqlFileScript)) ...{
if (HsqlListener.copy(propertiesFile, dsqlFileProperties)) ...{
System.out.println("hsql部署成功");
this.startServer(dsqlFilePath + File.separator, dbName,
port);
}
}
}
} catch (Exception e) ...{
e.printStackTrace();
}
}
/** *//**
* * 启动Hsqldb服务的方法。 *
*
* @param dbPath
* 数据库路径 *
* @param dbName
* 数据库名称 *
* @param port
* 所使用的端口号
*/
private void startServer(String dbPath, String dbName, int port) ...{
Server server = new Server();// 它可是hsqldb.jar里面的类啊。
server.setDatabaseName(0, dbName);
server.setDatabasePath(0, dbPath + dbName);
if (port != -1) ...{
server.setPort(port);
}
server.setSilent(true);
server.start();
System.out.println("hsqldb started...");
// 等待Server启动
try ...{
Thread.sleep(800);
} catch (InterruptedException e) ...{
// do nothing
}
}
/** *//**
* * Listener销毁方法,在Web应用终止的时候执行"shutdown"命令关闭数据库.
*/
public void contextDestroyed(ServletContextEvent arg0) ...{
// 这里就不用说了,自然是关闭数据库操作
Connection conn = null;
try ...{
System.out.println("ADL应用终止");
Class.forName("org.hsqldb.jdbcDriver");
conn = DriverManager.getConnection(
"jdbc:hsqldb:hsql://localhost:9002/samplerte", "sa", "");
Statement stmt = conn.createStatement();
stmt.executeUpdate("SHUTDOWN;");
System.out.println("持久化hsqldb");
} catch (Exception e) ...{
e.printStackTrace();
// do nothing
}
}
/** *//**
* 将指定文件复制到目标文件
*
* @param file1
* 源文件
* @param file2
* 目标文件
* @return 复制是否成功
*/
public static boolean copy(String file1, String file2) ...{
try // must try and catch,otherwide will compile error
...{
// instance the File as file_in and file_out
java.io.File file_in = new java.io.File(file1);
java.io.File file_out = new java.io.File(file2);
FileInputStream in1 = new FileInputStream(file_in);
FileOutputStream out1 = new FileOutputStream(file_out);
byte[] bytes = new byte[1024];
int c;
while ((c = in1.read(bytes)) != -1)
out1.write(bytes, 0, c);
in1.close();
out1.close();
return (true); // if success then return true
} catch (Exception e) ...{
e.printStackTrace();
System.out.println("Error!");
return (false); // if fail then return false
}
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.hsqldb.Server;
/** */ /**
* 该类的职责是在WebApp启动时自动开启HSQL服务. 依然使用Server方式,不受AppServer的影响.
*
* @author frank
* @author calvin
*/
public class HsqlListener implements ServletContextListener ... {
public void contextInitialized(ServletContextEvent sce) ...{
String dbName = sce.getServletContext().getInitParameter("hsql.dbName");
String path = sce.getServletContext().getInitParameter("hsql.dbPath");
path = sce.getServletContext().getRealPath("/") + path;
System.out.println("path:" + path);
int port = -1;
try ...{
port = Integer.parseInt(sce.getServletContext().getInitParameter(
"hsql.port"));
} catch (Exception e) ...{
port = 9001;
}
if (dbName == null || dbName.equals("")) ...{
System.out
.println("Cant' get hsqldb.dbName from web.xml Context Param");
return;
}
String scriptFile = path + File.separator + dbName + ".script";// 源hsqldb
// *.script文件
String propertiesFile = path + File.separator + dbName + ".properties";// 源hsqldb
// *.properties文件
String dsqlFilePath = File.separator + "scorm" + File.separator
+ "hsqldb";
File dsqlFileDir = new File(dsqlFilePath);
String dsqlFileScript = dsqlFilePath + File.separator + dbName
+ ".script";// 目标hsqldb *.script文件(最终使用的hsql数据库)
String dsqlFileProperties = dsqlFilePath + File.separator + dbName
+ ".properties";// 目标hsqldb *.properties文件(最终使用的hsql数据库)
File dsqlFileS = new File(dsqlFileScript);
File dsqlFileP = new File(dsqlFileProperties);
System.out.println(" 源Hsql文件:" + scriptFile + "," + propertiesFile
+ " ");
System.out.println(" 目标Hsql文件:" + dsqlFileScript + ","
+ dsqlFileProperties);
try ...{
if (dsqlFileP.exists() && dsqlFileS.exists()) ...{// 判断数据文件是否存在
System.out.println("数据库文件已存在");
this.startServer(dsqlFilePath + File.separator, dbName, port);
} else ...{
System.out.println("创建数据库文件");
if (!dsqlFileDir.exists()) ...{// 判断数据库目录是否存在
dsqlFileDir.mkdirs();
}
if (HsqlListener.copy(scriptFile, dsqlFileScript)) ...{
if (HsqlListener.copy(propertiesFile, dsqlFileProperties)) ...{
System.out.println("hsql部署成功");
this.startServer(dsqlFilePath + File.separator, dbName,
port);
}
}
}
} catch (Exception e) ...{
e.printStackTrace();
}
}
/** *//**
* * 启动Hsqldb服务的方法。 *
*
* @param dbPath
* 数据库路径 *
* @param dbName
* 数据库名称 *
* @param port
* 所使用的端口号
*/
private void startServer(String dbPath, String dbName, int port) ...{
Server server = new Server();// 它可是hsqldb.jar里面的类啊。
server.setDatabaseName(0, dbName);
server.setDatabasePath(0, dbPath + dbName);
if (port != -1) ...{
server.setPort(port);
}
server.setSilent(true);
server.start();
System.out.println("hsqldb started...");
// 等待Server启动
try ...{
Thread.sleep(800);
} catch (InterruptedException e) ...{
// do nothing
}
}
/** *//**
* * Listener销毁方法,在Web应用终止的时候执行"shutdown"命令关闭数据库.
*/
public void contextDestroyed(ServletContextEvent arg0) ...{
// 这里就不用说了,自然是关闭数据库操作
Connection conn = null;
try ...{
System.out.println("ADL应用终止");
Class.forName("org.hsqldb.jdbcDriver");
conn = DriverManager.getConnection(
"jdbc:hsqldb:hsql://localhost:9002/samplerte", "sa", "");
Statement stmt = conn.createStatement();
stmt.executeUpdate("SHUTDOWN;");
System.out.println("持久化hsqldb");
} catch (Exception e) ...{
e.printStackTrace();
// do nothing
}
}
/** *//**
* 将指定文件复制到目标文件
*
* @param file1
* 源文件
* @param file2
* 目标文件
* @return 复制是否成功
*/
public static boolean copy(String file1, String file2) ...{
try // must try and catch,otherwide will compile error
...{
// instance the File as file_in and file_out
java.io.File file_in = new java.io.File(file1);
java.io.File file_out = new java.io.File(file2);
FileInputStream in1 = new FileInputStream(file_in);
FileOutputStream out1 = new FileOutputStream(file_out);
byte[] bytes = new byte[1024];
int c;
while ((c = in1.read(bytes)) != -1)
out1.write(bytes, 0, c);
in1.close();
out1.close();
return (true); // if success then return true
} catch (Exception e) ...{
e.printStackTrace();
System.out.println("Error!");
return (false); // if fail then return false
}
}
}
接着是hsqldb的初始化script
CREATE
SCHEMA
PUBLIC
AUTHORIZATION
DBA
CREATE SCHEMA SAMPLERTE AUTHORIZATION DBA
CREATE MEMORY TABLE APPLICATIONDATA(DATANAME VARCHAR ( 50 ) NOT NULL PRIMARY KEY ,MEMORYVALUE VARCHAR ( 50 ),NUMBERVALUE INTEGER )
CREATE MEMORY TABLE COURSEINFO(COURSEID VARCHAR ( 50 ) NOT NULL PRIMARY KEY ,COURSETITLE VARCHAR ( 200 ),ACTIVE BOOLEAN NOT NULL ,IMPORTDATETIME VARCHAR ( 50 ),START BOOLEAN NOT NULL ,TOC BOOLEAN NOT NULL )
CREATE MEMORY TABLE ITEMINFO(COURSEID VARCHAR ( 255 ),ORGANIZATIONIDENTIFIER VARCHAR ( 255 ),ITEMIDENTIFIER VARCHAR ( 255 ),RESOURCEIDENTIFIER VARCHAR ( 255 ),LAUNCH VARCHAR ( 255 ),TYPE VARCHAR ( 255 ),TITLE VARCHAR ( 255 ),PARAMETERSTRING VARCHAR ( 255 ),PERSISTSTATE VARCHAR ( 255 ),DATAFROMLMS VARCHAR ( 255 ),MINNORMALIZEDMEASURE VARCHAR ( 255 ),ATTEMPTABSOLUTEDURATIONLIMIT VARCHAR ( 255 ),TIMELIMITACTION VARCHAR ( 255 ),COMPLETIONTHRESHOLD VARCHAR ( 255 )," Next " BOOLEAN NOT NULL ,PREVIOUS BOOLEAN NOT NULL , EXIT BOOLEAN NOT NULL ,EXITALL BOOLEAN NOT NULL ,ABANDON BOOLEAN NOT NULL ,ACTIVITYID INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1 ) NOT NULL PRIMARY KEY ,SUSPEND BOOLEAN NOT NULL )
CREATE MEMORY TABLE SCOCOMMENTS(ACTIVITYID INTEGER NOT NULL ,COMMENTID INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1 ) NOT NULL PRIMARY KEY ,COMMENT VARCHAR ( 255 ),COMMENTDATETIME DATE,COMMENTLOCATION VARCHAR ( 255 ))
CREATE MEMORY TABLE USERCOURSEINFO(USERID VARCHAR ( 50 ),COURSEID VARCHAR ( 50 ),SUSPENDALL BOOLEAN NOT NULL )
CREATE MEMORY TABLE USERINFO(USERID VARCHAR ( 50 ) NOT NULL PRIMARY KEY ,LASTNAME VARCHAR ( 50 ),FIRSTNAME VARCHAR ( 50 ),ADMIN BOOLEAN NOT NULL ,PASSWORD VARCHAR ( 50 ),ACTIVE BOOLEAN NOT NULL ,AUDIOLEVEL VARCHAR ( 50 ),AUDIOCAPTIONING INTEGER ,DELIVERYSPEED VARCHAR ( 50 ),LANGUAGE VARCHAR ( 50 ))
ALTER TABLE ITEMINFO ALTER COLUMN ACTIVITYID RESTART WITH 1
ALTER TABLE SCOCOMMENTS ALTER COLUMN COMMENTID RESTART WITH 1
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 10
SET SCHEMA SAMPLERTE
INSERT INTO APPLICATIONDATA VALUES ( ' nextCourseID ' , NULL , 0 )
CREATE SCHEMA SAMPLERTE AUTHORIZATION DBA
CREATE MEMORY TABLE APPLICATIONDATA(DATANAME VARCHAR ( 50 ) NOT NULL PRIMARY KEY ,MEMORYVALUE VARCHAR ( 50 ),NUMBERVALUE INTEGER )
CREATE MEMORY TABLE COURSEINFO(COURSEID VARCHAR ( 50 ) NOT NULL PRIMARY KEY ,COURSETITLE VARCHAR ( 200 ),ACTIVE BOOLEAN NOT NULL ,IMPORTDATETIME VARCHAR ( 50 ),START BOOLEAN NOT NULL ,TOC BOOLEAN NOT NULL )
CREATE MEMORY TABLE ITEMINFO(COURSEID VARCHAR ( 255 ),ORGANIZATIONIDENTIFIER VARCHAR ( 255 ),ITEMIDENTIFIER VARCHAR ( 255 ),RESOURCEIDENTIFIER VARCHAR ( 255 ),LAUNCH VARCHAR ( 255 ),TYPE VARCHAR ( 255 ),TITLE VARCHAR ( 255 ),PARAMETERSTRING VARCHAR ( 255 ),PERSISTSTATE VARCHAR ( 255 ),DATAFROMLMS VARCHAR ( 255 ),MINNORMALIZEDMEASURE VARCHAR ( 255 ),ATTEMPTABSOLUTEDURATIONLIMIT VARCHAR ( 255 ),TIMELIMITACTION VARCHAR ( 255 ),COMPLETIONTHRESHOLD VARCHAR ( 255 )," Next " BOOLEAN NOT NULL ,PREVIOUS BOOLEAN NOT NULL , EXIT BOOLEAN NOT NULL ,EXITALL BOOLEAN NOT NULL ,ABANDON BOOLEAN NOT NULL ,ACTIVITYID INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1 ) NOT NULL PRIMARY KEY ,SUSPEND BOOLEAN NOT NULL )
CREATE MEMORY TABLE SCOCOMMENTS(ACTIVITYID INTEGER NOT NULL ,COMMENTID INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1 ) NOT NULL PRIMARY KEY ,COMMENT VARCHAR ( 255 ),COMMENTDATETIME DATE,COMMENTLOCATION VARCHAR ( 255 ))
CREATE MEMORY TABLE USERCOURSEINFO(USERID VARCHAR ( 50 ),COURSEID VARCHAR ( 50 ),SUSPENDALL BOOLEAN NOT NULL )
CREATE MEMORY TABLE USERINFO(USERID VARCHAR ( 50 ) NOT NULL PRIMARY KEY ,LASTNAME VARCHAR ( 50 ),FIRSTNAME VARCHAR ( 50 ),ADMIN BOOLEAN NOT NULL ,PASSWORD VARCHAR ( 50 ),ACTIVE BOOLEAN NOT NULL ,AUDIOLEVEL VARCHAR ( 50 ),AUDIOCAPTIONING INTEGER ,DELIVERYSPEED VARCHAR ( 50 ),LANGUAGE VARCHAR ( 50 ))
ALTER TABLE ITEMINFO ALTER COLUMN ACTIVITYID RESTART WITH 1
ALTER TABLE SCOCOMMENTS ALTER COLUMN COMMENTID RESTART WITH 1
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 10
SET SCHEMA SAMPLERTE
INSERT INTO APPLICATIONDATA VALUES ( ' nextCourseID ' , NULL , 0 )
这样就解决adl的跨平台了。下篇会解决如何提取rte环境里的业务系统关心的learner学习sco的状态信息