狂奔 lion

自强不息

2008年9月12日

浅谈Java中的同步的方法和原理

Java的内存模型中Thread会附有自己的堆栈,寄存器,必要时需要和主存即heap之间同步。
可以使用Synchornized关键字和Concurrent包中的Lock可以保证线程互斥和可见性。

互斥性体现在类锁或者对象锁上,每个对象自身都包含一个监视器,该监视器是一个每次只能被一个线程所获取进入的临界区,可以通过wait和notify来退出和准入临界区。可以看出这是一个生产者-消费者的模型。而Concurrent包中的Lock为了能够获得更好的性能和更好的扩展性,以及不依赖于关键字的可读代码,自己实现了这样一个生产消费队列,也就是AbstractQueuedSynchronizer,被称为AQS的机制。每个Lock都内置了一个AbstractQueuedSynchronizer。需要说明的是AbstractQueuedSynchronizer内部实现采用了CAS机制,通过getState, setState, compareAndSetState访问控制一个32bit int的形式进行互斥。

那么可见性是如何保证的呢?

对于关键字的同步机制,其实可见性就是线程和主存之间的同步时机问题。共有4个时间点需要注意:
1 获取或释放类锁/对象锁的时候。Thread保证reload/flush全部变更
2 volatile就是flush on write或者reload on read
3 当线程首次访问共享变量时,可以得到最新的结果。
题外:所以在构造方法中公布this时很危险的。简单的说,就是构造时不逃脱任何变量,不开启新的线程,只做封装。关于安全构造,请参考
http://www.ibm.com/developerworks/cn/java/j-jtp0618/#resources
4 线程结束时,所有变更会写回主存

关于Concurrent Lock如何实现可见性的问题,Doug Lea大侠,只在他的论文中提到,按照JSR133,Unsafe在getState, setState, compareAndSetState时保证了线程的变量的可见性,不需要额外的volatile支持,至于具体这些native做了哪些magic就不得而知了,总之,最后的contract就是保证lock区间的共享变量可见性。开发团队被逼急了就这样回答:
There seems to be a real reluctance to explain the dirty details. I think the question was definitely understood on the concurrent interest thread, and the answer is that synchronized and concurrent locking are intended to be interchangable in terms of memory semantics when implemented correctly. The answer to matfud's question seems to be "trust us.”

不过这个地方的确是开发团队给我们用户迷惑的地方,在同样应用了CAS机制的Atomic类中,都内嵌了volatile变量,但是再lock块中,他告诉我们可以保证可见性。

感兴趣的同学可以下面的两个thread和Doug Lea的thesis:
http://altair.cs.oswego.edu/pipermail/concurrency-interest/2005-June/001587.html
http://forums.sun.com/thread.jspa?threadID=631014&start=15&tstart=0
http://gee.cs.oswego.edu/dl/papers/aqs.pdf

posted @ 2010-07-09 19:49 杨一 阅读(1842) | 评论 (0)编辑 收藏

commons-net FTPClient API存取设计

文件系统无非就是文件的存取和组织结构。
访问一个文件系统的API也应该是写,读,定位方法(Pathname?/URI?)

FTPClient针对文件的保存和获取各提供了两个方法,分别是:

public boolean storeFile(String remote, InputStream local)
public OutputStream storeFileStream(String remote)

public boolean retrieveFile(String remote, OutputStream local)
public InputStream retrieveFileStream(String remote)

 

两个方法貌似相同,实际不同,返回流的那个因为不能马上处理流,所以需要用户手工调用completePendingCommand,而另一个传递流进去的则不需要。可能有同学已经遇到过这个问题了,读写第一个文件时总是正确的,当相同API读写第二个文件时,block住了。这是因为FTPClient要求在进行流操作之后执行completePendingCommand,以确保流处理完毕,因为流处理不是即时的,所以也没有办法不手工调用completePendingCommand。问题是开发者把不返回流的方法末尾加上了completePendingCommand,如果不看代码可能根本不知道。
文档上说:

     * There are a few FTPClient methods that do not complete the
     
* entire sequence of FTP commands to complete a transaction.  These
     
* commands require some action by the programmer after the reception
     
* of a positive intermediate command.  After the programmer's code
     * completes its actions, it must call this method to receive
     
* the completion reply from the server and verify the success of the
     
* entire transaction.


