??xml version="1.0" encoding="utf-8" standalone="yes"?>国产亚洲欧洲一区高清在线观看,亚洲最好看的视频,最近最新中文字幕在线http://www.aygfsteel.com/esdsoftware/category/10839.html<-------------------------------------------------------------------& 珍惜生命 . 善用旉 . 把握未来 . 创造h倹{?/description>zh-cnFri, 02 Mar 2007 06:20:51 GMTFri, 02 Mar 2007 06:20:51 GMT60关于 J2SE5.0新特性之监控与管?/title><link>http://www.aygfsteel.com/esdsoftware/articles/69507.html</link><dc:creator>吴名?/dc:creator><author>吴名?/author><pubDate>Wed, 13 Sep 2006 15:44:00 GMT</pubDate><guid>http://www.aygfsteel.com/esdsoftware/articles/69507.html</guid><wfw:comment>http://www.aygfsteel.com/esdsoftware/comments/69507.html</wfw:comment><comments>http://www.aygfsteel.com/esdsoftware/articles/69507.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/esdsoftware/comments/commentRss/69507.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/esdsoftware/services/trackbacks/69507.html</trackback:ping><description><![CDATA[ <h4 class="TextColor1" id="subjcns!42D5DAEFFDE7D929!273" style="MARGIN-BOTTOM: 0px">j2se 5.0使用 Java Management Extensions (JMX)来管理和监控javaq_?br />我们以一个例子来试一?<br /></h4> <div class="wmqeeuq" id="msgcns!42D5DAEFFDE7D929!273"> <div> <div> <ol> <li> </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.management.ClassLoadingMXBean; </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.management.CompilationMXBean; </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.management.ManagementFactory; </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.management.MemoryMXBean; </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.management.MemoryManagerMXBean; </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.management.MemoryPoolMXBean; </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.management.OperatingSystemMXBean; </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.reflect.<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/reflect/InvocationTargetException.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>InvocationTargetException</u></font></a></b>; </li> <li> <b> <font color="#0000ff">import</font> </b> java.lang.reflect.<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/reflect/Method.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>Method</u></font></a></b>; </li> <li> <b> <font color="#0000ff">import</font> </b> java.util.<b><a title="http://www.javaresearch.org/source/jdk142/java/util/List.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>List</u></font></a></b>; </li> <li> </li> <li> <b> <font color="#0000ff">public</font> </b> <b><font color="#0000ff">class</font></b> JDKMBean </li> <li>{ </li> <li>     </li> <li>    <b><font color="#0000ff">public</font></b> <b><font color="#0000ff">static</font></b> <T> <b><font color="#0000ff">void</font></b> printMXBean(<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/Class.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>Class</u></font></a></b><T> t,<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/Object.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>Object</u></font></a></b> object) </li> <li>    { </li> <li>        <b><a title="http://www.javaresearch.org/source/jdk142/java/lang/reflect/Method.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>Method</u></font></a></b>[] methods = t.getMethods(); </li> <li>        T instance = (T)object; </li> <li>        <b><a title="http://www.javaresearch.org/source/jdk142/java/lang/System.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>System</u></font></a></b>.out.printf(<font color="#ff33ff">"%n---%s---%n"</font>, t.getName()); </li> <li>        <b><font color="#0000ff">for</font></b>(<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/reflect/Method.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>Method</u></font></a></b> m:methods) </li> <li>        { </li> <li>            <b><font color="#0000ff">if</font></b> (m.getName().startsWith(<font color="#ff33ff">"get"</font>)) </li> <li>            { </li> <li>                <b><font color="#0000ff">try</font></b></li> <li>                { </li> <li>                    <b><a title="http://www.javaresearch.org/source/jdk142/java/lang/Object.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>Object</u></font></a></b> rtValue = m.invoke(instance,<b><font color="#0000ff">new</font></b> <b><a title="http://www.javaresearch.org/source/jdk142/java/lang/Object.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>Object</u></font></a></b>[0]); </li> <li>                    <b><a title="http://www.javaresearch.org/source/jdk142/java/lang/System.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>System</u></font></a></b>.out.printf(<font color="#ff33ff">"%s:%s%n"</font>,m.getName().substring(3),rtValue); </li> <li>                 } </li> <li>                <b><font color="#0000ff">catch</font></b> (<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/IllegalArgumentException.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>IllegalArgumentException</u></font></a></b> e1) </li> <li>                {     </li> <li>                 } </li> <li>                <b><font color="#0000ff">catch</font></b> (<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/IllegalAccessException.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>IllegalAccessException</u></font></a></b> e) </li> <li>                {    </li> <li>                 } </li> <li>                <b><font color="#0000ff">catch</font></b> (<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/reflect/InvocationTargetException.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>InvocationTargetException</u></font></a></b> e) </li> <li>                { </li> <li>                 } </li> <li>             } </li> <li>         } </li> <li>     } </li> <li>    <b><font color="#0000ff">public</font></b> <b><font color="#0000ff">static</font></b> <T> <b><font color="#0000ff">void</font></b> printMXBeans(<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/Class.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>Class</u></font></a></b><T> t,<b><a title="http://www.javaresearch.org/source/jdk142/java/util/List.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>List</u></font></a></b><T> list) </li> <li>    { </li> <li>        <b><font color="#0000ff">for</font></b>(T bean:list) </li> <li>        { </li> <li>            printMXBean(t,bean); </li> <li>         } </li> <li>     } </li> <li>    <b><font color="#0000ff">public</font></b> <b><font color="#0000ff">static</font></b> <b><font color="#0000ff">void</font></b> main(<b><a title="http://www.javaresearch.org/source/jdk142/java/lang/String.java.html<br/><br/>#### ==> TARGET=_blank <== ###" ><font color="#0000ff"><u>String</u></font></a></b>[] args) </li> <li>    { </li> <li>        JDKMBean.printMXBean(OperatingSystemMXBean.<b><font color="#0000ff">class</font></b>,ManagementFactory.getOperatingSystemMXBean()); </li> <li>        JDKMBean.printMXBean(CompilationMXBean.<b><font color="#0000ff">class</font></b>,ManagementFactory.getCompilationMXBean()); </li> <li>        JDKMBean.printMXBean(ClassLoadingMXBean.<b><font color="#0000ff">class</font></b>,ManagementFactory.getClassLoadingMXBean()); </li> <li>        JDKMBean.printMXBean(MemoryMXBean.<b><font color="#0000ff">class</font></b>,ManagementFactory.getMemoryMXBean()); </li> <li>        JDKMBean.printMXBeans(MemoryManagerMXBean.<b><font color="#0000ff">class</font></b>,ManagementFactory.getMemoryManagerMXBeans()); </li> <li>        JDKMBean.printMXBeans(MemoryPoolMXBean.<b><font color="#0000ff">class</font></b>,ManagementFactory.getMemoryPoolMXBeans()); </li> <li>     } </li> <li>} </li> <li> </li> </ol> </div> <br />q行l果:<br /><br />---java.lang.management.OperatingSystemMXBean---<br />Arch:x86<br />AvailableProcessors:2<br />Name:Windows 2000<br />Version:5.0<br /><br />---java.lang.management.CompilationMXBean---<br />TotalCompilationTime:5<br />Name:HotSpot Client Compiler<br /><br />---java.lang.management.ClassLoadingMXBean---<br />LoadedClassCount:431<br />UnloadedClassCount:0<br />TotalLoadedClassCount:431<br /><br />---java.lang.management.MemoryMXBean---<br />HeapMemoryUsage:init = 0(0K) used = 458288(447K) committed = 2031616(1984K) max = 66650112(65088K)<br />NonHeapMemoryUsage:init = 29556736(28864K) used = 12541248(12247K) committed = 29851648(29152K) max = 121634816(118784K)<br />ObjectPendingFinalizationCount:0<br /><br />---java.lang.management.MemoryManagerMXBean---<br />MemoryPoolNames:[Ljava.lang.String;@6ca1c<br />Name:CodeCacheManager<br /><br />---java.lang.management.MemoryManagerMXBean---<br />MemoryPoolNames:[Ljava.lang.String;@1bf216a<br />Name:Copy<br /><br />---java.lang.management.MemoryManagerMXBean---<br />MemoryPoolNames:[Ljava.lang.String;@12ac982<br />Name:MarkSweepCompact<br /><br />---java.lang.management.MemoryPoolMXBean---<br />CollectionUsage:null<br />MemoryManagerNames:[Ljava.lang.String;@c20e24<br />PeakUsage:init = 196608(192K) used = 482048(470K) committed = 491520(480K) max = 33554432(32768K)<br />Usage:init = 196608(192K) used = 524352(512K) committed = 557056(544K) max = 33554432(32768K)<br />UsageThreshold:0<br />UsageThresholdCount:0<br />Name:Code Cache<br />Type:Non-heap memory<br /><br />---java.lang.management.MemoryPoolMXBean---<br />CollectionUsage:init = 524288(512K) used = 0(0K) committed = 0(0K) max = 4194304(4096K)<br />CollectionUsageThreshold:0<br />CollectionUsageThresholdCount:0<br />MemoryManagerNames:[Ljava.lang.String;@2e7263<br />PeakUsage:init = 524288(512K) used = 511160(499K) committed = 524288(512K) max = 4194304(4096K)<br />Usage:init = 524288(512K) used = 521688(509K) committed = 524288(512K) max = 4194304(4096K)<br />Name:Eden Space<br />Type:Heap memory<br /><br />---java.lang.management.MemoryPoolMXBean---<br />CollectionUsage:init = 65536(64K) used = 0(0K) committed = 0(0K) max = 458752(448K)<br />CollectionUsageThreshold:0<br />CollectionUsageThresholdCount:0<br />MemoryManagerNames:[Ljava.lang.String;@157f0dc<br />PeakUsage:init = 65536(64K) used = 65528(63K) committed = 65536(64K) max = 458752(448K)<br />Usage:init = 65536(64K) used = 65528(63K) committed = 65536(64K) max = 458752(448K)<br />Name:Survivor Space<br />Type:Heap memory<br /><br />---java.lang.management.MemoryPoolMXBean---<br />CollectionUsage:init = 1441792(1408K) used = 0(0K) committed = 0(0K) max = 61997056(60544K)<br />CollectionUsageThreshold:0<br />CollectionUsageThresholdCount:0<br />MemoryManagerNames:[Ljava.lang.String;@863399<br />PeakUsage:init = 1441792(1408K) used = 142120(138K) committed = 1441792(1408K) max = 61997056(60544K)<br />Usage:init = 1441792(1408K) used = 142120(138K) committed = 1441792(1408K) max = 61997056(60544K)<br />UsageThreshold:0<br />UsageThresholdCount:0<br />Name:Tenured Gen<br />Type:Heap memory<br /><br />---java.lang.management.MemoryPoolMXBean---<br />CollectionUsage:init = 8388608(8192K) used = 0(0K) committed = 0(0K) max = 67108864(65536K)<br />CollectionUsageThreshold:0<br />CollectionUsageThresholdCount:0<br />MemoryManagerNames:[Ljava.lang.String;@a59698<br />PeakUsage:init = 8388608(8192K) used = 641040(626K) committed = 8388608(8192K) max = 67108864(65536K)<br />Usage:init = 8388608(8192K) used = 641040(626K) committed = 8388608(8192K) max = 67108864(65536K)<br />UsageThreshold:0<br />UsageThresholdCount:0<br />Name:Perm Gen<br />Type:Non-heap memory<br /><br />---java.lang.management.MemoryPoolMXBean---<br />CollectionUsage:init = 8388608(8192K) used = 0(0K) committed = 0(0K) max = 8388608(8192K)<br />CollectionUsageThreshold:0<br />CollectionUsageThresholdCount:0<br />MemoryManagerNames:[Ljava.lang.String;@141d683<br />PeakUsage:init = 8388608(8192K) used = 5601632(5470K) committed = 8388608(8192K) max = 8388608(8192K)<br />Usage:init = 8388608(8192K) used = 5601632(5470K) committed = 8388608(8192K) max = 8388608(8192K)<br />UsageThreshold:0<br />UsageThresholdCount:0<br />Name:Perm Gen [shared-ro]<br />Type:Non-heap memory<br /><br />---java.lang.management.MemoryPoolMXBean---<br />CollectionUsage:init = 12582912(12288K) used = 0(0K) committed = 0(0K) max = 12582912(12288K)<br />CollectionUsageThreshold:0<br />CollectionUsageThresholdCount:0<br />MemoryManagerNames:[Ljava.lang.String;@16a55fa<br />PeakUsage:init = 12582912(12288K) used = 5850024(5712K) committed = 12582912(12288K) max = 12582912(12288K)<br />Usage:init = 12582912(12288K) used = 5850024(5712K) committed = 12582912(12288K) max = 12582912(12288K)<br />UsageThreshold:0<br />UsageThresholdCount:0<br />Name:Perm Gen [shared-rw]<br />Type:Non-heap memory</div> </div> <img src ="http://www.aygfsteel.com/esdsoftware/aggbug/69507.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/esdsoftware/" target="_blank">吴名?/a> 2006-09-13 23:44 <a href="http://www.aygfsteel.com/esdsoftware/articles/69507.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>?Java 应用E序中访问USB讑֤http://www.aygfsteel.com/esdsoftware/articles/59324.html吴名?/dc:creator>吴名?/author>Thu, 20 Jul 2006 18:06:00 GMThttp://www.aygfsteel.com/esdsoftware/articles/59324.htmlhttp://www.aygfsteel.com/esdsoftware/comments/59324.htmlhttp://www.aygfsteel.com/esdsoftware/articles/59324.html#Feedback0http://www.aygfsteel.com/esdsoftware/comments/commentRss/59324.htmlhttp://www.aygfsteel.com/esdsoftware/services/trackbacks/59324.html
  通用串行ȝ(Universal Serial Bus USB)规范的第一个版本发表于 1996q?1月。因为它的低成本、高数据传输率、用容易和灉|性,USB 在计机行业里获得了q泛接受。今天,许多周边讑֤和装|都是通过 USB 接口q接到计机上的。目前,大多C般用途的操作pȝ都提供了?USB 讑֤的支持,q且?C 或?C++ 可以相对Ҏ地开发访问这些外讄应用E序。不q,Java ~程语言在设计上对硬件访问提供的支持很少Q所以编写与 USB 讑֤交互的应用程序是相当困难的?

  IBM ?Dan Streetman 最早开始了?Java 语言中提供对 USB 讑֤的访问的努力?001q_他的目通过 Java 规范h(Java Specification RequestQJSR)q程被接受ؓ Java 语言的候选扩展标准。这个项目现在称?JSR-80 q且指定了官方包 javax.usb。同Ӟ?2000q?6月,Mojo Jojo ?David Brownell ?SourceForge 开始了 jUSB 目。这两个目都开发出?Linux 开发h员可以用的包,管它们都还很不完善。这两个目也都开始试囑֐其他操作pȝ上的 Java 应用E序提供?USB 讑֤的访问,管它们都还没有开发出可以使用的包(参阅 参考资?中有x文中讨论的这两个目及其他项目的资料)?br />
  在本文中Q将?jUSB ?JSR-80 目作一个简要介l,不过Q我们首先要看一?USB 协议的具体细节,q样您就可以理解q两个项目是如何?USB 讑֤交互的。我们还提供代码片D以展示如何用这两个目?API 讉K USB 讑֤?USB 介绍

  1994q_一个由四个行业伙伴(Compaq、Intel、Microsoft ?NEC)l成的联盟开始制?USB 协议。该协议最初的目的是将 PC 与电话相qƈ提供Ҏ扩展和重新配|的 I/O 接口?996q?1月,发表?USB 规范的第一个版本,1998q?9月发表了后箋版本(版本 1.1)。这个规范允?127台设备同时连接到一Pȝ通信带宽限制?12 Mbps。后来,又有三个成员(Hewlett-Packard、Lucent ?Philips)加入了这个联盟?000q?4月,发表?USB 规范?2.0版本Q它支持高达 480 Mbps 的传输率。今天,USB 在高?视频、图像、储?和全?音频、宽带、麦克风)数据传输应用中v了关键作用。它q各种低速设?键盘、鼠标、游戏外设、虚拟现实外?q接?PC 上?br />
  USB 协议有严格的层次l构。在所?USB pȝ中,只有一个主讑֤Q到主计机的的 USB 接口UCؓL?host controller)。主控器有两个标??开放主控器接口(Compaq ?Open Host Controller InterfaceQOHCI)和通用L器接?Intel ?Universal Host Controller InterfaceQUHCI)。这两个标准提供了同L能力Qƈ可用于所有的 USB 讑֤QUHCI 的硬件实现更单一些,但是需要更复杂的设备驱动程?因?CPU 的负h大一??br />
  USB 物理互连是分层的星Ş拓朴Q最多有七层。一?hub 是每个星形的中心QUSB L被认为是 root hub。每一D连UK?hub ?USB 讑֤的点对点q接Q后者可以是为系l提供更多附加点的另一?hubQ也可以是一个提供功能的某种讑֤。主Z用主/从协议与 USB 讑֤通信。这U方式解决了包冲H的问题Q但是同时也L了附加的讑֤彼此建立直接通信?br />
  所有传输的数据都是׃控器发v的。数据从L向讑֤UCؓ下行(downstream)或者输?out)传输Q数据从讑֤向LUCؓ??upstream)或者输?in)传输。数据传输发生在L?USB 讑֤上特定的端点(endpoint) 之间Q主Z端点之间的数据链接称为管?pipe)?一个给定的 USB 讑֤可以有许多个端点Q主Z讑֤之间数据道的数量与该设备上端点的数量相同。一个管道可以是单向或者是双向的,一个管道中的数据流与所有其他管道中的数据流无关?br />
  USB |络中的通信可以使用下面四种数据传输cd中的L一U:

  控制传输Q?q些是一些短的数据包Q用于设备控制和配置Q特别是在设备附加到L上时?

  扚w传输Q?q些是数量相对大的数据包。像扫描仪或?SCSI 适配器这L讑֤使用q种传输cd?

  中断传输Q?q些是定期轮询的数据包。主控器会以特定的间隔自动发Z个中断?

  {时传输Q?q些是实时的数据,它们对带宽的要求高于可靠性要求。音频和视频讑֤一般用这U传输类型?

  像串行端口一P计算Z每一?USB 端口都由 USB 控制器指定了一个惟一的标识数?端口 ID)。当 USB 讑֤附加?USB 端口上时Q就这?惟一端口 ID 分配l这台设备,q且 USB 控制器会d讑֤描述W。设备描q符包括适用于该讑֤的全局信息、以及设备的配置信息。配|定义了一?USB 讑֤的功能和 I/O 行ؓ。一?USB 讑֤可以有一个或者多个配|,q由它们相应的配|描q符所描述。每一个配|都有一个或者多个接口,它可以视Z个物理通信渠道 Q每一个接口有零个或者多个端点,它可以是数据提供者或者数据消费者,或者同时具有这两种w䆾。接口由接口描述W描qͼ端点qҎq符描述。ƈ且一?USB 讑֤可能q有字符串描q符以提供像厂商名、设备名或者序列号q样的附加信息?br />
  正如您所看到的,?USB q样的协议ؓ使用 Java q种q_和硬件无x的语言的开发h员提Z挑战。现在让我们看两个试图解册个问题的目?br />jUSB API

  jUSB 目是由 Mojo Jojo ?David Brownell ?2000q?6月创立的。其目标是提供一l免费的、在 Linux q_上访?USB 讑֤?Java API。这?API 是按?Lesser GPL (LGPL)条款发表的,q意味着您可以在专有和免费Y仉目中使用它。这?API 提供了对多个物理 USB 讑֤的多U程讉KQƈ支持本机和远E设备。具有多个接口的讑֤可以同时被多个应用程?或者设备驱动程?所讉KQ其中每一个应用程?或者设备驱动程?都占据一个不同的接口。该 API 支持控制传输、批量传输和中断传输Q不支持{时传输Q因为等时传输用于媒体数?如音频和视频)QJMF API 已经在其他标准设备驱动程序上Ҏ提供了很好的支持(参阅 参考资?。当前,?API 可以在具?Linux 2.4 核心或者以前的 2.2.18 核心?GNU/Linux 版本上工作。因此可支持大多数最新的版本Q例如,?API 可以在没有Q何补丁或者升U的 Red Hat 7.2 ?9.0 上工作?br />
  jUSB API 包括以下包:
 
  ·usb.core: q个包是 jUSB API 的核心部分。它使得 Java 应用E序可以?USB L讉K USB 讑֤?br />
  ·usb.linux: q个包包?usb.core.Host 对象?Linux 实现、bootstrapping 支持和其他可以提?Linux USB 支持的类。这个实现通过虚拟 USB 文gpȝ(usbdevfs)讉K USB 讑֤?br />
  ·usb.windows: q个包包?usb.core.Host 对象?Windows 实现、bootstrapping 支持和其他可以提?Windows USB 支持的类。这个实C然处于非常初U的阶段?br />
  ·usb.remote: q个包是 usb.core API 的远E版本。它包括一?RMI proxy 和一?daemon 应用E序Q它?Java 应用E序可以讉Kq程计算Z?USB 讑֤?br />
  ·usb.util: q个包提供了一些有用的实用E序Q可以将 firmware下蝲?USB 讑֤上、将 USB pȝ的内容{储到 XML 中、以及将只有 bulk I/O ?USB 讑֤工具转换成一个套接字(socket)?br />
  ·usb.devices: q个可选包攉了用 jUSB API 讉K不同 USB 讑֤?Java 代码Q包括柯达数码相机和 Rio 500 MP3 播放器。这?API l过特别~写以简化访问特?USB 讑֤的过E,q且不能用于讉K其他讑֤。这?API 是在 usb.core API 之上构徏的,它们可以工作在所有支?jUSB 的操作系l上?br />
  ·usb.view: q个可选包提供了基?Swing ?USB 树简单浏览器。它是一个展C?jUSB API 应用的很好的CZE序?

  管 usb.core.Host 对象的实现对于不同的操作pȝ是不同的Q但?Java E序员只需要理?usb.core 包就可以?jUSB API 开始应用程序的开发。表 1 列出?usb.core 的接口和c,Java E序员应该熟悉它们:

  ?1. jUSB 中的接口和类

接口说明
Bus一l?USB 讑֤q接?Host ?
Host表示h一个或者多?Bus ?USB 控制?

c?说明
Configuration提供对设备所支持?USB 配置的访问,以及对与该配|关联的接口的访?
Descriptor h USB cd的描q符的实体的基类
Device 提供?USB 讑֤的访?
DeviceDescriptor 提供?USB 讑֤描述W的讉K
EndPoint 提供?USB 端点描述W的讉K、在l定讑֤配置中构造设备数据输入或者输?/td>
HostFactory包含 bootstrapping Ҏ
Hub提供?USB hub 描述W以及一?hub 操作的访?
Interface 描述一l端点,q与一个特定设备配|相兌
PortIdentifier ?USB 讑֤提供E_的字W串标识W,以便在操作和故障诊断时?/td>

  ?jUSB API 讉K一?USB 讑֤的正常过E如下:

  ·通过?HostFactory 得到 USB Host q行 Bootstrap?br />
  ·?Host 讉K USB BusQ然后从q个 Bus 讉K USB root hub(?USB Device)?br />
  ·得到 hub 上可用的 USB 端口数量Q遍历所有端口以扑ֈ正确?Device?br />
  ·讉K附加到特定端口上?USB Device。可以用一?Device ?PortIdentifier 直接?Host 讉K它,也可以通过?root hub 开始遍?USB Bus 扑ֈ它?br />
  ·?ControlMessage 与该 Device 直接交互Q或者从?Device 的当?Configuration 中要求一?InterfaceQƈ与该 Interface 上可用的 Endpoint q行 I/O ?

  清单 1 展示了如何用 jUSB API 获得 USB pȝ中的内容。这个程序编写ؓ只是查看 root hub 上可用的 USB 讑֤Q但是很Ҏ它改ؓ遍历整个 USB 树。这里的逻辑对应于上q步?1 到步?4?br />
  清单 1. ?jUSB API 获得 USB pȝ的内?br />
import usb.core.*;

public class ListUSB
{
 public static void main(String[] args)
 {
  try
  {
   // Bootstrap by getting the USB Host from the HostFactory.
   Host host = HostFactory.getHost();

   // Obtain a list of the USB buses available on the Host.
   Bus[] bus = host.getBusses();
   int total_bus = bus.length;

   // Traverse through all the USB buses.
   for (int i=0; i<total_bus; i++)
   {
    // Access the root hub on the USB bus and obtain the
    // number of USB ports available on the root hub.
    Device root = bus[i].getRootHub();
    int total_port = root.getNumPorts();

    // Traverse through all the USB ports available on the
    // root hub. It should be mentioned that the numbering
    // starts from 1, not 0.
    for (int j=1; j<=total_port; j++)
    {
     // Obtain the Device connected to the port.
     Device device = root.getChild(j);
     if (device != null)
     {
      // USB device available, do something here.
     }
    }
   }
  } catch (Exception e)
  {
   System.out.println(e.getMessage());
  }
 }

清单 2 展示了在应用E序成功地找C Device 的条件下Q如何与 Interface ?EndPoint q行扚w I/O?q个代码D也可以修改为执行控制或者中?I/O。它对应于上q步?5?br />
  清单 2. ?jUSB API 执行扚w I/O

if (device != null)
{
 // Obtain the current Configuration of the device and the number of
 // Interfaces available under the current Configuration.
 Configuration config = device.getConfiguration();
 int total_interface = config.getNumInterfaces();

 // Traverse through the Interfaces
 for (int k=0; k<total_interface; k++)
 {
  // Access the currently Interface and obtain the number of
  // endpoints available on the Interface.
  Interface itf = config.getInterface(k, 0);
  int total_ep = itf.getNumEndpoints();

  // Traverse through all the endpoints.
  for (int l=0; l<total_ep; l++)
  {
   // Access the endpoint, and obtain its I/O type.
   Endpoint ep = itf.getEndpoint(l);
   String io_type = ep.getType();
   boolean input = ep.isInput();

   // If the endpoint is an input endpoint, obtain its
   // InputStream and read in data.
   if (input)
   {
    InputStream in;
    in = ep.getInputStream();
    // Read in data here
    in.close();
   }
   // If the Endpoint is and output Endpoint, obtain its
   // OutputStream and write out data.
   else
   {
    OutputStream out;
    out = ep.getOutputStream();
    // Write out data here.
    out.close();
   }
  }
 }
}

  jUSB 目?2000q?6月到 2001q?2月期间非常活跃。该 API 的最新的版本 0.4.4发表?2001q?2?14日。从那以后只提出了很的改进Q原因可能是 IBM 组成功地成Z Java 语言的候选扩展标准。不q,Z jUSB 已经开发出一些第三方应用E序Q包?JPhoto 目(q是一个用 jUSB q接到数码照相机的应用程??jSyncManager 目(q是一个用 jUSB 与?Palm 操作pȝ?PDA 同步的应用程??br />

JSR-80 API (javax.usb)

  正如前面提到的,JSR-80 目是由 IBM ?Dan Streetman ?1999q创立的?001q_q个目通过 Java 规范h(JSR)q程被接受ؓ Java 语言的候选扩展标准。这个项目现在称?JSR-80 q且被正式分z了 Java ?javax.usb。这个项目?Common Public License 的许可证形式Qƈ通过 Java Community Process q行开发。这个项目的目标是ؓ Java q_开发一?USB 接口Q可以从M Java 应用E序中完全访?USB pȝ。JSR-80 API 支持 USB 规范所定义的全部四U传输类型。目前,?API ?Linux 实现可以在支?2.4 核心的大多数最?GNU/Linux 版本上工作,?Red Hat 7.2 ?9.0?br />
  JSR-80 目包括三个包:javax-usb (javax.usb API)、javax-usb-ri (操作pȝ无关的基准实现的公共部分)以及 javax-usb-ri-linux (Linux q_的基准实玎ͼ它将公共基准实现链接?Linux USB 堆栈)。所有这三个部分都是构成 Linux q_?java.usb API 完整功能所必需的。在该项目的电子邮g列表中可以看到有人正在致力于这?API UL到其他操作系l上(主要?Microsoft Windows)Q但是还没有可以工作的版本发表?br />
  管 JSR-80 API 的操作系l无关的实现在不同的操作pȝ上是不同的,但是 Java E序员只需要理?javax.usb 包就可以开始开发应用程序了。表 2 列出?javax.usb 中的接口和类Q?Java E序员应该熟悉它们:

  ?2. JSR-80 API 中的接口和类

接口 说明
UsbConfiguration 表示 USB 讑֤的配|?
UsbConfigurationDescriptorUSB 配置描述W的接口
UsbDeviceUSB 讑֤的接?/td>
UsbDeviceDescriptorUSB 讑֤描述W的接口
UsbEndpointUSB 端点的接?
UsbEndpointDescriptorUSB 端点描述W的接口
UsbHubUSB hub 的接?/td>
UsbInterfaceUSB 接口的接?
UsbInterfaceDescriptorUSB 接口描述W的接口
UsbPipe USB 道的接?
UsbPort USB 端口的接?
UsbServices javax.usb 实现的接?

c?说明
UsbHostManager javax.usb 的入口点

  ?JSR-80 API 讉K USB 讑֤的正常过E如下:

  ·通过?UsbHostManager 得到相应?UsbServices q行 Bootstrap?br />
  ·通过 UsbServices 讉K root hub。在应用E序?root hub 是一?UsbHub?br />
  ·获得q接?root hub ?UsbDevices 清单。遍历所有低U?hub 以找到正的 UsbDevice?br />
  ·用控制消?UsbControlIrp)?UsbDevice 直接交互Q或者从 UsbDevice 的相?UsbConfiguration 中要求一?UsbInterface q与?UsbInterface 上可用的 UsbEndpoint q行 I/O?br />
  ·如果一?UsbEndpoint 用于q行 I/OQ那么打开与它兌?UsbPipe。通过q个 UsbPipe 可以同步或者异步提交上行数??USB 讑֤C计算?和下行数?从主计算机到 USB 讑֤)?br />
  ·当应用程序不再需要访问该 UsbDevice Ӟ关闭q个 UsbPipe q攄应的 UsbInterface?

  在清?3 中,我们?JSR-80 API 获得 USB pȝ的内宏V这个程序递归地遍?USB pȝ上的所?USB hub q找接到L计算Z的所?USB 讑֤。这D代码对应于上述步骤 1 到步?3?br />
  清单 3. ?JSR-80 API 获得 USB pȝ的内?br />
import javax.usb.*;
import java.util.List;

public class TraverseUSB
{
 public static void main(String argv[])
 {
  try
  {
   // Access the system USB services, and access to the root
   // hub. Then traverse through the root hub.
   UsbServices services = UsbHostManager.getUsbServices();
   UsbHub rootHub = services.getRootUsbHub();
   traverse(rootHub);
  } catch (Exception e) {}
 }

 public static void traverse(UsbDevice device)
 {
  if (device.isUsbHub())
  {
   // This is a USB Hub, traverse through the hub.
   List attachedDevices = ((UsbHub) device).getAttachedUsbDevices();
   for (int i=0; i<attachedDevices.size(); i++)
   {
    traverse((UsbDevice) attachedDevices.get(i));
   }
  }
  else
  {
   // This is a USB function, not a hub.
   // Do something.
  }
 }
}

  清单 4 展示了在应用E序成功地找?Device 后,如何?Interface ?EndPoint q行 I/O。这D代码还可以修改行所有四U数据传输类型的 I/O。它对应于上q步?4 到步?6?br />
  清单 4. ?JSR-80 API q行 I/O

public static void testIO(UsbDevice device)
{
 try
 {
  // Access to the active configuration of the USB device, obtain
  // all the interfaces available in that configuration.
  UsbConfiguration config = device.getActiveUsbConfiguration();
  List totalInterfaces = config.getUsbInterfaces();

  // Traverse through all the interfaces, and access the endpoints
  // available to that interface for I/O.
  for (int i=0; i<totalInterfaces.size(); i++)
  {
   UsbInterface interf = (UsbInterface) totalInterfaces.get(i);
   interf.claim();
   List totalEndpoints = interf.getUsbEndpoints();
   for (int j=0; j<totalEndpoints.size(); j++)
   {
    // Access the particular endpoint, determine the direction
    // of its data flow, and type of data transfer, and open the
    // data pipe for I/O.
    UsbEndpoint ep = (UsbEndpoint) totalEndpoints.get(i);
    int direction = ep.getDirection();
    int type = ep.getType();
    UsbPipe pipe = ep.getUsbPipe();
    pipe.open();
    // Perform I/O through the USB pipe here.
    pipe.close();
   }
   interf.release();
  }
 } catch (Exception e) {}
}

  JSR-80 目从一开始就非常z跃?003q?2月发表了 javax.usb API、RI ?RI ?0.10.0 版本。看hq一版本会提交给 JSR-80 委员会做最l批准。预计正式成?Java 语言的扩展标准后Q其他操作系l上的实C很快出现。Linux 开发者团体看来对 JSR-80 目的兴比 jUSB 目更大Q?Linux q_?javax.usb API 的项目数量在不断地增加?br />
  l束?/b>
 
  jUSB API ?JSR-80 API 都ؓ应用E序提供了从q行 Linux 操作pȝ的计机中访?USB 讑֤的能力。JSR-80 API 提供了比 jUSB API 更多的功能,很有可能成ؓ Java 语言的扩展标准。目前,只有 Linux 开发h员可以利?jUSB ?JSR-80 API 的功能。不q,有h正在U极地将q两U?API UL到其他操作系l上。Java 开发h员应该在不久可以在其他操作pȝ上访?USB 讑֤。从现在起就熟悉q些 APIQ当q些目可以在多个^C发挥作用Ӟ您就可以在自q应用E序中加?USB 功能了?br />

]]>
如何使用J2EE打印服务http://www.aygfsteel.com/esdsoftware/articles/44837.html吴名?/dc:creator>吴名?/author>Sun, 07 May 2006 00:27:00 GMThttp://www.aygfsteel.com/esdsoftware/articles/44837.htmlhttp://www.aygfsteel.com/esdsoftware/comments/44837.htmlhttp://www.aygfsteel.com/esdsoftware/articles/44837.html#Feedback0http://www.aygfsteel.com/esdsoftware/comments/commentRss/44837.htmlhttp://www.aygfsteel.com/esdsoftware/services/trackbacks/44837.html 摘要
在这文章里Q来自Pro Java Programming (Apress, June 2005)专家Brett Spell解释了如何一步一步的定位打印讑֤Q创建打印工E,创徏一个Doc文档接口的实例来描述你想要打印的数据q且初始化打印。(4500字,2005q??5日)

Java自从问世以来在各斚w发展q速,但是一直以来,打印输出是java最q斚w。事实上Qjava1.0不支持Q何打印功能。Java1.1在java.awt包里包含了一个叫做PrintJob的类Q但是这个类提供的打印功能十分粗p和不可靠。当java1.2问世Q它围绕PrinterJob设计了一个完整独立的打印机制Q叫做java2D printing APIQ,q且在java.awt.print包里定义了一些新的类和接口。这些得基于PrintJob打印机制Q就是AWT printingQ基本荒废,虽然PrintJob从未被抨击而且臛_在这文章里仍然是一个提供技术的cR?br />
在J2SE1.3里当PrintJob的功能扩展到可以通过在java.awt包里的JobAttributes 和PageAttributes两个c设定工E和面的属性时发生了一些额外的改变。随着J2SE1.3的发布,打印功能相应的得C完善Q但是在混合使用q两U完全不同的打印机制的时候仍然存在一些问题。比如,q两U机制用java.awt.Graphicsq个cȝ一个接口来展现打印内容Q意味着所有要打印的东襉K必须用一张图片表C。另外,完善的PrintJob提供了很有限的工E相兛_性的讄Q这两种机制都没有办法通过E序来选择目标打印机?br />
Java打印最大的改变来自于J2SE的发布带来的Java打印服务API。这个第三代Java打印支持接口H破了先前提到的使用javax.print包的PrintService和DocPrintJob接口的局限性。因为新的API是以前两种旧的打印机制定义的功能函数的一个父集,它是目前我们常用的方法ƈ且是q篇文章的焦炏V?br />更深入来?以下的步骤包含了怎么使用q个新的Java打印服务APIQ?br />1.定义打印机,限制那些q回到提供你要实现功能的函数的列表。打印服务实CPrintService接口.

2.通过调用接口中定义的createPrintJob()Ҏ创徏一个打CӞ作ؓDocPrintJob的一个实例?br />
3.创徏一个实现Doc接口的类来描qC惌打印的数?, 你也可以创徏一个PrintRequestAttributeSet的实例来定义你想要的打印选项?br />
4.通过DocPrintJob接口定义的printv()Ҏ来初始化打印Q指定你先前创徏的DocQ指定PrintRequestAttributeSet或者设为空倹{?br />
现在你可以检查每一步ƈ且试着完成它们?br />

注意

        
在这文章里Q我交替用打印机和打印服务,因ؓ在大部分情况下,打印服务不亚于一台真实的打印机?一般的打印服务反映了理Z可以发送到不仅仅是打印机的的输出。D例来_打印服务也许Ҏ不能打印东西但是可以往盘上的文g写数据。换句话_所有的打印机可以看成是Ҏ的打印服务,但是q不是所有打印服务和打印机有联系。就像你一般把你的文本送到打印机那里一P我有时候用更为简便的打印个名词来代替技术上更精的打印服务。       ?br />

定义打印服务
你可以用在PrintServiceLookupcM定义的三U静态方法中的一U来定义。最单的一U就是lookupDefaultPrintService()Q正如它的名字一P它返回一个你默认的打印机Q?br />

PrintService service = PrintServiceLookup.lookupDefaultPrintService(); 



虽然用这个办法很单也很方便,用它来选择你的打印机意味着用户的打印机一直都支持你的E序所要精传输的数据输出。实际上Q你真正惌的是那种可以处理你想要的数据的类型ƈ且可以支持你要的特征例如颜色或者两Ҏ印。ؓ了从列表中中q回你所要求的特D功能支持的打印机,你可以用剩下两个方法中的lookupPrintServices() 或者lookupMultiDocPrintServices()?br />
lookupPrintServices()Ҏ有两个参敎ͼ一个DocFlavor的实例和实现AttributeSet接口的实例?br />你马上将看到Q你可以使用两者中L一个来限制q回的打印机Q但是lookupPrintServices()允许你指定这两个参数为空倹{如果把两者都设ؓI,那么你得到的q回值将是Q意一个可用的打印机。在q种情况下,你ƈ不需要查看PrintService中定义的ҎQ其中一个getName()Ҏq回了一个字W串Q代表打印机的名字。你可以~译下面的代码来列出你的pȝ现有的打印机Q?br />

PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
for (int i = 0; i < services.length; i++) {
   System.out.println(services[i].getName());
}



例如你的pȝ名ؓPrintServerQ下面有Alpha, Beta, 和Gamma 打印?用以上代码可以得C下输?
\PrintServerAlpha
\PrintServerBeta
\PrintServerGamma

现在查看那些你可以传llookupPrintServices()Ҏ的参数来看看如何q回拥有Ҏ功能的打印机?br />
DocFlavor
W一个你可以指定的参数是一个DocFlavorcȝ实例Q它描述了将要打印的数据的类型和数据如何存储。在大部分情况下Qƈ不需要去创徏一个新的实例因为Java包含了很多预先定义的实例Q得你可以用它们来传给lookupPrintServices()。然而,我们q是来看一下DocFlavor的结构和Ҏ来探讨打印服务如何用这个实例?br />
创徏DocFlavor实例需要的两个参数都是字符Ԍ一个是MIME (Multipurpose Internet Mail Extensions)cd另一个是cȝ名字。MIMEcd被用于描q数据类型。例如,你要打印一个gif文gQ你需要用MIMEcd是image/gif的DocFlavor。相cMQ如果你要打印HTML文g里的文本信息要用MIMEcd似text/plain或者text/html?br />MIMEcd描述要打印的数据的cdQ表现的cd表示如何让打印服务得到这些数据。DocFlavor包含了几个静态的内部c,每一个相对应一个表现类和如何装载要打印得数据?br />
?中列Z上面提到的内部类和表现类。注意在SERVICE_FORMATTEDQ一会我会更详细地解释)旁边Q每一个和"binary"或?"character"相对应。事实上Q这些差别是Zؓ的,因ؓ"character"数据cd本n是一U特D的binarycd。这U情况下Q我们说的二q制QbinaryQ数据包括h们可以看懂的字符和一些格式化的字W比如tabsQ换行回车等。当Ӟq些差别很重要,反映出面向字W的表现cdƈ不适合存储二进制数据?br />
例如Q你不会用字W队列或者字W串来保存一个gif文gQ你也不能通过Reader接口来访问它。另一斚wQ因为字W也是一U特D的二进制数据,它完全适合储存文本信息到字节数l里或者通过InputStream或者一个URL来访问它?br />
Table 1. DocFlavor的表现类
image

上面定义的Q何一个静态内部类相对应一个表现类Q但是请C我说q每一个DocFlavor的实例通过一个表现类和一个MIME来确认要打印的数据的cd?br />要访问这样一个实例,你要通过?d出的内部cR例如,我们假设你要打印一个在|上通过URL讉K的gif文gQ这L话,选择表现cLjavav.net.urlQ对应的在DocFlavor中的静态类是URLcR如果你打开那个内部cȝ文档Q你会发现其实它定义了一pd静态的内部c,每一个对应一U打印机支持的MIMEcd。表2描述了在DocFlavor.URL里的内部cdMIME

Table 2. The DocFlavor.URL inner classes
image

因ؓ要通过URL打印gif囄Q你可以用一下代码来获得实例

DocFlavor flavor = DocFlavor.URL.GIF; 



q个代码创徏了一个DocFlavor实例Q代表类是java.net.URLQMIME是image/gif?br />?列出的了DocFlavor.URL的类Q那么其他六个内部类呢?我们{下来讨Z下SERVICE_FORMATTEDQ这之前Q看看与二进制数据联pȝ所有三U类型(BYTE_ARRAY, INPUT_STREAM, and URL)相关的内部类。例如,如果你把gif储存C一个字节数l里Q那么你可以用以下代码:

DocFlavor flavor = DocFlavor.BYTE_ARRAY.GIF; 



正如有三个与二进制类型关联的内部cMP与字W类型相关的另外三个cd在表3?

Table 3. CHAR_ARRAY, READER, and STRING

image

所以,如果你想打印储存在字W串中的文本数据Q用以下代码Q?br />

DocFlavor flavor = DocFlavor.STRING.TEXT_PLAIN;



cMQ如果文本来自于|页上的HTML文档Q用以下代码Q?br />

DocFlavor flavor = DocFlavor.STRING.TEXT_HTML;



选择正确的打印机
q记得我们在开始关于讨论DocFlavor之前关于打印机的那个_支持你想要打印的数据cd的假讑֐Q这g看v来没有必要。实际上Q你会对l你的打印机所支持的文档类型感到吃惊。例如,刚提到文本类型看hg是最Ҏ支持的,所以,如果你的E序要打C个普通文本或者HTML文本Q你可以随便选择一个打印服务ƈ把它送到打印机那厅R然而大部分打印Z支持Z文本的表现类Q如果你试图向打印机发送它不支持的DocFlavorQ会产生下面的异常:

Exception in thread "main" sun.print.PrintJobFlavorException: invalid flavor
   at sun.print.Win32PrintJob.print(Win32PrintJob.java:290)
   at PrintTest.main(PrintTest.java:11)



现在你已l知道了如何得到一个DocFlavor的引用而且我们也讨Z选择支持q个flavor的打印机重要性,接下来我来告诉你如何定你用的打印机支持它。我先前说过lookupPrintServices()允许你指定一个DocFlavor作ؓW一个参敎ͼ如果你指定的参数非空Q那么方法会q回相应支持q个的打印机的实例。例如以下代码将q回可以通过URL来打印gif文g的打印机的列表:

DocFlavor flavor = DocFlavor.URL.GIF;
PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, null);




另外Q如果你的程序已l获得了打印服务的实例,而你想知道它是否支持另一U特定的flavorQ你可以调用isDocFlavorSupported()Ҏ。在下面的代码里Q将得到一个默认打印机的引用,如果不能打印gif׃出现错误信息Q?br />

PrintService service = PrintServiceLookup.lookupDefaultPrintService();
DocFlavor flavor = DocFlavor.URL.GIF;
if (!service.isDocFlavorSupported(flavor)) {
   System.err.println("The printer does not support the appropriate DocFlavor");
}



AttributeSet
正如你看到的QDocFlavor描述打印数据而且可以用来定打印服务是否支持q种数据。然而,你的E序需要选择一个基于那些支持的元素的打印机。例如,你要打印囄用不同的颜色来描qC同的信息Q你想知道提供的服务是否支持彩色打印Q如果不Q那么要么禁止它使用或者要求提供一个黑白图片?br />
cM彩色打印Q两Ҏ印或者用不同的定位取决于打印机本n的属性,而javax.print.attribute包包含了许多你可以用于描q这些属性的包和接口。其中一个接口是前面提到的lookupPrintServices()中第二个参数AttributeSet。正如你愿,它返回属性的集合Q在调用lookupPrintServices()指定一个不为空的值将q回支持q些属性的打印服务。换句话_如果DocFlavor?AttributeSet都不为空Q那么方法将q回那些q两U属性都支持的打印机
Attribute
AttributeSet 是属性的集合,一个显而易见的问题是如何指定属性的值呢Q?javax.print.attribute包里同时含有一个叫Attribute的接口,你马上可以看到通过调用addҎ来给AttributeSet创徏一个Attribute实例来获得这个集合。在javax.print.attribute.standard包里定义了大量你要用到的接口。在之前Q你可以查看javax.print.attributeq个包里的其他接口?br />
属性模?/b>
目前为止Q我们把属性描q成打印服务的功能,而实际上在java支持的属性中很单的。对应每个属性,java都有相应的模块。只有遵循这些模块属性才有效。在不同的java打印服务位置使用不同的属性,而不是所有的属性在M地方都适用?br />Z更好的理解这个,来看一下javax.print.attribute.standard 包里定义?br />OrientationRequested?ColorSupported接口。创Z个新的打印文档时可以指定OrientationRequested属性和用于打印的定位。ColorSupported在你调用PrintService接口的getAttributesҎ时返回。OrientationRequested是一个你用来传给打印机的属性,而ColorSupported是打印服务用来提供给你关于打印机能力信息的工兗你可以在创建打印文档时把ColorSupported作ؓ属性指定,因ؓ打印机是否支持彩色打印是你的E序不能控制的?br />
接口和?/b>
你第一ơ查看javax.print.attribute包里的接口和cL你也怼感到选择那些列表里的接口和类很麻烦。除了Attribute 和AttributeSet和承AttributeSet的HashAttributeSetQjavax.print.attribute包里?个子接口和类Q列出在?和图1中?br />
Table 4. javax.print.attribute 里定义的接口和类

image

image  Figure 1. javax.print.attribute 包的一部分cȝ层次l构.        

那么有了Attribute, AttributeSet, ?HashAttributeSetZ么需要用这些不同的接口和承类呢?是因些特D的cL为那些特D的属性量w定做的。比方说Q我提到q当你创建打印文档的时候有个地方可以用的属性例如ColorSupported在那里不能用。当创徏q样的文档,你可以用DocAttributeSet接口Q或者更专业一点,HashDocAttributeSetq个l承的类Q,q个l承cd允许你添加承DocAttributeq个接口的属性。这四种不同的模块如下:
        Doc: 在创建文档时指        定如何打印文?br />        PrintJob: 打印d的属性描qCQ务的状?
        PrintRequest: 初始化打印时传给d的请?
        PrintService:由打印服        务q回来描q打印机的功?br />
要知道如何工作,我们来创Z个DocAttributeSet的实例然后ؓAttributeSet讄DocAttributeSet和OrientationRequested属性。HashDocAttributeSet定义了很好的l构Q所有你可以很简便的如下创徏实例Q?br />

DocAttributeSet attrs = new HashDocAttributeSet(); 



现在你已l创ZAttributeSetQ你可以调用addҎq把它传lAttribute的承实例去。如果你看了OrientationRequestedq个cȝ文档Q你会发现它包含了一pd静态的OrientationRequest实例Q每一个对应一U文档定位方式。要指定你想要的cdQ你所要做的只是按下面的方法传laddҎ一个静态的实例的引用:

DocAttributeSet attrs = new HashDocAttributeSet();
attrs.add(OrientationRequested.PORTRAIT);



ColorSupportedcL一点不同但一样很单,它定义了两种静态实?一个表C支持彩色打印另一个不是。你可以试着增加一个ColorSupported属性到DocAttributeSet去,代码如下Q?br />

DocAttributeSet attrs = new HashDocAttributeSet();
attrs.add(OrientationRequested.PORTRAIT);
attrs.add(ColorSupported.SUPPORTED);



早先提过Q去指定是否支持彩色打印不恰当因不是E序所能控制的内容。换句话_ColorSupportedq个属性放Cpd文档属性中q不合适,所以,q行先前的代码当dColorSupported属性时会抛Z个ClassCastException异常?br />
要学习怎么q行Q记住每一个AttributeSet子接口都有一个相应Attribute子接口和l承子类。当d一个属性时Q承的子类试图把Attribute作ؓ参数l相应的子接口,q样来确保只有当前适当的属性会成功d?br />
q样的话QHashDocAttributeSet 的addҎW一ơ和OrientationRequested的一个实例一赯用,q成功的把它作ؓ一个object传给DocAttribute。因为如?所C,OrientationRequestedl承了那个接口。与之相对应Q传ColorSupported实例的时候因为没有承DocAttribute所以失败了?br />
image
Figure 2. javax.print.attribute 包的一部分cȝ层次l构         

q个例子举例说明Q表4里的四个接口和类l来保证使用正确的属性。注意模块和不同的属性之间有大量的交互,所以很多属性与不止一个模块关联。例如,许多属性承了PrintJobAttribute ?PrintRequestAttribute因ؓ大部分是通过一个相关的打印d获得提供l你的。你可以在初始化时指定它们。D个例子,你可以把它加到PrintRequestAttributeSet中去来指定Q务名Qƈ且在打印的时候通过PrintJobAttributeSet来返回它。因此,JobName属性类同时l承
PrintRequestAttribute ?PrintJobAttribute?br />AttributeSet and HashAttributeSet

你已l知道了Z么会有四个子c,但是AttributeSet接口和HashAttributeSet父类又是什么呢QAttributeSet/HashAttributeSet在你不能定要存储在q个集合中的那些仅仅和一个模块相关的属性时使用。记得我以前提到的lookupPrintServices()Ҏ允许你指定AttributeSet参数来限制返回的打印服务。表面上看来最好指定PrintServiceAttributeSet的实例,但是很多你可能用到的属性ƈ不承PrintServiceAttribute?br />
我们假设你想要让lookupPrintServices()Ҏq回支持彩色打印和风景画打印的打印机。这些属性与ColorSupported和OrientationRequested属性关联,但是h意这些类q不׃n模块Q前者是一个PrintServiceAttribute而OrientationRequested与另外三个模?Doc, PrintRequest,?PrintJob)兌。这意味着不存在单个的AttributeSet接口或类来同时包含ColorSupported和Sides属性?br />
创徏AttributeSet的方法用一个HashAttributeSet实例同时包含一个OrientationRequested ?ColorSupported太简单了。不像它的子c,它ƈ不限制你往上加Ҏ的属性,所以可以用以下代码成功执行Q?br />

AttributeSet attrs = new HashAttributeSet();
attrs.add(ColorSupported.SUPPORTED);
attrs.add(OrientationRequested.LANDSCAPE);
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, attrs);



