随笔:15 文章:8 评论:1 引用:0
BlogJava 首页 发新随笔
发新文章 联系 聚合管理

2009年1月9日

Adobe的官方文档:http://livedocs.adobe.com/flex/3/html/help.html?content=anttasks_1.html
简介
Adobe Flex Ant tasks提供了一个方便的方式来使用行业标准构建管理工具来构建你的Flex工程。如果你已经使用Ant工程来构建Flex应用程序,你可以使用Flex Ant tasks来替换你的exec或者调用mxmlc和compc编译器的java命令行。如果你还没有使用ant来构建,你可以利用这些自定义任务来快速和容易地为你的Flex应用建立起复杂的构建过程。
Flex Ant tasks功能包括两个编译器任务:mxmlx和compc。你可以使用它们来编译Flex应用,模块,和组件库。除此以外,Flex Ant tasks包括html-wrapper任务,这可以让你生成自定义的HTML包装器和这些包装器的支持文件。
mxmlc和compc Flex Ant tasks继承了java Ant task.所以,你可以在Flex Ant tasks中使用java Ant task的所有属性。这包括fork,maxmemory和classpath。
更多关于使用Ant的信息,参考http://ant.apache.org.
安装
过程非常简单,从Flex SDK ant目录拷贝flexTasks.jar到你的Ant的lib目录。如果你不拷贝,你必须在命令行中使用Ant的-lib选项。
使用Flex Ant Tasks
你可以在你已有的工程或新建一个Ant工程使用Flex Ant tasks。在你的Ant工程中,有3个任务可以使用:
   >>mxmlc -- 调用应用编译器。你可以使用这个编译器来编译Flex应用程序,模块,资源模块,和CSS SWF文件。
   >>compc -- 调用组件编译器。你可以使用这个编译器来编译SWC文件和运行共享库。
   >>html-wrapper --生成HTML包装器和Flex应用的支持文件。通过使用这个任务,你可以选择包装器的类型(是否有深度链接支持,是否有快速安装,是否有播放器检测),和指定应用配置,譬如高度、宽度和背景颜色。

在你的Ant工程中使用自定义的Flex Ant tasks,你必须添加flexTasks.jar文件到你的工程lib目录,然后在taskdef task中指向这个jar文件。一个taskdef task添加了一个任务定义的新集合到你当前的工程中。你使用它来添加那些不是默认Ant安装的任务定义。此外,对大部分工程,你设定FLEX_HOME变量的值,以方便Ant能够找到你的flex-config.xml文件,然后你就能添加frameworks目录到你的source path。

在Ant中使用Flex tasks
1. 添加一个新的taskdef 任务到你的工程。在这个任务中,指定flexTasks.tasks文件作为资源,并将classpath指向flexTasks.jar文件。例如:
<taskdef resource="flexTasks.tasks" classpath="${basedir}/flexTasks/lib/flexTasks.jar" />
2. 定义FLEX_HOME和APP_ROOT属性。使用这两个属性来指向你的FLEX SDK的根目录和应用程序的根目录。虽然不是要求,但是为这两个目录创建属性是一个通用实践,因为你将可能在你的Ant tasks中多次使用它们。例如:
<property name="FLEX_HOME" value="C:/flex/sdk"/>
<property name="APP_ROOT" value="myApps"/>
3.写一个使用Flex Ant tasks的目标。下面这个例子定义了main目标,使用了mxmlc任务来编译Main.mxml文件:
<target name="main">
    <mxmlc file="${APP_ROOT}/Main.mxml" keep-generated-actionscript="true">
        <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml"/>
        <source-path path-element="${FLEX_HOME}/frameworks"/>
    </mxmlc>
</target>
下面是完整的例子:
<?xml version="1.0" encoding="utf-8"?>
<!-- mySimpleBuild.xml -->
<project name="My App Builder" basedir=".">
    <taskdef resource="flexTasks.tasks"
        classpath="${basedir}/flexTasks/lib/flexTasks.jar"/>
    <property name="FLEX_HOME" value="C:/flex/sdk"/>
    <property name="APP_ROOT" value="myApp"/>
    <target name="main">
        <mxmlc file="${APP_ROOT}/Main.mxml" keep-generated-actionscript="true">
            <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml"/>
            <source-path path-element="${FLEX_HOME}/frameworks"/>
        </mxmlc>
    </target>
</project>
这个例子展示了如何为mxmlc任务使用不同类型的选项。你可以指定keep-generated-actionscript选项的值作为mxmlc任务标志的一个属性,因为它没有任何儿子标志。为指定load-config和source-path的值,你创建了mxmlc任务标志的儿子标志。更多关于使用选项的信息,可参考http://livedocs.adobe.com/flex/3/html/help.html?content=anttasks_4.html#207401

4. 执行Ant工程,例如:
> ant -buildfile mySimpleBuild.xml main
如果你不拷贝flexTasks.jar文件到Ant的lib目录,你必须包括这个JAR文件,通过使用Ant的-lib选项,例如:
> ant -lib c:/ant/lib/flexTasks.jar -buildfile mySimpleBuild.xml main

输出如下:
Buildfile: mySimpleBuild.xml
main:
    [mxmlc] Loading configuration file C:\flex\sdk\frameworks\flex-config.xml
    [mxmlc] C:\myfiles\flex2\ant_tests\apps\Main.swf (150035 bytes)
BUILD SUCCESSFUL
Total time: 10 seconds
>
编译器选项
compc和mxmlc编译器共享了一组相似的选项。所以,mxmlc和compc Ant tasks的行为也相似。
你可以通过很多方式为mxmlc和compc Flex tasks指定选项:
>>任务属性
>>单参数选项
>>多参数选项
>>嵌套元素
>>含蓄的文件集

任务属性
为Flex Ant tasks指定选项最简单的方法就是指定名字和命令行选项的值为一个task属性。在下面的例子中,file和keep-generated-actionscript选项被指定为mxmlc任务的属性。
<mxmlc file="${APP_ROOT}/Main.mxml" keep-generated-actionscript="true">
很多mxmlc和compc选项有别名。Flex Ant tasks支持这些选项的所有文档别名。
单参数选项
posted @ 2009-06-19 09:46 TracyLu 阅读(1062) | 评论 (0)编辑 收藏
 
GEF是什么?
GEF(Graphical Editing Framework)允许开发人员从现有的应用模型来创建一个丰富的图形编辑器。GEF由2个插件构成,org.eclipse.draw2dorg.eclipse.geforg.eclipse.draw2d插件为显示图形提供一个布局和渲染的工具箱。然后开发人员能够利用许多GEF提供的通用的操作,或者为特定的域扩展这些操作。 GEF 采用了一个MVC (model-view-controller) 架构,使得简单的改变从视图被适用于模型。 GEF 是完全应用中性的,并且提供了基础来构建几乎任何应用,包括但是不局限于:活动图,GUI构建器,类图编辑器,状态机,甚至于所见即所得的文本编辑器。

特征:
        org.eclipse.draw2d
  • 高效的布局和渲染支持
  • 各种图形和布局实现
  • 边界 
  • 光标和提示支持
  • 连接锚、路由、和装饰。
  • 众多、透明层
  • 灵活的协调系统
  • 概览窗口(缩图与滚动)
  • 打印

org.eclipse.gef

  • 提供了选择、创建、连接、选取框工具 
  • 显示这些工具的调色板 
  • 处理调整对象和弯曲连接 
  • GEF Viewers的两种类型:Graphical和Tree 
  • 一个控制框架,映射业务模型到视图 
    • 插件规则,映射视图与模型的互动 
    • 多种实现来显示反馈和添加选择处理
    • 多种请求类型和工具或者行为来发送请求到控制器。
  • 通过命令和命令堆栈来实现取消/重做支持

 

posted @ 2009-06-09 17:57 TracyLu 阅读(257) | 评论 (0)编辑 收藏
 
提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested Class),关于这两者的区别与联系,在下文中会有对比。内部类从表面上看,就是在类中又定义了一个类(下文会看到,内部类可以在很多地方定义),而实际上并没有那么简单,乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著,但是随着对它的深入了解,你会发现Java的设计者在内部类身上的确是用心良苦。学会使用内部类,是掌握Java高级编程的一部分,它可以让你更优雅地设计你的程序结构。下面从以下几个方面来介绍:
  • 第一次见面

public interface Contents {
    int value();
}

public interface Destination {
    String readLabel();
}

public class Goods {
    private class Content implements Contents {
        private int i = 11;
        public int value() {
            return i;
         }
     }

    protected class GDestination implements Destination {
        private String label;
        private GDestination(String whereTo) {
            label = whereTo;
         }
        public String readLabel() {
            return label;
         }
     }

    public Destination dest(String s) {
        return new GDestination(s);
     }
    public Contents cont() {
        returnnew Content();
     }
}

class TestGoods {
    public static void main(String[] args) {
         Goods p = new Goods();
        Contents c = p.cont();
         Destination d = p.dest("Beijing");
     }
}

     在这个例子里类Content和GDestination被定义在了类Goods内部,并且分别有着protected和private修饰符来控制访问级别。Content代表着Goods的内容,而GDestination代表着Goods的目的地。它们分别实现了两个接口Content和 Destination。在后面的main方法里,直接用 Contents c和Destination d进行操作,你甚至连这两个内部类的名字都没有看见!这样,内部类的第一个好处就体现出来了——隐藏你不想让别人知道的操作,也即封装性。

     同时,我们也发现了在外部类作用范围之外得到内部类对象的第一个方法,那就是利用其外部类的方法创建并返回。上例中的cont()和dest()方法就是这么做的。那么还有没有别的方法呢?当然有,其语法格式如下:

outer Object=new outerClass(Constructor Parameters);

outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);

     注意在创建非静态内部类对象时,一定要先创建起相应的外部类对象。至于原因,也就引出了我们下一个话题——

  • 非静态内部类对象有着指向其外部类对象的引用

对刚才的例子稍作修改:

public class Goods {

   private valueRate=2;

    private class Content implements Contents {
        private int i = 11*valueRate;
        public int value() {
            return i;
         }
     }

    protected class GDestination implements Destination {
        private String label;
        private GDestination(String whereTo) {
            label = whereTo;
         }
        public String readLabel() {
            return label;
         }
     }

    public Destination dest(String s) {
        return new GDestination(s);
     }
    public Contents cont() {
        return new Content();
     }
}

     修改的部分用蓝色显玖恕T谡饫镂颐歉鳪oods类增加了一个private成员变量valueRate,意义是货物的价值系数,在内部类Content的方法value()计算价值时把它乘上。我们发现,value()可以访问valueRate,这也是内部类的第二个好处——一个内部类对象可以访问创建它的外部类对象的内容,甚至包括私有变量!这是一个非常有用的特性,为我们在设计时提供了更多的思路和捷径。要想实现这个功能,内部类对象就必须有指向外部类对象的引用。 Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。

     有人会问,如果内部类里的一个成员变量与外部类的一个成员变量同名,也即外部类的同名成员变量被屏蔽了,怎么办?没事,Java里用如下格式表达外部类的引用:

outerClass.this

有了它,我们就不怕这种屏蔽的情况了。

  • 静态内部类

     和普通的类一样,内部类也可以有静态的。不过和非静态内部类相比,区别就在于静态内部类没有了指向外部的引用。这实际上和C++中的嵌套类很相像了,Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用这一点上,当然从设计的角度以及以它一些细节来讲还有区别。

     除此之外,在任何非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类(内部类的嵌套可以不止一层)。不过静态内部类中却可以拥有这一切。这也算是两者的第二个区别吧。

  • 局部内部类

是的,Java内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。

public class Goods1 {
     public Destination dest(String s) {
          class GDestination implements Destination {
               private String label;
               private GDestination(String whereTo) {
                    label = whereTo;
                }
               public String readLabel() { return label; }
           }
          return new GDestination(s);
      }

     public static void main(String[] args) {
           Goods1 g= new Goods1();
           Destination d = g.dest("Beijing");
      }
}

     上面就是这样一个例子。在方法dest中我们定义了一个内部类,最后由这个方法返回这个内部类的对象。如果我们在用一个内部类的时候仅需要创建它的一个对象并创给外部,就可以这样做。当然,定义在方法中的内部类可以使设计多样化,用途绝不仅仅在这一点。