但是这样仍然还是让人有点困惑,为什么都是存储/读取的方法,有时候要调用completePendingCommand,有时候不调用?更严重的问题是completePendingCommand调用了getReply,如果一个命令通过socket stream传了过去但是没有getReply,即没有completePendingCommand,那么下次发命令时,将会受到本次返回码的干扰,得到无效的响应。而如果在completePendingCommand之后又进行了一次无辜的completePendingCommand,那么因为FTP Server上没有Reply了,就会block。所以completePendingCommand并不是可以随意添加的。

现在出现了两个问题:
1 completePendingCommand很容易多出来或遗漏
2 显式调用completePendingCommand暴露了底层实现,给用户带来不便,用户只想要InputStream或者OutputStream

为了解决这个问题,可以对InputStream进行扩展,建立一个ReplyOnCloseInputStream,如下:

private static ReplyOnCloseInputStream extends InputStream{
  
//
  public ReplyOnCloseInputStream(InputStream is, FTPClient c){
    
//
  }

  
//
  @override
  
public void close(){
    
if(c.completePendingCommand){
      is.close();
    }
else{
      
//throw Exception
    }

  }

}
 
//
return new ReplyOnCloseInputStream(is, client);


这样封装之后,FTPClient的用户只需要正常在处理完流之后关闭即可,而不必暴露实现细节。保存文件也可以用相同的方法封装OutputStream。

posted @ 2010-07-07 23:08 杨一 阅读(3431) | 评论 (1)编辑 收藏

关于ThreadLocal的内存泄露

ThreadLocal是一种confinement,confinement和local及immutable都是线程安全的(如果JVM可信的话)。因为对每个线程和value之间存在hash表,而线程数量未知,从表象来看ThreadLocal会存在内存泄露,读了代码,发现实际上也可能会内存泄露。

事实上每个Thread实例都具备一个ThreadLocal的map,以ThreadLocal Instance为key,以绑定的Object为Value。而这个map不是普通的map,它是在ThreadLocal中定义的,它和普通map的最大区别就是它的Entry是针对ThreadLocal弱引用的,即当外部ThreadLocal引用为空时,map就可以把ThreadLocal交给GC回收,从而得到一个null的key。

这个threadlocal内部的map在Thread实例内部维护了ThreadLocal Instance和bind value之间的关系,这个map有threshold,当超过threshold时,map会首先检查内部的ThreadLocal(前文说过,map是弱引用可以释放)是否为null,如果存在null,那么释放引用给gc,这样保留了位置给新的线程。如果不存在slate threadlocal,那么double threshold。除此之外,还有两个机会释放掉已经废弃的threadlocal占用的内存,一是当hash算法得到的table index刚好是一个null key的threadlocal时,直接用新的threadlocal替换掉已经废弃的。另外每次在map中新建一个entry时(即没有和用过的或未清理的entry命中时),会调用cleanSomeSlots来遍历清理空间。此外,当Thread本身销毁时,这个map也一定被销毁了(map在Thread之内),这样内部所有绑定到该线程的ThreadLocal的Object Value因为没有引用继续保持,所以被销毁。

从上可以看出Java已经充分考虑了时间和空间的权衡,但是因为置为null的threadlocal对应的Object Value无法及时回收。map只有到达threshold时或添加entry时才做检查,不似gc是定时检查,不过我们可以手工轮询检查,显式调用map的remove方法,及时的清理废弃的threadlocal内存。需要说明的是,只要不往不用的threadlocal中放入大量数据,问题不大,毕竟还有回收的机制。

综上,废弃threadlocal占用的内存会在3中情况下清理:
1 thread结束,那么与之相关的threadlocal value会被清理
2 GC后,thread.threadlocals(map) threshold超过最大值时,会清理
3 GC后,thread.threadlocals(map) 添加新的Entry时,hash算法没有命中既有Entry时,会清理

那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一个废弃ThreadLocal在map中命中)时。

posted @ 2010-07-02 18:27 杨一 阅读(2263) | 评论 (2)编辑 收藏

关于软件文档,我的看法

文档应该包括两大部分,一部分是清晰的代码结构和注释,比如Concurrent API就是这样,还有一部分是文字文档,包括三个小部分:一是开发文档,应该讲架构和功能;二是索引文档,详细介绍功能和参数,三是用户文档,包括安装和使用说明

文档最困难的莫过于版本的一致性,当软件升级后,一些obsolete的内容和新的feature很难同步。要是架构发生了变化,那就更困难了。一般document team都不是太精于技术,所以也会产生一些问题。

只能说任何事物永远都有改进的空间,但是同样也永远没有达到完美的程度

posted @ 2010-06-29 18:26 杨一 阅读(314) | 评论 (0)编辑 收藏

