我的家乡 (梁煜)

By , February 26, 2016 5:12 am

家乡是一个相对的概念。如果你生活在北京,你可以说你的家乡在海南;如果你生活在海南,你可说你的家乡在文昌;如果你生活在文昌,你可以说你的家乡在会文。我出生在会文,但是我从来不认为那里是我的家乡,一直到现在回去都会有一种陌生感,我认为我的家乡在重兴镇甘村大队忠厚岭村——我外婆家。

因为那个地方见证过我的成长,那块土地分享过我的喜怒哀乐,我与生活在那块土地上的人发生过非常真切的联系。
 
上学

 

我一满月,妈妈就把我带回外婆家,以后基本上就在外婆家生活了,顺理成章的,我就有机会在外婆家上学了。外婆家所在的甘村大队比较大,方圆十里村庄很多,为了方便孩子们上学读书,乡里除了设有总校之外,还设有两个分校,分校只设一、二年级,孩子们分校毕业后才能上总校。我读书比较早成绩也不坏没有留级过,所以在同龄的孩子还在分校读书的时候我已经跟着比我大两三岁的小伙伴们上总校了。总校离我婆家大概有两三公里的路程,步行大概要半个小时,每天早上五点多就要起床上学。

冬天的早晨,天还没有亮,村庄里的屋顶上开始飘起炊烟,各种的呼喊声此起彼伏:“萍啊,茹啊,去学校啦——”大家集中在一起之后开始步行去学校。那时候家里也没有多余的手电筒,照明的工具是我们自制的火把。简单一点的就是一把干枯了的椰子叶,反正文昌号称“椰乡”,这种制作火把的原材料随处可见,高级一点的砍一截竹子,在竹筒里倒上煤油,竹筒里塞一条破布,竹筒口处堵紧,一个简易的火把就做成了。一队衣衫单薄的少年嘻嘻哈哈你追我赶,不一会儿就到了学校了。那时候好像也不怕冷,路的两边没有人家,一路都是黑魆魆的树丛,但是我们也不觉得害怕,父母也从不担心我们受到侵害或是被拐,不像现在,孩子下楼玩玩我们都不放心。社会经济的发展,物质越来越丰富,为什么我们反倒失去了幸福感中很重要的一部分——安全感?

那个时候的孩子也没有早餐的概念,一般都是空着肚子去上学,最好的就是妈妈把昨天晚上的剩饭炒一炒就是早餐了。外婆爱我,每天早上都会起来为我做一碗炒饭。注意,是炒饭,不是蛋炒饭,加一点油、盐和酱油,但是在我的眼里已经是无比的美味了。如果时间还早,就在家里把炒饭吃完再去上学,如果时间来不及了,就端着饭碗边走边吃,吃完把饭碗往某个草丛里一丢,放学的时候再顺便把那个已经爬满蚂蚁的饭碗捡回来就是了。那时候我是上总校的孩子中年龄最小的,伙伴对我的关照会多一点,早起集中上学的时候一定不会漏掉我。可是也有意外的时候,有一天早上外婆估计是起得太早了——三十年前钟表是个奢侈品,早上起床只能以公鸡打鸣为准。我吃完炒饭好久还没听到小伙伴的呼喊,外婆以为他们已经走了,赶紧自己送我去上学。一轮寒月西斜,朦胧的月色下祖孙二人打着一个手电筒深一脚浅一脚地走在乡间的小路上。远处的村庄不时传来一两声的狗叫与路边的草丛中的虫鸣交织在一起,路上总有一种安静得有点凄凉的意味,这是平时和小伙伴一路喧哗时没有的感觉,但有外婆在身边,攥着外婆的手我的心里分外的笃定——如今外婆已经年近九十,回去看她的时候她已经不能攥着我的手送我到村口等车了,因为腿脚疼痛。可是她就那样攥着我的手走了三十多年呢,从我牙牙学语到年近不惑,我多想攥着她的手一直走下去……

白天上学方便一点,可以骑自行车,可是那时候我还不会骑自行车,外婆就会把家里二十八寸的自行车“大巴”推出来,找一个小伙伴骑着带我去。那辆自行车真高呀,个子高一点的伙伴还好,可以坐在坐垫上扭着屁股骑,矮一点的伙伴只能从三脚架里骑,奇怪的是小伙伴们的骑车技术都很好,我一次都没摔过。学校的老师中有一个是跟我们同村的,放学的时候他也会跟我们一起骑车回家,老师的自行车一经过,伙伴们的自行车队就会自动地往路边靠让老师先走。最近隔了一个月多没有回外婆家,再回外婆家的时候惊闻当年经常骑车带我上学的伙伴已经因为脑溢血不治,留下了三个年幼的孩子,怀着难以名状的心情,我去看了他的三个孩子……

 

