
2006年11月27日
代码很简单:
try {
IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
IWebBrowser browser = support.createBrowser("Some_ID");
browser.openURL(new URL(("http://www.eclipse.org")));
} catch (PartInitException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
这样就会在Eclipse中打开默认浏览器(就是在Preference中设置的那个)了。
posted @
2006-11-27 12:32 Allen Young 阅读(811) |
评论 (0) |
编辑

2006年10月11日
记得电影《疯狂的石头》里面有这样两个场景,一个是谢小萌在缆车里面泡妞,另一个是三宝从阳台跨到隔壁的房间。在这两个场景中,都有一个略显洋气的中老年妇 女的出场,而且她都会非常适时地说出她那仅有两个字的台词:“流氓”。这让我们感觉很亲切也很陌生,我出生在一个城市中相对比较乱的郊区,从小就清楚流氓是个啥东西,但是在上了大学以后,我就再也没有见过这个词了。说起来还真是悲哀,在我每天看的电影的电视剧中,脏话从来就少不了,但是它们要么是“bastard”,要么是“son of bitch”,甚至是“八个亚路”,还真就没有记不得什么中文词汇。我是不是不爱国了?呵呵~
亲切归亲切,但流氓这个词却引起了我的思考。到底什么样的人才能被称作流氓呢?于是,我努力回忆过去对流氓这个词的理解,终于多少得到了一点 儿答案。所谓流氓,不一定他/她有多坏,但是他/她至少要在“生活作风”上有问题,或至少表现得有问题。比如,一个小伙子如果多看一个小姑娘几眼,无论那个小姑娘漂亮不漂亮,他都可以被扣上流氓的帽子。这就让我很无奈,因为假如我是那个小伙子,如果姑娘漂亮,我被说成流氓还算甘心;如果姑娘本来就不漂亮,那我就真的太冤了。我的这种说辞肯定会引起某些女权主义者的不满,他/她们会说我是臭不要脸的大男子主义。那么我就说说让你们开心的话,流氓这个词往往用来形容男性,如果真的有女性想要高攀,那么还要加上一个“女”字,变成女流氓。这也不难解释,因为很少有女人会“耍流氓”,而如果一个女人“作风有问题”,我们会给她扣上别的帽子,而且不只一顶。
但是我想要在这里说的不是“作风问题”,所以我也不会去谈这个意义上的流氓。我想谈的是另一种流氓,他/她们和一般人没什么分别,没有什么卑劣的人格或龌龊的嗜好,他/她们甚至比普通老百姓更有文化,这种人就是文化流氓。
关于文化流氓的具体定义以及谁最先提出了这个词,我没有办法给出答案。我不是考古学家,甚至论坛中比较老的帖子我都不愿意看。因此,我只能从我可知的范围内说起。最早把这个词展现在我眼前的是王小波,在他那本《沉默的大多数》杂文集中,他给出了自己对文化流氓的定义和思考。让我很内疚的是,我看的那本《沉默的大多数》是盗版,里面错别字很多,我不是有心买盗版,只是那本书是从朋友不要的旧书里面翻出来的。更遗憾的是,如果你去看看介绍王小波的一个网站http://www.wangxiaobo.com/,你就会发现,上面还有卖“精品内衣”和“成人用品”的链接。虽然王小波的老婆是中国当代最知名的性学者,我想王小波本人也不希望见到这样的情形,而且我敢说,网站的拥有者肯定不是想我一样无心而为。
顺着王小波的意思,加上我自己的一点点理解。我可以把文化流氓定义为“用文化(特别是人文方面的知识)把不好的东西解释成好的东西并加以支持的人”。这些人可以有也可以没有“作风问题”,这完全不相干,但是有一点是肯定的,他/她们必须有文化。大多数情况下,文化流氓并不是一个人的长久属性,更多的时候,它是一种临时的状态。也就是说,一个人品非常好的人也可能会做出文化流氓式的事情来,也就是说他/她正处于文化流氓的状态。而且文化流氓也不一定是故意的,相反,大多数情况下都是由于无法抑制内心对于某种想法的冲动而产生的。因此,我不会在这里对曾经有过文化流氓经历的人口诛笔伐,我只是想分析这种现象。
说了这么半天,到底有没有什么活生生的文化流氓现象呢?当然有,现在就让我们来看一个例子。记得前一阵子《我行我秀》正热播的时候,曾经闹出过这样一件事来:一个评委没有投票支持师洋,并说了一些认为师洋不好的话,后来师洋的粉丝们(好像是叫格格吧?)对这个评委的blog进行了围攻,其间有不堪入耳的脏话、甚至有人身安全的威胁,但是其内容的主体可以概括为:师洋的“装疯卖傻”是一种独特的风格,是艺术,不准有任何人对其进行批评什么诬蔑。要把这个事情和文化流氓扯上关系,首先我们要建立一个假设,就是师洋这种表演是好还是不好。假设是不好的,那么格格们(不包括说脏话和要动粗的)把它解释为一种艺术而不许人批评就是文化流氓的表现,因为他/她们拿出了“艺术”这个词,而从学术上看,确实,各种艺术应该是没有高低贵贱之分的,因此,从学术上看,他/她们说得没错。那么现在,我们把假设反过来,假设师洋的表演是好的,那么那个评委是不是文化流氓了呢?答案是否定的,因为他/她只是做出了自己的判断,而不巧的是,他/她的判断对师洋的晋级起着很重要的作用。
从这里我们可以看到文化流氓的一种惯用伎俩,那就是“从学术上看,一切都是平等的”。这一本来非常令人尊敬的思想到了文化流氓手里就变成了恐怖的武器。因为“一切都是平等的”,所以你不可以批评任何东西;因为“一切都是平等的”,所以我无需听任何人的说教;因为“一切都是平等的”,因此我应该与任何人具有同样的地位。于是乎我们就看到了很多“力挺”这个、“力挺”那个的现象,我们也发现人们(特别是青少年和孩子)越来越不在乎别人对他/她们的建议和规劝。他/她们收到过良好的教育,他/她们信奉每个人是个独立的个体,而所有个体是平等的。他/她们构成了当今社会的文化流氓。
郭德纲在他的相声中总说“流氓会武术,谁也拦不住”。其实这更多是一句玩笑话。当今还是法制社会,虽然法律并不健全,但是管管流氓还是绰绰有余的。你流氓大发了的,可以判刑;流氓比较小的,原来可以拘留,现在拘留取消了,可以罚款。这就引发了这样一个现象,对于比较小的流氓,警察们往往是“不怕他凶,就怕他穷”。其中道理不言自明。如果说好动粗的流氓是通过暴力让人屈服他的思想(往往是不好的思想),那么文化流氓就是把自己的思想合理话,并堂而皇之的让你去接受。后者显然更厉害,因为稍有不慎,大家就会觉得他说得有道理,就会听他的。如果这个人是一个名人,那么就更厉害了。
我再举一个名人的例子,老罗。(我不知道他现在还算不算名人。)有一次他在新东方课上谈到“女体盛”的问题,就是把菜放到赤裸的女人身上吃的那种日本人发明的吃法。他说虽然他很讨厌这种东西,觉得它很龌龊,但是国家不能禁止它,因为没有“龌龊罪”,因为人民有龌龊的权利。当然,国家最后还是把“女体盛”禁掉了,老罗显然是不同意的,而且以知识分子自居的老罗显然是在用文化作为主要武器。这是不是一种文化流氓呢?
我无意说老罗是文化流氓,事实上他应该还算是个不错的知识分子。但是他的这一论点显然有些流氓(也就是说,那时他处于文化流氓的状态),这也就是我说的“由于无法抑制内心对于某种想法的冲动而产生的”。让我们来做一个很简单的类比,用看A片类比“女体盛”。A片很多人都看过,也算是龌龊的东西,大家一般都是在一个比较私人的场合下面把玩,也没有听说谁因为在家里拉上窗帘看A片而被捕的。但是,卖A片或聚众放映A片就不一样了,国家应该有法律命令禁止的。把这个道理放到“女体盛”上来,你在家里让你老婆脱光了来个“女体盛”,肯定没问题,有些人还会把这叫做情趣。但是如果你开个餐馆公开经营“女体盛”,那就和卖A片或聚众放映A片一个性质了。
人民有没有龌龊的权利?当然有。这并不算文化流氓,但是老罗犯错的地方就在于他把人民有没有龌龊的权利和人民有没有用龌龊影响他人的权利混淆在了一起。说得简单一点儿,你可以在家里看A片,这不妨碍你成为一个正人君子,但是如果你因为看了A片然后不能自已冲到隔壁强奸了邻居,那么这就变成了另一种性质的问题。说实话,我从不担心那些学生们听了老罗的论调后做出任何犯罪的举动,但是他们肯定会从老罗那里学到一些文化流氓的本领。对此我很是不安。
我们每个人或多或少肯定有过文化流氓的状态,我们都有感性的一面,这是无法避免的,关键是要认清这个问题,提醒自己不要再犯。“在学术上,一切都是平等的”,这句话一点儿都没有错,错的是我们把它用在了不恰当的地方。我们生活在一个现实的社会里,而不是学术。凡高的画和你的画永远不可能平等,因为一切东西除了看学术,还要看它对人们的影响。明白了这个道理,我们才可能正确使用文化的力量。
posted @
2006-10-11 18:01 Allen Young 阅读(251) |
评论 (2) |
编辑

2006年10月5日
Platform: Eclipse 3.2
Dialog是SWT和JFace的一个重要的组成部分,我们在开发Plug-in或RCP的时候也经常会用到它们。这篇随笔不会介绍 SWT的Dialog,因为我想很多人都已经非常熟悉它了。在这里,我要讨论的是JFace的Dialog,或者更进一步说是JFace的 TitleAreaDialog。什么是TitleAreaDialog呢?想想我们常常用到的New XX Wizard就知道了。在我们创建一个Java Project或Class的时候,我们所使用的Wizard其实就是由TitleAreaDialog构成的。这种Dialog有如下所示的 TitleArea和一个标准的Button Bar:

正常的TitleArea 带有错误信息的TitleArea

标准的Button Bar
这种GUI的表现力要比SWT的Dialog强很多,而且JFace为该 Dialog封装了很多东西,这也使开发工作变得更加简单,所以我极力推荐使用TitleAreaDialog。那么让我们来看一个最基本的 TitleAreaDialog:
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.jthin.jpssp.ide.configuration.Activator;
public class MyTitleAreaDialog extends TitleAreaDialog {
/**
* Create the dialog
*
* @param parentShell
*/
public MyTitleAreaDialog(Shell parentShell) {
super(parentShell);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
*/
protected Control createDialogArea(Composite parent) {
Composite area = (Composite) super.createDialogArea(parent);
Composite container = new Composite(area, SWT.NONE);
container.setLayoutData(new GridData(GridData.FILL_BOTH));
// TitleArea中的Title
setTitle("My TitleAreaDialog");
// TitleArea中的Message
setMessage("This is a simple TitleAreaDialog example.");
// TitleArea中的Image
setTitleImage(ResourceManager.getPluginImage(Activator.getDefault(), "icons/Neptune.png"));
return area;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
*/
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TitleAreaDialog#getInitialSize()
*/
protected Point getInitialSize() {
return new Point(500, 375);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
*/
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
// Dialog Title
newShell.setText("Test TitleAreaDialog Title");
// Dialog Icon
newShell.setImage(ResourceManager.getPluginImage(Activator.getDefault(), "icons/Neptune.png"));
}
}
这段代码非常容易理解,从方法签名中可以看出每个方法做了什么事情。注意createButtonsForButtonBar方法,其中用createButton方法创建了OK和Cancel这两个Button,并且把Button的默认点击事件也写好了,就是关闭该 Dialog。ResourceManager.getPluginImage是我自己编写的获得图片的helper method,这里就不讨论其实现了。这段代码会产生如下的Dialog:

有趣的是,我在这里故意使用了一个128×128的大图标, TitleAreaDialog不会自动缩小或裁减Image,而是调整TitleArea的大小来适应Image。
接下来我们要为OK Button编写我们自己的事件,例如把用户在Dialog中的输入保存到某处。有人可能会想到为OK Button添加SelectionListener,但实际上这样做是不对的,因为OK Button是JFace为Dialog封装好了的,同时JFace也提供了响应的callback:
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#okPressed()
*/
protected void okPressed() {
// implement your own function here
super.okPressed();
}
我们可以在这里实现我们自己的事件,不过最后一定要调用super.okPressed方法,否则Dialog就不会关闭了。
OK,以上就是TitleAreaDialog的基本Framework,非常容易理解,下面我们就来在 TitleArea中动态设置一些信息。你可以把这个scenario想象成在用户输入的同时提示用户输入的合法性。TitleAreaDialog提供了好3个方法可以动态设置TitleArea信息,具体如下:
- public void setErrorMessage(String newErrorMessage):显示传入的错误信息。(我们把用这个方法设置的信息叫做error message。)当前显示的信息会被保存起来,等到error message清空之后会再次显示,而清空error message要传入null,而不是传入空字符串。
- setMessage(String newMessage):显示传入的信息,等同于setMessage(String newMessage, IMessageProvider.NONE)。如果当前显示的是error message,那么newMessage会被保存起来,等到error message清空后再显示。
- setMessage(String newMessage, int newType):显示传入的信息,并显示指定的信息类型。可用的类型有NONE、INFORMATION、WARNING和ERROR。需要注意的是, setMessage(String newMessage, int IMessageProvider.ERROR)和setErrorMessage(String newErrorMessage)并不相同。后者会覆盖当前的任何信息,而前者只会覆盖当前的非error message,不会影响到error message(也就是说当error message清空后才会显示)。
这样,我们就可以为一些文本框添加ModifyListener,然后在其中设置TitleArea的信息了。
接着,再让我们来看看Button Bar。有些时候,我们希望把OK和Cancel这种默认的Button放置在Button Bar的右侧,而把其他Button放置在Button Bar的左侧,如下图中的Customize... Button:

这又如何实现呢?有人可能想到在 createButtonsForButtonBar方法中做一些手脚,但是遗憾的是这行不通,我们真正要覆写的是createButtonBar方法,下面是一个简单的例子:
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TrayDialog#createButtonBar(org.eclipse.swt.widgets.Composite)
*/
protected Control createButtonBar(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.numColumns = 0;
layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
composite.setLayout(layout);
composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
if (isHelpAvailable()) {
createHelpControl(composite);
}
createButton(composite, MyConstants.IMPORT_BUTTON_ID, "Import", false).addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
MessageDialog.openInformation(MaintainModuleDialog.this.getShell(), "Information",
"\"Import\" button has not been implemented.");
}
});
createButton(composite, MyConstants.EXPORT_BUTTON_ID, "Export", false).addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
MessageDialog.openInformation(MaintainModuleDialog.this.getShell(), "Information",
"\"Export\" button has not been implemented.");
}
});
createButton(composite, MyConstants.OTHER_BUTTON_ID, "Other", false).addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
MessageDialog.openInformation(MaintainModuleDialog.this.getShell(), "Information",
"\"Other\" button has not been implemented.");
}
});
Label filler = new Label(composite, SWT.NONE);
filler.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
layout.numColumns++;
super.createButtonsForButtonBar(composite);
return composite;
}
正如你所见,我们实际上创建了自己的Button Bar,然后在上面添加了3个Button:Import、Export和Other,最后 super.createButtonsForButtonBar会创建OK和Cancel Button。filler是用来在两组Button见占位的。代码中用到的两个convert方法来自 org.eclipse.jface.dialogs.Dialog类,你还可以在这个类中找到一个getButton(int)方法,它可以根据传入的 ID返回用createButton创建的Button。这些都是非常实用的方法。
回头看一下上面那个完整的 TitleAreaDialog图片,你会看到在Dialog左下角有一个问号符号,这其实是一个Button,点击它可以显示帮助信息,当然帮助信息是由你来创建的。让我们看看Eclipse Search的TitleAreaDialog中的帮助信息吧:

