多设备间同步 ssh 配置及密钥

ssh 客户端会在用户目录 ~/.ssh/ 目录下存放配置信息 (~/.ssh/config) 和公钥和私钥,如果有多个设备不同设备间的同步和管理就会成为一个比较头疼的问题。我在 Reddit 上抛出这个问题 后,我本来想的是通过 git 版本控制来进行管理,但有人说因为公钥和私钥都是二进制的文件,其实没有必要使用 git,任何一个同步工具就能够解决。

不过鉴于国内糟糕的网络环境,我还是通过 git 来管理,最后同步该私有仓库即可。

SSH 目录的权限

用户目录下的 ~/.ssh目录以及下面的文件需要特别小心的管理其权限,

  • 整个 ~/.ssh 目录需要设置 700 (drwx------) 权限
  • public key 需要设置 644(-rw-r--r--)
  • 客户端的私钥需要设置 600 (-rw-------)

需要保证该目录不会被其他 group 的用户读取和修改。

SSH 配置

ssh 客户端在解析配置时会按照如下顺序:

command line options
user-specific file, located at ~/.ssh/config
system-wide file, /etc/ssh/ssh_config

SSH 配置项说明

先来看看一个常见的配置:

Host github.com
	HostName github.com
	PreferredAuthentications publickey
	IdentityFile ~/.ssh/id_rsa_einverne_github

SSH config 中可配置字段解释。

Host

用于我们执行 SSH 命令的时候如何匹配到该配置。

  • *,匹配所有主机名。
  • *.example.com,匹配以 .example.com 结尾。
  • !*.dialup.example.com,*.example.com,以 ! 开头是排除的意思。
  • 192.168.0.?,匹配 192.168.0.[0-9] 的 IP。

AddKeysToAgent

是否自动将 key 加入到 ssh-agent,值可以为 no(default)/confirm/ask/yes。

如果是 yes,key 和密码都将读取文件并以加入到 agent ,就像 ssh-add。其他分别是询问、确认、不加入的意思。添加到 ssh-agent 意味着将私钥和密码交给它管理,让它来进行身份认证。

AddressFamily

指定连接的时候使用的地址族,值可以为 any(default)/inet(IPv4)/inet6(IPv6)。

BindAddress

指定连接的时候使用的本地主机地址,只在系统有多个地址的时候有用。在 UsePrivilegedPort 值为 yes 的时候无效。

ChallengeResponseAuthentication

是否响应支持的身份验证 chanllenge,yes(default)/no。

Compression

是否压缩,值可以为 no(default)/yes。

CompressionLevel

压缩等级,值可以为 1(fast)-9(slow)。6(default),相当于 gzip。

ConnectionAttempts

退出前尝试连接的次数,值必须为整数,1(default)。

ConnectTimeout

连接 SSH 服务器超时时间,单位 s,默认系统 TCP 超时时间。

ControlMaster

是否开启单一网络共享多个 session,值可以为 no(default)/yes/ask/auto。需要和 ControlPath 配合使用,当值为 yes 时,ssh 会监听该路径下的 control socket,多个 session 会去连接该 socket,它们会尽可能的复用该网络连接而不是重新建立新的。

ControlPath

指定 control socket 的路径,值可以直接指定也可以用一下参数代替:

  • %L 本地主机名的第一个组件
  • %l 本地主机名(包括域名)
  • %h 远程主机名(命令行输入)
  • %n 远程原始主机名
  • %p 远程主机端口
  • %r 远程登录用户名
  • %u 本地 ssh 正在使用的用户名
  • %i 本地 ssh 正在使用 uid
  • %C 值为 %l%h%p%r 的 hash

请最大限度的保持 ControlPath 的唯一。至少包含 %h,%p,%r(或者 %C)。

ControlPersist

结合 ControlMaster 使用,指定连接打开后后台保持的时间。值可以为 no/yes/ 整数,单位 s。如果为 no,最初的客户端关闭就关闭。如果 yes/0,无限期的,直到杀死或通过其它机制,如:ssh -O exit。

GatewayPorts

指定是否允许远程主机连接到本地转发端口,值可以为 no(default)/yes。默认情况,ssh 为本地回环地址绑定了端口转发器。

HostName

真实的主机名,默认值为命令行输入的值(允许 IP)。你也可以使用 %h,它将自动替换,只要替换后的地址是完整的就 ok。

