纯手工编译OpenJDK8。在Mac上尝试了一下,因为编译这玩意需要XCode4*,而现在XCode都升级到9了,虽然可以下载旧版的XCode,但试了一下貌似不太兼容。于是在Virtualbox中装lubuntu来编译一下玩玩。在Virtualbox使用NAT网络做端口转发的时候,竟然发现不支持主机的22端口(貌似是权限的问题),改为10240则Ok。

sudo apt-get install mercurial
sudo apt-get install lrzsz
hg clone http://hg.openjdk.java.net/jdk8u/jdk8u
cd jdk8u
sh get_source.sh

可能遇到的问题:

...
jdk:   abort: stream ended unexpectedly (got 8159 bytes, expected 29096)
...
WARNING: hotspot exited abnormally (255)
WARNING: jdk exited abnormally (255)
WARNING: nashorn exited abnormally (255)

解决办法是重试N次get_source.sh就可以了。

切换到最新的release tag:

hg tags
hg up jdk8u162-b12

安装依赖项:

sudo apt-get install build-essential
sudo apt-get install libasound2-dev libcups2-dev libfreetype6-dev
sudo apt-get install libx11-dev libxext-dev libxrender-dev libxtst-dev libxt-dev
sudo apt-get update
sudo apt-get upgrade

sudo add-apt-repository ppa:openjdk-r/ppa  
sudo apt-get update   
sudo apt-get install openjdk-7-jdk  
# refer https://github.com/hgomez/obuildfactory/wiki/How-to-build-and-package-OpenJDK-8-on-Linux

然后开始编译吧:

bash ./configure --with-target-bits=64 --with-freetype-include=/usr/include/freetype2/ --with-freetype-lib=/usr/lib/x86_64-linux-gnu

Ubuntu16下面必须指定freetype的路径,按照OpenJDK Build README里面所说,期望的路径是Expecting the freetype libraries under lib/ and the headers under include/. .而且特别指出:

*The build is now a "configure && make" style build
*Any GNU make 3.81 or newer should work
*The build should scale, i.e. more processors should cause the build to be done in less wall-clock time
*Nested or recursive make invocations have been significantly reduced, as has the total fork/exec or spawning of sub processes during the build
*Windows MKS usage is no longer supported
*Windows Visual Studio vsvars*.bat and vcvars*.bat files are run automatically
*Ant is no longer used when building the OpenJDK
*Use of ALT_* environment variables for configuring the build is no longer supported

因此有些文章上面设置ant, ALT_BOOTDIR等步骤不适用编译jdk8.为了提高编译速度,在虚拟机中设置了使用4个cpu核心。我们需要指定编译使用的cpu数来提高编译速度。

make clean
rm -rf build
bash ./configure --with-target-bits=64 --with-freetype-include=/usr/include/freetype2/ --with-freetype-lib=/usr/lib/x86_64-linux-gnu --with-jobs=4

这样配置完后的输出如下:

====================================================
A new configuration has been successfully created in
/home/riguz/jdk/jdk8u/build/linux-x86_64-normal-server-release
using configure arguments '--with-target-bits=64 --with-freetype-include=/usr/include/freetype2/ --with-freetype-lib=/usr/lib/x86_64-linux-gnu --with-jobs=4'.

Configuration summary:
* Debug level:    release
* JDK variant:    normal
* JVM variants:   server
* OpenJDK target: OS: linux, CPU architecture: x86, address length: 64

Tools summary:
* Boot JDK:       java version "1.7.0_95" OpenJDK Runtime Environment (IcedTea 2.6.4) (7u95-2.6.4-3) OpenJDK 64-Bit Server VM (build 24.95-b01, mixed mode)  (at /usr/lib/jvm/java-7-openjdk-amd64)
* C Compiler:     gcc-5 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 version 5.4.0 (at /usr/bin/gcc-5)
* C++ Compiler:   g++-5 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 version 5.4.0 (at /usr/bin/g++-5)

Build performance summary:
* Cores to use:   4
* Memory limit:   1997 MB
* ccache status:  not installed (consider installing)

这里有个performance的提示,就是ccache。但是这玩意安装之后貌似识别错误,索性不要了。然后就可以开始编译了

make images

尴尬的是虚拟机磁盘空间(10G)不够,幸亏可以动态调整一下

VBoxManage modifyhd ~/VirtualBox\ VMs/lubuntu/lubuntu.vdi --resize 20480
# 完了之后需要进入到系统,用分区工具调整分区大小,可能需要删除swap分区,扩展/后再重建swap分区
sudo apt-get install gparted
gparted

