IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> openmeetings-core模块分析的开始 -> 正文阅读

[开发工具]openmeetings-core模块分析的开始

2021SC@SDUSC

在上一篇文章中,我结束了openmeetings项目db模块的源码分析,完成了我负责的两个模块之一。接下来,我将开启openmeetings-core模块的分析。core模块,意为内核模块,也就是说这个模块的源码应该是与系统内核的处理相关,定义了一系列核心功能。

目录

core模块目录结构

util目录

IClientUtil.java

StrongPasswordValidator.java

WebSocketHelper.java

util/ws目录

WsMessageChat.java

WsMessageRoom.java

总结


core模块目录结构

同db模块一样,core模块下有src子目录、openmeetings-core.iml文件、pom.xml文件。其中pom.xml文件是配置文件,在之前的分析中已经介绍过;openm-core.iml文件是idea创建的模块文件,用于java应用开发,存储模块开发相关的信息及模块路径信息等等;src目录则是源码文件。

在src目录下,又存在main、site、test三个子目录。其中main是存放项目的java文件及资源,test是存放用来测试的java文件及其资源,site目录中保存了项目将要生成的各html文件等。因此,我们分析的目标仍然是main目录下的java文件。在src/main/java中,存在org.apache.openmeetings-core包,包中存在许多下面的包,如converter、data、documents、ldap、mail、notifier、remote、rss、service、util等,包中存放java文件,例如util包:

?如converter包:

可见,与db模块相比,整个模块的目录结构几乎是相同的。所以,对于core模块的分析也可以采取同样的思路,即分每个子目录进行分析。按照分析db的习惯,我们将首先开启作为工具的util目录的分析。

util目录

首先看util目录的结构:

在目录下存在一个子目录ws和三个java文件,ws子目录下又存在5个java文件。首先来分析外面的三个java文件。

IClientUtil.java

IClientUtil是对客户相关的工具。首先看引入的内容:

引入了三个类,第一个是经常见到的StreamClient类,后面两个则是较为陌生。

org.red5.server.api.IClient类,其源码大体如下:

可以看到,它的作用是负责获取客户与red5服务器的连接。其具体作用无需深究,了解意义即可。

再看IClientUtil类的定义:

类的字段只有一个,即枚举类型ConAttrs,其字段有omId、sharing、recordingId。再看类的方法:

所有的方法均是static方法,意味着直接通过类名调用。第一个方法是init方法,形参有IClient client、String uid、boolean sharing,返回值类型是void。在方法内部,对client的属性进行设置。所以init方法的目的是,对client的属性设置成本类的枚举类中的字段。

第二个方法是getId,形参是IClient client,返回类型是String。它是要获取client的id属性值,并且以字符串的形式返回。

第三个方法是isSharing,形参是IClient client,返回类型是boolean。它通过对比client的属性值是不是真,来判断client是不是可分享的。

第四、五个方法分别是getRecoringId和setRecordingId,是对client的recordingId进行get和set。

至此,IClientUtil.java分析完毕。它完成了对red5下IClient类的属性值的获取和设置。附上源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.core.util;

import org.apache.openmeetings.db.entity.room.StreamClient;
import org.red5.server.api.IClient;
import org.red5.server.api.scope.IScope;

public class IClientUtil {
	private enum ConAttrs {
		omId
		, sharing
		, recordingId
	}

	public static void init(IClient client, String uid, boolean sharing) {
		client.setAttribute(ConAttrs.omId.name(), uid);
		client.setAttribute(ConAttrs.sharing.name(), sharing);
	}

	/**
	 * Id of {@link StreamClient} for this IConnection
	 *
	 * @param client - {@link IClient} to get if from
	 * @return - Id of {@link StreamClient} for this IConnection, or <code>null</code>
	 */
	public static String getId(IClient client) {
		Object o = client.getAttribute(ConAttrs.omId.name());
		return o instanceof String ? (String)o : null;
	}