NIO学习之Web服务器示例

     摘要: 1 根据cpu core数量确定selector数量 2 用一个selector服务accept,其他selector按照core-1分配线程数运行 3 accept selector作为生产者把获得的请求放入队列 4 某个selector作为消费者从blocking queue中取出请求socket channel,并向自己注册 5 当获得read信号时,selector建立工作...  阅读全文

posted @ 2010-06-25 19:19 杨一 阅读(1952) | 评论 (0)编辑 收藏

多线程的知识

多线程的优点:
1 多核利用
2 为单个任务建模方便
3 异步处理不同事件,不必盲等
4 现代的UI也需要它
风险:
1 同步变量易错误
2 因资源限制导致线程活跃性问题
3 因2导致的性能问题
用途:
框架,UI,Backend
线程安全的本质是什么:
并非是线程和锁,这些只是基础结构,本质是如何控制共享变量访问的状态
什么是线程安全:
就是线程之间的执行还没有发生错误,就是没有发生意外
一个线程安全的类本身封装了对类内部方法和变量的异步请求,调用方无需考虑线程安全问题
无状态的变量总是线程安全的
原子性:
完整执行的单元,如不加锁控制,则会发生竞态条件,如不加锁的懒汉单例模式,或者复合操作。
锁,内在锁,重入:
利用synchronized关键字控制访问单元,同一线程可以重入锁内部,避免了面向对象产生的问题。同一变量的所有出现场合应该使用同一个锁来控制。synchronized(lock)。
即使所有方法都用synchronized控制也不能保证线程安全,它可能在调用时编程复合操作。
活跃性和性能问题:
过大的粒度会导致这个问题,用锁进行异步控制,导致了线程的顺序执行。
简单和性能是一对矛盾,需要适当的取舍。不能在没有考虑成熟的情况下,为了性能去牺牲简洁性。
要尽量避免耗时操作,IO和网络操作中使用锁

posted @ 2010-06-25 19:17 杨一 阅读(365) | 评论 (0)编辑 收藏

Ext Store Filter的实现和问题

Store包含两个数据缓存 - snapshot和data,grid,combo等控件的显示全部基于data,而snapshot是数据的完整缓存,当首次应用过滤器时,snapshot从data中备份数据,当应用过滤器时,filter从snapshot获取一份完整的数据,并在其中进行过滤,过滤后的结果形成了data并传递给展示,及data总是过滤后的数据,而snapshot总是完整的数据,不过看名字让人误以为它们的作用正好相反。
相应地,当进行store的增删改时,要同时维护两个缓存。
问题
Store包含两个增加Record的方法,即insert和add,其中的insert没有更新snapshot所以当重新应用filter时,即data被重新定义时,在data中使用insert新增的记录是无效的。
解决方法
用add不要用insert,如果用insert,记得把数据写进snapshot: store.snapshot.addAll(records)

posted @ 2010-06-25 19:16 杨一 阅读(1275) | 评论 (0)编辑 收藏

Ext中Combo组件的联动封装

     摘要: 在Extjs中构造N级联动下拉的麻烦不少,需定制下拉数据并设定响应事件。通过对Combo集合的封装,无需自己配置Combo,只需设定数据和关联层级,即可自动构造出一组支持正向和逆向过滤的联动下拉并获取其中某一个的实例。 如: 数据: Ext.test = {};       Ext.test.lcbdata&nb...  阅读全文

posted @ 2010-06-25 19:14 杨一 阅读(1297) | 评论 (0)编辑 收藏

前端框架动态组件和代码生成之间的选择

目前主流的SSH开发架构中,为减轻开发者工作,便于管理开发过程,往往用到一些公共代码和组件,或者采用了基于模版的代码生成机制,对于后台的DAO,Service等因为架构决定,代码生成必不可少,但是在前端页面的实现上,却可以有两种不同的思路,一种是把配置信息直接封装成更高级别的组建,一种是进行代码生成。请大家讨论一下这两种方案的优劣,这里先抛砖引玉了。

相同点:
配置信息:XML OR 数据库

控件化:
优点:
1 易于添加公共功能
2 修改配置数据直接生效
3 代码结构清晰,对开发者友好
缺点:
1 重组内存中对象结构,性能没有代码生成好(但渲染时间相同)
2 仅能控制组件自身封装的配置,不支持个性化修改,如果配置文件不支持的参数,则控件不支持
3 必须保证每个控件一个配置

代码生成:
优点:
1 性能较好
2 易于定制内容
3 可以只配置一个模版,然后做出多个简单的修改
缺点:
1 不能针对多个页面同时添加公共功能
2 业务修改需要重新生成代码
3 开发者需要修改自动生成的代码,并需要了解一些底层的实现结构