下面有一个更怪的例子:

public class Goods2{
     privatevoid internalTracking(boolean b) {
           if(b) {
               class TrackingSlip {
                    private String id;
                     TrackingSlip(String s) {
                          id = s;
                     }
                    String getSlip() { return id; }
                }
                TrackingSlip ts = new TrackingSlip("slip");
               String s = ts.getSlip();
           }
      }

     public void track() { internalTracking(true); }

     public staticvoid main(String[] args) {
           Goods2 g= new Goods2();
           g.track();
      }
}

     你不能在if之外创建这个内部类的对象,因为这已经超出了它的作用域。不过在编译的时候,内部类TrackingSlip和其他类一样同时被编译,只不过它由它自己的作用域,超出了这个范围就无效,除此之外它和其他内部类并没有区别。

  • 匿名内部类

     java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组一样,当你只需要创建一个类的对象而且用不上它的名字时,使用内部类可以使代码看上去简洁清楚。它的语法规则是这样的:

new interfacename(){......}; 或 new superclassname(){......};

下面接着前面继续举例子:

public class Goods3 {
     public Contents cont(){
          return newContents(){
               private int i = 11;
               public int value() {
                    return i;
                }
           };
      }
}

这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简洁。

在java的事件处理的匿名适配器中,匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码:

frame.addWindowListener(new WindowAdapter(){
     public void windowClosing(WindowEvent e){
           System.exit(0);
      }
});

     有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。如果你想要初始化它的成员变量,有下面几种方法:

  1. 如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为final。
  2. 将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。
  3. 在这个匿名内部类中使用初始化代码块。
  • 为什么需要内部类?

java内部类有什么好处?为什么需要内部类?

     首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有你直接实现这个接口的功能。

     不过你可能要质疑,更改一下方法的不就行了吗?

     的确,以此作为设计内部类的理由,实在没有说服力。

     真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨java中存在的一个问题——没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果。

    

posted @ 2009-05-18 21:55 TracyLu 阅读(158) | 评论 (0)编辑 收藏
 
FLEX内存释放优化原则:

1. 被删除对象在外部的所有引用一定要被删除干净才能被系统当成垃圾回收处理掉;

2. 父对象内部的子对象被外部其他对象引用了,会导致此子对象不会被删除,子对象不会被删除又会导致了父对象不会被删除;

3. 如果一个对象中引用了外部对象,当自己被删除或者不需要使用此引用对象时,一定要记得把此对象的引用设置为null;

4. 本对象删除不了的原因不一定是自己被引用了,也有可能是自己的孩子被外部引用了,孩子删不掉导致父亲也删不掉;

5. 除了引用需要删除外,系统组件或者全局工具、管理类如果提供了卸载方法的就一定要调用删除内部对象,否则有可能会造成内存泄露和性能损失;

6. 父对象立刻被删除了不代表子对象就会被删除或立刻被删除,可能会在后期被系统自动删除或第二次移除操作时被删除;

7. 如果父对象remove了子对象后没有清除对子对象的引用,子对象一样是不能被删除的,父对象也不能被删除;

8. 注册的事件如果没有被移除不影响自定义的强行回收机制,但有可能会影响正常的回收机制,所以最好是做到注册的事件监听器都要记得移除干净。

9. 父对象被删除了不代表其余子对象都删除了,找到一种状态的泄露代码不等于其他状态就没有泄露了,要各模块各状态逐个进行测试分析,直到测试任何状态下都能删除整个对象为止。

内存泄露举例:

1. 引用泄露:对子对象的引用,外部对本对象或子对象的引用都需要置null;

2. 系统类泄露:使用了系统类而忘记做删除操作了,如BindingUtils.bindSetter(),ChangeWatcher.watch()函数 时候完毕后需要调用ChangeWatcher.unwatch()函数来清除引用 ,否则使用此函数的对象将不会被删除;类似的还有MUSIC,VIDEO,IMAGE,TIMER,EVENT,BINDING等。

3. 效果泄露:当对组件应用效果Effect的时候,当本对象本删除时需要把本对象和子对象上的Effect动画停止掉,然后把Effect的target对象置null; 如果不停止掉动画直接把 Effect置null将不能正常移除对象。

4. SWF泄露:要完全删除一个SWF要调用它的unload()方法并且把对象置null;

5. 图片泄露:当Image对象使用完毕后要把source置null;(为测试);

6. 声音、视频泄露: 当不需要一个音乐或视频是需要停止音乐,删除对象,引用置null;


内存泄露解决方法:

1. 在组件的REMOVED_FROM_STAGE事件回掉中做垃圾处理操作(移除所有对外引用(不管是VO还是组件的都需要删除),删除监听器,调用系统类的清除方法)先remove再置null, 确保被remove或者removeAll后的对象在外部的引用全部释放干净;

2. 利用Flex的性能优化工具Profile来对项目进程进行监控,可知道历史创建过哪些对象,目前有哪些对象没有被删除,创建的数量,占用的内存比例和用量,创建过程等信息;

总结:关键还是要做好清除工作,自己设置的引用自己要记得删除,自己用过的系统类要记得做好回收处理工作。 以上问题解决的好的话不需要自定义强制回收器也有可能被系统正常的自动回收掉。
posted @ 2009-04-01 11:51 TracyLu 阅读(239) | 评论 (0)编辑 收藏
 
        对于业务对象怎么封装的在过去Core J2EE Pattern上,把业务对象的属性都是散列开的。而且其中有个不佳实践,就是把业务对象当中的所有属性都变成一个传输对象(这个传输对象看上去有点像领域模型)。新的技术编程模型下(EJB3.0)应该让业务对象直接持有实体,而不是把每一个属性都列开原因在于,如果把属性都列开,那么业务对象本身应该被管理,为什么呢?如果多个事务或多个现程在访问本来相同的业务对象(标识相同) 的时候,那么由于业务对象不受管,他们创建了各自的实例,彼此不相干,但是当最后提交内容的时候,就可能会导致结果很意外这个同 容器的四种事务隔离级别的意义很类似,如果要将BO的属性都列开,那么,就应该让BO受管,相当于单独实现一个BO的容器,控制并发,控制事务这个容器 要实现多个线程共享一个标识相同的业务对象,并控制并发访问,合适的隔离事务。也就是说要做一个容器应该做的事情应该巧妙的利用容器提供的持久性容器来解决这个问题将实体放到业务对象中,对业务对象的操作,实际上是业务对象的业务方法的操作,而业务方法操纵的是持久化上下文中的实体的状态,每一个被创建出来具有相同标识的业务对象,虽然是不同的实例,但是都包含对同一个实体对象的引用。这样对业务对象状态的变更,实际上是对一个实体对象状态的变更,仍然属于受管状态但是这个时候也一定要搞清楚,业务对象包裹的这个实体的状态:新建、受管、脱管、已删除
posted @ 2009-03-23 17:44 TracyLu 阅读(169) | 评论 (0)编辑 收藏
 

Adobe 官方简体中文帮助地址大全

兄弟们下载附件要用很多银两,银两来的也不容易。

今天 收集了官方帮助文件供大家参考。

到官方在页面的右上角 有 PDF 格式帮助下载


使用 Adobe Flash CS4 Professional
http://help.adobe.com/zh_CN/Flash/10.0_UsingFlash/

扩展 Adobe Flash CS4 Professional
http://help.adobe.com/zh_CN/Flash/10.0_ExtendingFlash/
开发 Adobe Flash Lite 2.x 和 3.x 应用程序
http://help.adobe.com/zh_CN/FlashLite/2.0_FlashLiteDevGuide2/

使用 Flash
http://help.adobe.com/zh_CN/Flash/10.0_Welcome/WS0440A5AD-E290-4bd6-A402-B09F727E9A05.html

Adobe ActionScript 3.0 编程
http://help.adobe.com/zh_CN/ActionScript/3.0_ProgrammingAS3/

使用 Adobe ActionScript 3.0 组件
http://help.adobe.com/zh_CN/ActionScript/3.0_UsingComponentsAS3/

================================================================================
使用 Flash 和 Flex
http://help.adobe.com/zh_CN/Flash/10.0_UsingFlash/WSFD77A256-0DE1-46c7-86FB-CC4A8AE2EAA6.html

安装 Flex 3 SDK
http://help.adobe.com/zh_CN/AIR/1.1/devappsflex/WS5b3ccc516d4fbf351e63e3d118666ade46-7fb8.html

本地化 Flex 内容
http://help.adobe.com/zh_CN/AIR/1.1/devappsflex/WS96A067CA-8F6B-4f4a-95AB-9A6CB9BF8E8A.html
使用 Flex AIR 组件
http://help.adobe.com/zh_CN/AIR/1.1/devappsflex/WS5b3ccc516d4fbf351e63e3d118666ade46-7fb6.html

Adobe AIR 简介
http://help.adobe.com/zh_CN/AIR/1.1/devappsflash/WS5b3ccc516d4fbf351e63e3d118666ade46-7fc4.html

ActionScript 3.0 语言和组件参考
http://help.adobe.com/zh_CN/AS3LCR/Flash_10.0/ExampleInstruct.html

主页
http://help.adobe.com/zh_CN/Flash/10.0_Welcome/index.html

======================================================================================

使用 HTML 和 Ajax 开发 Adobe AIR 应用程序
http://help.adobe.com/zh_CN/AIR/1.1/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7dbb.html
http://help.adobe.com/zh_CN/AIR/1.1/devappshtml/WSC04AFC0E-5BF5-4c2f-8A32-BEDE16E9DF95.html

为 Flash CS3 Professional 提供的 Adobe AIR 更新
http://help.adobe.com/zh_CN/AIR/1.1/devappsflash/WS5b3ccc516d4fbf351e63e3d118666ade46-7f57.html

为 Flash CS3 设置 Adobe AIR 更新
http://help.adobe.com/zh_CN/AIR/1.1/devappsflash/WS5b3ccc516d4fbf351e63e3d118666ade46-7fc3.html
http://help.adobe.com/zh_CN/AIR/1.1/devappsflash/WS5b3ccc516d4fbf351e63e3d118666ade46-7fc3.html#WS5b3ccc516d4fbf351e63e3d118666ade46-7fe1

flash CS4
为 Adobe AIR 发布
http://help.adobe.com/zh_CN/Flash/10.0_UsingFlash/WSF0126B20-BFF4-4c50-9978-BCA47C8C3C3F.html
用 Flash CS3 或 CS4创建您的第一个 AIR 应用程序
http://help.adobe.com/zh_CN/AIR/1.1/devappsflash/WS5b3ccc516d4fbf351e63e3d118676a27cc-7ffb.html

了解 Flash Player 和 AIR 运行时的 3D 功能
http://help.adobe.com/zh_CN/ActionScript/3.0_ProgrammingAS3/WS5467498E-BCF8-454f-8607-A51AD392CC07.html

为 Adobe AIR 发布
http://help.adobe.com/zh_CN/Flash/10.0_UsingFlash/WSF0126B20-BFF4-4c50-9978-BCA47C8C3C3F.html#WS25CC04BA-9718-4e5b-AF38-DAFEF461F2F9

本地化 Flash 内容
http://help.adobe.com/zh_CN/AIR/1.1/devappsflash/WSB2006AB2-8EFA-444e-92FB-DE369263382E.html

Flash Player 和 AIR 任务和系统打印
http://help.adobe.com/zh_CN/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7cc2.html

本地化 Flash 内容
http://help.adobe.com/zh_CN/AIR/1.1/devappsflash/WSB2006AB2-8EFA-444e-92FB-DE369263382E.html
posted @ 2009-03-09 09:50 TracyLu 阅读(353) | 评论 (0)编辑 收藏
 
     摘要: Basics of handling events Introduction to handling events You can think of events as occurrences of any kind in your SWF file that are of interest to you as a programmer. For example, most SWF...  阅读全文
posted @ 2009-03-04 22:38 TracyLu 阅读(343) | 评论 (0)编辑 收藏
 
