추석맞이 이직 1년 결산

9월 1일, 이직을 했다. 나는 덕택에 풀타임 오픈 소서 및 수석 소프트웨어 엔지니어라는 명칭을 얻었다. 전보다 훨씬 많은 급여를 받게 되었다. 거기다가 마치 ‘노동의 미래‘나 ‘세계는 평평하다‘를 그대로 옮겨 놓은 듯한 재택 근무 작업 환경도 차렸다.

무엇을 사더라도 예전보다도 ‘더 멋진’ 물건을 찾기 시작했다. 더 많은 음반이 아마존을 통해 내 콜렉션에 추가되었다. 더 좋은 의료 서비스를 받을 수 있게 되었고, 그 결과 전반적인 건강상의 향상도 얻었다. 이 정도라면 거의 모든 면에서 내 삶의 질은 진보했다, 라고 평할 수 있을까?

다소 설명하기 어렵지만, 그에 비해 내면의 질은 많은 향상을 필요로 하고 있다. 나는 전보다 더 바빠졌다. 수영왕 펠프스가 연습시간에는 수영을 하고, 수영 녹화 영상을 보는 것으로 여가를 보낸다면, 프로그래밍왕도 아닌 나는 시간대를 허물어가며 업무 시간에는 프로그래밍을 하고, 프로그래밍으로 여가를 보내고 있다.

아마도 그것은 단순히 새 직장 때문은 아닐 것이다. 그저 나의 프로그래밍에 대한 순수한, 아니 본능에 가까운 욕구가 새 직장이라는 침실을 차지하게 되면서 생긴 예측된 수순에 다름없다. 이러한 실락원에서나 볼법한 뜨거운 만남은 나를 자발적 과로로 인한 커리어 파멸로 이끌 것인가, 아니면 다음 단계로의 도약으로 이끌 것인가.

정답이 없는 것이 당연하다고 할 수 있겠지만, 그래도 어떻게 될 지 내 스스로도 모르겠다. 나는 전보다 교양서나 소설을 더 적게 읽는다. 뿐만 아니라 기술서도 더 적게 읽는다. 이 상황의 심각성은 내가 ‘알만큼 알아서‘와는 거리가 먼 상황에 놓여 있기 때문에 더욱 두드러진다. 한 때 날렸던 누군가가 새로 들어온 똑똑한 친구에게 험담이나 당하는 꼴을 상상하게 되는 것은 한 때 그 ‘똑똑한 친구‘에 해당했던 나로서는 당연한 일이 아닐 수 없다.

나는 어디까지 갈 수 있고, 또 어디까지 가야 하는가. 그것은 다르게 말한다면 ‘내가 가장 유능할 수 있는 위치는 어디까지이고, 내가 가장 무능할 수 있는 위치는 어디부터인가‘일 것이다. 그리고 그 위치는 지금으로서는 뭐랄까, 다소 무섭게 다가온다.

그렇기에 오늘도, 조금은 힘들지만 변화를 꿈꾼다.

한국 미나 사용자께 드리는 네티로의 전환 권고

미나 프로젝트에서 손을 뗀 후, 지난 약 3개월간 네티 프로젝트 에 상당한 시간을 할애하였고, 그 결과 현재 API 와 구현 모두 안정화 단계에 접어들었습니다.

적절한 문서화, 공개 가능한 성능 테스트 보고서 작성 및 웹 사이트 컨텐츠 보강을 마치는 대로 최초의 안정 버전이 릴리즈될 예정입니다. 지금까지 적어도 2 주에 한 번씩은 릴리즈를 해 왔고, 앞으로 2 주 내로 릴리즈 계획이 되어 있으므로, 미나처럼 불필요하게 프로젝트의 릴리즈가 늦어지는 일은 없을 것으로 예상합니다.

네티 안정 버전의 릴리즈와 동시에 미나 프로젝트에 대한 한국어 지원에 할애하는 시간을 많이 줄일 예정이니, 양해 부탁드리겠습니다. (상황에 따라 답장을 못 보내 드릴 수도 있습니다.) 따라서 모든 미나 관련 문의는 공식 커뮤니티를 통해 해 주시면 감사하겠습니다.

네티 프로젝트는 제가 직접 운영하는 한국어 사용자 그룹을 갖고 있습니다. 따라서 네티 프로젝트에 대한 모든 문의 사항 및 피드 백은 네티 한국어 사용자 그룹 을 통해 해 주시면 성심 성의껏 회신하도록 하겠습니다.

만약 진행중이신 프로젝트가 초기 단계에 있다면 네티로의 전환을 심각하게 고려하시기를 권합니다. 부가 기능 (예: JMX) 면에서 부족함이 있으나, 스루풋, 레이턴시, 유연성, 안정성 등 대부분의 면에서 비교할 수 없는 경험이 되리라 확신합니다.

Netty reaches 75%+ test coverage. What's next?

Netty 3.0.0.CR3 was my first serious attempt to achieve high test coverage by writing many test cases in my life, and it was very exciting for me. I was able to fix more than a dozen bugs with them.

