Jill Bolte Taylor: Stroke Of Insight (Transcript)

Jill Bolte Taylor had an opportunity few brain scientists would wish for: One morning, she realized she was having a massive stroke. As it happened -- as she felt her brain functions slip away one by one, speech, movement, understanding -- she studied and remembered every moment. This is a powerful story of recovery and awareness -- of how our brains define us and connect us to the world and to one another. (Recorded February 2008 in Monterey, California. Duration: 18:44.)
See source | blog | video | 中文 (翻译).

Stroke Of Insight - Jill Bolte Taylor - TRANSCRIPT


I grew up to study the brain because I have a brother who has been diagnosed with a brain disorder, schizophrenia. And as a sister and as a scientist, I wanted to understand, why is it that I can take my dreams, I can connect them to my reality, and I can make my dreams come true -- what is it about my brother's brain and his schizophrenia that he cannot connect his dreams to a common, shared reality, so they instead become delusions?

So I dedicated my career to research into the severe mental illnesses. And I moved from my home state of Indiana to Boston where I was working in the lab of Dr. Francine Benes, in the Harvard Department of Psychiatry. And in the lab, we were asking the question, What are the biological differences between the brains of individuals who would be diagnosed as normal control, as compared to the brains of individuals diagnosed with schizophrenia, schizoaffective, or bipolar disorder?

So we were essentially mapping the microcircuitry of the brain, which cells are communicating with which cells, with which chemicals, and then with what quantities of those chemicals. So there was a lot of meaning in my life because I was performing this kind of research during the day. But then in the evenings and on the weekends I traveled as an advocate for NAMI, the National Alliance on Mental Illness.

But on the morning of December 10 1996 I woke up to discover that I had a brain disorder of my own. A blood vessel exploded in the left half of my brain. And in the course of four hours I watched my brain completely deteriorate in its ability to process all information. On the morning of the hemorrhage I could not walk, talk, read, write or recall any of my life. I essentially became an infant in a woman's body.

If you've ever seen a human brain, it's obvious that the two hemispheres are completely separate from one another. And I have brought for you a real human brain. [Thanks.] So, this is a real human brain. This is the front of the brain, the back of the brain with a spinal cord hanging down, and this is how it would be positioned inside of my head. And when you look at the brain, it's obvious that the two cerebral cortices are completely separate from one another. For those of you who understand computers, our right hemisphere functions like a parallel processor. While our left hemisphere functions like a serial processor. The two hemispheres do communicate with one another through the corpus collosum, which is made up of some 300 million axonal fibers. But other than that, the two hemispheres are completely separate. Because they process information differently, each hemisphere thinks about different things, they care about different things, and dare I say, they have very different personalities. [Excuse me. Thank you. It's been a joy.]

Our right hemisphere is all about this present moment. It's all about right here right now. Our right hemisphere, it thinks in pictures and it learns kinesthetically through the movement of our bodies. Information in the form of energy streams in simultaneously through all of our sensory systems. And then it explodes into this enormous collage of what this present moment looks like. What this present moment smells like and tastes like, what it feels like and what it sounds like. I am an energy being connected to the energy all around me through the consciousness of my right hemisphere. We are energy beings connected to one another through the consciousness of our right hemispheres as one human family. And right here, right now, all we are brothers and sisters on this planet, here to make the world a better place. And in this moment we are perfect. We are whole. And we are beautiful.

My left hemisphere is a very different place. Our left hemisphere thinks linearly and methodically. Our left hemisphere is all about the past, and it's all about the future. Our left hemisphere is designed to take that enormous collage of the present moment. And start picking details and more details and more details about those details. It then categorizes and organizes all that information. Associates it with everything in the past we've ever learned and projects into the future all of our possibilities. And our left hemisphere thinks in language. It's that ongoing brain chatter that connects me and my internal world to my external world. It's that little voice that says to me, "Hey, you gotta remember to pick up bananas on your way home, and eat 'em in the morning." It's that calculating intelligence that reminds me when I have to do my laundry. But perhaps most important, it's that little voice that says to me, "I am. I am." And as soon as my left hemisphere says to me "I am," I become separate. I become a single solid individual separate from the energy flow around me and separate from you.

And this was the portion of my brain that I lost on the morning of my stroke.

On the morning of the stroke, I woke up to a pounding pain behind my left eye. And it was the kind of pain, caustic pain, that you get when you bite into ice cream. And it just gripped me and then it released me. Then it just gripped me and then released me. And it was very unusual for me to experience any kind of pain, so I thought OK, I'll just start my normal routine. So I got up and I jumped onto my cardio glider, which is a full-body exercise machine. And I'm jamming away on this thing, and I'm realizing that my hands looked like primitive claws grasping onto the bar. I thought "that's very peculiar" and I looked down at my body and I thought, "whoa, I'm a weird-looking thing." And it was as though my consciousness had shifted away from my normal perception of reality, where I'm the person on the machine having the experience, to some esoteric space where I'm witnessing myself having this experience.

And it was all every peculiar and my headache was just getting worse, so I get off the machine, and I'm walking across my living room floor, and I realize that everything inside of my body has slowed way down. And every step is very rigid and very deliberate. There's no fluidity to my pace, and there's this constriction in my area of perceptions so I'm just focused on internal systems. And I'm standing in my bathroom getting ready to step into the shower and I could actually hear the dialog inside of my body. I heard a little voice saying, "OK, you muscles, you gotta contract, you muscles you relax."

And I lost my balance and I'm propped up against the wall. And I look down at my arm and I realize that I can no longer define the boundaries of my body. I can't define where I begin and where I end. Because the atoms and the molecules of my arm blended with the atoms and molecules of the wall. And all I could detect was this energy. Energy. And I'm asking myself, "What is wrong with me, what is going on?" And in that moment, my brain chatter, my left hemisphere brain chatter went totally silent. Just like someone took a remote control and pushed the mute button and -- total silence.

And at first I was shocked to find myself inside of a silent mind. But then I was immediately captivated by the magnificence of energy around me. And because I could no longer identify the boundaries of my body, I felt enormous and expansive. I felt at one with all the energy that was, and it was beautiful there.

Then all of a sudden my left hemisphere comes back online and it says to me, "Hey! we got a problem, we got a problem, we gotta get some help." So it's like, OK, OK, I got a problem, but then I immediately drifted right back out into the consciousness, and I affectionately referred to this space as La La Land. But it was beautiful there. Imagine what it would be like to be totally disconnected from your brain chatter that connects you to the external world. So here I am in this space and any stress related to my, to my job, it was gone. And I felt lighter in my body. And imagine all of the relationships in the external world and the many stressors related to any of those, they were gone. I felt a sense of peacefulness. And imagine what it would feel like to lose 37 years of emotional baggage! I felt euphoria. Euphoria was beautiful -- and then my left hemisphere comes online and it says "Hey! you've got to pay attention, we've got to get help," and I'm thinking, "I got to get help, I gotta focus." So I get out of the shower and I mechanically dress and I'm walking around my apartment, and I'm thinking, "I gotta get to work, I gotta get to work, can I drive? can I drive?"

And in that moment my right arm went totally paralyzed by my side. And I realized, "Oh my gosh! I'm having a stroke! I'm having a stroke!" And the next thing my brain says to me is, "Wow! This is so cool. This is so cool. How many brain scientists have the opportunity to study their own brain from the inside out?"

And then it crosses my mind: "But I'm a very busy woman. I don't have time for a stroke!" So I'm like, "OK, I can't stop the stroke from happening so I'll do this for a week or two, and then I'll get back to my routine, OK."

So I gotta call help, I gotta call work. I couldn't remember the number at work, so I remembered, in my office I had a business card with my number on it. So I go in my business room, I pull out a 3-inch stack of business cards. And I'm looking at the card on top, and even though I could see clearly in my mind's eye what my business card looked like, I couldn't tell if this was my card or not, because all I could see were pixels. And the pixels of the words blended with the pixels of the background and the pixels of the symbols, and I just couldn't tell. And I would wait for what I call a wave of clarity. And in that moment, I would be able to reattach to normal reality and I could tell, that's not the card, that's not the card, that's not the card. It took me 45 minutes to get one inch down inside of that stack of cards.

In the meantime, for 45 minutes the hemorrhage is getting bigger in my left hemisphere. I do not understand numbers, I do not understand the telephone, but it's the only plan I have. So I take the phone pad and I put it right here, I'd take the business card, I'd put it right here, and I'm matching the shape of the squiggles on the card to the shape of the squiggles on the phone pad. But then I would drift back out into La La Land, and not remember when I come back if I'd already dialed those numbers.

So I had to wield my paralyzed arm like a stump, and cover the numbers as I went along and pushed them, so that as I would come back to normal reality I'd be able to tell, yes, I've already dialed that number. Eventually the whole number gets dialed, and I'm listening to the phone, and my colleague picks up the phone and he says to me, "Whoo woo wooo woo woo." [laughter] And I think to myself, "Oh my gosh, he sounds like a golden retriever!" And so I say to him, clear in my mind I say to him. "This is Jill! I need help!" And what comes out of my voice is, "Whoo woo wooo woo woo." I'm thinking, "Oh my gosh, I sound like a golden retriever." So I couldn't know, I didn't know that I couldn't speak or understand language until I tried.


So he recognizes that I need help, and he gets me help. And a little while later, I am riding in an ambulance from one hospital across Boston to Mass General Hospital. And I curl up into a little fetal ball. And just like a balloon with the last bit of air just, just right out of the balloon I felt my energy lift and I felt my spirit surrender. And in that moment I knew that I was no longer the choreographer of my life. And either the doctors rescue my body and give me a second chance at life or this was perhaps my moment of transition.


When I awoke later that afternoon I was shocked to discover that I was still alive. When I felt my spirit surrender, I said goodbye to my life, and my mind is now suspended between two very opposite planes of reality. Stimulation coming in through my sensory systems felt like pure pain. Light burned my brain like wildfire and sounds were so loud and chaotic that I could not pick a voice out from the background noise and I just wanted to escape. Because I could not identify the position of my body in space, I felt enormous and expensive, like a genie just liberated from her bottle. And my spirit soared free like a great whale gliding through the sea of silent euphoria. Harmonic. I remember thinking there's no way I would ever be able to squeeze the enormousness of myself back inside this tiny little body.

But I realized "But I'm still alive! I'm still alive and I have found Nirvana. And if I have found Nirvana and I'm still alive, then everyone who is alive can find Nirvana." I picture a world filled with beautiful, peaceful, compassionate, loving people who knew that they could come to this space at any time. And that they could purposely choose to step to the right of their left hemispheres and find this peace. And then I realized what a tremendous gift this experience could be, what a stroke of insight this could be to how we live our lives. And it motivated my to recover.

Two and a half weeks after the hemorrhage, the surgeons went in and they removed a blood clot the size of a golf ball that was pushing on my language centers. Here I am with my mama, who's a true angel in my life. It took me eight years to completely recover.


So who are we? We are the life force power of the universe, with manual dexterity and two cognitive minds. And we have the power to choose, moment by moment, who and how we want to be in the world. Right here right now, I can step into the consciousness of my right hemisphere where we are -- I am -- the life force power of the universe, and the life force power of the 50 trillion beautiful molecular geniuses that make up my form. At one with all that is. Or I can choose to step into the consciousness of my left hemisphere. where I become a single individual, a solid, separate from the flow, separate from you. I am Dr. Jill Bolte Taylor, intellectual, neuroanatomist. These are the "we" inside of me.

Which would you choose? Which do you choose? And when? I believe that the more time we spend choosing to run the deep inner peace circuitry of our right hemispheres, the more peace we will project into the world and the more peaceful our planet will be. And I thought that was an idea worth spreading.

Summer Comulus

Local Restaurants