通过用户界面的打印机选择
此观点而言Q我认ؓ使用的打印机应该由应用程序计划选择。但操作q程中,打印输出内容时往往会显CZ个对话框让用户选择。幸q的是,Java通过使用ServiceUIc(在javax.print包中定义Q中的静态printDialog()Ҏ使得q些操作单化?br />在显C的对话框旁边,仅在调用printDialog()时必L定的参数值如下:
用户可选用的PrintService实例数组?br />默认的PrintService?br />PrintRequestAttributeSet实例。这用来弹出昄的对话框Qƈ在对话框消失之前q回用户所作的M更改?br />要解释这些如何运作,可用下列简单的代码D|昄打印对话Q?br />

PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
PrintService selection = ServiceUI.printDialog(
   null, 100, 100, services, svc, null, attrs);



q行Ӟ代码产生如图?中所C的对话?br />
image
Figure 3. The printer dialog        

随着代码的说明,从printDialog()Ҏq回的值是一个PrintService实例Q识别用h选的打印机,或在用户取消打印机对话时识别为空。此外,PrintRequestAttributeSet已更新到可通过对话框来反映用户作出的更改,比如要打印的份数?br />通过使用printDialog()ҎQ可让用户选择其输发往的打印机Q提供用户对于专业应用程序的期望功能?br />
创徏打印d
q是打印中的一个简单步骤;因ؓ一旦获得PrintService的一个参考,你需要做的就是调用createPrintJob()ҎQ如Q?br />

PrintService service;
.
.
.
DocPrintJob job = service.createPrintJob();


代码中显C,从createPrintJob()q回的值是一个DocPrintJob实例Q可让您控制q监控打印操作的状态。要启动打印Q您会调用DocPrintJob对象的print()ҎQ但是,q之前,您需要定义待打印的文档或选用PrintRequestAttributeSe。您已经知道如何构造ƈ弹出AttributeSetQ这个步骤不再重复,接下来,您将了解定义待打印的文档?br />
定义要打印的文档
接下来这一步是定义要打印的文档Q用一个在javax.print包里的Doc的接口实例来创徏。每一个Doc的实例有两个必须定义的属性和一个可选择的属性:
        Object 代表要打印的内容
        DocFlavor的一个实例描q数据类?br />        可选的DocAttributeSet 包含打印时的属?br />
复习Doc接口的文档可以看出javax.print包里包含了一个叫SimpleDoc 的接口的l承Q它的构造函数包含了上面三个参数。要知道如何构徏SimpleDoc 的实例,我们假设你要打印两䆾存在http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif的gif文g拯?br />我们要做的就是构Z个SimpleDoc实例来描q这个文档创Z一个URL来指向图片,q且引用了DocFlavorQƈ把这两个传给SimpleDoc构造函敎ͼ

URL url = new URL(
   "http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif");
DocFlavor flavor = DocFlavor.URL.GIF;
SimpleDoc doc = new SimpleDoc(url, flavor, null);



启动打印
打印的最后一个步骤就是调用DocPrintJob?print()ҎQ传递待打印数据的Doc对象Q或选用PrintRequestAttributeSet实例。ؓ单v见,假设默认打印机支持你所需要的flavor和属性,在此情况下要使用下列代码上一个例子提及的gif文g打印两䆾Q?br />

PrintService service = PrintServiceLookup.lookupDefaultPrintService();
DocPrintJob job = service.createPrintJob();
URL url = new URL(
   "http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif ");
DocFlavor flavor = DocFlavor.URL.GIF;
Doc doc = new SimpleDoc(url, flavor, null);
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
attrs.add(new Copies(2));
job.print(doc, attrs)

;
注意Q一些情况下Q打C同步执行Q这可能会在实际打印完成之前q回对print()的调用?br />
关于作?br />Brett Spell是一个Frito-Lay的高U程序开发者ƈ写了Pro Java Programming 的初始版本?
资源
q是Pro Java Programming, Second Edition, Brett Spell (Apress, June 2005; ISBN: 1590594746):W十章“打印”的一部分 http://www.apress.com/book/bookDisplay.html?bID=421
要得到更多关于java API的文章,点击以下链接
http://www.javaworld.com/channel_content/jw-apis-index.shtml



]]>
JASS学习http://www.aygfsteel.com/esdsoftware/articles/44836.html吴名?/dc:creator>吴名?/author>Sun, 07 May 2006 00:24:00 GMThttp://www.aygfsteel.com/esdsoftware/articles/44836.htmlhttp://www.aygfsteel.com/esdsoftware/comments/44836.htmlhttp://www.aygfsteel.com/esdsoftware/articles/44836.html#Feedback0http://www.aygfsteel.com/esdsoftware/comments/commentRss/44836.htmlhttp://www.aygfsteel.com/esdsoftware/services/trackbacks/44836.html(来源Q{载自yesky|站Q?a >http://www.yesky.com/)

  摘要Q?/font>

  Java Authentication Authorization ServiceQJAASQJava验证和授权APIQ提供了灉|和可伸羃的机制来保证客户端或服务器端的JavaE序。Java早期的安全框架强调的是通过验证代码的来源和作者,保护用户避免受到下蝲下来的代码的d。JAAS的是通过验证谁在q行代码以及他/她的权限来保护系l面受用Ld。它让你能够一些标准的安全机制Q例如Solaris NISQ网l信息服务)、Windows NT、LDAPQ轻量目录存取协议)QKerberos{通过一U通用的,可配|的方式集成到系l中。本文首先向你介lJAAS验证中的一些核心部分,然后通过例子向你展示如何开发登录模块?br />
  你是否曾l需要ؓ一个应用程序实现登录模块呢Q如果你是一个比较有l验的程序员Q相信你q样的工作做q很多次Q而且每次都不完全一栗你有可能把你的d模块建立在Oracle数据库的基础上,也有可能使用的是NT的用户验证,或者用的是LDAP目录。如果有一U方法可以在不改变应用程序的代码的基础上支持上面提到的所有这一些安全机Ӟ对于E序员来说一定是一件幸q的事?br />
  现在你可以用JAAS实现上面的目标。JAAS是一个比较新的的Java API。在J2SE 1.3中,它是一个扩展包Q在J2SE 1.4中变成了一个核心包。在本文中,我们介lJAAS的一些核心概念,然后通过例子说明如何JAAS应用到实际的E序中。本文的例子是根据我们一个基于Web的Java应用E序q行改编的,在这个例子中Q我们用了关系数据库保存用Ld信息。由于用了JAASQ我们实C一个健壮而灵zȝd和n份验证模块?br />
  Java验证和授权:概论

  在JAAS出现以前QJava的安全模型是Z满跨^台的|络应用E序的需要而设计的。在Java早期版本中,Java通常是作E代码被使用Q例如AppletQ。因此最初的安全模型把注意力攑֜通过验证代码的来源来保护用户上。早期的Java安全机制中包含的概念Q如SercurityManagerQ沙概念,代码{֐Q策略文Ӟ多是Z保护用户?br />
  JAAS的出现反映了Java的演变。传l的服务器/客户端程序需要实现登录和存取控制QJAAS通过对运行程序的用户的进行验证,从而达C护系l的目的。虽然JAAS同时h验证和授权的能力Q在q篇文章中,我们主要介绍验证功能?br />
  通过在应用程序和底层的验证和授权机制之间加入一个抽象层QJAAS可以化涉及到Java Security包的E序开发。抽象层独立于^台的Ҏ开发h员可以用各U不同的安全机制Q而且不用修改应用E序U的代码。和其他Java Security API怼QJAAS通过一个可扩展的框Ӟ服务提供者接口(Service Provider InterfaceQSPIQ来保证E序独立于安全机制。服务提供者接口是׃l抽象类和接口组成的。图一中给ZJAASE序的整体框架图。应用程序的代码主要处理LoginContext。在LoginContext下面是一l动态配|的LoginModules。LoginModule使用正确的安全机制进行验证?br />
  图一l出了JAAS的概览。应用程序层的代码只需要和LoginContext打交道。在LoginContext之下是一l动态配|的LoginModule对象Q这些对象用相关的安全基础l构q行验证操作?br />

图一 JAAS概览

  JAAS提供了一些LoginModule的参考实C码,比如JndiLoginModule。开发h员也可以自己实现LoginModule接口Q就象在我们例子中的RdbmsLonginModule。同时我们还会告诉你如何使用一个简单的配置文g来安装应用程序?br />
  Z满可插接性,JAAS是可堆叠的。在单一d的情况下Q一l安全模块可以堆叠在一P然后被其他的安全机制按照堆叠的顺序被调用?br />
  JAAS的实现者根据现在一些流行的安全l构模式和框架将JASS模型化。例如可堆叠的特性同Unix下的可堆叠验证模块(PAMQPluggable Authentication ModuleQ框架就非常怼。从事务的角度看QJAAScM于双步提交(Two-Phase CommitQ?PCQ协议的行ؓ。JAAS中安全配|的概念Q包括策略文ӞPolice FileQ和许可QPermissionQ)来自于J2SE 1.2。JAASq从其他成熟的安全框架中借鉴了许多思想?br />


  客户端和服务器端的JAAS

  开发h员可以将JAAS应用到客L和服务器端。在客户端用JAAS很简单。在服务器端使用JAAS时情况要复杂一些。目前在应用服务器市Z的JAAS产品q不是很一_使用JAAS的J2EE应用服务器有一些细微的差别。例如JBossSx使用自己的结构,JAAS集成C一个更大的安全框架中;而虽然WebLogic 6.x也用了JAASQ安全框架却完全不一栗?br />
  现在你能够理解ؓ什么我们需要从客户端和服务器端的角度来看JAAS了。我们将在后面列ZU情况下的例子。ؓ了服务器端的例子程序更加简单,我们使用了Resin应用服务器?br />
  核心JAASc?/font>

  在用JAAS之前Q你首先需要安装JAAS。在J2SE 1.4中已l包括了JAASQ但是在J2SE 1.3中没有。如果你希望使用J2SE 1.3Q你可以从SUN的官方站点上下蝲JAAS。当正确安装了JAAS后,你会在安装目录的lib目录下找到jaas.jar。你需要将该\径加入Classpath中。(注:如果你安装了应用服务器,其中已l包括了JAASQ请阅读应用服务器的帮助文档以获得更详细的信息)。在Java安全属性文件java.security中,你可以改变一些与JAAS相关的系l属性。该文g保存在<jre_homeQ?lib/security目录中?br />
  在应用程序中使用JAAS验证通常会涉及到以下几个步骤Q?br />
  1. 创徏一个LoginContext的实例?br />
  2. Z能够获得和处理验证信息,一个CallBackHandler对象作ؓ参数传送给LoginContext?br />
  3. 通过调用LoginContext的loginQ)Ҏ来进行验证?br />
  4. 通过使用loginQ)Ҏq回的Subject对象实现一些特D的功能Q假讄录成功)?br />
  下面是一个简单的例子Q?br />
LoginContext lc = new LoginContext("MyExample");
try {
lc.login();
} catch (LoginException) {
// Authentication failed.
}

// Authentication successful, we can now continue.
// We can use the returned Subject if we like.
Subject sub = lc.getSubject();
Subject.doAs(sub, new MyPrivilegedAction());

  在运行这D代码时Q后台进行了以下的工作?br />
  1. 当初始化ӞLoginContext对象首先在JAAS配置文g中找到MyExample,然后更具该项的内容决定该加蝲哪个LoginModule对象Q参见图二)?br />
  2. 在登录时QLoginContext对象调用每个LoginModule对象的loginQ)Ҏ?br />
  3. 每个loginQ)Ҏq行验证操作或获得一个CallbackHandle对象?br />
  4. CallbackHandle对象通过使用一个或多个CallBackҎ同用戯行交互,获得用户输入?br />
  5. 向一个新的Subject对象中填入验证信息?br />
  我们对代码作进一步的解释。但是在q之前,让我们先看代码中涉及到的核心JAAScd接口。这些类可以被分ZU类型:

  普通类?SubjectQPrincipalQ凭?br />
  验证 LoginContextQLoginModuleQCallBackHandlerQCallback

  授权 PolicyQAuthPermissionQPrivateCredentialPermission

  上面列D的类和接口大多数都在javax.security.auth包中。在J2SE 1.4中,q有一些接口的实现cdcom.sun.security.auth包中?br />
  普通类型:SubjectQPrincipalQ凭?/b>

  SubjectcM表了一个验证实体,它可以是用户、管理员、Web服务Q设备或者其他的q程。该cd含了三中cd的安全信息:

   w䆾QIdentitiesQ:׃个或多个Principal对象表示

   公共凭证QPublic credentialsQ:例如名称或公q?br />
   U有凭证QPrivate credentialsQ:例如口o或私有密?br />
  Principal对象代表了Subject对象的n份。它们实Cjava.security.Principal和java.io.Serializable接口。在SubjectcMQ最重要的方法是getNameQ)。该Ҏq回一个n份名U。在Subject对象中包含了多个Principal对象Q因此它可以拥有多个名称。由于登录名U、n份证号和Email地址都可以作为用Lw䆾标识Q可见拥有多个n份名U的情况在实际应用中是非常普遍的情况?br />
  在上面提到的凭证q不是一个特定的cL借口Q它可以是Q何对象。凭证中可以包含M特定安全pȝ需要的验证信息Q例如标{(ticketQ,密钥或口令。Subject对象中维护着一l特定的U有和公有的凭证Q这些凭证可以通过getPrivateCredentialsQ)和getPublicCredentialsQ)Ҏ获得。这些方法通常在应用程序层中的安全子系l被调用?br />
  验证QLoginContext

  在应用程序层中,你可以用LoginContext对象来验证Subject对象。LoginContext对象同时体现了JAAS的动态可插入性(Dynamic PluggabilityQ,因ؓ当你创徏一个LoginContext的实例时Q你需要指定一个配|。LoginContext通常从一个文本文件中加蝲配置信息Q这些配|信息告诉LoginContext对象在登录时使用哪一个LoginModule对象?br />
  下面列出了在LoginContext中经怋用的三个ҎQ?

  login () q行d操作。该ҎȀzM配置中制定的所有LoginModule对象。如果成功,它将创徏一个经q了验证的Subject对象Q否则抛出LoginException异常?br />
  getSubject () q回l过验证的Subject对象

  logout () 注销Subject对象Q删除与之相关的Principal对象和凭?br />
  验证QLoginModule

  LoginModule是调用特定验证机制的接口。J2EE 1.4中包含了下面几种LoginModule的实现类Q?br />
  JndiLoginModule 用于验证在JNDI中配|的目录服务

  Krb5LoginModule 使用Kerberos协议q行验证

  NTLoginModul 使用当前用户在NT中的用户信息q行验证

  UnixLoginModule 使用当前用户在Unix中的用户信息q行验证

  同上面这些模块绑定在一Lq有对应的Principal接口的实现类Q例如NTDomainPrincipal和UnixPrincipal。这些类在com.sun.security.auth包中?br />
  LoginModule接口中包含了五个ҎQ?br />
  initialize () 当创ZLoginModule实例时会被构造函数调?br />
  login () q行验证

  commit () 当LgoninContext对象接受所有LoginModule对象传回的结果后调用该Ҏ。该ҎPrincipal对象和凭证赋lSubject对象?br />  
  abort () 当Q何一个LoginModule对象验证p|旉会调用该Ҏ。此时没有Q何Principal对象或凭证关联到Subject对象上?br />
  logout () 删除与Subject对象兌的Principal对象和凭证?br />
  在应用程序的代码中,E序员通常不会直接调用上面列出的方法,而是通过LigonContext间接调用q些Ҏ?br />
  验证QCallbackHandler和Callback

  CallbackHandler和Callback对象可以使LoginModule对象从系l和用户那里攉必要的验证信息,同时独立于实际的攉信息时发生的交互q程?br />  
  JAAS在javax.sevurity.auth.callback包中包含了七个Callback的实现类和两个CallbackHandler的实现类QChoiceCallback、ConfirmationCallback、LogcaleCallback、NameCallback、PasswordCallback、TextInputCallback、TextOutputCallback、DialogCallbackHandler和TextCallBackHandler。Callback接口只会在客L会被使用到。我在后面介绍如何~写你自qCallbackHandlercR?br />
  配置文g

  上面我已l提刎ͼJAAS的可扩展性来源于它能够进行动态配|,而配|信息通常是保存在文本。这些文本文件有很多个配|块构成Q我们通常把这些配|块UC甌QApplicationQ。每个申请对应了一个或多个特定的LoginModule对象?br />
  当你的代码构造一个LoginContext对象Ӟ你需要把配置文g中申L名称传递给它。LoginContext会Ҏ甌中的信息军_Ȁzd些LoginModule对象Q按照什么顺序激zM及用什么规则激zR?br />
  配置文g的结构如下所C?

Application {
ModuleClass Flag ModuleOptions;
ModuleClass Flag ModuleOptions;
...
};
Application {
ModuleClass Flag ModuleOptions;
...
};
...

  下面是一个名UCؓSample的申?br />
Sample {
com.sun.security.auth.module.NTLoginModule Rquired debug=true;
}

  上面q个单的甌指定了LoginContext对象应该使用NTLoginModuleq行验证。类的名U在ModuleClass中被指定。Flag控制当申请中包含了多个LoginModule时进行登录时的行为:Required、Sufficient、Requisite和Optional。最常用的是RequiredQ用它意味着对应的LoginModule对象必须被调用,q且必须需要通过所有的验证。由于Flag本n的复杂性,本文在这里不作深I?br />
  ModuleOption允许有多个参数。例如你可以讑֮调试参数为TrueQdebug=trueQ,q样诊断输出被送到System.out中?br />
  配置文g可以被Q意命名,q且可以被放在Q何位|。JAAS框架通过使用java.securty.auth.long.config属性来定配置文g的位|。例如当你的应用E序是JaasTestQ配|文件是当前目录下的jaas.configQ你需要在命o行中输入Q?br />
java -Djava.security.auth.login.config=jass.config JavaTest

  图二描述了配|文件中各元素之间的关系


图二 JAAS的配|文?

  通过命o行方式进行登录验?/font>

  Z说明JAAS到底能干什么,我在q里~写了两个例子。一个是单的由命令行输入调用的程序,另一个是服务器端的JSPE序。这两个E序都通过用户名/密码的方式进行登录,然后使用关系数据库对其进行验证?br />
  Z通过数据库进行验证,我们需要:

  1. 实现RdbmsLoginModulc,该类可以对输入的信息q行验证?br />
  2. ~辑一个配|文Ӟ告诉LoginContext如何使用RdbmsLoginModule?br />
  3. 实现ConsoleCallbackHandlerc,通过该类可以获取用户的输入?br />
  4. ~写应用E序代码?br />
  在RdbmsLoginModulcMQ我们必d现LgoinModule接口中的五个Ҏ。首先是initializeQ)ҎQ?br />
