2021SC@SDUSC
在 openmeetings-install分析(三)中,我们已经分析了关键方法loadAll()中的checkInstalled方法和loadSystem方法,在这篇文章中,我们来分析loadAll方法中的剩下2个方法:loadInitUserAndGroup和loadDefaultRooms
先回顾一下loadAll方法的代码:
// ImportInitvalues.java
public void loadAll(InstallationConfig cfg, boolean force) throws Exception {
checkInstalled(force);
loadSystem(cfg, force);
loadInitUserAndGroup(cfg);
progress = 80;
loadDefaultRooms(cfg.isCreateDefaultObjects(), cfg.getDefaultLangId());
progress = 100;
}
从前面的分析中,我们知道,前面的几个方法完成了检验之前是否已经执行过安装操作以及加载系统配置,下面我们首先分析loadInitUserAndGroup方法
loadInitUserAndGroup
源码
// ImportInitvalues.java
public void loadInitUserAndGroup(InstallationConfig cfg) throws Exception {
// 设置组的一些属性
Group g = new Group();
g.setName(cfg.getGroup());
g.setInsertedby(1L);
g.setDeleted(false);
g.setInserted(new Date());
// 在持久层对组进行更新
g = groupDao.update(g, null);
// 更新相应的配置对象
Configuration c = cfgDao.get(CONFIG_DEFAULT_GROUP_ID);
c.setValueN(g.getId());
cfgDao.update(c, null);
// 设置用户的一些属性
// getNewUserInstance方法中会新建一个User对象,设置该对象的各种信息
User u = getNewUserInstance(null);
u.setType(User.Type.user);
u.getRights().add(Right.Admin);
u.getRights().add(Right.Soap);
u.setLogin(cfg.getUsername());
u.setFirstname("firstname");
u.setLastname("lastname");
u.getAddress().setEmail(cfg.getEmail());
u.getGroupUsers().add(new GroupUser(g, u));
// 在持久层对用户进行更新
u = userDao.update(u, cfg.getPassword(), -1L);
log.debug("Installation - User Added user-Id " + u.getId());
// 若用户id为空,则抛出异常,这个方法没有设置id,为什么不会抛异常
if (u.getId() == null) {
throw new InstallException("Unable to add user");
}
}
从方法名可以猜出,这个类用于初始化用户和组
对代码的解释我已经以注释形式写在代码旁边了。其它例如InstallationConfig、Configuration等在前面的文章已经分析过,这里就不再赘述
值得注意的一个问题是,这个方法中并没有给新建的user对象设置id,但是最后它并没有抛出异常,我看了一下日志,它会正常打印出:
即执行了
log.debug("Installation - User Added user-Id " + u.getId());
语句,并且getId的结果是1
那么它是在哪里设置了它的id呢?
user对象是在getNewUserInstance方法中新建的,但是getNewUserInstance方法内也没有setId方法
// UserDao.java
public static User getNewUserInstance(User currentUser) {
User user = new User();
user.setSalutation(Salutation.mr);
user.setRights(getDefaultRights());
user.setLanguageId(getDefaultLang());
user.setTimeZoneId(getTimeZone(currentUser).getID());
user.setForceTimeZoneCheck(false);
user.setAge(new Date());
user.setLastlogin(new Date());
Address address = new Address();
address.setCountry(Locale.getDefault().getCountry());
user.setAddress(address);
user.setShowContactData(false);
user.setShowContactDataToContacts(false);
return user;
}
会不会是在user对象的某个属性的set方法里面包含了setId呢?这样它看起来是设置了其他属性,其实也把id设置了?我又去User类里面查找setId,但是查找结果只有一个,也就是它原本的setId方法…
于是我想到,它的id可能不是手动设置的,可能是插入数据库后,数据库自动为表中的数据递增地添加id
我开始关注
// 在持久层对用户进行更新
u = userDao.update(u, cfg.getPassword(), -1L);
这一条语句
userDao的update方法其实和之前我们分析的OAuth2Dao类的update方法类似
@Override
public User update(User u, Long userId) {
if (u.getId() == null) {
if (u.getRegdate() == null) {
u.setRegdate(new Date());
}
u.setInserted(new Date());
em.persist(u);
} else {
u.setUpdated(new Date());
u = em.merge(u);
}
return u;
}
一开始进入方法体时应该是没有id的,所以会走else,em.merge(u)方法表示对数据库进行插入或更新,所以执行完这条语句后,数据库中会多出一个元组(就是一行),然后对于索引id,它会自动地递增添加。em.merge(u)方法返回一个User对象实体,这个对象就是带有id的了,原来是这样…
loadDefaultRooms
源码
//ImportInitvalues.java
public void loadDefaultRooms(boolean createRooms, long langId) {
//传入的参数createRooms为true,langId为1
if (createRooms) {
// 新建房间,第一个参数表示房间名字,第二个表示房间类型,第三个表示房间容量,第四个表示是否是公开的,第五个表示组id
createRoom(LabelDao.getString("install.room.public.interview", langId), Type.interview, 16L, true, null);
createRoom(LabelDao.getString("install.room.public.conference", langId), Type.conference, 32L, true, null);
Room r = createRoom(LabelDao.getString("install.room.public.video.only", langId), Type.conference, 32L, true, null);
// 该房间默认隐藏白板
r.hide(RoomElement.Whiteboard);
// 在持久层添加房间
roomDao.update(r, null);
createRoom(LabelDao.getString("install.room.public.video.wb", langId), Type.conference, 32L, true, null);
createRoom(LabelDao.getString("install.room.public.presentation", langId), Type.presentation, 100L, true, null);
r = createRoom(LabelDao.getString("install.room.presentation.micro", langId), Type.presentation, 100L, true, null);
// 清除隐藏项
r.getHiddenElements().clear();
roomDao.update(r, null);
r = createRoom(LabelDao.getString("install.room.conference.micro", langId), Type.conference, 32L, true, null);
r.getHiddenElements().clear();
roomDao.update(r, null);
createRoom(LabelDao.getString("install.room.private.conference", langId), Type.conference, 32L, false, 1L);
}
}
从方法名可以猜出,这个类用于初始化房间,即会议室
代码的解释见相关注释,下面来关注一下LabelDao.getString这个方法
它是如何获取我们需要的房间名字的?
// LabelDao.java
public static String getString(String key, long langId) {
return _ensureApplication().getOmString(key, langId);
}
LabelDao类在openmeetings-db包下,getString方法接受2个参数,由以上对loadDefaultRooms方法的分析知,第二个传入的实参为1
_ensureApplication()方法会返回一个Application对象,该对象定义在openmeetings-web包下
getOmString方法会在Application类中进行层层转移,应该是一种多语言实现的方法
// Application.java
// getOmString→getString
@Override
public String getOmString(String key, long languageId) {
return getString(key, languageId);
}
//getString(String key, final long languageId)→getString(String key, final Locale loc, String... _params)
public static String getString(String key, final long languageId) {
return getString(key, getLocale(languageId));
}
// 这个方法会根据传入的id获取对应的语言
public static Locale getLocale(final long languageId) {
Locale loc = LabelDao.getLocale(languageId);
if (loc == null) {
loc = Session.exists() ? WebSession.get().getLocale() : Locale.ENGLISH;
}
// 返回id为1的Locale对象
return loc;
}
public static String getString(String key, final Locale loc, String... _params) {
// 判断该线程的应用是否存在
if (!exists()) {
ThreadContext.setApplication(org.apache.wicket.Application.get(appName));
}
String[] params = _params;
// 我们执行这个方法时没有传入第三个参数,所以params = null
// STRINGS_WITH_APP是一个哈希表
// STRINGS_WITH_APP.contains(key)检测哈希表中是否包含传入的key参数
if ((params == null || params.length == 0) && STRINGS_WITH_APP.contains(key)) {
params = new String[]{getApplicationName()};
}
Localizer l = get().getResourceSettings().getLocalizer();
String value = l.getStringIgnoreSettings(key, null, null, loc, null, "[Missing]");
if (params != null && params.length > 0) {
final MessageFormat format = new MessageFormat(value, loc);
value = format.format(params);
}
if (RuntimeConfigurationType.DEVELOPMENT == get().getConfigurationType()) {
value += String.format(" [%s]", key);
}
return value;
}
下面对上述部分代码进行详细分析:
Localizer l = get().getResourceSettings().getLocalizer()
get
get方法返回一个Wicket Application对象
//org/apache/openmeetings/web/app/Application.java
public static Application get() {
// 调用了Wicket Application类的get方法
// Application类是所有Wicket应用程序的基类。
// 要创建Wicket应用程序,通常子类化Application的某些子类,比如WebApplication。
// Wicket 是 Apache 旗下一款基于 Java 的 web 开发框架。
return (Application)org.apache.wicket.Application.get(getWicketApplicationName());
}
//org\apache\wicket\Application.class
public static Application get(String applicationKey) {
// 根据传入的key拿到相应的Application基类,applicationKeyToApplication是一个哈希表
return (Application)applicationKeyToApplication.get(applicationKey);
}
getResourceSettings
获得ResourceSettings对象,该类用于获取与资源相关的设置
getLocalizer
获得Localizer对象,Localizer是一个实用程序类,它封装了所有与本地化相关的功能,使框架的所有区域都可以以一致的方式访问它
我们还要分析一下
String value = l.getStringIgnoreSettings(key, null, null, loc, null, "[Missing]");
这段代码,不过内容比较多,就留到下一篇吧