	public static boolean isSharing(IClient client) {
		return Boolean.TRUE.equals(client.getAttribute(ConAttrs.sharing.name()));
	}

	public static Long getRecordingId(IScope scope) {
		return (Long)scope.getAttribute(ConAttrs.recordingId.name());
	}

	public static void setRecordingId(IScope scope, Long recordingId) {
		scope.setAttribute(ConAttrs.recordingId.name(), recordingId);
	}
}

StrongPasswordValidator.java

看字面意思,它是要对密码进行严格的验证。应用场景很有可能是在登录时,或者需要进行一些关乎很重要信息的操作时。

首先看引入的内容:

前面的两个都是util模块下OpenmeetingsVariables类下的getMinPasswdLength和getWebAppRootKey方法,都见过。接下来是两个经常见到的类,Locale和Map类。最后一部分,引入了db模块下的LabelDao、User类,还有一些org.apache.wicket.util下的一些类,以及validation相关的验证类,最后还有日志相关的类。

看类的定义:

首先StrongPasswordValidator类实现了IValidator接口,接口定义了validate方法,让实现类来实现验证功能。接着,它有四个字段,前面两个都不再多说。后面两个分别是final boolean web(不可修改,表面要验证的是不是web项目)和User u(用户)。

看定义的方法:

?

先看到的是构造器。第一个构造器调用了第二个构造器,而第二个构造器只是简单赋值,没有什么可说的。?继续看下面的方法:

在下面的方法中是对密码进行一系列确定。第一个noDigit方法是判断字符串password是不是没有数字,返回的是password为空或者password中不存在数字,判断方法是正则表达式。下面的noSymbol、noUpperCase、noLowerCase、badLength均是这样判断。再往下看:

下面的第一个方法是checkWord(String password,String word),是static方法,返回值是boolean。在方法体内,先判断word是不是空或者长度小于3,如果是则返回false。接着,对word进行每三个字符的扫描,判断password中是否含有这三个字符(忽略大小写),如果有则返回true,如果扫描完还没有则返回false。它的目的应该是检查password中是否存在某个关键词之类的。

再下面的方法是hasStopWords(String password),返回值也是boolean。在其中调用了checkWord方法,检查password中是否有login等特殊字符串。

再往下看:

重载了两个error方法,返回值均是void。第一个error调用了第二个error方法,所以看第二个。第二个error方法的形参是IValidatable<String> pass(String类型的IValidatable)、String key、Map<String,Object> params。在方法体内,分为项目是不是web进行操作。具体细节不需要展开,只要说明它的目的是对错误信息进行设置和存储即可。

最后的两个方法是validate和setUser。

在validate方法中就要进行综合的验证了,在其中调用了之前定义的badLength、noLowerCase、noUpperCase等方法,并且进行错误的收集。

最后的setUser只是个setter。

至此,StrongPasswordValidator类分配完毕。它的作用是完成对密码的一系列检验。附上其源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.core.util;

import static org.apache.openmeetings.util.OpenmeetingsVariables.getMinPasswdLength;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;

import java.util.Locale;
import java.util.Map;

import org.apache.openmeetings.db.dao.label.LabelDao;
import org.apache.openmeetings.db.entity.user.User;
import org.apache.wicket.util.collections.MicroMap;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidator;
import org.apache.wicket.validation.ValidationError;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;

public class StrongPasswordValidator implements IValidator<String> {
	private static final long serialVersionUID = 1L;
	private static final Logger log = Red5LoggerFactory.getLogger(StrongPasswordValidator.class, getWebAppRootKey());
	private final boolean web;
	private User u;

	public StrongPasswordValidator(final User u) {
		this(true, u);
	}

	public StrongPasswordValidator(final boolean web, final User u) {
		this.web = web;
		this.u = u;
	}

	private static boolean noDigit(String password) {
		return password == null || !password.matches(".*\\d+.*");
	}

	private static boolean noSymbol(String password) {
		return password == null || !password.matches(".*[!@#$%^&*\\]\\[]+.*");
	}

