openmeetings-install分析(八)——Admin类询问阶段processInstall分析(1)

2021SC@SDUSC


在上篇文章中,我结合Apache Commons CLI解析命令行的知识点对Admin类进行了相关方面的分析。接下来,我将具体分析该类询问阶段涉及到的一些方法,这些方法即为应用程序通过查询CommandLine的选项值确定采用的执行分支。
先回顾一下询问阶段的代码
    // Admin.java
	void process(String... args) throws Exception {
		...
		String file = cmdl.getOptionValue("file", "");
		switch(cmd) {
			case install:
				step = "Install";
				processInstall(file);
				break;
			case backup:
				step = "Backup";
				processBackup(file);
				break;
			case restore:
				step = "Restore";
				processRestore(checkRestoreFile(file));
				break;
			case files:
				step = "Files";
				processFiles();
				break;
			...
		}
	}


通过判断CommandLine的实例cmdl中包含的选项,赋给Command的实例cmd不同的值,若cmd值为“install”时,执行processInstall方法,其他以此类推。下面我们依次分析执行的这几个方法。

    // Admin.java
	private void processInstall(String file) throws Exception {
	    // 如果命令行包含参数file以及user/email/group中的一个,则需要用户指定文件或管理员
		if (cmdl.hasOption("file") && (cmdl.hasOption("user") || cmdl.hasOption("email") || cmdl.hasOption("group"))) {
			log("Please specify even 'file' option or 'admin user'.");
			throw new ExitException();
		}
		boolean force = cmdl.hasOption("force");
		// 以下是根据CommandLine中包含的选项,对cfg中的属性值进行设置,cfg是来自installation包下的InstallationConfig实例,之前已经分析过
		if (cmdl.hasOption("skip-default-objects")) {
			cfg.setCreateDefaultObjects(false);
		}
		if (cmdl.hasOption("disable-frontend-register")) {
			cfg.setAllowFrontendRegister(false);
		}
		if (cmdl.hasOption(OPTION_MAIL_REFERRER)) {
			cfg.setMailReferer(cmdl.getOptionValue(OPTION_MAIL_REFERRER));
		}
		if (cmdl.hasOption(OPTION_MAIL_SERVER)) {
			cfg.setSmtpServer(cmdl.getOptionValue(OPTION_MAIL_SERVER));
		}
		if (cmdl.hasOption(OPTION_MAIL_PORT)) {
			cfg.setSmtpPort(Integer.valueOf(cmdl.getOptionValue(OPTION_MAIL_PORT)));
		}
		if (cmdl.hasOption(OPTION_MAIL_USER)) {
			cfg.setMailAuthName(cmdl.getOptionValue(OPTION_MAIL_USER));
		}
		if (cmdl.hasOption(OPTION_MAIL_PASS)) {
			cfg.setMailAuthPass(cmdl.getOptionValue(OPTION_MAIL_PASS));
		}
		if (cmdl.hasOption("email-use-tls")) {
			cfg.setMailUseTls(true);
		}
		if (cmdl.hasOption("default-language")) {
			cfg.setDefaultLangId(Integer.parseInt(cmdl.getOptionValue("default-language")));
		}
		ConnectionProperties connectionProperties;
		// 用于创建一个持久层的File实例,OmFileHelper来自util包,在这就不展开分析了
		File conf = OmFileHelper.getPersistence();
		if (!conf.exists() || cmdl.hasOption(OPTION_DB_TYPE) || cmdl.hasOption(OPTION_DB_HOST)
				|| cmdl.hasOption(OPTION_DB_PORT) || cmdl.hasOption(OPTION_DB_NAME) || cmdl.hasOption(OPTION_DB_USER)
				|| cmdl.hasOption(OPTION_DB_PASS))
		{
			String dbType = cmdl.getOptionValue(OPTION_DB_TYPE, DbType.derby.name());
			// ConnectionPropertiesPatcher是同包下的一个类,在下文我将对其进行具体分析
			connectionProperties = ConnectionPropertiesPatcher.patch(dbType
					, cmdl.getOptionValue(OPTION_DB_HOST, "localhost")
					, cmdl.getOptionValue(OPTION_DB_PORT, null)
					, cmdl.getOptionValue(OPTION_DB_NAME, null)
					, cmdl.getOptionValue(OPTION_DB_USER, null)
					, cmdl.getOptionValue(OPTION_DB_PASS, null)
					);
		} else {
			//get properties from existent persistence.xml
			connectionProperties = ConnectionPropertiesPatcher.getConnectionProperties(conf);
		}
		...
	}

我们来具体分析一下这段代码