如果我们也想实现这种帮助机制,那么就要实现如下方法:
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TrayDialog#createHelpControl(org.eclipse.swt.widgets.Composite)
*/
protected Control createHelpControl(Composite parent) {
// TODO Auto-generated method stub
return super.createHelpControl(parent);
}
如果不想实现帮助机制,那么最好不要在Dialog中显示出那个问号符号,你可以覆写如下方法并永远返回false,这样就不会显示问号符号了。
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TrayDialog#isHelpAvailable()
*/
public boolean isHelpAvailable() {
return false;
}
那么这个酷酷的帮助机制到底是个什么东西呢?实际上,它的学名叫做DialogTray。TitleAreaDialog继承了 org.eclipse.jface.dialogs.TrayDialog类,而TrayDialog就可以显示这种 DialogTray,是不是有点儿拗口呢?实际上,我们不仅仅可以添加帮助信息这一种DialogTray,还可以添加任意的DialogTray,现在就让我们动手实现一个最简单的吧。代码很简单,最主要的就是要实现一个DialogTray,代码如下:
import org.eclipse.jface.dialogs.DialogTray;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
public class MyDialogTray extends DialogTray {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.DialogTray#createContents(org.eclipse.swt.widgets.Composite)
*/
protected Control createContents(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
final GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 2;
container.setLayout(gridLayout);
final Label label = new Label(container, SWT.NONE);
label.setText("Name:");
final Text text = new Text(container, SWT.BORDER);
text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
return container;
}
}
我们只在其中创建了一个Label和一个Text,这就足够了。最后,我们为MyTitleAreaDialog添加两个Button,用来打开和关闭MyDialogTray,代码如下:
final Button openTrayButton = new Button(container, SWT.NONE);
openTrayButton.setText("Open Tray");
final Button closeTrayButton = new Button(container, SWT.NONE);
closeTrayButton.setText("Close Tray");
closeTrayButton.setEnabled(false);
openTrayButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(final SelectionEvent e) {
// this method is from TrayDialog
openTray(new MyDialogTray());
openTrayButton.setEnabled(false);
closeTrayButton.setEnabled(true);
}
});
closeTrayButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(final SelectionEvent e) {
// this method is from TrayDialog
closeTray();
openTrayButton.setEnabled(true);
closeTrayButton.setEnabled(false);
}
});
最后我们会得到如下对话框:

好了,就讲这么多吧。如果能把这些东东适当地用在你的Application中,那么效果一定非常棒。
posted @
2006-10-05 21:37 Allen Young 阅读(1448) |
评论 (1) |
编辑
Platform: Eclipse 3.2
Eclipse Plug-in开发离不开与用户的交互,我们往往把用户的鼠标点击之类动作所调用的代码包装成一个Action。例如,如果用户点击了一个Menu Item,那么绑定到这个Menu Item上的Action就会运行,这一切都很直观。但是有的时候,用户触发的操作可能需要很长时间才能完成,比如连接数据库,在这种情况下,就必须把鼠标的状态改为忙碌,这样就可以用沙漏手势提示用户当前操作正在进行,也避免了用户在等待当前操作的时候又去触发其他的操作。那么如何实现这一点呢?Eclipse有一个High-level的实现,也有一个Low-level的实现。因为后者非常简单,而且在大多数情况下也能满足需求,所以这里会介绍后者。
我们可以在org.eclipse.swt.custom包中找到一个BusyIndicator类,它的Java Doc说得好:Support for showing a Busy Cursor during a long running process。该类只有一个static的方法,让我们来看看它的签名:
public static void showWhile(Display display, Runnable runnable)
其中,display参数指定了Busy Cursor应该显示在哪里,如果传入null,则默认使用当前线程的当前Display;runnable就是封装了我们要执行的操作的对象。这个方法会执行runnable中的操作,同时显示Busy Cursor,当runnable结束后,鼠标的状态就会恢复为正常。OK,让我们来看一个例子:
public class DoubleClickTreeNodeAction extends Action {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.action.Action#run()
*/
public void run() {
// get Display from your own plug-in
BusyIndicator.showWhile(Activator.getDefault().getWorkbench()
.getDisplay(), new Runnable() {
public void run() {
performAction();
}
});
}
private void performAction() {
// connecting to database...
}
}
怎么样,是不是很简单呢?
posted @
2006-10-05 15:55 Allen Young 阅读(344) |
评论 (0) |
编辑
Platform: Eclipse 3.2
开发任何软件都不得不处理Exception和Log,Eclipse Plug-in也是如此。不过幸运的是,Eclipse PDE提供了记录及显示Exception和Log的机制:Error Log View。作为Eclipse SDK的一部分,PDE的普及率很高,所以除非你是要做RCP,不然的话用Error Log View处理Exception和Log应该是你的最佳选择。当然,这也带来了对PDE的依赖性。
使用Error Log View实际上非常简单,每个Plug-in的Activator类都有一个getLog()方法,返回一个ILog对象,这个对象就可以把Exception和Log记录到Error Log View中。ILog对象最主要的方法就是log了,顾名思义,它接收一个IStatus类型的对象,并把其代表的状态记录下来。Eclipse和许多常用的插件(如JDT)实现了很多的IStatus,最common的就是Status类,我们可以简单地使用它,或创建自己的IStatus实现。Status的构造函数有5个参数,具体如下:
- int severity:日志的级别,可以是OK、ERROR、INFO、WARNING或CANCEL。这些常量都定义在Status类中。
- String pluginId:当前Plug-in的ID。
- int code:Plug-in指定的状态码,一般如果无需指定,则使用Status.OK。
- String message:日志信息。
- Throwable exception:记录的Exception,如果没有Exception,则传入null。
这样的话,我们就可以编写一个LogUtil类来负责日志工作,代码如下:
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.Status;
public class LogUtil {
private static LogUtil instance = null;
private ILog logger = null;
private LogUtil() {
logger = Activator.getDefault().getLog();
}
public static LogUtil getInstance() {
if (instance == null) {
instance = new LogUtil();
}
return instance;
}
public void log(int severity, String message, Throwable exception) {
logger.log(new Status(severity, Activator.getDefault().getPluginID(),
Status.OK, message, exception));
}
public void logCancel(String message, Throwable exception) {
logger.log(new Status(Status.CANCEL, Activator.getDefault()
.getPluginID(), Status.OK, message, exception));
}
public void logError(String message, Throwable exception) {
logger.log(new Status(Status.ERROR, Activator.getDefault()
.getPluginID(), Status.OK, message, exception));
}
public void logInfo(String message, Throwable exception) {
logger.log(new Status(Status.INFO,
Activator.getDefault().getPluginID(), Status.OK, message,
exception));
}
public void logOk(String message, Throwable exception) {
logger.log(new Status(Status.OK, Activator.getDefault().getPluginID(),
Status.OK, message, exception));
}
public void logWarning(String message, Throwable exception) {
logger.log(new Status(Status.WARNING, Activator.getDefault()
.getPluginID(), Status.OK, message, exception));
}
}
除此之外,我们还可以通过ILog的addLogListener方法和removeLogListener方法为日志动作添加和删除事件监听器。这些Listener可以帮助我们在日志记录完成后做一些额外的事情。例如,如果记录的是ERROR级别的Log,那么我们可能要弹出一个Alert对话框告诉用户出现了错误,但如果是INFO级别,就没这个必要了。
posted @
2006-10-05 15:36 Allen Young 阅读(619) |
评论 (0) |
编辑