IdentitiesOnly

指定 ssh 只能使用配置文件指定的 identity 和 certificate 文件或通过 ssh 命令行通过身份验证,即使 ssh-agent 或 PKCS11Provider 提供了多个 identities。值可以为 no(default)/yes。

IdentityFile

指定读取的认证文件路径,允许 DSA,ECDSA,Ed25519 或 RSA。值可以直接指定也可以用一下参数代替:

  • %d,本地用户目录 ~
  • %u,本地用户
  • %l,本地主机名
  • %h,远程主机名
  • %r,远程用户名

LocalCommand

指定在连接成功后,本地主机执行的命令(单纯的本地命令)。可使用 %d,%h,%l,%n,%p,%r,%u,%C 替换部分参数。只在 PermitLocalCommand 开启的情况下有效。

LocalForward

指定本地主机的端口通过 ssh 转发到指定远程主机。格式:LocalForward [bind_address:]post host:hostport,支持 IPv6。

PasswordAuthentication

是否使用密码进行身份验证,yes(default)/no。

PermitLocalCommand

是否允许指定 LocalCommand,值可以为 no(default)/yes。

Port

指定连接远程主机的哪个端口,22(default)。

ProxyCommand

指定连接的服务器需要执行的命令。%h,%p,%r

如:ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p

User

登录用户名

疑难杂症

sign and send pubkey signing failed

SSH 连接时发生这个错误:

sign_and_send_pubkey: signing failed: agent refused operation
: Permission denied (publickey).
ssh_exchange_identification: Connection closed by remote host

大概率是 ~/.ssh/ 目录下私钥的权限不对,找到对应的私钥:

chmod 600 ~/.ssh/id_rsa

reference


2020-07-15 ssh , config , sync , unix , linux

mosh: 使用 UDP 传输的 Shell

Mosh 是 mobile shell 的缩写,Mosh 允许间断性连接,使用传统的 SSH 连接远程设备时,如果遇到一点点的网络问题,SSH 连接就会被中断。Mosh 使用 UDP 传输,相较于 SSH,在漫游网络,Wi-Fi,移动 (cellular) 网络,长距离连接等网络场景下提供了更好的连接体验。

Mosh 提供的一些特性:

  • Mosh 会自动在连接的网络环境中进行切换,而不会中断连接。尤其当在移动设备上使用 Wi-Fi,3/4G 移动连接时,可以保持连接状态
  • 当笔记本进入睡眠状态,然后再被唤醒,网络连接会中断,使用 Mosh ,但网络中断时会提醒,而当网络恢复时会自动重连
  • SSH 会等待服务端的回应然后再回显终端上的输入。这就导致了如果连接延迟过高,用户所见内容会有延迟。Mosh 则是会立即显示输入内容,包括输入,删除,行编辑等等。Mosh 会自适应,即使是全屏的应用比如 emacs 或者 vim 也可以得益。在连接不好的时候,提前输入的内容会标记下划线以告诉用户区别
  • mosh 没有需要特殊权限运行的内容,客户端和服务端都是单独的可执行文件
  • Mosh 不会监听网络端口或者需要创建授权用户。mosh 客户端通过 SSH 登录服务端,使用 SSH 相同的用户名。然后 Mosh 在远端运行 mosh-server,通过 UDP 进行数据传输
  • Mosh 是一个命令行程序,和 ssh 一样。可以在 xterm, gnome-terminal, urxvt, Terminal.app, iTerm, emacs, screen, 或者 tmux 中运行。mosh 支持 UTF-8 编码,可以修复其他终端可能产生的 Unicode 错误
  • 支持 Ctrl-C,Mosh 不会填满网络缓冲,Ctrl-C 可以用来终止一个正在运行的程序

Install

安装过程就比较简单了:

apt install mosh

其他平台类似。

Usage

指定端口号:

mosh --ssh="ssh -p 2222" someone@host

指定 ssh key:

mosh --ssh="~/bin/ssh -i ./identity" someone@host

reference


2020-07-13 mosh , ssh , udp

Vim 插件之 vim-easymotion