// Admin.java
connectionProperties = ConnectionPropertiesPatcher.patch(dbType
					, cmdl.getOptionValue(OPTION_DB_HOST, "localhost")
					, cmdl.getOptionValue(OPTION_DB_PORT, null)
					, cmdl.getOptionValue(OPTION_DB_NAME, null)
					, cmdl.getOptionValue(OPTION_DB_USER, null)
					, cmdl.getOptionValue(OPTION_DB_PASS, null)
					);

看一下patch方法的源码

    // ConnectionPropertiesPatcher.java
	public static ConnectionProperties patch(String dbType, String host, String port, String db, String user, String pass) throws Exception {
		ConnectionProperties props = getConnectionProperties(OmFileHelper.getPersistence(dbType));
		props.setLogin(user);
		props.setPassword(pass);
		ConnectionPropertiesPatcher patcher = getPatcher(props);
		props.setURL(patcher.getUrl(props.getURL(), host, port, db));
		patch(props);
		return props;
	}

该方法传入6个参数,第一个是dbType,后面几个是数据库的主机名、端口号、数据库名、用户名、密码,这些都通过CommandLine里的Option选项值获得

    // ConnectionPropertiesPatcher.java
	public static ConnectionProperties getConnectionProperties(File conf) throws Exception {
	    // 新建一个ConnectionProperties对象
		ConnectionProperties props = new ConnectionProperties();
		// 通过传入的File实例获取Document实例
		Document doc = getDocument(conf);
		Attr attr = getConnectionProperties(doc);
		String[] tokens = attr.getValue().split(",");
		loadProperties(tokens, props);
		return props;
	}

这里的Document是一个接口,表示整个HTML或XML文档。Attr类表示Element对象的属性,Element对象是是HTML DOM 节点。HTML DOM 是 W3C 标准,定义了用于 HTML 的一系列标准的对象,以及访问和处理 HTML 文档的标准方法。HTML DOM 独立于平台和编程语言,它可被任何编程语言诸如 Java、JavaScript 和 VBScript 使用。

Document、Attr、Element都是来自org.w3c.dom(java dom)包,org.w3c.dom为文档对象模型 (DOM) 提供接口,该模型是 Java API for XML Processing 的组件 API,用于Java对XML文件进行操作,可以将XML看做是一颗树,DOM就是对这颗树的一个数据结构的描述。
org.w3c.dom接口摘要
Attr attr = getConnectionProperties(doc);中的getConnectionProperties方法体如下

	private static Attr getConnectionProperties(Document doc) throws Exception {
		XPath xPath = XPathFactory.newInstance().newXPath();
		XPathExpression expr = xPath.compile("/persistence/persistence-unit/properties/property[@name='openjpa.ConnectionProperties']");

		Element element = (Element)expr.evaluate(doc, XPathConstants.NODE);
		return element.getAttributeNode("value");
	}

XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
XPath 是 W3C XSLT 标准的主要元素,并且 XQuery 和 XPointer 都构建于 XPath 表达之上。因此,对 XPath 的理解是很多高级 XML 应用的基础。
newInstance
newInstance方法使用默认的对象模型DEFAULT_OBJECT_MODEL_URI去创建一个XPathFactory实例,newXPath()使用在实例化XPathFactory时确定的底层对象模型返回一个新的XPath。

XPathExpression接口提供对编译后的XPath表达式的访问。
compile
XPath的compile方法会编译一个XPath表达式以供以后计算。

  • 如果表达式包含XPathFunction,那它们必须可以通过XPathFunctionResolver解析表明其是可用的,否则会抛出XPathExpressionException 异常;

  • 如果表达式包含一些变量,那将使用在编译时有效的XPathVariableResolver来解析它们;

  • 如果表达式为null,则会抛NullPointerException异常。

关于XPath的路径表达式,可以参考这篇文章:https://www.w3school.com.cn/xpath/xpath_syntax.asp

在我们的代码中,"/persistence/persistence-unit/properties/property[@name='openjpa.ConnectionProperties']"的含义是选取persistence的子元素persistence-unit的子元素properties的子元素中name的值为openjpa.ConnectionProperties的property元素。
evaluate
evaluate()方法用于对已编译好的XPath表达式求值,依据特定的上下文,并且返回特定的类型。该方法为InputSource构建一个数据模型,并对结果文档对象调用evaluate(Object item, QName returnType) 。我们的代码中返回得到的是一个element节点。

getAttributeNode() 方法从当前元素中通过名称获取属性节点。在这里会返回以"value"为名称的节点。

后面的内容留到下一篇分析吧…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值