劳动

 

在农村生活过的孩子,总会干过各种农活儿,我在这方面比较愚笨,用老妈的话来形容就是“撒出去让你抢你都抢不过人家”,除了读书比别的孩子强一点,我在老妈的眼里基本就是一无是处。虽然愚笨,——别人七岁去放牛,我九岁去放牛;别人八岁会生火烧饭,我十岁会生火烧饭;别人十二岁会割稻谷,我十四岁会割稻谷——总而言之就是比别人慢半拍,但是活儿总是要干的。

暑假和小伙伴们去放牛,炎炎烈日下牛儿在山坡上悠闲地吃草,几个小屁孩闲得无聊,爬到公路边的一颗大树上对着公路上来来往往的车大声喊道:“司机——,你的车轮子在转,快停车看看呀——”今天想起来觉得伙伴们挺聪明的,这调皮话居然也能想出来。山坡上的野果不少,我们最喜欢的是当然是桃金娘(文昌俗话称“大尼”“罗尼”),“罗尼”的花期在每年的农历四月份左右,花朵一开始是艳丽的玫瑰红色,慢慢地颜色会变淡,到接近白色的时候就枯萎飘零了,同株开花有先有后,红白相间,绚丽多彩,灿若云霞。从开花到果子成熟大概是两个多月的时间,文昌民间有谚语“六月六,罗尼黑墨墨;七月来,罗尼不见个”。其果先青而黄,黄而赤,赤而紫,一两米高的树上挂果累累,像一个个缩小版的酒杯,果中有芯,很象一条虫子,芯外多籽。“罗尼”熟到变成紫黑色的时候最好吃,味道异常甜美,我们经常吃到舌头牙齿都被染成紫黑色,当然,享受美味的同时是要付出代价的——次日大便困难,但是这并不影响我们的贪吃。牛儿在山坡上悠闲地吃草的时候小伙伴们满山坡跑,发现有挂果多的就大喊:“快来,这里有一棵!”我们大家就一窝蜂跑过去,有些树高了点我们够不着摘果子,聪明的小伙伴抓住树干一阵猛摇,我们就可以坐享其成了。中午时分,牛儿吃饱了我们也吃得差不多了,然后骑着牛悠悠地回家去。关于“罗尼”的回忆最深刻的是我手捧一本《红楼梦》坐在门槛上,脚边放着一个小箩筐,箩筐中装满外婆刚刚摘回来的熟透的“罗尼”,我一边吃一边读,外婆在一旁手摇蒲扇笑眯眯地看着我,至今都认为这是读《红楼梦》的最佳境界——最爱的书,最爱的果子,最爱的人!如今这已是人生不可再得的享受,当年读过的书还在,但是外婆年事已高,不可能再到山坡上去摘“罗尼”了,再说那曾经满山坡的“罗尼”已经随着村民的开荒种地淹没在推土机的铁铲之下。

外婆家里种了很多胡椒,这是外婆家里的主要的经济来源。管理胡椒需要的工序很多,而且是每年都要重复做。其中我记得的最繁重的是施肥和挖沟——在种胡椒的那块地四周挖上宽五十公分深一百公分的地沟来隔断其他的树根,防止其他的树根伸过来和胡椒争抢肥料,如果周围的树很大,沟还要挖得更宽更深一些。干这样的农活是需要很大的力气的,一般都是年轻力壮的男劳力来做。可是这时候外公已经去世,外婆没有儿子,她带着三个外孙——十四岁的我、十二岁的妹妹、十岁的表弟在屋子后面的那块胡椒地里挖沟。我们三个配合,一个负责用锄头挖,一个用铲子把泥土铲到畚箕里面,一个把畚箕里的土倒在胡椒地里,如此轮换。但是我们三个毕竟力气太小了,沟挖得越来越小也越来越浅。外婆没有批评我们,她一个人负责上述的三项工作,一边擦汗一边对我们说:“侬(文昌话里长辈对晚辈的爱称,也是晚辈对长辈的谦称)慢慢来,累了就歇歇。”那是年纪小没有太多的想法,累了就从沟里爬上来歇息,歇够了又跳到沟里继续挖。今天想起来,外婆看着正在锄地的我们心中有没有涌起无人可靠的凉薄。收工之后回家外婆做饭给我们吃,饭桌上必有一盘外婆自己种的空心菜,配上外婆自己用橘子和蒜头做的调料,味道极好,空心菜的这种吃法是外婆家独有的,在别处再也没有吃到过。