images目标会create complete j2sdk and j2re images,花费了大约10分钟时间:

----- Build times -------
Start 2018-04-11 10:03:36
End   2018-04-11 10:13:47
00:00:23 corba
00:00:15 demos
00:04:49 hotspot
00:01:02 images
00:00:15 jaxp
00:00:20 jaxws
00:02:26 jdk
00:00:28 langtools
00:00:13 nashorn
00:10:11 TOTAL
-------------------------

生成的文件在build/*/images中

riguz@riguz-VirtualBox:~/jdk/jdk8u/build/linux-x86_64-normal-server-release/images/j2sdk-image$ cd bin/
riguz@riguz-VirtualBox:~/jdk/jdk8u/build/linux-x86_64-normal-server-release/images/j2sdk-image/bin$ ls
appletviewer  javadoc       jdeps       jsadebugd     pack200      servertool
extcheck      javah         jhat        jstack        policytool   tnameserv
idlj          javap         jinfo       jstat         rmic         unpack200
jar           java-rmi.cgi  jjs         jstatd        rmid         wsgen
jarsigner     jcmd          jmap        keytool       rmiregistry  wsimport
java          jconsole      jps         native2ascii  schemagen    xjc
javac         jdb           jrunscript  orbd          serialver
riguz@riguz-VirtualBox:~/jdk/jdk8u/build/linux-x86_64-normal-server-release/images/j2sdk-image/bin$ ./java -version
openjdk version "1.8.0-internal"
OpenJDK Runtime Environment (build 1.8.0-internal-riguz_2018_04_11_10_03-b00)
OpenJDK 64-Bit Server VM (build 25.71-b00, mixed mode)

最后就是测试了。测试需要安装jtreg,注意一定要指定JT_HOME.

sudo apt-get install jtreg
cd test && make PRODUCT_HOME=`pwd`/../build/*/images/j2sdk-image JT_HOME=/usr/bin/jtreg all

ส็็็็็็็็็็็็็็็็็็็็็็็็็༼ ຈل͜ຈ༽ส้้้้้้้้้้้้้้้้้้้้้้้
ส็็็็็็็็็็็็็็็็็็็็็็็็็༼ ಠ_ಠ ༽ส้้้้้้้้้้้้้้้้้้้้้้้
ส็็็็็็็็็็็็็็็็็็็( ͡° ͜ʖ ͡°)
ส้้้้้้้้้้้้้้้้้้้้้้้ ส็็็็็็็็็็็็็็็็็็็็็็็็็
S̢͎̳̞̲͈̪̳̻ͮͩt̟̳̏ͬ̔͒̈́ͦ͠a̞̤̝̟ͫ̽̂̈́ͪ͐͘n͕͐͑ͪ͐ͦ͋ͮ̅d͚̗̙̎ͫ̌â̗̬͓͍͍̳̥͆̕͠r̢̘ͣ̀d̢̢̢̘̲̺͙̂̈́̊ͬ ͎͎̫͚̣̺̤̖͊̏̀ͬ͞u̧͆ͩ́͒҉͔̠̪̖̹̠̰͎ṇ̸̛͚̟̫͎̟̣̜͋̈́ͧͯi̲̲̺͑̐ͣ͗̿̕͘͝c̦͈͇̦͈ͦ̆ͨ͝o̟̭̫̥͎̹͆́ͥ͊ͬ̏͝d̪͔̯̥̩͙̝ͩ̏͒̈́ͩ̿́̕͜ͅe͍͓̻̊͛ͅ ̸̧̻̺̤̠͙ͪ̋̽l̛̥̥ͬ͂̈́ͤ̓̀̓̚͘ͅͅͅǒ̮͓̼ͭ̂̆̇̕͘ͅl̯̯̟̗͔̳͉̰ͫ̒ͧͦͩͦ̓̓͢ͅs̝͎͚̗̮̟̒̔͛̈̊͋͒ͩͅ Cool!

我也来生成一个:
Rͨ̍̀̐iͩͤͦ̈́́̓g̃ͬ̾u̓͆ͬ̐̎ͨ͋̆z̑ͤͯ̒ͦ͗̿̍ ͤ̇̒L͒̂͑̎ͣͣͯ̉e̊e̐̏̏̆̑͗ͥ́

哈哈哈
了解更多参见