2006年9月8日
开发Eclipse Plugin的过程中,难免要用到其它的Framework或Lib,如果自己不做配置,那么Eclipse不会自动把这些jar文件作为Plugin运行的classpath,因此Plugin运行的时候就会出错。怎么办呢?
假设我们使用的第三方jar文件都放置在Plugin根目录的lib目录下。那么首先要做的是把这些jar文件包含到Plugin的build中,打开plugin.xml文件,在Build一页中选择lib目录,这样最后export出了Plugin就会包含这些jar文件了。但是这还不够,因为虽然有了jar文件,Plugin还是不知道应该使用它们。还是在Build页中,我们可以看到在最低端有一个叫做Extra Classpath Entries的section。是不是要配置这个东东呢?我们来试试。
点击这个section右侧的Add JARs按钮,添加相应的jar文件,发生了什么?哦,Eclipse报错了,它说当前Plugin的Build Path中含有重复的lib。这是怎么回事儿呢?去看看Build Path就会发现,原来你刚刚添加的jar文件出现在了Plug-in Dependencies中。再加上原来你已经把这些jar加入了Build Path,难怪会出错。好了,把你自己对Build Path的配置去掉,这样就没有重复了。运行Plugin试试?还是不行。
看来Extra Classpath Entries并不是需要配置的地方,但是这里也有一个Happy Accident,那就是你以后再开发Plugin的时候,不用自己配置Build Path了,只需要在Extra Classpath Entries中添加jar文件就好了。但是需要注意的是,这个配置只能保证你的Plugin通过编译,真正运行的时候还是找不到jar文件的。
其实,这个问题的正解在MANIFEST.MF文件中,你需要在这个文件中添加一个配置,才能在Plugin运行时使用第三方jar文件。来看看需要加什么配置吧~
Bundle-ClassPath: lib/cglib-full-2.0.2.jar,
lib/commons-beanutils.jar,
lib/commons-collections-2.1.1.jar,
lib/commons-lang-1.0.1.jar,
lib/commons-logging-1.0.4.jar,
lib/dom4j-1.4.jar,
lib/ehcache-0.9.jar,
lib/hibernate2.jar,
lib/jta.jar,
lib/jthincommon.jar,
lib/odmg-3.0.jar
看到了吧,以上是使用Hibernate 2.1.8所需要的配置。这个配置非常简单,但是我不知道为什么PDE没有提供一个interface让我们配置。没办法,目前我们只能自己添加了,因为在Eclipse 3.2中也没有可以配置这个属性的interface。
posted @
2006-09-08 17:44 Allen Young 阅读(519) |
评论 (1) |
编辑
我们在使用Hibernate时一般都会编写一个hibernate.cfg.xml文件,并在其中配置一些连接数据库的属性。这在很多情况下都是非常合适的,但是如果需要动态获得数据库链接信息,这种方法就不行了。假设我们要根据用户的输入信息连接响应的数据库,该怎么办呢?
我们都知道,Hibernate的SessionFactory是通过Configuration获得的,hibernate.cfg.xml文件中配置很多信息都对应为Configuration的Property,因此只要可以为Configuration设置属性并添加hbm.xml信息,就可以动态的获得SessionFactory了。具体代码如下:
Properties jdbcPros = new Properties();
jdbcPros.setProperty("hibernate.connection.useUnicode", "true");
jdbcPros.setProperty("hibernate.connection.characterEncoding", "UTF-8");
jdbcPros.setProperty("hibernate.connection.driver_class", "org.gjt.mm.mysql.Driver");
jdbcPros.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/your_db");
jdbcPros.setProperty("hibernate.connection.username", "root");
jdbcPros.setProperty("hibernate.connection.password", "your_pw");
jdbcPros.setProperty("hibernate.dialect", "net.sf.hibernate.dialect.MySQLDialect");
jdbcPros.setProperty("hibernate.show_sql", "false");
jdbcPros.setProperty("hibernate.use_outer_join", "true");
jdbcPros.setProperty("hibernate.transaction.factory_class", "net.sf.hibernate.transaction.JTATransactionFactory");
Configuration cfg = new Configuration();
cfg.setProperties(jdbcPros);
try {
cfg.addFile("A.hbm.xml");
cfg.addFile("B.hbm.xml");
} catch (MappingException e) {
e.printStackTrace();
}
try {
SessionFactory sessionFactory = cfg.buildSessionFactory();
} catch (HibernateException e) {
e.printStackTrace();
}
一目了然,代码说明了一切。我们可以为Configuration设置很多Property,其中一些是必须的,一些是可选的。具体的Property列表请参考Hibernate的Reference。
posted @
2006-09-08 11:36 Allen Young 阅读(490) |
评论 (0) |
编辑

2006年8月31日
随着Web 2.0浪潮的兴起,很多传统的C/S结构的应用开始飞快地出现在互联网上。编写文档有Writely,看RSS有抓虾。这不,一个叫meebo的在线IM工具又映入了大家的眼帘,并赢得一片喝彩。
meebo现在支持四种IM工具的在线聊天:AIM或ICQ、Yahoo!Messenger、Jabber或GTalk、MSN。乍一看这已经是很不错的功能了。如果你不在使用自己的电脑,而当前的电脑上又没有这些IM的客户端软件,那么meebo就帮了大忙了。
但是在我看到meebo这个东东的时候,第一个想到的问题就是:我凭什么相信它?我要使用meebo登陆我的MSN,就要输入用户名和密码,那这些重要的信息不就让meebo得到了吗?这么愚蠢的事情我怎么能做呢?后来我想到,这并不仅仅是用户名和密码的问题。随着越来越多的桌面应用在互联网上的出现,一定会有更多的个人信息会以各种方式流露到网上,那么它们的安全性又如何呢?
我是个比较笨的人,所以我想不出有什么技术手段可以保证这些信息不被“meeboer”得到。从非技术角度上看,无非就是“meeboer”会保证自己不接触这些信息的明文。然而这些保证终究不是可靠的,说白了就是看职业道德、看人品,那么我们能够信任任何一个互联网公司的人品吗?
我写这些,不是为了打击反对“meeboer”。像meebor这种网站的出现原本是好的,关键就是如何解决安全的问题。这里的安全不单单是网站的保证,更要有足够的依据让用户相信。如果这个问题解决不了,那么网络应用的发展肯定会受到阻碍。记得一位朋友说Web 3.0是网络操作系统,那么我来问各位,谁愿意把自己的私密信息放在这种网络操作系统上呢?
posted @
2006-08-31 17:43 Allen Young 阅读(352) |
评论 (0) |
编辑

2006年8月24日
翻译自Alex Russell的Blog文章:Comet: Low Latency Data
for the Browser
历史深处的一种古老的web technology正在慢慢复兴。已经多年未被触及的浏览器特性又一次登上了web开发的舞台,为UI带来了更好的响应。Server正在学习如何应付一种新的行事方法。并且,我说的不是Ajax。
像Jot Live和Meebo这样的新兴服务使用了一种新的数据传输形式,既不是传统的数据传输也不是Ajax。它们的招牌特点是数据传输的low-latency,而这种特点正变得越来越普遍。由于没有更好的术语,我把这种event-driven的、server-push data streaming的形式叫做“Comet”。它不代表任何意义,况且我也不觉得它应该代表什么。这种technique的工作原理可能会使开发人员十分困惑,所以使用已经存在的定义和名字很可能会产生不良的效果。
定义Comet
为了使用新的术语,我们至少要看一些该技术的实例,被该技术解决的问题,以及使它与众不同的特性。这些实例并不难找,几个典型的实例是:
GMail’s GTalk integration
Jot
Live
Renkoo
cgi:irc
Meebo
究竟是什么使这些应用与众不同的呢?究竟是什么使它们有别于那些乍看起来十分类似的应用呢?本质上的区别就是,它们都使用long-lived HTTP连接来降低server传输的信息的延迟。它们不会时不时地从server端poll信息。相反,server会维护一个通信的开路,这样它就可以把数据push到client了。
从网络活动的角度上看,我们可以修改JJG的原始Ajax图来说明Comet的不同之处:

如同上面图例所示,Comet应用可以在任何时候向client发送数据,而不是仅仅在用户输入的响应中发送数据。这些数据从一个单独的、预先打开的连接中被传输。这种方法极大地降低了数据传输的延迟。
这种架构基于对数据的一种新的认识,即数据在HTTP连接两端都是event-driven的。熟悉SOA或message-oriented中间件的工程师会发现这副图与它们出奇地相似。唯一的改动就是这里的终点是浏览器。
虽然在异步性方面Comet和Ajax很相似,但是实现了Comet的应用能够在几乎可以忽略的延迟下通信状态的改变。这使它非常适合监控和多用户协作类型的应用,如果没有Comet,这些应用将很难或不可能实现,除非在浏览器中添加插件。
为什么对用户来说Comet更好?
常规的Ajax提高了单个用户的UI响应,但其代价是使long-lived页面的内容变得“陈旧”。其他用户执行的数据改动是看不见的,直到用户刷新整个页面。应用也可以回到“旧社会”去,维护某种状态机制,告诉client从上次通信到现在的所有改动。用户不得不要么执行某个操作来请求查看其他用户更新的状态(这也许会对它们想要执行的操作造成影响!)要么每隔一段时间就向server请求改动信息(叫做“polling”)。因为web天生就是多用户的,所以很明显,常规的Ajax在usability和transparency hurdles上欺骗了用户。使用Comet技术的应用可以避免这个问题,它可以在更新发生时主动push给所有的client。UI的状态永远是同步的,使用应用的每个人都可以轻易理解他们的改动会影响其他用户。Ajax提高了单个用户的响应。Comet提高了协作的、多用户的应用的响应,并且避免了轮询所带来的性能问题。
但它会流行起来吗?
为了使Comet应用流行起来,新的server软件通常是必须的,但是服务器端的event-driven IO模式正变得越来越广泛。甚至连Apache都会在下一个版本2.2中提供Comet模块。在那之前,Twisted、POE、Nevow、mod_pubsub,以及其他高层次的event-driven IO抽象正使得站在技术前沿的开发人员可以使用Comet。几乎所有的现代OS也已经支持了某种内核级别的event-driven IO系统。我甚至听说Java的NIO包将会在下一个版本中利用这些系统。这些工具正悄悄地把event-driven的未来变为现实。Comet会流行起来,并且大多数工具已经可以使用了。
我会在ETech上再次讨论这个话题,并会描述Comet应用可以用来从server端向client端 push数据的各种技术。和往常一样,我也会把slides发布在我的Blog上。
Read-write web的未来是多用户的。Ajax之后还会有另一种生活。
尾注
略
posted @
2006-08-24 11:46 Allen Young 阅读(282) |
评论 (0) |
编辑

2006年8月14日
-----------------------------------------------------------------------------------------------------------------------
也许
我是被妈妈宠坏的孩子
我任性
我希望
每一个时刻
都像彩色蜡笔那样美丽
我希望
能在心爱的白纸上画画
画出笨拙的自由
画下一只永远不会
流泪的眼睛
一片天空
一片属于天空的羽毛和树叶
一个淡绿的夜晚和苹果
我想画下早晨
画下露水
所能看见的微笑
画下所有最年轻的
没有痛苦的爱情
画下想像中
我的爱人
她没有见过阴云
她的眼睛是晴空的颜色
她永远看着我
永远,看着
绝不会忽然掉过头去
我想画下遥远的风景
画下清晰的地平线和水波
画下许许多多快乐的小河
画下丘陵——
长满淡淡的茸毛
我让它们挨得很近
让它们相爱
让每一个默许
每一阵静静的春天的激动
都成为一朵小花的生日
我还想画下未来
我没见过她,也不可能
但知道她很美
我画下她秋天的风衣
画下那些燃烧的烛火和枫叶
画下许多因为爱她
而熄灭的心
画下婚礼
画下一个个早早醒来的节日——
上面贴着玻璃糖纸
和北方童话的插图
我是一个任性的孩子
我想涂去一切不幸
我想在大地上
画满窗子
让所有习惯黑暗的眼睛
都习惯光明
我想画下风
画下一架比一架更高大的山岭
画下东方民族的渴望
画下大海——
无边无际愉快的声音
最后,在纸角上
我还想画下自己
画下一只树熊
他坐在维多利亚深色的丛林里
坐在安安静静的树枝上
发愣
他没有家
没有一颗留在远处的心
他只有,许许多多
浆果一样的梦
和很大很大的眼睛
我在希望
在想
但不知为什么
我没有领到蜡笔
没有得到一个彩色的时刻
我只有我
我的手指和创痛
只有撕碎那一张张
心爱的白纸
让它们去寻找蝴蝶
让它们从今天消失
我是一个孩子
一个被幻想妈妈宠坏的孩子
我任性
一九八一年三月
-----------------------------------------------------------------------------------------------------------------------
我无法形容顾城在我心中的地位。记得那是在夏天,一个昏暗的下午,我的语文老师夹着本不厚的书邋邋遢遢地走进教师,在全班95%都已昏昏欲睡的情况下,让我第一次认识了顾城,认识了这首诗。虽然没有读过很多书,但是那时的我在文学上还是一个挺轻狂的孩子,我一直觉得近代诗和白话差不多,谁都能写,只要你把别人弄糊涂了,你就是诗人。但是在读过这首诗后,我的想法完全改变了。
我开始热衷于寻找顾城的诗。那个时候,网络还不发达,我只能从学校的小图书馆里碰运气。在找遍了所有的书架后,我终于发现了一本满是灰尘的《顾城诗全编》,于是欣喜地抱回家了。我已经记不起当时躺在床上读这本书时的心情了,我猜应该很激动吧,就像现在的小朋友们找到了自己歌星偶像一张限量唱片一样。在顾城之后,在语文老师孜孜不倦的“误导”下,我又知道了贾岛、穆旦这些人。我还知道了一些有趣的八卦,比如:穆旦是金庸的哥哥,徐志摩和郁达夫这两个“好色男人”是高中同班同学。语文老师总是唏嘘感叹,说为什么我们班就没有这等人物,于是我就会嘲笑他说,因为人家的班主任是梁实秋。
正如他的诗中写到的,顾城是个任性的孩子。他的任性脾气秉性上的幼稚,而是自身对世界的不妥协。让我们看看顾城传奇的一生吧。爸爸是个木匠,顾城也传成了其父的手艺,但是他在12岁的时候就已经写出了美妙的诗句,从此世界上少了一个木匠,多了一个诗人。顾城有两个老婆,他在太平洋上找了一个小岛住下来,自封为王。最后是他的死,他用斧子砍死了自己的一个老婆,然后自杀了。而一直到死,他也没有摘下那顶自制的帽子,他把它带到了坟墓里。
我记得有人说,神经正常的人无法成为一流的诗人。从很多方面上来看,顾城的神经是不正常的。但是引发这一切不正常的行为的根本原因是他与这个现实世界的格格不入。他的世界只能用一个词来形容:童话。于是,当他发现童话即将被打破的时候,他选择了最极端的方式结束了这一切。这让我想起了另一个诗人,海子。虽然我并不了解他,但是我觉得他和顾城是同一种人。
现在,我已告别了高中时代,告别了那本《顾城诗全编》。对于顾城,我只是在偶尔的精神恍惚之间会想起他那双孩子一样的大眼睛和那顶蹩脚的帽子。我想我不应该把他忘记,虽然他并不能作为我的榜样。但是在人人沉迷于经济或技术的今天,我不得不提醒自己,除了这些,还有很多其他的东西。有些人是不应该被忘记的,因为他们启迪了我们的人文精神。
让我们记住他--顾城。
posted @
2006-08-14 16:47 Allen Young 阅读(725) |
评论 (0) |
编辑