我还帮外婆割过稻谷,那时候我已经考上文昌中学读初中了。文昌中学是文昌最有威望的学校,在文昌人民心目中有着至高无上的地位,谁家的孩子要考上文昌中学那是很令父母骄傲的事情。考上文昌中学,我差不多就以“会读书”闻名甘村乡了。那年夏天,当我挽着裤管弯着腰在田里以慢别人两拍的节奏在田里割稻谷的时候,一个邻居家的“伯爹”(大伯)悄悄地站在我身旁的田埂上看着我,我抬起头看到他的时候他笑着说:“侬握笔的手会不会握镰刀呢?侬这是读书的料不是种田的料啦,好好读书考大学,咱们这一带都多少年没有出过大学生了。”——时至今日,我都没能为这些当年对我寄托过殷切期望的乡亲们做过什么,惭愧!

 

送灯

 

正月十五元宵节,海南文昌民间有一种风俗——送灯。

送灯,即把代表自己后代的大灯送到祖庙,有继传香火和让祖宗保佑子孙平安顺利的意思。每年正月十五, 元宵节来临,家家户户都要“送灯”,杀鸡宰羊,祭拜祖先,然后敲锣打鼓举行送灯仪式。文昌口音中“灯”和“丁”是谐音,送灯的习俗表示送去“人丁兴旺,家景兴隆”之意。这挂灯笼也十分讲究,各家各户所挂彩灯,是以当年这一家所生几个男孩为准,一般是生多少男孩挂多少彩灯。所以,外面的人一看,哪家挂了多少个彩灯,就知道这家有几个男孩。到了晚上,村里还请来戏班,演木偶戏或者是唱琼剧,敲锣打鼓,热闹非凡。

祭拜祖先之后,各家各户将纸灯笼集中堆放,一起点燃。在震耳欲聋的鞭炮声中,一切都化为灰烬,但乡亲们的心愿和美好祝福已经燃起。家家户户用祖庙里的灯火点燃各自的煤油灯,带上灯火回家,预示把美好的祝愿和平安吉祥带回家,保佑子孙后代平安幸福。

但是这送灯习俗在细节上各地并不同,送灯的时间各地也不统一,并不都是在十五元宵节,一般从年初八到十五元宵节这几天中的一天都可以。每年的正月十一,是外婆家送灯的日子,这是这个小村庄除了正月初一之外的一个大日子,白天杀鸡宰鹅招待客人,晚上送花灯唱“大台戏”,邻村的戏迷必定不会错过一定过来一饱眼福,一时间村里人声鼎沸。外婆也是一个标准的戏迷,我对琼剧没什么兴趣,但每年都会陪她看,就是为了陪她。外公去世得早,两个姨妈嫁人之后外婆就孤单了,别人子孙满堂欢声笑语过年外婆却形单影只一个人,外孙就成了她唯一的慰藉,大概是从八、九岁开始我就每年都在外婆家过年,一放寒假我就回去,一直待到正月十一送完花灯之后才回到爸爸妈妈身边。每年的琼剧唱的是什么我没有什么印象,曲终人散之后就挽着外婆的手回家,穿过那条铺着青石板的小巷,薄薄的月光照下来,不知怎的,心底总会生出一层寒意,也就在这一来二去的行走中,外婆佝偻了腰身,我增长了年岁。

但从2007年之后,我再也没有回外婆家过年、“送灯”、陪外婆看琼剧,因为,我结婚了,陪外婆过了二十个春节之后我有了自己的家,自己的孩子。好在还有妹妹、表妹,于是另外一个外孙女替代我回外婆家陪外婆过年、“送灯”、看琼剧,可是今年是外婆一个人过年、“送灯”、看琼剧,因为妹妹、表妹也都结婚了……外婆日见衰老,记忆力也不太好,我不知道今夜她一个人看完琼剧之后,走过那条铺着青石板的小巷的时候会想些什么,我只知道,今年的正月十一,外婆家“送灯”的日子,我在家里带着自己的孩子想起远方的她,泪不可抑……

外婆家的厨房后面有一座破败的房子,房顶早已坍塌,只剩下兀立的墙壁,房子里长满了不知名的灌木,斑驳的墙壁上爬满了爬山虎,孩童时代的我对这座没有主人的房子既好奇又恐惧,据外婆说是家族中一位叔公的房子,他在战乱中抛妻弃子随军队去了台湾,唯一的孩子死于六十年代的大饥荒,妻子迫于无奈改嫁他人,只有这荒凉的房子在岁月中诉说着无限荒凉的人生的故事。那时候的我怎么也不会想到这个给了我太多快乐和温暖的外婆的家最后也会长满不知名的灌木,爬满爬山虎,我生命中一段很重要的回忆将无处安放。遥想多年以后,我一个人站在荒芜的墙壁前面缅怀我无法荒芜的记忆,然后我的老外婆在时光中颤颤巍巍地向我走来……

