Ling Zhang Ling Zhang's Blog

Moving to Github Pages

I just finished migrating my blog website hosted on DreamHost to GitHub Pages (github.io). The old WordPress site has been deployed since July 2006 right after I migrated my blog content from a blogging host provider in China.

Moving from DreamHost to GitHub took me about half a day using Claude Code. It gave me the insights on how to complete this task and wrote all the migration code. Everything went extremely smooth, and Claude nearly made no mistakes. I spent more hours on polishing the UI and fixing some smaller user experience problems.

I no longer need to pay $191.88/year. Open Source and AI win:-)

中文翻译:

我刚刚完成了将托管在 DreamHost上的博客网站迁移到GitHub Pages(github.io)。早先的WordPress系统自2006年7月部署到DreamHost起就一直运行至今,而更我是从博客中国把早期的文章迁移到了DreamHost。

从DreamHost迁移到GitHub花了大约半天时间,全程使用Claude Code来完成所有迁移代码。整个过程非常顺畅,Claude几乎没有犯任何错误。我花了更多时间在打磨UI和修复一些小体验问题上。

我每年不再需要支付 191.88 美元了 - 感谢开源和AI。

Read more

为什么伟大不能被计划

这是2024年底在上海交通大学图书馆看的一本书,我在空白首页上看到了同学写的阅读心得,很欣喜。

why greatness cannot be planned - cover why greatness cannot be planned - reading note

English Translation:

This is a book I came across at the Shanghai Jiao Tong University Library toward the end of 2024. On the blank first page, I discovered reading notes left by a SJTU student — and what a delightful surprise it was.

Read more

FDE的中国实践

FDE(Forward Deployed Engineer)要解决的问题,是把AI能力和客户具体场景结合,构建满足客户需求的个性化解决方案。FDE角色是否能够成功,在中国取决于以下几个问题的回答:

  1. AI基础能力是否足够泛化;
  2. 标准化软件产品是否在中国有足够的市场;
  3. 企业客户是否愿意为价值买单;
  4. 以工时(人天)作为驻场咨询/交付的费用核算模式是否会继续;
  5. AI公司是否有足够的利润空间养活FDE。

English Translation:

The problem that FDEs (Forward Deployed Engineers) aim to solve is to combine AI capabilities with specific customer scenarios to build personalized solutions that meet client needs. Whether the FDE role can succeed in China depends on the answers to the following questions:

  1. Whether the foundational AI capabilities are sufficiently generalizable;
  2. Whether there is a large enough market for standardized software products in China;
  3. Whether enterprise clients are willing to pay for value;
  4. Whether the billing model based on man-days for on-site consulting and delivery will continue;
  5. Whether AI companies have sufficient profit margins to sustain FDE teams.

Read more

Housekeeping

It has been a long time since my last update on this blog site. I still pay the web hosting fee over the years but actually not contributing new content and did not spent time to manage this site.

I took some time today to do some housekeeping work on this web site and hope I could continue sharing my thoughts and opinions.

What I did for this housekeeping:

1. Update WordPress version from 4.x to 6.x;

2. Backup all WordPress blog posts, comments and uploaded files;

3. Enable SSL secure access;

4. Pick up a new and modern theme (and I like it);

5. Delete all pending spam comments.

Cheers~

Read more

Search bar in Firefox 34

The latest Firefox 34 shipped with a revised search bar design. To be honest, I don't like it because I need to change the default search engine from time to time and do not want to click my preferred search engine every time I search.

Lucky it's possible to restore the previous design by modifying the Firefox configuration - to change 'browser.search.showOneOffButtons' to false (details - http://forums.mozillazine.org/viewtopic.php?f=23&t=2890823)

Before

before

After
after

Read more

Windows 7自建虚拟无线WiFi

升级到iOS8后, Cisco LEAP协议不支持,无法连到公司内部无线网,中国移动GPRS实在太慢。找到几篇文章,使用Windows 7自带的虚拟WiFi在笔记本上自建AP,让移动设备上网,步骤简化如下:

1. 在管理员权限下运行命令行cmd;

2. 运行命令 netsh wlan set hostednetwork mode=allow ssid=<your_ap_name> key=<password>

其中<your_ap_name>是无线AP的ssid,用手机找WiFi可以发现;<password>是这个AP的连接密码;

3. 接着运行 netsh wlan start hostednetwork,这样在网络连接可以看到该虚拟WiFi连接:

virtual_wifi

4. 把一个实际网络连接的流量共享给该虚拟WiFi。方法是右键选择当前可用网络连接的‘属性’,然后在共享里分享给虚拟WiFi。

最后,如果你装了防火墙如Symantec Endpoint Protection,需要添加一条过滤规则放行移动设备对此虚拟WiFi适配器的访问。

Read more

CORS (Cross-Origin Resource Sharing) preflighted OPTIONS request

