关键字提取,开源,代码质量管理,多语言支持。
SonarQube is an open-source platform developed by SonarSource for continuous inspection of code quality to perform automatic reviews with static analysis of code to detect bugs, code smells, and security vulnerabilities on 20+ programming languages.
开源协议:Lesser GNU General Public License
SonarQube 可以从以下七个维度检测代码质量,而作为开发人员至少需要处理前 5 种代码质量问题
Jenkins 是一个独立的开源自动化服务器,可用于自动化各种任务,如构建,测试和部署软件。Jenkins 可以通过本机系统包 Docker 安装,甚至可以通过安装 Java Runtime Environment 的任何机器独立运行。
用于持续、自动构建,测试项目,监控外部任务运行等。
新建模板类型
单元测试的目的是在不涉及依赖的情况下测试代码(隔离)。一个设计良好的系统需要遵循 SOLID 原则。
Unit test 目标是针对一个模块或者一段代码隔离测试,应该消除其他类,或者系统依赖带来的副作用。
测试替身(Test Doubles)用来消除这类副作用,test Doubles 可以分为:
通常情况下可以通过手工代码来 mock objects 或者使用 mock framework 来模拟类的行为。Mockito 是一个非常流行的 Mock framework,使用 Mockito 的三段论:
在 http://search.maven.org 中搜索 a:"mockito-core"
或者 g:"org.mockito"
比如说增加:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
mockito-all 是一个包含了所有依赖 (hamcrest 和 objenesis) 的单一 jar 包。
mockito-core 不包含 hamcrest 和 objenesis 依赖,可以自己来控制依赖的版本。
第一种方法,使用 JUnit 的 @RunWith
@RunWith(MockitoJUnitRunner.class)
public class MockitoAnnotationTest {
...
}
或者用代码启用
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
最常用的注解就是 @Mock
注解,使用 @Mock
注解来创建和插入 mocked 实例,这样就省去了手动调用 Mockito.mock()
方法。
@Spy
注解可以 mock 真实对象的方法,让真实对象方法被调用时,就像 mock object 一样可以被配置。
参数捕获器,用于捕获 mock 方法参数,进行验证使用
@InjectMocks
注解会自动将 mock fields 注入到被测试的 object 中。
需要注意的是,在 JUnit 4 中必须使用 @RunWith(MockitoJUnitRunner.class)
或 MockitoAnnotations.initMocks(this)
来初始化 mocks 并注入。
在 JUnit 5 中必须使用 @ExtendWith(MockitoExtension.class)
@RunWith(MockitoJUnitRunner.class) // JUnit 4
// @ExtendWith(MockitoExtension.class) for JUnit 5
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
// tests...
}
通过 when().thenReturn()
方法链可以用来指定一个方法调用预先定义好的返回值。这个方法也可以指定抛出一个异常
Properties properties = mock(Properties.class);
when(properties.get(”Anddroid”)).thenThrow(new IllegalArgumentException(...));
try {
properties.get(”Anddroid”);
fail(”Anddroid is misspelled”);
} catch (IllegalArgumentException ex) {
// good!
}
当 mock 方法是知道确定的返回值,那么可以使用 thenReturn
或者 doReturn
,方法会 mock 一个确定的返回值。
thenReturn(T value) Sets a return value to be returned when the method is called.
Answer
当需要根据不同条件来 mock 方法并且返回不同返回值时需要 Answer
,比如需要根据方法传入参数来返回对应的返回值的情况。
Mockito 暂时还不支持 Mock 静态方法,所以需要借助 jmockit 完成静态方法的 Mock:
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.30</version>
<scope>test</scope>
</dependency>
new MockUp<StaticClass>() {
@mockit.Mock
public boolean isXXX() {
return true;
}
};
《Mockito Cookbook》参考代码:https://github.com/marcingrzejszczak/mockito-cookbook
最近又一次使用 Clonezilla 来克隆系统,和以往不同的是,这一次我是备份了整块硬盘到镜像,然后从镜像恢复系统到另外一块硬盘,而不是以往是复制一个分区,所以又产生了一些问题,所以有了这篇文章,一方面来记录一下中间遇到的问题,另一方面也学习巩固一下关于 Linux 启动过程中的必要流程。
关于最基础的计算机启动过程也就不展开说,阮一峰,和网上大部分文章已经讲的非常清晰了,这里只简单的列举一些基础名词,必须要知道的概念。
全称是 Basic Input Output System,也就是一块被写入开机程序的只读内存,电脑通电之后第一时间会读取的芯片。
POST 不是 HTTP 请求方法的 POST,而是 Power On Self Test,开机自检,在通电之后 BIOS 加载后会自动执行。
[[MBR]] 是主引导分区,全称是 Master Boot Record,该分区决定该设备是否能够启动。如果设备能够启动,那么该设备第一个扇区,512 字节就需要表明该设备能够启动。
主引导记录告诉计算机去哪一块硬盘寻找操作系统,主引导记录由三部分组成
[[GRUB]] 是 Linux 下最流行的启动管理器(Boot Loader),全称是 GRand Unified Bootloader。计算机在读取 MBR 前 446 字节机器码之后,将运行事先安装的启动管理器 boot loader 也就是 GRUB。
GRUB 设计兼容 multiboot specification,为了使得 GRUB 能够引导启动各种版本的 Linux。GRUB 也能够让用户选择从不同的 kernels 启动。Grub 的配置文件地址在 /boot/grub/grub.conf
.
GRUB1 现在正在被 GRUB2 代替,Grub2 在 /boot/grub2/grub.cfg
配置。
所以总结来看,计算机通电之后,先通过 BIOS,到 MBR,通过 GRUB 引导操作系统启动。以上就是计算机 boot sequence 的过程。
通过 GRUB 引导启动操作系统,之后操作系统就会接管系统启动,操作系统启动过程也分为很多步骤。
BIOS 在 MBR 中搜索 boot record,因为主引导分区必须保证尽量小,所以通过 stage 1.0 找到 1.5 GRUB 引导,GRUB 引导必须在 boot record 和第一个分区之间,将 GRUB 加载到内存之后进入 stage 1.5.
上面提到 GRUB 必须在引导记录和磁盘第一个分区之间,这一块区域因为历史原因被预留,磁盘的第一个分区从 sector 63 开始,MBR 在 sector 0,所以留下了 62512 byte sectors,大概 31774 bytes,用来保存 core.img, core.img 只有 25389 字节,所以完全足够。
需要注意的是,/boot
目录需要存放在支持 GRUB 的文件系统上。stage 1.5 在找到 /boot
文件和加载必要的驱动之后执行。
GRUB stage 2 阶段会加载 Linux kernel 到内存中,kernel 和相关的文件在 /boot
目录中。kernel 文件都以 vmlinuz
开头。
kernel 都是压缩的格式以节省空间,当计算机解压并加载内核到内存之后,会加载 systemd,自此系统启动过程结束,内核和 systemd 都已经在运行。
systemd 是所有进程的创始者,他负责将系统启动至一个可工作的状态,他负责包括挂载系统文件,启动管理系统服务等等工作。
首先 systemd 会尝试使用 /etc/fstab
中定义的内容挂载文件系统,包括定义的根分区,swap 分区文件等等。
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/sda1 during installation
UUID=3d1b7e3e-c184-4664-9555-2b088997f2c8 / ext4 errors=remount-ro 0 1
# swap was on /dev/sda5 during installation
UUID=b99bf592-a25b-4ca0-b597-fc62e121aae1 none swap sw 0 0
关于 systemd 的启动顺序,可以参考阮一峰的这篇
默认情况下 GRUB 会有 10 秒时间来给用户选择系统,这个设置可以通过修改 /etc/default/grub
来修改。
GRUB_DEFAULT=0 # 默认启动的系统序号
GRUB_TIMEOUT=4 # 默认等待多久以预设系统开机
更新之后使用 sudo update-grub
来更新 GRUB。
GRUB Manual 手册中提供了完整的参数解释。
使用 Clonezilla 复制硬盘所有分区到另外一块硬盘,而我这边遇到的情况是复制结束之后硬盘没有 boot 分区,导致 BIOS 无法找到主引导分区。
解决办法是使用 Clonezilla 的专家(高级)模式,在高级模式中会自动修复 grub 的问题
这个问题表现形式可能是各种各样的,开机黑屏,或者在 grub 引导之后出现各种乱码命令。对于这个问题的解决方法可能需要是修改 /etc/fstab
,将其中硬盘的 UUID ,通过 sudo blkid 查看获取后保证 /etc/fstab
中启动的硬盘是一块。可以参考之前的文章
一直都知道 grep 很强大,但是一直都没有办法来定义它,直到看到 man 介绍第一行,非常简洁精炼 “print lines matching a pattern”,一下子就知道了 grep 的作用。
grep 全称是 Global Regular Expression Print。grep 的工作方式是这样的,它在一个或多个文件中搜索字符串模板。如果模板包括空格,则必须被引用,模板后的所有字符串被看作文件名。搜索的结果被送到标准输出,不影响原文件内容。
grep 可用于 shell 脚本,因为 grep 通过返回一个状态值来说明搜索的状态,如果模板搜索成功,则返回 0,如果搜索不成功,则返回 1,如果搜索的文件不存在,则返回 2。我们利用这些返回值就可进行一些自动化的文本处理工作。
在 UNIX
早期的编辑器中,如果要查找比如 junk
这个单词,需要输入 /junk/p
来打印找到的一个行,如果要找所有行,就使用 g/junk/p
,g 是 global search 的意思。
这个功能被独立出来作为了命令,只做一件事情,就是全局搜索,并打印,叫做 grep,其实是 g/regular expression/p
的缩写,可以理解为 g/re/p
,在 vi
或者 sed
中类似的功能也经常能见到。
下面这些选项非常常用,记住:
-i 忽略大小写
-v invert match
-n 行号
-B NUM -A NUM
-C NUM
比如说在当前目录下,搜索文件中包含的 password 词
grep password *
grep 会自动在当前目录下搜索并将包含 password
行打印出来,所以千万不要在本地文件中存放密码一类的敏感信息,这一无异于将密码写在显示器上。
使用 -i
来忽略大小写
grep -i password *
这一行命令会匹配,比如 Password,PASSWORD 等等。
将 grep 作为过滤器来过滤标准输出的内容
cat /etc/passwd | grep 'sshd'
强制让 grep 输出文件名
grep 'sshd' /etc/passwd /dev/null
这时 grep 会打印文件名,冒号,结果
-n
参数会显示行号
grep -n pattern file.txt
正常情况下 grep 会过滤出匹配 正则的行,使用 -v
参数反之,显示不包含正则的行
grep -v 'junk' *
再比如,如果想要设置 every
但是不想搜索 everyone
,everybody
和 everywhere
可以使用
grep every * | grep -v one | grep -v body | grep -v where
grep -B 10 -A 20 pattern file.txt
输出匹配 PATTERN 行的前 10 行和 后 20 行。
很多时候一行日志中有非常多的内容,我们往往只关心特定的部分,这个时候可以使用 grep 的正则来过滤出我们关心的部分,比如日志中的耗时我们可能会打印出 cost 10ms
这样的内容
grep -o -P "cost [0-9]+ms"
说明:
-o
表示只输出匹配到的内容,每一行显示一个-P "regex"
表示开启正则过滤当然如果组合使用 sed 也能够做到同样的事情。
在 grep 之上,后人又开发了很多很有用的命令,比如不解压的情况下搜索压缩包中的内容,再比如简便版的 ack-grep
jinja2 是基于 Python 的模板引擎。
注意下面的 \ 是因为模板需要转义,使用时需要去掉,另外大括号和 % 之间的空格也需要去掉。
\{\% ... \%\} Statements 控制结构,比如 if/elif/else for-loops,macros,block 等等
\{\{ ... \}\} Expressions 表达式,用来输出结果
\{\# ... \#\} Comments 注释
\# ... \#\# Line Statements
\{\{ name \}\}
结构表示一个变量
过滤器用法
Hello, \{\{ name|capitalize \}\}
常用的过滤器
过滤器 | 说明 |
---|---|
safe | 渲染值时不转义 |
capitalize | 把值的首字母转换成大写,其他字母转换成小写 |
lower | 把值转换成小写形式 |
upper | 把值转换成大写形式 |
title | 把值中每个单词的首字母都转换成大写 |
trim | 把值的首尾空格去掉 |
striptags | 渲染之前把值中所有的 HTML 标签都删掉 |
length | 输出长度 |
完整的过滤器清单可以在文档中找到:http://jinja.pocoo.org/docs/templates/#builtin-filters
用来控制流程
常见的 if-else
{ % if user % }
Hello, !
{ % else % }
Hello, Stranger!
{ % endif % }
for 循环
<ul>
{ % for comment in comments % }
<li></li>
{ % endfor % }
</ul>
宏,类似方法
{ % macro render_comment(comment) % }
<li></li>
{ % endmacro % }
<ul>
{ % for comment in comments % }
{ % endfor % }
</ul>
保存到文件中使用 import
{ % import 'macros.html' as macros % }
<ul>
{ % for comment in comments % }
{ % endfor % }
</ul>
需要多次使用的模板可以单独放在文件中,然后 使用 include
{ % include 'common.html' % }
模板继承,先定义父模板
<html>
<head>
{ % block head % }
<title>{ % block title % }{ % endblock % } - My Application</title>
{ % endblock % }
</head>
<body>
{ % block body % }
{ % endblock % }
</body>
</html>
然后继承
{ % extends "base.html" % }
{ % block title % }Index{ % endblock % }
{ % block head % }
<style>
</style>
{ % endblock % }
{ % block body % }
<h1>Hello, World!</h1>
{ % endblock % }
WTForms 支持的字段类型
字段类型 | 说明 |
---|---|
StringField | 文本字段 |
TextAreaField | 多行文本字段 |
PasswordField | 密码文本字段 |
HiddenField | 隐藏文本字段 |
DateField | 文本字段,值为 datetime.date 格式 |
DateTimeField | 文本字段,值为 datetime.datetime 格式 |
IntegerField | 文本字段,值为整数 |
DecimalField | 文本字段,值为 decimal.Decimal |
FloatField | 文本字段,值为浮点数 |
BooleanField | 复选框,值为 True 和 False |
RadioField | 一组单选框 |
SelectField | 下拉列表 |
SelectMultipleField | 下拉列表,可选择多个值 |
FileField | 文件上传字段 |
SubmitField | 表单提交按钮 |
FormField | 把表单作为字段嵌入另一个表单 |
FieldList | 一组指定类型的字段 |
WTForms 支持的验证函数
验证函数 | 说明 |
---|---|
验证电子邮件地址 | |
EqualTo | 比较两个字段的值;常用于要求输入两次密码进行确认的情况 |
IPAddress | 验证 IPv4 网络地址 |
Length | 验证输入字符串的长度 |
NumberRange | 验证输入的值在数字范围内 |
Optional | 无输入值时跳过其他验证函数 |
Required | 确保字段中有数据 |
Regexp | 使用正则表达式验证输入值 |
URL | 验证 URL |
AnyOf | 确保输入值在可选值列表中 |
NoneOf | 确保输入值不在可选值列表中 |
在模板中使用
<form method="POST">
</form>
赋值语句比较简单,Learning Python 这本书中对赋值语句介绍比较详细,分类也讲述的比较细,这篇文章就只简单的记录一些容易混乱的知识点,并不记录所有赋值语句需要注意的点。
在之前的文章中就交代过共享引用是需要特别注意的,在 augmented assignment 中也是
>>> L = [1, 2]
>>> M = L # L M 共享同一个对象
>>> L = L + [3, 4] # Concatenation 会创建新的对象
>>> L, M # 所以他们的值不相同
([1, 2, 3, 4], [1, 2])
>>> L = [1, 2]
>>> M = L # Share
>>> L += [3, 4] # 但是 `+=` 其实用的是 extend 方法,所以是原地修改
>>> L, M
([1, 2, 3, 4], [1, 2, 3, 4])
表达式同样也分为很多种
>>> L = [1,2]
>>> L.append(3)
>>> L
[1, 2, 3]
不过需要注意的是,比如 append()
方法并没有返回值,所有返回的 L 会是 None
>>> L = L.append(4)
>>> L
None
Maven 是一个项目管理工具,主要用于项目构建,依赖管理,项目信息管理。自动化构建过程,从清理、编译、测试和生成报告、再到打包和部署。Maven 通过一小段描述信息来管理项目。
安装之前先把 JDK 环境配置好。
Debian/Ubuntu/Linux Mint 下
sudo apt install maven
如果要手动安装则按照下面步骤,选择一个合适的版本
tar zxvf apache-Maven-3.3.9-bin.tar.gz
sudo mv apache-maven-3.3.9/ /opt/
sudo ln -s /opt/apache-maven-3.3.9/ /opt/maven
root 身份修改配置命令 sudo vi ~/.bashrc
在文件最后添加:
#set Maven environment
#export Maven_OPTS="-Xms256m -Xmx512m"
export M2_HOME=/opt/maven
export M2=$M2_HOME/bin
export PATH=$M2:$PATH
保存并关闭。
mvn -version
如果进行了上面步骤在任意目录中 mvn 命令不能使用,可以在 /etc/profile
文件后面加入下面三行 sudo vim ~/.bashrc
然后输入以下内容
Maven_HOME=/usr/local/apache-maven-3.3.9
export Maven_HOME
export PATH=${PATH}:${Maven_HOME}/bin
设置好 Maven 的路径之后,需要运行下面的命令 source ~/.bashrc
使刚刚的配置生效
Maven 最熟悉的一个概念就是 POM,Maven 项目会有一个 pom.xml 文件, 在这个文件里面添加相应配置,Maven 就会自动帮你下载相应 jar 包
<dependency>
<groupId>com.google.firebase</groupId> 项目名
<artifactId>firebase-admin</artifactId> 项目模块
<version>5.3.1</version> 项目版本
</dependency>
项目名 - 项目模块 - 项目版本
三个坐标定义了项目在 Maven 世界中的基本坐标,任何 jar,pom, war 都是基于这些坐标进行区分的。
groupId
定义了项目组,组和项目所在组织或公司,或者开源项目名称,一般为公司域名反写,比如 com.google.firebase 等等。Maven 项目和实际项目并不一定是一对一关系,比如 SpringFramework 实际项目,对应的 Maven 项目会有很多,spring-core, spring-context 等等,更进一步推荐 groupId 应当定义项目隶属的实际项目,如果定义到组织或者公司,那么一个组织下可能会有很多实际项目,造成混乱artifactId
定义了 Maven 项目的名称,在组中的唯一 ID,在同一个项目中可能有不同的子项目,可以定义不同的 artifactId,可以理解为 Maven 项目的模块。artifactId 也是构建完成项目后生成的 jar 包或者 war 包的文件名的一部分。version
顾名思义,就是项目的版本号,如果项目维发布,一般在开发中的版本号习惯性加上 SNAPSHOT, 比如 1.0-SNAPSHOT根据上面的例子,比如上面定义的 Maven 坐标,可以在对应的中央仓库中 https://repo1.maven.org/maven2/com/google/firebase/firebase-admin/5.3.1/
目录下找到对应的文件。
scope
定义了依赖范围,如果依赖范围为 test ,那么该依赖只对测试有效,也就是说在测试代码中引入 junit 有效,在主代码中用 junit 会造成编译错误。如果不声明依赖范围则默认为 compile ,表示该依赖对主代码和测试代码都有效。Maven 有以下几种依赖范围:
依赖范围和 classpath 的关系
依赖范围 Scope | 编译 classpath 有效 | 测试 classpath 有效 | 运行 classpath 有效 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | - | Y | - | junit |
provided | Y | Y | - | servlet-api |
runtime | - | - | Y | JDBC 驱动 |
system | Y | Y | - | 本地的, Maven 仓库之外的类库 |
Maven 依赖调解 Dependency Mediation ,第一原则:路径最近者优先;第二原则:第一声明者优先。
SNAPSHOT 快照版本只应该在组织内部的项目或者模块之间的依赖使用,组织对于这些快照版本的依赖具有完全的理解和控制权。项目不应该依赖于任何组织外部的快照版本,由于快照的不稳定性,依赖会产生潜在的危险,即使项目构建今天是成功的,由于外部快照版本可能变化,而导致未来构建失败。
在上面介绍 Maven 的作用的时候提到了 Maven 的两个核心概念:坐标和依赖,这也是 Maven 首要解决的问题。这里要引入 Maven 另外一个核心概念:仓库。 Maven 时间中通过坐标来定位依赖,Maven 通过仓库来统一管理这些依赖。
Maven 项目的每一个构件对应着仓库的路径为: groupId/artifactId/version/artifactId-version.packageing
Maven 的仓库分为远程仓库和本地仓库,当第一次运行 Maven 命令时,需要网络连接,从远程仓库下载可用的依赖和插件。而当以后在运行 Maven 命令的时候,Maven 会自动先检查本地 ~/.m2/repository
目录下的依赖,如果本地有缓存优先从本地获取,在找不到的情况下去远程仓库寻找。
常用的在线 maven 依赖查看网站
Maven 的生命周期是抽象的,实际行为都有插件完成。
清理项目,包含三个阶段
pre-clean
执行清理前需要完成的工作clean
清理上一次构建生成的文件post-clean
执行一次清理后需要完成的工作定义了真正构建时所需要执行的步骤
建立和发布项目站点
具体的流程可以参考下图
下面是一个标准的 Maven 工程
src/main/java - 存放项目.java 文件;
src/main/resources - 存放项目资源文件;
src/test/java - 存放测试类.java 文件;
src/test/resources - 存放测试资源文件;
target - 项目输出目录;
pom.xml - Maven 核心文件(Project Object Model);
mvn archetype:create 创建 Maven 项目
mvn compile 编译源代码
mvn deploy 发布项目
mvn test-compile 编译测试源代码
mvn test 运行应用程序中的单元测试
mvn site 生成项目相关信息的网站
mvn clean 清除项目目录中的生成结果
mvn package 根据项目生成的 jar
mvn install 在本地 Repository 中安装 jar
mvn eclipse:eclipse 生成 eclipse 项目文件
mvn jetty:run 启动 jetty 服务
mvn tomcat:run 启动 tomcat 服务
mvn clean package -DMaven.test.skip=true 清除以前的包后重新打包,跳过测试类
mvn clean package 清除以前的包后重新打包
创建一个 Maven 项目
mvn -B archetype:generate
-DarchetypeGroupId=org.apache.maven.archetypes
-DgroupId=com.log4j.maven
-DartifactId=dependency-example
平时开发环境使用的是 jetty,而 Java Web 有一个更好更快的服务器 Resin,这篇文章就来说一下什么是 Resin,以及在 Debug 中如何使用。
Resin 是一个提供高性能的,支持 Java/PHP 的应用服务器。目前有两个版本:一个是 GPL 下的开源版本,提供给一些爱好者、开发人员和低流量网站使用;一种是收费的专业版本,增加了一些更加适用于生产环境的特性。
Resin 也可以和许多其他的 web 服务器一起工作,比如 Apache Server 和 IIS 等。Resin 支持 Servlets 2.3 标准和 JSP 1.2 标准。熟悉 ASP 和 PHP 的用户可以发现用 Resin 来进行 JSP 编程是件很容易的事情。Resin 支持负载平衡,可以增加 WEB 站点的可靠性。方法是增加服务器的数量。比如一台 Server 的错误率是 1% 的话,那么支持负载平衡的两个 Resin 服务器就可以使错误率降到 0.01%。到目前为止,Resin 对 WEB 应用的支持已经远远超过 Tomcat 等各种大型的 Server。
在 Resin 的官方 quick start 教程中有各大平台详细的安装指导。我在使用 apt 安装时没有成功,这里就记录下手工安装的过程。
在 http://caucho.com/download 网址下载, Resin 有两个版本, Pro 版和 GPL 开源版,个人使用开源基础版本已经足够。安装过程如下:
JAVA_HOME
中tar -vzxf resin-4.0.x.tar.gz
根据下载的最新版本解压cd resin-
./configure
更多参数参考 configure optionsmake
sudo make install
sudo resinctl start
, 或者运行 java -jar lib/resin.jar start
http://localhost:8080
可以使用 sudo resinctl stop
来停止运行,更多内容可以参考官方指南 。
第一步,添加 Resin Local 选项,在 IDEA 中 Run/Debug Configuration
中添加 Resin Local 选项
点击 configure 按钮,在弹出窗 Application Servers 中选择部分一中安装的 Resin 目录路径和目录下 Resin 的配置文件路径。
Run/Debug Configurations 中 Server 页面配置,基本都是默认。
Run/Debug Configurations 中 Deployment 页面配置,注意红色方框部分选择。选择 resin.xml 而不是 JMX 否则项目的 index 路径是 localhost:8080/appname/ 而不是 localhost:8080/
在 Resin 的服务器配置下 Depolyment 中 Depolyment method:有 JMX 和 resin.xml 两种选择,JMX 是把项目打包的文件放在 resin 服务器下 webapp 下 只有在服务器启动时 才把项目给拷贝过去 无法在 intellij 中实时更新;resin.xml 是在 C 盘 Temp 目录下 copy 了一份 resin.xml 的配置文件 然后把服务器目录空间指向了你的项目工作空间 可以实现 IntelliJ 修改实时更新。IntelliJ 默认的选择是 JMX 所以我们要选中 resin.xml 模式。同时当项目 Artifacts 指向的目录是 ROOT 时 上图中的 Use default context name(always true if depolyment method is JMX) 取消勾选
执行 build,得到 war 文件。执行 resin run/debug,会自动在你选择的浏览器中打开项目 index 页面。也可以在 IDEA 下方的 Application Servers 面板中进行 Resin 的启动,停止等操作。Resin 启动的打印信息也在此窗口显示。
在 Python 中调用系统命令可以使用两种方法,一种是 os 模块中的 system() 方法,一种是 subprocess 模块中的 call() 方法。
这个方法会接受一个字符串命令,然后在 subshell 中执行,通常是 linux/OSX 下的 bash ,或者 Windows 下面的 cmd.exe。根据官方的文档,os.system()
方法时使用标准 C 方法 system() 来调用实现的,所以存在和 C 方法中一样的限制。
os.system("python –version")
举例
import os
cmd = "git --version"
returned_value = os.system(cmd) # returns the exit code in unix
print('returned value:', returned_value)
默认情况下不使用 shell,他只是简单的执行传入的字符串
运行带参数的命令需要传 list
subprocess.call(["python", "–version"])
subprocess.call() 当使用 shell=True 时和 os.system() 一样使用 shell
subprocess.call(["python", "–version"], shell=True)
举例
import subprocess
cmd = "date"
# returns output as byte string
returned_output = subprocess.check_output(cmd)
# using decode() function to convert byte string to string
print('Current date is:', returned_output.decode("utf-8"))
subproecess 模块中如果想要实现更加复杂的命令,可以使用 Popen()。popen() 会创建一个管道,fork 一个子进程,然后该子进程执行命令。返回值在标准 IO 流中,该管道用于父子进程间通信。父进程要么从管道读信息,要么向管道写信息,至于是读还是写取决于父进程调用 popen 时传递的参数(w 或 r)。
import subprocess
child = subprocess.Popen(['ping','-c','4','douban.com'])
child.wait()
print 'main process'
默认情况下,开启子进程之后不会等待 child 执行完成,需要使用 wait() 来等待子进程完成。
父进程对子进程还有其他的操作
child.poll() # 检查子进程状态
child.kill() # 终止子进程
child.send_signal() # 向子进程发送信号
child.terminate() # 终止子进程
子进程的标准输入、标准输出和标准错误
child.stdin
child.stdout
child.stderr
举例
import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
print child1.stdout.read()
使用 subprocess 创建子进程能够重定向 stdin stdout,总体来说要比 os.system() 更加强大。