家乡家乡,那个地方见证过你的成长,那块土地分享过你的喜怒哀乐,你与生活在那块土地上的人发生过非常真切的联系,在你牙牙学语的时候他们逗过你,在你摇摇学步的时候他们扶过你,在你取得好成绩的时候他们赞美过你,同样,多年以后当你满身疲惫满心苍凉的时候她也能宽容你抚慰你。经常会想我的女儿会有很清晰的家乡的概念吗?哪里是她真正的家乡呢?我们现在住的文昌中学是她的家乡吗?这里没有她的乡亲,只有妈妈的同事。公坡镇那个小山村是她的家乡吗?她从未赤脚踩过那里的土地,她没有在上学的路上抓过蜻蜓,没有在田野的沟渠里捞过“双曼鱼”,她和那个小山村的联系只是一年几次的来去匆匆而已……

乡土,乡亲,脱离了泥土和乡亲,我们可能就没有了真正意义上的家乡。

 

【作者简介】

梁煜,女,繁华世界里一介平凡的教师。岁月流金,在学生的成长中得到些许的安慰,在文字的熏染中得到不少的快乐。认真读书,放慢脚步倾听那些来自或晶莹剔透或雄浑厚德或孤傲不阿的心灵的声音;平淡生活,观赏春花流连秋月只为心灵的充实和丰盈。

文昌民居·祖屋(陈晓洁)

By , February 16, 2016 4:59 pm

5.pic_hd

我的老家,准确说是祖父母的家,父亲长大的地方,叫霞场村。这是一个异常美丽的村庄。一条入海的大河两岸,生长着数百年的红树林,村庄依着蜿蜒的河流和富饶的红树林生生不息。现在村庄已经成了一个旅游点,“八门湾红树林绿道”之“霞场服务区”。村里的道路变了,房子也变了,很多坡顶旧屋变成了平顶小楼。童年时在乡下老家度过了金色的寒暑假,长大后越回得少,这个海湾边村庄的旧模样越发深刻在记忆里。这几年回去,我家那一排瓦房祖屋越发显得不起眼了。但再坐回通透敞亮的大堂屋中时,我想能永远地保存它多好。

文昌民居大格局的特点,是“门当户对”。同宗或亲兄弟的正屋,竖着连成几进房院,每户一进。每一进正屋厅堂(文昌话称为“教厅”)均开四扇前门两扇后门,每一进的前门正对上一进的后门。正月初一到十五,每户正屋教厅中间的八仙桌上,香烛摇红,柚桔飘香,贡果琳琅,满堂喜乐祥和。

文昌民居,在我自己琢磨来,应该是有些近代中西结合的风格,兼有本土的特色。从大格局上,祖屋沿承中式风格,正屋是一间大瓦房,正屋“一厅四室”,中间是厅堂,厅堂两侧是卧房。厅堂摆八仙桌,八仙桌正上方,距离屋顶1米多的地方会横架阁板,俗称“公阁”,摆放祖宗牌位,因为海南话中祖宗就是“公”,祖屋又称“祖公屋”。正屋外面的左侧或右侧,一般会建一排厢房,厢房回廊连着入院大门和门厅,厨房、餐厅、杂物布草都在厢房中。

文昌民居在装饰上,有一些南洋的风格,或者是南洋转舶来的地中海风格。这得缘于文昌是一个著名的侨乡,祖父母那辈人及往上,应该家家户户都有人下过南洋吧。正屋厅堂内墙上,沿着屋顶坡线会有一排壁画。正屋的窗户,户内门上方有半圆拱形装饰,画有鲜艳的壁画。还有,厢房的回廊,一排白色拱形通门不正像当下商品房中舶来的地中海元素吗。

祖屋已经起了20来年了,那些壁画还精致依旧,鲜艳如昨。我不清楚这到底用的是什么颜料。爸爸说,当年画这屋里屋外全部壁画才花了600多元。画匠是民间高手,画题除了花鸟虫鱼,更有本地风光的椰风海韵,水天一色,渔舟唱晚,他如今已做古。在大家都贴印花瓷片的年代,这门手艺也在慢慢古化了。

当然文昌民居离不开本地特产。正屋是一间倒V对称坡屋顶的大瓦房,在门,窗,屋顶梁架木料使用上,传统喜用当地称为“黑岩”的木料。黑岩并不见传于中国的名贵红木,但确实是文昌人的心头好,据传是造船用木,耐潮耐湿,造屋可耐百年之久。可惜现在黑岩和木工价格都飞涨,造黑岩瓦房大正屋是奢侈之事了,所以很多人新造祖屋,都是钢筋水泥平顶房。