CORS提供了安全访问跨域请求的方案,目前主流的浏览器都支持CORS。其中CORS preflighted request中提到浏览器需要先发出一个OPTIONS的请求在服务器端获得通过后(利用HTTP response中的CORS header进行审批)才可以进行后续的访问。根据规范,此HTTP OPTIONS请求中不允许携带任何cookie

我很想知道这样的设计是出于什么考虑?如果是出于安全考虑,那么当前用户session如果已经登录此跨站网站并且有cookie,这些cookie对所访问的跨域服务器来说是没有个人信息泄露的问题,因为本来这些cookie就是服务器产生并返回给浏览器的,你送不送不存在安全隐患。Chrome之前有个bug就是在OPTIONS请求里面带了cookie,最近刚刚修复,而Chrome并没有质疑规范的设计是否合理。

不允许带Cookie在采用了如TAM, Siteminder等统一认证的环境下很麻烦,因为这样的request还没有机会到达应用本身就被前端reverse proxy(或web server agent)拦住了,而它们都不具备判断目标应用是否会放行此跨域请求的能力(一般情况下应用程序负责维护一个信任网站的白名单),所以还得通过配置放行此无cookie的请求到目标应用。

Read more

上手Bluemix (3)

截止上篇文章,我们已经在Bluemix上构建了一个可以连接Bluemix数据库服务、基于WebSphere Liberty Server的Java Web应用了。我们提到Connections Blogs的老版本(相比新版本对WebSphere Application Server (WAS)已经是最小的了)跑起来还是会有很多错误,大致总结一下:

1) WAS专用API. 如做Basic Authentication用的是WAS Programmatic Login的API;

2) 和WAS环境相关的配置文件。代码中需要到WAS环境下特定的目录找某些配置文件;

3) 不同Web container的实现差异。在移植过程中发现对于URL的最后一个斜杠/,在WAS和Liberty中处理是不同的;

4) 文件系统。Blogs会把上传附件保存在文件系统,目前我也不清楚Bluemix上给我的空间大小(应该是1G)和位置。通过测试说明至少服务器端代码是可以在Liberty安装目录下创建目录和文件的;

5) Directory service。这是Connections自己的code,根据用户配置调用WAS VMM API从Federated Repository,或者Connections Profiles里查找用户详细信息。这两个数据源在Bluemix上都没有,所以需要更新一些代码跳过用户同步过程。

在disable了Basic Authentication、Connections navbar、Directory service和Lucene index后,Blogs最终可以跑在Bluemix上。

最后的问题是用户登录,我没打算搞个LDAP,只要创建些简单的测试用户就可以了。在Liberty里很容易做到,通过在server.xml中添加用户定义和角色定义,如下:

定义用户:

<basicRegistry id="basic" realm="WebRealm">
      <user name="ajones1" password="jones1"/>
      <user name="ajones2" password="jones2"/>
      <user name="ajones3" password="jones3"/>
      <user name="ajones4" password="jones4"/>
      <user name="ajones5" password="jones5"/>
      <user name="ajones6" password="jones6"/>
</basicRegistry>

定义用户在应用中的role:

<application type="war" id="blogs" name="blogs" location="blogs.war">
    <application-bnd>
        
        <security-role name="person">
            <special-subject type="ALL_AUTHENTICATED_USERS" />
        </security-role>
        <security-role name="admin">
            <user name="ajones1"/>
        </security-role>
        
    </application-bnd>
</application>

"ALL_AUTHENTICATED_USERS"是个特殊的保留角色,对应于WAS的相同角色,用来指定所有已登录用户。测试中我发现security-role的映射有bug,不是每次调用request.isUserInRole(role)都会返回正确的结果。

接下来的事情很关键 - 如何把一份自定义的server.xml文件上传到server端?答案:你做不到。目前找到的解决方案是按照Bluemix的buildpack去部署应用,即同时上传Liberty的Runtime和应用程序。利用Eclipse的Bluemix插件很容易实现:

1) 下载Eclipse Bluemix插件。在Eclipse Marketplace搜索‘bluemix’,找到后安装:

bluemix-plugin-install

2) 安装后右键在Server Tab里点New Server,选择IBM Bluemix,开始配置:

config-bluemix-plugin

domain

bind-db

3) 配置完成后,可以直接用Publish选项发布整个Runtime和application。

upload

<终>

Read more

上手Bluemix (2)

上次说到在Bluemix新建一个简单的Java webapp,现在这个Java应用需要访问数据库服务来存取数据,我们来看看在Bluemix上该如何使用和管理数据库服务。首先,Bluemix上有多种数据库服务供选择,有传统的关系型数据库也有NoSQL (包含了IBM收购的Cloudant),因为Connections本身支持包括DB2, Oracle和SQL Server,所以我们选择基于IBM DB2的数据服务。

可以从Dashboard的Service tab或者直接从Catalog进入服务选择界面,选择Data Management -> SQL Database:

database

创建时需要指定服务名(注意这个名字既不是数据库名也不是schema名,仅仅是个标示符),同时如果应用已经创建好,可以在创建数据库服务的时候直接绑定应用。