NamePhoneAddress
Bamboo Garden (四季飘香)(425) 688-7991202 106th Pl NE, Bellevue, WA 98004-5723
Chengdu Chinese Buffet (蓉园)(425) 451-838914625 NE 24th St., Bellevue, WA 98007
Chiang's Gourmet (敘香園)(206) 527-88887845 Lake City Way NE Seattle, WA 98125
China Gate (龙门酒家)(206) 624-1730516 7th Ave. South,Seattle WA 98104
China Harbor (海景樓)(206) 286-16882040 Westlake Avenue N. Seattle, WA 98109
House of Hong (康樂酒家)(206) 622-7997409 8th Avenue S. Seattle, WA 98104
Jade Garden (翠园)(206) 622-8181424 7th Ave S, Seattle, WA 98104
Jeem Asian (醉翁楼)(425) 883-885814850 Ne 24th St Redmond, WA 98052
Kung-Ho Gourmet (更好小館)(425) 643-22683640 128th Avenue SE Bellevue, WA 98006
Maple Garden (枫林)(425) 644-903814725 NE 20th St, Bellevue, WA
Ming's Chinese Seafood (明苑)(425) 378-800913200 NE 20th St, Bellevue, WA
New Kowloon Seafood (新九龍)(206) 223-7999900 S. Jackson St. #203 Seattle, WA 98104
New Star (新星酒家)(206) 622-8801516 S. Jackson St. Seattle, WA 98104
Noble Court (豪苑)(425) 641-60111644 140th Ave NE Bellevue, WA 98005
Regent Bakery & Café (丽晶)(425) 378-149815159 NE 24th St, Redmond, WA 98052
Shanghai Garden (滬江春)(206) 625-1688524 6th Avenue S. Seattle, WA 98104
Sichuanese Cuisine (老四川)(425) 562-155215005 NE 24th St, Redmond, WA‎ 98052
Spiced (过桥园)(425) 644-88881299 156th Ave NE Suite #135, Bellevue, WA 98007
Szechuan Chef (川霸王)(425) 746-900815015 Main St. Ste 107 Bellevue WA 98007
Szechuan Express (小四川)(425) 746-47642245 140th Ave NE, Bellevue, WA 98005
Top Gun Seafood (半島海鮮酒家)(425) 641-338612450 SE 38th St. Bellevue, WA 98006
V-Garden (顺利得)(206) 622-2686310 4th Ave S, Seattle, WA

Drama, Life, and Review

  美国电影断档了,最近看的最多的就是大陆的电视连续剧。过去中国的影视剧一直是个弱项,难得见到和大国相符的水平。随着岁月的增长,反倒是儿时看多了的《红色娘子军》、《小兵张嘎》、《永不消逝的电波》、《党的女儿》、《红日》、《林海雪原》、《英雄儿女》、等等变成了经典。近些年国产电影仍然是胡编烂造的多,钱花的越多越丢人现眼。除了个别几部,象《云水谣》和每年的贺岁片,大多数的质量总是徘徊在二三流的水准。
  也不知是传统文化的原因还是市场的问题,和电影相比,中国的电视剧一直在稳步发展。即使不算早期港台剧的精品,各内陆省市和央视的作品都是越拍越好,似乎编剧和导演的素质都有飞跃,技术手法越来越成熟。之所以强调编剧和导演,主要是发现大多数电视剧的故事比电影曲折精彩,比早期的内容严谨多了;另外,一个好剧能捧红一个演员,而同样的一个演员在其他剧中的表现却差之千里,这应该是导演的功力。
  和欧美的电影相比,有一个奇怪的现象:许多经过名著改编的美国电影,即使不是奥斯卡的水平,也不会太一般;可国产影剧,越是名著改编的影视,质量越差。象一些历史剧和武侠剧,都有一种倒胃口的感觉。或者编剧自作主张画蛇添足,或者导演和演员象没文化根本就没读过原作似的,不仅没表现原著的精髓,还加了不少糟粕,生生毁了许多经典的素材。这些往往都是戏外的功夫和市场炒作太多,象靠名气和绯闻挣票房的结果。
  其实只要踏踏实实把心思放在本职上,以国人的聪慧,辅以国内外同行的借鉴,低成本、没有明星的片子一样可以拍的很好。国产电视和欧美相比还是有长处的。比如时下美国的电视更趋向快餐经营,除了“脱口兽”、“真人兽”、“生活兽”这种纯粹绑定收视率的怪胎,电视剧几乎完全以系列剧为主,这样各集独立,既缩短编排周期,又减少成本和风险。未来的大陆影视可千万不要走这个方向。看看《二十四小时》就知道,一个好的故事还是有市场的。
  除了故事的完整连续,大陆影视的另一个突出看点就是对爱情的描写。欧美的电影在这方面是先驱,也是大碗,可在电视剧方面却显得很粗糙。过去大陆由于政治因素,爱情故事写的很勉强,脱离生活实际,对白老套无味虚假。后来应该是受到香港电视剧的影响,加之近年网络文学的累积,新近的许多连续剧非常贴近生活或者设定的历史背景,让人身临其境、追看至终。特别是在情感营造上的细腻委婉,非中国文化莫属。
  例如,象《死亡日记-沉默的证人》这样一部以刑侦探案和心理变态杀人狂为主线的警匪片,其中不着斧痕顺其自然地插入了一些情感故事,竟然能在冷冰冰的解剖室外看到那种青涩凄美的初恋描写,其细微婉转之处恰好和警察制服的干练严肃形成强烈对比。再如《士兵突击》中纯粹兄弟情谊,《天道》和《半路夫妻》中看似不协调的感情纠葛,都是源于生活,又有艺术加工。更不用说象《我们的八十年代》《不能没有她》这种纯爱情的故事,其复杂曲折更胜以往的台湾言情小说。
  由于中国历史的重大变迁,尤其是近代人文在每个十字路口的快速转向,给每一个时代都留下了深刻的烙印。这种变化以现在的视角来看,真有恍如隔世的感叹。如《一年又一年》用时间的节奏和社会现象循序推进家庭生活中的大事。其他一些类似家族史跨越从清末至近代传奇的电视剧也很流行。还有一些故事,虽然设定在特殊的年代,但在剧情描写上却只专注于一个特定的层面,而把其他内容如时代背景、政治特色故意模糊。比如《王贵和安娜》的故事虽然开始于文革后期,但已经不再局限于当时的背景,甚至完全可以作为一部婚姻指南。
  国产的生活电视剧从《渴望》开始就已经很有特色了。喜剧不够多,但质量还可以,当然众多期盼的《地下交通站》不能续播是非常遗憾的。其他新的成就主要表现在警匪片(如海岩的系列作品)、谍战片(《潜伏》)、和军旅片(《士兵突击》)。这些优秀的电视剧都来源于好的文学作品,连历史剧也是这样───大家看腻了“辫子戏”,可是以二月河的小说为蓝本的《雍正王朝》就显示出极高的水平。这可能是商业化的戏剧创作仍然没有入门。
  顺带检讨一个题外话:国内的互联网发展势头是很猛的了,可对资料的组织实在是太差了。这可能就是长久以来一直诟病的软件水平远远落后于硬件发展的问题。目前美国无论象“亚马逊”这样的商业网站,电影数据库,还是图书馆,都可以快速简捷地查到一个影剧的详细资料。可国内的电视剧,很多时候连哪一年首播或出品的信息都找不到,这还是受千万人追捧的剧目。这方面国家没人管,也没有商业文化公司愿意长期投资这样的项目。
  总之,中国影视仍然缺乏气势磅礴的鸿篇巨制,文化和历史的底蕴不足。这和中国几千年的现成遗产是相背的,令人不可思议。细致严谨不足还可以是受经验、资金等的影响,总是可以学习的。但自家的东西不懂,就不是抄袭外国的“大片”手法或用什么电脑制作可以弥补的了。只希望随着经济发展和生活富裕,影视产品能有更长足的底气和自我建设,焕发出本源和新曲。

  这里顺手罗列一些佳作,以备存念:

  《我的兄弟叫顺溜
  《死亡日记──沉默的证人
  《天道》(2009.02.03)、《墓道》
  《潜伏
  《我的青春谁做主》、《奋斗》
  《我们的八十年代》、《王贵与安娜》、《北風那個吹》
  《不能没有她》、《一言為定》
  《纸醉金迷》(2008.11.22)
  《地下交通站》、《我爱我家》(1993-1994)、《编辑部的故事》
  《大染坊》(2009.09.26)、《闯关东》、《大宅门》
  《士兵突击》(2006.01.01)
  《雍正王朝》、《雍正皇帝》
  《便衣警察》、《一场风花雪夜的事》、《永不瞑目》、《玉观音》
  《一年又一年》、《过把瘾》
  《大时代》、《流氓大亨》、《人在边缘》、《我本善良》……
  《含羞草》、《星星知我心》

CCNet vs. CruiseControl

Set once and let it go, this is how Continuous Integration works for automated build process along with development cycle. For a large number of projects, the maintenance for such continously integrated build environment could be complicate and cumbersome. This article will provide and discuss sample config files from practice experience to make easy using CruiseControl.NET and CruiseControl with subversion repository. For comparison of all other similar products, see ThoughtWorks CI Feature Matrix.

Contents


CruiseControl.NET

CCNET (CruiseControl.NET) starts with ccnet.config:

<!--ccnet.config-->
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE cruisecontrol SYSTEM "file:ccnet_definitions.dtd">

<cruisecontrol xmlns:cb="urn:ccnet.config.builder">
<cb:include href="ccnet_definitions.xml" xmlns:cb="urn:ccnet.config.builder"/>

<queue name="$(computername)" duplicates="ApplyForceBuildsReAdd" />
<cb:include href="ccnet_Project.Standard_Solution.xml" xmlns:cb="urn:ccnet.config.builder"/>

<cb:include href="ccnet_MyProject.xml" xmlns:cb="urn:ccnet.config.builder"/>
</cruisecontrol>

In order to maintain CCNET config and projects in clear XML code, the ccnet.config in above example takes advantage of CCNET powerful preprocessor features. There are three types of CCNET configuration preprocessors: constant (including text/variable and nodeset XML fragment), nested expansion (defining a class whose instance can take parameters), and include file (<cb:include />).

Including external config files helps putting constant/variable definitions, project definitions, and reusable XML pieces together in an organized structure, so that ccnet.config can be read in a from-top-to-down order while presenting high-level view in the main config. Another advanced use of include files is to allow editing definition or project file individually without touching ccnet.config. CCNET has the ability to notice any include file changed and reload ccnet.config automatically.

Like writing a program, the first thing in design is to define constants, variables, and reusable functions (or templates) that can be shared across the projects. These are all implemented by <cb:define /> in CCNET. Constants and XML fragments can be defined by DTD, such as in XHTML DTDs and following sample of ccnet_definitions.dtd included in ccnet.config.

<!--ccnet_definitions.dtd-->
<!ENTITY dir_ccnet "$(ProgramFiles)\CruiseControl.NET">
<!ENTITY dir_ccnet_server "$(ProgramFiles)\CruiseControl.NET\server">
<!ENTITY dir_ccnet_artifacts "$(ProgramFiles)\CruiseControl.NET\server\projects">
<!ENTITY dir_svn "$(ProgramFiles)\CollabNet Subversion Server">
<!ENTITY dir_svn_target "\\10.0.1.100\svnbuilds">
<!ENTITY dir_dotnet "$(SystemRoot)\Microsoft.NET\Framework\v3.5">
<!ENTITY dir_msvs "$(ProgramFiles)\Microsoft Visual Studio 9.0">

<!ENTITY svn_server "svn://192.168.0.100">
<!ENTITY auth_svn "<username>svn_user</username><password>svn_password</password>" >

<!ENTITY exec_devenv "<executable>&dir_msvs;\Common7\IDE\devenv.com</executable>" >
<!ENTITY exec_nmake "<executable>&dir_msvs;\VC\bin\nmake.exe</executable>" >
<!ENTITY exec_msbuild "<executable>&dir_dotnet;\MSBuild.exe</executable>" >
<!ENTITY exec_svn "<executable>&dir_svn;\svn.exe</executable>" >

<!ENTITY url_ccnet "http://localhost/ccnet">

However, entity definition has its limitation in use (such as in string value of a property) and modifying a system file included by <!DOCTYPE > cannot trigger ccnet.config to reload. In CCNET, <cb:define /> is recommended to perform the same and even better job. The following sample illustrates syntax of defining text constant (variable) and nodeset (xml fragment). The third usage of <cb:define />, nested expansion, is for reusable class that can take parameters by instance, such as defining a project template.

<!--ccnet_definitions.xml-->
<cb:config-template xmlns:cb="urn:ccnet.config.builder">
<!--# Preprocessor: Text Constants -->
<cb:define const_name="value" /> <-- defines $(const_name), or <cb:const_name/> -->
<cb:define dir_ccnet="$(ProgramFiles)\CruiseControl.NET" />
<cb:define dir_ccnet_server="$(dir_ccnet)\server" />
<cb:define dir_ccnet_artifacts="$(dir_ccnet_server)\projects" />
<cb:define dir_ccnet_buildlogger="$(dir_ccnet)\server\ThoughtWorks.CruiseControl.MsBuild.dll" />
<cb:define dir_dotnet="$(SystemRoot)\Microsoft.NET\Framework\v3.5" />
<cb:define dir_svn="$(ProgramFiles)\CollabNet Subversion Server" />
<cb:define dir_svn_source="c:\svn_checkout" />
<cb:define dir_svn_builds="d:\svn_builds" />
<cb:define dir_svn_target="\\10.0.1.100\svnbuilds" />
<cb:define dir_system32="$(SystemRoot)\system32" />
<cb:define dir_msvs="$(ProgramFiles)\Microsoft Visual Studio 9.0" />

<cb:define svn_username="svn_username" />
<cb:define svn_password="svn_password" />

<cb:define url_svn_target="file://///10.0.1.100/svnbuilds" />
<cb:define url_ccnet="http://localhost/ccnet" />

<!--# Preprocessor: Nodeset Constants -->
<cb:define name="xml_default_extlink">
<externalLink name="CCNET Builds [Ctrl+Click to open in Explorer]" url="$(url_svn_target)" />
</cb:define>
<cb:define name="xml_logger">
<xmllogger logDir="$(dir_ccnet_server)\projects\$(project)" />
</cb:define>

<cb:define name="auth_svn">
<username>$(svn_username)</username>
<password>$(svn_password)</password>
</cb:define>
<cb:define name="exec_devenv">
<executable>$(dir_msvs)\Common7\IDE\devenv.com</executable>
</cb:define>
<cb:define name="exec_nmake">
<executable>$(dir_msvs)\VC\bin\nmake.exe</executable>
</cb:define>
<cb:define name="exec_vcvars">
<executable>$(dir_msvs)\VC\vcvarsall.bat</executable>
<baseDirectory>$(dir_msvs)\VC</baseDirectory>
<buildArgs>x86</buildArgs>
</cb:define>
<cb:define name="exec_msbuild">
<executable>$(dir_dotnet)\MSBuild.exe</executable>
</cb:define>
<cb:define name="exec_svn">
<executable>$(dir_svn)\svn.exe</executable>
</cb:define>

<!--# Preprocessor: Nested Expansions (see other ccnet_*.xml)
<cb:define name="xml_element"><some_element property="$(var1)" /><more>$(var2)</more></cb:define>
<cb:xml_element var1="value1" var2="value2" />
-->

</cb:config-template>
The use of definitions in CCNET is easy to understand from above sample config file. First of all, any system environment variables can be referenced by $(env_var), where $(env_var) is an environment variable accessible in CCNET runtime context. For example, if CCNET is started by ccnet.exe manually in a Command Prompt window, all system variables and logon user variables should be available; otherwise, if CCNET is started in service.msc, only system variables can be used.

Once a CCNET constant/variable (<cb:define var_name="text" />) or XML fragment (<cb:define name="element_name" >...</cb:define>) is defined, it can be referenced immediately afterward thru whole CCNET runtime environment. The constant/variable can be referenced as a string by $(var_name), or <cb:var_name />. The XML fragment must be referenced by <cb:element />. All <cb:define /> are global definitions. To control the scope of a preprocessor definition, use <cb:scope />. See configuration preprocessors.

Next, let's take a look at how to use CCNET nested expansions and parameters of preprocessor definition to create a project template. This template can be used to meet the following conditions:
  • All project properties can be initiated by an instance of the template
  • There is only one build target path needed to be published
  • There is only one developer and who will be in notification for all build states
  • The project can be built by a MS Visual Studio 2008 solution file
Similar to ccnet_definitions.xml, ccnet_Project.Standard_Solution.xml uses <cb:config-template /> but only defines one XML fragment named "project_template_solution".

<!--ccnet_Project.Standard_Solution.xml-->
<?xml version="1.0" encoding="utf-8" ?>
<cb:config-template xmlns:cb="urn:ccnet.config.builder">
<cb:define name="project_template_solution">
<project name="$(project_name)" queue="$(project_queue)" queuePriority="0">
<category>$(project_category)</category>
<workingDirectory>$(dir_svn_source)\$(project_name)</workingDirectory>
<artifactDirectory>$(dir_ccnet_artifacts)\$(project_name)</artifactDirectory>
<webURL>$(url_ccnet)</webURL>

<labeller type="svnRevisionLabeller">
<prefix>$(project_name)-r</prefix>
<major>0</major>
<minor>0</minor>
<url>svn://$(project_svnServer)$/(project_Root)$(project_Path)/$(project_Branch)</url>
<cb:auth_svn />
</labeller>
<!--
<labeller type="lastChangeLabeller"><prefix>$(project_name)-</prefix></labeller>
-->
<triggers>
<!--NOTE: build machine must sync clock with svn server!!!-->
<intervalTrigger buildCondition="IfModificationExists" seconds="$(project_checkTime)" initialSeconds="60" />
</triggers>
<sourcecontrol type="svn">
<trunkUrl>svn://$(project_svnServer)$/(project_Root)$(project_Path)/$(project_Branch)</trunkUrl>
<cb:auth_svn />
<cb:exec_svn />
<workingDirectory>$(dir_svn_source)\$(project_name)</workingDirectory>
<timeout units="seconds">$(project_checkTime)</timeout>
</sourcecontrol>

<tasks>
<msbuild>
<cb:exec_msbuild />
<projectFile>$(project_solution)</projectFile>
<workingDirectory>$(dir_svn_source)\$(project_name)</workingDirectory>
<buildArgs>/noconsolelogger /p:Configuration=Release</buildArgs>
<logger>$(dir_ccnet_buildlogger)</logger>
</msbuild>
</tasks>

<publishers>
<cb:xml_logger project="$(project_name)" />

<buildpublisher>
<sourceDir>$(dir_svn_source)\$(project_name)\$(project_buildTarget)</sourceDir>
<publishDir>$(dir_svn_target)</publishDir>
<useLabelSubDirectory>true</useLabelSubDirectory>
<alwaysPublish>false</alwaysPublish>
</buildpublisher>

<cb:include href="ccnet_email.xml" xmlns:cb="urn:ccnet.config.builder" />
<cb:ccnet_email
developer_name="Developer" developer_alias="$(project_developer)"
notification="Always"
/>
</publishers>

</project>
</cb:define>

</cb:config-template>

Different from ccnet_definitions.xml, ccnet_Project.Standard_Solution.xml also uses some variables that have never been defined, such as $(project_name), $(project_path), and etc., basically anything with "project_" prefix in this example. This is the exclusive feature of CCNET preprocessor that allow all these variable dynamically bound to an instance of the template when it is used. In ccnet.config, after ccnet_Project.Standard_Solution.xml is loaded, a project file ccnet_MyProject.xml comes after to initiate a project configuration based on the template. Here is code:

<!--ccnet_MyProject.xml-->
<cb:config-template xmlns:cb="urn:ccnet.config.builder">
<cb:project_template_solution
project_name="My Project"
project_alias="myproject"
project_category="My Category"
project_path="MyProject"
project_root=""
project_branch="trunk"
project_svnServer="192.168.10.100"
project_svnPath="svn://192.168.10.100/Software/MyProject/trunk"
project_solution="MyProject.sln"
project_buildTarget="Setup\Release"
project_buildTime="1800"
project_checkTime="600"
project_developer="myalias"
project_queue="$(computername)"
/>

</cb:config-template>

The properties defined in "My Project" configuration makes the template used in complete. Actually, the property names after cb:project_template_solution may not be necessary matching what have been defined in the template. At least CCNET does not check if a referenced constant or variable is predefined. Undefined constant/variable will be replaced by empty string (- be caution that this could cause exception at the runtime). And any property defined in the instance but not used in the template will be ignored (e.g. project_svnPath).

CCNET preprocessor config template can be nested. Here is the file of ccnet_email.xml that has been included in ccnet_Project.Standard_Solution.xml:

<!--ccnet_email.xml-->
<cb:config-template xmlns:cb="urn:ccnet.config.builder">

<cb:define name="ccnet_email">
<email from="Builder@mycompany.com"
mailhost="192.168.10.25" includeDetails="TRUE" useSSL="FALSE"
subjectPrefix="CCNET:" description="CruiseControl.NET E-mail Notification ">
<users>
<user name="Builder" group="Builder" address="builder@mycompany.com"/>
<user name="$(developer_name)" group="Developer" address="$(developer_alias)@mycompany.com"/>
<user name="Build Request" group="Supervisor" address="buildrequest@mycompany.com"/>
<user name="Admin" group="Admin" address="admin@mycompany.com"/>
</users>
<groups>
<group name="Admin" notification="Failed"/>
<group name="Builder" notification="Success"/>
<group name="Developer" notification="$(notification)"/>
</groups>

<modifierNotificationTypes>
<NotificationType>Always</NotificationType>
<NotificationType>Failed</NotificationType>
<NotificationType>Fixed</NotificationType>
<NotificationType>Success</NotificationType>
<NotificationType>Change</NotificationType>
</modifierNotificationTypes>

<subjectSettings>
<subject buildResult="Broken" value="Build Failed - ${CCNetProject} - [Broken] :: ${CCNetBuildCondition}" />
<subject buildResult="StillBroken" value="Build Failed - ${CCNetProject} - [StillBroken] :: ${CCNetBuildCondition}" />
<subject buildResult="Fixed" value="Build Success - ${CCNetProject} - [Fixed] :: ${CCNetBuildCondition}" />
<subject buildResult="Exception" value="Build Failed - ${CCNetProject} - [Exception] :: ${CCNetBuildCondition}" />
<subject buildResult="Success" value="Build Success - ${CCNetProject} - [Success] :: ${CCNetBuildCondition}" />
</subjectSettings>
</email>
</cb:define>

<!--# after <cb:include href="ccnet_email.xml" />, define following properties:
developer_name - developer name
developer_alias - developer email alias
notification - notification type: Always|Change|Failed|Success|Fixed
<cb:ccnet_email
developer_name=""
developer_alias=""
notfication=""
/>
==-->
</cb:config-template>
For label variables used in <subjectSettings />, please refer to CCNET Email Publisher.

The last thing left uncovered in the project template is "svnRevisionLabeller". This is a modified plugin based on original svnrevisionlabeller. The code (SvnRevisonLabeller.cs) is attached below:

/**
****************************************
* Class Name: SvnRevisionLabeller
* Description: Subversion functions used in NAnt
* Requisite: Reference to NAnt.Core.dll
* Notes: See http://code.google.com/p/svnrevisionlabeller/
****************************************
*/
using System;
using System.Xml;
using Exortech.NetReflector;
using ThoughtWorks.CruiseControl.Core;
using ThoughtWorks.CruiseControl.Core.Util;

namespace ccnet.SvnRevisionLabeller.plugin
{
/// <summary>
/// Generates label numbers using the Subversion revision number.
/// </summary>
/// <remarks>
/// This class was inspired by Jonathan Malek's post on his blog
/// (<a href="http://www.jonathanmalek.com/blog/CruiseControlNETAndSubversionRevisionNumbersUsingNAnt.aspx">CruiseControl.NET and Subversion Revision Numbers using NAnt</a>),
/// which used NAnt together with Subversion to retrieve the latest revision number. This plug-in moves it up into
/// CruiseControl.NET itself, so that you can see the latest revision number appearing in CCTray. The label can
/// then be retrieved from within NAnt by accessing the property <c>${CCNetLabel}</c>.
/// </remarks>
[ReflectorType("svnRevisionLabeller")]
public class SvnRevisionLabeller : ILabeller
{
#region Private members

private int major;
private int minor;
private string _url;
private string executable;
private string prefix;
private string username;
private string password;
private const string RevisionXPath = "/log/logentry/@revision";

#endregion

#region Constructors

/// <summary>
/// Initializes a new instance of the <see cref="SvnRevisionLabeller"/> class.
/// </summary>
public SvnRevisionLabeller()
{
major = 1;
minor = 0;
executable = "svn.exe";
prefix = String.Empty;
}

#endregion

#region Public methods

/// <summary>
/// Returns the label to use for the current build.
/// </summary>
/// <param name="resultFromLastBuild">IntegrationResult from last build used to determine the next label</param>
/// <returns>the label for the new build</returns>
public string Generate(IIntegrationResult resultFromLastBuild)
{
// Get the last revision from the Subversion repository
int svnRevision = GetRevision();

// Get the last revision from CruiseControl
Version version = ParseVersion(svnRevision, resultFromLastBuild);

// If the revision number hasn't changed (because no new check-ins have been made), increment the build number;
// Otherwise, reset the build number to 0
int revision = (svnRevision == version.Build) ? version.Revision + 1 : 0;

// Construct a new version number, adding any specified prefix
Version newVersion = new Version(major, minor, svnRevision, revision);

if (major == 0 && minor == 0)
{
// <major>0</major><minor>0</minor> must be explicitly defined
// assume we only care about subversion revision - 2008-11-18 jzhu@zetron.com
return prefix + svnRevision;
}
else // keep original usage for a 4-field version format
{
return prefix + newVersion;
}
}

/// <summary>
/// Runs the task, given the specified <see cref="IIntegrationResult"/>, in the specified <see cref="IProject"/>.
/// </summary>
/// <param name="result"></param>
public void Run(IIntegrationResult result)
{
result.Label = Generate(result);
}

#endregion

#region Public properties

/// <summary>
/// Gets or sets the major build number.
/// </summary>
/// <value>The major build number.</value>
[ReflectorProperty("major", Required=false)]
public int Major
{
get
{
return major;
}
set
{
major = value;
}
}

/// <summary>
/// Gets or sets the minor build number.
/// </summary>
/// <value>The minor build number.</value>
[ReflectorProperty("minor", Required=false)]
public int Minor
{
get
{
return minor;
}
set
{
minor = value;
}
}

/// <summary>
/// Gets or sets the repository URL from which the <c>svn log</c> command will be run.
/// </summary>
/// <value>The repository.</value>
[ReflectorProperty("url", Required = true)]
public string Url
{
get
{
return _url;
}
set
{
_url = value;
}
}

/// <summary>
/// Gets or sets the Subversion client executable.
/// </summary>
/// <value>The path to the executable.</value>
/// <remarks>
/// If the value is not supplied, the task will expect to find <c>svn.exe</c> in the <c>PATH</c> environment variable.
/// </remarks>
[ReflectorProperty("executable", Required=false)]
public string Executable
{
get
{
return executable;
}
set
{
executable = value;
}
}

/// <summary>
/// Gets or sets an optional prefix for the build label.
/// </summary>
/// <value>A string to prefix the version number with.</value>
[ReflectorProperty("prefix", Required=false)]
public string Prefix
{
get
{
return prefix;
}
set
{
prefix = value;
}
}

/// <summary>
/// Gets or sets the username to access SVN repository.
/// </summary>
/// <value>The repository.</value>
[ReflectorProperty("username", Required = false)]
public string Username
{
get
{
return username;
}
set
{
username = value;
}
}

/// <summary>
/// Gets or sets the password to access SVN repository.
/// </summary>
/// <value>The repository.</value>
[ReflectorProperty("password", Required = false)]
public string Password
{
get
{
return password;
}
set
{
password = value;
}
}

#endregion

#region Private methods

/// <summary>
/// Parses the version.
/// </summary>
/// <param name="revision">The revision.</param>
/// <param name="resultFromLastBuild">The result from last build.</param>
private Version ParseVersion(int revision, IIntegrationResult resultFromLastBuild)
{
try
{
string label = resultFromLastBuild.LastSuccessfulIntegrationLabel;
if (prefix.Length > 0)
{
label = label.Replace(prefix, String.Empty).TrimStart('_');
}
return new Version(label);
}
catch (SystemException)
{
return new Version(major, minor, revision, 0);
}
}

/// <summary>
/// Gets the latest Subversion revision by checking the last log entry.
/// </summary>
private int GetRevision()
{
// Set up the command-line arguments required
ProcessArgumentBuilder argBuilder = new ProcessArgumentBuilder();
argBuilder.AppendArgument("log");
argBuilder.AppendArgument("--xml");
argBuilder.AppendArgument("--limit 1");
argBuilder.AppendArgument(Url);
if (Username != null && Username.Length > 0 && Password != null && Password.Length > 0)
{
AppendCommonSwitches(argBuilder);
}

// Run the svn log command and capture the results
ProcessResult result = RunProcess(argBuilder);
Log.Debug("Received XML : " + result.StandardOutput);

// Load the results into an XML document
XmlDocument xml = new XmlDocument();
xml.LoadXml(result.StandardOutput);

// Retrieve the revision number from the XML
XmlNode node = xml.SelectSingleNode(RevisionXPath);
return Convert.ToInt32(node.InnerText);
}

/// <summary>
/// Appends the arguments required to authenticate against Subversion.
/// </summary>
/// <param name="buffer"><The argument builder./param>
private void AppendCommonSwitches(ProcessArgumentBuilder buffer)
{
buffer.AddArgument("--username", Username);
buffer.AddArgument("--password", Password);
buffer.AddArgument("--non-interactive");
buffer.AddArgument("--no-auth-cache");
}

/// <summary>
/// Runs the Subversion process using the specified arguments.
/// </summary>
/// <param name="arguments">The Subversion client arguments.</param>
/// <returns>The results of running the process, including captured output.</returns>
private ProcessResult RunProcess(ProcessArgumentBuilder arguments)
{
ProcessInfo info = new ProcessInfo(executable, arguments.ToString(), null);
Log.Debug("Running Subversion with arguments : " + info.Arguments);

ProcessExecutor executor = new ProcessExecutor();
ProcessResult result = executor.Execute(info);
return result;
}
#endregion
}
}


CruiseControl

Comparing to CCNET, CruiseControl does not provide very powerful preprocessor and template feature. There is no syntax to define XML fragment (except using DTD). The main config file, config.xml, cannot be automatically reloaded if any configuration changed.

<!--config.xml-->
<?xml version="1.0" encoding="utf-8" ?>
<!--# Always deploy ccnet.config with following files under ${env.CCDIR}
config.properties
cc_antpublisher_copy.xml
prj_*.xml
also place cruisecontrol.jar under "${env.CCDIR}\lib" to overwrite
original one for a customized new plugin: <ZSVNLabeller />
==-->
<cruisecontrol>
<property file="config.properties" />
<property environment="env" toupper="true" />
<property name="url_cruisecontrol" value="${url_cchost}/cruisecontrol" />
<property name="url_buildresults" value="${url_cruisecontrol}/buildresults" />
<property name="url_ccdashboard" value="${url_cchost}/dashboard" />
<property name="url_ccdoc" value="${url_cchost}/documentation" />

<include.projects file="prj_MyProject.xml" />

</cruisecontrol>

As in above sample of config.xml, property definition is either by <property name="var" value="text" /> element or an external file (e.g. config.properties).

###############################################################################
# Filename: config.properties
# Usage: called by cruisecontrol element, i.e.:
# <property file="config.properties" />
# Syntax: defined by the class java.util.Properties, with the same rules
# about how non-ISO8859-1 characters must be escaped.
###############################################################################

### default value predefinitions
def_hostip=10.0.1.194
def_hostname=hostname
def_project_buildTime=1800
def_project_checkTime=600

### dir/path predefinitions
dir_cruisecontrol=%ProgramFiles%\CruiseControl
dir_svn_target=\\10.0.1.100\svnbuilds
pwd_cruisecontrol=/srv/cruisecontrol-bin-2.8.2
smb_svn_target=/svnbuilds

### svn settings predefinitions
svn_server=svn://192.168.10.100
svn_username=svn_username
svn_password=svn_password

### user id predefinitions
uid_admin=admin
uid_builder=builder

### url predefinitions
url_cchost=http://hostname:8080
url_ccdashboard=http://hostname:8080/dashboard
url_ccdoc=http://hostname:8080/documentation
url_cruisecontrol=http://hostname:8080/cruisecontrol
url_buildresults=http://hostname:8080/cruisecontrol/buildresults
url_ccnet=http://localhost/ccnet

Certainly, a project file (e.g. prj_MyProject.xml) can be included in config.xml so that the configuration is well organized by each individual config file. Here only provides a project template file that a real project config (e.g. prj_MyProject.xml) will be based on.

<!--prj_project.template.xml-->
<?xml version="1.0" encoding="utf-8" ?>
<!--#
For each project, use following steps to create a project file from this template by
copying this file to a new project file, in convention of "prj_$[project_name].xml":
(1) replace "$[project_name]" with a project name
(2) replace any place holder definition, like in format of $[place_holder], in property definitions
(3) optionally use ${def_project_buildTime} for ${project_buildTime}
(4) optionally add more ${project_buildTarget[n]} properties and publishers
(5) choose one of the build methods in <project><schedule></schedule></project> block
(6) replace "\\" to "/" on Linux system
(7) replace ${dir_svn_target} with ${smb_svn_target} on a Linux build system
(8) define properties ${eid_*} for build notification; ${uid_builder} has been notified on success
(9) refer to any property definitions in config.properties
==-->
<cruisecontrol>
<project name="$[project_name]">
<property name="project_name" value="${project.name}" />
<property name="project_alias" value="$[project_alias]" />
<property name="project_category" value="$[project_category]" />
<property name="project_root" value="$[project_root]" />
<property name="project_path" value="$[project_path]" />
<property name="project_branch" value="$[project_branch]" />
<property name="project_svnServer" value="$[project_svnServer]" />
<property name="project_svnPath" value="svn://${project_svnServer}/${project_root}${project_path}/${project_branch}" />
<property name="project_source" value="${env.CCDIR}\\projects\\${project.name}" />
<property name="project_buildTarget" value="$[project_buildTarget]" />
<property name="project_buildTime" value="${def_project_buildTime}" />
<property name="project_buildTime" value="$[project_buildTime]" />
<property name="project_checkTime" value="30" />
<property name="url_cruisecontrol" value="${url_cruisecontrol}" />
<property name="eid_failure" value="builder" />
<property name="eid_success" value="builder" />
<property name="eid_always" value="developer_alias" />