	private static boolean noUpperCase(String password) {
		return password == null || password.equals(password.toLowerCase(Locale.ROOT));
	}

	private static boolean noLowerCase(String password) {
		return password == null || password.equals(password.toUpperCase(Locale.ROOT));
	}

	private static boolean badLength(String password) {
		return password == null || password.length() < getMinPasswdLength();
	}

	private static boolean checkWord(String password, String word) {
		if (Strings.isEmpty(word) || word.length() < 3) {
			return false;
		}
		for (int i = 0; i < word.length() - 3; ++i) {
			String substr = word.toLowerCase(Locale.ROOT).substring(i, i + 3);
			if (password.toLowerCase(Locale.ROOT).indexOf(substr) > -1) {
				return true;
			}
		}
		return false;
	}

	private boolean hasStopWords(String password) {
		if (checkWord(password, u.getLogin())) {
			return true;
		}
		if (u.getAddress() != null) {
			String email = u.getAddress().getEmail();
			if (!Strings.isEmpty(email)) {
				for (String part : email.split("[.@]")) {
					if (checkWord(password, part)) {
						return true;
					}
				}
			}
		}
		return false;
	}

	private void error(IValidatable<String> pass, String key) {
		error(pass, key, null);
	}

	private void error(IValidatable<String> pass, String key, Map<String, Object> params) {
		if (web) {
			ValidationError err = new ValidationError().addKey(key);
			if (params != null) {
				err.setVariables(params);
			}
			pass.error(err);
		} else {
			String msg = LabelDao.getString(key, 1L);
			if (params != null && !params.isEmpty() && !Strings.isEmpty(msg)) {
				for (Map.Entry<String, Object> e : params.entrySet()) {
					msg = msg.replace(String.format("${%s}", e.getKey()), "" + e.getValue());
				}
			}
			log.warn(msg);
			pass.error(new ValidationError(msg));
		}
	}

	@Override
	public void validate(IValidatable<String> pass) {
		if (badLength(pass.getValue())) {
			error(pass, "bad.password.short", new MicroMap<String, Object>("0", getMinPasswdLength()));
		}
		if (noLowerCase(pass.getValue())) {
			error(pass, "bad.password.lower");
		}
		if (noUpperCase(pass.getValue())) {
			error(pass, "bad.password.upper");
		}
		if (noDigit(pass.getValue())) {
			error(pass, "bad.password.digit");
		}
		if (noSymbol(pass.getValue())) {
			error(pass, "bad.password.special");
		}
		if (hasStopWords(pass.getValue())) {
			error(pass, "bad.password.stop");
		}
	}

	public void setUser(User u) {
		this.u = u;
	}
}

WebSocketHelper.java

作为习惯于开发前端的人,我对websocket相对熟悉。它在服务端可以操作socket套接字,不但可以接收前端发来的消息,还可以主动向前端发送信息,可以用来向前端发通知等等。因此,WebSocketHelper应该是对客户端与服务端之间信息的交流提供工具。

直接看类的定义:

类一共有5个字段。第一个是司空见惯的log,后面的几个都定义了一系列static final的字符串变量,在后面作为标志或者信息。看类的方法:

首先看到的是static方法setScope,返回类型是JSONObject,形参是JSONObject o、ChatMessage m(发送的消息)、long curUserId。在方法体内,首先定义了两个空串scope、scopeName,然后判断m是要发送给用户还是房间。如果是用户,则根据curUserId与m对比,获取到发送的用户的id,然后对scope进行相应标志。同理,对发送给房间的消息也是这样。如果是同时发送给用户和房间,则把scope进行"ID_ALL"的标志。最终,将scope和scopeName封装到o中返回。所以,setScope方法就是根据其接收方的类型对发送的消息进行相应的标志。

再下面看到了:

这是getMessage方法,也是静态方法,返回类型是JSONObject,形参有User curUser、List<ChatMessage> list、BiConsumer<JSONObject,User> uFmt。方法内部代码比较多,但逻辑很清晰,就是把list中的每个消息封装成对象,然后放入对象数组中,最终返回一个包含了该数组的对象。也就是说,getMessage就是对获取的消息进行封装,便于以后的访问与提取。