程序中大都需要定义各种配置,诸如数据库连接之类的,最近也需要开发Web框架,于是也想找个比较好用的配置文件格式。搞来搞去,发现都不是很喜欢。先来看一下几种常见的配置文件格式吧:

Properties

Java所带来的Properties文件可能是用的比较多的格式了吧,就是一个简单的key-value的文本文件,但是缺点也很明显:

  • Unicode需要转码,看着不是很蛋疼么?
  • 不支持数组类型,所以以前经常会用key.1,key.2...key.n这样的方式来遍历得到一个数组
  • 扁平结构,如果碰到一些比较长的key就有点不好看了(比如SpringCloud的配置,spring.jpa.datasource.xxx)写起来比较麻烦
spring.data.mongodb.host= localhost
spring.data.mongodb.port=27017 # the connection port (defaults to 27107)
spring.data.mongodb.uri=mongodb://localhost/test # connection URL
spring.data.mongo.repositories.enabled=true # if spring data repository support is enabled

Yaml/TOML

Yaml好像很流行的样子,我们在springcloud的项目中大量使用,但是说实话这个格式我也不喜欢,为啥?

  • 依赖于缩进,复制粘贴的时候麻烦了
  • 语法有点复杂了
    TOML感觉和YAML差不多,也挺复杂的样子。
# Zuul
zuul:
  host:
    connect-timeout-millis: 50000
    socket-timeout-millis: 10000


# Hystrix
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000

Ini

Windows所带来的格式,优点是可以带分组,好像比Properties文件更舒服一点,但是对于上面提到的缺点也有。

[curentUser]      ;  this is a Section
name=wisdo     ; this is Parameters
organization=cnblogs   ; this is Parameters
 
[database] 
server=127.0.0.0   ; use IP address in case network name resolution is not working 
port=143 
file = "user.dat" 

JSON/LUA

Json的缺点在于你要用很多个引号,同时最大的问题在于不支持注释。Lua可能是我最想用的脚本了,但是在Java中使用也比较麻烦,尤其是我想手写一个配置文件解析器,这样就麻烦了(主要是不会)。

还有Ini + Json的方法,但是感觉也比较丑,于是想来想去,还不如按照自己的意愿发明一种配置文件格式好了,主要有以下的考虑:

  • 语法应该简单,不需要依赖缩进
  • 支持数组
  • 支持使用变量(类似shell)
  • 支持Unicode,中文直接写,所见即所得
  • 支持某种形式的命名空间(类似ini中的section)来对配置进行分组
  • 支持注释
  • 支持多行字符串
  • 格式好看...

目前正在计划中,准备利用Antlr实现解析。

在一个“敏捷”的团队,写注释被认作是一个不好的习惯,因为他们认为,

Good programming is self-explanatory. Bad Programming requires explanation
总结一下,认为程序中不需要写注释的原因主要有如下的几点:

  • 需要写注释的程序说明代码不够清晰啊,可以可以通过重构的方式,让代码变得“可读”
  • 维护注释是一件工作量很大的事情,改完代码之后,时常会忘记修改注释
  • 注释如果解释的不清楚,那就需要“注释的注释”...
  • ……
    不能不说这些没有道理,实际上也都是很关心的问题,代码写的更好更可读,当然是值得推崇的。并且诚如所言,代码应该是“自解释”的,大部分情况下,我们可能的确不需要注释。代码的可读性,和注释,目的都是一样的,让别人看得懂,不会掉坑里面。这里的坑,可能是代码逻辑的,可能是业务逻辑的,可能是某个库的bug,可能是某种奇怪的设计或者历史原因。

所以说,有另外一个更重要的他们没有考虑到的就是:

self-explanatory code only tell how it is working. It rarely tells how it should work.

正好最近又遇到一次坑。来描述一下这个故事:
起因是我们系统需要从一个第三方系统中查询数据。这个系统调用,我们代码里面是这么写的:

try {
    return client.getVehicleBaseData(finOrVin);
} catch (Exception e) {
    log.error("error loading vehicle basic data from eva for finOrVin:{}", finOrVin);
    throw new EvaAccessFailureException(evaLoadService.generateFallback(e.getMessage()));
}

这段代码的功能是,调用外部系统的api,然后返回一个结果;如果出错则抛出异常。同时,需要根据出错的“代码”来判断是对方系统的内部错误,还是资源找不到。

