2006年3月29日

http://www.firstui.com/read.php/250.htm

posted @ 2006-11-23 23:47 killvin| 编辑 收藏

很久都已经下定了决心不再维护那里的空间了,即使是成为了特约的作者也难以挽回我对他们Blog的信心,虽然blogjava的系统与csdn的是同样出自一份代码,但显然无论从服务的内容还是代码的理解上,csdn与blogjava相差的太远,当然我并不是完全的赞同目前blogjava的风格,尤其是身为介绍java的博客系统,竟然采用asp的技术来实现,实在是太煞风景了,甚至有些让你匪夷所思,不过瑕不掩瑜,毕竟无论从速, 专业程度还是目前提供的服务上来看,blogjava确实比同类型的网站要优秀一些.

posted @ 2006-10-29 15:54 killvin| 编辑 收藏

Version 1.01 on Bindows enhanced its Mozilla support considerably. At this point, the level of support is such that many complicated applications are available to Mozilla users. For example: now these can use our forum and the registration application.
We are dedicated to make Bindows work at least as well with Mozilla 1.4+ as it does with Internet Explorer 6.0 SP1. The Mozilla support is now integral part of Bindows and we will continue to develop, maintain and support it.
However, some issues are not solved yet and this document details them.


Known Issues
Web Services

We do not yet have a working SOAP web service client working with Mozilla. Mozilla has a built in web service client but it does not yet work with all the major web services platforms. We anticipate this to be resolve in a near future version of Bindows.


Graphs, Charts and Gauges
Graphs, charts and gauges are not supported. For the Bindows charts and gauges we rely on VML to do the drawing. Mozilla does not yet have a way to draw vector graphics at the client side without using a plugin. We consider several alternatives here but there is no time frame for a solution yet.


Miscellaneous
The caret is sometimes not shown in a focused textfield. This is a Mozilla bug and unless this is fixed in Mozilla this issue will remain.
Preferred size is not working consistently. Please report the cases where you are experiencing issues with the preferred size and we will see what can be done in those specific cases.
Icons in menu items works inconsistently between Mozilla versions. This is due to differences in the Mozilla toolkits used by different browsers.
Menu labels do not accept HTML. If you use HTML, the markup will be stripped.
The rendering of the group box is incorrect.
Labels with text align center and icon position set to top or bottom are a bit jumpy, ie. the icon move around depending on the state.
Dragging elements sometimes inconsistent. If an image is dragged an OS level drag and drop is initiated. Also, for some components a dragging will not work when the mouse leaves the browser window or enters a frame.
The upper border for the tab pages are not correctly drawn in Mozilla Firefox using Windows Classic. This is a Mozilla bug.
BiKeyboardEvent keyCode is sometimes incorrect in a keypress event (for special keys). The reason for this bug is that Internet Explorer and Mozilla are handling keyboard events differently.
Resize events are not fired on all BiComponents.
Focus and blur events are inconsistent when using BiRichEdit.

posted @ 2006-10-14 16:21 killvin| 编辑 收藏

Jsparse is a parse to parse the schema file with javascript.
If you are interested in it, you can vist the url
http://code.google.com/p/jsparse/  

get source with svn
svn checkout http://jsparse.googlecode.com/svn/trunk/ jsparse

posted @ 2006-09-13 23:23 killvin| 编辑 收藏

ACE中的Double Checked Locking 模式


(作者:Douglas C. Schmidt ,by huihoo.org CORBA课题 Thzhang 译 , Allen整理,制作)

意图

无论什么时候当临界区中的代码仅仅需要加锁一次,同时当其获取锁的时候必须是线程安全的,可以用Double Checked Locking 模式来减少竞争和加锁载荷。

动机

1、标准的单例。开发正确的有效的并发应用是困难的。程序员必须学习新的技术(并发控制和防止死锁的算法)和机制(如多线程和同步API)。此外,许多熟悉的设计模式(如单例和迭代子)在包含不使用任何并发上下文假设的顺序程序中可以工作的很好。为了说明这点,考虑一个标准的单例模式在多线程环境下的实现。单例模式保证一个类仅有一个实例同时提供了全局唯一的访问这个实例的入口点。在c++程序中动态分配单例对象是通用的方式,这是因为c++程序没有很好的定义静态全局对象的初始化次序,因此是不可移植的。而且,动态分配避免了单例对象在永远没有被使用情况下的初始化开销。


class Singleton
{
public:
static Singleton *instance (void)
{
if (instance_ == 0)
// Critical section.
instance_ = new Singleton;
return instance_;
}
void method (void);
// Other methods and members omitted.
private:
static Singleton *instance_;
};



应用代码在使用单例对象提供的操作前,通过调用静态的instance方法来获取单例对象的引用,如下所示:
Singleton::instance ()->method ();
2、问题:竞争条件。不幸的是,上面展示的标准单例模式的实现在抢先多任务和真正并行环境下无法正常工作。例如,如果在并行主机上运行的多个线程在单例对象初始化之前同时调用Singleton::instance方法,Singleton的构造函数将被调用多次,这是因为多个线程将在上面展示的临界区中执行new singleton操作。临界区是一个必须遵守下列定式的指令序列:当一个线程/进程在临界区中运行时,没有其他任何线程/进程会同时在临界区中运行。在这个例子中,单例的初始化过程是一个临界区,违反临界区的原则,在最好的情况下将导致内存泄漏,最坏的情况下,如果初始化过程不是幂等的(idempotent.),将导致严重的后果。

3、 通常的陷阱和弊端。实现临界区的通常方法是在类中增加一个静态的Mutex对象。这个Mutex保证单例的分配和初始化是原子操作,如下:


class Singleton
{
public:
static Singleton *instance (void)
{
// Constructor of guard acquires lock_ automatically.
Guard guard (lock_);
// Only one thread in the critical section at a time.
if (instance_ == 0)
instance_ = new Singleton;
return instance_;
// Destructor of guard releases lock_ automatically.
}
private:
static Mutex lock_;
static Singleton *instance_;
};


guard类使用了一个c++的习惯用法,当这个类的对象实例被创建时,它使用构造函数来自动获取一个资源,当类对象离开一个区域时,使用析构器来自动释放这个资源。通过使用guard,每一个对Singleton::instance方法的访问将自动的获取和释放lock_。
即使这个临界区只是被使用了一次,但是每个对instance方法的调用都必须获取和释放lock_。虽然现在这个实现是线程安全的,但过多的加锁负载是不能被接受的。一个明显(虽然不正确)的优化方法是将guard放在针对instance进行条件检测的内部:


static Singleton *instance (void)
{
if (instance_ == 0) {
Guard guard (lock_);
// Only come here if instance_ hasn't been initialized yet.
instance_ = new Singleton;
}
return instance_;
}

这将减少加锁负载,但是不能提供线程安全的初始化。在多线程的应用中,仍然存在竞争条件,将导致多次初始化instance_。例如,考虑两个线程同时检测 instance_ == 0,都将会成功,一个将通过guard获取lock_另一个将被阻塞。当第一线程初始化Singleton后释放lock_,被阻塞的线程将获取lock_,错误的再次初始化Singleton。
4、解决之道,Double Checked Locking优化。解决这个问题更好的方法是使用Double Checked Locking。它是一种用于清除不必要加锁过程的优化模式。具有讽刺意味的是,它的实现几乎和前面的方法一样。通过在另一个条件检测中包装对new的调用来避免不必要的加锁:


class Singleton
{
public:
static Singleton *instance (void)
{
// First check
if (instance_ == 0)
{
// Ensure serialization (guard constructor acquires lock_).
Guard guard (lock_);
// Double check.
if (instance_ == 0)
instance_ = new Singleton;
}
return instance_;
// guard destructor releases lock_.
}
private:
static Mutex lock_;
static Singleton *instance_;
};


第一个获取lock_的线程将构建Singleton,并将指针分配给instance_,后续调用instance方法的线程将发现instance_ != 0,于是将跳过初始化过程。如果多个线程试图并发初始化Singleton,第二个检测件阻止竞争条件的发生。在上面的代码中,这些线程将在lock_上排队,当排队的线程最终获取lock_时,他们将发现instance_ != 0于是将跳过初始化过程。

上面Singleton::instance的实现仅仅在Singleton首次被初始化时,如果有多个线程同时进入instance方法将导致加锁负载。在后续对Singleton::instance的调用因为instance_ != 0而不会有加锁和解锁的负载。 通过增加一个mutex和一个二次条件检测,标准的单例实现可以是线程安全的,同时不会产生过多的初始化加锁负载。

适应性

> 当一个应用具有下列特征时,可以使用Double Checked Locking优化模式:
1、应用包含一个或多个需要顺序执行的临界区代码。
2、多个线程可能潜在的试图并发执行临界区。
3、临界区仅仅需要被执行一次。
4、在每一个对临界区的访问进行加锁操作将导致过多加锁负载。
5、在一个锁的范围内增加一个轻量的,可靠的条件检测是可行的。