Vim 中已经提供了非常多移动的动作,从简单的字符间移动 (jkhl),到 word 间 (w/e/b),句子间 ((/)),段落间 ({/}),行首行尾 (0/^/$),文档开始 (gg),文档结尾 (G),还有搜索 (//np) 等等一系列的操作,但 easy-motion 将 Vim 中的移动又提升了一个高度。

继续往下阅读之前先确保阅读了 vim 文档中关于 motion 的内容。

想象一个场景,想要跳转到当前行下一个段落中的第二个句子的第三个单词开头,使用上面提到的方法,可能需要按下不同的按键,并且可能还需要组合使用,那有没有什么方法能降低这个移动(或者说跳转)操作的复杂度呢?答案就是 easy-motion。不知道有多少人用过 Chrome 下的 Vimium 插件,在网页中按下 f,页面中每一个能点击的地方都会显示几个字符,然后按下字符就会相当于在页面上点击,easy-motion 也使用相同的方式实现这一功能。

easy-motion 插件提供了更强的移动操作,在 easymotion 的官方文档中是这样定义 easymotion 的,easymotion 可以将 <number>w 或者 <number>f{char} 简化为几个按键的操作,举一个简单的例子,在 easymotion 下,如果按下 w,那么 easymotion 会高亮所有 w(下一个词首) 的结果,然后只需要按下一个键,就可以跳转到任何 w 按键按下后的目标地址。

Install

Plug 'easymotion/vim-easymotion'

Usage

:help easymotion

Easymotion 的触发需要按下两次 <leader><leader>,当然推荐熟悉之后使用 vim 的 map 配置更改一下 leader.

Default Mapping      | Details
---------------------|----------------------------------------------
<Leader>f{char}      | Find {char} to the right. See |f|.
<Leader>F{char}      | Find {char} to the left. See |F|.
<Leader>t{char}      | Till before the {char} to the right. See |t|.
<Leader>T{char}      | Till after the {char} to the left. See |T|.
<Leader>w            | Beginning of word forward. See |w|.
<Leader>W            | Beginning of WORD forward. See |W|.
<Leader>b            | Beginning of word backward. See |b|.
<Leader>B            | Beginning of WORD backward. See |B|.
<Leader>e            | End of word forward. See |e|.
<Leader>E            | End of WORD forward. See |E|.
<Leader>ge           | End of word backward. See |ge|.
<Leader>gE           | End of WORD backward. See |gE|.
<Leader>j            | Line downward. See |j|.
<Leader>k            | Line upward. See |k|.
<Leader>n            | Jump to latest "/" or "?" forward. See |n|.
<Leader>N            | Jump to latest "/" or "?" backward. See |N|.
<Leader>s            | Find(Search) {char} forward and backward.
                     | See |f| and |F|.

下面将 <leader><leader> 简写成 <ll>

向后向前跳转

<ll>w
<ll>b

双向跳转

<ll>s

向上向下跳转到行:

<ll>j
<ll>k

reference


2020-07-06 vim , vim-plugin

JetBrains IntelliJ IDEA 中使用 vim 总结

虽然使用了很长时间的 Vim,也使用了很长时间的 IntelliJ IDEA,但总感觉没有充分利用,所以想再这里总结一下,系统的浏览一遍 Idea Vim 插件能提供的功能,看看能不能有所受益,Vim 和 IntelliJ IDEA 的基本操作和内容就省略了。

Introduction

首先 ideavim 这个插件是 JetBrains 官方提供的,基本上安装后即可。GitHub 的页面还提到 ideavim 插件提供了一些 Vim 插件的扩展功能,比如:

  • vim-easymotion
  • vim-surround
  • vim-multiple-cursors
  • vim-commentary
  • argtextobj.vim
  • vim-textobj-entire
  • ReplaceWithRegister

可以根据这个页面 上的方式配置和开启这个扩展功能。

为什么要用 IdeaVim

  • 既充分利用了 IntelliJ 提供的代码补全,重构,代码浏览等等功能,又可以充分利用 Vim 的多模式,以及 Vim 在编辑器中的高效
  • 利用 ~/.ideavimrc 来复用 Vim 的工作方式,以及充分利用 Idea 提供的 Action

vim-easymotion

Idea 中的 vim-easymotion 插件支持的配置,可以参考这里

AceJump

在了解 easymotion 时意外收获了 AceJump,IntelliJ IDEA 中的 easymotion 实际上是通过 AceJump 插件来实现的。 :q

默认情况下,使用 Ctrl + ; 来开启 AceJump 模式,不过我的 Ctrl + ; 已经作为输入法的多粘贴板来使用了,所以就改成 Alt + k

AceJump 的工作流程,按下 Alt + K 进入 AceJump ,此时按下任何按键就会在当前文件搜索,并给每一个结果一个 tag,按下回车,然后输入 tag 就可以快速跳转过去。

结合 action

在 IntelliJ IDEA 中,任何选项操作都会映射到一个 action 上,点击按钮,就执行对应的 action,所以记住 Ctrl + Shift + a 这个快捷键。

在编辑器模式下,可以输入如下命令查看 actionlist:

:actionlist

vim-surround

启用方式:

set surround

支持的 Commands: ys, cs, ds, S

下面的例子中,假设 *是当前光标的位置:

Old Text Command Text After command execute
“Hello *world!” ds” Hello world!
[123 + 4*56]/2 cs]) (123+456)/2
“Look ma, I’m *HTML!” cs” <q>Look ma, I'm HTML!</p>
if *x>3 ysW( if ( x>3 )
my $str = *www; vllS’ my $str = 'www';

vim-surround 在想要改变 surround 的时候非常方便。

vim-multiple-cursors

目前我的使用场景大部分通过 IDEA 自带的 rename 功能批量替换变量即可做到,所以目前还没有开启这个功能的需求,更多多光标的操作技巧可以参考这篇文章

IdeaVim 支持的所有快捷键:

总结

最后,这里 是我的 .ideavimrc 配置。

reference


2020-07-06 intellij , vim , editor , shortcut , summary

在树莓派上安装 Prometheus node-exporter

前些天正好在我的二代树莓派上安装了 AdGuard Home,这样一个基础服务必然不能少了监控,所以正好把 Prometheus node-exporter 安装一下。

Prerequisite

首先确认一下 CPU 型号,我的是二代,比较老,直接 lscpu 看一下就知道:

Model name:            ARMv7 Processor rev 5 (v7l)

这是一个 armv5 版本的,然后到 node-exporter 下载二进制:

注意选对版本。

Install

wget https://github.com/prometheus/node_exporter/releases/download/v1.0.1/node_exporter-1.0.1.linux-armv5.tar.gz

然后接着这篇文章 即可。

最后验证:

curl http://localhost:9100/metrics

添加到 Prometheus Server

  - job_name: 'raspberry pi node exporter'
	scrape_interval: 5s
	scrape_timeout: 5s
	static_configs:
	- targets: ['192.168.2.3:9100']

之后 Grafana 添加数据源,和之前的文章 一样。


2020-07-05 prometheus , raspberry-pi , linux , node-expoter , monitor

使用 Clonezilla 恢复 Windows 系统遇到的几个问题

之前也总结过两篇文章,我是如何使用 Clonezilla 进行全盘备份和恢复的 以及备份 Linux 过程中遇到的问题,今天这篇就记录一下恢复之前备份过的 Windows

我先来还原一下现在情况,原来我有两台小米的 Air 笔记本,所有的配置一样,不过一台我从之前的电脑上恢复了一个 Linux Mint 的系统,暂且叫这台 A1 笔记本,然后还有一台是默认的 Windows 系统,不过这一台用的比较少,暂且叫这台 A2 笔记本。前段时间我把 A2 笔记本使用 Clonezilla 备份了一下生成了一个从 device 到 image 的镜像,然后我把 A2 笔记本卖了,所以现在只剩下 A1 笔记本。

我在卖 A2 笔记本的时候,当时也做了系统的恢复,就是把当年 A1 原始的默认 Windows 系统恢复到了 A2 笔记本上,正好省去了我格式化硬盘,备份数据的苦恼,恢复上去之后 A2 没啥问题也可以直接启动。

但今天恢复 A1 笔记时,却遇到了一些问题,这里记录一下。

no bootable devices found

安装我之前的操作,恢复之前备份的 A2 的硬盘镜像文件到 A1 的整块硬盘上,官网的教程非常详细,这里就略过了,就安装默认的下一步下一步直接走了,可以所有操作完成后等等重启时,屏幕只出现了 “no bootable devices found”,我一想不应该呀,虽然用的是另一台机器的镜像恢复的数据,但是理论上应该还是能找到系统的。

之后还想着是不是引导坏了,还用着 Win PE 进去想修复一下,谁知道在 PE 里面根本找不到系统的硬盘,后来想想是不是恢复的时候把 MBR 搞坏了,还是说默认的 Windows 是安装在 GPT 分区表的硬盘上的。

然后使用如下的方法重新恢复了一次:

然后去 BIOS 中把之前引导 Linux 是的 Legacy 改成了 UEFI Mode,果然就能进入系统了。


2020-07-04 clonezilla , backup , restore

Albert launcher

Albert 是一个 Linux 上的启动器,使用 C++ 和 QT 实现,实现了如下的功能:

  • Run Applications
  • Open files
  • Open bookmarks
  • Search web
  • calculate things
  • GPL-licensed

Install

Official Build

通过下面的网站下载官方编译的版本。

从源代码编译安装

从源码编译安装:

git clone --recursive https://github.com/albertlauncher/albert.git
mkdir albert-build
cd albert-build
cmake ../albert -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
make
sudo make install

顺利的话可以走到最后一步,不过大概率会出现各种依赖的问题。

➜ cmake ../albert -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
CMake Error at /opt/qt59/lib/cmake/Qt5/Qt5Config.cmake:28 (find_package):
  Could not find a package configuration file provided by "Qt5X11Extras" with
  any of the following names:

	Qt5X11ExtrasConfig.cmake
	qt5x11extras-config.cmake

  Add the installation prefix of "Qt5X11Extras" to CMAKE_PREFIX_PATH or set
  "Qt5X11Extras_DIR" to a directory containing one of the above files.  If
  "Qt5X11Extras" provides a separate development package or SDK, be sure it
  has been installed.
Call Stack (most recent call first):
  lib/globalshortcut/CMakeLists.txt:16 (find_package)


-- Configuring incomplete, errors occurred!

安装缺失的依赖:

➜ sudo apt install libqt5x11extras5 libqt5x11extras5-dev



➜ cmake ../albert -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
CMake Error at /usr/lib/x86_64-linux-gnu/cmake/Qt5/Qt5Config.cmake:28 (find_package):
  Could not find a package configuration file provided by "Qt5Svg" with any
  of the following names:

	Qt5SvgConfig.cmake
	qt5svg-config.cmake

  Add the installation prefix of "Qt5Svg" to CMAKE_PREFIX_PATH or set
  "Qt5Svg_DIR" to a directory containing one of the above files.  If "Qt5Svg"
  provides a separate development package or SDK, be sure it has been
  installed.
Call Stack (most recent call first):
  plugins/widgetboxmodel/CMakeLists.txt:7 (find_package)


-- Configuring incomplete, errors occurred!

安装缺失的依赖:

➜ sudo apt install libqt5svg5 libqt5svg5-dev

reference


2020-06-29 linux , albert , launcher , application

Moshi : 新一代的 Java 解析 JSON 工具

最近 FastJson 安全问题频发,所以 JSON 解析又被拉到台面上,而正好不久前看 Reddit 看到 Gson 的作者在推荐一个叫 Moshi 的库,这就花点时间看一下。1

Gson 存在的问题

序列化 Date 的时候不包含时区的信息

Date epoch = new Date(0);
String epochJson = new Gson().toJson(epoch);
// "Dec 31, 1969 7:00:00 PM"

RFC 3339 标准 里面规定的日期表示法:

2020-06-12T07:20:50.52Z

其中 T 用来分割前面的日期和后面的时间,而最后的 Z 表示这个时间是 UTC+0,其他人看到这个时间就可以根据自己的时区进行转换。2

Gson 在序列化 HTML 标签时,会进行 HTML escaping 成:

private String e = "12 & 5";
private String f = "12 > 6"

"e":"12 \u0026 5"
"f":"12 \u003e 6"

Moshi 优势

  • Moshi 库特别小,对于 Android 来说自然可以减小 APK 大小
  • Moshi 自带的 Adapter 可以满足大部分的需求,如果需要扩充也非常方便
  • 可以使用 @Json 来自定义 Field 名
  • Kotlin Support
  • 使用类似于 @HexColor int 的限定符可以让多个 JSON 映射到同一个 Java Type

Moshi 潜在的问题和 Gson 的差别

  • Moshi 只有少量的内置 Adapter,Moshi 不会去序列化平台相关的类型,比如 java.*, javax.*, android.* 等等,以防止被 Lock 在某一个特殊 JDK 或者 Android 版本
  • Moshi 只有少量的配置选项,没有 field naming strapegy, versioning, instance creator, long serialization policy.
  • Moshi 没有 JsonElement 模型
  • No HTML-safe escaping

使用

更加具体的使用方法可以参考源代码中的实现。

最基本的使用,序列化一个 Java Object 到 JSON,或者将 JSON 映射到 Java Object 里面。

将 JSON 字符串解析成 Java 对象

Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);