public void initialize(Subject subject, CallbackHandler
callbackHandler,

Map sharedState, Map options)
{
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;

url = (String)options.get("url");
driverClass = (String)options.get("driver");
debug = "true".equalsIgnoreCase((String)options.get("debug"));
}

  LoginContext在调用loginQ)Ҏ时会调用initializeQ)Ҏ。RdbmsLoginModule的第一个Q务就是在cM保存输入参数的引用。在验证成功后将向Subject对象中送入Principal对象和凭证?br />
  CallbackHandler对象会在loginQ)Ҏ中被使用到。sharedState可以使数据在不同的LoginModule对象之间׃nQ但是在q个例子中我们不会用它。最后是名ؓoptions的Map对象。options向LgoinModule对象传递在配置文gModuleOption域中定义的参数的倹{配|文件如下所C:

Example {
RdbmsLoginModule required
driver="org.gjt.mm.mysql.Driver"
url="jdbc:mysql://localhost/jaasdb?user=root"
debug="true";
};

  在配|文件中QRdbmsLoginModule包含了五个参敎ͼ其中driver、url、user和password是必需的,而debug是可选阐q。driver、url、user和password参数告诉我们如何获得JDBCq接。当然你q可以在ModuleOption域中加入数据库中的表或列的信息。用这些参数的目的是ؓ了能够对数据库进行操作。在LoginModulecȝinitializeQ)Ҏ中我们保存了每个参数的倹{?br />
  我们前面提到一个LoginContext对应的配|文件告诉它应该使用文g中的哪一个申诗这个信息是通过LgoinContext的构造函C递的。下面是初始化客L的代码,在代码中创徏了一个LoginContex对象q调用了loginQ)Ҏ?br />
ConsoleCallbackHandler cbh = new ConsoleCallbackHandler();
LoginContext lc = new LoginContext("Example", cbh);
lc.login();

  当LgoinContext.loginQ)Ҏ被调用时Q它调用所有加载了的LoginModule对象的loginQ)Ҏ。在我们的这个例子中是RdbmsLoginModule中的loginQ)Ҏ?br />
  RdbmsLoginModule中的loginQ)Ҏq行了下面的操作Q?br />
  1. 创徏两个Callback对象。这些对象从用户输入中获取用户名/密码。程序中使用了JAAS中的两个Callbackc?NameCallback和PasswordCallbackQ这两个cd含在javax.security.auth.callback包中Q?br />
  2. 通过callbacks作ؓ参数传递给CallbackHandler的handleQ)Ҏ来激zCallback?br />
  3. 通过Callback对象获得用户名/密码?br />
  4. 在rdbmsValidateQ)Ҏ中通过JDBC在数据库中验证获取的用户名/密码?br />
  下面是RdbmsLoginModule中的loginQ)Ҏ的代?br />
public boolean login() throws LoginException {
if (callbackHandler == null)
throw new LoginException("no handler");

NameCallback nameCb = new NameCallback("user: ");
PasswordCallback passCb = new PasswordCallback("password: ", true);
callbacks = new Callback[] { nameCb, passCb };
callbackHandler.handle(callbacks);

String username = nameCb.getName();
String password = new String(passCb.getPassword());
success = rdbmsValidate(username, password);

return(true);
}

  在ConsoleCallbackHandlercȝhandleQ)Ҏ中你可以看到Callback对象是如何同用户q行交互的:

  使用JSP和关pL据库q行d验证

  现在我们希望通过命o行调用的E序一直到Web应用E序中。由于Web应用E序与一般的应用E序的交互方式有区别不同Q我们将不能使用JAAS提供的标准Callback和CallbackHandlercR因为我们不能在WebE序中打开一个命令窗口让用户输入信息。也怽会想到我们也可以使用ZHTTP的验证,q样我们可以从浏览器弹出的用户名Q密码窗口中获得用户输入。但是这样做也有一些问题,它要求徏立v双向的HTTPq接Q在loginQ)Ҏ很难实现双向q接Q。因此在我们的例子中我们会用利用表单进行登录,从表单中获取用户输入的信息,然后通过RdbmsLoginModulec验证它?br />
  ׃我们没有在应用层同LoginModule直接打交道,而是通过LgoinContext来调用其中的Ҏ的,我们如何获得用户名和密码放入LoginModule对象中呢Q我们可以用其它的Ҏ来绕q这个问题,例如我们可以在创建LoginContext对象前先初始化一个Subject对象Q在Subject对象的凭证中保存用户名和密码。然后我们可以将该Subject对象传递给LoginContext的构造函数。这U方法虽然从技术上来说没有什么问题,但是它在应用E序层增加了很多与安全机制相关的代码。而且通常是在验证后向Subject送入凭证Q而不是之前?br />前面我们提到可以实现一个CallbackHandlerc,然后它的实例传递给LoginContext对象。在q里我们可以采用cM的方法来处理用户名和密码。我们实C一个新的类PassiveCallbackHandler。下面是在JSP中用该cȝ代码Q?br />