Fallback generateFallback(String message) {
    try {
        int startPos = message.indexOf("{\"error\":");
        if (startPos == -1) {
            return new Fallback(UNEXPECTED, message);
        }
        EvaErrorResponse response = JsonUtils.unmarshal(message.substring(startPos), EvaErrorResponse.class);
        return new Fallback(getByStringValue(response.getError().getErrorCode()));
    } catch (Exception e) {
        log.error("unexpected error message from EVA {}", message);
        log.error(e.getMessage(), e);
        return new Fallback(UNEXPECTED, message);
    }
}

这段代码尝试从message里面解析一串error,然后再反序列化为JSON,这里是这个EvaErrorResponse的定义:

@Data
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class EvaErrorResponse {
    private Error error;
}

@Data
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
class Error {
    private String errorCode;
    private String errorDesc;
}

姑且不说一个简单的Bean用这么多Lombok注解的问题~ 然后我需要做的是,模拟这个系统的出错返回,因为我们的开发环境无法连真实的三方系统测试。那么问题来了,三方系统出错的时候,应该返回什么呢?

首先问问写这个代码的人(也就是直接对接这个系统的人)吧。他给了我一个文档,文档里面是这么描述的:
error_3rd.png

那么问题来了,这和代码定义完全不一样啊!然后告诉我以代码为准。从这个代码根本无法确定错误返回结构。然后又看看我们这个模拟的stub的代码,关于出错的地方是这么定义的:

public class OabResponseDto {

    private boolean success;

    private Object result;

    private String error;

后来才觉察到,这是另一个系统的接口返回了。但几个系统的模拟stub都写到了一起,让人完全无法确定真实的三方接口定义。最终,我找到了调用这个接口的测试环境,自己调用了一次,原来结果是这样的:

{"error":{"errorCode":"WDCS0003","errorDesc":"Resource not available!"}}

这耗费了我半天的时间。于是为了避免有人再踩这种坑,我加了个注释在这里:

Fallback generateFallback(String message) {
    try {
        /**
         * example actual response from eva:
         * {"error":{"errorCode":"WDCS0003","errorDesc":"Resource not available!"}}
         */
        int startPos = message.indexOf("{\"error\":");

然而这又被批判了,理由是这段注释不能解释代码。因为这里message并不是这样。那message到底是什么样?他们说,你可以调试打个断点看。难道让每一个看代码的人都打个断点来看么,这是什么逻辑!我就呵呵了。最终我还是妥协了,删了呗,对我而言无任何影响。

很多人,如同我的同事,似乎觉得lombok这玩意就像神一样的存在,“极大”的方便了项目的开发。我个人是不喜欢这玩意的,很简单的理由:

  • 生成getter/setter不是多么困难的事情,IDE很简单就能帮你搞定
  • 我不喜欢为自己的IDE装一大堆插件,还要为项目手动开启一下Annotation Processing
  • 代码不可见,意味着生成的getters/setter方法,以及@AllArgConstructor生成的方法无法维护

当我把这些想法告诉同事的时候,同事们都觉得我脑子有问题,理由不充分,”lombok只是一个工具,只是没有找到使用工具的最佳实践“。实际上对于技术人员来说,想说服别人是很困难的事情,然而我们为什么要试图说服别人呢?没有多大的意义。相比于工具,有一些更重要的东西就是:经验和原则。

就我的经验,能简单的事情就不要复杂化,越是复杂越难以维护。当然也有人和我是相同的观点,看了一些有意思的关于为什么不用lombok的讨论,贴出来看看:

OK, let me put it one more time: this has caused me too many bugs. Let me tell
you my past experiences with Lombok, as this is the root of the issue.

On one project, a new version of the Lombok plugin caused the IDE to
crash (I think this was Intellij). So nobody could work anymore. On
another project, Lombok made the CI server crash (and would have
probably caused the production server to crash), as it triggered a bug
in the JVM On a third project, we achieved 30% performance increase by
recoding the equals/hashcode from Lombok
-> In those 3 projects, some developer gained 5 minutes, and I spent hours recoding everything. So yes, a bad experience.

Then, for JHipster, the story is also that we can't ask people to
install a plugin on their IDE:

1st goal is to have a smooth experience: you generate the app and it
works in your IDE, by default 2nd goal is that you can use whatever
IDE you want. And some people have very exotic things, for example I
just tried https://codenvy.com/ -> no plugin for this one, of course

Oh, and I just got 2 more:

Lombok crashing with MapStruct Lombok making Jacoco fails, which meant
the project didn't pass the Sonar quality gate

参考阅读: