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

2009年6月19日

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 阅读(1057) | 评论 (0)编辑 收藏

2009年6月9日

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 阅读(249) | 评论 (0)编辑 收藏

2009年5月18日

提起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 阅读(153) | 评论 (0)编辑 收藏

2009年4月1日

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 阅读(227) | 评论 (0)编辑 收藏

2009年3月23日

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

2009年3月9日

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 阅读(348) | 评论 (0)编辑 收藏

2009年3月4日

     摘要: 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 阅读(334) | 评论 (0)编辑 收藏

2009年3月3日

关键字: 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 阅读(215) | 评论 (0)编辑 收藏

2009年2月28日

级别: 中级

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 阅读(203) | 评论 (0)编辑 收藏

2009年2月26日

有可能Glassfish 域的安全策略配置需要:
grant {
      permission javax.management.MBeanServerPermission "*";
      permission javax.management.MBeanPermission "*","*";
      permission javax.management.MBeanTrustPermission "*";
};

posted @ 2009-02-26 12:54 TracyLu 阅读(212) | 评论 (0)编辑 收藏
CALENDER
<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿(2)

随笔分类

随笔档案

文章分类

网站链接

搜索

  •  

最新评论

阅读排行榜

评论排行榜


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