String user = request.getParameter("user");
String pass = request.getParameter("pass");
PassiveCallbackHandler cbh = new PassiveCallbackHandler(user, pass);
LoginContext lc = new LoginContext("Example", cbh);

  PassiveCallbackHandler中构造函数的参数包含了用户名和密码。因此它可以在Callbick对象中设定正的倹{下面是PassiveCallbackHandlercȝhandleQ)Ҏ的代码:

public void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException {
for (int i = 0; i Q?callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback nameCb = (NameCallback)callbacks[i];

nameCb.setName(user);
} else if(callbacks[i] instanceof PasswordCallback) {
PasswordCallback passCb = (PasswordCallback)callbacks[i];
passCb.setPassword(pass.toCharArray());
} else {
throw(new UnsupportedCallbackException(callbacks[i],
"Callback class not supported"));
}
}
}
}

  从上面的代码中可以发现实际上我们只是从ConsoleCallbackHandler中去除了那些提示用户输入的代码?br />
  在运行这个JSP例子的时候,我们需要设定系l属性,q样LgoinContext对象才知道如何查扑֐UCؓ"Example"的配|。我们用的是Resin服务器。在resin.conf中,我们增加了一个<caucho.comQ节点:

Qsystem-property
java.security.auth.login.config="/resin/conf/jaas.config"/Q?

  

  JAAS通过提供动态的、可扩展的模型来q行用户验证和控制权限,从而应用E序有更加健壮的安全机制。同时它q能够让你能够很L地创qd机制。JAAS可以同时在在客户端和服务器端应用E序上工作。虽然在服务器端的JAAS到目前还不是很稳定,但是随着技术的发展Q相信会很好地解册个问?

(来源Q{载自yesky|站Q?a >http://www.yesky.com/)

  摘要Q?/font>

  Java Authentication Authorization ServiceQJAASQJava验证和授权APIQ提供了灉|和可伸羃的机制来保证客户端或服务器端的JavaE序。Java早期的安全框架强调的是通过验证代码的来源和作者,保护用户避免受到下蝲下来的代码的d。JAAS的是通过验证谁在q行代码以及他/她的权限来保护系l面受用Ld。它让你能够一些标准的安全机制Q例如Solaris NISQ网l信息服务)、Windows NT、LDAPQ轻量目录存取协议)QKerberos{通过一U通用的,可配|的方式集成到系l中。本文首先向你介lJAAS验证中的一些核心部分,然后通过例子向你展示如何开发登录模块?br />
  你是否曾l需要ؓ一个应用程序实现登录模块呢Q如果你是一个比较有l验的程序员Q相信你q样的工作做q很多次Q而且每次都不完全一栗你有可能把你的d模块建立在Oracle数据库的基础上,也有可能使用的是NT的用户验证,或者用的是LDAP目录。如果有一U方法可以在不改变应用程序的代码的基础上支持上面提到的所有这一些安全机Ӟ对于E序员来说一定是一件幸q的事?br />
  现在你可以用JAAS实现上面的目标。JAAS是一个比较新的的Java API。在J2SE 1.3中,它是一个扩展包Q在J2SE 1.4中变成了一个核心包。在本文中,我们介lJAAS的一些核心概念,然后通过例子说明如何JAAS应用到实际的E序中。本文的例子是根据我们一个基于Web的Java应用E序q行改编的,在这个例子中Q我们用了关系数据库保存用Ld信息。由于用了JAASQ我们实C一个健壮而灵zȝd和n份验证模块?br />
  Java验证和授权:概论

  在JAAS出现以前QJava的安全模型是Z满跨^台的|络应用E序的需要而设计的。在Java早期版本中,Java通常是作E代码被使用Q例如AppletQ。因此最初的安全模型把注意力攑֜通过验证代码的来源来保护用户上。早期的Java安全机制中包含的概念Q如SercurityManagerQ沙概念,代码{֐Q策略文Ӟ多是Z保护用户?br />
  JAAS的出现反映了Java的演变。传l的服务器/客户端程序需要实现登录和存取控制QJAAS通过对运行程序的用户的进行验证,从而达C护系l的目的。虽然JAAS同时h验证和授权的能力Q在q篇文章中,我们主要介绍验证功能?br />
  通过在应用程序和底层的验证和授权机制之间加入一个抽象层QJAAS可以化涉及到Java Security包的E序开发。抽象层独立于^台的Ҏ开发h员可以用各U不同的安全机制Q而且不用修改应用E序U的代码。和其他Java Security API怼QJAAS通过一个可扩展的框Ӟ服务提供者接口(Service Provider InterfaceQSPIQ来保证E序独立于安全机制。服务提供者接口是׃l抽象类和接口组成的。图一中给ZJAASE序的整体框架图。应用程序的代码主要处理LoginContext。在LoginContext下面是一l动态配|的LoginModules。LoginModule使用正确的安全机制进行验证?br />
  图一l出了JAAS的概览。应用程序层的代码只需要和LoginContext打交道。在LoginContext之下是一l动态配|的LoginModule对象Q这些对象用相关的安全基础l构q行验证操作?br />

图一 JAAS概览

  JAAS提供了一些LoginModule的参考实C码,比如JndiLoginModule。开发h员也可以自己实现LoginModule接口Q就象在我们例子中的RdbmsLonginModule。同时我们还会告诉你如何使用一个简单的配置文g来安装应用程序?br />
  Z满可插接性,JAAS是可堆叠的。在单一d的情况下Q一l安全模块可以堆叠在一P然后被其他的安全机制按照堆叠的顺序被调用?br />
  JAAS的实现者根据现在一些流行的安全l构模式和框架将JASS模型化。例如可堆叠的特性同Unix下的可堆叠验证模块(PAMQPluggable Authentication ModuleQ框架就非常怼。从事务的角度看QJAAScM于双步提交(Two-Phase CommitQ?PCQ协议的行ؓ。JAAS中安全配|的概念Q包括策略文ӞPolice FileQ和许可QPermissionQ)来自于J2SE 1.2。JAASq从其他成熟的安全框架中借鉴了许多思想?br />


  客户端和服务器端的JAAS

  开发h员可以将JAAS应用到客L和服务器端。在客户端用JAAS很简单。在服务器端使用JAAS时情况要复杂一些。目前在应用服务器市Z的JAAS产品q不是很一_使用JAAS的J2EE应用服务器有一些细微的差别。例如JBossSx使用自己的结构,JAAS集成C一个更大的安全框架中;而虽然WebLogic 6.x也用了JAASQ安全框架却完全不一栗?br />
  现在你能够理解ؓ什么我们需要从客户端和服务器端的角度来看JAAS了。我们将在后面列ZU情况下的例子。ؓ了服务器端的例子程序更加简单,我们使用了Resin应用服务器?br />
  核心JAASc?/font>

  在用JAAS之前Q你首先需要安装JAAS。在J2SE 1.4中已l包括了JAASQ但是在J2SE 1.3中没有。如果你希望使用J2SE 1.3Q你可以从SUN的官方站点上下蝲JAAS。当正确安装了JAAS后,你会在安装目录的lib目录下找到jaas.jar。你需要将该\径加入Classpath中。(注:如果你安装了应用服务器,其中已l包括了JAASQ请阅读应用服务器的帮助文档以获得更详细的信息)。在Java安全属性文件java.security中,你可以改变一些与JAAS相关的系l属性。该文g保存在<jre_homeQ?lib/security目录中?br />
  在应用程序中使用JAAS验证通常会涉及到以下几个步骤Q?br />
  1. 创徏一个LoginContext的实例?br />
  2. Z能够获得和处理验证信息,一个CallBackHandler对象作ؓ参数传送给LoginContext?br />
  3. 通过调用LoginContext的loginQ)Ҏ来进行验证?br />
  4. 通过使用loginQ)Ҏq回的Subject对象实现一些特D的功能Q假讄录成功)?br />
  下面是一个简单的例子Q?br />
LoginContext lc = new LoginContext("MyExample");
try {
lc.login();
} catch (LoginException) {
// Authentication failed.
}

// Authentication successful, we can now continue.
// We can use the returned Subject if we like.
Subject sub = lc.getSubject();
Subject.doAs(sub, new MyPrivilegedAction());

  在运行这D代码时Q后台进行了以下的工作?br />
  1. 当初始化ӞLoginContext对象首先在JAAS配置文g中找到MyExample,然后更具该项的内容决定该加蝲哪个LoginModule对象Q参见图二)?br />
  2. 在登录时QLoginContext对象调用每个LoginModule对象的loginQ)Ҏ?br />
  3. 每个loginQ)Ҏq行验证操作或获得一个CallbackHandle对象?br />
  4. CallbackHandle对象通过使用一个或多个CallBackҎ同用戯行交互,获得用户输入?br />
  5. 向一个新的Subject对象中填入验证信息?br />
  我们对代码作进一步的解释。但是在q之前,让我们先看代码中涉及到的核心JAAScd接口。这些类可以被分ZU类型:

  普通类?SubjectQPrincipalQ凭?br />
  验证 LoginContextQLoginModuleQCallBackHandlerQCallback

  授权 PolicyQAuthPermissionQPrivateCredentialPermission

  上面列D的类和接口大多数都在javax.security.auth包中。在J2SE 1.4中,q有一些接口的实现cdcom.sun.security.auth包中?br />
  普通类型:SubjectQPrincipalQ凭?/b>

  SubjectcM表了一个验证实体,它可以是用户、管理员、Web服务Q设备或者其他的q程。该cd含了三中cd的安全信息:

   w䆾QIdentitiesQ:׃个或多个Principal对象表示

   公共凭证QPublic credentialsQ:例如名称或公q?br />
   U有凭证QPrivate credentialsQ:例如口o或私有密?br />
  Principal对象代表了Subject对象的n份。它们实Cjava.security.Principal和java.io.Serializable接口。在SubjectcMQ最重要的方法是getNameQ)。该Ҏq回一个n份名U。在Subject对象中包含了多个Principal对象Q因此它可以拥有多个名称。由于登录名U、n份证号和Email地址都可以作为用Lw䆾标识Q可见拥有多个n份名U的情况在实际应用中是非常普遍的情况?br />
  在上面提到的凭证q不是一个特定的cL借口Q它可以是Q何对象。凭证中可以包含M特定安全pȝ需要的验证信息Q例如标{(ticketQ,密钥或口令。Subject对象中维护着一l特定的U有和公有的凭证Q这些凭证可以通过getPrivateCredentialsQ)和getPublicCredentialsQ)Ҏ获得。这些方法通常在应用程序层中的安全子系l被调用?br />
  验证QLoginContext

  在应用程序层中,你可以用LoginContext对象来验证Subject对象。LoginContext对象同时体现了JAAS的动态可插入性(Dynamic PluggabilityQ,因ؓ当你创徏一个LoginContext的实例时Q你需要指定一个配|。LoginContext通常从一个文本文件中加蝲配置信息Q这些配|信息告诉LoginContext对象在登录时使用哪一个LoginModule对象?br />
  下面列出了在LoginContext中经怋用的三个ҎQ?

  login () q行d操作。该ҎȀzM配置中制定的所有LoginModule对象。如果成功,它将创徏一个经q了验证的Subject对象Q否则抛出LoginException异常?br />
  getSubject () q回l过验证的Subject对象

  logout () 注销Subject对象Q删除与之相关的Principal对象和凭?br />
  验证QLoginModule

  LoginModule是调用特定验证机制的接口。J2EE 1.4中包含了下面几种LoginModule的实现类Q?br />
  JndiLoginModule 用于验证在JNDI中配|的目录服务

  Krb5LoginModule 使用Kerberos协议q行验证

  NTLoginModul 使用当前用户在NT中的用户信息q行验证

  UnixLoginModule 使用当前用户在Unix中的用户信息q行验证

  同上面这些模块绑定在一Lq有对应的Principal接口的实现类Q例如NTDomainPrincipal和UnixPrincipal。这些类在com.sun.security.auth包中?br />
  LoginModule接口中包含了五个ҎQ?br />
  initialize () 当创ZLoginModule实例时会被构造函数调?br />
  login () q行验证

  commit () 当LgoninContext对象接受所有LoginModule对象传回的结果后调用该Ҏ。该ҎPrincipal对象和凭证赋lSubject对象?br />  
  abort () 当Q何一个LoginModule对象验证p|旉会调用该Ҏ。此时没有Q何Principal对象或凭证关联到Subject对象上?br />
  logout () 删除与Subject对象兌的Principal对象和凭证?br />
  在应用程序的代码中,E序员通常不会直接调用上面列出的方法,而是通过LigonContext间接调用q些Ҏ?br />
  验证QCallbackHandler和Callback

  CallbackHandler和Callback对象可以使LoginModule对象从系l和用户那里攉必要的验证信息,同时独立于实际的攉信息时发生的交互q程?br />  
  JAAS在javax.sevurity.auth.callback包中包含了七个Callback的实现类和两个CallbackHandler的实现类QChoiceCallback、ConfirmationCallback、LogcaleCallback、NameCallback、PasswordCallback、TextInputCallback、TextOutputCallback、DialogCallbackHandler和TextCallBackHandler。Callback接口只会在客L会被使用到。我在后面介绍如何~写你自qCallbackHandlercR?br />
  配置文g

  上面我已l提刎ͼJAAS的可扩展性来源于它能够进行动态配|,而配|信息通常是保存在文本。这些文本文件有很多个配|块构成Q我们通常把这些配|块UC甌QApplicationQ。每个申请对应了一个或多个特定的LoginModule对象?br />
  当你的代码构造一个LoginContext对象Ӟ你需要把配置文g中申L名称传递给它。LoginContext会Ҏ甌中的信息军_Ȁzd些LoginModule对象Q按照什么顺序激zM及用什么规则激zR?br />
  配置文g的结构如下所C?

Application {
ModuleClass Flag ModuleOptions;
ModuleClass Flag ModuleOptions;
...
};
Application {
ModuleClass Flag ModuleOptions;
...
};
...

  下面是一个名UCؓSample的申?br />
Sample {
com.sun.security.auth.module.NTLoginModule Rquired debug=true;
}

  上面q个单的甌指定了LoginContext对象应该使用NTLoginModuleq行验证。类的名U在ModuleClass中被指定。Flag控制当申请中包含了多个LoginModule时进行登录时的行为:Required、Sufficient、Requisite和Optional。最常用的是RequiredQ用它意味着对应的LoginModule对象必须被调用,q且必须需要通过所有的验证。由于Flag本n的复杂性,本文在这里不作深I?br />
  ModuleOption允许有多个参数。例如你可以讑֮调试参数为TrueQdebug=trueQ,q样诊断输出被送到System.out中?br />
  配置文g可以被Q意命名,q且可以被放在Q何位|。JAAS框架通过使用java.securty.auth.long.config属性来定配置文g的位|。例如当你的应用E序是JaasTestQ配|文件是当前目录下的jaas.configQ你需要在命o行中输入Q?br />
java -Djava.security.auth.login.config=jass.config JavaTest

  图二描述了配|文件中各元素之间的关系


图二 JAAS的配|文?

  通过命o行方式进行登录验?/font>

  Z说明JAAS到底能干什么,我在q里~写了两个例子。一个是单的由命令行输入调用的程序,另一个是服务器端的JSPE序。这两个E序都通过用户名/密码的方式进行登录,然后使用关系数据库对其进行验证?br />
  Z通过数据库进行验证,我们需要:

  1. 实现RdbmsLoginModulc,该类可以对输入的信息q行验证?br />
  2. ~辑一个配|文Ӟ告诉LoginContext如何使用RdbmsLoginModule?br />
  3. 实现ConsoleCallbackHandlerc,通过该类可以获取用户的输入?br />
  4. ~写应用E序代码?br />
  在RdbmsLoginModulcMQ我们必d现LgoinModule接口中的五个Ҏ。首先是initializeQ)ҎQ?br />