BlackjackHand blackjackHand = jsonAdapter.fromJson(json);
System.out.println(blackjackHand);

如果是 JsonArray:

Moshi moshi = new Moshi.Builder().build();

Type listOfCardsType = Types.newParameterizedType(List.class, Card.class);
JsonAdapter<List<Card>> jsonAdapter = moshi.adapter(listOfCardsType);

List<Card> cards = jsonAdapter.fromJson(json);

序列化 Java Object:

Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);

String json = jsonAdapter.toJson(blackjackHand);
System.out.println(json);

自定义 Field

比如有一个 JSON 字符串:

String json = ""
    + "{"
    + "  \"username\": \"jesse\","
    + "  \"lucky number\": 32"
    + "}\n";

其中的 lucky number 是带空格的,假如要解析到 Java Object

public final class Player {
  public final String username;
  public final @Json(name = "lucky number") int luckyNumber;
}

可以看到使用 @Json 注解即可。

源码解析

Moshi

Moshi 只能使用 Builder 模式创建,看其源码会发现,构造函数并不是 public 的。唯一一个带参数的

  Moshi(Builder builder) {
	List<JsonAdapter.Factory> factories = new ArrayList<>(
		builder.factories.size() + BUILT_IN_FACTORIES.size());
	factories.addAll(builder.factories);
	factories.addAll(BUILT_IN_FACTORIES);
	this.factories = Collections.unmodifiableList(factories);
  }