<plugin name="svn" classname="net.sourceforge.cruisecontrol.sourcecontrols.SVN" />
<plugin name="ZSVNLabeller" classname="net.sourceforge.cruisecontrol.labelincrementers.ZSVNLabeller" />

<ZSVNLabeller separator="r" labelprefix="r" workingcopypath="${project_source}"
/>
<!--#
<listeners>
<currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
</listeners>
==-->
<bootstrappers>
<!--#
<antbootstrapper anthome="apache-ant-1.7.0"
buildfile="projects/${project.name}/build.xml"
target="clean" />
<execbootstrapper command=""
args="" workingdir="projects/${project.name}"
errorstr="" showProgress=""
timeout=""
/>
==-->
<svnbootstrapper localWorkingCopy="${project_source}"
username="${svn_username}"
password="${svn_password}"
/>
</bootstrappers>

<modificationset quietperiod="60">
<!--# <filesystem/> triggers a build if any file in ${project.name} project is touched
<filesystem folder="${project_source}" includedirectories="false" />
==-->
<!--# <svn/> sets property ${svnrevision}
==-->
<svn localWorkingCopy="${project_source}"
useLocalRevision="false"
username="${svn_username}"
password="${svn_password}"
property=""
/>
</modificationset>

<schedule interval="{project_checkTime}">
<!--#<ant />
<ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"/>
==-->
<!--#<exec/>
==-->
<exec command=".\\build.cmd"
args='"${project_source}"' workingdir="${project_source}"
errorstr="Build Failed" showProgress="true"
timeout="${project_buildTime}"
/>
</schedule>

<log>
<!--# merge from log files with specified pattern from specified dir
<merge dir="${project_source}\\log" pattern="*.log" />
==-->
<deleteartifacts every="30" unit="DAY" />
<delete every="30" unit="DAY" />
</log>

<publishers>
<htmlemail buildresultsurl="${url_cruisecontrol}/buildresults/${project_name}"
defaultsuffix=".com" mailhost="192.168.10.66" mailport="25" usessl="false"
returnaddress="builder@mycompany.com" returnname="Builder"
subjectprefix="CruiseControl:"
>
<success address="${uid_builder}@mycompany.com" /><!--send to builder success notification-->
<success address="${eid_success}@mycompany.com" />
<failure address="${eid_failure}@mycompany.com" />
<always address="${eid_always}@mycompany.com" />
</htmlemail>

<onsuccess>
<antpublisher anthome="apache-ant-1.7.0"
buildfile="${env.CCDIR}\\cc_antpublisher_copy.xml"
target="publish_dir"
>
<property name="publish_source" value="${project_source}\\${project_buildTarget}" />
<property name="publish_target" value="${dir_svn_target}\\${project_alias}" />
</antpublisher>
<!--
<antpublisher anthome="apache-ant-1.7.0"
buildfile="${env.CCDIR}\\cc_antpublisher_copy.xml"
target="publish_file"
>
<property name="publish_source" value="${project_source}\\${project_buildTarget}" />
<property name="publish_target" value="${dir_svn_target}\\${project_alias}" />
</antpublisher>
==-->
</onsuccess>
</publishers>

</project>

</cruisecontrol>

The template cannot be used as nicely as in CCNET. But it gives a start for most projects. Please refer to usage comments at beginning of the template. Also remember since CruiseControl is running on both Windows and Linux systems, backslash or forward-slash need to be carefully used wherever a path is typed.

One of the difference in CruiseControl than CCNET is the sequence of continuous integration cycle. In CCNET, the <sourcecontrol /> compares last build state with repository server, then checkout (for the first time of check) or update source code on local working directory before starting build tasks. But in CruiseControl, the bootstrapper is always called at the beginning to update local working directory from server before using <modificationset /> to detect if tasks in <schedule /> need to be executed.

Such difference implies that the local working directory and source code in CruiseControl project must be manually checked out from the repository (such as `svn co`) at the time when the project is setup, which is not necessary in CCNET - where the local working directory can be created automatically if it does not exist. This also explains why LocalWorkingCopy (rather than RepositoryLocation) property should be used in <svn /> source control under <modificationset /> to detect new commit for a CruiseControl project.

Note: There could be a chance that local working copy has not been updated yet when a new change is committed on repository server after bootstrapper but before modificationset. The race condition will end up building old source against a newer revision label.

Modification of subversion in CruiseControl is parsed from `svn log -r` by checking between last build time and current time. If useLocalRevision property is used in <svn /> source control, the local revision number will be in place of current time.

At the end of the template, artifacts publishing is implemented by the following include file.

<!--cc_antpublisher_coppy.xml-->
<project name="antpublisher_copy" default="publish">
<target name="publish">
<echo>Copying ${publish_source} to ${publish_target} ...</echo>
<copy todir="${publish_target}"><fileset dir="${publish_source}" /></copy>
</target>
<target name="publish_dir">
<echo>Copying ${publish_source} to ${publish_target}-${label} ...</echo>
<copy todir="${publish_target}-${label}"><fileset dir="${publish_source}" /></copy>
</target>
<target name="publish_file">
<echo>Copying ${publish_source} to ${publish_target}-${label} ...</echo>
<copy todir="${publish_target}-${label}" file="${publish_source}"></copy>
</target>
<target name="publish_artifacts">
<echo>Copying ${publish_source_dir} to ${publish_target} ...</echo>
<copy todir="${publish_target}">
<fileset dir="${publish_source_dir}">
<include name="${publish_source_files}" />
</fileset>
</copy>
</target>
</project>

The Java source code of "ZSVNLabeller" plugin (based on SVNLabelIncrementer, implements LabelIncrementer) is attached as below. The file "ZSVNLabeller.java" should be place under source code folder "net\sourceforge\cruisecontrol\labelincrementers". After rebuilt, copy "cruisecontrol.jar" to ${env.CCDIR}, where CruiseControl installed.

package net.sourceforge.cruisecontrol.labelincrementers;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import net.sourceforge.cruisecontrol.LabelIncrementer;

import org.apache.log4j.Logger;
import org.jdom.Element;

/**
* This class provides a label incrementation based on svn revision numbers.
* This class expects the label format to be "x&lt;sep&gt;y[&lt;sep&gt;z]",
* where x is any String and y is an integer and &lt;sep&gt; a separator, the
* last part z, is optional, and gets generated and later incremented in case a
* build is forced. The default separator is "." and can be modified using
* {@link #setSeparator}.
*
* @author Ketan Padegaonkar &lt; KetanPadegaonkar gmail &gt;
*/

/// UPDATE: return revisionNumber if separator is as same as prefix - Boathill

public class ZSVNLabeller implements LabelIncrementer {
private static final Logger LOG = Logger.getLogger(ZSVNLabeller.class);

private String workingCopyPath = ".";

private String labelPrefix = "svn";

private String separator = ".";

public boolean isPreBuildIncrementer() {
return true;
}

public String incrementLabel(String oldLabel, Element buildLog) {
String revisionNumber = "";
String result = oldLabel;
try {
revisionNumber = getSvnRevision();

if (getSeparator().equals(getLabelPrefix())) {
return labelPrefix + revisionNumber; // return svn revison only
}
if (revisionNumber == null || revisionNumber.equals("")) {
return labelPrefix;
}
result = labelPrefix + getSeparator() + revisionNumber;

if (oldLabel.indexOf(result) > -1) {
int lastSeparator = oldLabel.lastIndexOf(getSeparator());
int firstSeparator = oldLabel.indexOf(getSeparator());
int lastPart = 1;
if (lastSeparator != firstSeparator) {
String suffix = oldLabel.substring(lastSeparator + 1);
lastPart = Integer.parseInt(suffix) + 1;
}
result += getSeparator() + lastPart;
}
LOG.debug("Incrementing label from " + oldLabel + " to " + result);
} catch (IOException e) {
LOG.error("could not execute svn binary", e);
} catch (NumberFormatException e) {
LOG.error("could not increment label. Old label was " + oldLabel + ". svn revision was " + revisionNumber,
e);
}

return result;
}

protected String getSvnRevision() throws IOException {
String rev;
Process p = null;
try {
p = Runtime.getRuntime().exec(new String[]{"svnversion", workingCopyPath});
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
rev = stdInput.readLine();
} finally {
if (p != null) {
p.destroy();
}
}
LOG.debug("SVN revision is: " + rev);
return rev;
}

public boolean isValidLabel(String label) {
// we don't mind what the previous label is,
// when the next label is built, then parsing is performed to add / increment a suffix.
return true;
}

public void setWorkingCopyPath(String path) {
LOG.debug("Working Path is: " + path);
workingCopyPath = path;
}

public String getLabelPrefix() {
return this.labelPrefix;
}

public void setLabelPrefix(String labelPrefix) {
this.labelPrefix = labelPrefix;
}

public String getDefaultLabel() {
return getLabelPrefix() + getSeparator() + "0";
}

public String getSeparator() {
return this.separator;
}

public void setSeparator(String separator) {
this.separator = separator;
}
}

Comparison

Some other Continuous Integration products, e.g. Cruise and Hudson, have much nicer web interface, while CruiseControl.NET and CruiseControl provides better flexibility and can be greatly customized. However, the flexibility sometimes means difficulty. For example, CruiseControl has three web sites - dashboard (:8080), project cruisecontrol (:8000), and JMS console - but not be seen on one page; CruiseControl supports CCNET CCTray but could not integrate with CCNET projects or dashboard (even if both developed by ThoughtWorks).

The build publisher supports using build label in both CCNET (by <labeller />) and CruiseControl (${label} in ant config). And in CCNET, the build publisher has to copy all contents in a specified source to another location although ant or nant can be used to customize such task. CruiseControl web interfaces do have a little more advanced features than CCNET. As an instance, the build target files (artifacts) can be published on CruiseControl dashboard so that can be downloaded. But the artifact in CCNET is just a local folder not accessible from the web.

On the other hand, CruiseControl does not display the runtime log on web interfaces. The build log can only be reviewed after the build is done. In CCNET dashboard, this is provided by a server log link. Although the server log in CCNET is not automatically updated (as nicely as a real time console display in Hudson), it at least gives user some hint before knowing the build succeeded or failed. Having better integration with Windows, CCNET can be run in either command line or service mode. For CruiseControl, you have to write a /etc/init.d/cruisecontrol.

Since CruiseControl.NET derived from CruiseControl, both work the same way and the cofiguration shares similarity. As open source projects, it is convenient to develop or modify plugin to suite need during implementation. CruiseControl is cross-platform by Java technology. CCNET has improved on web integration and preprocessor configuration so that project file can be well structured via nodeset expansion template.

Note: Source code samples are formatted by SyntaxHighlighter.

HDR - Hight Dynamic Range Imaging

Easton Lake State Park Kachess Lake
Sunset Aki Beach Sunset Aki Beach - HDR

Sunset Aki Beach Sunset Aki Beach
Sunset Seattle - HDR Sunset Seattle - HDR

Life Art

Epigram

  拟旧文学诗一首──驾车于高速公路戏作

  我们是──
  寄生在车轮上的腐朽阶级,
  不爱工作,也不爱学习。
  整日好吃好喝,每天昏睡不起,
  沉迷了思想,又虚淘了身体。

  有时也曾想振作自己,
  可懒惰总是最大的天敌。
  让舒适摧残了健康、
  和用金钱也买不到的毅力;
  才知道没有艰苦的付出,
  不明白宝葫芦的秘密。

  挥霍人生作游戏,
  似在虚拟的空间里逃避。
  坐井城市的天地,
  分不清麦子和稻米。
  如果和劳动人民比一比,
  这样的生活有何意义?