再往下看,可以看到封装好的send方法,进行消息的发送:

send方法比较特殊的地方在于,它的形参里面包含了函数类型,这是我在以往的java代码中很少看到的。传入了函数对象,就可以在方法内部调用这个函数。在send方法内部,新建一个线程,在线程内部进行消息的发送,并且start这个线程。?再后面,定义了sendUser、sendRoom等方法,其中都调用了send方法,进行消息的发送。

所以,整体来看,WebSocketHelper类定义了消息的收发方法,供其他地方可调用。

附上源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.core.util;

import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.getApp;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;

import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.openmeetings.IApplication;
import org.apache.openmeetings.core.util.ws.WsMessageAll;
import org.apache.openmeetings.core.util.ws.WsMessageChat;
import org.apache.openmeetings.core.util.ws.WsMessageRoom;
import org.apache.openmeetings.core.util.ws.WsMessageRoomMsg;
import org.apache.openmeetings.core.util.ws.WsMessageUser;
import org.apache.openmeetings.db.entity.basic.ChatMessage;
import org.apache.openmeetings.db.entity.basic.Client;
import org.apache.openmeetings.db.entity.room.Room.Right;
import org.apache.openmeetings.db.entity.user.User;
import org.apache.openmeetings.db.manager.IClientManager;
import org.apache.openmeetings.db.util.FormatHelper;
import org.apache.openmeetings.db.util.ws.RoomMessage;
import org.apache.openmeetings.db.util.ws.TextRoomMessage;
import org.apache.openmeetings.util.ws.IClusterWsMessage;
import org.apache.wicket.Application;
import org.apache.wicket.protocol.ws.WebSocketSettings;
import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
import org.apache.wicket.protocol.ws.concurrent.Executor;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;

import com.github.openjson.JSONArray;
import com.github.openjson.JSONObject;

public class WebSocketHelper {
	private static final Logger log = Red5LoggerFactory.getLogger(WebSocketHelper.class, getWebAppRootKey());
	public static final String ID_TAB_PREFIX = "chatTab-";
	public static final String ID_ALL = ID_TAB_PREFIX + "all";
	public static final String ID_ROOM_PREFIX = ID_TAB_PREFIX + "r";
	public static final String ID_USER_PREFIX = ID_TAB_PREFIX + "u";

	private static JSONObject setScope(JSONObject o, ChatMessage m, long curUserId) {
		String scope, scopeName = null;
		if (m.getToUser() != null) {
			User u = curUserId == m.getToUser().getId() ? m.getFromUser() : m.getToUser();
			scope = ID_USER_PREFIX + u.getId();
			scopeName = u.getDisplayName();
		} else if (m.getToRoom() != null) {
			scope = ID_ROOM_PREFIX + m.getToRoom().getId();
			o.put("needModeration", m.isNeedModeration());
		} else {
			scope = ID_ALL;
		}
		return o.put("scope", scope).put("scopeName", scopeName);
	}

	public static JSONObject getMessage(User curUser, List<ChatMessage> list, BiConsumer<JSONObject, User> uFmt) {
		JSONArray arr = new JSONArray();
		final FastDateFormat fullFmt = FormatHelper.getDateTimeFormat(curUser);
		final FastDateFormat dateFmt = FormatHelper.getDateFormat(curUser);
		final FastDateFormat timeFmt = FormatHelper.getTimeFormat(curUser);
		for (ChatMessage m : list) {
			String smsg = m.getMessage();
			smsg = smsg == null ? smsg : " " + smsg.replaceAll("&nbsp;", " ") + " ";
			JSONObject from = new JSONObject()
					.put("id", m.getFromUser().getId())
					.put("displayName", m.getFromName())
					.put("name", m.getFromUser().getDisplayName());
			if (uFmt != null) {
				uFmt.accept(from, m.getFromUser());
			}
			arr.put(setScope(new JSONObject(), m, curUser.getId())
				.put("id", m.getId())
				.put("message", smsg)
				.put("from", from)
				.put("actions", curUser.getId() == m.getFromUser().getId() ? "short" : "full")
				.put("sent", fullFmt.format(m.getSent()))
				.put("date", dateFmt.format(m.getSent()))
				.put("time", timeFmt.format(m.getSent()))
				);
		}
		return new JSONObject()
			.put("type", "chat")
			.put("msg", arr);
	}