EclEmma was very helpful to review all the source code and to achieve high test coverage. The byte code instrumented by Emma ran much faster than other open source test coverage tools, yet providing reliable numbers.

Granted that both the API and implementation of Netty became stable enough, I’m going to focus on writing JavaDoc and user guide for the next week. The first GA is expected in a couple weeks – please stay tuned!

As always, your feed back is more than appreciated.

Netty moves its nest from GoogleCode to JBoss.org with its first release candidate

Finally, I have managed to move Netty to JBoss.org and announce the first release candidate. If you were using Google Groups, you need to subscribe again.

Here’s the new URL:

and here are the related announcement messages:

For those who don’t know what Netty is yet — The Netty project is an effort to provide an asynchronous · event-driven network application framework for rapid development of maintainable high-performance · high-scalability protocol servers and clients, including its related out-of-the-box protocol extensions and tool suite. To somewhat oversimplify, it’s a framework that allows you to write a NIO client and server very easily.

Using GNOME Evolution with a huge IMAP mailbox

Update: The latest development snapshot of Opera 9.5 has full support for virtual folder with powerful filter which supports a regular expression. Let’s give it a try! Adding GPG signing and better multiple identity support would be an icing on the cake.

I’ve been struggling with various mail clients to deal with my huge IMAP mailbox which contains about 200k messages. I didn’t want to split it into more than one. It’s not my job but an e-mail client’s job; see what GMail does!

Although Opera Mail does a great job on dealing with such a big IMAP mailbox, it lacks a couple critical features which make me have a difficulty in organizing my messages. I have missed Evolution virtual folders especially.

However, all other GUI/Web-based e-mail clients than Opera Mail are very poor at dealing with a huge IMAP mailbox. It seems like IMAP support is not a high priority task for most e-mail clients in the world. The latest alpha version of RoundCube seems to be very efficient but it lacks too many features at this moment.

Before I give up again and stick to Opera Mail, I decided to give a try to various IMAP-to-Maildir synchronization tools – isync and OfflineIMAP. Long time agao, I had a bad experience with OfflineIMAP and it seems like it doesn’t work with my account yet. By contrast, isync was indeed a great tool which does its job without any problem.

One problem with isync is that its configuration file format is somewhat undocumented, so I’d like to share my settings:

# ~/.mbsyncrc
MaildirStore Local
Path ~/.maildir
Inbox ~/.maildir

IMAPStore Remote
Host <hostname>
User <username>
Pass <password>
CertificateFile ~/.mbsync.crt

Channel Local-Remote
Master :Remote:
Slave :Local:
Create Slave
Sync All

You also need to set up your crontab to run mbsync periodically because Evolution doesn’t provide an option to execute a command before refreshing the local Maildir. Here’s my crontab:

*/5 * * * * pgrep -f "^(/usr/bin/)?evolution" > /dev/null && mbsync -a -q

Please note that I used pgrep to make sure mbsync runs only when Evolution is running.

Finally, I succeeded to make Evolution run pretty fast with my IMAP mailbox, although it’s a kind of workaround. However, I don’t think this is a ugly hack. Considering that Opera Mail stores my all messages in its local storage, what Evolution does for the local Maildir is very similar to what Opera Mail does. It creates a full text index for all messages and maintains the index for every message operation. What’s missing is immediate mailbox synchronization based on IMAP notification, and it shouldn’t be difficult to be integrated into Evolution codebase IMHO.

PS: I had a difficulty searching for isync because Apple has a product with the same name. The actual executable of isync is mbsync, so try this google search.

Filling the gap between blocking I/O and NIO

A non-blocking NIO Channel and a blocking InputStream have inevitable impedance mismatch. Because an InputStream is supposed to block for every read operation unless there’s some data available in the buffer, any InputStream-based decoder implementation can’t be used with a non-blocking NIO application right away.

A common workaround is to prepend a length field for each message so you can wait until you read a whole message before calling InputStream.read(). However, this turns your NIO application incompatible with a legacy blocking I/O application because the legacy application doesn’t prepend a length field at all. You might have managed to modify the legacy application to prepend a length field, but we know it’s not always the case. We need something to fill this gap between two I/O paradigms.

An ObjectInput/OutputStream-based blocking I/O network applications are the most common case because it was considered to be the easiest quick-and-dirty solution for intranet Java object exchange. It’s as simple as wrapping an InputStream of a Socket with an ObjectInputStream (i.e. in = new ObjectInputStream(socket.getInputStream());).

How can we implement a NIO network application to be interoperable with those legacy applications without any modification? It was considered to be impossible… until today!

I’ve just released a new milestone of Netty which addresses the issue I described above. It provides CompatibleObjectEncoder and CompatibleObjectDecoder, which retains interoperability with the legacy ObjectInput/OutputStream-based socket applications.

You will also find you can do the same for any kind of InputStream implementations with Netty’s ReplayingDecoder with fairly small amount of effort, which means you can shift the paradigm of your complicated blocking protocol client/server to more scalable non-blocking paradigm while retaining most legacy code.