2009.07.12 Seattle

Qing-Yu-Nian (庆余年)

  穿越、架空、玄幻……一枝穿云箭,千军万马来相见……
  不管叫什么名字,这种跨越时空、指点乾坤的构想可能曾经存在于每个孩子的少年梦里。以前在起点中文上看了不少此类称为“玄幻”的小说。当时看到司马的《一统三国》,就好像是自己想写的那种纵横山河的故事。其他一些玄幻的架构也各有翻新。印象较深的是一部进行时空机器试验的小说,其中第一卷穿越到战国,正值秦国统一战争的末期,主人公遇到荆轲,义结金兰,决心用现代武器协助荆轲刺秦,并相约在某日同行。不想就在出发的前一夜被时光机器召回,却不能及时通知荆轲,悔恨异常。而荆轲苦守誓约,等了七天后,终于无奈启程。不知是不是受这种伤心的情绪影响,导致刺秦失败。最有意思的是,作者引用《史记》中“荆轲有所待,欲与俱,其人居远未来,而为留待……”指的就是这次爽约,反证当时荆轲确实在等一个人,而不是象有些考证说荆轲是个胆小鬼,临阵退缩不敢前行。
  二零零八年最流行的网络小说里,有两部都是穿越小说:《窃明》和《庆余年》。《窃明》引起的争论就不说了。《庆余年》可是备受推崇,还被书商奉为什么“权臣宝典”。其实元素很多,不仅是政治斗争,书中还埋下了无数的大坑小坑,真是“雨打沙滩万点坑”。作者开宗明义:“千穿万穿,唯有马屁不穿”,就是要“穿越到了一个完全不同的世界”。网络连载到二零零九年才结束,等找全了七卷百多万字,到下半年才看完。这部书借穿越主角范闲反复提出:“人的一生应当怎样度过?”──他是在穿越中获得重生的幸运儿,新的一辈子都是赚来的。虽然一开始范闲就很惜命,但有一个确定不移的目标就是:“抡圆了活一把”。这暗藏在他对练功的执着和耐苦上,也表现在平时狂傲不羁的作风。经过反复思索和考验,加上前世的理念,最终确定信仰,走向强大。
  这里下载文字版排印版。以下是摘录。


庆余年:大结局!2009.02.25

  二〇〇七年五月一日,《庆余年》第一章上传。二〇〇九年二月二十四日晚,大结局。近乎两年的时间。看小说连载就是这个好处,不管我愿意不愿意,这个故事就这么慢悠悠的陪了我这两年的时间。
  这是我最喜欢的一本小说。时代总是在发展进步的。当年喜欢《悟空传》,《此间的少年》,后来喜欢《搜神记》,再后来喜欢《亵渎》,现在就是《庆余年》了,读下来似乎是一本比一本好的感觉。长江后浪推前浪,我很欣慰。
  最初这书名实在是不能引诱人点进去看看。我从第一章就开始追着看的原因是作者猫腻的前一本小说《朱雀记》。那本书还是写得水了点,除了最后十万字填它的大坑填得极精彩精妙之外,没有什么值得一提之处,但那个大坑实在是太漂亮了!那个坑在讲,主角一行人被大势至菩萨追杀得没处躲没处藏,因为大势至菩萨要灭世,主角们拦着不让灭……但是主角们一直很奇怪,没有真正的大慈悲心,是不能成菩萨果位的啊,这正牌的大菩萨怎么还能成天一门心思想灭世呢?原来是佛祖当年苦思如何可以解救世人,想啊想,就把自己想魔怔了,发现“有生皆苦”,人只要活着就归根结底会惨兮兮到不行,所以干脆把人们全都宰了,至少他们就不会再痛苦了。他把这个研究成果传授给大势至菩萨之后就涅磐往生去了,于是大势至菩萨就怀着救万民于水火的大慈悲心,变成了毁灭世界的反派大角……多么有趣的作者啊,看得我那叫一个心花怒放,听说猫腻写新书了,就急急忙忙赶过去看,于是这一看就是两年。
  第一章就是一个俗到不行的穿越……我是怀着对猫腻的信任才迈过这道坎的。之后追下来就容易得多了,高潮不断。能打动我的不仅仅是情节和人物,还有猫腻对这本书倾注的感情,和每天的更新最后的括号里洋洋得意的自夸文字。他很满意他刻画的人物,他很满意他渲染的气氛,他很满意他编的情节,他很满意他挖的坑──理所当然的,我更加满意很多……他会在括号里面的免费字数里写,“今天这章我自己很满意”,“今天的情节我写得很高兴”,“今天这段小爱情故事我写high了”,“今天这句台词我三个月前我就想好了,今天终于说出来了,可憋死我了”,“今天,雨中送陈萍萍,我不想多说”。
  这本书中的名字很有趣。主角叫范闲(犯嫌),字安之,就很汗。人家老爹叫范建(这已经是全书最败笔的名字了),有个弟弟叫范思辙(我上星期才无意中被老婆告知,这居然是个时尚品牌),有个妹妹叫范若若(我喜欢这名字!),生个女儿叫红豆饭,生个儿子叫范良(这个最赞,据典:闲妻良母……),娶个老婆叫林婉儿。林婉儿本来是个正常名字,但偏偏被封了个晨郡主,猫腻还描写人家厚嘴唇小圆脸胖胖的,还喜欢啃鸡腿。我一开始就觉得不对头,终于某一天猫腻自己写啊写的写秃噜了,写林依晨如何如何林依晨如何如何……上传了的章节被大家都看到了,他自己就很不好意思了:“还不许怪叔叔喜欢小胖萝莉么?”更不要提,庆历四年春,有个叫藤子京的被他发配到巴陵去了……
  当然感动更多的是人物,不过料想大家都没看过,也就不讲了。若论情节,从头到尾没有写散,很赞,这是多少字啊?4,000,000字?只多不少吧?很优秀的成绩。若论全文摘要,大结局中最后两段话倒是很符合全文的神韵:
  海风拂在他的面容上,拂散了他又准备露出来的微羞的笑容。沉默片刻后,他轻声说道:“我的人生,大概便是……既来之,则安之。”
  父女二人相视一笑,面朝大海,春暖花开。
  孤帆一叶澹州天,只在相携师友间。社稷岂独一姓重,乾坤谁怜万民悬?冲天黑骑三千里,孤苑白首二十年。莫道秋至残躯老,笑看英雄不等闲。