public void initialize(Subject subject, CallbackHandler
callbackHandler,

Map sharedState, Map options)
{
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;

url = (String)options.get("url");
driverClass = (String)options.get("driver");
debug = "true".equalsIgnoreCase((String)options.get("debug"));
}

  LoginContext在调用loginQ)Ҏ时会调用initializeQ)Ҏ。RdbmsLoginModule的第一个Q务就是在cM保存输入参数的引用。在验证成功后将向Subject对象中送入Principal对象和凭证?br />
  CallbackHandler对象会在loginQ)Ҏ中被使用到。sharedState可以使数据在不同的LoginModule对象之间׃nQ但是在q个例子中我们不会用它。最后是名ؓoptions的Map对象。options向LgoinModule对象传递在配置文gModuleOption域中定义的参数的倹{配|文件如下所C:

Example {
RdbmsLoginModule required
driver="org.gjt.mm.mysql.Driver"
url="jdbc:mysql://localhost/jaasdb?user=root"
debug="true";
};

  在配|文件中QRdbmsLoginModule包含了五个参敎ͼ其中driver、url、user和password是必需的,而debug是可选阐q。driver、url、user和password参数告诉我们如何获得JDBCq接。当然你q可以在ModuleOption域中加入数据库中的表或列的信息。用这些参数的目的是ؓ了能够对数据库进行操作。在LoginModulecȝinitializeQ)Ҏ中我们保存了每个参数的倹{?br />
  我们前面提到一个LoginContext对应的配|文件告诉它应该使用文g中的哪一个申诗这个信息是通过LgoinContext的构造函C递的。下面是初始化客L的代码,在代码中创徏了一个LoginContex对象q调用了loginQ)Ҏ?br />
ConsoleCallbackHandler cbh = new ConsoleCallbackHandler();
LoginContext lc = new LoginContext("Example", cbh);
lc.login();

  当LgoinContext.loginQ)Ҏ被调用时Q它调用所有加载了的LoginModule对象的loginQ)Ҏ。在我们的这个例子中是RdbmsLoginModule中的loginQ)Ҏ?br />
  RdbmsLoginModule中的loginQ)Ҏq行了下面的操作Q?br />
  1. 创徏两个Callback对象。这些对象从用户输入中获取用户名/密码。程序中使用了JAAS中的两个Callbackc?NameCallback和PasswordCallbackQ这两个cd含在javax.security.auth.callback包中Q?br />
  2. 通过callbacks作ؓ参数传递给CallbackHandler的handleQ)Ҏ来激zCallback?br />
  3. 通过Callback对象获得用户名/密码?br />
  4. 在rdbmsValidateQ)Ҏ中通过JDBC在数据库中验证获取的用户名/密码?br />
  下面是RdbmsLoginModule中的loginQ)Ҏ的代?br />
public boolean login() throws LoginException {
if (callbackHandler == null)
throw new LoginException("no handler");

NameCallback nameCb = new NameCallback("user: ");
PasswordCallback passCb = new PasswordCallback("password: ", true);
callbacks = new Callback[] { nameCb, passCb };
callbackHandler.handle(callbacks);

String username = nameCb.getName();
String password = new String(passCb.getPassword());
success = rdbmsValidate(username, password);

return(true);
}

  在ConsoleCallbackHandlercȝhandleQ)Ҏ中你可以看到Callback对象是如何同用户q行交互的:

  使用JSP和关pL据库q行d验证

  现在我们希望通过命o行调用的E序一直到Web应用E序中。由于Web应用E序与一般的应用E序的交互方式有区别不同Q我们将不能使用JAAS提供的标准Callback和CallbackHandlercR因为我们不能在WebE序中打开一个命令窗口让用户输入信息。也怽会想到我们也可以使用ZHTTP的验证,q样我们可以从浏览器弹出的用户名Q密码窗口中获得用户输入。但是这样做也有一些问题,它要求徏立v双向的HTTPq接Q在loginQ)Ҏ很难实现双向q接Q。因此在我们的例子中我们会用利用表单进行登录,从表单中获取用户输入的信息,然后通过RdbmsLoginModulec验证它?br />
  ׃我们没有在应用层同LoginModule直接打交道,而是通过LgoinContext来调用其中的Ҏ的,我们如何获得用户名和密码放入LoginModule对象中呢Q我们可以用其它的Ҏ来绕q这个问题,例如我们可以在创建LoginContext对象前先初始化一个Subject对象Q在Subject对象的凭证中保存用户名和密码。然后我们可以将该Subject对象传递给LoginContext的构造函数。这U方法虽然从技术上来说没有什么问题,但是它在应用E序层增加了很多与安全机制相关的代码。而且通常是在验证后向Subject送入凭证Q而不是之前?br />前面我们提到可以实现一个CallbackHandlerc,然后它的实例传递给LoginContext对象。在q里我们可以采用cM的方法来处理用户名和密码。我们实C一个新的类PassiveCallbackHandler。下面是在JSP中用该cȝ代码Q?br />
String user = request.getParameter("user");
String pass = request.getParameter("pass");
PassiveCallbackHandler cbh = new PassiveCallbackHandler(user, pass);
LoginContext lc = new LoginContext("Example", cbh);

  PassiveCallbackHandler中构造函数的参数包含了用户名和密码。因此它可以在Callbick对象中设定正的倹{下面是PassiveCallbackHandlercȝhandleQ)Ҏ的代码:

public void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException {
for (int i = 0; i Q?callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback nameCb = (NameCallback)callbacks[i];

nameCb.setName(user);
} else if(callbacks[i] instanceof PasswordCallback) {
PasswordCallback passCb = (PasswordCallback)callbacks[i];
passCb.setPassword(pass.toCharArray());
} else {
throw(new UnsupportedCallbackException(callbacks[i],
"Callback class not supported"));
}
}
}
}

  从上面的代码中可以发现实际上我们只是从ConsoleCallbackHandler中去除了那些提示用户输入的代码?br />
  在运行这个JSP例子的时候,我们需要设定系l属性,q样LgoinContext对象才知道如何查扑֐UCؓ"Example"的配|。我们用的是Resin服务器。在resin.conf中,我们增加了一个<caucho.comQ节点:

Qsystem-property
java.security.auth.login.config="/resin/conf/jaas.config"/Q?

  

  JAAS通过提供动态的、可扩展的模型来q行用户验证和控制权限,从而应用E序有更加健壮的安全机制。同时它q能够让你能够很L地创qd机制。JAAS可以同时在在客户端和服务器端应用E序上工作。虽然在服务器端的JAAS到目前还不是很稳定,但是随着技术的发展Q相信会很好地解册个问?