=====================20091029
代码生成并不能提高工作效率,尤其是针对复杂的富客户端开发
开发组件可提提供一种有效的选项,但是在运行效率和内存处理上需要细心处理

posted @ 2010-06-25 19:11 杨一 阅读(440) | 评论 (0)编辑 收藏

Javascript工作流引擎代码及实例

 

最近在学习jBPMJavascript,所以按照一些相关概念自己写了下面的200行代码的“工作流引擎”,工作流管理系统包含了流程定义,引擎,及应用系统三个主要部分,下面的代码实现了流程的分支合并,目前只支持一种环节上的迁移。拷贝到html,双击就可以跑起来。

 

var workflowDef = {
         start:{
                   fn:
"begin"//对应处理方法可以在内部定义,也可以在外部定义
                   next:[
"task1","task2"]
         },
         end:
"end",
         tasks:[{
                   id:
"task1",
                   fn:
function(){
                            alert(
"执行任务一");
                   },
                   before:
function(){
                            alert(
"执行任务一前");
                   },
                   after:
function(){
                            alert(
"执行任务一后");
                   },
                   next:[
"task4","task5"]
         },{
                   id:
"task2",
                   fn:
function(){
                            alert(
"执行任务二");
                   },
                   before:
function(){
                            alert(
"执行任务二前");
                   },
                   after:
function(){
                            alert(
"执行任务二后");
                   },
                   next:[
"task3"]
         },{
                   id:
"task3",
                   fn:
function(){
                            alert(
"执行任务三");
                   },
                   before:
function(){
                            alert(
"执行任务三前");
                   },
                   after:
function(){
                            alert(
"执行任务三后");
                   },
                   
//定义合并的数量
                   merge: 
3,
                   next:
"EOWF"
         },{
                   id:
"task4",
                   fn:
function(){
                            alert(
"执行任务四");
                   },
                   before:
function(){
                            alert(
"执行任务四前");
                   },
                   after:
function(){
                            alert(
"执行任务四后");
                   },
                   next:[
"task3"]
         },{
                   id:
"task5",
                   fn:
function(){
                            alert(
"执行任务五");
                   },
                   before:
function(){
                            alert(
"执行任务五前");
                   },
                   after:
function(){
                            alert(
"执行任务五后");
                   },
                   next:[
"task3"]
         }]
}

 

 

//////////定义引擎////////////

Yi 
= {};
Yi.Utils 
= {};
Yi.Utils.execute 
= function(o){
         
if(typeof o != 'function')
                   eval(o)();
         
else
                   o();
}
//工作流类
Yi.Workflow 
= function(workflowDef){
         
this.def = workflowDef;
         
this.tasks = this.def.tasks;
}
//public按照环节id查找查找
Yi.Workflow.prototype.findTask 
= function(taskId){
         
for(var i=0;i<this.tasks.length;i++){
                   
if(this.tasks[i].id == taskId)
                            
return this.tasks[i];
         }
}
//public启动工作流
Yi.Workflow.prototype.start 
= function(){
         
this.currentTasks = [];
         Yi.Utils.execute(
this.def.start.fn);
         
for(var i=0;i<this.def.start.next.length;i++){
                   
this.currentTasks[i] = this.findTask(this.def.start.next[i]);
                   Yi.Utils.execute(
this.currentTasks[i].before);
         }
}
//private
Yi.Workflow.prototype.findCurrentTaskById 
= function(taskId){
         
for(var i=0;i<this.currentTasks.length;i++){
                   
if(this.currentTasks[i].id == taskId)
                            
return this.currentTasks[i];
         }
         
return null;
}
//private
Yi.Workflow.prototype.removeFromCurrentTasks 
= function(task){
         
var temp = [];
         
for(var i=0;i<this.currentTasks.length;i++){
                   
if(!(this.currentTasks[i] == task))
                            temp.push(
this.currentTasks[i]); 
         }
         
this.currentTasks = temp;
         temp 
= null;
}
//public触发当前环节
Yi.Workflow.prototype.signal 
= function(taskId){
         
//只处理当前活动环节
         
var task = this.findCurrentTaskById(taskId);
         
if(task == null){
                   alert(
"工作流未流转到此环节!");
                   
return;
         }
         
//对于合并的处理
         
if(task.merge != undefined){
                   
if(task.merge != 0){
                            alert(
"工作流流转条件不充分!");
                            
return;
                   }
else{
                            Yi.Utils.execute(task.before);
                   }        
         }
         
//触发当前环节
         Yi.Utils.execute(task.fn);
         
//触发后动作
         Yi.Utils.execute(task.after);
         
//下一步如果工作流结束
         
if(task.next === "EOWF"){
                   Yi.Utils.execute(
this.def.end);
                   
delete this.currentTasks;
                   
return;
         }
         
//遍历下一步环节
         
this.removeFromCurrentTasks(task);
         
for(var i=0;i<task.next.length;i++){
                   
var tempTask = this.findTask(task.next[i]);
                   
if(!tempTask.inCurrentTasks)
                            
this.currentTasks.push(tempTask);
                   
if(tempTask.merge != undefined){
                            tempTask.merge
--;
                            tempTask.inCurrentTasks 
= true;
                   }
                   
else
                            Yi.Utils.execute(tempTask.before);
         }
}
//public获取当前的活动环节
Yi.Workflow.prototype.getCurrentTasks 
= function(){
         
return this.currentTasks;
}
//public获取流程定义
Yi.Workflow.prototype.getDef 
= function(){
         
return this.def;
}

 