可以看到 Moshi 有一系列 adapter() 公开方法,通过 adapter() 方法可以返回一个 JsonAdapter<> 对象,之后的操作都在该 adapter 之上进行。

成员变量

private final List<JsonAdapter.Factory> factories;
private final ThreadLocal<LookupChain> lookupChainThreadLocal = new ThreadLocal<>();
private final Map<Object, JsonAdapter<?>> adapterCache = new LinkedHashMap<>();

说明:

  • factories: 是 JsonAdapter.Factory 数组,工厂模式产生 JsonAdapter
  • lookupChainThreadLocal 是一个 ThreadLocal 内部存放了 LookupChain
  • adapterCache 是一个 LinkedHashMap 用来保存 Object 到 JsonAdapter 的映射关系

Moshi 类中初始化的时候有 5 个内置的 Adapter Factory.

  static final List<JsonAdapter.Factory> BUILT_IN_FACTORIES = new ArrayList<>(5);

  static {
	BUILT_IN_FACTORIES.add(StandardJsonAdapters.FACTORY);
	BUILT_IN_FACTORIES.add(CollectionJsonAdapter.FACTORY);
	BUILT_IN_FACTORIES.add(MapJsonAdapter.FACTORY);
	BUILT_IN_FACTORIES.add(ArrayJsonAdapter.FACTORY);
	BUILT_IN_FACTORIES.add(ClassJsonAdapter.FACTORY);
  }
  • StandardJsonAdapters.FACTORY 包含了基本类型,包装类型,还有运行时才能决定的 ObjectJsonAdapter
  • CollectionJsonAdapter.FACTORY 包含 List, Collection, Set 类型
  • MapJsonAdapter.FACTORY 包含将 Map(Key 是 String) 的 JSON 转换成 Object 的 Adapter
  • ArrayJsonAdapter.FACTORY 处理包含了原始值或 Object 的 JSON Array
  • ClassJsonAdapter.FACTORY