	public static void sendClient(final Client _c, byte[] b) {
		if (_c != null) {
			send(a -> Arrays.asList(_c), (t, c) -> {
				try {
					t.sendMessage(b, 0, b.length);
				} catch (IOException e) {
					log.error("Error while broadcasting byte[] to room", e);
				}
			}, null);
		}
	}

	public static void send(IClusterWsMessage _m) {
		if (_m instanceof WsMessageRoomMsg) {
			sendRoom(((WsMessageRoomMsg)_m).getMsg(), false);
		} else if (_m instanceof WsMessageRoom) {
			WsMessageRoom m = (WsMessageRoom)_m;
			sendRoom(m.getRoomId(), m.getMsg(), false);
		} else if (_m instanceof WsMessageChat) {
			WsMessageChat m = (WsMessageChat)_m;
			sendRoom(m.getChatMessage(), m.getMsg(), false);
		} else if (_m instanceof WsMessageUser) {
			WsMessageUser m = (WsMessageUser)_m;
			sendUser(m.getUserId(), m.getMsg(), false);
		} else if (_m instanceof WsMessageAll) {
			sendAll(((WsMessageAll)_m).getMsg(), false);
		}
	}

	public static void sendRoom(final RoomMessage m) {
		sendRoom(m, true);
	}

	private static void sendRoom(final RoomMessage m, boolean publish) {
		if (publish) {
			publish(new WsMessageRoomMsg(m));
		}
		log.debug("Sending WebSocket message: {} {}", m.getType(), m instanceof TextRoomMessage ? ((TextRoomMessage)m).getText() : "");
		sendRoom(m.getRoomId(), (t, c) -> t.sendMessage(m), null);
	}

	public static void sendRoom(final Long roomId, final JSONObject m) {
		sendRoom(roomId, m, true);
	}

	private static void sendRoom(final Long roomId, final JSONObject m, boolean publish) {
		if (publish) {
			publish(new WsMessageRoom(roomId, m));
		}
		sendRoom(roomId, m, null, null);
	}

	public static void sendRoom(ChatMessage m, JSONObject msg) {
		sendRoom(m, msg, true);
	}

	private static void sendRoom(ChatMessage m, JSONObject msg, boolean publish) {
		if (publish) {
			publish(new WsMessageChat(m, msg));
		}
		sendRoom(m.getToRoom().getId(), msg
				, c -> !m.isNeedModeration() || (m.isNeedModeration() && c.hasRight(Right.moderator))
				, null);
	}

	public static void sendUser(final Long userId, final String m) {
		sendUser(userId, m, true);
	}

	private static void sendUser(final Long userId, final String m, boolean publish) {
		if (publish) {
			publish(new WsMessageUser(userId, m));
		}
		send(a -> ((IApplication)a).getOmBean(IClientManager.class).listByUser(userId), (t, c) -> {
			try {
				t.sendMessage(m);
			} catch (IOException e) {
				log.error("Error while sending message to user", e);
			}
		}, null);
	}

	public static void sendAll(final String m) {
		sendAll(m, true);
	}

	private static void sendAll(final String m, boolean publish) {
		if (publish) {
			publish(new WsMessageAll(m));
		}
		new Thread(() -> {
			Application app = (Application)getApp();
			WebSocketSettings settings = WebSocketSettings.Holder.get(app);
			IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
			Executor executor = settings.getWebSocketPushMessageExecutor(); // new NewThreadExecutor();
			for (IWebSocketConnection c : reg.getConnections(app)) {
				executor.run(() -> {
					try {
						c.sendMessage(m);
					} catch (IOException e) {
						log.error("Error while sending message to ALL", e);
					}
				});
			}
		}).start();
	}