(来源Q{载自yesky|站Q?a >http://www.yesky.com/)

  摘要Q?/font>

  Java Authentication Authorization ServiceQJAASQJava验证和授权APIQ提供了灉|和可伸羃的机制来保证客户端或服务器端的JavaE序。Java早期的安全框架强调的是通过验证代码的来源和作者,保护用户避免受到下蝲下来的代码的d。JAAS的是通过验证谁在q行代码以及他/她的权限来保护系l面受用Ld。它让你能够一些标准的安全机制Q例如Solaris NISQ网l信息服务)、Windows NT、LDAPQ轻量目录存取协议)QKerberos{通过一U通用的,可配|的方式集成到系l中。本文首先向你介lJAAS验证中的一些核心部分,然后通过例子向你展示如何开发登录模块?br />
  你是否曾l需要ؓ一个应用程序实现登录模块呢Q如果你是一个比较有l验的程序员Q相信你q样的工作做q很多次Q而且每次都不完全一栗你有可能把你的d模块建立在Oracle数据库的基础上,也有可能使用的是NT的用户验证,或者用的是LDAP目录。如果有一U方法可以在不改变应用程序的代码的基础上支持上面提到的所有这一些安全机Ӟ对于E序员来说一定是一件幸q的事?br />
  现在你可以用JAAS实现上面的目标。JAAS是一个比较新的的Java API。在J2SE 1.3中,它是一个扩展包Q在J2SE 1.4中变成了一个核心包。在本文中,我们介lJAAS的一些核心概念,然后通过例子说明如何JAAS应用到实际的E序中。本文的例子是根据我们一个基于Web的Java应用E序q行改编的,在这个例子中Q我们用了关系数据库保存用Ld信息。由于用了JAASQ我们实C一个健壮而灵zȝd和n份验证模块?br />
  Java验证和授权:概论

  在JAAS出现以前QJava的安全模型是Z满跨^台的|络应用E序的需要而设计的。在Java早期版本中,Java通常是作E代码被使用Q例如AppletQ。因此最初的安全模型把注意力攑֜通过验证代码的来源来保护用户上。早期的Java安全机制中包含的概念Q如SercurityManagerQ沙概念,代码{֐Q策略文Ӟ多是Z保护用户?br />
  JAAS的出现反映了Java的演变。传l的服务器/客户端程序需要实现登录和存取控制QJAAS通过对运行程序的用户的进行验证,从而达C护系l的目的。虽然JAAS同时h验证和授权的能力Q在q篇文章中,我们主要介绍验证功能?br />
  通过在应用程序和底层的验证和授权机制之间加入一个抽象层QJAAS可以化涉及到Java Security包的E序开发。抽象层独立于^台的Ҏ开发h员可以用各U不同的安全机制Q而且不用修改应用E序U的代码。和其他Java Security API怼QJAAS通过一个可扩展的框Ӟ服务提供者接口(Service Provider InterfaceQSPIQ来保证E序独立于安全机制。服务提供者接口是׃l抽象类和接口组成的。图一中给ZJAASE序的整体框架图。应用程序的代码主要处理LoginContext。在LoginContext下面是一l动态配|的LoginModules。LoginModule使用正确的安全机制进行验证?br />
  图一l出了JAAS的概览。应用程序层的代码只需要和LoginContext打交道。在LoginContext之下是一l动态配|的LoginModule对象Q这些对象用相关的安全基础l构q行验证操作?br />

图一 JAAS概览

  JAAS提供了一些LoginModule的参考实C码,比如JndiLoginModule。开发h员也可以自己实现LoginModule接口Q就象在我们例子中的RdbmsLonginModule。同时我们还会告诉你如何使用一个简单的配置文g来安装应用程序?br />
  Z满可插接性,JAAS是可堆叠的。在单一d的情况下Q一l安全模块可以堆叠在一P然后被其他的安全机制按照堆叠的顺序被调用?br />
  JAAS的实现者根据现在一些流行的安全l构模式和框架将JASS模型化。例如可堆叠的特性同Unix下的可堆叠验证模块(PAMQPluggable Authentication ModuleQ框架就非常怼。从事务的角度看QJAAScM于双步提交(Two-Phase CommitQ?PCQ协议的行ؓ。JAAS中安全配|的概念Q包括策略文ӞPolice FileQ和许可QPermissionQ)来自于J2SE 1.2。JAASq从其他成熟的安全框架中借鉴了许多思想?br />


  客户端和服务器端的JAAS

  开发h员可以将JAAS应用到客L和服务器端。在客户端用JAAS很简单。在服务器端使用JAAS时情况要复杂一些。目前在应用服务器市Z的JAAS产品q不是很一_使用JAAS的J2EE应用服务器有一些细微的差别。例如JBossSx使用自己的结构,JAAS集成C一个更大的安全框架中;而虽然WebLogic 6.x也用了JAASQ安全框架却完全不一栗?br />
  现在你能够理解ؓ什么我们需要从客户端和服务器端的角度来看JAAS了。我们将在后面列ZU情况下的例子。ؓ了服务器端的例子程序更加简单,我们使用了Resin应用服务器?br />
  核心JAASc?/font>

  在用JAAS之前Q你首先需要安装JAAS。在J2SE 1.4中已l包括了JAASQ但是在J2SE 1.3中没有。如果你希望使用J2SE 1.3Q你可以从SUN的官方站点上下蝲JAAS。当正确安装了JAAS后,你会在安装目录的lib目录下找到jaas.jar。你需要将该\径加入Classpath中。(注:如果你安装了应用服务器,其中已l包括了JAASQ请阅读应用服务器的帮助文档以获得更详细的信息)。在Java安全属性文件java.security中,你可以改变一些与JAAS相关的系l属性。该文g保存在<jre_homeQ?lib/security目录中?br />
  在应用程序中使用JAAS验证通常会涉及到以下几个步骤Q?br />
  1. 创徏一个LoginContext的实例?br />
  2. Z能够获得和处理验证信息,一个CallBackHandler对象作ؓ参数传送给LoginContext?br />
  3. 通过调用LoginContext的loginQ)Ҏ来进行验证?br />
  4. 通过使用loginQ)Ҏq回的Subject对象实现一些特D的功能Q假讄录成功)?br />
  下面是一个简单的例子Q?br />
LoginContext lc = new LoginContext("MyExample");
try {
lc.login();
} catch (LoginException) {
// Authentication failed.
}

// Authentication successful, we can now continue.
// We can use the returned Subject if we like.
Subject sub = lc.getSubject();
Subject.doAs(sub, new MyPrivilegedAction());

  在运行这D代码时Q后台进行了以下的工作?br />
  1. 当初始化ӞLoginContext对象首先在JAAS配置文g中找到MyExample,然后更具该项的内容决定该加蝲哪个LoginModule对象Q参见图二)?br />
  2. 在登录时QLoginContext对象调用每个LoginModule对象的loginQ)Ҏ?br />
  3. 每个loginQ)Ҏq行验证操作或获得一个CallbackHandle对象?br />
  4. CallbackHandle对象通过使用一个或多个CallBackҎ同用戯行交互,获得用户输入?br />
  5. 向一个新的Subject对象中填入验证信息?br />
  我们对代码作进一步的解释。但是在q之前,让我们先看代码中涉及到的核心JAAScd接口。这些类可以被分ZU类型:

  普通类?SubjectQPrincipalQ凭?br />
  验证 LoginContextQLoginModuleQCallBackHandlerQCallback

  授权 PolicyQAuthPermissionQPrivateCredentialPermission

  上面列D的类和接口大多数都在javax.security.auth包中。在J2SE 1.4中,q有一些接口的实现cdcom.sun.security.auth包中?br />
  普通类型:SubjectQPrincipalQ凭?/b>

  SubjectcM表了一个验证实体,它可以是用户、管理员、Web服务Q设备或者其他的q程。该cd含了三中cd的安全信息:

   w䆾QIdentitiesQ:׃个或多个Principal对象表示

   公共凭证QPublic credentialsQ:例如名称或公q?br />
   U有凭证QPrivate credentialsQ:例如口o或私有密?br />
  Principal对象代表了Subject对象的n份。它们实Cjava.security.Principal和java.io.Serializable接口。在SubjectcMQ最重要的方法是getNameQ)。该Ҏq回一个n份名U。在Subject对象中包含了多个Principal对象Q因此它可以拥有多个名称。由于登录名U、n份证号和Email地址都可以作为用Lw䆾标识Q可见拥有多个n份名U的情况在实际应用中是非常普遍的情况?br />
  在上面提到的凭证q不是一个特定的cL借口Q它可以是Q何对象。凭证中可以包含M特定安全pȝ需要的验证信息Q例如标{(ticketQ,密钥或口令。Subject对象中维护着一l特定的U有和公有的凭证Q这些凭证可以通过getPrivateCredentialsQ)和getPublicCredentialsQ)Ҏ获得。这些方法通常在应用程序层中的安全子系l被调用?br />
  验证QLoginContext

  在应用程序层中,你可以用LoginContext对象来验证Subject对象。LoginContext对象同时体现了JAAS的动态可插入性(Dynamic PluggabilityQ,因ؓ当你创徏一个LoginContext的实例时Q你需要指定一个配|。LoginContext通常从一个文本文件中加蝲配置信息Q这些配|信息告诉LoginContext对象在登录时使用哪一个LoginModule对象?br />
  下面列出了在LoginContext中经怋用的三个ҎQ?

  login () q行d操作。该ҎȀzM配置中制定的所有LoginModule对象。如果成功,它将创徏一个经q了验证的Subject对象Q否则抛出LoginException异常?br />
  getSubject () q回l过验证的Subject对象

  logout () 注销Subject对象Q删除与之相关的Principal对象和凭?br />
  验证QLoginModule

  LoginModule是调用特定验证机制的接口。J2EE 1.4中包含了下面几种LoginModule的实现类Q?br />
  JndiLoginModule 用于验证在JNDI中配|的目录服务

  Krb5LoginModule 使用Kerberos协议q行验证

  NTLoginModul 使用当前用户在NT中的用户信息q行验证

  UnixLoginModule 使用当前用户在Unix中的用户信息q行验证

  同上面这些模块绑定在一Lq有对应的Principal接口的实现类Q例如NTDomainPrincipal和UnixPrincipal。这些类在com.sun.security.auth包中?br />
  LoginModule接口中包含了五个ҎQ?br />
  initialize () 当创ZLoginModule实例时会被构造函数调?br />
  login () q行验证

  commit () 当LgoninContext对象接受所有LoginModule对象传回的结果后调用该Ҏ。该ҎPrincipal对象和凭证赋lSubject对象?br />  
  abort () 当Q何一个LoginModule对象验证p|旉会调用该Ҏ。此时没有Q何Principal对象或凭证关联到Subject对象上?br />
  logout () 删除与Subject对象兌的Principal对象和凭证?br />
  在应用程序的代码中,E序员通常不会直接调用上面列出的方法,而是通过LigonContext间接调用q些Ҏ?br />
  验证QCallbackHandler和Callback

  CallbackHandler和Callback对象可以使LoginModule对象从系l和用户那里攉必要的验证信息,同时独立于实际的攉信息时发生的交互q程?br />  
  JAAS在javax.sevurity.auth.callback包中包含了七个Callback的实现类和两个CallbackHandler的实现类QChoiceCallback、ConfirmationCallback、LogcaleCallback、NameCallback、PasswordCallback、TextInputCallback、TextOutputCallback、DialogCallbackHandler和TextCallBackHandler。Callback接口只会在客L会被使用到。我在后面介绍如何~写你自qCallbackHandlercR?br />
  配置文g

  上面我已l提刎ͼJAAS的可扩展性来源于它能够进行动态配|,而配|信息通常是保存在文本。这些文本文件有很多个配|块构成Q我们通常把这些配|块UC甌QApplicationQ。每个申请对应了一个或多个特定的LoginModule对象?br />
  当你的代码构造一个LoginContext对象Ӟ你需要把配置文g中申L名称传递给它。LoginContext会Ҏ甌中的信息军_Ȁzd些LoginModule对象Q按照什么顺序激zM及用什么规则激zR?br />
  配置文g的结构如下所C?

Application {
ModuleClass Flag ModuleOptions;
ModuleClass Flag ModuleOptions;
...
};
Application {
ModuleClass Flag ModuleOptions;
...
};
...

  下面是一个名UCؓSample的申?br />
Sample {
com.sun.security.auth.module.NTLoginModule Rquired debug=true;
}

  上面q个单的甌指定了LoginContext对象应该使用NTLoginModuleq行验证。类的名U在ModuleClass中被指定。Flag控制当申请中包含了多个LoginModule时进行登录时的行为:Required、Sufficient、Requisite和Optional。最常用的是RequiredQ用它意味着对应的LoginModule对象必须被调用,q且必须需要通过所有的验证。由于Flag本n的复杂性,本文在这里不作深I?br />
  ModuleOption允许有多个参数。例如你可以讑֮调试参数为TrueQdebug=trueQ,q样诊断输出被送到System.out中?br />
  配置文g可以被Q意命名,q且可以被放在Q何位|。JAAS框架通过使用java.securty.auth.long.config属性来定配置文g的位|。例如当你的应用E序是JaasTestQ配|文件是当前目录下的jaas.configQ你需要在命o行中输入Q?br />
java -Djava.security.auth.login.config=jass.config JavaTest

  图二描述了配|文件中各元素之间的关系


图二 JAAS的配|文?

  通过命o行方式进行登录验?/font>

  Z说明JAAS到底能干什么,我在q里~写了两个例子。一个是单的由命令行输入调用的程序,另一个是服务器端的JSPE序。这两个E序都通过用户名/密码的方式进行登录,然后使用关系数据库对其进行验证?br />
  Z通过数据库进行验证,我们需要:

  1. 实现RdbmsLoginModulc,该类可以对输入的信息q行验证?br />
  2. ~辑一个配|文Ӟ告诉LoginContext如何使用RdbmsLoginModule?br />
  3. 实现ConsoleCallbackHandlerc,通过该类可以获取用户的输入?br />
  4. ~写应用E序代码?br />
  在RdbmsLoginModulcMQ我们必d现LgoinModule接口中的五个Ҏ。首先是initializeQ)ҎQ?br />
public void initialize(Subject subject, CallbackHandler
callbackHandler,

Map sharedState, Map options)
{
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;

url = (String)options.get("url");
driverClass = (String)options.get("driver");
debug = "true".equalsIgnoreCase((String)options.get("debug"));
}

  LoginContext在调用loginQ)Ҏ时会调用initializeQ)Ҏ。RdbmsLoginModule的第一个Q务就是在cM保存输入参数的引用。在验证成功后将向Subject对象中送入Principal对象和凭证?br />
  CallbackHandler对象会在loginQ)Ҏ中被使用到。sharedState可以使数据在不同的LoginModule对象之间׃nQ但是在q个例子中我们不会用它。最后是名ؓoptions的Map对象。options向LgoinModule对象传递在配置文gModuleOption域中定义的参数的倹{配|文件如下所C:

Example {
RdbmsLoginModule required
driver="org.gjt.mm.mysql.Driver"
url="jdbc:mysql://localhost/jaasdb?user=root"
debug="true";
};

  在配|文件中QRdbmsLoginModule包含了五个参敎ͼ其中driver、url、user和password是必需的,而debug是可选阐q。driver、url、user和password参数告诉我们如何获得JDBCq接。当然你q可以在ModuleOption域中加入数据库中的表或列的信息。用这些参数的目的是ؓ了能够对数据库进行操作。在LoginModulecȝinitializeQ)Ҏ中我们保存了每个参数的倹{?br />
  我们前面提到一个LoginContext对应的配|文件告诉它应该使用文g中的哪一个申诗这个信息是通过LgoinContext的构造函C递的。下面是初始化客L的代码,在代码中创徏了一个LoginContex对象q调用了loginQ)Ҏ?br />
ConsoleCallbackHandler cbh = new ConsoleCallbackHandler();
LoginContext lc = new LoginContext("Example", cbh);
lc.login();

  当LgoinContext.loginQ)Ҏ被调用时Q它调用所有加载了的LoginModule对象的loginQ)Ҏ。在我们的这个例子中是RdbmsLoginModule中的loginQ)Ҏ?br />
  RdbmsLoginModule中的loginQ)Ҏq行了下面的操作Q?br />
  1. 创徏两个Callback对象。这些对象从用户输入中获取用户名/密码。程序中使用了JAAS中的两个Callbackc?NameCallback和PasswordCallbackQ这两个cd含在javax.security.auth.callback包中Q?br />
  2. 通过callbacks作ؓ参数传递给CallbackHandler的handleQ)Ҏ来激zCallback?br />
  3. 通过Callback对象获得用户名/密码?br />
  4. 在rdbmsValidateQ)Ҏ中通过JDBC在数据库中验证获取的用户名/密码?br />
  下面是RdbmsLoginModule中的loginQ)Ҏ的代?br />
public boolean login() throws LoginException {
if (callbackHandler == null)
throw new LoginException("no handler");

NameCallback nameCb = new NameCallback("user: ");
PasswordCallback passCb = new PasswordCallback("password: ", true);
callbacks = new Callback[] { nameCb, passCb };
callbackHandler.handle(callbacks);

String username = nameCb.getName();
String password = new String(passCb.getPassword());
success = rdbmsValidate(username, password);

return(true);
}

  在ConsoleCallbackHandlercȝhandleQ)Ҏ中你可以看到Callback对象是如何同用户q行交互的:

  使用JSP和关pL据库q行d验证

  现在我们希望通过命o行调用的E序一直到Web应用E序中。由于Web应用E序与一般的应用E序的交互方式有区别不同Q我们将不能使用JAAS提供的标准Callback和CallbackHandlercR因为我们不能在WebE序中打开一个命令窗口让用户输入信息。也怽会想到我们也可以使用ZHTTP的验证,q样我们可以从浏览器弹出的用户名Q密码窗口中获得用户输入。但是这样做也有一些问题,它要求徏立v双向的HTTPq接Q在loginQ)Ҏ很难实现双向q接Q。因此在我们的例子中我们会用利用表单进行登录,从表单中获取用户输入的信息,然后通过RdbmsLoginModulec验证它?br />
  ׃我们没有在应用层同LoginModule直接打交道,而是通过LgoinContext来调用其中的Ҏ的,我们如何获得用户名和密码放入LoginModule对象中呢Q我们可以用其它的Ҏ来绕q这个问题,例如我们可以在创建LoginContext对象前先初始化一个Subject对象Q在Subject对象的凭证中保存用户名和密码。然后我们可以将该Subject对象传递给LoginContext的构造函数。这U方法虽然从技术上来说没有什么问题,但是它在应用E序层增加了很多与安全机制相关的代码。而且通常是在验证后向Subject送入凭证Q而不是之前?br />前面我们提到可以实现一个CallbackHandlerc,然后它的实例传递给LoginContext对象。在q里我们可以采用cM的方法来处理用户名和密码。我们实C一个新的类PassiveCallbackHandler。下面是在JSP中用该cȝ代码Q?br />
String user = request.getParameter("user");
String pass = request.getParameter("pass");
PassiveCallbackHandler cbh = new PassiveCallbackHandler(user, pass);
LoginContext lc = new LoginContext("Example", cbh);

  PassiveCallbackHandler中构造函数的参数包含了用户名和密码。因此它可以在Callbick对象中设定正的倹{下面是PassiveCallbackHandlercȝhandleQ)Ҏ的代码:

public void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException {
for (int i = 0; i Q?callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback nameCb = (NameCallback)callbacks[i];

nameCb.setName(user);
} else if(callbacks[i] instanceof PasswordCallback) {
PasswordCallback passCb = (PasswordCallback)callbacks[i];
passCb.setPassword(pass.toCharArray());
} else {
throw(new UnsupportedCallbackException(callbacks[i],
"Callback class not supported"));
}
}
}
}

  从上面的代码中可以发现实际上我们只是从ConsoleCallbackHandler中去除了那些提示用户输入的代码?br />
  在运行这个JSP例子的时候,我们需要设定系l属性,q样LgoinContext对象才知道如何查扑֐UCؓ"Example"的配|。我们用的是Resin服务器。在resin.conf中,我们增加了一个<caucho.comQ节点:

Qsystem-property
java.security.auth.login.config="/resin/conf/jaas.config"/Q?

  

  JAAS通过提供动态的、可扩展的模型来q行用户验证和控制权限,从而应用E序有更加健壮的安全机制。同时它q能够让你能够很L地创qd机制。JAAS可以同时在在客户端和服务器端应用E序上工作。虽然在服务器端的JAAS到目前还不是很稳定,但是随着技术的发展Q相信会很好地解册个问?/h4>



]]>
վ֩ģ壺 | ͩ| | ɽ| Ӣɳ| ǭ| | ǰ| Ӣ| ͷ| ɽ| ¡| | ½| | | | ذ| | | | ͬ| | | ̩| | | | ɽ| ױ| | | ˹| | | | ˹| ˮ| Դ| Т| |