2021SC@SDUSC
软件工程应用与实践——OpenMeetings项目分析(三):
概述:上周分析了项目管理工具maven的各种信息,并对其安装使用等过程进行了阐述,同时分析了maven的配置文件pom.xml中各项的含义;这周开始我着手分析我负责的两个文件包,先分析的是WebService目录;
如图,WebService包含了main下的java文件,site下的xml配置文件以及test目录下的测试文件;主要精力应该集中在分析java文件上。
error包的分析
package org.apache.openmeetings.webservice.error;
import javax.xml.ws.WebFault;
import org.apache.openmeetings.db.dto.basic.ServiceResult;
@WebFault
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 1L;
public static final ServiceException NO_PERMISSION = new ServiceException(ServiceResult.NO_PERMISSION.getMessage());
public ServiceException(String msg) {
super(msg);
}
}
首先分析的是error包下的ServiceException.java;
从名字可看出,是自定义的一个Exception,这个自定义的Exception继承自RuntimeException,表明这是一个运行时的错误 类;同时自定义一个ServiceException的成员变量,调用了openmeetings.db.dto.basic包下的ServiceResult类,表明了一个不被允许的信息;
public static final ServiceResult NO_PERMISSION = new ServiceResult("error.notallowed", Type.ERROR);
后面的构造方法直接通过super使用了父类的构造方法;
类上面的@WebFault是java的注解的一种。
拓展
java的注解:通常也叫做元数据,是一种代码级别的说明,可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
刚开始,打开类报错,信息为无法识别@Webfault,分析之后是这个版本的openmeetings项目需要的jdk只能是jdk8,于是在项目设置里将项目的jdk版本切换后,webfault即找到了。
作用:@webfault用来注释特定于服务的异常类,以自定义故障元素的本地名称和名称空间名称以及故障 bean 的名称。
util包的基础分析
首先通过图标可以看出来BaseWebService是一个抽象类,大概率后续多个类 都要继承自它,因此先从它入手。
点击代码左侧的蓝色图标,就是查找其子类,果然,下面的七个类都是继承自BaseWebService;
同时,左边有个小叶子的图标,可以看出用了Spring管理bean,后续再说;
public abstract class BaseWebService {
private static final Logger log = Red5LoggerFactory.getLogger(BaseWebService.class, getWebAppRootKey());
static IApplication getApp() {
return (IApplication)Application.get(getWicketApplicationName());
}
static <T> T getBean(Class<T> clazz) {
T b = null;
try {
b = getApp()._getOmBean(clazz);
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
if (b == null) {
throw new ServiceException("");
}
return b;
}
static SessiondataDao getSessionDao() {
return getBean(SessiondataDao.class);
}
// this one is fail safe
static Sessiondata check(String sid) {
try {
return getSessionDao().check(sid);
} catch (ServiceException e) {
log.debug("Exception while checking sid", e);
}
return new Sessiondata();
}
static Set<Right> getRights(String sid) {
Sessiondata sd = check(sid);
return getRights(sd.getUserId());
}
static UserDao getUserDao() {
return getBean(UserDao.class);
}
static RoomDao getRoomDao() {
return getBean(RoomDao.class);
}
static FileItemDao getFileDao() {
return getBean(FileItemDao.class);
}
// this one is fail safe
static Set<Right> getRights(Long id) {
try {
return getUserDao().getRights(id);
} catch (ServiceException e) {
log.debug("Exception while getting rights", e);
}
return new HashSet<>();
}
static <T> T performCall(String sid, User.Right level, Function<Sessiondata, T> action) {
return performCall(sid, sd -> AuthLevelUtil.check(getRights(sd.getUserId()), level), action);
}
static <T> T performCall(String sid, Predicate<Sessiondata> allowed, Function<Sessiondata, T> action) {
try {
Sessiondata sd = check(sid);
if (allowed.test(sd)) {
return action.apply(sd);
} else {
throw NO_PERMISSION;
}
} catch (ServiceException err) {
throw err;
} catch (Exception err) {
log.error("[performCall]", err);
throw new ServiceException(err.getMessage());
}
}
}
BaseWebService的结构如上图所示;
不加修饰符的方法即为缺省default;
getApp方法用来得到一个IApplication类型得对象,IApplication是openmeetings-db下的一个接口;
getBean方法使用了泛型,避免了直接写死类型得死板,增加了灵活性;
getSessionDao调用getBean方法,传入SessiondataDao(db.dao包下的一个类)的class对象,返回一个SessiondataDao类型对象;
check方法顾名思义,检查一下传入的String类型得id是不是符合要求;
getRights方法同样接受一个sid参数,返回一个Right的集合,Right是db.entity.user下的一个实体类;同时下面有一个重名的方法但是参数不同,此方法内通过getuserid得到了long类型得id后,调用同为成员方法的另一个getRights,返回一个hashset;
getUserDao和getRoomDao、getFileDao方法就是获得呀一个Dao的对象;
performCall方法同样用参数列表区分不同的函数体,且使用了泛型,其中的一个参数Predicate是一个jdk8下的接口, 提供的为逻辑判断操作,即断言:静态方法isEqual:判断是否相等,并返回一个Predicate对象
默认方法and,or,negate:分别代表逻辑判断与、或、非并都返回一个Predicate对象
方法test:按照给定的Predicate条件进行逻辑判断。
Function也是一个函数式编程接口;它代表的含义是“函数”,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个输入与一个输出
方法根据参数sid,allowed,以及action判断符合条件后,放行,使用apply方法将参数传递,并返回;
同时我也想学习了一下java8的Function的函数式编程接口,函数式接口亦即接口内只有一个方法;下面贴上一段比较经典的代码:
/**
* Function测试
*/
public static void functionTest() {
Function<Integer, Integer> f = s -> s++;
Function<Integer, Integer> g = s -> s * 2;
/**
* 下面表示在执行F时,先执行G,并且执行F时使用G的输出当作输入。
* 相当于以下代码:
* Integer a = g.apply(1);
* System.out.println(f.apply(a));
*/
System.out.println(f.compose(g).apply(1));
/**
* 表示执行F的Apply后使用其返回的值当作输入再执行G的Apply;
* 相当于以下代码
* Integer a = f.apply(1);
* System.out.println(g.apply(a));
*/
System.out.println(f.andThen(g).apply(1));
/**
* identity方法会返回一个不进行任何处理的Function,即输出与输入值相等;
*/
System.out.println(Function.identity().apply("a"));
}
至此,util即工具包下的BaseWebservice这个父类分析完成,下面只需要分析其子类,看每个子类根据自身的不同情况进行了怎么样的特化即可;
测试类的简略分析以及junit介绍
public class TestBaseService {
private static void checkException(Runnable r) {
try {
r.run();
fail("ServiceException expected");
} catch (ServiceException e) {
assertTrue("expected", true);
}
}
@Test
public void testGetBeanExc() {
checkException(() -> BaseWebService.getBean(UserDao.class));
}
@Test
public void testCheck() {
Sessiondata sd = BaseWebService.check(null);
assertNotNull("NOT null Sessiondata should be returned", sd);
assertNull("UserId should be null", sd.getUserId());
}
private static void checkRights(Set<Right> rights) {
assertNotNull("NOT null Rights should be returned", rights);
assertTrue("Rights should be empty", rights.isEmpty());
}
@Test
public void testGetRights1() {
checkRights(BaseWebService.getRights(1L));
}
@Test
public void testGetRights2() {
checkRights(BaseWebService.getRights(""));
}
@Test
public void testPerformCall() {
checkException(() -> BaseWebService.performCall("", sd -> true
, sd -> { throw new RuntimeException("test"); }));
}
}
可以看到,标记为测试的源文件夹,即test下的java文件夹是绿色的,此文件夹对于项目没有任何影响,对于项目的主要代码是透明的存在;idea在编译项目的时候,会将测试的文件夹下的代码忽略;
可以看到,此类里面的方法全部是test开头的,为了测试以上编写的各个类的功能是否正常,其中,@Test是junit的一个标识,导入junit之后,可以不需要写main方法,直接用@Test进行测试;
拓展
JUnit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个。 JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。它包括以下特性:
1、用于测试期望结果的断言(Assertion)
2、用于共享共同测试数据的测试工具
3、用于方便的组织和运行测试的测试套件
4、图形和文本的测试运行器
以上,就是本周对于openmeetings的项目源代码的分析,下一周的任务是首先要把util包下的所有子类分析完,再根据情况分析其他文件,分析不是目的,最终还是为了学习,因此在分析代码时遇到的不会的知识是正常的,应该不惧挑战,积极通过网络寻找资源,学习巩固,将知识存储在自己大脑里在以后遇到类似问题就能更快的解决。