结构和参与者

通过使用伪代码能够最好地展示Double Checked Locking模式的结构和参与者,图1展示了在Double Checked Locking模式有下列参与者:



1、仅有一次临界区(Just Once Critical Section,)。临界区所包含的代码仅仅被执行一次。例如,单例对象仅仅被初始化一次。这样,执行对new Singleton的调用(只有一次)相对于Singleton::instance方法的访问将非常稀少。
2、mutex。锁被用来序列化对临界区中代码的访问。
3、标记。标记被用来指示临界区的代码是否已经被执行过。在上面的例子中单例指针instance_被用来作为标记。
4、 应用线程。试图执行临界区代码的线程。

协作

图2展示了Double Checked Locking模式的参与者之间的互动。作为一种普通的优化用例,应用线程首先检测flag是否已经被设置。如果没有被设置,mutex将被获取。在持有这个锁之后,应用线程将再次检测flag是否被设置,实现Just Once Critical Section,设定flag为真。最后应用线程释放锁。



结论

使用Double Checked Locking模式带来的几点好处:
1、最小化加锁。通过实现两个flag检测,Double Checked Locking模式实现通常用例的优化。一旦flag被设置,第一个检测将保证后续的访问不要加锁操作。
2、防止竞争条件。对flag的第二个检测将保证临界区中的事件仅实现一次。
使用Double Checked Locking模式也将带来一个缺点:产生微妙的移植bug的潜能。这个微妙的移植问题能够导致致命的bug,如果使用Double Checked Locking模式的软件被移植到没有原子性的指针和正数赋值语义的硬件平台上。例如,如果一个instance_指针被用来作为Singleton实现的flag,instance_指针中的所有位(bit)必须在一次操作中完成读和写。如果将new的结果写入内存不是一个原子操作,其他的线程可能会试图读取一个不健全的指针,这将导致非法的内存访问。
在一些允许内存地址跨越对齐边界的系统上这种现象是可能的,因此每次访问需要从内存中取两次。在这种情况下,系统可能使用分离的字对齐合成flag,来表示instance_指针。
如果一个过于激进(aggressive)编译器通过某种缓冲手段来优化flag,或是移除了第二个flag==0检测,将带来另外的相关问题。后面会介绍如何使用volatile关键字来解决这个问题。

实现和例子代码

ACE在多个库组件中使用Double Checked Locking模式。例如,为了减少代码的重复,ACE使用了一个可重用的适配器ACE Singleton来将普通的类转换成具有单例行为的类。下面的代码展示了如何用Double Checked Locking模式来实现ACE Singleton。


// A Singleton Adapter: uses the Adapter
// pattern to turn ordinary classes into
// Singletons optimized with the
// Double-Checked Locking pattern.
template 
class ACE_Singleton
{
public:
static TYPE *instance (void);
protected:
static TYPE *instance_;
static LOCK lock_;
};
template  TYPE *
ACE_Singleton::instance ()
{
// Perform the Double-Checked Locking to
// ensure proper initialization.
if (instance_ == 0) {
ACE_Guard lock (lock_);
if (instance_ == 0)
instance_ = new TYPE;
}
return instance_;
}


ACE Singleton类被TYPE和LOCK来参数化。因此一个给定TYEP的类将被转换成使用LOCK类型的互斥量的具有单例行为的类。
ACE中的Token Manager.是使用ACE Singleton的一个例子。Token Manager实现在多线程应用中对本地和远端的token(一种递归锁)死锁检测。为了减少资源的使用,Token Manager被按需创建。为了创建一个单例的Token Manager对象,只是需要实现下面的typedef:

typedef ACE_Singleton Token_Mgr;
Token Manager单例将被用于本地和远端的token死锁检测。在一个线程阻塞等待互斥量之前,它首先查询Token Manager单例,来测试阻塞是否会导致死锁状态。对于系统中的每一个token,Token Manager单例维护一个持有token线程和所有阻塞等待在该token的线程记录链表。这些数据将提供充足的检测死锁状态的依据。使用Token Manager单例的过程如下:


// Acquire the mutex.
int Mutex_Token::acquire (void)
{
// If the token is already held, we must block.
if (mutex_in_use ()) {
// Use the Token_Mgr Singleton to check
// for a deadlock situation *before* blocking.
if (Token_Mgr::instance ()->testdeadlock ()) {
errno = EDEADLK;
return -1;
				}
else
// Sleep waiting for the lock...
		// Acquire lock...
		}

变化

一种变化的Double Checked Locking模式实现可能是需要的,如果一个编译器通过某种缓冲方式优化了flag。在这种情况下,缓冲的粘着性(coherency)将变成问题,如果拷贝flag到多个线程的寄存器中,会产生不一致现象。如果一个线程更改flag的值将不能反映在其他线程的对应拷贝中。

另一个相关的问题是编译器移除了第二个flag==0检测,因为它对于持有高度优化特性的编译器来说是多余的。例如,下面的代码在激进的编译器下将被跳过对flag的读取,而是假定instance_还是为0,因为它没有被声明为volatile的。

Singleton *Singleton::instance (void)
{
if (Singleton::instance_ == 0)
{
// Only lock if instance_ isn't 0.
Guard guard (lock_);
// Dead code elimination may remove the next line.
// Perform the Double-Check.
if (Singleton::instance_ == 0)
// ...



解决这两个问题的一个方法是生命flag为Singleton的volatile成员变量,如下:
private:
static volatile long Flag_; // Flag is volatile.
使用volatile将保证编译器不会将flag缓冲到编译器,同时也不会优化掉第二次读操作。使用volatile关键字的言下之意是所有对flag的访问是通过内存,而不是通过寄存器。

相关模式

Double Checked Locking模式是First-Time-In习惯用法的一个变化。First-Time-In习惯用法经常使用在类似c这种缺少构造器的程序语言中,下面的代码展示了这个模式:


	static const int STACK_SIZE = 1000;
static T *stack_;
static int top_;
void push (T *item)
{
// First-time-in flag
if (stack_ == 0) {
stack_ = malloc (STACK_SIZE * sizeof *stack);
assert (stack_ != 0);
top_ = 0;
}
stack_[top_++] = item;
// ...
}



第一次push被调用时,stack_是0,这将导致触发malloc来初始化它自己。

posted @ 2006-09-13 23:16 killvin| 编辑 收藏

在上一篇的文章中,比较仔细的分析了IE对于Cookie处理过程中出现的问题,既然存在问题就要想办法解决,而问题的关键在于IE没有能够"老实"的报告用户对于Cookie的设置问题

IE
GET /mail HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Maxthon)
Host: mail.google.com
Connection: Keep-Alive
Cookie:
gmailchat=killvin.liu@gmail.com/519974 ; PREF=ID=d68c481e542af276:NW=1:TM=1151742225:LM=1151742225:S=2qbdhg0_z3i-OAbW; SID=DQAAAG8AAACEdcjD2IZMNqZVatDbD62X8_U18oJuTVQc9XZUJi7MgCkM8sggJ8M5npZ35GXjdalT2o8QWPUve04tepy61MPv4v_EpILafg3JdIf8AFjD91aMT0tI5gb763FouV3e_2-C364HDO5Qzb4P4gjjgpHC

Firefox
GET /mail HTTP/1.1
Host: mail.google.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

我们可以注意到:如果用户关闭了浏览器的Cookie选项,在Firefox中对于新的请求是包含Cookie的信息的,然而IE却恰恰相反!这也就直接用IE浏览一些网站所导致的Cookie校验失败问题(比如即使用户在使用过程中突然关闭Cookie,依然可以自由的访问网站的各项服务),进一步,如果某个网站是读取依靠Cookie中的信息来验证用户(尤其是那种保存客户信息的提示),即使这个用户离开或者关闭窗口,任何一个后来的人都可以轻松的浏览这个用户的私有信息!这就是Cookie的最大的问题所在,当然这是另外的话题,不属于本次讨论的范畴,这一次要说的是
如何避免IE浏览器下,用户关闭Cookie的问题。

有一个思路,经过测试我们发现IE浏览器只是在首次页面请求的时候才会去读取Cookie的值,所以,为了避免IE浏览器关闭Cookie的问题,可以在用户请求某个链接的时候,先将用户的Cookie存放在某处,并清空此时所有的Cookie值,再次的请求页面,而这样做的目的就是强迫IE读取硬盘上的Cookie,如果此时用户关闭了Cookie,就不会在请求的头信息中看到Cookie信息;如果没有关闭Cookie,就可以放心的将原先的Cookie写回Reponse对象。


只是不知道Google的网站是否也是按照这个思路去解决此问题的?

posted @ 2006-07-02 21:53 killvin| 编辑 收藏

IE在处理COOKIE方面的问题

1。即使你提高了浏览器的隐私登记,在第一次打开窗口的时候,你获取不到任何的Cookies对象(很显然的结果),然而当你再次刷新本页面的,Cookie此时会奇迹般的出现!而在Firefox浏览器中按照以上的步骤,是不会出现这样的情况的。

2。不仅如此,你还可以透过Action的处理,多次的往Cookie中增加Cookie的数量(当然在Action中你依然可以自由的获取到Cookie这个数组,并且不为空),然而让人匪夷所思的是在Cookie的存放目录下你是找寻不到任何的蛛丝马迹的。而在Firefox没有出现以上的情况。

-解决
1。在首次进入页面时查询客户端的Cookie,如果不存在则警告用户,并要求再次的登陆。
2。在用户登陆后,如果更改了浏览器的隐私级别,对于Firefox标准的浏览器,此时肯定不会再找到Cookie数组对象了,你需要做的仅仅只是将页面调转到登陆窗口;而在IE下就非常的麻烦了甚至无法解决,因为你依然可以访问到原来的Cookie数组值,(比如,用IE在CSDN登陆后提高隐私级别,你依然可以登陆到其他的服务区域)此时没有什么好的办法,不过Google解决了这样的问题,只是不知道如何解决的。



IE在处理Frame标记的问题
1。如果你在某个页面中嵌入了Frame标签,并且希望与这个嵌入的页面共享某些存放在Session中的数据,此时你需要将外部的sessionId传入到frame标记的页面中。然而在IE中你可能并不能完全的享受这样的逻辑,原因在于IE对于嵌入的页面不会自动的传递sessionId,也许你碰巧可以,也许不行,也就是说完全是在IE的"掌控"之下。而在Firefox没有出现这样的情况。

-解决
为嵌入的页面中所有的链接增加sessionId参数。


最好的办法就是:说服客户使用标准的浏览器Firefox!

posted @ 2006-07-01 15:53 killvin| 编辑 收藏

在Struts中我们经常这样循环的打印Message
<logic:messagesPresent message="true">
  <html:messages id="msg" message="true">
    <div class="success">
      <bean:write name="msg"/>
    </div><br/>
  </html:messages>
</logic:messagesPresent>

查阅struts tag的文档我们看到了关于messagesPresent的message属性注释如下

message

By default the tag will retrieve the request scope bean it will iterate over from the Globals.ERROR_KEY constant string, but if this attribute is set to 'true' the request scope bean will be retrieved from the Globals.MESSAGE_KEY constant string. Also if this is set to 'true', any value assigned to the name attribute will be ignored


也就是说在将message设置为true时,会去request中寻找Globals.MESSAGE_KEY所代表的bean,然而我们在具体的Action使用ActionMessages类的时候往往是这样的
ActionMessages messages = getErrors(request);
messages.add(ActionMessages.GLOBAL_MESSAGE , new ActionMessage(key , value0));
saveMessages(request,messages);

而往往困扰人的就在于为什么要给messages中放入名称为"ActionMessages.GLOBAL_MESSAGE"的ActionMessage对象,而且还需要再次的调用saveErrors(request,messages)方法?

首先要说明的是你为ActionMessage起任何的名称都没有关系,因为ActionMessages本身维持着一个HashMap,而参数property就是这个HashMap中的key值,如果不存在则会建立相应的key,并将需要保存的ActionMessage对象存入到这个key所对应的List中。
    public void add(String property, ActionMessage message) {

        ActionMessageItem item = (ActionMessageItem) messages.get(property);
        List list = null;

        if (item == null) {
            list = new ArrayList();
            item = new ActionMessageItem(list, iCount++, property);

            messages.put(property, item);
        } else {
            list = item.getList();
        }

        list.add(message);

    }

至于为什么一定要调用saveMessages(request,messages)?看看它具体的实现逻辑就清楚了
    protected void saveMessages(
        HttpServletRequest request,
        ActionMessages messages) {

        // Remove any messages attribute if none are required
        if ((messages == null) || messages.isEmpty()) {
            request.removeAttribute(Globals.MESSAGE_KEY);
            return;
        }

        // Save the messages we need
        request.setAttribute(Globals.MESSAGE_KEY, messages);
    }

再对比前面介绍的messagesPresent标签的使用,是不是就清楚了呢?原来它是将ActionMessages对象保存在request中,并且名称是Globals.ERROR_KEY!从而为tag的顺利解析铺平了道路。当然按理你可以选择将这样的对象放置在任何的scope中,但Action只是提供了request , session两种Scope(不过page , application不经常使用,可以理解,但不提供相应的结构就不太好了)

至于messagesPresent标签是如何在scope中寻找ActionMessages对象

org.apache.struts.taglib.logic.MessagesPresentTag
    protected boolean condition(boolean desired) throws JspException {
        ActionMessages am = null;

        String key = name;
        if (message != null && "true".equalsIgnoreCase(message)){
           key = Globals.MESSAGE_KEY;
        }

        try {
            am = TagUtils.getInstance().getActionMessages(pageContext, key);
           
        } catch (JspException e) {
            TagUtils.getInstance().saveException(pageContext, e);
            throw e;
        }

        Iterator iterator = (property == null) ? am.get() : am.get(property);

        return (iterator.hasNext() == desired);

    }

org.apache.struts.taglib.TagUtils
   public ActionErrors getActionErrors(PageContext pageContext, String paramName)
            throws JspException {

        ActionErrors errors = new ActionErrors();

        Object value = pageContext.findAttribute(paramName);
        if (value != null) {
            try {
                if (value instanceof String) {
                    errors.add(
                            ActionMessages.GLOBAL_MESSAGE,
                            new ActionMessage((String) value));

                } else if (value instanceof String[]) {
                    String keys[] = (String[]) value;
                    for (int i = 0; i < keys.length; i++) {
                        errors.add(
                                ActionMessages.GLOBAL_MESSAGE,
                                new ActionMessage(keys[i]));
                    }

                } else if (value instanceof ActionErrors) {
                    errors = (ActionErrors) value;

                } else {
                    throw new JspException(
                            messages.getMessage(
                                    "actionErrors.errors",
                                    value.getClass().getName()));
                }

            } catch (JspException e) {
                throw e;

            } catch (Exception e) {
                log.debug(e, e);
            }
        }
        return errors;
    }

PageContext中的findAttribute会帮你在scope中寻找名称为Globals.MESSAGE_KEY的ActionMessage对象。


注意
虽然Struts已经声明:不推荐使用ActionErrors & ActionError对象,但在一些遗留的系统中,依然还是可以看到其影子,所以如果你的系统不幸属于这样的两种混合系统,有以下的几种方法可以参考
1。两次调用messagesPresent,如下
<!-- Print ActionErrors Object -->
<logic:messagesPresent>
  <html:messages id="msg" message="true">
    <div class="success">
      <bean:write name="msg"/>
    </div><br/>
  </html:messages>
</logic:messagesPresent>

<!-- Print ActionMessages Object -->
<logic:messagesPresent message="true">
  <html:messages id="msg" message="true">
    <div class="success">
      <bean:write name="msg"/>
    </div><br/>
  </html:messages>
</logic:messagesPresent>

2.分别使用<html:messages> <html:errors>标签,当然在老系统中需要调用Action的saveErrors方法,而在新的应用中要调用saveMessages方法。

3.更换所有的ActionErrors为ActionMessages,并将所有调用saveErrors的地方更换成saveMessages,并将<html:errors>标签相应的更换成<html:messages message="true"> - 推荐!

 

posted @ 2006-06-27 23:02 killvin| 编辑 收藏

第2章 并发问题及控制手段

什么是并发问题?假设有这么一家书吧,顾客可以到那里喝茶读书。顾客拿着选好要读的图书到柜台登记,然后找个地方去阅读,临走时将图书归还店家。有一天,一个顾客相中了一本书后正要拿去登记,另一个顾客的手也抓住了这仅有的一本书,并发问题出现了。两个顾客要读同一本书,互不相让,这让店主伤透了脑筋。这个案例仅仅是众多并发问题中的一个微小部分,但从中我们可以看出并发问题主要出现在多个用户对有限资源进行访问的时候,如果解决不好会直接影响系统的有效、正常运行。数据库是一个共享的资源,并发问题的出现是必不可免的,如何识别并发类型并加以控制是这一章重点要讲述的内容。

本章将分成两大部分,一部分主要讲Visual FoxPro中并发控制机制。VFP中并发控制相对简单,数据加锁的形式比较单一,非常适合作为初步了解并发问题的切入点。第二部分以SQL Server 2000、ADO.NET以及C#为主要工具,深入了解并发一致性问题、封锁协议、事务隔离等内容,难度相对较深。象一些更为深入的并发控制手段,例如多粒度封锁和意象锁等内容在本章中将不做深入讨论,感兴趣可以参考相关书籍。

 

[实在不好意思COPY别人的成果,不过这篇文章出奇的精彩,将并发操作的来龙去脉说的清清楚楚,也是我正要找的,比JAVAEYE上面所谓的专家叫嚷着什么"悲观锁"、"乐观锁"而不解是原因要强的多!值得收藏]

posted @ 2006-04-06 10:57 killvin| 编辑 收藏

痛快!。。。人们总是认为骂人是没有素质的象征,然而我到觉得要看对谁而言,如果你的面前是个谦虚、严谨而又懂得尊重别人的人,你根本就不可能让自己愤怒,也不可能让自己表现的很没有素质;相反如果你的面前是个先入为主、自傲、不懂得尊重别人的人,你的愤怒就会在最短的时间内激发出来。-对于小人,就要骂!

而这有与北大青鸟有什么关系呢?

在我的职业生涯里,有一段让自己愤怒的培训经历,到今天我都为自己在这样的培训部的学习经历而感到耻辱!甚至不愿意在简历里提及ACCP的字样,而这样的培训经历就是在北大青鸟(西安兆隆)里度过的。

我参加的是北大青鸟(西安兆隆)的第二期培训,培训的课程涉及到数据库、编程语言、职业素质、项目实践等,看上去非常的正规,可是真正的学习效果却是让人非常的失望的!

首先,就是它的课程体系,完全的引入印度的教程,而翻译的质量非常的差,其实这到没有什么,只是教程的讲解范围和深度都非常的浅(这还是第二期的培训),如果你只是按照它的教程我可以说你根本学不到任何有价值的东西,只是皮毛而已。-拿着淡薄的教学资料的同时你也就上了贼船了。


再次,就是它的老师的水平实在不敢恭维,(当然并不是说根本就没有好的老师,曾经就遇到过一个讲解JAVA与UML的老师,水平非常的高深,可惜ACCP没有留住),比如编程语言中学习了VB,仅仅只是这一门课程就换了3个老师,最后是一位号称是西工大的博士上的课,然而这位博士显然是心思不再这里,除了讲解课本上已经有的代码,根本不敢深入的讲解原理,这门课程也就这样混过去了。-如果这样学习编程语言道不如自学来得轻松。


还有,就是所谓的项目实践根本就不是老师带的项目,而是一些书本上或者捏造出来的一些项目,比如图书管理系统等,如果学员无法真正的理解需求,怎么去理解设计,又如何来进行实际的编码呢?如果遇到图书管理的专业问题来问谁呢?-别指望在他们宣传的所谓的项目实践中得到锻炼。

在看看他们引以为傲的工作分配问题吧?就我个人的亲身感受来言,这里会提供一些工作的机会,但都是一些小公司,而且往往是一些前三个月不给工资的公司;当时我在我们班级里是比较认真的一个(我是班里唯一的一个交了对项目分析文档的学员,并且是项目实践中的小组组长),可是在一次很好的面试机会面前,她们竟然由于工作的失误忘记了给我打电话?!-如果这样的工作分配策略也算作是给予你的答案的话,你一定会变得和我一样的愤怒!


我没有在ACCP学到任何的有用的东西,然而在真实的世界里却学到了人们对于ACCP的看法,很多的技术经理 、 技术总监从骨子里非常的厌恶从北大青鸟出来的学生,对于他们开设的一些课程也是相当的反感,当时在西部世纪工作的时候,同组的就有和我一起上课的同班同学,在他交了8000多元的学习费用(总共的费用接近1万多元)后,得到的仅仅只是西部世纪的一张试用通知单,而条件是必须无条件的为公司工作三个月!


这就是我在北大青鸟的学习经历,然而愤怒的不是这个,却是今天参加的面试经历,对照着自己的个人的工作经历我希望自己能够成为一名培训部的老师,教给那些原来如我一样的学生一些在实践当中有用的东西,而不是空洞的理论!所以我给ACCP投了自己的简历,然而让我却又一次的遇到了那个欺骗过我的咨询师,不过她的记忆力也是非常的好,一眼就认出了我(当然了我是曾经给她带来奖金的人),在紧接着的面试过程中已经变了味道,谈论的话题也不是具体项目技术的问题,(不是我笑话她与她谈论技术问题根本就如同对牛弹琴)她显然是一个没有素质的WOMAN,也可以肯定的是她根本没有在外企工作过的经历,所以说话的词汇以及与其如同大街上的泼妇一般,可以想见她一定是一个被ACCP严重洗过脑的人,而且是属于那种技术失败的产品,她无法接受别的培训部的名字,一旦你提起别的培训部的某个老师的水平如何如何的好,她就如同一个快被点燃的鞭炮一般,两眼充满了愤怒!

然而让我最终和她一般的没有素质的大骂的是她对于我辞职的评点,我很纳闷的是她在不知道一件事情的原因的情况下,可以讲述自己的观点!并且是带着教育的口吻,实在是人才呀!这就是北大青鸟的"人才力量",对于这样的白痴级别的任务的观点,我只好打断,因为它甚至连我在的那个单位的名字都不知道!!

实在是一场闹剧,而她更像是个小丑,除了唧唧喳喳的重复一些"洗脑"的词汇已经不知道窗外的天气了,可以预料北大青鸟的牌子必将会毁在这些个白痴的手中,世界需要白痴,那如同鲜花需要牛粪一样,我们改变不了白痴的世界观的,你能够做的就是别被这臭烘烘的牛粪给熏臭了。

posted @ 2006-04-01 12:53 killvin| 编辑 收藏


[深入的思考] 为什么要采用java这个平台?


从开发项目的类别角度看java平台

基于B/S结构的系统,在这个方向上的竞争是激烈的,有专注于此的LAMP(Linux + Apache + Mysql + Php);也有刚刚兴起的Rails(Ruby Frameworks)甚至是号称快速开发的ASP.NET;当然了java在这个领域里的MVC框架数都数不完,比如Struts . Webwork等,然而即便是如此,选择java作为开发的理由也是不充分的,因为在这个梯队里java顶多排名最后。

基于C/S结构的系统,在这个方面java显然没有考虑周到,面对VB 、DELPHI、vc这些个如狼似虎的快速开发IDE,JAVA实在是显得异常的淡薄,即使你找到了一个可以匹敌这些个ide的工具,面对第三方的组件又会成为一大障碍,所以java在这个方面又一次的输了。


从java所强调的特性角度看java平台

java的重点是业务逻辑!(我以前也是如此坚信不移)可是谁有能够说别的语言不注重业务逻辑呢,业务逻辑只是一个抽象的概念,java只是依靠ejb提出了业务组件而已,其他的语言在实现业务逻辑的时候也可以包装成POJO的形式,看来这个观点也是失败的。

java强调的是跨平台的优势!这可以理解为初级的、商业的、忽悠人的词汇,面对众多动态语言如Python,在若干平台上的表现,java又如何来强调自己这方面的优势呢?失败

java支持分布式应用的项目!可笑的言论,分布式根本不是值得炫耀的资本,在java之前的c/s项目中何尝不是分布式的应用呢?失败


既然没有了这些个优势,我们看看java到底还剩下些什么?对了其实就是应用服务器!然而看过J2EE WITHOUT EJB的读者肯定知道Spring所希望达到的目的,也就是脱离应用服务器概念上的J2EE体系实现,既然在作者的眼里APPLICATION SERVER只不过是一个忽悠人的词汇,那么任何项目都选择java作为开发的依据显然就是自找苦吃,

那么什么情况下改选择java作为开发的平台呢?
<1> 如果你真的遇到了大型的系统开发任务,恭喜你,你终于可以看到分布式对象、集群的优势了。
<2> 客户是一个java的忠实fans或者是sun、ibm的金牌合作伙伴之类的,选择java是不得已的,但记住并不能证明java是最好的实现方式
<3> 如果你只想关心业务逻辑的实现,对于事务、缓存、查找等服务的实现没有兴趣的话,倒是不妨考虑采用ejb的形式,当然前提是你不愿意在寻找合适的替代品的情况下。
<4> 如果项目迫切的寻找某种框架的支持,选择java就是对的,你有众多优秀的、免费的、可扩展的、天才的框架可以选择,更多的时候你是出于尴尬的境地,因为任何一个都让你心动、而这样的选择往往是最痛苦、和快乐的。


正确的选择
<1>
条件: 如果项目仅仅只是一个小型的网站系统
选择: LAMP、Rails

<2>
条件: 项目规模中等
并且项目的时间比较紧,
项目可以架构在windows的系统之上,
选择: .Net  / Delphi

<3>
条件: 大型的系统,有支持分布式对象、集群的要求;或者SUN / IBM的金牌合作伙伴 ; 想要寻找某种优秀的框架来解决问题
选择: java是不二的选择,可是我想问一下,在现实中你能遇到这样的项目吗?

所以,从实际的角度出发,我们面对的99%可能都是一些符合条件1,2的系统,而选择java实在是得不偿失的。最后以一段Code Complete中的话来作为结束语

每个程序员都有很多的工具,但并不存在任何一个能够适用于所有工作的工具,因地制宜的选择正确工具是成为能有效编程的程序员的关键。

posted @ 2006-03-29 13:49 killvin| 编辑 收藏