庆余年:章节摘录

  (第七卷 天子─★─第五章 断刀)
  “然后我想向陛下证明。如果……我是说如果,如果真地要一统天下,不见得……非要打仗,就算要打,也不见得一定是武斗。文攻也是可行,即便一定要武斗……能小打就小打。”
  范闲垂着头,低声说道:“我想什么?如果我说希望天下太平,没有战争……你会不会觉得这个想法很荒谬。”

  (第五卷 京华江南─★─第一百四十六章 一剑倾人楼)
  “浪花只开一时,但比千年石,并无甚不同……先生亦如此。”范闲狠狠盯着对方说道:“你如果是叶流云,你又怎么敢杀我?”
  ……
  范闲第一次,也是唯一一见看见叶流云,是他十二岁的那一年。
  那一年他伏在悬崖之上,眼中幻着奇彩,注视着悬崖下的半片孤舟,沙滩上的万点坑,那两个绝世的人和那一场一触即敛的强者战。
  一位是庆国的大宗师叶流云,一位是自己的叔。
  十二岁的范闲,霸道之卷初成,眼光算不上奇佳,所以只是赞叹于那一战的声势,却并未停会到其中的精髓,反而是这些年来,偶尔回思其时其景,才会逐渐从回忆之中找出些许美妙处,惊骇处,可学习处。
  回忆的越多,对于五竹叔与叶流云的绝世手段,便更加佩服。有时候他甚至会觉得叶流云那乘着半片孤舟踏海而去的身影还浮现在自己的脑中,那古意十足的歌声还回响在耳边。
  可是万万没有想到,这位庆国的大宗师,受万民敬仰的大人物,居然会在一间青楼的最顶层,成了自己必须要面对的人。
  范闲是这个世界上最怕死的人,所以对于自己单人可能面对的敌人,他都曾经做过充分的了解与分析。
  他算来算去,掂量了几番自己的实力与背景,在这个人间,最值得他警惧的人,应该是东夷城的四顾剑,最深不可测的,应该是北齐的苦荷,最麻烦的,当然是皇宫里的那几位。
  不过四顾剑虽然是个白痴,虽然可以毫不在乎地杀死自己。可是众人皆知,但凡白痴都是不喜欢出门到陌生地方去的。
  而深不可测地,喜欢吃人肉的苦修士苦荷大师,在亲爱的五竹叔亲自出手后。也终于被打落凡尘——一个能受伤的人,从感觉上说,就不是那么可怕了。
  至于庆国皇宫里地那几位,都有亲属关系,暂时不去考虑。
  范闲所真正警惧的,都是大宗师级别的人物,由此可见此子不是过于自信,就是有些自大,不过话说回来,以他的实力。再加上瞎子叔,实在也只需要考虑这些人。
  而在四大宗师之中,唯独对于叶流云。范闲一直不怎么担心。
  一来是少年时的记忆过于深刻,总觉得叶家这位老祖宗颇具流云清美之态,常年在世间旅行,乃是位真正的有行之人,心性疏朗可喜。不应该参合到人世间这些无趣的斗争之中。
  二来是京都叶家的状况,让范闲眼尖地看清楚,叶流云乃是位地地道道的有情之人。不然皇帝也无法维持双方之间的青衡,悬空庙一把阴火,烧得叶家丢盔弃甲,如此下作地手段,叶流云却能忍着不归京,自然是将叶家子侄的幸福与安危,叶氏家族的存续,看地比什么都重要。
  叶流云不停驻在京都,影响时势的平衡。皇帝也不会真地把叶家如何。这便是不能宣诸于口,但在皇权与叶流云的超世武力之间自然形成的一种默契。
  所以范闲怎么也想不明白,叶流云会因为君山会的事情出手,还会如此决然地杀到了自己地面前,用自己的生死来要胁自己。
  这不是愚蠢是什么?就算此次黑骑撤了回来,难道皇帝就不知道叶家与君山会之间的关系?这种平衡不一样是被打破了?
  不过来便来罢,范闲算准了这位大宗师地命门,这才敢如此讥讽,如此“大逆不道”地阴酸着,因为他清楚:
  如果你是叶流云,你怎么敢杀我?……
  范闲盯着笠帽之下那双静如秋水的眼睛,似乎想看出这位大宗师突至苏州的真正用意,内心深处甚至做好了准备,如果叶流云马上反问:“我怎么不敢杀你?”……自己马上冷冷地抛出自己行走江湖的大杀器以做说明。
  杀了我,五竹叔自然会杀了你们叶家所有人——这是一个很简单朴素的真理,叶流云绝对会相信,而且不会接受。
  ——————————
  “原来……当年你躲在悬崖上偷看。”
  出乎范闲的意料,叶流云根本没有接着范闲那句话说下去,只是缓缓将手中的剑重又插入剑鞘之中,看着他那张俊美的脸庞叹了口气。
  范闲心中一怔,面上却没有什么表情,兀自冷静着。
  “不明白?”叶流云问道。
  范闲真的不明白,所以点了点头,先前刻意扮出来地狞狠与成竹成胸顿时弱了少许。
  叶流云微笑说道:“如果你不在那崖上,怎么能念得出来那两句,怎么能知道我就是我,怎么能料定我知道你是他的你,怎么知道我就不敢杀你?”
  很复杂,听上去似乎很复杂,所以范闲真的有些晕了,好在他的启蒙比一般的正常人要早十几年,过了两次人生,关于逻辑之类的基础知识比旁人要扎实许多,自己在脑子里绕了几圆,终于绕清楚了叶流云的话。
  叶流云想表达的意思很简单——这个世界上,至少是如今,至少是江南,能认识他的人没有几个。
  而这个意思让范闲感到无比惊愕,庆国的大宗师,难道真的没有几个人认识?
  ……
  他下意识里放开手中紧紧握着的纸扇,唇角泛起一丝讥讽说道:“不要以为装酷就可以冒充我叔,不要以为戴着笠帽就能冒充苦荷光头,不要以为提把破剑就可以让别人相信你是四顾剑。”
  “你是叶流云,不管我认不认得出你来,你终究就是叶流云。”
  四顾剑的行踪是监察院监视的重中之重。叶流云根本没有可能冒充,所以这也是范闲很不理解的一点,叶流云弄这一出,是真地想和皇帝老子撕破脸?
  他嘲笑说道:“虽然四顾剑确实有些白痴。被咱们大庆人铸了无数个锅戴到头上,可是您这出戏也太不讲究了。”
  ……
  “我是谁并不重要。”叶流云冷漠地看着范闲,“我只是来提醒你一句,你下江南,江南死的人已经太多了。”
  范闲眯着双眼,毫不退缩地看着这位天地间仅存的四位超级强者之一,缓缓说道:“这世上哪有不死人就能达成的目标?”
  “你要达成什么目标?”
  “我是臣子……我地责任是保护皇上的利益不受丝毫损坏。”范闲眼中闪过一丝异色,微笑说道:“除此之外,我没有任何的想法。”
  “即便是死?”
  “不,我不会死。”
  叶流云沉默了下来。半晌之后说道:“你……母亲当年似乎不是这样的人。”
  范闲并不意外对方会提到自己的老妈,脸色却像挂了霜一般寒冷,冷冷应道:“不要用先母来压我。而且说起杀人,想必您也记得清楚,我母亲并不比我差。”
  “我说的是根骨与禀性。”叶流云的声音忽然沉了下去,“好杀之人,如何能手握大权?”
  将将因为叙旧这种事情稍显缓的楼中气氛。顿时又冷冰了起来,紧张了起来。
  “你在京都,有那些费心费神的可怜人替你操心。我且不论。”叶流云就这样直直地坐在桌旁,整个人像那东山之松一般倔耿而不屈,“你下江南,江南多事,多少人因为你的巧手善织而死去?”
  范闲眯着眼睛,心头无比恼怒,压低声音说道:“莫非我不下江南,这江南地人便不会死了?内库里的王八就不再是王八,明家一窝烂鼠就变成锦毛鼠?”
  他轻蔑笑道:“老人家。先前说过不要用先母的名义来压我,这时候再添一句,大义地名份对于我也没有什么效果。”
  叶流云面色不变,不知其喜怒,只听他静静说道:“杀袁梦一事,那宅中丫环仆妇你尽数点昏,看似犹有三分温柔,可这些昏迷之人,事后却被苏州府尽数擒去杀了灭口。”
  他温柔看着范闲的双眼,继续说道:“你离开的时候,应该就会猜到在监察院的压力下,那些无辜的人,只有死路一条。你不杀无辜,无辜因你而死。”
  “我只需要承担我应该承担地责任。”
  范闲嘴里用前世某教练的无耻话语淡淡应着,心里却是涌起大震骇!
  当然不是因为那些无辜的人因为自己死亡地缘故,虽然这也让他的心里稍微黯了一下。这种大震骇来自于叶流云的话语,那话语里似乎隐约透露出……自己入宅杀人的细节,对方清楚知晓。
  范闲盯着叶流云的眼睛,不知道这位大宗师究竟知道多少,如果对方知道自己已经学会了四顾剑,那便惨了……这是范闲的秘密之一,一旦被京都陛下知晓,整个监察院都会因为影子与悬空庙的事情被踩倒在地。
  对方完全可以用这个来要挟自己,但是看叶流云的神情,似乎并不知道细节。
  可是为什么叶流云诸事不提,却偏偏要提那个毫无轻重的袁梦?
  范闲眼中闪过一道厉光,马上回复平静,放弃了杀人灭口地念头——今日之状况较诸往时不同,往日自己为刀,世人为鱼肉,今日却是自己在砧板之上垂死挣扎,想杀死面前这个竹笠客,在五竹叔养伤期间,基本上是个不可能完成的任务。
  所以……范闲一拍桌面,大怒吼道:“成大事不拘小节!若不雷霆一击,仍让江南若往年一般,明家要害死多少人?那些海盗还要杀死多少人?国库的亏空你给我填回来?”
  不等叶流云回话,他那犯嫌的手指尖又伸了过去,极为大胆无礼地戳着叶流云的鼻子,骂道:“还有那个君山会?难道比我干净。你是什么身份地人……怎么好意思放低身段给他们做事,您是我朝宗师,不站在我这边,凭什么站在那边?”
  最后一句话巧妙一转。直指人心。
  叶流云眉头微皱,缓缓说道:“君山会,本就不是你想的那般。”
  范闲嘲笑道:“我当然明白,您是高高在上的大宗师,可是终究还是个人,总是需要享受的,行于天下?浪迹天涯倒是快活,可是若日晒雨淋着,哪里有半点潇洒感觉?每至天下一州一地,若有人应着。服侍着,崇拜着……您自然是快活了,而能用整个天下都供奉着您。除了那个君山会,还有谁能做到?”
  叶流云微笑望着他,似乎没有想到这个年轻人竟然能如此简单地瞧出自己与君山会地关系。
  事情本来就是这般简单,苦荷有北齐供奉,四顾剑有东夷城供奉。皇宫里那位自然由庆国供奉,可是堂堂叶流云呢?行于天下不归家,吹海上的风。抚东山的松,渡江游湖,所有的这些,总是需要有人打理,有人照应的。
  大宗师也要吃饭,也要住客栈,尤其是这种地位的人,肯定不喜欢一应俗套的马屁,愿意住在幽静的园子中。和一些隐于山野的孤客打交道?
  园子是要钱的,进山访友也是需要盘缠地,旅行,环游世界,其实是最奢侈的一种人生。
  总不能让堂堂大宗师去当车匪路霸。
  范闲的话还没有说完,他冷笑着说道:“可是您地孝子贤孙与君山会的关系就没这么简单了……要在本官的手下捞人,可不是那么简单。君山会为您保着这双娘们儿一般的手,难道您就打算用这双手为君山会把天穹撑着?”
  说话间,他的目光有意无意落在叶流云扶在桌旁地那双手上。
  那双手有若白玉,没有一丝皱纹,浑不似老人的手,而像是从不见阳光,只知深闺绣花鸟的姑娘家双手。
  这是许多年前,叶轻眉推五竹入庆国京都,五竹与叶流云第一场大战后,叶流云弃剑而散手大成地迹像,这么多年来,一直没有丝毫变化。
  叶流云听着范闲将自己的双手形容成娘们儿,静若秋水的双眸渐有沸腾之意。
  ……
  谈判的关键在于掌握对方的情绪,哪怕对方是一个高高在上的大宗师,所以范闲初一发现叶流云心中真正的火意将要勃发时,马上将话风一转,缓缓说道:“黑骑动手的时间,应该还有一会儿……如果您真是在意那园子里的孝子贤孙……是不是应该把周先生给我了?”
  叶流云似笑非笑地望着他,似乎是在嘲笑他,又似乎是在看着一个无知地黄口小儿:“这时候又愿意接受我的条件?”
  范闲微低眼帘,心里却是咯登一声,他本来想着,叶流云既然不怕辛苦提溜着君山会的帐房先生到了抱月楼,当然是打着用周先生换君山会里叶家后人的打算。
  难道,对方根本就没有这个意思?
  “我从来不接受被人胁迫下的……任何条件。”
  他抬起头来,宁静的双眸很有诚意地看着叶流云那张古拙的面容:“但这并不代表,我不愿意和一位值得尊敬的前辈达成某种协议。”
  叶流云听到此时,终于有些动容了,叹息着说道:“果然无耻……”
  范闲微笑道:“您以武力胁迫人,我以人命胁迫人,若说无耻,其实差不了太多。”
  叶流云缓缓地站了起来。
  范闲心头大凛,面色平静,复又打开那把已经汗湿变形的可怜扇子,胡乱摇着。
  叶流云看着他手中那把扇子,眼中闪过一丝笑意,看出来这个年轻人内心深处的真实紧张。
  ……
  “不要以为,你了解所有的事情,你可以控制所有的事情。”叶流云如此说道。
  “不然,总有一天。你会死的很可惜。”叶流云叹息道。
  “你是聪明人,但是不要过于聪明。”叶流云教训道。
  ……
  “你应该知道后面地事情怎样处理。”叶流云缓缓低头,任由那张竹笠帽遮住自己古拙的面容,倒提粗布缚住的长剑。走到栏边,反手提住周先生的衣领。
  此时地范闲终于感到了一丝无助与迷茫,堂堂叶流云,如果不是来送周帐房给自己,又怎么会屈尊与自己谈这么半天?
  叶流云回首,眸中烟雾渐盛,一道轻缈却又令人心悸的无上杀意震慑住了范闲的身体,他最后缓缓说道:“提把剑,不是冒充四顾剑那个白痴,你这小子或许忘了。我当年本来就是用剑的。”
  说话间,他缓缓抽出剑,雪亮锋芒此时并无一丝反光。仿似所有的光芒都被吸入那只稳定而洁白的手掌中。
  范闲眼帘一跳,集蓄心神,拼命将舌尖一咬,痛楚让自己清醒了少许。生死存恨之际,什么计谋斗智都是假的。他惶惶然将身后雪山处汹涌的霸道真气尽数逼了出来,运至双拳处,往前方一击!
  击在桌上。
  伴随着一声怪异地尖叫。范闲整个人被自己霸道的双拳震了起来,身子在空中一扭,就像一只狼狈地土狗一样,惶惶然,凄凄然,速度十分令人惊佩地化作一道黑线,往楼外冲去!
  ……
  范闲掠到了长街之上,整个人飘浮在空气中,双眼里却全是惊骇之色。即便此时,他依然能感觉到身后那一抹厉然绝杀的剑意在追缀着自己,似乎随时可能将自己斩成两截。
  所以他一拧身,一弹腿,张口吐血,倏然再次加速,在空中翻了三个筋斗,脚尖一踢对面楼子地青幡,借着那软弹之力,再化一道淡烟,落到了街面上。
  六名虎卫与监察院的剑手早已冲了过来,将他死死地护在了中间,层层叠叠,悍不畏死地做着人肉盾牌。
  不过一刹那,范闲便感觉自己的身周全部是人,根本看不到外面是什么情况,一丝感动一闪即过,全身复又晋入最灵敏地状态之中,随时准备逃命!
  ……
  然而长街之上一片安静,一片诡异的安静。
  范闲不敢妄动,躲在护卫们的身后,不知道过了多久,他才感到了一丝蹊跷,吩咐属下们让开了一道小缝。
  叶流云已经不在抱月楼中。
  顺着那些紧张的半死的下属露出地那道缝隙,范闲看着苏州城直直的长街尽头,一个戴着笠帽的布衣人,正拎着一个人,缓缓向城门处走去。
  虽是缓缓地走着,但对方似乎一步便有十数丈,渐渐远离。
  范闲咽了口唾沫,润了润火辣地嗓子,满脸疑惑地从人群里钻了出来,站在长街之上,看着远方叶流云的背影发呆。
  ……
  高达已经从对面楼下来,看到平安无事的提司大人,大喜过望,颤抖着声音说道:“大人,没事吧?”
  范闲将有些颤抖的双手藏在身后,强自平静说道:“能有什么事?”
  说话的时候,他看着叶流云的背影消失在城门之中。
  便在此时,谁也没有察觉到抱月楼顶楼,除了高达斩出的那个口子之外,渐渐又有了些新的变化。在范闲双拳击碎的桌砾之旁,粗大廊柱上近半人高地地方,那层厚厚的红色油漆忽然间裂开了一道口子。
  范闲逃命时扔下的那折扇却不知所踪。
  漆皮上的口子嗤的一声裂的更开,就像是一道凄惨的伤口,皮肤正往外翻着,露出里面的木质。
  然而……里面的实木也缓缓裂开了!
  裂痕深不见底,直似已经贯穿了这粗大的廊柱!
  其实不止这一根柱子,整座抱月楼顶楼的木柱、栏杆,厢壁、摆投、花几,沿着半人高的地方都开始生出一道裂口。裂口渐渐蔓延,渐渐拉伸,逐渐连成一体,就像是鬼斧神工在瞬间沿着那处画了一道墨线。
  只是这线不是用墨画地。是用剑画的。
  喀喇一声脆响,首先倾倒的,是摆在抱月楼顶楼一角的花盆架,花盆落在地板上,砸成粉碎。
  然后便是一声巨响。
  ……
  长街上早已清空,只有范闲与团团围住他地几十名亲信下属,听着声音,这些人们下意识抬头往右上方望去。
  然后所有的人都目瞪口呆,包括范闲在内也不例外,所有的人眼中都充满着震惊与恐惧。所有人的嘴巴都大张着,露出里面或完好洁白,或满是茶渍。或缺了几颗的牙齿,以至于那渐渐漫天弥起的灰尘木砾吹入他们的嘴中,他们也没有丝毫反应。
  抱月楼塌了!
  准确的说,应该是抱月楼的顶楼塌了。
  更准确的是说是,抱月楼顶楼地一半。此时正以一种绝决的姿态,按照完美的设计,整整齐齐地塌了下来。震起漫天灰尘!
  灰尘渐伏,所有人都看清楚了,抱月楼顶楼就像是被一柄天剑从中斩开一般,上面地全部塌陷,只留下半截整整齐齐的厢板与摆设。
  断的很整齐,断口很平滑,真的很像是一把大剑从中剖开一般。
  当然,此时所有人都清楚,这确实就是被一个“人”用一把剑剖开的。
  众人地心里重新浮现出最开始的那种感觉——这个人,不是人。
  ……