Moshi.Builder

Moshi.Builder 是 Moshi 内部的一个类,用来创建 Moshi,它有一系列方法:

com.squareup.moshi.Moshi.Builder#add(java.lang.reflect.Type, com.squareup.moshi.JsonAdapter<T>)
com.squareup.moshi.Moshi.Builder#add(java.lang.reflect.Type, java.lang.Class<? extends java.lang.annotation.Annotation>, com.squareup.moshi.JsonAdapter<T>)
com.squareup.moshi.Moshi.Builder#add(com.squareup.moshi.JsonAdapter.Factory)
com.squareup.moshi.Moshi.Builder#add(java.lang.Object)
com.squareup.moshi.Moshi.Builder#addAll
com.squareup.moshi.Moshi.Builder#build

可以看到除了 overload 重载的 add() 方法外,就是 build 方法,而 add() 方法可以添加一系列类型。

JsonAdapter 抽象类

JsonAdapter 的公开方法:

com.squareup.moshi.JsonAdapter#fromJson(com.squareup.moshi.JsonReader)
com.squareup.moshi.JsonAdapter#fromJson(okio.BufferedSource)
com.squareup.moshi.JsonAdapter#fromJson(java.lang.String)
com.squareup.moshi.JsonAdapter#toJson(com.squareup.moshi.JsonWriter, T)
com.squareup.moshi.JsonAdapter#toJson(okio.BufferedSink, T)
com.squareup.moshi.JsonAdapter#toJson(T)
com.squareup.moshi.JsonAdapter#toJsonValue
com.squareup.moshi.JsonAdapter#fromJsonValue
com.squareup.moshi.JsonAdapter#serializeNulls
com.squareup.moshi.JsonAdapter#nullSafe
com.squareup.moshi.JsonAdapter#nonNull
com.squareup.moshi.JsonAdapter#lenient
com.squareup.moshi.JsonAdapter#failOnUnknown
com.squareup.moshi.JsonAdapter#indent

JsonAdapter 有两个抽象方法需要实现 fromJsontoJson

大致就能看出 JsonAdapter 的主要功能:

  • 一方面提供 fromJson to toJson的转换
  • 另一方面提供转换过程中的一些选项

    • serializeNulls serializes nulls when encoding JSON
    • nullSafe support for reading and writing
    • 比如 nonNull 调用时会返回一个不允许 null 值的 JsonAdapter
    • lenient 宽容处理 JSON
    • failOnUnknown 在遇到未知的 name 或 value 时抛出 JsonDataException 异常
    • indent 输出的 JSON 是格式化好的

JsonAdapter 主要去处理各个类型的转换,需要实现如下三个方法:

  static final JsonAdapter<Boolean> BOOLEAN_JSON_ADAPTER = new JsonAdapter<Boolean>() {
	@Override public Boolean fromJson(JsonReader reader) throws IOException {
	  return reader.nextBoolean();
	}

	@Override public void toJson(JsonWriter writer, Boolean value) throws IOException {
	  writer.value(value.booleanValue());
	}

	@Override public String toString() {
	  return "JsonAdapter(Boolean)";
	}
  };

JsonAdapter.Factory 接口

这个接口定义在抽象类 JsonAdapter 中,Factory 只需要实现一个方法:

  public interface Factory {
	@CheckReturnValue
	@Nullable JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi);
  }

Factory 看名字也能猜到这是一个工厂方法,用来创造给定 Type 给定 Annotation 类型的 Adapter,如果无法创建则返回 null.

在 Factory 的实现中,可能会使用 moshi.adapter 来构建 Adapter.

Mosh.Builder

再看 Moshi.Builder 类成员

final List<JsonAdapter.Factory> factories = new ArrayList<>();

在源代码的基础上加了一些注释

reference


2020-06-23 java , json , gson , moshi

KIE API 学习笔记

Kie have these concepts which every user need to know.

KieService

KieService 允许创建 KieContainer

KieContainer

KieContainer 是所有给定 KieModule 的 KieBases 的集合。

KieContainer 可以加载 KieModule 和其依赖,KieContainer 可以拥有一个或者多个 KieBases.

KieModule

KieModule 在 org.kie.api.builder 包下,KieModule 是一个存放所有定义好的 KieBases 资源的容器,和 pom.xml 文件相似,定义了其 ReleaseId, kmodule.xml 文件定义了 KieBase 的名字,配置,以及其他用来创建 KieSession 的资源,以及其他用来 build KIEBases 的资源。

KieModule 用来定义多个 KieBases 和 KieSessions。KieModule 可以包含其他 KieModules.