文昌是椰乡,不仅因为文昌盛产椰子,更因为文昌人对椰子的感情。民居正屋厅堂坡屋顶的正中间,必须有一根装饰大梁。大梁是一根又直又圆,抛光色亮的椰树主干。选这一棵椰树还有讲究,如果是买来的树,树的主人家要得是子孙满堂的和谐之家。“升梁”是造新屋最重要的事,梁下挂俩装了米的红布袋子,寓意“生粮”。

祖屋自从祖父母过世后,再没有人常住。但我们对它,就像对祖宗一样地供养起来,爸爸三年五载地会将墙壁门窗粉漆一番。穿梭在红树林中的游客渐渐多了,我和姐姐妹妹还开玩笑幻想着将来老了退休了,回到村庄,在老屋给游客开一间花园咖啡馆,就像欧洲小镇上的老房子一样,让它开满鲜花,古老又常青。但是今年回去,却听到一个不好的消息,据说政府要将这一片村庄拆迁了改造成湿地公园。虽然这只是还在道听途说阶段的消息,但在这个大拆大建的大时代,这种消息还是像飘过来一丝乌云一样让人隐隐担忧。

春节时候,一对外地朋友来海南旅游,从三亚沿海岸玩到文昌来。想着他们肯定对大海景都审美疲劳了,我带他们去了我的老家霞场。游览了美丽的红树林河流后,我带大家回我家老屋上洗手间,没想到这短短十几分钟的驻足,令外地朋友大大赞叹这才是“深度游”啊,因为看到了这样特色的当地民居。他们说你一定要保留好它呀,我的答应中有一点点的无奈。

离开家乡的路上,年轻人的车里放着一个翻唱版本的《鹿港小镇》。罗大佑写着:“听说他们挖走了家乡的红砖,砌上了水泥墙。家乡的人们得到他们想要的,却又失去他们拥有的……”和我半毛关系都没有的鹿港小镇,此刻令我十分沉默。这首30多年前的流行歌曲,今天还会重复同样的故事吗?我想……不会的吧。但愿不会的。我的祖屋,会开满鲜花,古老又常青。

陈晓洁 2016丙申年猴年 正月初九 改

10.pic_hd

6.pic_hd

7.pic_hd

11.pic_hd

清泉老师的书店(陈晓洁)

By , February 16, 2016 4:50 pm

文昌中学正北大门往东拐角有一家清泉书店,是林清泉老师开的书店。

前一阵子,我和几个发小老友在微信群里聊孩子教育的问题,从孩子们的学习阅读回想起我们少年儿童时的学习读书事。我突然有所感触,说我对于阅读严肃文学的一点爱好,要感谢吴亚利老师和林清泉老师。亚利老师是我们多年的班主任兼语文老师,他的要求和影响自然勿需多说,发小问另外一个为什么是教英语的清泉老师?我说因为清泉老师开了一家好书店。

上个世纪90年代初,我在上初中,家里少量的古典书和那些救国爱党文学都被消费得差不多了。没有清泉书店之前,文昌这个县城真没有地方能买到好看的书(起码我不知道有)。文中校园北门前的文中路安静少人,马路东侧有一家新华书店,马路西侧有中国邮政,邮局门口有一个报刊亭。我家住在文中操场边上,这家新华书店和报刊亭,就是我初中时候购买新文字流连的地方了。

那时候的新华书店已经没落得不成样子,因为实在没有别的地方提供书籍买卖,所以每一学期开学我还是不死心地要照例去新华书店看看,希望能买到一些有用的学习辅导书。可惜在新华书店实在没有过令人惊喜的购书体验,书店有些港台的小言情,摆着蒙灰的烹饪百科,在这个热带海岛上兜售各种毛衣织法大全。报刊亭则好些了,起码会按时回来我喜欢的期刊《少年文艺》和《故事会》。我记得有一个暑假,我几乎每天都要去守报刊亭等它十点回货开门,看看有没有回来一些我喜欢的读物。是的,在像野草一样疯长的年龄,只要是新的文字和完整的故事就能吸引我们。

高一有个小半学期,清泉老师来代教我们班的英语。那时候清泉老师还是个年轻英俊的小伙子,高瘦个子带着眼镜和一副傲娇脸。可能是代课吧,他教学的心态很放松要求并不高,学生们对他也不警戒。有一次我们几个学生在做壁报,清泉老师也来到教室,大家放下活儿聊起天来。年轻的老师给我们各人指点了人生江山后,说他自己的心愿就是开一家书店,提供经典文学,顺便卖点辅导书。此话得到同学们的热烈响应,纷纷表示太需要了。然后不出两个月吧,清泉书店果然开了起来,看来老师早已付诸行动绸缪妥当。