The excitement of ReplayingDecoder doesn’t stop here. It also allows you to implement a non-blocking decoder in a blocking paradigm. In a non-blocking paradigm, you always had to check if there’s enough data in the buffer, like the following:

public boolean decode(ByteBuffer in) {
  if (in.remaining() < 4) {
    return false;
  }

  // Read the length header.
  int position = in.position();
  int length = in.getInt();
  if (in.remaining() < length) {
    in.position(position);
    return false;
  }

  // Read the body.
  byte[] data = new byte[length];
  in.get(data);
  ...
  return true;
}

With ReplayingDecoder, you don’t need to check the availability of the input buffer at all:

public void decode(ByteBuffer in) {
  // Read the length header.
  int length = in.getInt();

  // Read the body.
  byte[] data = new byte[length];
  in.get(data);
  ...
}

How could this work? ReplayingDecoder uses a sort of continuation technique. It rewinds the buffer position to the beginning when there’s not enough data in the buffer automatically and calls decode() again (i.e. replays the decode) when more data is received from a remote peer.

You might think this is pretty inefficient, but it turned out to be very efficient in most cases. Higher throughput means lower chance of replay because we will receive more than one message for a single input buffer (often dozens). Consequently, most messages will be decoded in one shot without a replay. In case of slow connection, it will be less than optimal but you won’t see much difference because it’s already slow because the connection itself is slow. Just compare the code complexity of the two paradigms. I’d definitely go for the latter.

A problem with recent RSS feeds in gleamynode.net

Brett Porter kindly pointed me out that there’s some problem with the RSS feed of gleamynode.net. I have updated FeedBurner information along with the meta tags to make sure the same problem doesn’t occur again. Also, please make sure that you are using the FeedBurner RSS feed if you are subscribed to this blog. Apologies.

Brett Porter가 친절하게도 gleamynode.net의 RSS 피드에 문제가 있다는 사실을 지적해 주었습니다. 메타 태그와 피드버너 정보를 수정해서 같은 문제가 다시 일어나지 않도록 조치했습니다. 또, 만약 이 블로그를 구독중이시라면 피드버너 RSS 주소를 사용하고 있는지 확인해 주시길 바랍니다. 불편을 끼쳐 드려 죄송합니다.

네티 한국어 사용자 그룹을 개설했습니다.

원문은 여기

미나 프로젝트를 떠난 지도 상당한 시간이 흘렀습니다. 네티 프로젝트 를 시작하고 처음부터 모든 것을 다시 작성하느라 바쁘게 보낸 몇 달 간이었습니다. 이제 버그 리포트도 들어 오고 커뮤니티가 형성되어 가는 과정이어서 흐뭇합니다.

그동안 국문으로 여러분들께 미나에 대한 지원을 하고 싶었지만 재단 정책을 포함한 여러 가지 이유로 하지 못해 아쉬운 점이 많았는데요. 네티에서는 별도로 네티 한국어 사용자 그룹 을 운영하여 더 많은 분들의 편의를 도모해 보기로 하였습니다. 네티 프로젝트가 아직 초기 단계이니만큼 많은 질문과 논의점, 버그가 있으리라고 생각됩니다. 언제든지 자유롭게 이 그룹을 통해 메시지를 남겨 주시면 감사하겠습니다.

단, 공식 영문 네티 사용자 그룹 및 이슈 트래커는 전 세계인이 함께 보는 자리이므로 항상 영문을 사용하셔야 한다는 점 양지해 주시길 부탁드리겠습니다.

그럼 앞으로 많은 메시지가 이 그룹에서 오고 가기를 기원해 봅니다.

Using Fail2Ban to refuse brute-force attacks

I have been using a quick and dirty shell script to update /etc/hosts.deny file when brute-force attack flows into my server. It was pretty effective but was not effective enough to block the break-in attempts immediately. Today, I found a better solution – Fail2Ban. It scans the system log files and bans brute-force attacks for a certain period.

Most examples use iptables, but I always prefer /etc/hosts.deny and I don’t even care about unbanning once a host is banned. Therefore, I added the following to /etc/fail2ban/jail.conf:

[ssh-hostsdeny]
enabled  = true
filter   = sshd
action   = hostsdeny-nounban
           mail-whois[name=SSH, [email protected]]
logpath  = /var/log/messages

[ssh-ddos-hostsdeny]
enabled  = true
filter   = sshd-ddos
action   = hostsdeny-nounban
           mail-whois[name=SSH-DDoS, [email protected]]
logpath  = /var/log/messages

Please note that I defined a new action called hostsdeny-nounban, which doesn’t unban the attacker’s IP address once banned It’s /etc/fail2ban/action.d/hostsdeny-nounban.conf:

[Definition]
actionstart =
actionstop =
actioncheck =
actionban = IP=<ip> && grep -q "ALL: $IP" <file> || echo "ALL: $IP" >> <file>
actionunban =

[Init]
file = /etc/hosts.deny

For more information, I’d recommend you to read the Gentoo HOWTO fail2ban.