KieBase

KieBase 是应用所有定义好的 Knowledge 合集,包括了 rules(规则), processes(流程), functions(方法), type models, KieBase 自身不包含任何运行时数据,sessions 可以从 KieBase 中创建,然后运行时数据可以被装入,并且通过 sessions 可以启动一个流程。

KieBase 代表着编译好的资源的版本,可以有 Stateless 和 Stateful Session,一个典型的使用场景是为一个 packages 使用一个 KieBase ,另一个 package 使用另一个 KieBase.

KieSession

KieSession 是和工作流引擎交互的最常用的方式,KieSession 允许应用建立和引擎的交互,session 的状态会在每一次调用的时候保存下来。而流程会在一组相同的变量上触发多次。当应用完成调用,必须调用 dispose() 来释放资源以及使用的内存。

KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession();

for( Object fact : facts ) {
	kSession.insert( fact );
}
kSession.fireAllRules();
kSession.dispose();

每一个 KieBase 都可以有一个或者多个 KieSessions.

KieBuilder

KieBuilder is a builder for the resources contained in a KieModule

reference


2020-06-17 kie , drools , jbpm , decision-table , score-card

JBoss 工作流相关 Docker 镜像整理

源代码地址:

三个可用的镜像:

  • business-central-workbench

JBoss Business-Central Workbench 基础镜像,可以根据该镜像来扩展创建自己的镜像。

  • business-central-workbench-showcase

继承自 JBoss Business-Central Workbench 镜像,可以直接使用的 Docker 镜像。提供了自定义的配置文件,默认的用户和角色。

  • jBPM Server Full distribution

提供了可以立即执行的全部 jBPM 功能的镜像,包含全部必须的配置文件。包括 jBPM Workbench, Kie Server and jBPM Case Management Showcase。可以访问 新手教程 来查看。


2020-06-12 jboss , kie , kie-workbench , drools

电子书

最近文章

  • 使用 zinit 管理 zsh 插件 一直使用的 antigen 来管理 zsh 的插件,但是最近 zsh 加载因为加了一些插件变的非常慢,所以就像找找办法提速 zsh,在查询的过程中发现 antigen 已经很久没有更新,很多人推荐 antibody, 于是又试了一下 antibody, 不过在调研的过程中又发现了 zinit. 之后经过对比,发现 antibody 所谓的并行执行也没有提速很多,反而是名不见经传的 zinit 通过配置将加载时间稳稳的降低,在新建终端时几乎立即可见。
  • 阿里云函数计算中使用 Python psycopg2 访问 PostgreSQL 在之前的文章中提到过 函数计算 但一直没有正式的用起来,现在正好通过在阿里云函数计算中连接访问 PostgreSQL 来系统性的学习一下阿里云的函数计算。 首先要了解的几个概念:
  • Mac OS 上的平铺窗口管理工具 yabai yabai 是一个 Mac OS 上的平铺窗口管理工具。Linux 上很早就有一系列的平铺窗口管理工具,比如 i3, awesome 等等。yabai 将这个功能带到了 Mac 上。所谓的平铺式窗口管理,是相较于普通的浮动窗口管理,在通常使用的情况下,系统上的应用如果打开了很多,就不可避免的相互叠加,需要频繁的使用 ⌘+Tab 来切换窗口。而平铺式窗口管理,则将所有的窗口平铺在桌面上,窗口之间不会相互重叠。
  • You Quiz on the Block E70 世代差异 Covid-19 以来很少有让我继续追下去的韩综,除了带我入坑的周末综艺,一个 《[[Begin Again Korea]]》剩下的就只有《You Quiz On the Block》了。可以看得出来韩国的综艺人,电视人总还是带着一些社会关怀再做内容,之前在《Begin Again》里面就提过,在这个特殊的时代,他们把歌声第一时间先给一线的医护人员,公共服务人员,在这个疫情的特殊时期《Begin Again》就是一个治愈的存在,而相同的《You Quiz》第三期开篇就是对社会一线人员的致敬,公交司机,私营店老板,快递员,防疫人员,社会正是在这些人继续工作下去的时候才能维持正常的运行。
  • 使用 Goku 配置 Karabiner Karabiner 是 MacOS 上一款强大的自定义键盘的软件,可以非常自由的定义任何键位。