高中仍然是亚利老师教语文,他要求假期必须读世界名著,开学要把读书清单和心得写进日记里交上来。开学期间学校图书馆尚且有些书可借,放了羊的假期里,图书资源直接进入枯水期。清泉书店开得真是好及时。回想到这里,我都不禁莞尔,两位老师您俩是商量好的吗?

那是一段美好的时光。校门前的文中路两侧,一树一树的紫荆花姹紫嫣红地开着;大白天里人车稀少的静谧,衬着青青校园的朝气。新华书店和报刊亭依然也在着,更加旧旧的没人光顾,但它们安静地呆在紫荆花树后面,竟也为这段路添了几分学院气息。清泉书店的位置很理想,在文中校园门口东拐角,往东叉路去不远就是华侨中学和文昌师范学校。清泉书店的书品跟新华书店报刊亭相比当然是云泥之别,在文中路显得熠熠生辉。书店生意应该是很好的,起码我那不多的零花钱大多都贡献给林清泉老师了。换回来一套套的书和着纸墨清香,以及可以成天懒在床上读书的日子,成了回忆中淡淡的幸福,能穿越岁月,温暖至今。

2014年的春节,时隔多年回去看望老班主任,我才知道清泉老师因重疾英年早逝了。除了沉默和叹息,我一下子不知道怎样来感怀这位老师。他只是教了我半个学期的代课老师,那时候他年轻,没有桃李芬芳的资本和德高望重的师表,他就是校园里一个有点个性的普通青年教师,也听到有人评论他“有经济头脑”,所幸他没有在学校旁开一间电玩室或是一个网吧,而是一家书店。我们买着他的书,轻松地上着他要求不高的课,消遣他监考时也能睡着的嗜睡故事。我毕业以后,和校友同学师弟妹聊天几乎无人提及他。但当我说起我在清泉书店买过的书,发小老友们都纷纷记忆开闸,无不感念这家书店带来的读书记忆。我想清泉老师可以欣慰安眠吧,从文中北门走向远方的一茬一茬的学生,都会因为清泉书店记得您。

文中路也变了,变化得悄然又突然,以至于我虽然年年走过,都不能回忆起一条落英缤纷的安静马路,在变成俗闹嘈杂的县城商业街之间,过渡的状态是怎么样的。栽种多年的紫荆花树一棵都没有了,全换了易长易折冠型粗俗的常绿树,新华书店倒是顾客盈门了因为换成了肯德基,报刊亭早已消失那里有流动商贩在卖盐焗鸡(报刊亭的盐焗鸡仍然比新华书店的肯德基更得我心)。马路西侧的大超市和茶馆大棚车水马龙,马路东侧一家糟粕醋粉店的名声大大超过旁边的党校大门。清泉书店还在那个拐角,显得陈旧且黯淡。在当当网和亚马逊上动动鼠标,次日就能收到一大箱书的时代,真不知道清泉书店还够能开多久。

2016年的春节前,爸爸妈妈搬了新居,搬离居住了三十来年的文中坡。日后回家恐怕是不会有太多时间去文中路溜达了,也许匆匆路过都要刻意而为。在发出此文前,我曾想专门再去文中路清泉书店一趟,可怜俗事缠身竟不能成,希望来年吧,希望它一直在。春节回到老居,我从书架里找到当年在清泉书店买的书,都是软皮平装的书,好多书高中时读后都想将来学业不紧张了要再读一遍的,然而后来并没有再读。我将少年的书包装了,托爸爸帮我寄到深圳。其他的,那一路的紫荆花,那一间街角书店,那些年轻的脸庞,我记下了。你们曾经,不,是永远,如石上清泉,如松间明月。

陈晓洁 2016丙申年猴年 正月初九

Getting Started with AWS SDK for Java (3)

By , February 13, 2016 10:40 am

This is the 3rd part of my tutorial on “Getting Started with AWS SDK for Java”. If you have not already do so, I suggest that you first take a look at the first chapter of this set of training “Getting Started with AWS SDK for Java (1)” to properly set up your development environment. In this part, we will cover the basic concepts related to the DataPipelineClient. Through this example, you will be able to create and activate a simple pipeline with a ShellCommandActivity running on an Ec2Resource.

Before you get started with this demo, you should get yourself familiar with what Data Pipeline is. In particular, the following AWS documentation on “Data Pipeline Concepts” is very helpful.