Two Letters

一份检讨
  我刚到纪委工作就犯了严重错误:前天我接到通知说中央电视台记者要来采访我市廉政先进典型,领导要我通知几个表现好的局长来接受访问。当时为了赶快下班,所以我通知各单位时说的略显草率了:“请你们局长明天到纪委来一趟。”
  没想到国土局长接到通知后大小便失禁,心脏病突发,不醒人事。财政局长自首了。交通局长当晚就失踪,据说已逃往加拿大。工商局长连夜杀死情妇,他以为她出卖了他。卫生局长服毒自杀……
  这惨痛的一切都是我工作方法简单造成的,我痛定思痛,深感内疚,特作检讨!

两封家信
  已经很多年了,国内的官场腐败仍是一个说不完的话题。最近看到网上比较两个父亲给儿女的信。一个来自本不该公开的家书,满纸肺腑之言;而另一封作为公开信,明显有作秀的成分。哪一个更有说服力呢?

徐其耀给儿子的信:为官的原则
  2000年10月8日,江苏省建设厅厅长徐其耀因贪污受贿2千余万元,被当地检察机关批捕。在侦查中,发现了徐与146名二奶的“日记”等证据,其中写给儿子的一封信,畅述做官心得,总结为官原则,具有较高的理论价值和应用价值,实为官场“金科玉律”,符合“付诸史馆”的标准;该信境界高超,观点鲜明,论述坦率朴实,语言精炼,警句迭出,振聋发聩,足可编入教科书当范文,聊以纠正当今假话空话套话充斥的文风。当然,该信有感而发,属即兴之作,逻辑上欠推敲,然白玉微瑕,瑕不掩瑜也!下为该信全文:

孩子:
  你的来信我已收到,对你在大学里的表现,我很欣慰,你要再接再厉。既然你选择了一定要走仕途这条路,你就一定要把我下面的劝告铭记在心:

  1.不要追求真理,不要探询事物的本来面目。
  把探索真理这这类事情让知识分子去做吧,这是他们的事情。要牢牢记住这样的信条:对自己有利的,就是正确的。实在把握不了,可简化为:上级领导提倡的就是正确的。

  2.不但要学会说假话,更要善于说假话。
  要把说假话当成一个习惯,不,当成事业,说到自己也相信的程度。妓女和做官是最相似的职业,只不过做官出卖的是嘴。记住,做官以后你的嘴不仅仅属于你自己的,说什么要根据需要。

  3.要有文凭,但不要真有知识,真有知识会害了你。
  有了知识你就会独立思考,而独立思考是从政的大忌。别看现在的领导都是硕士博士,那都是假的。有的人博士毕业就去应招公务员走向仕途,那是他从读书的那天起就没想研究学问,肯定不学无术。记住,真博士是永远做不了官的。(评注:毛泽东名言“知识越多越反动”的今日解读。)

  4.做官的目的是什么?是利益。
  要不知疲倦地攫取各种利益。有人现在把这叫腐败。你不但要明确的把攫取各种利益作为当官的目的,而且要作为唯一的目的。你的领导提拔你,是因为你能给他带来利益;你的下属服从你,是因为你能给他带来利益;你周围的同僚朋友关照你,是因为你能给他带来利益。你自己可以不要,但别人的你必须给。记住,攫取利益这个目的一模糊,你就离失败不远了。

  5.必须把会做人放在首位,然后才是会做事。
  这里的做人做事你可别理解为德才兼备的意思。这里说的做人,就是处关系。做事是实际工作,这点会不会都无所谓。做人就是把自己作为一个点编织到上下左右的网中,成为这个网的一部分。记住,现在说谁工作能力强,一点都不是说他做事能力强,而是指做人能力强。呵呵,你看那些把能力理解为做事的人,有好日子过才怪。

  6.我们的社会无论外表怎样变化,其实质都是农民社会。
  谁迎合了农民谁就会成功。我们周围的人无论外表是什么,骨子里都是农民。农民的特点是目光短浅,注重眼前利益。所以你做事的方式方法必须具有农民特点,要搞短期效益,要鼠目寸光。一旦你把眼光放远,你就不属于这个群体了,后果可想而知。要多学习封建的那一套,比如拜个把兄弟什么的,这都不过分。

  7.要相信拍马是一种高级艺术。
  千万不要以为拍马只要豁出脸皮就行,豁得出去的女人多了,可傍上大款的或把自己卖个好价钱的是极少数,大部分还是做了低层的三陪小姐。这和拍马是一样的道理。拍马就是为了得到上级的赏识。在人治的社会里,上级的赏识是升官的唯一途径,别的都是形式,这一点不可不察。(评注:自古以来,中国是个熟人社会、关系社会、陋规社会,而不是信用社会、商业社会、法规社会。而现有体制下为这些封建主义的东西,找到了生长的土壤。)

  8.所有的法律法规、政策制度都不是必须严格遵守的,确切地说,执行起来都是可以变通的。
  法律法规、政策制度的制订者从没想到要用这些来约束自己,而是想约束他人。但你要知道,这些不是人人都可以违反的。什么时候坚决遵守,什么时候偷偷违反,让谁违反,要审势而定,否则宽严皆误。(评注:说的是一回事,做的又是一回事——这就是我们的政治游戏的潜规则。)

  以上这些都是做官的原则。现在要仔细想想,如果你真能逐条做到,你就能一帆风顺,如果感觉力不从心,就马上另外选择职业吧。


奥巴马给女儿的信:梦想不受限制无事不能成就
  总统当选人奥巴马在即将上任之际,写了封感性十足的公开信给2个尚未成年的女儿,为这2年来多半时间没能陪在她们身边致以歉意,并为自己为何选择迈向白宫之路做了番解释。这封信发表在美国大观杂志(Parade Magazine)上,全文翻译如下:

亲爱的玛莉亚和莎夏:
  我知道这2年你们俩随我一路竞选都有过不少乐子,野餐、游行、逛州博览会,吃了各种或许我和你妈不该让你们吃的垃圾食物。然而我也知道,你们俩和你妈的日子,有时候并不惬意。新来的小狗虽然令你们兴奋,却无法弥补我们不在一起的所有时光。我明白这2年我错过的太多了,今天我要再向你们说说为何我决定带领我们一家走上这趟旅程。

  发掘潜能挑战自我
  当我还年轻的时候,我认为生活就该绕着我转:我如何在这世上得心应手,成功立业,得到我想要的。后来,你们俩进入了我的世界,带来的种种好奇、淘气和微笑,总能填满我的心,照亮我的日子。突然之间,我为自己谱写的伟大计划显得不再那么重要了。我很快便发现,我在你们生命中看到的快乐,就是我自己生命中最大的快乐。而我也同时体认到,如果我不能确保你们此生能够拥有追求幸福和自我实现的一切机会,我自己的生命也没多大价值。总而言之,我的女儿,这就是我竞选总统的原因:我要让你们俩和这个国家的每一个孩子,都能拥有我想要给他们的东西。
  我要让所有儿童都在能够发掘他们潜能的学校就读;这些学校要能挑战他们,激励他们,并灌输他们对身处的这个世界的好奇心。我要他们有机会上大学,那怕他们的父母并不富有。而且,我要他们能找到好的工作:薪酬高还附带健康保险的工作,让他们有时间陪孩子、并且能带着尊严退休的工作。

  责任伴随权利而来
  我要大家向发现的极限挑战,让你在有生之年能够看见改善我们生活、使这个行星更干净更安全的新科技和发明。我也要大家向自己的人际界限挑战,跨越使我们看不到对方长处的种族、地域、性别和宗教藩篱。有时候为了保护我们的国家,我们不得不把青年男女派到战场或其他危险的地方,然而当我们这么做的时候,我要确保师出有名,我们尽了全力以和平方式化解与他人的争执,也想尽了一切办法保障男女官兵的安全。我要每个孩子都明白,这些勇敢的美国人在战场上捍卫的福祉是无法平白得到的:在享有作为这个国家公民的伟大特权之际,重责大任也随之而来。
  这正是我在你们这年纪时,外婆想要教我的功课,她把独立宣言开头几行念给我听,告诉我有一些男女为了争取平等挺身而出游行抗议,因为他们认为2个世纪前白纸黑字写下来的这些句子,不应只是空话。
  她让我了解到,美国所以伟大,不是因为它完美,而是因为我们可以不断让它变得更好,而让它更好的未竟工作,就落在我们每个人的身上。这是我们交给孩子们的责任,每过一代,美国就更接近我们的理想。

  具慈悲心坚持理想
  我希望你们俩都愿接下这个工作,看到不对的事要想办法改正,努力帮助别人获得你们有过的机会。这并非只因国家给了我们一家这么多,你们也当有所回馈,虽然你们的确有这个义务,而是因为你们对自己负有义务。因为,唯有在把你的马车套在更大的东西上时,你才会明白自己真正的潜能有多大。

  这些是我想要让你们得到的东西:在一个梦想不受限制、无事不能成就的世界中长大,长成具慈悲心、坚持理想,能帮忙打造这样一个世界的女性。我要每个孩子都有和你们一样的机会,去学习、梦想、成长、发展。这就是我带领我们一家展开这趟大冒险的原因。
  我深以你俩为荣,你们永远不会明白我有多爱你们,在我们准备一同在白宫开始新生活之际,我没有一天不为你们的忍耐、沉稳、明理和幽默而心存感激。

爱你们的老爹
2009.01.16