////////应用系统///////////////
var wf = new Yi.Workflow(workflowDef);
alert(
"启动工作流");
wf.start();
alert(
"尝试手工执行任务3,返回工作流没有流转到这里");
wf.signal(
"task3");
alert(
"分支开始");
alert(
"手工执行任务1");
wf.signal(
"task1");
alert(
"手工执行任务2");
wf.signal(
"task2");
alert(
"手工执行任务4");
wf.signal(
"task4");
alert(
"手工执行任务5");
wf.signal(
"task5");
alert(
"手工执行任务3");
wf.signal(
"task3");
function begin(){
         alert(
"流程开始,该函数在外部定义");
}
function end(){
         alert(
"流程结束");
}

posted @ 2009-03-06 17:39 杨一 阅读(1978) | 评论 (1)编辑 收藏

Spring Security 2 中动态角色权限的实现

 

安全框架的主体包括两部分即验权和授权。Spring Security2可以很好的实现这两个过程。Spring Security2对其前身acegi最大的改进是提供了自定义的配置标签,通过Security的命名空间定义了httpauthentication-provider等标签,这样做的好处是极大地简化了框架的配置,并很好地隐藏了框架实现的细节,在配置的表述上也更清晰,总体上提高了框架的易用性。

然而,该框架默认的权限配置方式在xml中,又因为新版本隐藏了实现细节,在动态权限的扩展上,能力变小了。在验权过程中,遇到的问题不多。但在授权时,如果是acegi,人们可以通过继承AbstractFilterInvocationDefinitionSource类实现在授权(即资源角色和用户角色的匹配)前,针对资源的角色的获取。而新版本因为用新标签进行了整合,这个过程被默认的类实现隐藏掉了,包括过滤器,资源获取和角色定义等过程都由框架来实现,于是很多人在使用Spring Security2时也想通过改动DefaultFilterInvocationDefinitionSource对资源的获取来实现数据库或文件中的动态的角色。不过这样的改动侵入性比较高,而且还保留了acegi的痕迹,也违背了开闭的原则。

其实,我们完全可以通过Spring Security2 accessManager提供的自定义投票机制来解决这个问题,这样既不影响现有的基于URL的配置,还可以加入自己的动态的权限配置。

         其实现策略如下:

1 定义类DynamicRoleVoter实现AccessDecisionVoter,注入实现接口DynamicRoleProvider(用来定义获取角色的方法)的提供动态角色的类

2 在两个supports方法中返回true

3 vote方法中,有三个参数(Authentication authentication, Object object,

ConfigAttributeDefinition config) 通过第一个获取用户的权限集合,第二个可以获取到资源对象,进而通过DynamicRoleProvider获取到角色集合进行匹配。

4 在配置文件中加入DynamicRoleVoter,如下:

<beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<beans:property name="decisionVoters">
<beans:list>
<beans:bean class="org.springframework.security.vote.RoleVoter" />
<beans:bean class="org.springframework.security.vote.AuthenticatedVoter" />
<beans:bean class="DynamicRoleVoter">
    
<beans:property name="dynamicRoleProvider">
        
<beans:ref local="dynamicRoleProvider"/>
</beans:property>
</beans:bean>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id=” dynamicRoleProvider” class=”…”>
    
……
</beans:bean
>

posted @ 2009-03-04 12:55 杨一 阅读(5051) | 评论 (0)编辑 收藏

实现Ext表单对checkBoxGroup的统一管理