http://docs.aws.amazon.com/datapipeline/latest/DeveloperGuide/dp-concepts.html

First of all we create an instance of the DataPipelineClient in the constructor, then set the region to ap-southeast-2. For debugging purposes, we enable logging using log4j.

public class DemoDataPipeline
{
	static DataPipelineClient client;
	final static Logger logger = Logger.getLogger(DemoDataPipeline.class);

	/**
	 *
	 * Constructor
	 *
	 */

	public DemoDataPipeline()
	{
		// Create the DataPipelineClient
		client = new DataPipelineClient();
		// Set the region to ap-southeast-2
		client.configureRegion(Regions.AP_SOUTHEAST_2);
	}

We use the createPipeline() method in DataPipelineClient to create a new pipeline. This methods takes a CreatePipelineRequest as the parameter, which requires a name and a unique id for the pipeline to be created. Here we use the java.util.UUID utility to generate a unique id for the pipeline. This creates an empty pipeline for us.

	public void createPipeline() throws Exception
	{
		System.out.println("CREATE PIPELINE.");
		
		CreatePipelineRequest request = new CreatePipelineRequest();
		request.setName("Java SDK Demo");
		String uuid = UUID.randomUUID().toString();
		request.setUniqueId(uuid);
		client.createPipeline(request);
	}

We can use the listPipelines() method in DataPipelineClient to get a list of the pipelines. This returns a ListPipelinesResult, which includes a list of PipelineIdName objects. We traverse through this list to obtain the id and name of all the pipelines.

	public void listPipeline() throws Exception
	{
		System.out.println("LIST PIPELINE.");
		
		ListPipelinesResult result = client.listPipelines();
		List<PipelineIdName> list = result.getPipelineIdList();
		for (PipelineIdName pipeline : list)
		{
			System.out.println(pipeline.getId() + "\t- " + pipeline.getName());
		}
	}

Now we have the id of the newly created pipeline. In the AWS SDK for Java, pipeline components specifying the data sources, activities, schedule, and preconditions of the workflow are represented in PipelineObject. The following code defines a Default object, a Schedule object, an Ec2Resource object, and a ShellCommandActivity object. A PipelineObject is a collection of key-value fields. For example, the following JSON string defines an Ec2Resource in a VPC:

{
“id” : “MyEC2Resource”,
“type” : “Ec2Resource”,
“actionOnTaskFailure” : “terminate”,
“actionOnResourceFailure” : “retryAll”,
“maximumRetries” : “1″,
“instanceType” : “m1.medium”,
“securityGroupIds” : [
"sg-12345678",
"sg-12345678"
],
“subnetId”: “subnet-12345678″,
“associatePublicIpAddress”: “true”,
“keyPair” : “my-key-pair”
}

When the value of a key is another pipeline object, we use Field().withKey(“field_name”).withRefValue(“object_id”) to represent the key-value pair. Otherwise, we use Field().withKey(“field_name”).withStringValue(“field_value”) to represent the key-value pair. Please refer to the part of ShellCommandActivity in the following code for details.