create-db-instance

创建好数据库服务后回到Dashboard,可以看到名下有一个应用和一个服务 - APPS(1) & SERVICES(1) - 移植Connections Blogs需要的全部组件都齐了。

db-service

在创建并绑定数据库服务后,点击应用的Runtime选项,可以看到新生成的环境变量。这里包含了有关数据库连接的全部信息 - 注意所有的配置信息都无法更改,一旦重新创建数据库服务,数据库名、用户名、密码等都会随之改变。这些所谓’环境变量‘实际上是保存在Liberty的server.xml中,这样app在启动的时候可以正确的找到JNDI data source。

env_variable

接下来,我们需要创建数据库schema,tables,indexes...点击进入添加好的数据库服务,点'Launch'按钮会打开一个数据库管理界面,点击‘Work with Database Objects'就可以开始创建数据对象了。Bluemix的数据库服务不支持用本地的数据库客户端直接连接,所有的数据管理操作都必须在Web界面上完成。

schema

大家注意到上图有个名叫‘REUNOBRW'的schema,它和上上图里的环境变量中的数据库用户名是一致的。之所以要手工创建‘REUNOBRW' schema的原因在于我们要移植的Blogs早期版本(1.0.2)里,所有的数据库访问代码是仅有table名,不带schema名的,如下:

<select id="getWebsiteById" parameterClass="string" resultMap="Website" >
      SELECT * FROM website WHERE id = #value#
</select>

在Bluemix环境中,这样的调用会自动在前面以用户名作为schema名。除非我们修改全部的SQL把它们都带上schema名,否则只能创建一个和用户名同名的schema。这个做法虽然可以工作,但一旦数据库服务重建schema也必须要重建,作为测试可以但作为生产环境的开发这样是不可取的。创建好schema后,可以创建table等对象了,点击Run DDL来指行数据库命令(注意不是所有的SQL都支持,比如我们无法创建table space)。下面是在创建好tables、运行应用后查询用户表的例子 -

run-sql现在可以把Connections Blogs build成一个.war文件,通过cf push到Bluemix上,它可以正确的连接数据库,但界面上有很多错误,绝大部分是因为已有代码是在WebSphere Application Server下测试运行的,在Liberty环境下存在问题。另外也没有办法登录,因为我们还没有定义用户库。下篇文章我们将继续讨论应用的迁移。

Read more

上手Bluemix (1)

之前写了个短文,说把IBM Connections Blogs移植到了Bluemix上。今天花点时间,写写具体的过程。对于不熟悉Bluemix的人,我一句话解释一下:Bluemix是IBM的PaaS平台,供开发者构建在云上跑的应用,而Bluemix本身是架在SoftLayer (IBM IaaS)之上的。

先来看看Bluemix长啥样。对于首次访问的用户,基本上唯一有用的界面就是Catalog了,里面列举了Bluemix提供的runtime、service和各种组件,大家的第一个应用就从这里开始。

catalog

IBM Connections是基于Java的Web应用,所以我们选择的Runtime是Liberty for Java(一个约20MB左右的Java Web容器)。输入基本参数,一个缺省的Webapp和一个WebSphere Liberty Profile的instance就产生了。这个webapp的首页是显示当前应用的一些运行时参数。

liberty当创建好第一个应用后,Dashboard就成了常用的视图了,应用的启/停、监控、服务的创建和绑定都要在这个界面上完成。

quickstart

对于新手来说,'View Quick Start'按钮很有帮助,它指导开发者之后需要做的事情,包括安装cf工具来做应用上载,以及如何登陆和访问。我一开始就是按照这个步骤在写了Hello World之后把应用打包后用cf push到Bluemix上的。

有了第一个可以运行的demo webapp后,可以从Dashboard中把这个应用打包下载到本地,展开后用Eclipse在本地继续开发,完成后重新打包上传。多尝试几次后,就可以熟练的更新、上传应用了。需要注意的是,应用更新的最小粒度是.war(至少今天我都没有找到文件级别的更新),也就是说即使要改一个JSP文件也需要完整打包后上传。我试图找到类似SSH的方式允许远程文件上传,但最终没有找到。开发者能够在文件级别范围的访问仅限于在'Files and Logs'菜单下浏览文件目录并下载里面的文件,如log等。

filesystem

最后谈谈本地开发和测试。开发者可以使用Eclipse加上WebSphere Liberty Profile runtime集成到Eclipse中,具体下载地址 - https://developer.ibm.com/wasdev/downloads/liberty-profile-using-eclipse/。搞定后的开发环境大概是这样的(截图里面的Bluemix插件,下次再介绍)。

eclipse

好了,到此为止一个简单的Java webapp已经可以欢快的跑在Bluemix之上,我们也初步了解了开发者该如何更新和部署这个应用了。接下来,谈谈如何创建一个数据库服务并绑定应用,待续。

Read more