1 对于类型是checkboxgroup的数据,数据库中保存数据的格式是value1,value2...valueN,其中1~N的数据有可能不存在,如果选中则存在,最后拼接成一个串。
在Ext中,通过Record对象向FormPanel中的内置对象BasicForm加载数据时,采用的是setValues方法,而setValues第一步要通过Record中定义的name使用findField方法找到表单元素,遗憾的是,继承了Field的checkboxgroup组件并不能正确的通过getName返回自身引用,所以,需要对getName方法进行重写,此外,为了适应我们采用的数据格式,对于该组件的setValue(被setValues调用)和getValue(获取到已加工的数据,此事后话)也要进行重写。故而对于形如:
{     
   xtype: 'checkboxgroup',   
   name: 'biztype',     
   width: 
220,   
   columns: 
3,   
   fieldLabel: '业务类别',   
   items: [     
       {boxLabel: '类别1', inputValue: '
01'},     
       {boxLabel: '类别2', inputValue: '
02'},     
       {boxLabel: '类别3', inputValue: '
03'},     
       {boxLabel: '类别4', inputValue: '
04'}     
       ]     
 }  

的checkboxgroup定义,需重写类如下:
 
Ext.override(Ext.form.CheckboxGroup,{    
    
//在inputValue中找到定义的内容后,设置到items里的各个checkbox中    
    setValue : function(value){   
        
this.items.each(function(f){   
            
if(value.indexOf(f.inputValue) != -1){   
                f.setValue(
true);   
            }
else{   
                f.setValue(
false);   
            }   
        });   
    },   
    
//以value1,value2的形式拼接group内的值   
    getValue : function(){   
        
var re = "";   
        
this.items.each(function(f){   
            
if(f.getValue() == true){   
                re 
+= f.inputValue + ",";   
            }   
        });   
        
return re.substr(0,re.length - 1);   
    },   
    
//在Field类中定义的getName方法不符合CheckBoxGroup中默认的定义,因此需要重写该方法使其可以被BasicForm找到   
    getName : function(){   
        
return this.name;   
    }   
});

2 通过内置对象basicForm的getValues方法可以获取到一个form的完整json数据,但遗憾的事,这里取到的是dom的raw数据,类似emptyText的数据也会被返回,而Field的getValue方法不存在这个问题,所以如果想要返回一个非raw的json集合,可以给formpanel添加如下方法:
getJsonValue:function(){   
    
var param = '{';   
    
this.getForm().items.each(function(f){   
        
var tmp = '"' + f.getName() + '":"' + f.getValue() + '",';   
        param 
+=  tmp;   
    });   
    param 
= param.substr(0,param.length - 1+ '}';   
    
return param;   
}  

这个方法同样适用于上面定义的checkboxgroup,如此就可以把前后台的数据通过json统一起来了

posted @ 2009-03-04 12:50 杨一 阅读(2229) | 评论 (0)编辑 收藏

富客户端技术中的JavaScript脚本国际化

     摘要: 当前的富客户端可以包含两部分:分别为JSP页面和通过富客户端js组件(如extjs)渲染的组件化窗口页。针对这两部分分别做如下处理: 对于JSP页面的部分采用JSTL标准库的fmt标签,如通过: <fmt:message key="page.login.title"/>这样的形式进行展现,其中message对应的文本在服务端配置,并在web.xml中配置资源文件的位置,也可以采用s...  阅读全文

posted @ 2008-12-23 12:07 杨一 阅读(2562) | 评论 (1)编辑 收藏

windows中不能双击打开jar文件的解决办法

看此文前请保证jar包中有至少一个Main方法入口,及图形化的界面。
并保证META-INF/MANIFEST文件中的Main-Class已经指向之前实现的main方法入口。

最近硬盘坏了,于是重新安装了OS,发现拷贝后的jdk或jre(未经安装的版本),不能打开jar文件执行(jdk版本1.6_11),
于是在打开方式中指向了javaw程序,发现无效,并提示"cannot find main class", 与此同时windows把jar类型的文件关联到了
指定的javaw程序上,上网找了一通,没有人提及这个问题的解决办法,而显然这个问题又不是由开篇中提到的问题导致的。
于是在注册表中当前用户配置中删除了当前jar类型的定义。但是重新尝试后依然无效。

于是重新安装了jdk,发现这次可以打开jar文件了,并且这次用来打开的程序从打开方式来看仍然是javaw。
比较注册表中文件类型的定义,并没有差别。从文件夹选项 -> 文件类型来看终于看到了差别,

高级里面的open操作定义如下:
"C:\Program Files\Java\jre6\bin\javaw.exe" -jar "%1" %*
而如果我们自己选择javaw,默认的open操作是没有 -jar参数的,必须手工加进去。
我们知道java启动jar包的参数是 -jar,但是记得以前javaw是可以直接打开jar的,不知什么时候起也需要带有-jar参数了。

所以对于一个拷贝的绿色jre只要修改一下open操作的定义就可以解决上面的问题了。

解决了上面的问题,又产生了新的问题,之前选择打开的javaw程序在打开方式中丢不掉了,比较多余,这个可以在注册表中修改
在HKEY_CLASSES_ROOT\Applications下面找到响应的程序删除就可以了,原来每次用一个程序打开一个类型的文件windows都会在
注册表中这个地方留下相关的记录

posted @ 2008-12-22 18:19 杨一 阅读(3172) | 评论 (0)编辑 收藏

关于文本协议中二进制到文本的转码

偶然间注意到一个困扰了我很久的问题,那就是如果我不通过Socket而通过应用层的某种基于文本的协议,比如SOAP进行通信的话,
如何传递二进制的数据呢?现在SOA,Web Service等很火,应该会遇到这种问题吧?

现在已知的方法可以通过Base64进行编码,其原理和方法见:
http://baike.baidu.com/view/469071.htm

这种方法采用了字节中的6位进行文本转换,并且在其他论坛上也看到了帖子说淘宝的搜索也采用了这种编码方式进行处理。
但是采用了5位进行转换。并且大胆地给出了5位转码的算法,见:
http://www.javaeye.com/topic/286240

不过这种5位的转换会产生更多多余的字节,6位的转码充分利用了现今的可读文本,可是5位却没有,因为5和8的最小公倍数是40,
所以当每转换40位即5个字节的二进制数据需要8个字节来表示,这样就多产生3个字节,浪费的效率是3/5, 而6位转码浪费的效率是
1/3。而且随着字节增多,转化效率也在下降。可见采用5位转码是一种既浪费空间,又浪费效率的解决方案。在不增加url长度的情况下充分提高效率,6位编码是最佳的。如果可以任意的饿牺牲url长度,
可以把0-9全部拿出来当做标记位,0-9不会单独出现,这样一共有10*26 + 26 = 286 种可能还不包括小写字母,
此外还有=,+,-什么的至少256可以编码8位的字节了,这样处理效率就提高了。

现在把问题优化一下,人类可读无歧义的文本码有0-9,A-Z,a-z共62个
设取出x个作为标志位则(62-x) * x + (62 - x) >= 256
解这个二元一次方程得到:
3.366<=X<=57.634
考虑到编码的文本长度,取x的最小值,即 4
最优解:
用0, 1, 2, 3做为标志位
4-9,A-Z, a-z参与编码并与标志位配合实现8位字节的文本化
可以看到这种方法的转码效率会比较高,但是空间冗余大。

此外其实可用的文本不知62个,包括感叹号等用上后补足64 = 2^6
它的高位是 00
那么只要再找到三个文本符保存其他三个高位01 10 11就可以了
这样的转码空间可以更小一些。


想法还很不成熟,欢迎大家批评

posted @ 2008-12-04 15:56 杨一 阅读(1652) | 评论 (2)编辑 收藏

JSON通用服务端处理

     摘要: 最近在学习JavaScript,发现不论是ext还是prototype都很推崇json这种通信协议的格式,但是这两个框架都是比较偏前端的,和dwr不同,dwr是一个一站式的ajax框架,不仅提供了客户端的工具方法,也包括服务端的配置和通信的处理。 而ext和prototype等仅仅设置好了json的接口并对ajax通信做了封装,相对而言是一种比较“纯粹”的AJAX实现,当...  阅读全文

posted @ 2008-11-24 18:14 杨一 阅读(2224) | 评论 (1)编辑 收藏

软件开发平台及框架的意义

学过软件工程的都知道,软件产品的生产周期是一个经历若干阶段的漫长过程,包括需求获取 - 设计 - 开发 - 维护等等。

需求阶段 - 总想考虑到所有的问题,或是一切按合同办事。但在现实中根本不得能,因此很多公司开始提倡“随需而变”的能力,希望快速的响应用户的需求变化
维护阶段 - 总希望自己开发出来的东西一劳永逸,永远不要再产生任何麻烦,产生了麻烦也不要找到我。甚至有些项目组的人员开发出来一大堆不成熟的产品、项目后撒手不管,走人了事,毫无职业操守,亦是对自身行业声誉(至少是国内IT服务提供商声誉)的一个打击。真正的项目开发不应该这样,一定是非常易于维护的,能够快速地找出问题的所在,或是新需求切入点的所在,然后解决

 

很明显,前面提到的两个问题都要也只能通过设计和开发来解决。问题来了,怎样开发出的软件才能快速地响应需求,易于维护?可以有很多不相冲突的说法,比如解耦,比如通过POJO封装数据等等。这些东西流行开来以后,很多人有疑问,为什么我的项目中一定要用这些框架?我不用这些框架也可以快速的开发出我要的功能,而且更加简单,等等。如果孤立地从设计和开发的角度看这些问题,这种说法并没有错误,但是如果从整个软件开发的生命周期来看,则不是这样。当然,这里还有一个是否“过度设计”的trade-off在里面,不过那又是另一个话题了。

再说说各种各样的平台吧,它们和框架不同,软件体系结构中有一种架构模型即层次模型,我们现在的TCP/IP协议栈即属于这种模型,我们的软件对于平台产品的依赖是一种朝向稳定的依赖,就好像我们在调试代码时往往不会去调试操作系统API的bug一样,因此在开发这种平台层次级别的产品时就没有必要再去采用那些为了保障“企业应用”Web软件生命周期中所采用的方法了,完全可以用最基础,最底层的手段。只要能够做到高效、稳定即可。因此,平台中间件产品的开发必须和应用软件产品分开来看,虽然它们可能都在用Java这种编程语言。

posted @ 2008-10-26 13:24 杨一 阅读(1628) | 评论 (0)编辑 收藏

形式化与自动化

本科读书时,曾听过离散数学老师一句很精彩的论断:“只要能够形式化的东西,就可以自动化”。可是今天我不谈离散数学,倒想说说其他不相关的东西。

你一定听到过“一流的企业卖标准,二流的企业卖品牌,三流的企业卖产品”。

什么是形式化?为什么形式化的东西就可以自动化呢?撇开数学符号不谈,对企业来说,形式化的东西可以是一些规章及做事的方法,生产产品的方法等等。为什么人民币稍一升值,中国的中小制造型企业就要痛苦不堪?因为我们每既没有品牌,更没有标准,拿生产DVD机为例,最初的90年代生产DVD机卖到欧美很赚,可是现在据说不赔钱就不错了,因为要缴纳一大笔的专利费。中国的大多数企业处在整个产业链的最下端,所得的利润的大多数被其他企业剥夺了,因为我们用的是别人的品牌,别人的标准。我们的公司在全球经济中处于“民工”的境地。

回到我们的问题中来,一流企业所做的标准,是生产一种产品的方式和规约,这一层面是抽象世界的最高层次,无法对这层抽象的产生进行形式化,所以是一种创造性的劳动;二流企业所卖的品牌是对一种产品具体生产方法的规约,通过“模板方法模式”的应用,这一层次的抽象的模板可以从上一个层次的形式化中自动化,然后只需形式化具体的操作细节,对于生产细节的形式化,也需要一定的创造性的劳动;三流的企业是一部机器,因为到此为止一切的东西都已经形式化了,只需要有时间和精力的机器去自动完成而已。

让我们好好想想在一个知识经济的社会里,什么事物是创造性的,是只能被形式化而不能被自动化的。因为只有人类的创造性思想不能被机器所取代,这也是为什么机器人无法取代人类的原因。

80年代出生的人应该记得历史教科书中的论述,工业革命时,一些工人去砸毁机器,觉得这些机器剥夺了他们的工作。如果有一天,老板突然来告诉您:你可以离开了,请不要沮丧和懊悔,因为您早该意识到您其实就是一部机器。当然为了防止上面悲剧的发生,早点去从事有创造性的工作吧,停止对于各种软件自动化辅助工具的抱怨和担忧,勇敢地迎接明天。

还记得小时候常看的电影《神鞭》吧!

posted @ 2008-09-12 12:49 杨一 阅读(1488) | 评论 (1)编辑 收藏

<2008年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

导航

公告

本人在blogjava上发表的文章及随笔除特别声明外均为原创或翻译,作品受知识产权法保护并被授权遵从 知识分享协议:署名-非商业性使用-相同方式共享 欢迎转载,请在转载时注明作者姓名(杨一)及出处(www.blogjava.net/yangyi)
/////////////////////////////////////////
我的访问者

常用链接

留言簿(5)

随笔分类(55)

随笔档案(55)

相册

Java

其他技术

生活

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

自强不息


用心 - 珍惜时间,勇于创造