	public void definePipeline(String id) throws Exception
	{
		System.out.println("Define PIPELINE.");

		// Definition of the default object
		Field defaultType = new Field().withKey("scheduleType").withStringValue("CRON");
		Field defaultScheduleType = new Field().withKey("schedule").withRefValue("RunOnceSchedule");
		Field defaultFailureAndRerunMode = new Field().withKey("failureAndRerunMode").withStringValue("CASCADE");
		Field defaultRole = new Field().withKey("role").withStringValue("DataPipelineDefaultRole");
		Field defaultResourceRole = new Field().withKey("resourceRole").withStringValue("DataPipelineDefaultResourceRole");
		Field defaultLogUri = new Field().withKey("pipelineLogUri").withStringValue("s3://331982-syd/java-dp-log");
		List<Field> defaultFieldList = Lists.newArrayList(defaultType, defaultScheduleType, defaultFailureAndRerunMode, defaultRole, defaultResourceRole, defaultLogUri);
		PipelineObject defaultObject = new PipelineObject().withName("Default").withId("Default").withFields(defaultFieldList);

		// Definition of the pipeline schedule
		Field scheduleType = new Field().withKey("type").withStringValue("Schedule");
		Field scheduleStartAt = new Field().withKey("startAt").withStringValue("FIRST_ACTIVATION_DATE_TIME");
		Field schedulePeriod = new Field().withKey("period").withStringValue("1 day");
		Field scheduleOccurrences = new Field().withKey("occurrences").withStringValue("1");
		List<Field> scheduleFieldList = Lists.newArrayList(scheduleType, scheduleStartAt, schedulePeriod, scheduleOccurrences);
		PipelineObject schedule = new PipelineObject().withName("RunOnceSchedule").withId("RunOnceSchedule").withFields(scheduleFieldList);

		// Definition of the Ec2Resource
		Field ec2Type = new Field().withKey("type").withStringValue("Ec2Resource");
		Field ec2TerminateAfter = new Field().withKey("terminateAfter").withStringValue("15 minutes");
		List<Field> ec2FieldList = Lists.newArrayList(ec2Type, ec2TerminateAfter);
		PipelineObject ec2 = new PipelineObject().withName("Ec2Instance").withId("Ec2Instance").withFields(ec2FieldList);

		// Definition of the ShellCommandActivity
		// The ShellCommandActivity is a command "df -h"
		Field activityType = new Field().withKey("type").withStringValue("ShellCommandActivity");
		Field activityRunsOn = new Field().withKey("runsOn").withRefValue("Ec2Instance");
		Field activityCommand = new Field().withKey("command").withStringValue("df -h");
		Field activityStdout = new Field().withKey("stdout").withStringValue("s3://331982-syd/dp-java-demo-stdout");
		Field activityStderr = new Field().withKey("stderr").withStringValue("s3://331982-syd/dp-java-demo-stderr");
		Field activitySchedule = new Field().withKey("schedule").withRefValue("RunOnceSchedule");
		List<Field> activityFieldList = Lists.newArrayList(activityType, activityRunsOn, activityCommand, activityStdout, activityStderr, activitySchedule);
		PipelineObject activity = new PipelineObject().withName("DfCommand").withId("DfCommand").withFields(activityFieldList);

		// setPipelineObjects
		List<PipelineObject> objects = Lists.newArrayList(defaultObject, schedule, ec2, activity);

		// putPipelineDefinition
		PutPipelineDefinitionRequest request = new PutPipelineDefinitionRequest();
		request.setPipelineId(id);
		request.setPipelineObjects(objects);
		PutPipelineDefinitionResult putPipelineResult = client.putPipelineDefinition(request);

		if (putPipelineResult.isErrored()) 
		{
			logger.error("Error found in pipeline definition: ");
			putPipelineResult.getValidationErrors().stream().forEach(e -> logger.error(e));
		}

		if (putPipelineResult.getValidationWarnings().size() > 0) 
		{
			logger.warn("Warnings found in definition: ");
			putPipelineResult.getValidationWarnings().stream().forEach(e -> logger.warn(e));
		}
	}

Now you can activate the pipeline for execution:

	public void activatePipeline(String id) throws Exception
	{
		System.out.println("ACTIVATE PIPELINE.");	

		ActivatePipelineRequest request = new ActivatePipelineRequest();
		request.setPipelineId(id);
		client.activatePipeline(request);
	}

Then, you can terminate the pipeline:

	public void deletePipeline(String id) throws Exception
	{
		System.out.println("DELETE PIPELINE.");	

		DeletePipelineRequest request = new DeletePipelineRequest();
		request.setPipelineId(id);
		client.deletePipeline(request);
	}

After checking out the demo code from github, you should modify the code to use your own S3 bucket for logging, as well as the stdout and stderr for the ShellCommandActivity. After making these changes, you can run the demo code using the following commands:

$ mvn clean; mvn compile; mvn package
$ java -cp target/demo-1.0-SNAPSHOT.jar:third-party/guava-18.0.jar -Dlog4j.configurationFile=log4j2.xml net.qyjohn.aws.DemoDataPipeline create
$ java -cp target/demo-1.0-SNAPSHOT.jar:third-party/guava-18.0.jar -Dlog4j.configurationFile=log4j2.xml net.qyjohn.aws.DemoDataPipeline list
$ java -cp target/demo-1.0-SNAPSHOT.jar:third-party/guava-18.0.jar -Dlog4j.configurationFile=log4j2.xml net.qyjohn.aws.DemoDataPipeline define df-0098814S3FS9ACXICID  (make sure you change this part using your own pipeline id)
$ java -cp target/demo-1.0-SNAPSHOT.jar:third-party/guava-18.0.jar -Dlog4j.configurationFile=log4j2.xml net.qyjohn.aws.DemoDataPipeline activate df-0098814S3FS9ACXICID  (make sure you change this part using your own pipeline id)
$ java -cp target/demo-1.0-SNAPSHOT.jar:third-party/guava-18.0.jar -Dlog4j.configurationFile=log4j2.xml net.qyjohn.aws.DemoDataPipeline delete df-0098814S3FS9ACXICID  (make sure you change this part using your own pipeline id)

Panorama Theme by Themocracy