	protected static void publish(IClusterWsMessage m) {
		IApplication app = getApp();
		new Thread(() -> {
			app.publishWsTopic(m);
		}).start();
	}

	protected static void sendRoom(final Long roomId, final JSONObject m, Predicate<Client> check, BiFunction<JSONObject, Client, String> func) {
		log.debug("Sending WebSocket message: {}", m);
		sendRoom(roomId, (t, c) -> {
			try {
				t.sendMessage(func == null ? m.toString() : func.apply(m, c));
			} catch (IOException e) {
				log.error("Error while broadcasting message to room", e);
			}
		}, check);
	}

	private static void sendRoom(final Long roomId, BiConsumer<IWebSocketConnection, Client> consumer, Predicate<Client> check) {
		send(a -> ((IApplication)a).getOmBean(IClientManager.class).listByRoom(roomId), consumer, check);
	}

	private static void send(
			final Function<Application, Collection<Client>> func
			, BiConsumer<IWebSocketConnection, Client> consumer
			, Predicate<Client> check)
	{
		new Thread(() -> {
			Application app = (Application)getApp();
			WebSocketSettings settings = WebSocketSettings.Holder.get(app);
			IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
			Executor executor = settings.getWebSocketPushMessageExecutor(); //new NewThreadExecutor();
			for (Client c : func.apply(app)) {
				if (check == null || check.test(c)) {
					final IWebSocketConnection wc = reg.getConnection(app, c.getSessionId(), new PageIdKey(c.getPageId()));
					if (wc != null && wc.isOpen()) {
						executor.run(() -> consumer.accept(wc, c));
					}
				}
			}
		}).start();
	}

	public static class NewThreadExecutor implements Executor {
		@Override
		public void run(Runnable command) {
			new Thread(() -> {
				command.run();
			}).start();
		}
	}
}

util/ws目录

刚才将util目录下的三个java文件分析结束了,接下来需要展开分析的是util目录下的ws子目录,其结构如下:

在ws目录下有5个java文件,每个java文件与不同的角色相关,下面来分析一下。

WsMessageChat.java

WsMessageChat显然是与聊天相关的。看引入的内容:

引入的内容包括了db模块下定义的实体ChatMessage,还有util模块下的NullStringer类和IClusterWsMessage接口。IClusterWsMessage接口比较简单,只是代表可序列化。NullStringer类是JSONStringer的子类,可以封装字符串。最后引入的是JSONObject,非常熟悉了。

看类的定义:

首先类实现了IClusterWsMessage接口。类体内定义了三个字段,分别是序列化Id、final ChatMessage m、final String msg,都比较容易理解。

它的构造器传入两个参数:ChatMessage m和JSONObject msg。其中,对this.msg赋值时,是通过new NullStringer()进行初始化,调用JSONObject对象的toString方法进行赋值。后面两个方法就是getter了。

WsMessageChat类并没有太多很难的内容,字段内只是简单地定义了消息实体ChatMessage和消息内容msg。源码都在图中,不再给出。

WsMessageRoom.java

WsMessageRoom类的定义结构与WsMessageChat几乎一样:

字段的内容和方法都如出一辙,因此就不再多提。包括ws目录下的其余的三个java文件:WsMessageRoomMsg.java、WsMessageUser.java、WsMessageAll.java的内容也完全一样,所以对它们的分析也直接跳过了。

总结

在本文中,我开启了core模块的源码分析,梳理了core的结构,并且完成了util子目录的分析。接下来的几篇文章里,我将会逐步完成core模块的源码分析,最后从整体上总结一下一学期中对db模块和core模块的分析探索。

  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2021-11-23 12:34:27  更:2021-11-23 12:35:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 18:54:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码