关键字: java/java编程

 本文是Exception处理的一篇不错的文章,从Java Exception的概念介绍起,依次讲解了Exception的类型(Checked/Unchecked),Exception处理的最佳实现:
1. 选择Checked还是Unchecked的几个经典依据
2. Exception的封装问题
3. 如无必要不要创建自己的Exception
4. 不要用Exception来作流程控制
5. 不要轻易的忽略捕获的Exception
6. 不要简单地捕获顶层的Exception
原文地址:
http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html


关于异常处理的一个问题就是要对何时(when)和如何(how)使用它们做到了然于心。在本文中我将介绍一些关于异常处理的最佳实践,同时我也会涉及到最近争论十分激烈的checked Exception的使用问题。

作为开发员,我们都希望能写出解决问题并且是高质量的代码。不幸的是,一些副作用(side effects)伴随着异常在我们的代码中慢慢滋生。无庸置疑,没有人喜欢副作用(side effects),所以我们很快就用我们自己的方式来避免它,我曾经看到一些聪明的程序员用下面的方式来处理异常:

public void consumeAndForgetAllExceptions(){
try {
...some code that throws exceptions
} catch (Exception ex){
ex.printStacktrace();
}
}

上边的代码有什么问题么?

在回答以前让我们想想怎样才是正确的?是的,一旦程序碰到异常,它就该挂起程序而"做"点什么。那么上边的代码是这样子的么?看吧,它隐瞒了什么?它把所有的"苦水"往肚里咽(在控制台打印出异常信息),然后一切继续,从表面上看就像什么都没有发生过一样......,很显然,上边代码达到的效果并不是我们所期望的。

后来又怎样?

public void someMethod() throws Exception{
}

上边的代码又有什么问题?

很明显,上边的方法体是空的,它不实现任何的功能(没有一句代码),试问一个空方法体能抛出什么异常?当然Java并不阻止你这么干。最近,我也遇到类似的情景,方法声明会抛出异常,但是代码中并没有任何"机会"来"展示"异常。当我问开发员为什么要这样做的时候,他回答我说"我知道,它确实有点那个,但我以前就是这么干的并且它确实能为我工作。"

在C++社区曾经花了数年实践来实践如何使用异常,关于此类的争论在 java社区才刚刚开始。我曾经看到许多Java程序员针对使用异常的问题进行争论。如果对于异常处理不当的话,异常可以大大减慢应用程序的执行速度,因为它将消耗内存和CPU来创建、抛出并捕获异常。如果过分的依赖异常处理,代码对易读和易使用这两方面产生影响,以至于会让我们写出上边两处"糟糕"代码。

异常原理

大体上说,有三种不同的"情景"会导致异常的抛出:

l 编程错误导致异常(Exception due Programming errors): 这种情景下,异常往往处于编程错误(如:NullPointerException 或者 IllegalArgumentException),这时异常一旦抛出,客户端将变得无能为力。

l 客户端代码错误导致异常(Exception due client code errors): 说白点就是客户端试图调用API不允许的操作。

l 资源失败导致异常(Exception due to resource failures): 如内存不足或网络连接失败导致出现异常等。这些异常的出现客户端可以采取相应的措施来恢复应用程序的继续运行。

Java中异常的类型

Java 中定义了两类异常:

l Checked exception: 这类异常都是Exception的子类

l Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是特殊的,它们不能通过client code来试图解决,所以称为Unchecked exception

举个例子,下图为NullPointerException的继承关系:



图中,NullPointerException继承自RuntimeException,所以它是Unchecked exception.

以往我都是应用checked exception多于Unchecked exception,最近,在java社区激起了一场关于checked exception和使用它们的价值的争论。这场争论起源于JAVA是第一个拥有Checked exception的主流OO语言这样一个事实,而C++和C#都是根本没有Checked exception,它们所有的异常都是unchecked。

一个checked exception强迫它的客户端可以抛出并捕获它,一旦客户端不能有效地处理这些被抛出的异常就会给程序的执行带来不期望的负担。
Checked exception还可能带来封装泄漏,看下面的代码:

public List getAllAccounts() throws
FileNotFoundException, SQLException{
...
}

上边的方法抛出两个异常。客户端必须显示的对这两种异常进行捕获和处理即使是在完全不知道这种异常到底是因为文件还是数据库操作引起的情况下。因此,此时的异常处理将导致一种方法和调用之间不合适的耦合。



接下来我会给出几种设计异常的最佳实践 (Best Practises for Designing the API)

1. 当要决定是采用checked exception还是Unchecked exception的时候,你要问自己一个问题,"如果这种异常一旦抛出,客户端会做怎样的补救?"
如果客户端可以通过其他的方法恢复异常,那么这种异常就是checked exception;如果客户端对出现的这种异常无能为力,那么这种异常就是Unchecked exception;从使用上讲,当异常出现的时候要做一些试图恢复它的动作而不要仅仅的打印它的信息,总来的来说,看下表:

Client's reaction when exception happens
Exception type

Client code cannot do anything
Make it an unchecked exception

Client code will take some useful recovery action based on information in exception
Make it a checked exception


此外,尽量使用unchecked exception来处理编程错误:因为unchecked exception不用使客户端代码显示的处理它们,它们自己会在出现的地方挂起程序并打印出异常信息。Java API中提供了丰富的unchecked excetpion,譬如:NullPointerException , IllegalArgumentException 和 IllegalStateException等,因此我一般使用这些标准的异常类而不愿亲自创建新的异常类,这样使我的代码易于理解并避免的过多的消耗内存。

2. 保护封装性(Preserve encapsulation)

不要让你要抛出的checked exception升级到较高的层次。例如,不要让SQLException延伸到业务层。业务层并不需要(不关心?)SQLException。你有两种方法来解决这种问题:

l 转变SQLException为另外一个checked exception,如果客户端并不需要恢复这种异常的话;

l 转变SQLException为一个unchecked exception,如果客户端对这种异常无能为力的话;

多数情况下,客户端代码都是对SQLException无能为力的,因此你要毫不犹豫的把它转变为一个unchecked exception,看看下边的代码:
public void dataAccessCode(){
try{
..some code that throws SQLException
}catch(SQLException ex){
ex.printStacktrace();
}
}


上边的catch块紧紧打印异常信息而没有任何的直接操作,这是情有可原的,因为对于SQLException你还奢望客户端做些什么呢?(但是显然这种就象什么事情都没发生一样的做法是不可取的)那么有没有另外一种更加可行的方法呢?

public void dataAccessCode(){
try{
..some code that throws SQLException
}catch(SQLException ex){
throw new RuntimeException(ex);
}
}

上边的做法是把SQLException转换为RuntimeException,一旦SQLException被抛出,那么程序将抛出RuntimeException,此时程序被挂起并返回客户端异常信息。

如果你有足够的信心恢复它当SQLException被抛出的时候,那么你也可以把它转换为一个有意义的checked exception, 但是我发现在大多时候抛出RuntimeException已经足够用了。

3. 不要创建没有意义的异常(Try not to create new custom exceptions if they do not have useful information for client code.)

看看下面的代码有什么问题?

public class DuplicateUsernameException
extends Exception {}


它除了有一个"意义明确"的名字以外没有任何有用的信息了。不要忘记Exception跟其他的Java类一样,客户端可以调用其中的方法来得到更多的信息。

我们可以为其添加一些必要的方法,如下:

public class DuplicateUsernameException
extends Exception {
public DuplicateUsernameException
(String username){....}
public String requestedUsername(){...}
public String[] availableNames(){...}
}



在新的代码中有两个有用的方法:reqeuestedUsername(),客户但可以通过它得到请求的名称;availableNames(),客户端可以通过它得到一组有用的usernames。这样客户端在得到其返回的信息来明确自己的操作失败的原因。但是如果你不想添加更多的信息,那么你可以抛出一个标准的Exception:

throw new Exception("Username already taken");
更甚的情况,如果你认为客户端并不想用过多的操作而仅仅想看到异常信息,你可以抛出一个unchecked exception:

throw new RuntimeException("Username already taken");

另外,你可以提供一个方法来验证该username是否被占用。

很有必要再重申一下,checked exception应该让客户端从中得到丰富的信息。要想让你的代码更加易读,请倾向于用unchecked excetpion来处理程序中的错误(Prefer unchecked exceptions for all programmatic errors)。

4. Document exceptions.

你可以通过Javadoc's @throws 标签来说明(document)你的API中要抛出checked exception或者unchecked exception。然而,我更倾向于使用来单元测试来说明(document)异常。不管你采用哪中方式,你要让客户端代码知道你的API中所要抛出的异常。这里有一个用单元测试来测试IndexOutOfBoundsException的例子:

public void testIndexOutOfBoundsException() {
ArrayList blankList = new ArrayList();
try {
blankList.get(10);
fail("Should raise an IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException success) {}
}



上边的代码在请求blankList.get(10)的时候会抛出IndexOutOfBoundsException,如果没有被抛出,将fail ("Should raise an IndexOutOfBoundsException")显示说明该测试失败。通过书写测试异常的单元测试,你不但可以看到异常是怎样的工作的,而且你可以让你的代码变得越来越健壮。


下面作者将介绍界中使用异常的最佳实践(Best Practices for Using Exceptions)
1. 总是要做一些清理工作(Always clean up after yourself)

如果你使用一些资源例如数据库连接或者网络连接,请记住要做一些清理工作(如关闭数据库连接或者网络连接),如果你的API抛出Unchecked exception,那么你要用try-finally来做必要的清理工作:

java 代码
  1. public void dataAccessCode(){   
  2. Connection conn = null;   
  3. try{   
  4. conn = getConnection();   
  5. ..some code that throws SQLException   
  6. }catch(SQLException ex){   
  7. ex.printStacktrace();   
  8. finally{   
  9. DBUtil.closeConnection(conn);   
  10. }   
  11. }   
  12.   
  13. class DBUtil{   
  14. public static void closeConnection   
  15. (Connection conn){   
  16. try{   
  17. conn.close();   
  18. catch(SQLException ex){   
  19. logger.error("Cannot close connection");   
  20. throw new RuntimeException(ex);   
  21. }   
  22. }   
  23. }   

DBUtil是一个工具类来关闭Connection.有必要的说的使用的finally的重要性是不管程序是否碰到异常,它都会被执行。在上边的例子中,finally中关闭连接,如果在关闭连接的时候出现错误就抛出RuntimeException.



2. 不要使用异常来控制流程(Never use exceptions for flow control)

下边代码中,MaximumCountReachedException被用于控制流程:

java 代码
  1. public void useExceptionsForFlowControl() {   
  2. try {   
  3. while (true) {   
  4. increaseCount();   
  5. }   
  6. catch (MaximumCountReachedException ex) {   
  7. }   
  8. //Continue execution   
  9. }   
  10.   
  11. public void increaseCount()   
  12. throws MaximumCountReachedException {   
  13. if (count >= 5000)   
  14. throw new MaximumCountReachedException();   
  15. }    

上边的useExceptionsForFlowControl()用一个无限循环来增加count直到抛出异常,这种做法并没有说让代码不易读,但是它是程序执行效率降低。

记住,只在要会抛出异常的地方进行异常处理。



3. 不要忽略异常

当有异常被抛出的时候,如果你不想恢复它,那么你要毫不犹豫的将其转换为unchecked exception,而不是用一个空的catch块或者什么也不做来忽略它,以至于从表面来看象是什么也没有发生一样。



4. 不要捕获顶层的Exception

unchecked exception都是RuntimeException的子类,RuntimeException又继承Exception,因此,如果单纯的捕获Exception,那么你同样也捕获了RuntimeException,如下代码:

try{
..
}catch(Exception ex){
}
一旦你写出了上边的代码(注意catch块是空的),它将忽略所有的异常,包括unchecked exception.

5. Log exceptions just once

Logging the same exception stack trace more than once can confuse the programmer examining the stack trace about the original source of exception. So just log it once.

posted @ 2009-03-03 15:45 TracyLu 阅读(222) | 评论 (0)编辑 收藏
 

级别: 中级

Paul Ilechko (paul.ilechko@us.ibm.com), 高级解决方案架构师, IBM
Mannie Kagan (kagan@ca.ibm.com), WebSphere IT 专家, IBM

2006 年 9 月 25 日

授权体系结构可能非常不合理,存在大量问题。在本文中,您将了解如何调整应用程序的开发工作,以有效地使用 IBM® WebSphere® Application Server 中的授权功能。

引言

在安全方面,身份验证和隐私已基本标准化和产品化。而对于授权,每个应用程序通常都有自己独特的方式,这取决于相关应用程序的具体特征。虽然这样说,但在许多应用程序中,重复使用某些模式也非常常见,在如何有效利用 IBM WebSphere Application Server(及其他应用服务器)提供的各项功能方面,存在着一些最佳实践(尽管内置授权功能并不能完全满足特定场景的业务需求)。

本文总结了一些基本授权模式和 J2EE™ 领域内授权技术的发展情况,并提出了一种常用方法,以解决一般情况下的授权问题。其中的大部分信息都是与产品无关的,不过我们将在合适的时候讨论 WebSphere Application Server 的具体功能。





回页首


什么是授权?

授权就是基于用户的权限或用户的类别(在这种情况下,“用户”可能指某个外部系统,而不一定是个人)管理对受保护系统资源的访问。因此,授权假定已进行了身份验证,因为如果不知道用户是谁,则根本无法提供恰当的访问控制——而不是在通常情况下所有用户都具有相同的权限。稍后我们将详细说明各种授权模式或方式,但首先我们将对其做一个简要的总结。

  • 基于角色的授权

    基于角色的授权基于用户是某类用户的成员这一事实提供对资源的访问。这通常依赖于用户具有在用户注册中心定义的组标识。此方法定义受保护系统的各个组件,并将这些组件映射到能够对其进行访问的用户组。这种类型的授权是 J2EE 领域的标准模式。总的说来,这是一种非常粗粒度的安全形式,用于处理对系统的功能领域的访问,可以由 URL 或对 EJB 组件的方法调用进行定义。这并不是说无法使用角色定义更细粒度的保护,但这样做的效果通常不好。

  • 基于实例的授权

    与 J2EE 基于角色的授权通常提供的授权相比,此模式提供了更细粒度的授权,可以一直细化到项目内的单个对象的级别。我们现在讨论的是数据保护,而不只是功能保护。

  • 基于关系/所有权的授权

    这种类型的授权是基于实例的模式的一个特例,在基于实例的模式中,在应用程序的数据结构内存在用户与某些其他受保护的数据之间的所有权关系。这通常意味着必须将授权规则嵌入到应用程序本身的数据访问逻辑之中。

  • 用户界面自定义

    虽然这不是真正的授权类型,但与此紧密相关的一个问题是对用户界面进行自定义,以使其仅显示允许特定用户看到的特定功能。例如,菜单或门户页将不会显示未授权用户访问的链接或 Portlet。这实际上是一种预先授权;授权询问“我能这样做吗?”,而预先授权则询问一个略为不同的问题“如果我询问是否能进行此操作,答案会是什么?”不过,正如我们稍后将看到的,这两种情况下的编码技术可以完全相同。

J2EE 基于角色的授权

基于角色的授权通过仅将资源提供给已分配相应角色的用户来保护资源。J2EE 提供了通过使用声明和编程技术来完成此任务的标准方法。我们稍后将在授权技术部分中详细讨论 J2EE 安全性。不过,务必理解 J2EE 领域中的角色概念。关键要记住,J2EE 角色不是一组人员。它们是特定于应用程序的逻辑名称,可以在部署时映射到用户和组。角色本身通常表示 J2EE 应用程序中的权限集合。

实际定义哪些用户属于哪些组是在用户注册中心中进行管理的。这可以是企业 LDAP 服务器,也可以是仅由单个应用程序使用的数据库,但用户和组信息的存储库位于 J2EE 环境之外。在 J2EE 应用程序内,您可以基于这些角色定义和命名安全角色及约束资源访问。因此,角色是权限集合;为了使用它,必须在部署应用程序时将其绑定到某一组用户。可以按名称将其映射到一个或多个用户,也可以映射到一个或多个用户组,还可以映射到二者的任意组合。术语主体通常是指用户或用户组。

您将看到,虽然 J2EE 角色可能通过简单的一对一映射紧密地映射到注册中心组,但并不一定非得如此;在最简单的情况下,可以将“manager”角色映射到称为“manager”的组,但 J2EE 角色的强大之处在于,它提供了出色的灵活性,能够在组织发生更改时更改绑定,而无需进行编程更改。以下列情况为例,假定特定的应用程序功能仅对法律部门的员工可用,而这些员工都属于 Department 102。可以在应用程序中创建一个名为“legal”的 J2EE 角色,并绑定到注册中心组“Dept102”。如果以后对该部门进行了重组,其中一半的员工转到了 Department 507,则可以更改绑定,以映射到“Dept102”和“Dept507”。然后,当属于这两个部门的任何员工经过了系统的身份验证后,就会向其授予“legal”角色,系统会向其提供恰当的访问权限。

自定义基于角色的授权

即使使用编程 API,仍然会有因 J2EE 角色模型不够灵活而难以满足业务需求的情况。不过,在草率行事前,应该对以 J2EE 模型为基础(而不是将其替换)进行构建的可能性进行分析。这样做的优势包括:

另外要注意,有时候,可以通过应用服务器安全运行时的其他可插入功能来获得所需的灵活性。例如,如果需要角色-组映射在运行时具有动态性(根据用户登录时提供的信息),则可以使用 JAAS 登录扩展来满足此需求(请参见 Advanced authentication in WebSphere Application Server)。在 WebSphere Application Server V5.1.1 或更高版本中,可以在 Trust Association Interceptor 或 JAAS 登录模块中创建动态组成员身份。不过,如果角色与上下文相关(基于具体的应用程序使用情况),则可能需要购买或构建授权框架。医疗应用程序就是上下文使用模式的一个例子:医生可以一次性登录到系统,但根据所查看的病人或医疗实体(如医院或诊所)的上下文的不同,该医生的角色可能会从检查医师更改为主治医师。此更改是在会话过程中动态更改的,而不仅限于进行身份验证时可用的信息。

只要认为 J2EE 应用程序授权本身不足以解决您的问题,下一个问题就是要确定是否构建或购买授权解决方案。IBM Tivoli® Access Manager 等企业安全产品提供了灵活的、基于策略的功能授权。不过,将这些功能与应用服务器相集成可能有一定的挑战性。您需要考虑的一些问题包括:

  • 是否使用专用 API?
  • 将提供何种功能?
  • 授权请求外部化会带来何种性能损失?

Java™ Authorization Contract for Containers (JACC) 是一种基于标准的方法,用于将外部安全管理器与应用服务器集成。JACC 提供了将安全授权的权限检查委托给外部提供程序的功能。由于授权检查是在容器将控制权交给应用程序前进行的,因此 JACC 具有能够清楚区分自定义授权逻辑和应用程序逻辑的优势,从而满足了关注点分离的需求。不过,使用 JACC 时有一些方面需要特别注意,因此有必要更深入地探讨 JACC,以准确了解其执行的工作以及可对其加以应用的场合。我们稍后将对 JACC 进行更为详细的讨论。

基于实例的授权

顾名思义,基于实例的授权就是将访问权限授予某个对象的特定实例。基于实例的授权通常使用访问控制列表(Access Control List,ACL)来保护实例,而 ACL 存储在某种类型的策略存储区中,并且可用来制定访问决策。可以将 J2EE 角色作为 ACL 使用,不过这个方法可能会不方便。正如我们前面所讨论的,J2EE 角色终究只是一个名称,是可以绑定到任何一组主体的逻辑构造。这种方法不能很好地扩展来大量使用 ACL,也不能处理在应用程序执行时动态修改 ACL 的情况;因为我们曾提到,部署描述符在应用程序启动时以静态方式定义 J2EE 角色。考虑到这些限制,在许多情况下,更好的方法可能是使用外部安全解决方案或 ACL 基于实例的安全性自定义框架。

为了更便于理解,让我们看一个例子。假定我们有一个新的 J2EE 人力资源系统,可允许用户执行打印相关的任务,如将文档发送到打印机和查看与管理打印队列。由于可打印数据的敏感特征,需要进行某些限制。例如,可能存在有关授权哪些用户使用或管理哪些打印机的规则。还可能存在有关在每天的特定时段使用打印机的规则;或许会考虑连夜在特定的打印机上打印敏感材料。

我们可以使用标准的 J2EE 基于角色的安全性来管理打印机访问。例如,可以为打印机和所需访问类型的每种组合定义一个角色,最终得到“Allowed_to_print_to_PrinterX”或“Allowed_to_manage_queue_for_PrinterY”之类的角色。然后,我们必须在应用程序中编写代码,以使用 isUserInRole() 调用遍历所有可能的角色,从而验证当前用户是否被授权执行他们所尝试的任何打印相关操作,而且我们必须将每个角色绑定到相应的用户注册中心条目。

正如您所看到的,这并不是授权需求的一个条理非常清楚的实现。添加任何新打印机都会要求对应用程序进行更改;而有关何人可以进行何种操作的规则的变化则会要求重新将这些角色绑定到主体,并重新部署应用程序。此外,我们尚未开始考虑时段问题。我们需要能够存储与每台打印机对应的可配置信息,但在标准 J2EE 授权模式中,实际上却没有任何地方能够放置此类信息。

现在,如果我们要使用独立的授权服务,这可以极大地简化解决方案,因为我们将只需要询问授权服务,当前用户是否可以对某个对象执行某项操作。所有这些都将外部化,从而极大地简化了编程模型,并使得对外部提供程序的更改以实时的方式反映到正在运行的应用程序中。我们稍后将再次对此问题进行讨论。

所有权关系

直接所有权是用户和某些受保护数据间的一种十分常见的基于实例的关系。例如,在经纪业应用程序中,理财顾问可以查看其个人客户的帐户,但却不能查看公司的其他客户的帐户。部门经理可以查看所属的理财顾问的所有客户的所有帐户,但却不能查看其他部门的客户的帐户,诸如此类。在这种情况下,权限内置到应用程序的数据结构中。

应用程序很少会采用以下这种方式,即向用户界面提供用于检索所有数据的帐户列表,然后再向每一行应用基于 ACL 的权限。这里存在的主要问题是性能:与在 Java 代码中进行筛选相比,在数据库引擎中进行行筛选通常要快得多。尽管缓存可能在一定程度上减轻这种性能影响,但如果使用特定大小的数据块检索数据来支持用户界面分页,则可能出现另一个问题。如果筛选在应用程序中进行,则无法知道请求了多少行,因此可能导致通过多次调用来获取单页数据的情况。

遗憾的是,尽管存在种种缺陷,这种应用程序端筛选方法却被经常使用。如果应用程序使用对象关系映射工具来执行数据访问,然后尝试以某种通用方式将安全机制应用到实例化的类,则尤其可能出现问题。这是对计算资源的一种极为低效的使用,获得可接受的性能和可伸缩性的可能性非常小。

唯一合理的方法是让数据库进行筛选。如果数据库知道最终用户的标识,或通过修改对实际数据访问逻辑的筛选将授权逻辑嵌入到应用程序中(通常通过将相应的项目添加到 SQL 语句的 Where 子句中),则可以通过利用本机数据库授权来实现此目的。取决于应用程序复杂性,或许可以开发一个使用元数据描述授权规则的自定义框架,以自动将安全更改应用到 SQL。如果将存储过程用于数据访问,则这些存储过程也是应用这些规则的最佳场所。

用户界面自定义

动态修改用户界面,以仅向用户显示其能够执行的操作,这样的做法常常也被视为授权的一个方面。虽然这很有争议,但它无疑是一个常见的问题;向用户显示他们未被授权执行的链接的做法肯定非常不好(并可能招致黑客攻击)。

自定义 UI 以删除链接和菜单选项的做法与基于角色的授权非常相似。这通常只是简单地使用 isUserInRole() 调用来遍历各项,从而验证用户是否应该看到此项。还可以采用相同的方式自定义表单上的实际数据,不过这可能会导致进行冗长烦琐的编程,而且不能提供足够的灵活性,具体取决于业务需求。当需要将访问控制自定义与其他类型(如用户首选项)集成时,这种方法可能会变得尤为复杂。如果用户能够访问允许开发自己的特定 UI 组件视图的某种首选项工具,则该工具也需要识别授权规则元数据,以防止用户向其个性化表单添加其不应该具有访问权限的项。正如前面讨论自定义基于角色的授权时所提到的,可能会存在数据视图与上下文相关的情况,并不能仅简单地基于静态角色对其进行调整。





回页首


授权技术

J2EE 同时提供了声明和编程形式的基于角色的授权功能。尽管本文并不打算作为 J2EE 安全方面的教程,但我们将对其主要方面进行简单的总结。

J2EE 声明形式的授权

可将 J2EE 安全角色和约束添加到应用程序的部署描述符。Web 约束添加到 web.xml 中,它们是基于 URL 模式的。EJB 约束添加到 ejb-jar.xml 中,用于定义方法级别的权限。WebSphere Application Server 提供了相应的工具,允许部署人员定义哪些用户和组(按照用户注册中心中的定义)有权访问哪些安全角色。应用服务器运行时使用这些绑定来确定是否向特定的用户授予了某个角色,然后决定是否可访问受保护的资源。

J2EE 编程形式的授权

声明形式的授权简单而强大,但经常并不能提供足够的控制。一旦对用户界面自定义的问题进行分析后,此问题则会变得更为明显。为了提供额外的灵活性,J2EE 提供了 API,以允许开发人员使用下列调用来测试当前用户是否具有访问特定角色的权限:

  • 对于 Servlet 使用 isUserInRole()
  • 对于 EJB 使用 isCallerInRole()

此外,还有用于提供对用户标识的访问的 API 调用:

  • 对于 Servlet 使用 getUserPrincipal()
  • 对于 EJB 使用 getCallerPrincipal()

通过使用这些 API,您可以开发任意复杂的授权模型。在极端的情况下,如果 J2EE 角色信息没有用处,则可以获取用户标识信息,并使用其查找和执行存储在某个其他来源(如数据库或规则引擎)中任意复杂的授权规则。

任何针对以后的 API 编写的代码都可能存在移植性问题,因为 Principal 对象的格式并未标准化,在不同的应用服务器平台上可能会有所不同。

JACC

正如前面所提到的,JACC 提供了一种标准方法来将外部授权提供程序插入到 J2EE 容器中。这样,第三方授权提供程序就能够在用户访问 J2EE 资源时进行授权决策了。

在 JACC 之前,并没有针对应用服务器作出的访问决策的规范。在没有 JACC 的情况下,要使用供应商实现和专用接口进行第三方供应商产品集成。当时并没有标准方法可以使第三方授权提供程序(如 Tivoli Access Manager)插入应用服务器以作出访问决策。为了解决这些问题,Sun Microsystems™ 在 J2EE 1.4 规范中引入了 JACC,从而提供了一项标准机制,以供第三方提供程序从应用程序和应用服务器收集安全策略信息并在运行时实现策略决策。

这些运行时策略决策是通过 java.security.Policy 类作出的。在方法预分派阶段,Web 和 EJB 容器会通过调用 Java Security Manager checkPermission 方法来在运行时执行安全策略;Java Security Manager checkPermission 方法会委托给抽象类中的 implies 方法。JACC 提供程序必须提供实现 implies 方法的具体 Policy 子类。此外,JACC 提供程序还必须实现 PolicyConfiguration 接口,并提供具体 PolicyConfigurationFactory 类。

在部署应用程序时,策略配置权限对象会将自身添加到提供程序中。它们包含容器中配置的与 Web 和 EJB 安全相关的 J2EE 部署描述符信息。这是容器用于将安全策略信息传递给提供程序的机制。

在运行时,如果用户访问 Web 或 EJB 资源,容器将创建相应的 Web 或 EJB 权限对象,并调用提供程序的 Policy 对象的 implies 方法。permission 对象包含有关所访问的资源的信息:如果资源是 Web 资源,则 permission 对象为 URL;如果资源为 EJB,则 permission 对象包含 EJB 对象和方法的名称。封装 Principal 的 Protection Domain 对象也有可能会传递给 Policy 对象。而且,提供程序能够通过容器注册的 PolicyContext 处理程序的 getContext 方法来访问细粒度资源属性,如 EJB 调用的特定参数。提供程序将随后通过向 Policy.implies 方法返回 True 或 False 来负责准许或拒绝对资源的访问。(有关 JACC 的更多信息,请参见 JACC API 规范。)

JACC 最初的目的是为了提供一个标准接口,以便第三方能够插入授权提供程序来作出访问决策。下面让我们看看 JACC 可能能够满足的一系列安全需求。

外部化标准 J2EE 安全模型

标准 J2EE 安全模型包含角色的描述,包括可由主体访问的受安全机制约束的资源。正如前面所提到的,JACC 提供程序可访问此信息,并全面支持这些访问决策的外部化。

扩展标准 J2EE 安全模型

标准模型是静态的。约束和角色在部署受安全保护的应用程序时确定。标准模型中并没有规定如何在部署后修改角色-约束关系。不过,JACC 提供程序并不仅限于标准 J2EE 安全模型的静态特性。JACC 提供程序还具有进行动态的基于运行时的访问决策所必需的自主性和信息。

例如,我们经常要考虑这样的问题:特定角色是否可以对特定的对象执行特定的操作?在讨论 JACC 带来的利弊前,让我们考虑如何将这种关系映射到标准 J2EE 安全性以及 JACC 使用。

标准 J2EE 可以部分地满足此映射,例如,可以将对象 映射到会话 EJB 类,将 操作 映射到特定的安全 EJB 方法。同样,正如我们前面所提到的,此映射是静态 的。

而另一方面,JACC 能够支持上述映射,而且还支持动态的基于实例 的访问控制。例如,可以通过以下方式执行授权检查:将操作参数传递给 EJB 方法调用,以允许动态确定授权检查。我们将在稍后讨论一个示例。

现在,让我们了解一下在考虑 JACC 时需要注意的一些与性能及实现复杂性相关的事项:

  • 在使用 JACC 基于方法参数的访问时可能会存在性能影响。例如,WebSphere Application Server 支持 JACC 规范所要求的所有策略上下文处理程序。不过,由于存在性能影响,除非提供程序专门要求,否则 EJB 参数策略上下文处理程序并不会激活。如果必须为每个 EJB 方法的每个参数创建对象,则会带来性能影响。

  • 实现 JACC 来支持标准 J2EE 安全性之外的功能可能是一项非常复杂的任务。JACC 为实现标准 J2EE 安全模型提供了良好的支持。例如,在 permssion 对象上提供了一些易于使用的有用方法,用于检查传入 permission 对象是否满足 J2EE 部署描述符的策略要求。 另一方面,JACC 并未提供对执行非标准权限检查的支持,而扩展 J2EE 安全模型又将需要这种支持。实现扩展 J2EE 安全模型的 JACC 提供程序是一项复杂的任务,不应轻视。

  • JACC 配置应用于整个计算单元内。任何 JACC 提供程序都必须满足计算单元内的所有应用程序的授权要求。对于其中一些具有标准 J2EE 特征,而一些又有特殊要求的情况,则必须提供同时满足这两组要求的提供程序——而这就增加了复杂性。

现在让我们简单了解一下如何使用 JACC 处理我们的打印机授权问题。我们可以使用具有以下类似方法的“Printer”会话 Facade EJB:

  • print(printerName:string, itemToPrint:string)
  • manageQueue(printerName:string, queueOperation: string)

在我们的 JACC 提供程序内部,可以提供一个 EJB、角色、操作和对象的映射表;例如,通过使用“Printer”EJB,“Managers”能够对“PrinterX”执行“ManageQueue”操作。

能够采用可选的方式使用 J2EE 授权约束对 Facade EJB 方法进行保护。不过,无论是否提供安全保护,都将调用 JACC 提供程序的 Policy.implies 方法。在提供程序内部,一旦完成了常规 J2EE 授权检查,我们就会检查 EJB 是否为映射中使用的 EJB 之一,然后根据我们的映射表来检查 EJB 参数。如果调用已授权,则将从 Policy.implies 方法返回 True。

顺便提一下,在我们的 JACC 提供程序内部,由于我们是在用户的调用线程上进行执行,因此可以通过针对表中的角色(例如,“Managers”)调用常规 J2EE isCallerInRole 方法来检查调用主体是否属于相应角色。

企业安全产品

正如我们前面所讨论的,Tivoli Access Manager 之类的企业安全产品通常设计用于解决一系列授权问题。要实现这个目的,可以采用两种方法,第一种方法是将授权规则从应用程序外部化,并提供 API 来调用授权规则引擎,第二种方法是通过使用 JACC 接口插入 J2EE 容器来透明地调用授权规则引擎,另外,还可以同时使用这两种方法。当产品同时提供 JACC 和基于专用 API 的解决方案时,务必了解每个模型中可用的功能。例如,对于 Tivoli Access Manager,JACC 并没有提供太多的附加值,因为它实际上仅提供了 J2EE 安全检查的委托功能,但却无法将关于访问控制检查的对象的其他信息(如参数)传递给方法调用。在这种情况下,API 方法的功能要丰富得多,但要在可移植性和功能之间进行折衷。

现在让我们回头看看如何使用 Tivoli Access Manager 解决我们的打印机授权问题。

Tivoli Access Manager 提供了定义受保护对象空间和向对象层中任何位置的对象添加 ACL 的功能。在我们的示例中,我们可以为希望保护的打印机定义一个树形结构。ACL 具有与其关联的特定缺省操作(或权限),但如有必要,可以创建自定义操作组。对于我们的场景,可能会随后决定创建一个名为“print_actions”的操作组,并在其上定义专门用于控制打印的权限位,如“q”表示加入打印机的打印队列,“p”表示打印,“v”表示查看打印队列,“d”表示从队列中删除,等等。我们可以随后在对象空间中恰当的位置添加 ACL,并将用户或组添加到这些 ACL 中,以定义可用权限。添加到 ACL 的每个用户或组可以具有不同的活动权限,从而可对任何用户可用的访问权限类型进行适当的细粒度控制。如有必要,我们还可以向任何对象添加受保护对象策略(Protected Object Policy,POP),从而提供额外的保护,如时段访问规则等。

完成了此配置后,剩下的工作就是添加一个简单的 API 来调用应用程序了。这将使用 aznAPI,以便询问 Tivoli Access Manager 是否允许进行某项操作。在此调用中,我们将传递用户凭据、受保护资源的标识,以及所请求的操作,然后 Tivoli Access Manager 将对该对象上的 ACL 和 POP 进行计算,并返回一个 Boolean 值。正如您所看到的,与尝试对基于角色的安全模型进行修改来适应其设计时并未考虑的情况相比,这显然是更为灵活的解决方案。

展望:SAML 和 XACML

尝试确定任何技术将来的发展方向是一件风险很大的事,但安全性断言标记语言(Security Assertion Markup Language,SAML)和可扩展访问控制标记语言(eXtensible Access Control Markup Language,XACML)极有可能成为未来安全策略实现的重要方面。尽管本文并不打算详细描述这些技术,但这里将进行简要的总结,并概括说明在 J2EE 授权领域中这些技术之间的关系。

SAML 通常被认为是一项 Web 服务标准,不过事实上,它却提供了独立于平台和协议的联合标识的总体管理结构。SAML 引入了策略决策点(Policy Decision Point,PDP)和策略执行点(Policy Enforcement Point,PEP)的概念。顾名思义,PEP 是安全体系结构中基于 PDP 中定义的策略执行身份验证和授权规则的位置。XACML 构建于 SAML 之上,提供了用于定义访问控制和授权请求及响应消息的实际语义。SAML 和 XACML 都没有提供用于交付和管理安全服务的基础设施;需由企业安全供应商提供此基础设施。

因此,所面临的问题是:这将如何影响 J2EE 应用服务器的安全性,特别是它如何影响在此领域内的授权?正如我们前面所讨论的,J2EE 应用程序中的授权功能以两种形式提供:应用服务器本身的嵌入式安全引擎和使用 JACC 接口将授权委托给外部提供程序的功能。虽然 J2EE 应用服务器供应商可以在内部使用 XACML 来定义通过应用 J2EE 安全约束创建的授权策略,但这通常是一种貌似神秘的做法,其意义并不大。而更重要的是企业安全解决方案供应商可能支持 XACML——特别是支持请求-响应协议。为了使用目前的 JACC 接口,您需要安装安全服务供应商的特定提供程序,因为并未定义从提供程序到服务的协议。通过使用定义了该协议的 XACML,可以(至少理论上如此)获得能够与任何识别 XACML 的服务进行通信的通用提供程序。





回页首


授权层次

在结束本文之前,我们还需要讨论最后一个问题。在分层应用程序(J2EE 应用程序通常属于此类)中,需要在所有层次执行授权——尤其是在最底层,这一点非常重要。例如,如果用户从浏览器发出了访问 Serlvet 的 URL 请求,而 Servlet 又调用 EJB,则在许多情况下,授权都仅发生在 Servlet 层——或者更糟糕,仅在浏览器内的 JavaScript 代码中进行授权。这可能导致系统不安全,因为黑客可以编写代码来直接调用 EJB,而得以绕过 Web 层。类似地,如果 EJB 受到保护,但数据库没有受到保护,则黑客完全可以绕过应用程序,直接进入核心地带操纵公司的数据。务必考虑系统的每个层次的可能访问路径,同时还要考虑授权如何与其他安全功能(如网络级别的保护)集成。





回页首


结束语

本文说明了 J2EE 应用程序授权中涉及的一些问题,并介绍了 WebSphere Application Server 和其他 J2EE 应用服务器的一些现有标准功能。尽可能使用现有标准将最为有利。虽然现有标准并未提供完整的解决方案,但更为明智的方法是尽可能地对其加以利用,并随后扩展其功能,而不要使用自定义解决方案完全将其替换。授权是一个复杂而颇有难度的主题,因此要慎重地考虑各方面的问题。





回页首


致谢

作者非常感谢 Keys Botzum,在提出本文包含的许多原创想法的过程中,他给予了大量的帮助,而在我们完善本文时他也多次对稿件进行了审阅。



 

参考资料



 

作者简介

 

Paul Ilechko 是 IBM WebSphere 软件服务部的一名高级解决方案架构师。Ilechko 先生拥有 25 年的 IT 行业经验,具有大型机和分布式技术背景。他从 WebSphere 和 J2EE 技术的起始阶段就开始从事这些技术工作。他的主要目标是帮助 IBM 的客户成功地使用这些产品。Ilechko 先生拥有伦敦大学的数学学士学位。


 

Mannie Kagan 是位于加拿大多伦多的 IBM WebSphere 软件服务部 (ISSW) 的一名认证 IT 咨询专家。Mannie 提供与 SOA、WebSphere 中间件、性能管理和安全性相关的咨询服务、技能培训和辅导。您可以通过 kagan@ca.ibm.com 与 Mannie 联系。

posted @ 2009-02-28 15:41 TracyLu 阅读(210) | 评论 (0)编辑 收藏
 
有可能Glassfish 域的安全策略配置需要:
grant {
      permission javax.management.MBeanServerPermission "*";
      permission javax.management.MBeanPermission "*","*";
      permission javax.management.MBeanTrustPermission "*";
};

posted @ 2009-02-26 12:54 TracyLu 阅读(222) | 评论 (0)编辑 收藏
 




PureMVC结构

PureMVC框架的目标很明确,即把程序分为低耦合的三层:Model、View和Controller。
降低模块间的耦合性,各模块如何结合在一起工作对于创建易扩展,易维护的应用程序是非常重要的。

在PureMVC实现的经典MVC元设计模式中,这三部分由三个单例模式类管理,分别是Model、View和Controller。三者合称为核心层或核心角色。

PureMVC中还有另外一个单例模式类——Façade,Façade提供了与核心层通信的唯一接口,以简化开发复杂度。

    Model 与 Proxy

 


 

Model保存对Proxy对象的引用,Proxy负责操作数据模型,与远程服务通信存取数据。
这样保证了Model层的可移植性。

    View 与 Mediator

 


 

View保存对Mediator对象的引用。由Mediator对象来操作具体的视图组件(View Component,例如Flex的DataGrid组件),包括:添加事件监听器,发送或接收Notification ,直接改变视图组件的状态。

这样做实现了把视图和控制它的逻辑分离开来。

    Controller 与 Command

 


 

Controller保存所有Command的映射。Command类是无状态的,只在需要时才被创建。

Command可以获取Proxy对象并与之交互,发送Notification,执行其他的Command。经常用于复杂的或系统范围的操作,如应用程序的“启动”和“关闭”。应用程序的业务逻辑应该在这里实现。


    Façade 与 Core

 


 


Façade类应用单例模式,它负责初始化核心层(Model,View和Controller),并能访问它们的Public方法。

这样,在实际的应用中,你只需继承Façade类创建一个具体的Façade类就可以实现整个MVC模式,并不需要在代码中导入编写Model,View和Controller类。
Proxy、Mediator和Command就可以通过创建的Façade类来相互访问通信。

    Observer 与 Notification



PureMVC的通信并不采用Flash的EventDispatcher/Event,因为PureMVC可能运行在没有Flash Event和EventDispatcher类的环境中,它的通信是使用观察者模式以一种松耦合的方式来实现的。

你可以不用关心PureMVC的Observer/Notification机制是怎么实现的,它已经在框架内部实现了。你只需要使用一个非常简单的方法从Proxy, Mediator, Command和Facade发送Notification,甚至不需要创建一个Notification实例。

 



    Notification可以被用来触发Command的执行


Facade保存了Command与Notification之间的映射。当Notification(通知)被发出时,对应的Command(命令)就会自动地由Controller执行。Command实现复杂的交互,降低View和Model之间的耦合性。


Mediator发送、声明、接收Notification


当用View注册Mediator时,Mediator的listNotifications方法会被调用,以数组形式返回该Mediator对象所关心的所有Notification。
之后,当系统其它角色发出同名的Notification(通知)时,关心这个通知的Mediator都会调用handleNotification方法并将Notification以参数传递到方法。

Proxy发送,但不接收Notification


在很多场合下Proxy需要发送Notification(通知),比如:Proxy从远程服务接收到数据时,发送Notification告诉系统;或当Proxy的数据被更新时,发送Notification告诉系统。

如果让Proxy也侦听Notification(通知)会导致它和View(视图)层、Controller(控制)层的耦合度太高。

View和Controller必须监听Proxy发送的Notification,因为它们的职责是通过可视化的界面使用户能与Proxy持有的数据交互。
不过对View层和Controller层的改变不应该影响到Model层。

例如,一个后台管理程序和一个面向用户程序可能共用一个Model类。如果只是用例不同,那么View/Controller通过传递不同的参数就可以共用相同的Model类。


Façade


MVC元设计模式的核心元素在PureMVC中体现为Model类、View类和Controller类。为了简化程序开发,PureMVC应用了Façade模式。

Façade是Model、View和Controller三者的“经纪人”。实际编写代码时你并不用导入这三者的类文件,也不用直接使用它们。Façade类已经在构造方法包含了对核心MVC三者单例的构造。

一般地,实际的应用程序都有一个Façade子类,这个Façade类对象负责初始化Controller(控制器),建立Command与Notification名之间的映射,并执行一个Command注册所有的Model和View。

    具体Façade是什么样子的?

Façade类应被当成抽象类, 永远不被直接实例化。针对具体的应用程序,你应该具体编写Façade的子类,添加或重写Façade的方法来实现具体的应用。按照惯例,这个类命名为“ApplicationFacade”(当然,命名随你喜欢),如前所述,它主要负责访问和通知Command,Mediator和Proxy。

通常,不同的运行平台都会创建视图结构,尽管创建过程不一样。(比如Flex中MXML程序负责实例化所有子视图组件,Flash影片在Stage上构建可视对象)。视图结构构建完毕时,整个PureMVC机制也已经安置妥当。

创建的Facade子类也被用来简化“启动”的过程。应用程序调用Facade子类的startup方法,并传递自身的一个引用即完成启动,使得应用程序不需要过多了解PureMVC。


为程序创建Façade

Façade类的内容很简单。思考下面的例子:
 1package com.me.myapp
 2{
 3import org.puremvc.as3.interfaces.*;
 4import org.puremvc.as3..patterns.facade.*;
 5import com.me.myapp.view.*;
 6import com.me.myapp.model.*;
 7import com.me.myapp.controller.*;
 8// MyApp程序的Façade类
 9public class ApplicationFacade extends Façade implements IFacade
10{
11//定义Notification(通知)常量
12public static const STARTUP:String = "startup";
13public static const LOGIN:String = "login";
14//得到ApplicationFacade单例的工厂方法
15public static function getInstance() : ApplicationFacade
16{
17if ( instance == null ) instance = new ApplicationFacade( );
18return instance as ApplicationFacade;
19}

20//注册Command,建立Command与Notification之间的映射
21override protected function initializeController( ) : void
22{
23super.initializeController();
24registerCommand( STARTUP, StartupCommand );
25registerCommand( LOGIN, LoginCommand );
26registerCommand( LoginProxy.LOGIN_SUCCESS, GetPrefsCommand );
27}

28//启动PureMVC,在应用程序中调用此方法,并传递应用程序本身的引用 public function startup( app:MyApp ) : void
29{
30sendNotification( STARTUP, app );
31}

32}

33}


上述代码需要注意以下几点:
oApplicationFacade继承自PureMVC的Façade类,Façade类实现了IFacade接口。

o这个例子里ApplicationFacade没有重写构造方法。如果重写构造方法,应该在构造方法里先调用父类的构造方法。

o类方法getInstance用于返回ApplicationFacade的单例,并将实例保存在父类的一个protect变量中。在返回该实例之前必须先把它转化为ApplicationFacade类型。

o定义了Notification名称常量。Façade类是整个系统其他角色相互访问通信的核心,所以在这里定义Notification(通知)名称常量是最合适的。

o初始化Controller(控制器),并建立Command与Notification之间的映射,当Notification(通知)发出时相关的Command(命令)就会被执行。

o提供一个带有应用程序类型参数的startup方法,该参数能过Notification传递到StartupCommand。
posted @ 2009-02-22 14:30 TracyLu 阅读(648) | 评论 (0)编辑 收藏
 

There are times when the system throws a ClassCastException if you are casting an object of type A to type A (where A can be any class within your system). Doesn't make any sense, does it? Well, there is a good explanation to this problem that keeps confusing people working with Java environments especially with JSP:s and servlets.

Reason and technical background#

The understanding of this strange phenomenon goes down to the very basics of the Java virtual machine and its data structures. The cause of ClassCastException is technically a difference between two pointers (or references to be more exact) pointing to instances of java.lang.Class. For each object instance in Java we also need an instance of that java.lang.Class representing its bytecode and static allocations. Typically this instance of Class is shared among all objects of same type, which means that a.getClass() == b.getClass() if objects a and b are type MyClass.

The construction of Class objects is usually hidden from application developers and takes place when the JVM meets new object types (classes) for the first time. For example when the code says "new MyClass();" the system notices that there is no code for MyClass and the class loader gets activated. The loader loads the bytecode, allocates static fields and executes the static initializer. The next step after the class construction is to allocate an actual instance of the class. The JVM now calls the new java.lang.Class instance representing MyClass for a new instance, which allocates some memory for instance variables (non-static fields). One important part of this new allocation is a reference back to the actual class object that defines the object type. You can get this reference with getClass() method of the object to see its content.

In some special cases it is possible that there are two or more Class instances representing the same class (either with the same or different bytecode) with their own static areas. It is a common and incorrect belief that static fields are unique within the same virtual machine. Static areas are part of the class instance and since there can be several instances for the same class they all have their own copies of those fields (this is a fact that can be used to detect this problem). So, if we have an A and a B of types MyClass but their instances of MyClass code are different it means that a.getClass() != b.getClass() and we will get a ClassCastException if the code says:

 MyClass A = (MyClass)vector.elementAt(0); 
where the element is B. Confusing? Ok, let's take a closer look to the code. What is really happening is that "(MyClass)" expects that the parameter object has a reference to the class instance (or its class instance has recursively a base class reference to support inheritance) that would be exactly the same as the one given my the local class loader (this.getClass().getClassLoader()). This is important because although we might have the same class name we would still have wrong static allocations and perhaps even wrong bytecode if the instance would not be the same.

When does this happen?#

  • If there are several ClassLoaders and they do not share all their classes. Classes loaded with separate loaders cannot see Class objects from each other and reload binaries. This is usally the case when you are using applications servers and you have several applications that interact together.
  • If you are loading classes explicitly using Class.forName() or by passing their codes directly to the class loader. Explicit class construction can even be used to reconstruct the situation in question.
  • If you are using a system that automatically reloads classes that have been modified. This is usually the case with JSPs. The Servlet engine re-compiles JSPs and loads them although old instances still remain somewhere in memory. You might also meet this problem with some IDEs for the same reason.

Some possible ways to fix the problem#

You can try to specify the problematic classes in the beginning of the CLASSPATH of the default ClassLoader. This usually prevents custom loaders from creating their local copies because default classes are usually visible for all of them.

In case of the rare JSP problems it might be enough if you just restart the servlet engine after page modifications to dispose old classes.

Use explicit ClassLoader definitions in your code to makes sure that you are using the correct ClassLoader for all classes.

If you are getting this problem because you have written your own ClassLoader it usually means that you are not accessing the base loader properly. By default you should always ask if the default ClassLoader already has an instance for the requested class type.

If you are type casting an ActionForm to the appropriate bean, then you can simply use BeanUtils.getProperty(obj, property) to get the value.

Original author: Asser (Fixed some typos. -- ebu)
=================================================================================
Sun Glassfish Enterprise Server V2UR2 Class Loader Hieriachy:




So to avoid A 2 A ClassCastException, the class will be casted between deployed MBean and LifeCycle Module, we can put the class and dependency classes into Shared Chain ClassPath. I solved my problem this way.

posted @ 2009-02-19 08:48 TracyLu 阅读(226) | 评论 (0)编辑 收藏
 

  非可变对象一旦创建之后就不能再被改变,可变对象则可以在创建之后被改变。String对象是非可变对象,StringBuffer对象则是可变对象。为获得更佳的性能你需要根据实际情况小心谨慎地选择到底使用这两者中的某一个。下面的话题会作详细的阐述。(注意:这个章节假设读者已经具备Java的String和StringBuffer的相关基础知识。)
 
创建字符串的较佳途径
你可以按照以下方式创建字符串对象:
1. String s1 = "hello"; 
    String s2 = "hello"; 
2. String s3 = new String("hello");
    String s4 = new String("hello");
 
上面哪种方式会带来更好的性能呢?下面的代码片断用来测量二者之间的区别。

StringTest1.java
package com.performance.string;
/** This class shows the time taken for creation of
 *  String literals and String objects.
 */
public class StringTest1 {
public static void main(String[] args){
    // create String literals
    long startTime = System.currentTimeMillis();
    for(int i=0;i<50000;i++){
    String s1 = "hello";
    String s2 = "hello";
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time taken for creation of String literals : "
                  + (endTime - startTime) + " milli seconds" );
    // create String objects using 'new' keyword       
    long startTime1 = System.currentTimeMillis();
    for(int i=0;i<50000;i++){
    String s3 = new String("hello");
    String s4 = new String("hello");
    }
    long endTime1 = System.currentTimeMillis();
    System.out.println("Time taken for creation of String objects : "
                  + (endTime1 - startTime1)+" milli seconds");
    }
}
这段代码的输出:
Time taken for creation of String literals : 0 milli seconds
Time taken for creation of String objects : 170 milli seconds
 
JVM是怎样处理字符串的呢?
  Java虚拟机会维护一个内部的滞留字符串对象的列表(唯一字符串的池)来避免在堆内存中产生重复的String对象。当JVM从class文件里加载字符串字面量并执行的时候,它会先检查一下当前的字符串是否已经存在于滞留字符串列表,如果已经存在,那就不会再创建一个新的String对象而是将引用指向已经存在的String对象,JVM会在内部为字符串字面量作这种检查,但并不会为通过new关键字创建的String对象作这种检查。当然你可以明确地使用String.intern()方法强制JVM为通过 new关键字创建的String对象作这样的检查。这样可以强制JVM检查内部列表而使用已有的String对象。
  所以结论是,JVM会内在地为字符串字面量维护一些唯一的String对象,程序员不需要为字符串字面量而发愁,但是可能会被一些通过 new关键字创建的String对象而困扰,不过他们可以使用intern()方法来避免在堆内存上创建重复的String对象来改善Java的运行性能。下一小节会向大家展示更多的信息。
 
下图展示了未使用intern()方法来创建字符串的情况。
 
string_creating_without_intern() method
  你可以自己使用==操作符和String.equals()方法来编码测试上面提到的区别。==操作符会返回true如果一些引用指向一个相同的对象但不会判断String对象的内容是否相同;String.equals()方法会返回true如果被操作的String对象的内容相同。对于上面的代码会有s1==s2,因为s1和s2两个引用指向同一个对象,对于上面的代码,s3.equals(s4)会返回true因为两个对象的内容都一样为”hello”。你可以从上图看出这种机制。在这里有三个独立的包含了相同的内容(”hello”)的对象,实际上我们不需要这么三个独立的对象—— 因为要运行它们的话既浪费时间又浪费内存。
 
  那么怎样才能确保String对象不会重复呢?下一个话题会涵盖对于内建String机制的兴趣。
 
滞留字符串的优化作用
  同一个字符串对象被重复地创建是不必要的,String.intern ()方法可以避免这种情况。下图说明了String.intern()方法是如何工作的,String.intern()方法检查字符串对象的存在性,如果需要的字符串对象已经存在,那么它会将引用指向已经存在的字符串对象而不是重新创建一个。下图描绘了使用了intern()方法的字符串字面量和字符串对象的创建情况。
 
string_creating_with_intern() method
下面的例程帮助大家了解String.intern()方法的重要性。
StringTest2.java
 
package com.performance.string;
// This class shows the use of intern() method to improve performance
public class StringTest2 {
public static void main(String[] args){
    // create String references like s1,s2,s3...so on..
    String variables[] = new String[50000];
    for( int i=0;i<variables.length;i++){
        variables[i] = "s"+i;
    }
    // create String literals
    long startTime0 = System.currentTimeMillis();
    for(int i=0;i<variables.length;i++){
        variables[i] = "hello";
    }
    long endTime0 = System.currentTimeMillis();
    System.out.println("Time taken for creation of String literals : "
                         + (endTime0 - startTime0) + " milli seconds" );
    // create String objects using 'new' keyword       
    long startTime1 = System.currentTimeMillis();
    for(int i=0;i<variables.length;i++){
        variables[i] = new String("hello");
    }
    long endTime1 = System.currentTimeMillis();
    System.out.println("Time taken for creation of String objects with 'new' key word : "
                        + (endTime1 - startTime1)+" milli seconds");
    // intern String objects with intern() method   
    long startTime2 = System.currentTimeMillis();
    for(int i=0;i<variables.length;i++){
        variables[i] = new String("hello");
        variables[i] = variables[i].intern();
    }
    long endTime2 = System.currentTimeMillis();
    System.out.println("Time taken for creation of String objects with intern(): "
                        + (endTime2 - startTime2)+" milli seconds");
    }
}
这是上面那段代码的输出结果:
Time taken for creation of String literals : 0 milli seconds
Time taken for creation of String objects with 'new' key word : 160 milli seconds
Time taken for creation of String objects with intern(): 60 milli seconds
 
连接字符串时候的优化技巧
  你可以使用+操作符或者String.concat()或者StringBuffer.append()等办法来连接多个字符串,那一种办法具有最佳的性能呢?
  如何作出选择取决于两种情景,第一种情景是需要连接的字符串是在编译期决定的还是在运行期决定的,第二种情景是你使用的是 StringBuffer还是String。通常程序员会认为StringBuffer.append()方法会优于+操作符或 String.concat()方法,但是在一些特定的情况下这个假想是不成立的。
 
1) 第一种情景:编译期决定相对于运行期决定
请看下面的StringTest3.java代码和输出结果。

package com.performance.string;
/** This class shows the time taken by string concatenation at compile time and run time.*/
public class StringTest3 {
  public static void main(String[] args){
    //Test the String Concatination
    long startTime = System.currentTimeMillis();
    for(int i=0;i<5000;i++){
    String result = "This is"+ "testing the"+ "difference"+ "between"+
            "String"+ "and"+ "StringBuffer";
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time taken for string concatenation using + operator : "
         + (endTime - startTime)+ " milli seconds");
    //Test the StringBuffer Concatination
    long startTime1 = System.currentTimeMillis();
    for(int i=0;i<5000;i++){
    StringBuffer result = new StringBuffer();
         result.append("This is");
        result.append("testing the");
        result.append("difference");
        result.append("between");
       result.append("String");
       result.append("and");
       result.append("StringBuffer");
     }
    long endTime1 = System.currentTimeMillis();
    System.out.println("Time taken for String concatenation using StringBuffer : "
           + (endTime1 - startTime1)+ " milli seconds");
  }
}
这是上面的代码的输出结果:
Time taken for String concatenation using + operator : 0 milli seconds
Time taken for String concatenation using StringBuffer : 50 milli seconds
很有趣地,+操作符居然比StringBuffer.append()方法要快,为什么呢?
 
  这里编译器的优化起了关键作用,编译器像下面举例的那样简单地在编译期连接多个字符串。它使用编译期决定取代运行期决定,在你使用new关键字来创建String对象的时候也是如此。
 
编译前:
String result = "This is"+"testing the"+"difference"+"between"+"String"+"and"+"StringBuffer";
编译后:
String result = "This is testing the difference between String and StringBuffer";

这里String对象在编译期就决定了而StringBuffer对象是在运行期决定的。运行期决定需要额外的开销当字符串的值无法预先知道的时候,编译期决定作用于字符串的值可以预先知道的时候,下面是一个例子。
 
编译前:
public String getString(String str1,String str2) {
    return str1+str2;
}
编译后:
return new StringBuffer().append(str1).append(str2).toString();
运行期决定需要更多的时间来运行。
 
2) 第二种情景:使用StringBuffer取代String
看看下面的代码你会发现与情景一相反的结果——连接多个字符串的时候StringBuffer要比String快。
StringTest4.java
 
package com.performance.string;
/** This class shows the time taken by string concatenation
using + operator and StringBuffer  */
public class StringTest4 {
 public static void main(String[] args){
    //Test the String Concatenation using + operator
    long startTime = System.currentTimeMillis();
    String result = "hello";
    for(int i=0;i<1500;i++){
        result += "hello";
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time taken for string concatenation using + operator : "
                  + (endTime - startTime)+ " milli seconds");
    //Test the String Concatenation using StringBuffer
    long startTime1 = System.currentTimeMillis();
    StringBuffer result1 = new StringBuffer("hello");
    for(int i=0;i<1500;i++){
        result1.append("hello");
    }
    long endTime1 = System.currentTimeMillis();
    System.out.println("Time taken for string concatenation using StringBuffer :  "
                  + (endTime1 - startTime1)+ " milli seconds");
    }
}
这是上面的代码的输出结果:
Time taken for string concatenation using + operator : 280 milli seconds
Time taken for String concatenation using StringBuffer : 0 milli seconds
看得出StringBuffer.append()方法要比+操作符要快得多,为什么呢?

  原因是两者都是在运行期决定字符串对象,但是+操作符使用不同于StringBuffer.append()的规则通过String和StringBuffer来完成字符串连接操作。(译注:什么样的规则呢?)
 
借助StringBuffer的初始化过程的优化技巧
  你可以通过StringBuffer的构造函数来设定它的初始化容量,这样可以明显地提升性能。这里提到的构造函数是StringBuffer(int length),length参数表示当前的StringBuffer能保持的字符数量。你也可以使用ensureCapacity(int minimumcapacity)方法在StringBuffer对象创建之后设置它的容量。首先我们看看StringBuffer的缺省行为,然后再找出一条更好的提升性能的途径。
 
StringBuffer的缺省行为:
  StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候,因为没有设置初始化字符长度,StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2*旧值+2)。
  如果你使用缺省值,初始化之后接着往里面追加字符,在你追加到第16个字符的时候它会将容量增加到34(2*16+2),当追加到34个字符的时候就会将容量增加到70(2*34+2)。无论何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和新字符都拷贝一遍——这也太昂贵了点。所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来立竿见影的性能增益。
  我利用两个StringBuffer重新测试了上面的StringTest4.java代码,一个未使用初始化容量值而另一个使用了。这次我追加了50000个’hello’对象没有使用+操作符。区别是我使用StringBuffer(250000)的构造函数来初始化第二个 StringBuffer了。
 
输出结果如下:
Time taken for String concatenation using StringBuffer with out setting size: 280 milli seconds
Time taken for String concatenation using StringBuffer with setting size: 0 milli seconds
StringBuffer初始化过程的调整的作用由此可见一斑。所以,使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。
 
关键点
1. 无论何时只要可能的话使用字符串字面量来常见字符串而不是使用new关键字来创建字符串。
2. 无论何时当你要使用new关键字来创建很多内容重复的字符串的话,请使用String.intern()方法。
3. +操作符会为字符串连接提供最佳的性能——当字符串是在编译期决定的时候。
4. 如果字符串在运行期决定,使用一个合适的初期容量值初始化的StringBuffer会为字符串连接提供最佳的性能。

posted @ 2009-02-14 16:22 TracyLu 阅读(140) | 评论 (0)编辑 收藏
 
        Java管理扩展(也叫做JMX规范)在Java编程语言中定义了应用程序以及网络管理和监控的体系结构、设计模式、应用程序接口以及服务。本章介绍所有这些元素,宽泛地呈现这些扩展。JMX规范为跨所有企业的Java开发者编写java工具、创建智能代理、实现分布式管理中间件、管理程序以及向现存管理和监控系统平滑集成上述方案提供了方法。此外,JMX规范针对现存的标准管理和监控技术参考了大量的Java APIs。
  
      需要注意,全文余下的部分所提到的文档和管理的概念均因用自管理和监控服务。

      JMX 架构被分成3个层次:

     设备层(Instrumentation Level)
     代理层(Agent Level)
     分布式服务层(Distributed Service Level)    

     本章对各层加以介绍并描述他们基本的组件。

      1.1 JMX架构的优点
    
      通过执行JMX规范,将从JMX规范中获得下列益处:

      (1)使得Java应用程序在无巨大投资的情况下能够被管控
      JMX架构依赖于一个核心受控对象服务器,该服务器作为一个管理代理运行在大多数Java使能的设备上。这使得Java应用程序能够对其设计影响很小的情况下被管控。一个Java应用程序只需简单的嵌入一个受控对象服务器并且将其一些功能设计成为为一个或多个注册在对象服务器中的受控Beans(MBeans)即可。这些就是从管理基础结构中受益所需花费的全部成本。
    JMX提供了一个标准的方法以使任何基于Java的应用程序、服务或者设备受管。比如,企业级JavaBean应用程序能够遵从JMX架构以使其可以被管理。

     (2)提供了可升级扩展的管理体系结构
     每一个JMX代理服务都是一个独立的模块,满足必要条件后,可以插入到管理代理中。这种基于组件的方法意味着JMX解决方案可以从节省空间的设备升级扩展到电信领域的交换机,并可以向更大级别继续升级扩展。
     JMX规范提供了一系列核心的代理服务。额外的服务将会Comformant Implementations和众多管理方案集大成者来开发。所有这些服务都可以在管理基础架构中被动态的加载,卸载和更新。

     (3)集成现有的管理方案
     JMX智能代理可以通过HTML浏览器或者大量管理协议,如SNMP和WBEM,实现受控。JMX API是开放的接口,任何管理系统供应商都可以使用。
  
    (4)增强现有标准的Java技术
     任何时候只要需要,JMX规范将会参考现有的Java规范,如JNDI,JDBC,JTS等等。

    (5)丰富未来的管理概念
    JMX规范的API可以灵活动态的实现管理方案,通过Java编程语言,来增强显现的技术。例如,JMX方案能够使用查找和发现服务和一些协议,如Jini网络技术,UPNP和服务定位协议。
    在Sun公司给出的演示中,Jini网络技术提供了对网络中的资源和服务的自发发现技术,然后这些资源和服务通过一个JMX应用程序被管理起来。将这两种能力组合起来被称为Sun 自发管理软件。

    (6)仅定义了管理必备的接口
    JMX API并没有设计成通用目的的分布式对象系统。虽然它提供了很多被设计成为适用于分布式环境的服务,这些的目标依然是提供网络,系统以及应用程序的管理功能。

1.2 规范的范围
JMX规范定义了一个管理体系结构以及一套描述了该架构中组件的API。这些API涵盖了管理者和代理两方面的功能,向管理应用程序开发人员提供了应允的的实现。

这篇JMX规范文档陈述了管理体系架构中的整个三个层次。他们是:

(1)设备规范
(2)代理规范
(3)分布式服务规范

1.2.1引用实现
引用实现是第一个可工作的实现了JMX规范的应用程序,委托给JCP定义Java编程语言的扩展。引用实现是由Sun公司开发,作为JMX规范的领军团体,针对设备以及代理两方面规范进行的实现。

对于JMX 1.3版本的API, 引用实现对于规范来说是不可分割的一部分。那个引用实现现在已成为编号为“Mustang”的Java平台标准第六版的一部分。

1.2.2兼容测试套件
JMX规范的兼容测试套件(CTS)用来检查是否和JMX实现标准吻合。这一套件也同样委托给JCP。这个套件检验了声称遵从某一具体JMX规范部分的应用程序的每一个规范点。该套件也是Sun公司开发,并由其主导。

由于JMX规范定义的类对Java平台来说是可选的包,CTS作为一个兼容性工具包由JavaTest软件来运行。

JMX规范的每一个部分都能够确定为必选和可选组件。一个JMX兼容的实现必须提供所有必选服务,并且提供可悬服务的任何子集,但是这些必须遵从规范。

当声明了JMX兼容性,实现将列出所支持的可选服务,并且经过CTS对于规范性陈述的测试。这要求了CTS中的一些模块能够针对很多那种仅近是规范自集的实现来进行测试。


1.2.3 JMX API 作为Java平台的一部分

对于5.0的Java 2平台,编号为“Tiger”的标准版,JMX API和 JMX Remote API是该平台核心的一部分。Tiger包含了JMX版本1.2的API和版本1.0的JMX Remote API。本规范所覆盖的API被Java平台6所囊括。任何一个代替品必须通过CTS的每一项测试。

该规范对于MXBeans部分,定义了注释将产生的一些行为,如出现了@java.beans.ConstructorProperties。而该注视并未出现在Tiger平台当中,产生的行为问题不会出现在该平台中。

1.3 架构总观

 
posted @ 2009-02-13 17:02 TracyLu 阅读(660) | 评论 (0)编辑 收藏
 
posted @ 2009-02-13 16:03 TracyLu 阅读(114) | 评论 (0)编辑 收藏
 
Just browsing,thanks. 谢谢,我只是逛逛。
No,thanks.Just browsing.Maybe later Imight need your help.不,谢谢。我只是逛逛。可能过一会需要你的帮忙。
No,Just browsing.I have nothing to buy.不,我只是逛逛,不想买什么。
posted @ 2009-02-13 15:46 TracyLu 阅读(104) | 评论 (0)编辑 收藏
 
public class MakeDir{
   
public static void main(){
     String fileString 
= "c:\\test\\do\\test.txt";
     File file 
= new File("fileString");
     MakeDir makeDir = new MakeDir();
         makeDir.make(file);
   }

   
public void make(File file){
     
if(file.exists()){
        
if(file.isFile()){
           file.delete();
        }
else{
           
return;
        }

     }
else{
        
if(file.getParentFile().exists()){
           
return;
        }
else{
           make(file.getParentFile());
           file.getParentFile.mkdir();
           
return;
        }
  
     }

   }

}
posted @ 2009-02-13 14:53 TracyLu 阅读(150) | 评论 (0)编辑 收藏
 

1、java.lang包:java的核心类库,包含了运行java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包
2、java.io包:java语言的标准输入/输出类库,如基本输入/输出流、文件输入/输出、过滤输入/输出流等等
3、java.util包:包含如处理时间的date类,处理变成数组的Vector类,以及stack和HashTable类
4、java.awt包:构建图形用户界面(GUI)的类库,低级绘图操作Graphics类,图形界面组件和布局管理如Checkbox类、Container类、LayoutManger接口等,以及界面用户交互控制和事件响应,如Event类
5、java.awt.image包:处理和操纵来自于网上的图片的java工具类库
6、java.applet包
7、java.net包:实现网络功能的类库有Socket类、ServerSocket类
8、java.lang.reflect包:提供用于反射对象的工具
9、java.util.zip包:实现文件压缩功能
10、java.awt.datatransfer包:处理数据传输的工具类,包括剪贴板,字符串发送器等
11、java.awt.event包
12、java.sql包:实现JDBC的类库

posted @ 2009-02-12 14:41 TracyLu 阅读(145) | 评论 (0)编辑 收藏
 
类介绍     http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/lang/ClassLoader.html#name 

相关文章:http://blog.csdn.net/luanxj/archive/2008/02/15/2096657.aspx
                    http://tech.ccidnet.com/art/5929/20071106/1266135_1.html
                    http://www.jdon.com/article/15456.html
posted @ 2009-02-12 14:21 TracyLu 阅读(99) | 评论 (0)编辑 收藏
 
越来越发现,设计是一种艺术,需要很细致的推敲,拿构造函数举例,设计一个类的构造函数也很讲究,是否应该公开默认的构造函数,该类是否有在创建时就必须非空的属性,这些非空的属性中,哪些是必须有意义的数据,哪些必须赋值,哪些默认即可,哪些属性不能更改,哪些属性是系统生成的并且对用户透明,都会影响构造器的设计。
posted @ 2009-02-11 18:01 TracyLu 阅读(129) | 评论 (0)编辑 收藏
 
        将字符串写入文本文件
        
写入文本数据与写入对象是很类似的,你可以使用FileWriter来代替FileOutputStream。
         例子如下:
 1package serial;
 2
 3import java.io.FileWriter;
 4import java.io.IOException;
 5
 6/**
 7 *
 8 * @author Administrator
 9 */

10public class WriteAFile {
11
12    public static void main(String[] args) {
13        FileWriter fw;
14        try {
15            fw = new FileWriter("Storage.txt");
16            fw.write("Hu lu is coming back");
17            fw.close();
18        }
 catch (IOException ex) {
19            ex.printStackTrace();
20        }

21
22    }

23}

        输入/输出相关的操作都必须包含在try/catch块中。在工程目录下找到"storage.txt",内容即为"Hu lu is coming back"。
        
        
posted @ 2009-01-09 22:35 TracyLu 阅读(134) | 评论 (0)编辑 收藏
 
     摘要:         对象有状态和行为两种属性。行为存在于类中,而状态存在于个别的对象中。存储状态的选择有很多种,这可能要看你如何使用存储下来的状态而决定。下面我们讨论两种选项:         如果只有自己写的Java程序会用到这些数据:用序列化(Se...  阅读全文
posted @ 2009-01-09 19:16 TracyLu 阅读(235) | 评论 (0)编辑 收藏
CALENDER
<2009年1月>
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567

常用链接

留言簿(2)

随笔分类

随笔档案

文章分类

网站链接

搜索

  •  

最新评论

阅读排行榜

评论排行榜


Powered By: 博客园
模板提供沪江博客