`
zjfgf
  • 浏览: 12009 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论
收藏列表
标题 标签 来源
解析struts-plugin.xml struts2 http://blog.sina.com.cn/s/blog_62c732960100ulnf.html
解析struts-plugin.xml(2011-05-11 22:40:04)转载▼标签: struts-pluginit 分类: Struts  
前言:

Struts2提供了一种非常灵活的扩展方式,这种被称之为plugin的扩展方式与Eclipse或者Firefox的plugin具备相同的概念,通过独立于主体之外的程序,来扩展或者增强主体的自身功能。

       无疑,plugin的扩展方式是当前最为流行,也最为合理的一种扩展方式。通过plugin,你可以扩展、替换Struts2中的某些功能点,你也可以加入自己的实现类,从而使得Struts2具备新的功能。而plugin的方式也使得任何的功能扩展,都与Struts2的主体程序保持独立性。也使得任何人都可以按照自己的意愿去实现自己的plugin。

plugin的方方面面

依赖关系

Struts2 Reference 写道
Plugins are not loaded in any particular order. Plugins should not have dependencies on each other. A plugin may depend on classes provided by Struts Core, but it should not depend on classes loaded by another plugin.
这段摘自Struts2自身reference的描述已经比较清晰的告诉我们:Struts2的plugin为Struts2的主程序本身提供额外的功能支持。所以plugin本身是依赖于Struts2的主程序,也就是struts-core.jar。当然,针对某个特定功能的plugin可能还会有其他的library的依赖,不过这些依赖绝不应该影响到Struts2主程序本身。但是,不同的plugin之间,互相之间不应该形成依赖。

表现形式

Struts2 Reference 写道
A Struts 2 plugin is a single JAR that contains classes and configuration that extend, replace, or add to existing Struts framework functionality.
很显然,Struts2的plugin以JAR包的形式存在。

安装使用

Struts2 Reference 写道
A plugin can be installed by adding a JAR file to the application’s class path, in addition to the JAR files to fulfill whatever dependencies the plugin itself may have.
如果你要获得某个plugin的功能支持,你只需要将plugin的JAR包放到classpath下即可,同时别忘记了plugin自身所依赖的JAR包。

构成要素

Struts2 Reference 写道
To configure the plugin, the JAR should contain a struts-plugin.xml file, which follows the same format as an ordinary struts.xml file.
Since a plugin can contain the struts-plugin.xml file, it has the ability to:

* Define new packages with results, interceptors, and/or actions
* Override framework constants
* Introduce new extension point implementation classes

清楚的不要再清楚了。plugin的内部需要一个名为struts-plugin.xml的文件,这个文件与struts.xml的格式相同。在这个文件中,可以对这个plugin对Struts2的扩展点进行配置,而这些扩展点包含上面提到的三种不同的类型。

运行机制

Struts2 Reference 写道
The framework loads its default configuration first, then any plugin configuration files found in others JARs on the classpath, and finally the “bootstrap” struts.xml.
1. struts-default.xml (bundled in the Core JAR)
2. struts-plugin.xml (as many as can be found in other JARs)
3. struts.xml (provided by your application)

Since the struts.xml file is always loaded last, it can make use of any resources provided by the plugins bundled with the distribution, or any other plugins available to an application.

这段话也摘自Struts2自身的reference,它其实已经深刻的描述了Struts2内部是如何处理plugin的。从这里,我们也可以看到plugin之所以能够以JAR包的形式如此简单的对Struts2自身进行扩展,其核心原因在于Struts2会在系统启动的时候load所有自身以及plugin的配置文件,并且根据这些配置文件来决定Struts2运行期到底具备何种特性。

从这些话中,我们实际上也可以挖掘出来很多东西:

1. 位于struts-core.jar包内部的struts-default.xml永远会被先加载,从而保证Struts2具备许多默认的行为方式。

2. 所有的plugin互相之间是平等的,所以他们互相之间不应该存在依赖关系。同时,Struts2在系统启动时加载plugin的配置文件并没有特定的顺序。

3. Struts2最后会加载位于你自身程序classpath下的名为struts.xml的文件。所以,我见到论坛上有很多朋友问类似这样的问题,都可以迎刃而解了:

1)struts2默认的配置文件是不是只能叫”struts.xml”? ———— 是
2)struts2默认的配置文件是不是只能放在classpath根目录下? ———— 是

不过,struts2提供了完善的配置文件管理机制,所以,你可以在struts.xml中通过extends方式定义任何你需要覆盖其默认行为的配置方式,当然也可以通过import等方式将配置文件分开。

Struts2的扩展点

上面我们看到了通过plugin,可以对Struts2的许多默认行为进行扩展。那么Struts2的哪些行为可以进行扩展呢?在Struts2的内部,又是如何支持这些扩展的呢?

我按照我的理解,将Struts2的可扩展点分成了三类:

自定义Interceptor,Result等,对运行程序进行扩展

自定义的Interceptor和Result非常简单,只需要分别实现XWork的Interceptor接口和Result接口即可。然后,在你的struts.xml中,定义这些实现类,使得他们被Struts2默认加载。对于Interceptor,你可能需要特别留心它在整个Interceptor Stack中的位置。

这类扩展本身并不涉及到Struts2内部自身的运行机制,所改变的只是Action的运行方式和运行结构,所以,这类扩展属于应用级别的扩展。

覆盖Struts2的静态变量

Struts2内部定义了很多静态变量,这些静态变量定义了Struts2许多的默认行为。这些静态变量可以以多种不同的方式定义。下面的来自于Struts2 Reference的例子展示了在不同的的文件中定义这些静态变量。

这是在struts.xml中进行定义:

 ...
这是在struts.properties中进行定义:

struts.devMode = true
这是在web.xml中进行定义:

<web-app id="WebApp_9" version="2.4"     
    xmlns="http://java.sun.com/xml/ns/j2ee"     
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">    
   
    <filter>    
        <filter-name>struts</filter-name>    
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>    
        <init-param>    
            <param-name>struts.devMode</param-name>    
            <param-value>true</param-value>    
        </init-param>    
    </filter>    
    ...    
</web-app>   
所有的这些定义都会被Struts2接受并作为参数改变Struts2的默认行为。不过,在Struts2启动的时候,会按照一定的顺序按文件进行加载。

Struts2 Reference 写道
Constants can be declared in multiple files. By default, constants are searched for in the following order, allowing for subsequent files to override previous ones:
1. struts-default.xml
2. struts-plugin.xml
3. struts.xml
4. struts.properties
5. web.xml

Struts2的reference告诉我们,Struts2默认按照上面的顺序去加载所有的静态变量的值,并且允许后加载的定义覆盖之前的定义。

由于我们无法修改struts-default.xml和struts-plugin.xml中的定义、而struts.xml通常又被用于放置应用程序相关的一些配置、与此同时,为了保持web.xml的独立性,所以,我们在这里可以总结出一条最佳实践来:把你自定义需要加载的静态变量定义或者需要覆盖struts默认的静态变量的定义尽量放在struts.properties中。这样就可以使得你的配置变得更加清晰。

覆盖Struts2或者XWork自身的接口实现,替换默认行为机制

无论是Struts2还是XWork,都是面向接口编程的框架(请读者在读完这段后,自行理解面向接口的好处)。而这些定义的接口,我们可以编写自己的实现类实现这些接口,从而替换Struts的默认行为机制。

在Struts2的Reference中,也指出了一些Struts2的接口扩展点,请参考下面的链接:http://struts.apache.org/2.0.14/docs/plugins.html

在这里,我想提出的是,上面的链接其实只是列出了Struts2的一些可扩展接口,除此之外,还有XWork的很多接口也具备可扩展性。所以,我们还可以参考XWork的相关配置文件,寻找更多的扩展点,这些就留给读者自行去研究了。

在之后的章节,也会根据具体的实例进行讲解,如何扩展这些接口,从而替换Struts2的默认行为。

Struts2常见的plugin

Struts2 Reference 写道
Many popular but optional features of the framework are distributed as plugins. An application can retain all the plugins provided with the distribution, or just include the ones it uses. Plugins can be used to organize application code or to distribute code to third-parties.
按照Struts2的reference中的指示,其实许多常见的Struts2的扩展功能都是通过plugin来实现的,而这些常见的plugin已经由很多先辈贡献出来,并且作为Struts2的分发包的一部分共同发布,所以,其实你可以在Struts2的发布包中找到这些plugin以及他们的源码进行学习。

当然,你也可以自己实现自己的plugin。有很多的Struts2的plugin就不是hosting在apache上的,但是在apache的plugin的列表中,我们可以找到他们:

http://cwiki.apache.org/S2PLUGINS/home.html

面对这些纷繁复杂的plugin,许多程序员可能会不知所措。所以我在这里对plugin进行了简单的分类,并且列出一些典型的plugin,提供给大家进行学习:

1. 框架整合类

这类的plugin提供了Struts2与其他许多开源框架的整合方式。所以,那些在论坛上提问Struts2与Spring怎么整合、Struts2与DWR怎么整合的朋友,不妨先看看这些plugin的说明,试着跑一下这些plugin的例子,或许你会很有收获。

Spring Plugin: http://cwiki.apache.org/S2PLUGINS/spring-plugin.html

Guice Plugin:http://cwiki.apache.org/S2PLUGINS/guice-plugin.html

JRuby Plugin: http://cwiki.apache.org/S2PLUGINS/jruby-plugin.html

2. 简化配置类

这类的plugin的主旨是为了简化Struts2原有的配置结构,这些简化可能包含使用CoC的方式省略XML配置,使得Struts2支持Restful等等。不过这类的plugin中许多都会涉及到Struts2比较底层的内部实现,所以使用的时候请大家慎重选择。

Codebehind Plugin:http://cwiki.apache.org/S2PLUGINS/codebehind-plugin.html

SmartURLs plugin:http://cwiki.apache.org/S2PLUGINS/smarturls-plugin.html

Convention Plugin:http://cwiki.apache.org/S2PLUGINS/convention-plugin.html

3. 页面装饰类

这类plugin没什么好说的,是为了整合类似Tiles或者Sitemesh的框架,提供一个良好的页面结构化的环境整合。

Sitemesh Plugin:http://cwiki.apache.org/S2PLUGINS/sitemesh-plugin.html

Tiles Plugin:http://cwiki.apache.org/S2PLUGINS/tiles-plugin.html

4. 功能扩展类

这类的plugin最为丰富,包含了各种各样的额外功能扩展,例如整合JFreechart做图表整合输出、整合Open Flash Chart做Flash样式的报表输出、整合JasperReport做PDF输出等等。

JFreeChart Plugin:http://cwiki.apache.org/S2PLUGINS/jfreechart-plugin.html

JasperReports Plugin:http://cwiki.apache.org/S2PLUGINS/jasperreports-plugin.html

Connext Graph Plugin:http://cwiki.apache.org/S2PLUGINS/connext-graph-plugin.html

在之后的章节中,我会挑选其中几个觉有代表性的plugin做详细的分析和说明。
Eclipse 发布Web Project到Tomcat的三种方法 tomcat http://www.cnblogs.com/shihujiang/archive/2012/06/06/2538569.html
Eclipse 发布Web Project到Tomcat的三种方法 
以前开发一直使用的是MyEclipse,通过几下鼠标操作,"web工程被Myeclipse自动部署到了webapps下面",很是方便.

Myeclipse是如何帮助我们把工程自动部署到webapps呢?通过这个疑问才发现,不是Myeclipse把工程部署到webapps下面,而是tomcat自己,试想一下,Myeclipse怎么会知道在tomcat下有个webapps目录呢.只有tomcat自己才知道自己有这么目录,所以说"web工程被Myeclipse自动部署到了webapps下面"是不确切的,web 工程被部署到哪个目录是tomcat干的活.

既然知道了是tomcat干的,那么它是如何做的呢?是tomcat的配置文件server.xml告诉他的,

<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">

appBase这个参数,让tomcat把web工程部署到应该的地方.

 

除了上述方法外还有两种部署web程序的方法,上述方法姑且称为第一种方法,这种方法在myeclipse中使用个人觉得比较顺手(可能是习惯的原因吧),那咱们再来说一下后两种方法.

第二种方法:同样是通过配置文件来告诉tomcat,在server.xml中的host节点中 增加一个web工程实力的配置,如下:

<Context   path="/test"   reloadable="true"   docBase="D:\workspace\test"   workDir="D:\jspclass"></Context>

初步解释一下各个参数含义:

配置采用Context作为关键字;

path关键字指明的是项目的名称;

reloadable关键字指明的是该站点可以自动加载;

docBase关键字指明的是项目根目录所在的位置;

workDir关键字指明的是JSP编译时候输出的所在位置,自行制定.

 

第三种方法:在tomcat的conf\Catalina\localhost 目录下,新建一个配置文件,比如:text.xml(文件名要与你要访问的应用同名)

文件内容同第二种方法,参数含义一样,此处不在啰嗦啦.

上述三种部署方式,个人认为第一种比较好,方便,傻瓜;第二种每当增加新的应用时要修改配置文件,不爽;第三种相对比较灵活.根据个人喜好选择不同的部署方式吧.


×××××××××
如果将工程打包到webapps下面的话,http://localhost:8080/surveym(××××目录名××××)/admin.jsp

xFire1.2.6+webservices接口wsdl地址编写客户端获取对象集合 xfire http://blog.163.com/wp_2002wp/blog/static/314722522010379276548/
xFire1.2.6+webservices接口wsdl地址编写客户端获取对象集合  2010-04-07 09:27:06|  分类: 工作 |  标签: |字号大中小 订阅 .

import org.codehaus.xfire.XFire;
import org.codehaus.xfire.XFireFactory;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
import org.codehaus.xfire.service.Service;

public class Client {
 public List<<(返回对象)> > call() {
  List<(返回对象)> list = new ArrayList<(返回对象)> ();
  Service service = new ObjectServiceFactory().create(webservices接口类.class);
  XFire xfire = XFireFactory.newInstance().getXFire();
  XFireProxyFactory factory = new XFireProxyFactory(xfire);
  String serviceUrl = "url(?wsdl)";
  (webservices接口类) client = null;
  try {
   client = (webservices接口类) factory.create(service, serviceUrl);
  } catch (MalformedURLException e) {
  }
  try {
   list = client.getList();
   System.out.println(ui.getUserid());
  } catch (Exception e) {
  }
  return list;
 }

客户端返回对象的包名与服务器端相同(webservices接口类.aegis.xml中componentType定义相同)

否则的到LIST中对象属性为null
JAVA使用XFire开发Web Service客户端几种调用方式 xfire http://my.oschina.net/lovedreamland/blog/52977
JAVA使用XFire开发Web Service客户端几种调用方式
0人收藏此文章, 我要收藏 发表于3个月前(2012-04-10 14:41) , 已有307次阅读 共0个评论
一、服务提供者告诉你interface,你可以使用如下三种方式来开发: 
YourService即是服务提供者告诉给你的一个interface(当然,也可以根据WSDL的定义,自己定义一个同样的interface)。 
1、简单的方式

1
Service serviceModel = new ObjectServiceFactory().create(YourService.class);
2
YourService service = (YourService)new XFireProxyFactory().create(serviceModel, "http://your/remote/url");
2、JSR 181注释的方式

1
Service serviceModel = new AnnotationServiceFactory().create(YourService.class);
2
YourService client = (YourService)new XFireProxyFactory().create(serviceModel, "http://your/remote/url");
3、混合方式

1
Service serviceModel = new AnnotationServiceFactory(new Jsr181WebAnnotations(), XFireFactory.newInstance().getXFire().getTransportManager(), new AegisBindingProvider(new JaxbTypeRegistry())).create(YourService.class);

二、通过WSDL创建一个动态的客户端,如下:

01
import java.net.MalformedURLException;
02
import java.net.URL;
03
 
04
import org.codehaus.xfire.client.Client;
05
 
06
public class DynamicClientTest {
07
    public static void main(String[] args) throws MalformedURLException, Exception {
08
        Client client = new Client(new URL("http://localhost:8080/xfiretest/services/TestService?wsdl"));
09
        Object[] results = client.invoke("sayHello", new Object[] { "Firends" });
10
        System.out.println(results[0]);
11
    }
12
}
 

三,使用ANT工具或命令行通过WSDL生成一个客户端:
1、使用ANT生成客户端,ANT脚本如下:

01
<?xml version="1.0"?>
02
<project name="wsgen" default="wsgen" basedir=".">
03
    <path id="classpathId">
04
        <fileset dir="./WebRoot/WEB-INF/lib">
05
            <include name="*.jar" />
06
        </fileset>
07
    </path>
08
    <taskdef classpathref="classpathId" name="wsgen" classname="org.codehaus.xfire.gen.WsGenTask">
09
    </taskdef>
10
    <target name="wsgen" description="generate client">
11
        <wsgen outputDirectory="./src/" wsdl="abc.wsdl" binding="xmlbeans" package="com.abc.p" overwrite="true" />
12
    </target>
13
</project>
请注意,脚本中有一个参数binding,可以指定如下两种不同的方式:
(1)jaxb(Java Architecture for XML Binding,https://jaxb.dev.java.net/):使用此种方式时,会自动生成更多的Request和Resopnse类。
(2)xmlbeans

调用方式如下:

1
AbcServiceClient client = new AbcServiceClient();
2
String url = "http://localhost:8080/xfireTest/services/TestService";
3
String result = client.getAbcPort(url).sayHello("Robin");
2、使用命令生成客户端的命令如下:

1
gpath=xfire-all-1.2-SNAPSHOT.jar:ant-1.6.5.jar:jaxb-api-2.0EA3.jar:stax-api-1.0.1.jar:jdom-1.0.jar:jaxb-impl-2.0EA3.jar\
2
:jaxb-xjc-2.0-ea3.jar:wstx-asl-2.9.3.jar:commons-logging-1.0.4.jar:activation-1.1.jar:wsdl4j-1.5.2.jar:XmlSchema-1.0.3.jar:xfire-jsr181-api-1.0-M1.jar;
3
 
4
java -cp $gpath org.codehaus.xfire.gen.WsGen -wsdl http://localhost:8080/xfire/services/Bookservice?wsdl -o . -p pl.tomeks.client -overwrite true
其结果与ANT生成的一样。
StringUtils java
org.apache.commons.lang.StringUtils类 
(转)StringUtils 方法的操作对象是 java.lang.String 类型的对象,是 JDK 提供的 String 类型操作方法的补充,并且是 null 安全的(即如果输入参数 String 为 null 则不会抛出 NullPointerException ,而是做了相应处理,例如,如果输入为 null 则返回也是 null 等,具体可以查看源代码)。 
除了构造器,StringUtils 中一共有130多个方法,并且都是 static 的,所以我们可以这样调用 StringUtils.xxx() 
下面分别对一些常用方法做简要介绍: 
1. public static boolean isEmpty(String str) 
判断某字符串是否为空,为空的标准是 str==null 或 str.length()==0 
下面是 StringUtils 判断是否为空的示例: 

StringUtils.isEmpty(null) = true 
StringUtils.isEmpty("") = true 
StringUtils.isEmpty(" ") = false //注意在 StringUtils 中空格作非空处理 
StringUtils.isEmpty(" ") = false 
StringUtils.isEmpty("bob") = false 
StringUtils.isEmpty(" bob ") = false 

2. public static boolean isNotEmpty(String str) 
判断某字符串是否非空,等于 !isEmpty(String str) 
下面是示例: 

StringUtils.isNotEmpty(null) = false 
StringUtils.isNotEmpty("") = false 
StringUtils.isNotEmpty(" ") = true 
StringUtils.isNotEmpty(" ") = true 
StringUtils.isNotEmpty("bob") = true 
StringUtils.isNotEmpty(" bob ") = true 
3. public static boolean isBlank(String str) 
判断某字符串是否为空或长度为0或由空白符(whitespace) 构成 
下面是示例: 
StringUtils.isBlank(null) = true 
StringUtils.isBlank("") = true 
StringUtils.isBlank(" ") = true 
StringUtils.isBlank(" ") = true 
StringUtils.isBlank("\t \n \f \r") = true //对于制表符、换行符、换页符和回车符 

StringUtils.isBlank() //均识为空白符 
StringUtils.isBlank("\b") = false //"\b"为单词边界符 
StringUtils.isBlank("bob") = false 
StringUtils.isBlank(" bob ") = false 
4. public static boolean isNotBlank(String str) 
判断某字符串是否不为空且长度不为0且不由空白符(whitespace) 构成,等于 !isBlank(String str) 
下面是示例: 

StringUtils.isNotBlank(null) = false 
StringUtils.isNotBlank("") = false 
StringUtils.isNotBlank(" ") = false 
StringUtils.isNotBlank(" ") = false 
StringUtils.isNotBlank("\t \n \f \r") = false 
StringUtils.isNotBlank("\b") = true 
StringUtils.isNotBlank("bob") = true 
StringUtils.isNotBlank(" bob ") = true 
5. public static String trim(String str) 
去掉字符串两端的控制符(control characters, char <= 32) , 如果输入为 null 则返回null 
下面是示例: 
StringUtils.trim(null) = null 
StringUtils.trim("") = "" 
StringUtils.trim(" ") = "" 
StringUtils.trim(" \b \t \n \f \r ") = "" 
StringUtils.trim(" \n\tss \b") = "ss" 
StringUtils.trim(" d d dd ") = "d d dd" 
StringUtils.trim("dd ") = "dd" 
StringUtils.trim(" dd ") = "dd" 
6. public static String trimToNull(String str) 
去掉字符串两端的控制符(control characters, char <= 32) ,如果变为 null 或"",则返回 null 
下面是示例: 
StringUtils.trimToNull(null) = null 
StringUtils.trimToNull("") = null 
StringUtils.trimToNull(" ") = null 
StringUtils.trimToNull(" \b \t \n \f \r ") = null 
StringUtils.trimToNull(" \n\tss \b") = "ss" 
StringUtils.trimToNull(" d d dd ") = "d d dd" 
StringUtils.trimToNull("dd ") = "dd" 
StringUtils.trimToNull(" dd ") = "dd" 
7. public static String trimToEmpty(String str) 
去掉字符串两端的控制符(control characters, char <= 32) ,如果变为 null 或 "" ,则返回 "" 
下面是示例: 
StringUtils.trimToEmpty(null) = "" 
StringUtils.trimToEmpty("") = "" 
StringUtils.trimToEmpty(" ") = "" 
StringUtils.trimToEmpty(" \b \t \n \f \r ") = "" 
StringUtils.trimToEmpty(" \n\tss \b") = "ss" 
StringUtils.trimToEmpty(" d d dd ") = "d d dd" 
StringUtils.trimToEmpty("dd ") = "dd" 
StringUtils.trimToEmpty(" dd ") = "dd" 
8. public static String strip(String str) 
去掉字符串两端的空白符(whitespace) ,如果输入为 null 则返回 null 
下面是示例(注意和 trim() 的区别): 
StringUtils.strip(null) = null 
StringUtils.strip("") = "" 
StringUtils.strip(" ") = "" 
StringUtils.strip(" \b \t \n \f \r ") = "\b" 
StringUtils.strip(" \n\tss \b") = "ss \b" 
StringUtils.strip(" d d dd ") = "d d dd" 
StringUtils.strip("dd ") = "dd" 
StringUtils.strip(" dd ") = "dd" 
9. public static String stripToNull(String str) 
去掉字符串两端的空白符(whitespace) ,如果变为 null 或"",则返回 null 
下面是示例(注意和 trimToNull() 的区别): 
StringUtils.stripToNull(null) = null 
StringUtils.stripToNull("") = null 
StringUtils.stripToNull(" ") = null 
StringUtils.stripToNull(" \b \t \n \f \r ") = "\b" 
StringUtils.stripToNull(" \n\tss \b") = "ss \b" 
StringUtils.stripToNull(" d d dd ") = "d d dd" 
StringUtils.stripToNull("dd ") = "dd" 
StringUtils.stripToNull(" dd ") = "dd" 
10. public static String stripToEmpty(String str) 
去掉字符串两端的空白符(whitespace) ,如果变为 null 或"" ,则返回"" 
下面是示例(注意和 trimToEmpty() 的区别): 
StringUtils.stripToNull(null) = "" 
StringUtils.stripToNull("") = "" 
StringUtils.stripToNull(" ") = "" 
StringUtils.stripToNull(" \b \t \n \f \r ") = "\b" 
StringUtils.stripToNull(" \n\tss \b") = "ss \b" 
StringUtils.stripToNull(" d d dd ") = "d d dd" 
StringUtils.stripToNull("dd ") = "dd" 
StringUtils.stripToNull(" dd ") = "dd" 

以下方法只介绍其功能,不再举例: 
11. public static String strip(String str, String stripChars) 
去掉 str 两端的在 stripChars 中的字符。 
如果 str 为 null 或等于"" ,则返回它本身; 
如果 stripChars 为 null 或"" ,则返回 strip(String str) 。 
12. public static String stripStart(String str, String stripChars) 
和11相似,去掉 str 前端的在 stripChars 中的字符。 
13. public static String stripEnd(String str, String stripChars) 
和11相似,去掉 str 末端的在 stripChars 中的字符。 
14. public static String[] stripAll(String[] strs) 
对字符串数组中的每个字符串进行 strip(String str) ,然后返回。 
如果 strs 为 null 或 strs 长度为0,则返回 strs 本身 
15. public static String[] stripAll(String[] strs, String stripChars) 
对字符串数组中的每个字符串进行 strip(String str, String stripChars) ,然后返回。 
如果 strs 为 null 或 strs 长度为0,则返回 strs 本身 
16. public static boolean equals(String str1, String str2) 
比较两个字符串是否相等,如果两个均为空则也认为相等。 
17. public static boolean equalsIgnoreCase(String str1, String str2) 
比较两个字符串是否相等,不区分大小写,如果两个均为空则也认为相等。 
18. public static int indexOf(String str, char searchChar) 
返回字符 searchChar 在字符串 str 中第一次出现的位置。 
如果 searchChar 没有在 str 中出现则返回-1, 
如果 str 为 null 或 "" ,则也返回-1 
19. public static int indexOf(String str, char searchChar, int startPos) 
返回字符 searchChar 从 startPos 开始在字符串 str 中第一次出现的位置。 
如果从 startPos 开始 searchChar 没有在 str 中出现则返回-1, 
如果 str 为 null 或 "" ,则也返回-1 
20. public static int indexOf(String str, String searchStr) 
返回字符串 searchStr 在字符串 str 中第一次出现的位置。 
如果 str 为 null 或 searchStr 为 null 则返回-1, 
如果 searchStr 为 "" ,且 str 为不为 null ,则返回0, 
如果 searchStr 不在 str 中,则返回-1 
21. public static int ordinalIndexOf(String str, String searchStr, int ordinal) 
返回字符串 searchStr 在字符串 str 中第 ordinal 次出现的位置。 
如果 str=null 或 searchStr=null 或 ordinal<=0 则返回-1 
举例(*代表任意字符串): 
StringUtils.ordinalIndexOf(null, *, *) = -1 
StringUtils.ordinalIndexOf(*, null, *) = -1 
StringUtils.ordinalIndexOf("", "", *) = 0 
StringUtils.ordinalIndexOf("aabaabaa", "a", 1) = 0 
StringUtils.ordinalIndexOf("aabaabaa", "a", 2) = 1 
StringUtils.ordinalIndexOf("aabaabaa", "b", 1) = 2 
StringUtils.ordinalIndexOf("aabaabaa", "b", 2) = 5 
StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1 
StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4 
StringUtils.ordinalIndexOf("aabaabaa", "bc", 1) = -1 
StringUtils.ordinalIndexOf("aabaabaa", "", 1) = 0 
StringUtils.ordinalIndexOf("aabaabaa", "", 2) = 0 
22. public static int indexOf(String str, String searchStr, int startPos) 
返回字符串 searchStr 从 startPos 开始在字符串 str 中第一次出现的位置。 
举例(*代表任意字符串): 
StringUtils.indexOf(null, *, *) = -1 
StringUtils.indexOf(*, null, *) = -1 
StringUtils.indexOf("", "", 0) = 0 
StringUtils.indexOf("aabaabaa", "a", 0) = 0 
StringUtils.indexOf("aabaabaa", "b", 0) = 2 
StringUtils.indexOf("aabaabaa", "ab", 0) = 1 
StringUtils.indexOf("aabaabaa", "b", 3) = 5 
StringUtils.indexOf("aabaabaa", "b", 9) = -1 
StringUtils.indexOf("aabaabaa", "b", -1) = 2 
StringUtils.indexOf("aabaabaa", "", 2) = 2 
StringUtils.indexOf("abc", "", 9) = 3 
23. public static int lastIndexOf(String str, char searchChar) 
基本原理同18 
24. public static int lastIndexOf(String str, char searchChar, int startPos) 
基本原理同19 
25. public static int lastIndexOf(String str, String searchStr) 
基本原理同20 
26. public static int lastIndexOf(String str, String searchStr, int startPos) 
基本原理同22 
另附: 
String 的 split(String regex) 方法的用法 
如果我们需要把某个字符串拆分为字符串数组,则通常用 split(String regex) 来实现。 
例如: 
Java代码 复制代码 

1. String str = "aa,bb,cc,dd"; 
2. String[] strArray = str.split(","); 
3. System.out.println(strArray.length); 
4. for (int i = 0; i < strArray.length; i++) { 
5. System.out.println(strArray[i]); 
6. } 

String str = "aa,bb,cc,dd"; String[] strArray = str.split(","); System.out.println(strArray.length); for (int i = 0; i < strArray.length; i++) { System.out.println(strArray[i]); } 



结果为: 
4 
aa 
bb 
cc 
dd 
如果, 
String str = "aa.bb.cc.dd"; 
String[] strArray = str.split("."); 
则结果为:0 
为什么结果不是我们所想的呢,原因是参数 String regex 是正则表达式 (regular expression) 而不是普通字符串,而 "." 在正则表达式中有特殊含义,表示匹配所有单个字符。如果要那样拆分,我们必须给 "." 进行转义,String[] strArray = str.split(".") 修改为 String[] strArray = str.split("\\.") 即可。 



另外收藏几个实用的方法:

org.apache.commons.lang.StringUtils中提供许多有用的字符串操作方法,了解这些方法,我们可以避免许多不必要的重复工作。下面介绍其中比较有用的几个方法:

检查空字符串:

01.StringUtils.isBlank(String str);  
02.StringUtils.isNotBlank(String str);  




缩写字符串: 

String test  =   " This is a test of the abbreviation. "   
02.System.out.println( StringUtils.abbreviate( test,  10  ) );  
03.  
04.[Console输出]  
05.This is   






查找嵌套字符串: 01. String htmlContent  =   " <html>/n "   +   
02.                      "   <head>/n "   +   
03.                      "     <title>Test Page</title>/n "   +   
04.                      "   </head>/n "   +   
05.                      "   <body>/n "   +   
06.                      "     <p>This is a TEST!</p>/n "   +   
07.                      "   </body>/n "   +   
08.                      " </html> " ;  
09.  
10. //  Extract the title from this XHTML content     
11. String title  =  StringUtils.substringBetween(htmlContent,  " <title> " ,  " </title> " );  
12.System.out.println(  " Title:  "   +  title );  
13.  
14.[Console输出]  
15.Title: Test Page   



验证字符串:
01. String test1  =   " ORANGE " ;  
02.  
03.String test2  =   " ICE9 " ;  
04.  
05.String test3  =   " ICE CREAM " ;  
06.  
07.String test4  =   " 820B Judson Avenue " ;  
08.  
09.//判断字母   
10. boolean  t1val  =  StringUtils.isAlpha( test1 );  //  returns true    
11. //判断是不是字母数字   
12. boolean  t2val  =  StringUtils.isAlphanumeric( test2 );  //  returns true    
13. //字母空格   
14. boolean  t3val  =  StringUtils.isAlphaSpace( test3 );  //  returns true    
15. //字母数字空格   
16. boolean  t4val  =    
17.  
18.    StringUtils.isAlphanumericSpace( test4 );  //  returns true   


计算字符串出现频率: 
01. File manuscriptFile  =   new  File( " manuscript.txt " );  
02.  
03.Reader reader  =   new  FileReader( manuscriptFile );  
04.  
05.StringWriter stringWriter  =   new  StringWriter( );  
06.  
07. while ( reader.ready( ) )   { writer.write( reader.read( ) ); }   
08.   
09.String manuscript  =  stringWriter.toString( );  
10.  
11. //  Convert string to lowercase    
12.   
13.manuscript  =  StringUtils.lowerCase(manuscript);  
14.  
15. //  count the occurrences of "futility"    
16.   
17. int  numFutility  =  StringUtils.countMatches( manuscript,  " futility "  );   



比较不同字符串: 01. int  dist  =  StringUtils.getLevenshteinDistance(  " Word " ,  " World "  );  
02.  
03.String diff  =  StringUtils.difference(  " Word " ,  " World "  );  
04.  
05. int  index  =  StringUtils.indexOfDifference(  " Word " ,  " World "  );  
06.  
07.System.out.println(  " Edit Distance:  "   +  dist );  
08.  
09.System.out.println(  " Difference:  "   +  diff );  
10.  
11.System.out.println(  " Diff Index:  "   +  index );  
12.  
13.[Console输出]  
14.Edit Distance:  2   
15.   
16.Difference: ld  
17.  
18.Diff Index:  3   
jsonObject测试代码 java
http://json-lib.sourceforge.net/xref-test/net/sf/json/TestJSONObject.html
应用JUnit实施单元测试 junit http://bbs.chinaunix.net/thread-215124-1-1.html
应用JUnit实施单元测试(原创作者:eric )


(献给想保证java coding软件质量的朋友们)
--------------------------------------------------------------------------------

测试的概念

长期以来,我所接触的软件开发人员很少有人能在开发的过程中进行测试工作。大部分的项目都是在最终验收的时候编写测试文档。有些项目甚至没有测试文档。现在情况有了改变。我们一直提倡UML、RUP、软件工程、CMM,目的只有一个,提高软件编写的质量。举一个极端的例子:如果你是一个超级程序设计师,一个传奇般的人物。(你可以一边喝咖啡,一边听着音乐,同时编写这操作系统中关于进程调度的模块,而且两天时间内就完成了!)我真得承认,有这样的人。(那个编写UNIX中的vi编辑器的家伙就是这种人。)然而非常遗憾的是这些神仙们并没有留下如何修成正果的README。所以我们这些凡人--在同一时间只能将注意力集中到若干点(据科学统计,我并不太相信,一般的人只能同时考虑最多7个左右的问题,高手可以达到12个左右),而不能既纵览全局又了解细节--只能期望于其他的方式来保证我们所编写的软件质量。

为了说明我们这些凡人是如何的笨。有一个聪明人提出了软件熵(software entropy)的概念:一个程序从设计很好的状态开始,随着新的功能不断地加入,程序逐渐地失去了原有的结构,最终变成了一团乱麻。你可能会争辩,在这个例子中,设计很好的状态实际上并不好,如果好的话,就不会发生你所说的情况。是的,看来你变聪明了,可惜你还应该注意到两个问题:1)我们不能指望在恐龙纪元(大概是十年前)设计的结构到了现在也能适用吧。2)拥有签字权的客户代表可不理会加入一个新功能是否会对软件的结构有什么影响,即便有影响也是程序设计人员需要考虑的问题。如果你拒绝加入这个你认为致命的新功能,那么你很可能就失去了你的住房贷款和面包(对中国工程师来说也许是米饭或面条,要看你是南方人还是北方人)。

另外,需要说明的是我看过的一些讲解测试的书都没有我写的这么有人情味(不好意思...)。我希望看到这片文章的兄弟姐妹能很容易地接受测试的概念,并付诸实施。所以有些地方写的有些夸张,欢迎对测试有深入理解的兄弟姐妹能体察民情,并不吝赐教。

好了,我们现在言归正传。要测试,就要明白测试的目的。我认为测试的目的很简单也极具吸引力:写出高质量的软件并解决软件熵这一问题。想象一下,如果你写的软件和Richard Stallman(GNU、FSF的头儿)写的一样有水准的话,是不是很有成就感?如果你一致保持这种高水准,我保证你的薪水也会有所变动。

测试也分类,白箱测试、黑箱测试、单元测试、集成测试、功能测试...。我们先不管有多少分类,如何分类。先看那些对我们有用的分类,关于其他的测试,有兴趣的人可参阅其他资料。白箱测试是指在知道被测试的软件如何(How)完成功能和完成什么样(What)的功能的条件下所作的测试。一般是由开发人员完成。因为开发人员最了解自己编写的软件。本文也是以白箱测试为主。黑箱测试则是指在知道被测试的软件完成什么样(What)的功能的条件下所作的测试。一般是由测试人员完成。黑箱测试不是我们的重点。本文主要集中在单元测试上,单元测试是一种白箱测试。目的是验证一个或若干个类是否按所设计的那样正常工作。集成测试则是验证所有的类是否能互相配合,协同完成特定的任务,目前我们暂不关心它。下面我所提到的测试,除非特别说明,一般都是指单元测试。

需要强调的是:测试是一个持续的过程。也就是说测试贯穿与开发的整个过程中,单元测试尤其适合于迭代增量式(iterative and incremental)的开发过程。Martin Fowler(有点儿像引用孔夫子的话)甚至认为:“在你不知道如何测试代码之前,就不应该编写程序。而一旦你完成了程序,测试代码也应该完成。除非测试成功,你不能认为你编写出了可以工作的程序。”我并不指望所有的开发人员都能有如此高的觉悟,这种层次也不是一蹴而就的。但我们一旦了解测试的目的和好处,自然会坚持在开发过程中引入测试。

因为我们是测试新手,我们也不理会那些复杂的测试原理,先说一说最简单的:测试就是比较预期的结果是否与实际执行的结果一致。如果一致则通过,否则失败。看下面的例子:


        //将要被测试的类
        public class Car {
            public int getWheels() {
                        return 4;
                }
        }
        
        //执行测试的类
        public class testCar {
                public static void main(String[] args) {
                        testCar myTest = new testCar();
                        myTest.testGetWheels();
                }
                
                public testGetWheels() {
                        int expectedWheels = 4;
                        Car myCar = Car();
                        if (expectedWheels==myCar.getWheels()) 
                                System.out.println("test [Car]: getWheels works perfected!";
                        else
                                System.out.println("test [Car]: getWheels DOESN'T work!";        
                }
        }
        
如果你立即动手写了上面的代码,你会发现两个问题,第一,如果你要执行测试的类testCar,你必须必须手工敲入如下命令:

        [Windows] d:>;java testCar 
           [Unix] % java testCar
        
即便测试如例示的那样简单,你也有可能不愿在每次测试的时候都敲入上面的命令,而希望在某个集成环境中(IDE)点击一下鼠标就能执行测试。后面的章节会介绍到这些问题。第二,如果没有一定的规范,测试类的编写将会成为另一个需要定义的标准。没有人希望查看别人是如何设计测试类的。如果每个人都有不同的设计测试类的方法,光维护被测试的类就够烦了,谁还顾得上维护测试类?另外有一点我不想提,但是这个问题太明显了,测试类的代码多于被测试的类!这是否意味这双倍的工作?不!1)不论被测试类-Car 的 getWheels 方法如何复杂,测试类-testCar 的testGetWheels 方法只会保持一样的代码量。2)提高软件的质量并解决软件熵这一问题并不是没有代价的。testCar就是代价。

我们目前所能做的就是尽量降低所付出的代价:我们编写的测试代码要能被维护人员容易的读取,我们编写测试代码要有一定的规范。最好IDE工具可以支持这些规范。 好了,你所需要的就是JUnit。一个Open Source的项目。用其主页上的话来说就是:“JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。用于Java开发人员编写单元测试之用。”所谓框架就是 Erich Gamma 和 Kent Beck 定下了一些条条框框,你编写的测试代码必须遵循这个条条框框:继承某个类,实现某个接口。其实也就是我们前面所说的规范。好在JUnit目前得到了大多数软件工程师的认可。遵循JUnit我们会得到很多的支持。回归测试就是你不断地对所编写的代码进行测试:编写一些,测试一些,调试一些,然后循环这一过程,你会不断地重复先前的测试,哪怕你正编写其他的类,由于软件熵的存在,你可能在编写第五个类的时候发现,第五个类的某个操作会导致第二个类的测试失败。通过回归测试我们抓住了这条大Bug。


回归测试框架-JUnit
通过前面的介绍,我们对JUnit有了一个大概的轮廓。知道了它是干什么的。现在让我们动手改写上面的测试类testCar使其符合Junit的规范--能在JUnit中运行。

        //执行测试的类(JUnit版)
        import junit.framework.*;
        
        public class testCar extends TestCase {
                
                protected int expectedWheels;
                protected Car myCar;
                
                public testCar(String name) {
                        super(name);
                }
                
                protected void setUp() {
                        expectedWheels = 4;
                        myCar = new Car();
                }
                
                public static Test suite() {
                        /*
                         * the type safe way
                         *
                        TestSuite suite= new TestSuite();
                        suite.addTest(
                                new testCar("Car.getWheels" {
                                         protected void runTest() { testGetWheels(); }
                                }
                        );
                        return suite;
                        */
        
                        /*
                         * the dynamic way
                         */                
                        return new TestSuite(testCar.class);
                }
                
                public void testGetWheels() {
                        assertEquals(expectedWheels, myCar.getWheels());
                }
        }
        
改版后的testCar已经面目全非。先让我们了解这些改动都是什么含义,再看如何执行这个测试。

1>;import语句,引入JUnit的类。(没问题吧)

2>;继承 TestCase 。可以暂时将一个TestCase看作是对某个类进行测试的方法的集合。详细介绍请参看JUnit资料

3>;setUp()设定了进行初始化的任务。我们以后会看到setUp会有特别的用处。

4>;testGetWheeels()对预期的值和myCar.getWheels()返回的值进行比较,并打印比较的结果。assertEquals是junit.framework.Assert中所定义的方法,junit.framework.TestCase继承了junit.framework.Assert。

5>;suite()是一个很特殊的静态方法。JUnit的TestRunner会调用suite方法来确定有多少个测试可以执行。上面的例子显示了两种方法:静态的方法是构造一个内部类,并利用构造函数给该测试命名(test name, 如 Car.getWheels ),其覆盖的runTest()方法,指明了该测试需要执行那些方法--testGetWheels()。动态的方法是利用内省(reflection )来实现runTest(),找出需要执行那些测试。此时测试的名字即是测试方法(test method,如testGetWheels)的名字。JUnit会自动找出并调用该类的测试方法。

6>;将TestSuite看作是包裹测试的一个容器。如果将测试比作叶子节点的话,TestSuite就是分支节点。实际上TestCase,TestSuite以及TestSuite组成了一个composite Pattern。 JUnit的文档中有一篇专门讲解如何使用Pattern构造Junit框架。有兴趣的朋友可以查看JUnit资料。

如何运行该测试呢?手工的方法是键入如下命令:

        [Windows] d:>;java junit.textui.TestRunner testCar 
           [Unix] % java junit.textui.TestRunner testCar
        
别担心你要敲的字符量,以后在IDE中,只要点几下鼠标就成了。运行结果应该如下所示,表明执行了一个测试,并通过了测试:

        .
        Time: 0
        
        OK (1 tests)
        
如果我们将Car.getWheels()中返回的的值修改为3,模拟出错的情形,则会得到如下结果: 
        .F
        Time: 0
        There was 1 failure:
        1) testGetWheels(testCar)junit.framework.AssertionFailedError: expected:<4>; but was:<3>;
                        at testCar.testGetWheels(testCar.java:37)

        FAILURES!!!
        Tests run: 1,  Failures: 1,  Errors: 0        
        
注意:Time上的小点表示测试个数,如果测试通过则显示OK。否则在小点的后边标上F,表示该测试失败。注意,在模拟出错的测试中,我们会得到详细的测试报告“expected:<4>; but was:<3>;”,这足以告诉我们问题发生在何处。下面就是你调试,测试,调试,测试...的过程,直至得到期望的结果。


Design by Contract(这句话我没法翻译)

Design by Contract本是Bertrand Meyer(Eiffel语言的创始人)开发的一种设计技术。我发现在JUnit中使用Design by Contract会带来意想不到的效果。Design by Contract的核心是断言(assersion)。断言是一个布尔语句,该语句不能为假,如果为假,则表明出现了一个bug。Design by Contract使用三种断言:前置条件(pre-conditions)、后置条件(post-conditions)和不变式(invariants)这里不打算详细讨论Design by Contract的细节,而是希望其在测试中能发挥其作用。

前置条件在执行测试之前可以用于判断是否允许进入测试,即进入测试的条件。如 expectedWheels >; 0, myCar != null。后置条件用于在测试执行后判断测试的结果是否正确。如 expectedWheels==myCar.getWheels()。而不变式在判断交易(Transaction)的一致性(consistency)方面尤为有用。我希望JUnit可以将Design by Contract作为未来版本的一个增强。


Refactoring(这句话我依然没法翻译)

Refactoring本来与测试没有直接的联系,而是与软件熵有关,但既然我们说测试能解决软件熵问题,我们也就必须说出解决之道。(仅仅进行测试只能发现软件熵,Refactoring则可解决软件熵带来的问题。)软件熵引出了一个问题:是否需要重新设计整个软件的结构?理论上应该如此,但现实不允许我们这么做。这或者是由于时间的原因,或者是由于费用的原因。重新设计整个软件的结构会给我们带来短期的痛苦。而不停地给软件打补丁甚至是补丁的补丁则会给我们带来长期的痛苦。(不管怎样,我们总处于水深火热之中)

Refactoring是一个术语,用于描述一种技术,利用这种技术我们可以免于重构整个软件所带来的短期痛苦。当你refactor时,你并不改变程序的功能,而是改变程序内部的结构,使其更易理解和使用。如:该变一个方法的名字,将一个成员变量从一个类移到另一个类,将两个类似方法抽象到父类中。所作的每一个步都很小,然而1-2个小时的Refactoring工作可以使你的程序结构更适合目前的情况。Refactoring有一些规则:

1>; 不要在加入新功能的同时refactor已有的代码。在这两者间要有一个清晰的界限。如每天早上1-2个小时的Refactoring,其余时间添加新的功能。

2>; 在你开始Refactoring前,和Refactoring后都要保证测试能顺利通过。否则Refactoring没有任何意义。

3>; 进行小的Refactoring,大的就不是Refactoring了。如果你打算重构整个软件,就没有必要Refactoring了。

只有在添加新功能和调试bug时才又必要Refactoring。不要等到交付软件的最后关头才Refactoring。那样和打补丁的区别不大。Refactoring 用在回归测试中也能显示其威力。要明白,我不反对打补丁,但要记住打补丁是应该最后使用的必杀绝招。(打补丁也需要很高的技术,详情参看微软网站)


IDE对JUnit的支持

目前支持JUnit的Java IDE 包括 IDE 方式 个人评价(1-5,满分5) 
Forte for Java 3.0 Enterprise Edition plug-in 3 
JBuilder 6 Enterprise Edition integrated with IDE 4 
Visual Age for Java  support N/A 

在IDE中如何使用JUnit,是非常具体的事情。不同的IDE有不同的使用方法。一旦理解了JUnit的本质,使用起来就十分容易了。所以我们不依赖于具体的IDE,而是集中精力讲述如何利用JUnit编写单元测试代码。心急的人可参看资料。


JUnit简介

既然我们已经对JUnit有了一个大致的了解,我希望能给大家提供一个稍微正式一些的编写JUnit测试文档的手册,明白其中的一些关键术语和概念。但我要声明的是这并不是一本完全的手册,只能认为是一本入门手册。同其他OpenSource的软件有同样的问题,JUnit的文档并没有商业软件文档的那种有规则,简洁和完全。由开发人员编写的文档总是说不太清楚问题,全整的文档需要参考"官方"指南,API手册,邮件讨论组的邮件,甚至包括源代码中及相关的注释。

事实上问题并没有那么复杂,除非你有非常特别的要求,否则,只需参考本文你就可以得到所需的大部分信息。


安装

首先你要获取JUnit的软件包,从JUnit下载最新的软件包(截至写作本文时,JUnit的最新版本是3.7)。将其在适当的目录下解包。这样在安装目录(也就是你所选择的解包的目录)下你找到一个名为junit.jar的文件。将这个jar文件加入你的CLASSPATH系统变量。(IDE的设置会有所不同,参看你所喜爱的IDE的配置指南)JUnit就安装完了。太easy了!

你一旦安装完JUnit,就有可能想试试我们的Car和testCar类,没问题,我已经运行过了,你得到的结果应该和我列出的结果类似。(以防新版JUnit使我的文章过时)

接下来,你可能会先写测试代码,再写工作代码,或者相反,先写工作代码,再写测试代码。我更赞成使用前一种方法:先写测试代码,再写工作代码。因为这样可以使我们编写工作代码时清晰地了解工作类的行为。

要注意编写一定能通过的测试代码(如文中的例子)并没有任何意义,只有测试代码能帮助我们发现bug,测试代码才有其价值。此外测试代码还应该对工作代码进行全面的测试。如给方法调用的参数传入空值、错误值和正确的值,看看方法的行为是否如你所期望的那样。

你现在已经知道了编写测试类的基本步骤:
1>;扩展TestCase类;
2>;覆盖runTest()方法(可选);
3>;写一些testXXXXX()方法;


Fixture

解下来的问题是,如果你要对一个或若干个的类执行多个测试,该怎么办?JUnit对此有特殊的解决办法。

如果需要在一个或若干个的类执行多个测试,这些类就成为了测试的context。在JUnit中被称为Fixture(如testCar类中的 myCar 和 expectedWheels )。当你编写测试代码时,你会发现你花费了很多时间配置/初始化相关测试的Fixture。将配置Fixture的代码放入测试类的构造方法中并不可取,因为我们要求执行多个测试,我并不希望某个测试的结果意外地(如果这是你要求的,那就另当别论了)影响其他测试的结果。通常若干个测试会使用相同的Fixture,而每个测试又各有自己需要改变的地方。

为此,JUnit提供了两个方法,定义在TestCase类中。

                protected void setUp() throws java.lang.Exception
                protected void tearDown() throws java.lang.Exception
                
覆盖setUp()方法,初始化所有测试的Fixture(你甚至可以在setUp中建立网络连接),将每个测试略有不同的地方在testXXX()方法中进行配置。

覆盖tearDown()(我总想起一首叫雨滴的吉他曲),释放你在setUp()中分配的永久性资源,如数据库连接。

当JUnit执行测试时,它在执行每个testXXXXX()方法前都调用setUp(),而在执行每个testXXXXX()方法后都调用tearDown()方法,由此保证了测试不会相互影响。


TestCase

需要提醒一下,在junit.framework.Assert类中定义了相当多的assert方法,主要有assert(), assert(), assertEquals(), assertNull(), assertSame(), assertTrue(), fail()等方法。如果你需要比较自己定义的类,如Car。assert方法需要你覆盖Object类的equals()方法,以比较两个对象的不同。实践表明:如果你覆盖了Object类的equals()方法,最好也覆盖Object类的hashCode()方法。再进一步,连带Object类的toString()方法也一并覆盖。这样可以使测试结果更具可读性。

当你设置好了Fixture后,下一步是编写所需的testXXX()方法。一定要保证testXXX()方法的public属性,否则无法通过内省(reflection)对该测试进行调用。

每个扩展的TestCase类(也就是你编写的测试类)会有多个testXXX()方法。一个testXXX()方法就是一个测试。要想运行这个测试,你必须定义如何运行该测试。如果你有多个testXXX()方法,你就要定义多次。JUnit支持两种运行单个测试的方法:静态的和动态的方法。

静态的方法就是覆盖TestCase类的runTest()方法,一般是采用内部类的方式创建一个测试实例: 
                TestCase test01 = new testCar("test getWheels" {
                        public void runTest() {
                                testGetWheels();
                        }
                }        
                
采用静态的方法要注意要给每个测试一个名字(这个名字可以任意起,但你肯定希望这个名字有某种意义),这样你就可以区分那个测试失败了。

动态的方法是用内省来实现runTest()以创建一个测试实例。这要求测试的名字就是需要调用的测试方法的名字: 
                TestCase test01 = new testCar("testGetWheels";        
                
JUnit会动态查找并调用指定的测试方法。动态的方法很简洁,但如果你键入了错误的名字就会得到一个令人奇怪的NoSuchMethodException异常。动态的方法和静态的方法都很好,你可以按照自己的喜好来选择。(先别着急选择,后面还有一种更酷的方法等着你呢。)


TestSuite

一旦你创建了一些测试实例,下一步就是要让他们能一起运行。我们必须定义一个TestSuite。在JUnit中,这就要求你在TestCase类中定义一个静态的suite()方法。suite()方法就像main()方法一样,JUnit用它来执行测试。在suite()方法中,你将测试实例加到一个TestSuite对象中,并返回这个TestSuite对象。一个TestSuite对象可以运行一组测试。TestSuite和TestCase都实现了Test接口(interface),而Test接口定义了运行测试所需的方法。这就允许你用TestCase和TestSuite的组合创建一个TestSuite。这就是为什么我们前面说TestCase,TestSuite以及TestSuite组成了一个composite Pattern的原因。例子如下: 
                public static Test suite() {
                        TestSuite suite= new TestSuite();
                        suite.addTest(new testCar("testGetWheels");
                        suite.addTest(new testCar("testGetSeats");
                        return suite;
                }        
                
从JUnit 2.0开始,有一种更简单的动态定义测试实例的方法。你只需将类传递给TestSuite,JUnit会根据测试方法名自动创建相应的测试实例。所以你的测试方法最好取名为testXXX()。例子如下: 
                public static Test suite() {
                        return new TestSuite(testCar.class);
                }        
                
从JUnit的设计我们可看出,JUnit不仅可用于单元测试,也可用于集成测试。关于如何用JUnit进行集成测试请参考相关资料。

为了兼容性的考虑,下面列出使用静态方法的例子: 
                public static Test suite() {
                        TestSuite suite= new TestSuite();
                        suite.addTest(
                                new testCar("getWheels" {
                                         protected void runTest() { testGetWheels(); }
                                }
                        );
        
                        suite.addTest(
                                new testCar("getSeats" {
                                         protected void runTest() { testGetSeats(); }
                                }
                        );
                        return suite;                        
                }        
                
TestRunner

有了TestSuite我们就可以运行这些测试了,JUnit提供了三种界面来运行测试 
                [Text  UI] junit.textui.TestRunner
                [AWT   UI] junit.awtui.TestRunner
                [Swing UI] junit.swingui.TestRunner        
                
我们前面已经看过文本界面了,下面让我们来看一看图形界面:



界面很简单,键入类名-testCar。或在启动UI的时候键入类名: 
                [Windows] d:>;java junit.swingui.TestRunner testCar 
                   [Unix] % java junit.swingui.TestRunner testCar
                
从图形UI可以更好的运行测试可查单测试结果。还有一个问题需要注意:如果JUnit报告了测试没有成功,JUnit会区分失败(failures)和错误(errors)。失败是一个期望的被assert方法检查到的结果。而错误则是意外的问题引起的,如ArrayIndexOutOfBoundsException。

由于TestRunner十分简单,界面也比较直观,故不多介绍。朋友们可自行参考相关资料。


JUnit最佳实践 

Martin Fowler(又是这位高人)说过:“当你试图打印输出一些信息或调试一个表达式时,写一些测试代码来替代那些传统的方法。”一开始,你会发现你总是要创建一些新的Fixture,而且测试似乎使你的编程速度慢了下来。然而不久之后,你会发现你重复使用相同的Fixture,而且新的测试通常只涉及添加一个新的测试方法。

你可能会写许多测试代码,但你很快就会发现你设想出的测试只有一小部分是真正有用的。你所需要的测试是那些会失败的测试,即那些你认为不会失败的测试,或你认为应该失败却成功的测试。

我们前面提到过测试是一个不会中断的过程。一旦你有了一个测试,你就要一直确保其正常工作,以检验你所加入的新的工作代码。不要每隔几天或最后才运行测试,每天你都应该运行一下测试代码。这种投资很小,但可以确保你得到可以信赖的工作代码。你的返工率降低了,你会有更多的时间编写工作代码。

不要认为压力大,就不写测试代码。相反编写测试代码会使你的压力逐渐减轻,应为通过编写测试代码,你对类的行为有了确切的认识。你会更快地编写出有效率地工作代码。下面是一些具体的编写测试代码的技巧或较好的实践方法:

1. 不要用TestCase的构造函数初始化Fixture,而要用setUp()和tearDown()方法。
2. 不要依赖或假定测试运行的顺序,因为JUnit利用Vector保存测试方法。所以不同的平台会按不同的顺序从Vector中取出测试方法。
3. 避免编写有副作用的TestCase。例如:如果随后的测试依赖于某些特定的交易数据,就不要提交交易数据。简单的会滚就可以了。
4. 当继承一个测试类时,记得调用父类的setUp()和tearDown()方法。
5. 将测试代码和工作代码放在一起,一边同步编译和更新。(使用Ant中有支持junit的task.)
6. 测试类和测试方法应该有一致的命名方案。如在工作类名前加上test从而形成测试类名。
7. 确保测试与时间无关,不要依赖使用过期的数据进行测试。导致在随后的维护过程中很难重现测试。
8. 如果你编写的软件面向国际市场,编写测试时要考虑国际化的因素。不要仅用母语的Locale进行测试。
9. 尽可能地利用JUnit提供地assert/fail方法以及异常处理的方法,可以使代码更为简洁。
10.测试要尽可能地小,执行速度快。

事实上,JUnit还可用于集成测试,但我并没涉及到,原因有两个:一是因为没有单元测试,集成测试无从谈起。我们接受测试地概念已经很不容易了,如果再引入集成测试就会更困难。二是我比较懒,希望将集成测试的任务交给测试人员去做。在JUnit的网站上有一些相关的文章,有空大家可以翻一翻。


JUnit与J2EE 

如果大家仔细考虑一下的话,就会发现,JUnit有自己的局限性,比如对图形界面的测试,对servlet/JSP以及EJB的测试我们都没有举相关的例子。实际上,JUnit对于GUI界面,servlet/JSP,JavaBean以及EJB都有办法测试。关于GUI的测试比较复杂,适合用一整篇文章来介绍。这里就不多说了。

前面我们所做的测试实际上有一个隐含的环境,JVM我们的类需要这个JVM来执行。而在J2EE框架中,servlet/JSP,EJB都要求有自己的运行环境:Web Container和EJB Container。所以,要想对servlet/JSP,EJB进行测试就需要将其部署在相应的Container中才能进行测试。由于EJB不涉及UI的问题(除非EJB操作XML数据,此时的测试代码比较难写,有可能需要你比较两棵DOM树是否含有相同的内容)只要部署上去之后就可以运行测试代码了。此时setUp()方法显得特别有用,你可以在setUp()方法中利用JNDI查找特定的EJB。而在testXXX()方法中调用并测试这些EJB的方法。

这里所指的JavaBean同样没有UI的问题,比如,我们用JavaBean来访问数据库,或用JavaBean来包裹EJB。如果这类JavaBean没有用到Container的提供的服务,则可直接进行测试,同我们前面所说的一般的类的测试方法一样。如果这类JavaBean用到了Container的提供的服务,则需要将其部署在Container中才能进行测试。方法与EJB类似。

对于servlet/JSP的测试则比较棘手,有人建议在测试代码中构造HttpRequest和HttpResponse,然后进行比较,这就要求开发人员对HTTP协议以及servlet/JSP的内部实现有比较深的认识。我认为这招不太现实。也有人提出使用HttpUnit。由于我对Cactus和HttpUnit 了解不多,所以无法做出合适的建议。希望各位先知们能不吝赐教。

正是由于JUnit的开放性和简单易行,才会引出这篇介绍文章。但技术总在不断地更新,而且我对测试并没有非常深入的理解;我可以将一个复杂的概念简化成一句非常容易理解的话。但我的本意只是希望能降低开发人员步入测试领域的门槛,而不是要修改或重新定义一些概念。这一点是特别要强调的。最后,如果有些兄弟姐妹能给我指出一些注意事项或我对某些问题的理解有误,我会非常感激的。
hibernate集合映射inverse和cascade详解 1、到底在哪用cascade="..."? java http://www.cnblogs.com/amboyna/archive/2008/02/18/1072260.html
hibernate集合映射inverse和cascade详解 1、到底在哪用cascade="..."?

cascade属性并不是多对多关系一定要用的,有了它只是让我们在插入或删除对像时更方便一些,只要在cascade的源头上插入或是删除,所有cascade的关系就会被自己动的插入或是删除。便是为了能正确的cascade,unsaved-value是个很重要的属性。Hibernate通过这个属性来判断一个对象应该save还是update,如果这个对象的id是unsaved-value的话,那说明这个对象不是persistence object要save(insert);如果id是非unsaved-value的话,那说明这个对象是persistence object(数据库中已存在),只要update就行了。saveOrUpdate方法用的也是这个机制。

2、到底在哪用inverse="ture"?

inverse属性默认是false的,就是说关系的两端都来维护关系。这个意思就是说,如有一个Student, Teacher和TeacherStudent表,Student和Teacher是多对多对多关系,这个关系由TeacherStudent这个表来表现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢?在用hibernate时,我们不会显示的对TeacherStudent表做操作。对TeacherStudent的操作是hibernate帮我们做的。hibernate就是看hbm文件中指定的是"谁"维护关系,那个在插入或删除"谁"时,就会处发对关系表的操作。前提是"谁"这个对象已经知道这个关系了,就是说关系另一头的对象已经set或是add到"谁"这个对象里来了。前面说过inverse默认是false,就是关系的两端都维护关系,对其中任一个操作都会处发对表系表的操作。当在关系的一头,如Student中的bag或set中用了inverse="true"时,那就代表关系是由另一关维护的(Teacher)。就是说当这插入Student时,不会操作TeacherStudent表,即使Student已经知道了关系。只有当Teacher插入或删除时才会处发对关系表的操作。所以,当关系的两头都用inverse="true"是不对的,就会导致任何操作都不处发对关系表的操作。当两端都是inverse="false"或是default值是,在代码对关系显示的维护也是不对的,会导致在关系表中插入两次关系。

在一对多关系中inverse就更有意义了。在多对多中,在哪端inverse="true"效果差不多(在效率上)。但是在一对多中,如果要一方维护关系,就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。而如果让"多"方面维护关系时就不会有update操作,因为关系就是在多方的对象中的,直指插入或是删除多方对象就行了。当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管怎样说,还是让"多"方维护关系更直观一些。

3、cascade和inverse有什么区别?

可以这样理解,cascade定义的是关系两端对象到对象的级联关系;而inverse定义的是关系和对象的级联关系。



all : 所有情况下均进行关联操作。 
none:所有情况下均不进行关联操作。这是默认值。 
save-update:在执行save/update/saveOrUpdate时进行关联操作。 
delete:在执行delete时进行关联操作。 
 

all的意思是save-update + delete 
all-delete-orphan 的意思是当对象图中产生孤儿节点时,在数据库中删除该节点 
all比较好理解,举个例子说一下all-delete-orphan: 
Category与Item是一对多的关系,也就是说Category类中有个Set类型的变量items. 
举个例子,现items中存两个Item, item1,item2,如果定义关系为all-delete-orphan 
当items中删除掉一个item(比如用remove()方法删除item1),那么被删除的Item类实例 
将变成孤儿节点,当执行category.update(),或session.flush()时 
hibernate同步缓存和数据库,会把数据库中item1对应的记录删掉 

//////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////

4. hibernate如何根据pojo来更新数据库

4.0  在commit/flush之前,hibernate不会对pojo对象作神秘的处理。
4.0.1 在select查询出pojo时,hibernate根据“字段--属性”的对应关系,用字段的值填充pojo的属性;
然后根据“关系标记”生成sql语句从relationTable中查询出满足条件的relationPojo,并把这些relatinPojo
放到“关系属性”中。这个过程是机械的。

4.0.2 在pojo对象被查出来后,到commit(或flush)之前,它将是一个普通的java对象,hibernate不会做额外的手脚。
比如,不会限制你设置一个属性的值为null或其它任何值
在集合类Set的add(object)操作时, 不会改变object的值,不会检查参数object是否是一个pojo对象
设置mainPojo的一个“桥属性”的值,不会自动设置relationPojo的对应的“桥属性”的值。
执行session.delete(pojo)时,pojo本身没有变化,他的属性值也没有变化。
执行session.save(pojo)时,如果pojo的id不是hibernate或数据库生成,则它的值没有变化。
  如果pojo的id是hibernate或数据库生成,则hibernate会把id给pojo设上去。

extend: 对lazy=true的set,hibernate在进行set的操作(调用java.util.Set中声明的方法)时
会先inialize这个set,仅此而已。而inialize仅仅是从数据库中捞出set的数据。 
如果一个set已经被inialize了,那么对它进行的操作就是java.util.Set接口中定义的语义。

另外,如果id由hibernate来生成,那么在save(pojo)时,hibernate会改变该pojo,会设置它的id,这
可能改变该pojo的hashCode,详细地讨论见帖《》

mapping文件中标记的某些属性及pojo对象的操作会对数据库操作产生影响,这些影响都是在commit时才会起作用。
而在commit前pojo的状态不受它们的影响。

不过,待commit之时,将由hibernate完全掌控,它好像知道pojo对象从创建到commit这中间的所有变化。


4.01. 关联更新
"关系标记"对应的属性是一个pojo或一个pojo的集合,修改“关系属性”的值能会导致更新mainTable表,也可能会更新relationTable表。

这种更新暂叫“关联更新”。


4.1.inverse属性的作用(假定没有设置cascade属性) 
4.1.1 “只有集合标记(set/map/list/array/bag)才有inverse属性”。
————不妨以标记set为例,具体为“一个地区(Address表)的学校(School表)” -- address.schoolSet。

4.1.2 “set的inverse属性决定是否把对set的改动反映到数据库中去。
inverse=false————反映;inverse=true————不反映”
inverse属性默认为false

对<one-to-many>和<many-to-many>子标记,这两条都适用。
不管是对set做什么操作,4.1.2都适用。

4.1.3当inverse=false时,hibernate如何将对set的改动反映到数据库中:

对set的操作主要有:(1)新增元素 address.getSchoolSet().add(oneSchool);
(2)删除元素 address.getSchoolSet().remove(oneSchool);
(3)删除set  address.setSchoolSet(null);
(4)设新set  address.setSchoolSet( newSchoolSet);
(5)转移set  otherSchoolSet = otherAddress.getSchoolSet();
  otherAddress.setSchoolSet(null);
  address.setSchoolSet(otherSchoolSet);
(6)改变set中元素的属性的值  如果是改变key属性,这会导致异常
  如果改变的是普通的属性,则hibernate认为set没有变化(在后面可以看出缘由)。
  所以这种情形不予考虑。
  
改变set后,hibernate对数据库的操作根据是<one-to-many>关系还是<many-to-many>关系而有不同。

对one-to-many,对school set的改动,会改变表SCHOOL中的数据:
  #SCHOOL_ID是school表的主键,SCHOOL_ADDRESS是school表中的地址栏位
  #表School的外键为SCHOOL_ADDRESS,它对应表Address的主键ADDRESS_ID
(11)insert oneSchool———— sqlInsertRowString: 
update SCHOOL set SCHOOL_ADDRESS=? where SCHOOL_ID=? 
(仅仅update foreign-key的值。)
(22)delete oneSchool———— sqlDeleteRowString: 
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ID=?
(很奇怪,把foreign-key设置为null不知道有什么实际意义?)
(33)delete 属于某一address的所有school ————sqlDeleteString:
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ADDRESS=?
(44)update ————sqlUpdateRowString:"", no need

对many-to-many,对school set的改动,会改变关系表ADDRESS_SCHOOL中的数据:
#“地区————学校”的关系为多对多的关系有点牵强,只是为了方便与上面的one-to-many作比较
#假设有一个关系表ADDRESS_SCHOOL,有两个字段ADDRESS_ID, SCHOOL_ID,
#这两个字段分别对应ADDRESS和SCHOOL两表的key
(11)insert的SQL语句为: insert into ADDRESS_SCHOOL(ADDRESS_ID, SCHOOL_ID) 
values(?,?)
(22)delete的SQL语句为: delete from ADDRESS_SCHOOL 
where ADDRESS_ID=? AND SCHOOL_ID=?
(33)delete all的SQL语句为: delete from ADDRESS_SCHOOL
where ADDRESS_ID=?
(44)update的sql语句为 ————sqlUpdateRowString:
update ADDRESS_SCHOOL set ADDRESS_ID=?
where ADDRESS_ID=? AND SCHOOL_ID=?

对set的操作(1),hibernate会执行(11)sqlInsertRowString
对set的操作(2),hibernate会执行(22)sqlDeleteRowString
对set的操作(3),hibernate会执行(33)sqlDeleteString
对set的操作(4),老的schoolSet因为没有所属的address,所以被全部delete掉,即先执行(33)sqlDeleteString
然后新增新的schoolSet,即再执行sqlInsertRowString
对set的操作(5),实际上就是将set从一个pojo转移到另一pojo:
首先,执行sqlDeleteString,删除掉otherAddress所属的school
然后,执行sqlDeleteString,删除掉address原先的school
最后,执行sqlInsertRowString,将otherSchoolSet新增给address

总结:(1)对one-to-many而言,改变set,会让hibernate执行一系列的update语句, 不会delete/insert数据
(2)对many-to-many而言,改变set,只修改关系表的数据,不会影响many-to-many的另一方。
(3)虽然one-to-many和many-to-many的数据库操作不一样,但目的都是一个:维护数据的一致性。执行的sql都
只涉及到“桥字段”,不会考虑或改变其他的字段,所以对set的操作(6)是没有效果地。
extend:对list,可能还会维护index字段。

4.1.4 “inverse与cascade没有什么关系,互无牵扯。”
commit后,这两个属性发挥作用的时机不同,hibernate会根据对pojo对象的改动,及cascade属性的设置,
生成一系列的Action,比如UpdateAction,DeleteAction,InsertAction等,每个Action都有execute方法以执行对应的sql语句。
待所有这些Action都生成好了后,hibernate再一起执行它们,在执行sql前,inverse属性起作用,
当inverse=true时,不执行sql;当inverse=false时,执行sql。

4.1.5 inverse的默认值为false,所以inverse属性默认会进行“关联更新”。

4.1.6 建议:只对set + many-to-many设置inverse=false,其他的标记不考虑inverse属性。
   糟糕的是,不设置inverse属性时,inverse默认为false。

4.2. 级联(cascade)属性的作用: 
4.2.1 只有“关系标记”才有cascade属性:many-to-one,one-to-one ,any, 
set(map, bag, idbag, list, array) + one-to-many(many-to-many)

4.2.2 级联指的是当主控方执行操作时,关联对象(被动方)是否同步执行同一操作。
pojo和它的关系属性的关系就是“主控方 -- 被动方”的关系,如果关系属性是一个set,那么被动方就是set中的一个一个元素,。
比如:学校(School)有三个属性:地区(Address),校长(TheMaster)和学生(Set, 元素为Student)
执行session.delete(school)时,级联决定是否执行session.delete(Address),session.delete(theMaster),
是否对每个aStudent执行session.delete(aStudent)。

extend:这点和inverse属性是有区别的。见4.3.

4.2.3 一个操作因级联cascade可能触发多个关联操作。前一个操作叫“主控操作”,后一个操作叫“关联操作”。
cascade属性的可选值:
all : 所有情况下均进行关联操作。
none:所有情况下均不进行关联操作。这是默认值。
save-update:在执行save/update/saveOrUpdate时进行关联操作。
delete:在执行delete时进行关联操作。 

具体执行什么“关联操作”是根据“主控操作”来的:
  “主控操作”         “关联操作”
session.saveOrUpdate --> session.saveOrUpdate (执行saveOrUpdate实际上会执行save或者update)
session.save ----> session.saveOrUpdate
session.udpate --> session.saveOrUpdate
session.delete --> session.delete

4.2.4 主控操作和关联操作的先后顺序是“先保存one,再保存many;先删除many,再删除one;先update主控方,再update被动方”
对于one-to-one,当其属性constrained="false"(默认值)时,它可看作one-to-many关系;
   当其属性constrained="true"时,它可看作many-to-one关系;
对many-to-many,它可看作one-to-many。

比如:学校(School)有三个属性:地区(Address),校长(TheMaster,其constrained="false")和学生(Set, 元素为Student) 
当执行session.save(school)时,
实际的执行顺序为:session.save(Address);
session.save(school);
session.save(theMaster);
for( 对每一个student ){
session.save(aStudent);
}

当执行session.delete(school)时,
实际的执行顺序为:session.delete(theMaster);
for( 对每一个student ){
session.delete(aStudent);
}
session.delete(school);
session.delete(Address);

当执行session.update(school)时,
实际的执行顺序为:session.update(school);
session.saveOrUpdate(Address);
session.saveOrUpdate(theMaster);
for( 对每一个student ){
session.saveOrUpdate(aStudent);
}
注意:update操作因级联引发的关联操作为saveOrUpdate操作,而不是update操作。
saveOrUpdate与update的区别是:前者根据操作对象是保存了还是没有保存,而决定执行update还是save

extends: 实际中,删除学校不会删除地区,即地区的cascade一般设为false
另外,many-to-many关系很少设置cascade=true,而是设置inverse=false。这个反映了cascade和inverse的区别。见4.3

4.2.6 cascade的默认值为false,所以inverse属性默认会进行“关联更新”。

4.2.7 总结:级联(cascade)就是操作一个对象时,对它的属性(其cascade=true)也进行这个操作。


4.3 inverse和cascade的比较
这两个属性本身互不影响,但起的作用有些类似,都能引发对关系表的更新。

4.3.1 inverse只对set+one-to-many(或many-to-many)有效,对many-to-one, one-to-one无效。
  cascade对关系标记都有效。
  
4.3.2 inverse对集合对象整体起作用,cascade对集合对象中的一个一个元素起作用,如果集合为空,那么cascade不会引发关联操作。
比如将集合对象置为null, school.setStudentSet(null)
inverse导致hibernate执行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=?
cascade则不会执行对STUDENT表的关联更新, 因为集合中没有元素。

再比新增一个school, session.save(school)
inverse导致hibernate执行:
for( 对(school的每一个student ){
udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //将学生的school_id改为新的school的id
}
cascade导致hibernate执行:
for( 对school的每一个student ){
session.save(aStudent); //对学生执行save操作
}

extends:如果改变集合中的部分元素(比如新增一个元素),
inverse: hibernate先判断哪些元素改变了,对改变的元素执行相应的sql
cascade: 它总是对集合中的每个元素执行关联操作。
(在关联操作中,hibernate会判断操作的对象是否改变)

4.3.2 两个起作用的时机不同:
cascade:在对主控方操作时,级联发生。
inverse: 在flush时(commit会自动执行flush),对session中的所有set,hibernate判断每个set是否有变化,
对有变化的set执行相应的sql,执行之前,会有个判断:if( inverse == true ) return;

可以看出cascade在先,inverse在后。

4.3.3 inverse 对set + one-to-many 和 set + many-to-many 起的作用不同。hibernate生成的sql不同。
  对one-to-many,hibernate对many方的数据库表执行update语句。
  对many-to-many, hibernate对关系表执行insert/update/delte语句,注意不是对many方的数据库表而是关系表。
  
  cascase 对set都是一致的,不管one-to-many还是many-to-many。都简单地把操作传递到set中的每个元素。所以它总是更新many
方的数据库表。

4.3.4 建议:只对set + many-to-many设置inverse=false,其他的标记不考虑inverse属性,都设为inverse=true。
   
   对cascade,一般对many-to-one,many-to-many,constrained=true的one-to-one 不设置级联删除。
hibernate3的cascade导致deleted object would be re-saved by cascade与 A collection with java http://blog.csdn.net/koyo25/article/details/5618515
hibernate3的cascade导致deleted object would be re-saved by cascade与 A collection with cascade="all-delete-orphan" was no longer ref 
分类: SSH架构 2010-05-23 20:16 507人阅读 评论(0) 收藏 举报 
我在处理数据库的级联关系时,老是碰到题目中的两个异常:deleted object would be re-saved by cascade和A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance。这两个问题,删除cascade这一项会解决一个问题但是也带来了另一个问题,删除cascade以后呢,就相反,总有问题。后来发现原来是hibernate3不支持cascade=“all-delete-orphan”的值,只要把set里面的cascade改成cascade="save-update,delete"就可以了

ThreadPoolExecutor运转机制详解 java ThreadPoolExecutor运转机制详解
最近发现几起对ThreadPoolExecutor的误用,其中包括自己,发现都是因为没有仔细看注释和内部运转机制,想当然的揣测参数导致,先看一下新建一个ThreadPoolExecutor的构建参数:

public ThreadPoolExecutor(int corePoolSize,   
                          int maximumPoolSize,   
                          long keepAliveTime,   
                          TimeUnit unit,   
                          BlockingQueue<Runnable> workQueue,   
                          ThreadFactory threadFactory,   
                          RejectedExecutionHandler handler)  

看这个参数很容易让人以为是线程池里保持corePoolSize个线程,如果不够用,就加线程入池直至maximumPoolSize大小,如果还不够就往workQueue里加,如果workQueue也不够就用RejectedExecutionHandler来做拒绝处理。

但实际情况不是这样,具体流程如下:

1)当池子大小小于corePoolSize就新建线程,并处理请求

2)当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理

3)当workQueue放不下新入的任务时,新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来做拒绝处理

4)另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁

内部结构如下所示:




从中可以发现ThreadPoolExecutor就是依靠BlockingQueue的阻塞机制来维持线程池,当池子里的线程无事可干的时候就通过workQueue.take()阻塞住。

其实可以通过Executes来学学几种特殊的ThreadPoolExecutor是如何构建的。

public static ExecutorService newFixedThreadPool(int nThreads) {   
    return new ThreadPoolExecutor(nThreads, nThreads,   
                                  0L, TimeUnit.MILLISECONDS,   
                                  new LinkedBlockingQueue<Runnable>());   
}  

newFixedThreadPool就是一个固定大小的ThreadPool

public static ExecutorService newCachedThreadPool() {   
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,   
                                  60L, TimeUnit.SECONDS,   
                                  new SynchronousQueue<Runnable>());   
}  

newCachedThreadPool比较适合没有固定大小并且比较快速就能完成的小任务,没必要维持一个Pool,这比直接new Thread来处理的好处是能在60秒内重用已创建的线程。

其他类型的ThreadPool看看构建参数再结合上面所说的特性就大致知道它的特性
Java简单实现Ping功能 java http://www.micmiu.com/lang/java/java-ping/
下面将给出上述的两种实现的详细过程:

一、直接调用服务器本身的ping命令

TestPingCmd.java


package michael.net;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @blog http://www.micmiu.com
 * @author Michael
 */
public class TestPingCmd {

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {

        // 读取txt文件中的IP列表
         TestPingCmd pinger = new TestPingCmd();
        List<String> iplist = pinger
                .getIpListFromTxt("d:/test/idc_ping_ip.txt");

        // List<String> iplist = new ArrayList<String>();
        // iplist.add("222.*.*.*");
        // iplist.add("222.*.*.*");
        // iplist.add("222.*.*.*");
        // iplist.add("222.*.*.*");
        // iplist.add("222.*.*.*");
        ThreadPoolExecutor executorPool = new ThreadPoolExecutor(50, 60, 60,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50),
                new ThreadPoolExecutor.CallerRunsPolicy());
        long startTime = System.currentTimeMillis();
        final int maxCount = 4;
        for (final String ip : iplist) {
            executorPool.execute(new Runnable() {
                public void run() {
                    TestPingCmd pinger = new TestPingCmd();
                    Integer countSucce = pinger.doPingCmd(ip, maxCount);
                    if (null != countSucce) {
                        System.out.println("host:[ " + ip + " ] ping cout: "
                                + maxCount + " success: " + countSucce);

                    } else {
                        System.out
                                .println("host:[ " + ip + " ] ping cout null");
                    }
                }

            });
        }
        while (executorPool.getActiveCount() > 0) {
            Thread.sleep(100);
        }
        System.out.println("complete ping jobs count = " + iplist.size()
                + " , total used time(ms) = "
                + (System.currentTimeMillis() - startTime));
        executorPool.shutdown();
    }

    /**
     * @param destIp
     * @param maxCount
     * @return
     */
    public Integer doPingCmd(String destIp, int maxCount) {
        LineNumberReader input = null;
        try {
            String osName = System.getProperties().getProperty("os.name");
            String pingCmd = null;
            if (osName.startsWith("Windows")) {
                pingCmd = "cmd /c ping -n {0} {1}";
                pingCmd = MessageFormat.format(pingCmd, maxCount, destIp);
            } else if (osName.startsWith("Linux")) {
                pingCmd = "ping -c {0} {1}";
                pingCmd = MessageFormat.format(pingCmd, maxCount, destIp);
            } else {
                System.out.println("not support OS");
                return null;
            }
            Process process = Runtime.getRuntime().exec(pingCmd);
            InputStreamReader ir = new InputStreamReader(process
                    .getInputStream());
            input = new LineNumberReader(ir);
            String line;
            List<String> reponse = new ArrayList<String>();

            while ((line = input.readLine()) != null) {
                if (!"".equals(line)) {
                    reponse.add(line);
                    // System.out.println("====:" + line);
                }
            }
            if (osName.startsWith("Windows")) {
                return parseWindowsMsg(reponse, maxCount);
            } else if (osName.startsWith("Linux")) {
                return parseLinuxMsg(reponse, maxCount);
            }

        } catch (IOException e) {
            System.out.println("IOException   " + e.getMessage());

        } finally {
            if (null != input) {
                try {
                    input.close();
                } catch (IOException ex) {
                    System.out.println("close error:" + ex.getMessage());

                }
            }
        }
        return null;
    }

    private int parseWindowsMsg(List<String> reponse, int total) {
        int countTrue = 0;
        int countFalse = 0;
        for (String str : reponse) {
            if (str.startsWith("来自") || str.startsWith("Reply from")) {
                countTrue++;
            }
            if (str.startsWith("请求超时") || str.startsWith("Request timed out")) {
                countFalse++;
            }
        }
        return countTrue;
    }

    private int parseLinuxMsg(List<String> reponse, int total) {
        int countTrue = 0;
        for (String str : reponse) {
            if (str.contains("bytes from") && str.contains("icmp_seq=")) {
                countTrue++;
            }
        }
        return countTrue;
    }

    /**
     * @param filepath
     * @return list
     */
    public List<String> getIpListFromTxt(String filepath) {
        BufferedReader br = null;
        List<String> iplist = new ArrayList<String>();
        try {
            File file = new File(filepath);
            br = new BufferedReader(new FileReader(file));
            while (br.ready()) {
                String line = br.readLine();
                if (null != line && !"".equals(line)) {
                    iplist.add(line);
                }
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);

        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (Exception ex) {
                    ex.printStackTrace(System.out);
                }
            }
        }
        return iplist;
    }

}
二、通过指定服务器去ping测试

主要思路:利用Mina在指定的第三方服务器上运行server端,然后实现客户端和 第三方 服务器建立socket连接,发送ping任务的消息给第三方服务器,第三方服务器再把执行结果实时反馈给客户端。

代码包括四个类:

服务端:PingServerIoHandler.java PingServer.java 
客户端:PingClientIoHandler.java PingClient.java 


package michael.mina.ping;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.text.MessageFormat;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

/**
 * @blog http://www.micmiu.com
 * @author Michael
 */
public class PingServerIoHandler extends IoHandlerAdapter {
    private String logId = "SERVER:: ";
    private int msgCount = 0;

    @Override
    public void exceptionCaught(IoSession pSession, Throwable pCause)
            throws Exception {
        System.out.println(logId + "发生异常:" + pCause.getLocalizedMessage());
    }

    @Override
    public void messageReceived(IoSession pSession, Object pMessage)
            throws Exception {
        String msg = String.valueOf(pMessage);
        msgCount++;
        System.out.println(logId + "收到客户端第 " + msgCount + " 条消息:" + msg);
        pSession.write(msgCount);

        if (msg.startsWith("ping")) {
            String destIp = msg.split(" ")[1];
            doPingCmd(pSession, destIp);
        }

    }

    @Override
    public void messageSent(IoSession pSession, Object pMessage)
            throws Exception {
        System.out.println(logId + "发出消息:" + pMessage);
    }

    @Override
    public void sessionClosed(IoSession pSession) throws Exception {
        System.out.println(logId + "one client closed ");
    }

    @Override
    public void sessionCreated(IoSession pSession) throws Exception {
        System.out.println(logId + "sessionCreated ");
    }

    @Override
    public void sessionIdle(IoSession pSession, IdleStatus pStatus)
            throws Exception {
        super.sessionIdle(pSession, pStatus);
    }

    @Override
    public void sessionOpened(IoSession pSession) throws Exception {
        System.out.println(logId + "sessionOpened ");
    }

    private Integer doPingCmd(IoSession pSession, String destIp) {
        LineNumberReader input = null;
        int maxCount = 4;
        try {
            String osName = System.getProperties().getProperty("os.name");
            String pingCmd = null;
            if (osName.startsWith("Windows")) {
                pingCmd = "cmd /c ping -n {0} {1}";
                pingCmd = MessageFormat.format(pingCmd, maxCount, destIp);
            } else if (osName.startsWith("Linux")) {
                pingCmd = "ping -c {0} {1}";
                pingCmd = MessageFormat.format(pingCmd, maxCount, destIp);
            } else {
                System.out.println("not support OS");
                return null;
            }
            Process process = Runtime.getRuntime().exec(pingCmd);
            InputStreamReader ir = new InputStreamReader(process
                    .getInputStream());
            input = new LineNumberReader(ir);
            String line;

            while ((line = input.readLine()) != null) {
                if (!"".equals(line)) {
                    pSession.write(line);
                }
            }
        } catch (IOException e) {
            System.out.println("IOException   " + e.getMessage());

        } finally {
            if (null != input) {
                try {
                    input.close();
                } catch (IOException ex) {
                    System.out.println("close error:" + ex.getMessage());

                }
            }
        }
        return null;
    }
}
图片压缩 java
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
 * 图片处理工具类:<br>
 * 功能:缩放图像、切割图像、图像类型转换、彩色转黑白、文字水印、图片水印等
 * @author Administrator
 */
public class ImageUtils {
    /**
     * 几种常见的图片格式
     */
    public static String IMAGE_TYPE_GIF = "gif";// 图形交换格式
    public static String IMAGE_TYPE_JPG = "jpg";// 联合照片专家组
    public static String IMAGE_TYPE_JPEG = "jpeg";// 联合照片专家组
    public static String IMAGE_TYPE_BMP = "bmp";// 英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式
    public static String IMAGE_TYPE_PNG = "png";// 可移植网络图形
    public static String IMAGE_TYPE_PSD = "psd";// Photoshop的专用格式Photoshop
    /**
     * 程序入口:用于测试
     * @param args
     */
    public static void main(String[] args) {
        // 1-缩放图像:
        // 方法一:按比例缩放
        ImageUtils.scale("e:/abc.jpg", "e:/abc_scale.jpg", 2, true);//测试OK
        // 方法二:按高度和宽度缩放
        ImageUtils.scale2("e:/abc.jpg", "e:/abc_scale2.jpg", 500, 300, true);//测试OK
        // 2-切割图像:
        // 方法一:按指定起点坐标和宽高切割
        ImageUtils.cut("e:/abc.jpg", "e:/abc_cut.jpg", 0, 0, 400, 400 );//测试OK
        // 方法二:指定切片的行数和列数
        ImageUtils.cut2("e:/abc.jpg", "e:/", 2, 2 );//测试OK
        // 方法三:指定切片的宽度和高度
        ImageUtils.cut3("e:/abc.jpg", "e:/", 300, 300 );//测试OK
        // 3-图像类型转换:
        ImageUtils.convert("e:/abc.jpg", "GIF", "e:/abc_convert.gif");//测试OK
        // 4-彩色转黑白:
        ImageUtils.gray("e:/abc.jpg", "e:/abc_gray.jpg");//测试OK
        // 5-给图片添加文字水印:
        // 方法一:
        ImageUtils.pressText("我是水印文字","e:/abc.jpg","e:/abc_pressText.jpg","宋体",Font.BOLD,Color.white,80, 0, 0, 0.5f);//测试OK
        // 方法二:
        ImageUtils.pressText2("我也是水印文字", "e:/abc.jpg","e:/abc_pressText2.jpg", "黑体", 36, Color.white, 80, 0, 0, 0.5f);//测试OK
        
        // 6-给图片添加图片水印:
        ImageUtils.pressImage("e:/abc2.jpg", "e:/abc.jpg","e:/abc_pressImage.jpg", 0, 0, 0.5f);//测试OK
    }
    /**
     * 缩放图像(按比例缩放)
     * @param srcImageFile 源图像文件地址
     * @param result 缩放后的图像地址
     * @param scale 缩放比例
     * @param flag 缩放选择:true 放大; false 缩小;
     */
    public final static void scale(String srcImageFile, String result,
            int scale, boolean flag) {
        try {
            BufferedImage src = ImageIO.read(new File(srcImageFile)); // 读入文件
            int width = src.getWidth(); // 得到源图宽
            int height = src.getHeight(); // 得到源图长
            if (flag) {// 放大
                width = width * scale;
                height = height * scale;
            } else {// 缩小
                width = width / scale;
                height = height / scale;
            }
            Image image = src.getScaledInstance(width, height,
                    Image.SCALE_DEFAULT);
            BufferedImage tag = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics g = tag.getGraphics();
            g.drawImage(image, 0, 0, null); // 绘制缩小后的图
            g.dispose();
            ImageIO.write(tag, "JPEG", new File(result));// 输出到文件流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 缩放图像(按高度和宽度缩放)
     * @param srcImageFile 源图像文件地址
     * @param result 缩放后的图像地址
     * @param height 缩放后的高度
     * @param width 缩放后的宽度
     * @param bb 比例不对时是否需要补白:true为补白; false为不补白;
     */
    public final static void scale2(String srcImageFile, String result, int height, int width, boolean bb) {
        try {
            double ratio = 0.0; // 缩放比例
            File f = new File(srcImageFile);
            BufferedImage bi = ImageIO.read(f);
            Image itemp = bi.getScaledInstance(width, height, bi.SCALE_SMOOTH);
            // 计算比例
            if ((bi.getHeight() > height) || (bi.getWidth() > width)) {
                if (bi.getHeight() > bi.getWidth()) {
                    ratio = (new Integer(height)).doubleValue()
                            / bi.getHeight();
                } else {
                    ratio = (new Integer(width)).doubleValue() / bi.getWidth();
                }
                AffineTransformOp op = new AffineTransformOp(AffineTransform
                        .getScaleInstance(ratio, ratio), null);
                itemp = op.filter(bi, null);
            }
            if (bb) {//补白
                BufferedImage image = new BufferedImage(width, height,
                        BufferedImage.TYPE_INT_RGB);
                Graphics2D g = image.createGraphics();
                g.setColor(Color.white);
                g.fillRect(0, 0, width, height);
                if (width == itemp.getWidth(null))
                    g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2,
                            itemp.getWidth(null), itemp.getHeight(null),
                            Color.white, null);
                else
                    g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0,
                            itemp.getWidth(null), itemp.getHeight(null),
                            Color.white, null);
                g.dispose();
                itemp = image;
            }
            ImageIO.write((BufferedImage) itemp, "JPEG", new File(result));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 图像切割(按指定起点坐标和宽高切割)
     * @param srcImageFile 源图像地址
     * @param result 切片后的图像地址
     * @param x 目标切片起点坐标X
     * @param y 目标切片起点坐标Y
     * @param width 目标切片宽度
     * @param height 目标切片高度
     */
    public final static void cut(String srcImageFile, String result,
            int x, int y, int width, int height) {
        try {
            // 读取源图像
            BufferedImage bi = ImageIO.read(new File(srcImageFile));
            int srcWidth = bi.getHeight(); // 源图宽度
            int srcHeight = bi.getWidth(); // 源图高度
            if (srcWidth > 0 && srcHeight > 0) {
                Image image = bi.getScaledInstance(srcWidth, srcHeight,
                        Image.SCALE_DEFAULT);
                // 四个参数分别为图像起点坐标和宽高
                // 即: CropImageFilter(int x,int y,int width,int height)
                ImageFilter cropFilter = new CropImageFilter(x, y, width, height);
                Image img = Toolkit.getDefaultToolkit().createImage(
                        new FilteredImageSource(image.getSource(),
                                cropFilter));
                BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                Graphics g = tag.getGraphics();
                g.drawImage(img, 0, 0, width, height, null); // 绘制切割后的图
                g.dispose();
                // 输出为文件
                ImageIO.write(tag, "JPEG", new File(result));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 图像切割(指定切片的行数和列数)
     * @param srcImageFile 源图像地址
     * @param descDir 切片目标文件夹
     * @param rows 目标切片行数。默认2,必须是范围 [1, 20] 之内
     * @param cols 目标切片列数。默认2,必须是范围 [1, 20] 之内
     */
    public final static void cut2(String srcImageFile, String descDir,
            int rows, int cols) {
        try {
            if(rows<=0||rows>20) rows = 2; // 切片行数
            if(cols<=0||cols>20) cols = 2; // 切片列数
            // 读取源图像
            BufferedImage bi = ImageIO.read(new File(srcImageFile));
            int srcWidth = bi.getHeight(); // 源图宽度
            int srcHeight = bi.getWidth(); // 源图高度
            if (srcWidth > 0 && srcHeight > 0) {
                Image img;
                ImageFilter cropFilter;
                Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
                int destWidth = srcWidth; // 每张切片的宽度
                int destHeight = srcHeight; // 每张切片的高度
                // 计算切片的宽度和高度
                if (srcWidth % cols == 0) {
                    destWidth = srcWidth / cols;
                } else {
                    destWidth = (int) Math.floor(srcWidth / cols) + 1;
                }
                if (srcHeight % rows == 0) {
                    destHeight = srcHeight / rows;
                } else {
                    destHeight = (int) Math.floor(srcWidth / rows) + 1;
                }
                // 循环建立切片
                // 改进的想法:是否可用多线程加快切割速度
                for (int i = 0; i < rows; i++) {
                    for (int j = 0; j < cols; j++) {
                        // 四个参数分别为图像起点坐标和宽高
                        // 即: CropImageFilter(int x,int y,int width,int height)
                        cropFilter = new CropImageFilter(j * destWidth, i * destHeight,
                                destWidth, destHeight);
                        img = Toolkit.getDefaultToolkit().createImage(
                                new FilteredImageSource(image.getSource(),
                                        cropFilter));
                        BufferedImage tag = new BufferedImage(destWidth,
                                destHeight, BufferedImage.TYPE_INT_RGB);
                        Graphics g = tag.getGraphics();
                        g.drawImage(img, 0, 0, null); // 绘制缩小后的图
                        g.dispose();
                        // 输出为文件
                        ImageIO.write(tag, "JPEG", new File(descDir
                                + "_r" + i + "_c" + j + ".jpg"));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 图像切割(指定切片的宽度和高度)
     * @param srcImageFile 源图像地址
     * @param descDir 切片目标文件夹
     * @param destWidth 目标切片宽度。默认200
     * @param destHeight 目标切片高度。默认150
     */
    public final static void cut3(String srcImageFile, String descDir,
            int destWidth, int destHeight) {
        try {
            if(destWidth<=0) destWidth = 200; // 切片宽度
            if(destHeight<=0) destHeight = 150; // 切片高度
            // 读取源图像
            BufferedImage bi = ImageIO.read(new File(srcImageFile));
            int srcWidth = bi.getHeight(); // 源图宽度
            int srcHeight = bi.getWidth(); // 源图高度
            if (srcWidth > destWidth && srcHeight > destHeight) {
                Image img;
                ImageFilter cropFilter;
                Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
                int cols = 0; // 切片横向数量
                int rows = 0; // 切片纵向数量
                // 计算切片的横向和纵向数量
                if (srcWidth % destWidth == 0) {
                    cols = srcWidth / destWidth;
                } else {
                    cols = (int) Math.floor(srcWidth / destWidth) + 1;
                }
                if (srcHeight % destHeight == 0) {
                    rows = srcHeight / destHeight;
                } else {
                    rows = (int) Math.floor(srcHeight / destHeight) + 1;
                }
                // 循环建立切片
                // 改进的想法:是否可用多线程加快切割速度
                for (int i = 0; i < rows; i++) {
                    for (int j = 0; j < cols; j++) {
                        // 四个参数分别为图像起点坐标和宽高
                        // 即: CropImageFilter(int x,int y,int width,int height)
                        cropFilter = new CropImageFilter(j * destWidth, i * destHeight,
                                destWidth, destHeight);
                        img = Toolkit.getDefaultToolkit().createImage(
                                new FilteredImageSource(image.getSource(),
                                        cropFilter));
                        BufferedImage tag = new BufferedImage(destWidth,
                                destHeight, BufferedImage.TYPE_INT_RGB);
                        Graphics g = tag.getGraphics();
                        g.drawImage(img, 0, 0, null); // 绘制缩小后的图
                        g.dispose();
                        // 输出为文件
                        ImageIO.write(tag, "JPEG", new File(descDir
                                + "_r" + i + "_c" + j + ".jpg"));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 图像类型转换:GIF->JPG、GIF->PNG、PNG->JPG、PNG->GIF(X)、BMP->PNG
     * @param srcImageFile 源图像地址
     * @param formatName 包含格式非正式名称的 String:如JPG、JPEG、GIF等
     * @param destImageFile 目标图像地址
     */
    public final static void convert(String srcImageFile, String formatName, String destImageFile) {
        try {
            File f = new File(srcImageFile);
            f.canRead();
            f.canWrite();
            BufferedImage src = ImageIO.read(f);
            ImageIO.write(src, formatName, new File(destImageFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 彩色转为黑白 
     * @param srcImageFile 源图像地址
     * @param destImageFile 目标图像地址
     */
    public final static void gray(String srcImageFile, String destImageFile) {
        try {
            BufferedImage src = ImageIO.read(new File(srcImageFile));
            ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
            ColorConvertOp op = new ColorConvertOp(cs, null);
            src = op.filter(src, null);
            ImageIO.write(src, "JPEG", new File(destImageFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 给图片添加文字水印
     * @param pressText 水印文字
     * @param srcImageFile 源图像地址
     * @param destImageFile 目标图像地址
     * @param fontName 水印的字体名称
     * @param fontStyle 水印的字体样式
     * @param color 水印的字体颜色
     * @param fontSize 水印的字体大小
     * @param x 修正值
     * @param y 修正值
     * @param alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
     */
    public final static void pressText(String pressText,
            String srcImageFile, String destImageFile, String fontName,
            int fontStyle, Color color, int fontSize,int x,
            int y, float alpha) {
        try {
            File img = new File(srcImageFile);
            Image src = ImageIO.read(img);
            int width = src.getWidth(null);
            int height = src.getHeight(null);
            BufferedImage image = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, width, height, null);
            g.setColor(color);
            g.setFont(new Font(fontName, fontStyle, fontSize));
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                    alpha));
            // 在指定坐标绘制水印文字
            g.drawString(pressText, (width - (getLength(pressText) * fontSize))
                    / 2 + x, (height - fontSize) / 2 + y);
            g.dispose();
            ImageIO.write((BufferedImage) image, "JPEG", new File(destImageFile));// 输出到文件流
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 给图片添加文字水印
     * @param pressText 水印文字
     * @param srcImageFile 源图像地址
     * @param destImageFile 目标图像地址
     * @param fontName 字体名称
     * @param fontStyle 字体样式
     * @param color 字体颜色
     * @param fontSize 字体大小
     * @param x 修正值
     * @param y 修正值
     * @param alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
     */
    public final static void pressText2(String pressText, String srcImageFile,String destImageFile,
            String fontName, int fontStyle, Color color, int fontSize, int x,
            int y, float alpha) {
        try {
            File img = new File(srcImageFile);
            Image src = ImageIO.read(img);
            int width = src.getWidth(null);
            int height = src.getHeight(null);
            BufferedImage image = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, width, height, null);
            g.setColor(color);
            g.setFont(new Font(fontName, fontStyle, fontSize));
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                    alpha));
            // 在指定坐标绘制水印文字
            g.drawString(pressText, (width - (getLength(pressText) * fontSize))
                    / 2 + x, (height - fontSize) / 2 + y);
            g.dispose();
            ImageIO.write((BufferedImage) image, "JPEG", new File(destImageFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 给图片添加图片水印
     * @param pressImg 水印图片
     * @param srcImageFile 源图像地址
     * @param destImageFile 目标图像地址
     * @param x 修正值。 默认在中间
     * @param y 修正值。 默认在中间
     * @param alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
     */
    public final static void pressImage(String pressImg, String srcImageFile,String destImageFile,
            int x, int y, float alpha) {
        try {
            File img = new File(srcImageFile);
            Image src = ImageIO.read(img);
            int wideth = src.getWidth(null);
            int height = src.getHeight(null);
            BufferedImage image = new BufferedImage(wideth, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, wideth, height, null);
            // 水印文件
            Image src_biao = ImageIO.read(new File(pressImg));
            int wideth_biao = src_biao.getWidth(null);
            int height_biao = src_biao.getHeight(null);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                    alpha));
            g.drawImage(src_biao, (wideth - wideth_biao) / 2,
                    (height - height_biao) / 2, wideth_biao, height_biao, null);
            // 水印文件结束
            g.dispose();
            ImageIO.write((BufferedImage) image,  "JPEG", new File(destImageFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 计算text的长度(一个中文算两个字符)
     * @param text
     * @return
     */
    public final static int getLength(String text) {
        int length = 0;
        for (int i = 0; i < text.length(); i++) {
            if (new String(text.charAt(i) + "").getBytes().length > 1) {
                length += 2;
            } else {
                length += 1;
            }
        }
        return length / 2;
    }
}
Struts2的国际化 java http://www.blogjava.net/toby/archive/2009/03/19/260918.html
Struts2的国际化 
资源文件的命名:basename_language_country.properties

JAVA国际化

如果系统同时存在资源文件、类文件,系统将以类文件为主,而不会调用资源文件。

对于简体中文的Locale,ResourceBundle搜索资源的顺序是:

(1)baseName_zh_CN.class

(2)baseName_zh_CN.properties

(3)baseName_zh.class

(4)baseName_zh.properties

(5)baseName.class

(6)baseName.properties

 

Struts2的国际化入门

Struts2国际化是建立在Java国际化的基础上的,一样是通过提供不同国家/语言环境的消息资源,然后通过ResourceBundle

加载指定Locale对应的资源文件,再取得该资源文件中指定key对应的消息--整个过程与JAVA程序的国家化完全相同,只是

Struts2框架对JAVA程序国际化进行了进一步封装,从而简化了应用程序的国际化。

Struts2需要国际化的部分

类型转换:

数据校验:

验证框架xml配置文件的国际化:RegisterAction-validation.xml文件<message key="username.xml.invalid"/>

JSP页面的国际化:<s:text name="addUser"/>

Action的国际化:利用ActionSupport类提供的getText()方法.

Struts2中加载全局资源文件

struts.xml

<constant name="struts.custom.i18n.resources" value="baseName"/>

或

struts.properties

struts.custom.i18n.resources=baseName

访问国际化消息

Struts2访问国际化消息主要有如下三种方式:

(1)JSP页面:<s:text name="key"/>

(2)Action类中:使用ActionSupport类的getText方法。

(3)表单元素的Label里:为表单元素指定一个key属性

输出带占位符的国际化消息

Struts2中提供了如下两种方式来填充消息字符串中的占位符

(1)JSP页面,在<s:text.../>标签中使用多个<s:param.../>标签来填充消息中的占位符。

(2)Action中,在调用getText方法时使用getText(String aTextName,List args)或getText(String key, String[] args)方法来填充占位符。

除此之外,Struts2还提供了对占位符的一种替代方式,这种方式允许在国际化消息资源文件中使用表达式,对于这种方式,则可避免在使用国际化消息时还需要为占位符传入参数值。

如下在消息资源中使用表达式

succTip=${username}, 欢迎, 您已经登录!

在上面的消息资源中,通过使用表达式,可以从ValueStack中取出该username属性值,自动填充到该消息资源中。

加载资源文件的方式

(1)加载全局资源文件: <constant name="struts.custom.i18n.resources" value="baseName"/>

(2)包范围资源文件 :为Struts2指定包范围资源文件的方法是,在包的根路径下建立多个文件名为package_language_country.properties的文件,一旦建立了

    这个系列的国际化资源文件,应用中处于该包下的所有Action都可以访问该资源文件。需要注意的是上面的包范围资源文件的baseName就是package,

 不是Action所在的包名。

(3)Action范围资源文件:在Action类文件所在的路径建立多个文件名为ActionName_language_country.properties的文件。

(4)临时指定资源文件:<s:i18n.../>标签的name属性指定临时的国际化资源文件

加载资源文件的顺序

Action中加载资源文件,假设我们在某个ChildAction中调用了getText("user.title"),Struts 2.0的将会执行以下的操作:

(1)优先加载系统中保存在ChildAction的类文件相同位置,且baseName为ChildAction的系列资源文件。

(2)如果在(1)中找不到指定key对应的消息,且ChildAction有父类ParentAction,则加载系统中保存在ParentAction的类文件相同位置,且

   baseName为ParentAction的系列资源文件。

(3)如果在(2)中找不到指定key对应的消息,且ChildAction有实现接口IChildAction,则加载系统中保存在IChildAction的类文件相同位置,且

   baseName为IChildAction的系列资源文件。

(4)如果在(3)中找不到指定key对应的消息,且ChildAction有实现接口ModelDriven(即使用模型驱动模式),则对于getModel()方法返回的model对象,

   重新执行第(1)步操作。

(5)如果在(4)中找不到指定key对应的消息,则查找当前包下baseName为package的系列资源文件。

(6)如果在(5)中找不到指定key对应的消息,则沿着当前包上溯,直到最顶层包来查找baseName为package的系列资源文件。

(7)如果在(6)中找不到指定key对应的消息,则查找struts.custom.i18n.resources常量指定baseName的系列资源文件。

(8)如果经过上面的步骤一直找不到key对应的消息,将直接输出该key的字符串值。

对于在JSP中访问国际化消息,则简单的多,他们又可以分为两种形式:

(1)对于使用<s:i18n.../>标签作为父标签的<s:text.../>标签、表单标签的形式:

   a、将从<s:i18n.../>标签指定的国际化资源文件中加载指定key对应的消息。

   b、如果在a中找不到指定key对应的消息,则查找struts.custom.i18n.resources常量指定baseName的系列资源文件。

   c、如果经过上面步骤一直找不到该key对应的消息,将直接输出该key的字符串值。

(2)如果<s:text.../>标签、表单标签没有使用<s:i18n.../>标签作为父标签:

    直接加载struts.custom.i18n.resources常量指定baseName的系列资源文件。如果找不到该key对应的消息,将直接输出该key的字符串值。

 

允许用户自行选择程序语言

Struts2国际化的运行机制

    在Struts2中,可以通过ActionContext.getContext().setLocale(Locale arg)设置用户的默认语言。

为了简化设置用户默认语言环境,Struts2提供了一个名为i18n的拦截器(Interceptor),并且将其注册在默认的拦截器中(defaultStack)。

    i18n拦截器在执行Action方法前,自动查找请求中一个名为request_locale的参数。如果该参数存在,拦截器就将其作为参数,转换成Locale对象,

并将其设为用户默认的Locale(代表国家/语言环境)。

 除此之外,i18n拦截器还会将上面生成的Locale对象保存在用户Session的名为WW_TRANS_I18N_LOCALE的属性中。一旦用户Session中存在一个名为

WW_TRANS_I18N_LOCALE的属性,则该属性指定的Locale将会作为浏览者的默认Locale。

<%@ page language="java" contentType="text/html; charset=GBK"%>

<%@taglib prefix="s" uri="/struts-tags"%>

<script. type="text/javascript">

function langSelecter_onChanged()

{

 document.getElementById("langForm").submit();

}

</script>

<%-- 设置SESSION_LOCALE为用户session中的WW_TRANS_I18N_LOCALE属性值 --%>

<s:set name="SESSION_LOCALE" value="#session['WW_TRANS_I18N_LOCALE']"/>

<%-- 使用lee.Locales创建locales实例 --%>

<s:bean id="locales" name="lee.Locales">

 <%-- 为locales实例传入current参数值,如果SESSION_LOCALE为空,则返回ValueStack中locale属性值(即用户浏览器设置的Locale) --%>

 <s:param name="current" value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"/>

</s:bean>

<%-- 让用户选择语言的表单 --%>

<form. action="<s:url/>" id="langForm" 

    style="background-color:#bbbbbb; padding-top: 4px; padding-bottom: 4px;">

    <s:text name="languag"/>

 <s:select label="Language" list="#locales.locales" listKey="value" listValue="key"

        value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"

        name="request_locale" id="langSelecter" 

        nchange="langSelecter_onChanged()" theme="simple"/>

</form>

在其他页面中包含该页面:

<s:include value="selectlanguage.jsp"/>

在struts.xml文件中增加Action通配符的配置:

<?xml version="1.0" encoding="GBK"?>

<!DOCTYPE struts PUBLIC

        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

 <constant name="struts.custom.i18n.resources" value="messageResource"/>

 <constant name="struts.i18n.encoding" value="GBK"/>

    <package name="lee" extends="struts-default">

  <!-- 使用通配符定义Action的name -->

  <action name="*">

   <!-- 将请求转发给/WEB-INF/jsp/路径下同名的JSP页面 -->

   <result>/WEB-INF/jsp/{1}.jsp</result>

  </action>

    </package>

</struts>

struts2国际化 java http://www.cnblogs.com/langlang/archive/2010/01/14/1647627.html
struts2国际化 在struts2中需要做国际化的有:

jsp页面的国际化,action错误信息的国际化,转换错误信息的国际化,校验错误信息的国际化

在之前的例子中已经做过和国际化相关的例子了,在struts.xml中配置过

 


view plaincopy to clipboardprint?
<constant name="struts.custom.i18n.resources" value="message"></constant>    

 

 

其中的message就是国际化资源文件的baseNmae。

我们先看看简单的在jsp中进行国际化

在src目录下新建message_en_US.properties,内容为

hello=add user

新建message_zh_CN.properties文件,内容为

hello=\u589e\u52a0\u7528\u6237

然后修改register2.jsp

要想使用国际化显示,可以将信息添加到<s:text></s:text> 标签中,也可以放在<s:i18n></s:i18n> 中,

在这里,先使用标签 <s:text></s:text>

增加以下内容:

 


view plaincopy to clipboardprint?
<s:text name="hello"></s:text>    

 

 

重启服务器后,浏览该页,默认会显示出“增加用户”,可以在IE中打开Internet选项,在常规中选择语言,增加英语(美国)[en-US],然后设置为第一项,刷新就可以看到输出“add user”。


 

action错误的国际化

在message_en_US.properties中增加以下内容

username.invalid=username invalid...

在message_zh_CN.properties中增加以下内容

username.invalid=\u7528\u6237\u540d\u4e0d\u5408\u6cd5...

修改RegisterAction中的validate方法,将错误加到ActionError中,在这里将使用到ActionSupport中的getText方法获得和国际化资源文件相关的信息。

以username验证为例:

 


view plaincopy to clipboardprint?
if (null == username || username.length() < 5 || username.length() > 10) {    
   
    this.addActionError(this.getText("username.invalid"));    
   
}    


这样就从资源文件中读取username.invalid的值,增加到ActionError中。 
 

查看该页面不输入任何数据,提交后就可以看到显示效果了。


 

验证框架的国际化(field级别错误)

在message_en_US.properties文件中增加以下内容

username.xml.invalid=validate information

在message_zh_CN.properties文件中增加以下内容

username.xml.invalid=\u9a8c\u8bc1\u6846\u67b6\u4fe1\u606f

然后修改验证框架,需要将在properties文件中的内容增加到框架中。

以username为例 


 


view plaincopy to clipboardprint?
<field name="username">    
    <field-validator type="requiredstring">    
        <param name="trim">true</param>    
        <message key="username.xml.invalid"></message>    
    </field-validator>    
</field>    


 

在message标签中增加属性key,值为properties文件中的key


标签中key大多是和国际化相关的


 

国际化资源文件的分类

当应用程序很大时,需要国际化的东西会很多,因此需要将国际化资源文件进行分类。

需要知道的是在src中的properties文件是全局资源文件,另外还可以分为包级别的和类级别的

首先看看包级别的

命名规则为package_language_country.properties

新建package_en_US.properties,内容为

username.xml.invalid=package validate information

新建package_zh_CN.properties,内容为

username.xml.invalid=\u5305\u9a8c\u8bc1\u4fe1\u606f

可以看到输出的信息为“包验证信息”,由此可见包级别的国际化资源文件的优先级高于全局国际化资源文件。


 

类级别

新建RegisterAction_en_US.properties,内容为

username.xml.invalid=class validate information

新建RegisterAction_zh_CN.properties,内容为

username.xml.invalid=\u7c7b\u9a8c\u8bc1\u4fe1\u606f

此时可以看到输出的信息为“类验证信息”。

由此可以得到国际化资源文件的优先级


全局<包级别<类级别

另外要进行表单的国际化时,要去掉theme="simple"

在RegisterAction_en_US.properties中增加

username.name=username

在RegisterAction_zh_CN.properties中增加

username.name=\u7528\u6237\u540d

修改表单标签

 


view plaincopy to clipboardprint?
<s:textfield name="username" key="username.name"></s:textfield>    

 

 

注意到key一般是和国际化相关的。

另外除了用

 

 

另外除了用<s:text>这个标签外,还可以使用<s:i18n>这个标签

 


view plaincopy to clipboardprint?
<s:i18n name="temp"></s:i18n>    

 

 

标签中包含name,代表着可以定义资源文件的baseName,如可以定义成temp,那么对应着

temp_en_US.properties和temp_zh_CN.properties这两个资源文件。

 

如定义:

 

 


view plaincopy to clipboardprint?
<s:i18n name="hello">    
    <s:text name="world">    
        <s:param>struts2</s:param>    
    </s:text>    
</s:i18n>    

 

 

注意到可以在<s:text>标签中增加<s:i18n> 标签。

在hello_en_US.properties文件中增加

world=hello {0}

hello_zh_CN.properties中增加

world=\u4f60\u597d,struts2

在struts2的默认拦截器栈中已经定义了i18n拦截器,所以struts2已经是一个国际化的框架了。

struts2会查找从客户端提交的request_locale属性,并存到session中的WW_TRANS_I18N_LOCALE字段

中。

这个<s:text> 标签外,还可以使用<s:i18n> 这个标签  


view plaincopy to clipboardprint?
<s:i18n name="temp"></s:i18n>    

 总结一下显示方法: 

<s:textname="hello"></s:text>

getText("username.invalid") 

<message key="username.xml.invalid"></message>  

<s:textfield name="username" key="username.name"></s:textfield>    

<s:i18n name="temp"></s:i18n>

Struts2的I18n Interceptor(二)【转】 java http://speakoutshaoye.blog.sohu.com/178816963.html
标签: struts2国际化  分类: 软件编程 2011-07-19 12:01国际化是商业系统中不可或缺的一部分,所以无论您学习的是什么Web框架,它都是必须掌握的技能。

其实,Struts 1.x在此部分已经做得相当不错了。它极大地简化了我们程序员在做国际化时所需的工作,例如,如果您要输出一条国际化的信息,只需在代码包中加入 FILE-NAME_xx_XX.properties(其中FILE-NAME为默认资源文件的文件名),然后在struts-config.xml中指明其路径,再在页面用<bean:message>标志输出即可。 

不过,所谓“没有最好,只有更好”。Struts 2.0并没有在这部分止步,而是在原有的简单易用的基础上,将其做得更灵活、更强大。

国际化Hello World
下面让我们看一个例子——HelloWorld。这个例子演示如何根据用户浏览器的设置输出相应的HelloWorld。

在Eclipse创建工程配置开发和运行环境(如果对这个步骤有问题,可以参考我早前的文章《为Struts 2.0做好准备》)。 

在src文件夹中加入struts.properties文件,内容如下: 

struts.custom.i18n.resources=globalMessages
 Struts 2.0有两个配置文件,struts.xml和struts.properties都是放在WEB-INF/classes/下。 
struts.xml用于应用程序相关的配置 
struts.properties用于Struts 2.0的运行时(Runtime)的配置
 

在src文件夹中加入globalMessages_en_US.properties文件,内容如下:

HelloWorld=Hello World!
在src文件夹中加入globalMessages_zh_CN.properties文件,内容如下:

HelloWorld=你好,世界!
 在此想和大家分享一个不错的编写properties文件的Eclipse插件(plugin),有了它我们在编辑一些简体中文、繁体中文等Unicode 文本时,就不必再使用native2ascii编码了。您可以通过Eclipse中的软件升级(Software Update)安装此插件,步骤如下:

1、展开Eclipse的Help菜单,将鼠标移到Software Update子项,在出现的子菜单中点击Find and Install;
2、在Install/Update对话框中选择Search for new features to install,点击Next;
3、在Install对话框中点击New Remote Site;
4、在New Update Site对话框的Name填入“PropEdit”或其它任意非空字符串,在URL中填入http://propedit.sourceforge.jp/eclipse/updates/;
5、在Site to include to search列表中,除上一步加入的site外的其它选项去掉,点击Finsih;
6、在弹出的Updates对话框中的Select the features to install列表中将所有结尾为“3.1.x”的选项去掉(适用于Eclipse 3.2版本的朋友);
7、点击Finish关闭对话框;
8、在下载后,同意安装,再按提示重启Eclipse,在工具条看到形似vi的按钮表示安装成功,插件可用。此时,Eclpise中所有properties文件的文件名前有绿色的P的图标作为标识。  

在WebContent文件夹下加入HelloWorl.jsp文件,内容如下:

<%@ page  contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
    <title>Hello World</title>
</head>
<body>
    <h2><s:text name="HelloWorld"/></h2>
    <h2><s:property value="%{getText('HelloWorld')}"/></h2>
</body>
</html>
发布运行应用程序,在浏览器地址栏中输入http://localhost:8080/Struts2_i18n/HelloWorld.jsp ,出现图1所示页面。

图1 中文输出
将浏览器的默认语言改为“英语(美国)”,刷新页面,出现图2所示页面。

图2 英文输出
上面的例子的做法,与Struts 1.x的做法相似,似乎并不能体现Struts 2.0的优势。不过,我在上面的例子用了两种方法来显示国际化字符串,其输出是相同的。其实,这就是Struts 2.0的一个优势,因为它默认支持EL,所示我们可以用getText方法来简洁地取得国际化字符串。另外更普遍的情况——在使用UI表单标志时,getText可以用来设置label属性,例如:

<s:textfield name="name" label="%{getText('UserName')}"/>
资源文件查找顺序
之所以说Struts 2.0的国际化更灵活是因为它可以能根据不同需要配置和获取资源(properties)文件。在Struts 2.0中有下面几种方法:

使用全局的资源文件,方法如上例所示。这适用于遍布于整个应用程序的国际化字符串,它们在不同的包(package)中被引用,如一些比较共用的出错提示; 
使用包范围内的资源文件。做法是在包的根目录下新建名的package.properties和package_xx_XX.properties文件。这就适用于在包中不同类访问的资源; 
使用Action范围的资源文件。做法为Action的包下新建文件名(除文件扩展名外)与Action类名同样的资源文件。它只能在该Action中访问。如此一来,我们就可以在不同的Action里使用相同的properties名表示不同的值。例如,在ActonOne中 title为“动作一”,而同样用title在ActionTwo表示“动作二”,节省一些命名工夫; 
使用<s:i18n>标志访问特定路径的properties文件。使用方法请参考我早前的文章《常用的Struts 2.0的标志(Tag)介绍》。在您使用这一方法时,请注意<s:i18n>标志的范围。在<s:i18n name="xxxxx">到</s:i18n>之间,所有的国际化字符串都会在名为xxxxx资源文件查找,如果找不到,Struts 2.0就会输出默认值(国际化字符串的名字)。
上面我列举了四种配置和访问资源的方法,它们的范围分别是从大到小,而Struts 2.0在查找国际化字符串所遵循的是特定的顺序,如图3所示:


图3 资源文件查找顺序图
假设我们在某个ChildAction中调用了getText("user.title"),Struts 2.0的将会执行以下的操作: 

查找ChildAction_xx_XX.properties文件或ChildAction.properties; 
查找ChildAction实现的接口,查找与接口同名的资源文件MyInterface.properties; 
查找ChildAction的父类ParentAction的properties文件,文件名为ParentAction.properties; 
判断当前ChildAction是否实现接口ModelDriven。如果是,调用getModel()获得对象,查找与其同名的资源文件; 
查找当前包下的package.properties文件; 
查找当前包的父包,直到最顶层包; 
在值栈(Value Stack)中,查找名为user的属性,转到user类型同名的资源文件,查找键为title的资源; 
查找在struts.properties配置的默认的资源文件,参考例1; 
输出user.title。
参数化国际化字符串
许多情况下,我们都需要在动行时(runtime)为国际化字符插入一些参数,例如在输入验证提示信息的时候。在Struts 2.0中,我们通过以下两种方法做到这点:

在资源文件的国际化字符串中使用OGNL,格式为${表达式},例如:

validation.require=${getText(fileName)} is required
使用java.text.MessageFormat中的字符串格式,格式为{ 参数序号(从0开始), 格式类形(number | date | time | choice), 格式样式},例如:

validation.between=Date must between {0, date, short} and {1, date, short}
在显示这些国际化字符时,同样有两种方法设置参数的值:

使用标志的value0、value1...valueN的属性,如:

<s:text name="validation.required" value0="User Name"/>
使用param子元素,这些param将按先后顺序,代入到国际化字符串的参数中,例如:

<s:text name="validation.required">
   <s:param value="User Name"/>
</s:text>
让用户方便地选择语言
开发国际化的应用程序时,有一个功能是必不可少的——让用户快捷地选择或切换语言。在Struts 2.0中,通过ActionContext.getContext().setLocale(Locale arg)可以设置用户的默认语言。不过,由于这是一个比较普遍的应用场景(Scenario),所以Struts 2.0为您提供了一个名i18n的拦截器(Interceptor),并在默认情况下将其注册到拦截器链(Interceptor chain)中。它的原理为在执行Action方法前,i18n拦截器查找请求中的一个名为"request_locale"的参数。如果其存在,拦截器就将其作为参数实例化Locale对象,并将其设为用户默认的区域(Locale),最后,将此Locale对象保存在session的名为 “WW_TRANS_I18N_LOCALE”的属性中。

下面,我将提供一完整示例演示它的使用方法。

package tutorial;

import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;

publicclass Locales {
   public Map<String, Locale> getLocales() {
       Map<String, Locale> locales =new Hashtable<String, Locale>(2);
       locales.put("American English", Locale.US);
       locales.put("Simplified Chinese", Locale.CHINA);
       return locales;
   }
}
tutorial/Locales.java


<%@taglib prefix="s" uri="/struts-tags"%>
<script type="text/javascript">
<!--
    function langSelecter_onChanged() {
        document.langForm.submit();
    }
//-->
</script>
<s:set name="SESSION_LOCALE" value="#session['WW_TRANS_I18N_LOCALE']"/>
<s:bean id="locales" name="tutorial.Locales"/>
<form action="<s:url includeParams="get" encode="true"/>" name="langForm" 
    style="background-color: powderblue; padding-top: 4px; padding-bottom: 4px;">
    Language: <s:select label="Language" 
        list="#locales.locales" listKey="value"    listValue="key"
        value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"
        name="request_locale" id="langSelecter" 
        onchange="langSelecter_onChanged()" theme="simple"/>
</form>
LangSelector.jsp
上述代码的原理为,LangSelector.jsp先实例化一个Locales对象,并把对象的Map类型的属性locales赋予下拉列表(select) 。如此一来,下拉列表就获得可用语言的列表。大家看到LangSelector有<s:form>标志和一段Javascript脚本,它们的作用就是在用户在下拉列表中选择了后,提交包含“reqeust_locale”变量的表单到Action。在打开页面时,为了下拉列表的选中的当前区域,我们需要到session取得当前区域(键为“WW_TRANS_I18N_LOCALE”的属性),而该属性在没有设置语言前是为空的,所以通过值栈中locale属性来取得当前区域(用户浏览器所设置的语言)。

你可以把LangSelector.jsp作为一个控件使用,方法是在JSP页面中把它包含进来,代码如下所示: 
<s:include value="/LangSelector.jsp"/>

在例1中的HellloWorld.jsp中<body>后加入上述代码,并在struts.xml中新建Action,代码如下:

<action name="HelloWorld">
    <result>/HelloWorld.jsp</result>
</action>

或者,如果你多个JSP需要实现上述功能,你可以使用下面的通用配置,而不是为每一个JSP页面都新建一个Action。

<action name="*">
    <result>/{1}.jsp</result>
</action>

分布运行程序,在浏览器的地址栏中输入http://localhost:8080/Struts2_i18n/HelloWorld.action,出现图4所示页面:

图3 HelloWorld.action

在下拉列表中,选择“American English”,出现图5所示页面:

图4 HelloWorld.action
 可能大家会问为什么一定要通过Action来访问页面呢?
你可以试一下不用Action而直接用JSP的地址来访问页面,结果会是无论你在下拉列表中选择什么,语言都不会改变。这表示不能正常运行的。其原因为如果直接使用JSP访问页面,Struts 2.0在web.xml的配置的过滤器(Filter)就不会工作,所以拦截器链也不会工作。 
import com.sun.image.codec.jpeg.JPEGCodec出错 java http://blog.sina.com.cn/s/blog_6a5f09820101249i.html
import com.sun.image.codec.jpeg.JPEGCodec出错

在Eclipse中处理图片,需要引入两个包:
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
报错:
Access restriction: The type JPEGImageEncoder is not accessible due to restriction on required library C:Javajre1.6.0_07librt.jar


此时解决办法:
Eclipse默认把这些受访问限制的API设成了ERROR。只要把Windows-Preferences-Java-Complicer-Errors/Warnings里面的Deprecated and restricted API中的Forbidden references(access rules)选为Warning就可以编译通过。
java图片压缩 java
package additional.util;

import java.awt.Color;  
import java.awt.Graphics2D;  
import java.awt.Image;  
import java.awt.image.BufferedImage;  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.util.Calendar;  
import java.util.Date;  

import org.apache.log4j.Logger;

import com.sun.image.codec.jpeg.ImageFormatException;  
import com.sun.image.codec.jpeg.JPEGCodec;  
import com.sun.image.codec.jpeg.JPEGEncodeParam;  
import com.sun.image.codec.jpeg.JPEGImageEncoder;  
  
/** 
 *   图片压缩工具类 提供的方法中可以设定生成的 缩略图片的大小尺寸、压缩尺寸的比例、图片的质量等 
 */  
public class ImageUtil {  
	
	private static Logger log=Logger.getLogger(ImageUtil.class);
  
    /** 
     * * 图片文件读取 
     *  
     * @param srcImgPath 
     * @return 
     */  
    public  static BufferedImage InputImage(String srcImgPath) {  
  
        BufferedImage srcImage = null;  
        try {  
            // 构造BufferedImage对象   
            File file = new File(srcImgPath);  
            FileInputStream in = new FileInputStream(file);  
            byte[] b = new byte[5];  
            in.read(b);  
            srcImage = javax.imageio.ImageIO.read(file);  
        } catch (IOException e) {  
            log.error("读取图片文件出错!",e);
        }  
        return srcImage;  
    }  
  
    /** 
     * * 将图片按照指定的图片尺寸、源图片质量压缩(默认质量为1) 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param new_w 
     *            :压缩后的图片宽 
     * @param new_h 
     *            :压缩后的图片高 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            int new_w, int new_h) {  
        return Tosmallerpic(srcImgPath, outImgPath, new_w, new_h, 1F);  
    }  
  
    /** 
     * 将图片按照指定的尺寸比例、源图片质量压缩(默认质量为1) 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param ratio 
     *            :压缩后的图片尺寸比例 
     * @param per 
     *            :百分比 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            float ratio) {  
        return Tosmallerpic(srcImgPath, outImgPath, ratio, 1F);  
    }  
  
    /** 
     * 将图片按照指定长或者宽的最大值来压缩图片(默认质量为1) 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param maxLength 
     *            :长或者宽的最大值 
     * @param per 
     *            :图片质量 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            int maxLength) {  
        return Tosmallerpic(srcImgPath, outImgPath, maxLength, 1F);  
    }  
  
    /** 
     * * 将图片按照指定的图片尺寸、图片质量压缩 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param new_w 
     *            :压缩后的图片宽 
     * @param new_h 
     *            :压缩后的图片高 
     * @param per 
     *            :百分比 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            int new_w, int new_h, float per) {  
        // 得到图片   
        BufferedImage src = InputImage(srcImgPath);  
        int old_w = src.getWidth();  
        // 得到源图宽   
        int old_h = src.getHeight();  
        // 得到源图长   
        // 根据原图的大小生成空白画布   
        BufferedImage tempImg = new BufferedImage(old_w, old_h,  
                BufferedImage.TYPE_INT_RGB);  
        // 在新的画布上生成原图的缩略图   
        Graphics2D g = tempImg.createGraphics();  
        g.setColor(Color.white);  
        g.fillRect(0, 0, old_w, old_h);  
        g.drawImage(src, 0, 0, old_w, old_h, Color.white, null);  
        g.dispose();  
        BufferedImage newImg = new BufferedImage(new_w, new_h,  
                BufferedImage.TYPE_INT_RGB);  
        newImg.getGraphics().drawImage(  
                tempImg.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0,  
                0, null);  
        // 调用方法输出图片文件   
        OutImage(outImgPath, newImg, per); 
        return newImg;
    }  
  
    /** 
     * * 将图片按照指定的尺寸比例、图片质量压缩 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param ratio 
     *            :压缩后的图片尺寸比例 
     * @param per 
     *            :百分比 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            float ratio, float per) {  
        // 得到图片   
        BufferedImage src = InputImage(srcImgPath);  
        int old_w = src.getWidth();  
        // 得到源图宽   
        int old_h = src.getHeight();  
        // 得到源图长   
        int new_w = 0;  
        // 新图的宽   
        int new_h = 0;  
        // 新图的长   
        BufferedImage tempImg = new BufferedImage(old_w, old_h,  
                BufferedImage.TYPE_INT_RGB);  
        Graphics2D g = tempImg.createGraphics();  
        g.setColor(Color.white);  
        // 从原图上取颜色绘制新图g.fillRect(0, 0, old_w, old_h);   
        g.drawImage(src, 0, 0, old_w, old_h, Color.white, null);  
        g.dispose();  
        // 根据图片尺寸压缩比得到新图的尺寸
        new_w = (int) Math.round(old_w * ratio);   
        new_h = (int) Math.round(old_h * ratio);  
        BufferedImage newImg = new BufferedImage(new_w, new_h,  
                BufferedImage.TYPE_INT_RGB);  
        newImg.getGraphics().drawImage(  
                tempImg.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0,  
                0, null);  
        // 调用方法输出图片文件
        OutImage(outImgPath, newImg, per);  
        return newImg;
    }  
  
    /** 
     * * 指定长或者宽的最大值来压缩图片 
     *  
     * @param srcImgPath 
     *            :源图片路径 
     * @param outImgPath 
     *            :输出的压缩图片的路径 
     * @param maxLength 
     *            :长或者宽的最大值 
     * @param per 
     *            :图片质量 
     */  
    public static BufferedImage Tosmallerpic(String srcImgPath, String outImgPath,  
            int maxLength, float per) {  
        // 得到图片   
        BufferedImage src = InputImage(srcImgPath);  
        int old_w = src.getWidth();  
        // 得到源图宽   
        int old_h = src.getHeight();  
        // 得到源图长   
        int new_w = 0;  
        // 新图的宽   
        int new_h = 0;  
        // 新图的长   
        BufferedImage tempImg = new BufferedImage(old_w, old_h,  
                BufferedImage.TYPE_INT_RGB);  
        Graphics2D g = tempImg.createGraphics();  
        g.setColor(Color.white);  
        // 从原图上取颜色绘制新图   
        g.fillRect(0, 0, old_w, old_h);  
        g.drawImage(src, 0, 0, old_w, old_h, Color.white, null);  
        g.dispose();  
        // 根据图片尺寸压缩比得到新图的尺寸   
        if (old_w > old_h) {  
            // 图片要缩放的比例   
            new_w = maxLength;  
            new_h = (int) Math.round(old_h * ((float) maxLength / old_w));  
        } else {  
            new_w = (int) Math.round(old_w * ((float) maxLength / old_h));  
            new_h = maxLength;  
        }  
        BufferedImage newImg = new BufferedImage(new_w, new_h,  
                BufferedImage.TYPE_INT_RGB);  
        newImg.getGraphics().drawImage(  
                tempImg.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0,  
                0, null);  
        // 调用方法输出图片文件   
        OutImage(outImgPath, newImg, per);  
        return newImg;
    }  
  
    /** 
     * * 将图片文件输出到指定的路径,并可设定压缩质量 
     *  
     * @param outImgPath 
     * @param newImg 
     * @param per 
     */  
    private static void OutImage(String outImgPath, BufferedImage newImg,  
            float per) {  
        // 判断输出的文件夹路径是否存在,不存在则创建   
        File file = new File(outImgPath);  
        if (!file.getParentFile().exists()) {  
            file.getParentFile().mkdirs();  
        }// 输出到文件流   
        try {  
            FileOutputStream newimage = new FileOutputStream(outImgPath);  
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(newimage);  
            JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(newImg);  
            // 压缩质量   
            jep.setQuality(per, true);  
            encoder.encode(newImg, jep);  
            newimage.close();  
        } catch (FileNotFoundException e) {  
            // TODO Auto-generated catch blocke.printStackTrace();   
        } catch (ImageFormatException e) {  
            // TODO Auto-generated catch blocke.printStackTrace();   
        } catch (IOException e) {  
            // TODO Auto-generated catch blocke.printStackTrace();   
        }  
    }  
  
//    public static void main(String args[]) {  
//        String f = "d:/img/";  
//        File file = new File(f);  
//        if (file.exists()) {  
//            File[] filelist = file.listFiles();  
//            for (int i = 0; i < filelist.length; i++) {  
//                File fi = filelist[i];  
//                System.out.println(fi.length());  
//                String n = filelist[i].getName();  
//                // Tosmallerpic(f, filelist[i], "_ratio_small", n,   
//                // 0.303,(float)0.7);   
//                // Tosmallerpic(f, filelist[i], "_ratio_smaller", n,   
//                // 0.083,(float)0.7);   
//            }  
//        }  
//        String srcImg = "d:/img/2.jpg";  
//        String tarDir = "d:/img/newImg/";  
//        long startTime = new Date().getTime();  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_1.jpg", 400);  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_2.jpg", 0.5F);  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_3.jpg", 400, 500);  
//        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_11.jpg", 400, 0.7F);  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_22.jpg", 0.5F, 0.8F);  
////        Tosmallerpic(srcImg, tarDir + "car_1_maxLength_33.jpg", 400, 500, 0.2F);  
//        System.out.println(new Date().getTime() - startTime);  
//    }  
}  
<context-param>与<init-param>的区别与作用 java http://www.cnblogs.com/hzj-/articles/1689836.html
<context-param>与<init-param>的区别与作用

spring   2009-11-04 16:49    阅读39    评论0  字号: 大  中  小<context-param>的作用:
web.xml的配置中<context-param>配置作用
1. 启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件web.xml.读两个节点: <listener></listener> 和 <context-param></context-param>2.紧接着,容器创建一个ServletContext(上下文),这个WEB项目所有部分都将共享这个上下文.3.容器将<context-param></context-param>转化为键值对,并交给ServletContext.4.容器创建<listener></listener>中的类实例,即创建监听.5.在监听中会有contextInitialized(ServletContextEvent args)初始化方法,在这个方法中获得ServletContext = ServletContextEvent.getServletContext();
context-param的值 = ServletContext.getInitParameter("context-param的键");6.得到这个context-param的值之后,你就可以做一些操作了.注意,这个时候你的WEB项目还没有完全启动完成.这个动作会比所有的Servlet都要早.
换句话说,这个时候,你对<context-param>中的键值做的操作,将在你的WEB项目完全启动之前被执行.7.举例.你可能想在项目启动之前就打开数据库.
那么这里就可以在<context-param>中设置数据库的连接方式,在监听类中初始化数据库的连接.8.这个监听是自己写的一个类,除了初始化方法,它还有销毁方法.用于关闭应用前释放资源.比如说数据库连接的关闭.如:
<!-- 加载spring的配置文件 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml,/WEB-INF/action-servlet.xml,/WEB-INF/jason-servlet.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>又如: --->自定义context-param,且自定义listener来获取这些信息<context-param>
    <param-name>urlrewrite</param-name>
    <param-value>false</param-value>
</context-param>
<context-param>
    <param-name>cluster</param-name>
    <param-value>false</param-value>
</context-param>
<context-param>
    <param-name>servletmapping</param-name>
    <param-value>*.bbscs</param-value>
</context-param>
<context-param>
    <param-name>poststoragemode</param-name>
    <param-value>1</param-value>
</context-param>
<listener>
    <listener-class>com.laoer.bbscs.web.servlet.SysListener</listener-class>
</listener>public class SysListener extends HttpServlet implements ServletContextListener {private static final Log logger = LogFactory.getLog(SysListener.class);public void contextDestroyed(ServletContextEvent sce) {   //用于在容器关闭时,操作
}//用于在容器开启时,操作public void contextInitialized(ServletContextEvent sce) {
   String rootpath = sce.getServletContext().getRealPath("/");
   System.out.println("-------------rootPath:"+rootpath);   if (rootpath != null) {
    rootpath = rootpath.replaceAll("\\\\", "/");
   } else {
    rootpath = "/";
   }
   if (!rootpath.endsWith("/")) {
    rootpath = rootpath + "/";
   }
   Constant.ROOTPATH = rootpath;
   logger.info("Application Run Path:" + rootpath);
   String urlrewrtie = sce.getServletContext().getInitParameter("urlrewrite");
   boolean burlrewrtie = false;
   if (urlrewrtie != null) {
    burlrewrtie = Boolean.parseBoolean(urlrewrtie);
   }
   Constant.USE_URL_REWRITE = burlrewrtie;
   logger.info("Use Urlrewrite:" + burlrewrtie);
   其它略之....    }}
   /*最终输出
   -------------rootPath:D:\tomcat_bbs\webapps\BBSCS_8_0_3\
   2009-06-09 21:51:46,526 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]Application Run Path:D:/tomcat_bbs/webapps/BBSCS_8_0_3/
   2009-06-09 21:51:46,526 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]Use Urlrewrite:true
   2009-06-09 21:51:46,526 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]Use Cluster:false
   2009-06-09 21:51:46,526 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]SERVLET MAPPING:*.bbscs
   2009-06-09 21:51:46,573 [com.laoer.bbscs.web.servlet.SysListener]-[INFO]Post Storage Mode:1
   */context-param和init-param区别
web.xml里面可以定义两种参数:
(1)application范围内的参数,存放在servletcontext中,在web.xml中配置如下:
<context-param>
           <param-name>context/param</param-name>
           <param-value>avalible during application</param-value>
</context-param>(2)servlet范围内的参数,只能在servlet的init()方法中取得,在web.xml中配置如下:
<servlet>
    <servlet-name>MainServlet</servlet-name>
    <servlet-class>com.wes.controller.MainServlet</servlet-class>
    <init-param>
       <param-name>param1</param-name>
       <param-value>avalible in servlet init()</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
</servlet>在servlet中可以通过代码分别取用:
package com.wes.controller;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;public class MainServlet extends HttpServlet ...{    public MainServlet() ...{
        super();
     }
    public void init() throws ServletException ...{
         System.out.println("下面的两个参数param1是在servlet中存放的");
         System.out.println(this.getInitParameter("param1"));
         System.out.println("下面的参数是存放在servletcontext中的");
        System.out.println(getServletContext().getInitParameter("context/param"));
      }
}第一种参数在servlet里面可以通过getServletContext().getInitParameter("context/param")得到
第二种参数只能在servlet的init()方法中通过this.getInitParameter("param1")取得.
session监听器 java http://hi.baidu.com/hero8_8_8/blog/item/e2068c8fc2f792e6f11f36e7.html
session监听器2009-03-02 14:14servlet中对session的监听有很多接口,功能很灵活,最常用的是监听Session和Attribute。这里要澄清一下概念,servlet中的session监听和Attribute监听含义有差别,session监听指的不是我们一般所理解的放置一个session或者销毁一个session,这是Attribute监听的功能,因为servlet中放置session的语法是session.setAttribute(“session名”,要放入的对象)。而session监听,监听的是HTTP连接,只要有用户与server连接,就算连接的是一个空白的jsp页面,也会触发session事件,所以此处的session实际上指的是connection,用来统计当前在线用户数最合适了。不知道我说清楚了没有。下面分别讲解这两种监听方式。 

1、 session监听 

首先编写一个session监听类,实作HttpSessionListener接口,它的作用是计算当前有多少个在线用户: 
/* 
*@Author bromon 
*2004-6-12 
*/ 
package org.bromon.test; 

import javax.servlet.*; 
import javax.servlet.http.*; 

public class SessionCount implements HttpSessionListener{ 
private static int count=0; 

public void sessionCreated(HttpSessionEvent se){ 
count++; 
System.out.println(“session创建:”+new java.util.Date()); 
} 

public void sessionDestroyed(HttpSessionEvent se){ 
count--; 
System.out.println(“session销毁:”+new java.util.Date()); 
} 

public static int getCount(){ 
return(count); 
} 
} 

怎么样,是不是一目了然?count被定义为static,是因为要保证整个系统只有这一个count。如果你实在不放心,可以把它写成一个单例类。 

然后在web.xml中声明这个监听器: 
<listener> 
<listener-class> 
org.bromon.test.SessionCount 
</listener-class> 
</listener> 

编写一个测试页面test.jsp,内容是获得count: 
<% 
int count=org.bromon.test.SessionCount.getCount(); 
out.println(count); 
%> 
需要注意的是,这里根本不涉及任何session的操作。重启动App server,试着连接test.jsp,可以看到监听器已经开始工作。 

2、 Attribute监听 
例如一个站内消息系统,肯定要获得所有登陆者的ID,才有可能互发消息。这就涉及Attribute监听。假设我们写了个用户登陆的模块,用户通过身份验证之后会产生一个session,保存它的相关信息,比如: 
//check.jsp 
<% 
String name=request.getParameter(“name”); 
Name=new String(name.getBytes(“ISO8859-1”)); 
session.setAttribute(“user”,name); 
%> 

做过jsp的兄弟应该对这段代码再熟悉不过了,下面写个监听器来监听用户登陆,把所有用户的ID保存到一个List当中,这个监听器实作HttpSessionAttributeListener接口: 

/* 
*@Author bromon 
*2004-6-12 
*/ 
package org.bromon.test; 

import javax.servlet.*; 
import javax.servlet.http.*; 
import java.util.*; 

public class OnlineList implements HttpSessionAttributeListener{ 
private static List list=new ArrayList(); 

public void attributeAdded(HttpSessionBindingEvent se){ 
if("user".equals(se.getName())){ 
list.add(se.getValue()); 
} 
} 

public void attributeRemoved(HttpSessionBindingEvent se){ 
if("user".equals(se.getName())){ 
list.remove(se.getValue()); 
} 
} 

public void attributeReplaced(HttpSessionBindingEvent se){} 

public static List getList(){ 
return(list); 
} 
} 

写个简单的jsp来得到用户列表: 
<% 
java.util.List list=org.bromon.test.OnlineList.getList(); 
out.println("共有"+list.size()+"名用户已登陆:"); 
for(int I=0;I <lise.size();i++){ 
out.println(list.get(i)); 
} 
%>  
ComboBox默认值显示 jquery
ComboBox默认值显示  2008-12-23 22:43:27|  分类: ext学习笔记 |  标签: |字号大中小 订阅 .

今天 搞了一下午,哎!也没搞出一个东西来,呵呵,让那个ComboBox搞得我很难受,在网上找了好长时间,终于搞定了,呵呵,代码贴在此:
第一种方式(这种方式,我在使用时,如果数据不是从后台传过来的就可以,如果是从后台传过来的就不行了,要使用第二种方式)、
var CountryCode = [
    ['93','Afghanistan(93)'],
    ['355','Albania (355)'],
    ['213','Algeria (213)'],
    ['684','American Samoa (684)'],
    ['376','Andorra (376)'],
    ['244','Angola (244)']
 ]
var checkupperson = new Ext.form.ComboBox({
            fieldLabel: '审核人',
            name:'checkupperson',
            forceSelection: true,
            listWidth: 200,
            store: new Ext.data.SimpleStore({
                fields: ['value', 'text'],
                data : CountryCode
                }),
            valueField:'value',
            displayField:'text',
            typeAhead: true,
            mode: 'local',
            triggerAction: 'all',
            selectOnFocus:true,//用户不能自己输入,只能选择列表中有的记录
            allowBlank:false,
            listeners:{
             select:function(){
              alert(this.value);
             }
            }
        });
  checkupperson.on('beforerender',function(){
        this.value=376;
  });
第二种方式:
  var depCombo = new Ext.form.ComboBox({
     fieldLabel:'部门',
              allowBlank: false,
     allowNegative: false,
     triggerAction: 'all',
     displayField :'depName',
     valueField :'depId',
     id:'test',
             store: new Ext.data.JsonStore({
      fields: ['depId','depName'],
      url: '../combobox.do?action=getDepartmentComboBox',
      autoLoad : true,
      listeners :{
        load:function(){
        Ext.getCmp('test').setValue(17);
        }
       } //在此加一个这个玩意,就可以了,呵呵,是在jsonstore中加的,注意
      }),
      
             editable :false
        });
呵呵,该回家了,不早了,嘿嘿......................................
嵌入在网页中的正在播放的MediaPlayer不响应zIndex属性,始终显示在顶层 jquery
Q:嵌入在网页中的正在播放的MediaPlayer不响应zIndex属性,始终显示在顶层  2011-03-09 19:47:58|  分类: js |  标签:mediaplayer  zindex  顶层遮盖   |字号大中小 订阅 .

在以Flash流媒体的主流的业界,不知道还有多少人会用MediaPlayer。


页面中使用到了绝对定位,而且元素还是可以拖动的,这样,拖着拖着就被这个正在播放中的MediaPlayer给挡住了,给他设置了相应的zIndex还是不行,搜索了一下网上的解决方案,也是说只需要把windowlessVideo属性的值设置为true就行,结果改了之后还是不行,甚至考虑用Iframe来解决,但是还是一样iframe中的视频依然会挡住最外层。再次搜索一下,居然在<object>又发现了一个windowlessVideo属性,而它的值正是0.删掉它后,一切终于正常了。


如下必须将windowlessVideo属性的值设置为true.
<div id="player" style="display:block"> 
<OBJECT ID="MediaPlayer" classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" 
codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,7,1112" 
standby="Loading Microsoft Windows Media Player components..." type="application/x-oleobject"> 
<PARAM NAME="URL" VALUE="http://media_src/play_point"> 
<PARAM NAME="ShowControls" VALUE="0"/><PARAM NAME="ShowDisplay" VALUE="0"/> 
<PARAM NAME="ShowCaptioning" VALUE="0"/><PARAM NAME="ShowStatusBar" VALUE="0"/> 
<PARAM NAME="AutoSize" VALUE="1"/><PARAM NAME="windowlessVideo" VALUE="true"> 
<PARAM NAME="rate" VALUE="1"/><PARAM NAME="balance" VALUE="0"><PARAM NAME="currentPosition" VALUE="0"/> 
<PARAM NAME="defaultFrame" VALUE=""/><PARAM NAME="playCount" VALUE="1"><PARAM NAME="autoStart" VALUE="0"/> 
<PARAM NAME="currentMarker" VALUE="0"/><PARAM NAME="invokeURLs" VALUE="0"><PARAM NAME="baseURL" VALUE=""/> 
<PARAM NAME="mute" VALUE="0"/><PARAM NAME="uiMode" VALUE="None"><param name="volume" value="75"/> 
<PARAM NAME="stretchToFit" VALUE="-1"/><PARAM NAME="enabled" VALUE="-1"/> 
<PARAM NAME="enableContextMenu" VALUE="-1"/><PARAM NAME="fullScreen" VALUE="0"><PARAM NAME="SAMIStyle" VALUE=""/> 
<PARAM NAME="SAMILang" VALUE=""/><PARAM NAME="SAMIFilename" VALUE=""/><PARAM NAME="captioningID" VALUE="mscc"/><PARAM NAME="enableErrorDialogs" VALUE="0"/> 
</OBJECT></div> 

mediapalyer 脚本命令 jquery
下面是新式播放器代码,相对以前的来说要简单很多:
<object id="player" height="64" width="260" classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6">
<param NAME="AutoStart" VALUE="-1">
<!--是否自动播放-->
<param NAME="Balance" VALUE="0">
<!--调整左右声道平衡,同上面旧播放器代码-->
<param name="enabled" value="-1">
<!--播放器是否可人为控制-->
<param NAME="EnableContextMenu" VALUE="-1">
<!--是否启用上下文菜单-->
<param NAME="url" VALUE="1.wma">
<!--播放的文件地址-->
<param NAME="PlayCount" VALUE="1">
<!--播放次数控制,为整数-->
<param name="rate" value="1">
<!--播放速率控制,1为正常,允许小数,1.0-2.0-->
<param name="currentPosition" value="0">
<!--控件设置:当前位置-->
<param name="currentMarker" value="0">
<!--控件设置:当前标记-->
<param name="defaultFrame" value="">
<!--显示默认框架-->
<param name="invokeURLs" value="0">
<!--脚本命令设置:是否调用URL-->
<param name="baseURL" value="">
<!--脚本命令设置:被调用的URL-->
<param name="stretchToFit" value="0">
<!--是否按比例伸展-->
<param name="volume" value="50">
<!--默认声音大小0%-100%,50则为50%-->
<param name="mute" value="0">
<!--是否静音-->
<param name="uiMode" value="mini">
<!--播放器显示模式:Full显示全部;mini最简化;None不显示播放控制,只显示视频窗口;invisible全部不显示-->
<param name="windowlessVideo" value="0">
<!--如果是0可以允许全屏,否则只能在窗口中查看-->
<param name="fullScreen" value="0">
<!--开始播放是否自动全屏-->
<param name="enableErrorDialogs" value="-1">
<!--是否启用错误提示报告-->
<param name="SAMIStyle" value>
<!--SAMI样式-->
<param name="SAMILang" value>
<!--SAMI语言-->
<param name="SAMIFilename" value>
<!--字幕ID-->
</object>

Real Player 网页播放器 参数含义

参数:autostart 属性:True或是False 作用:指定是否自动播放指定的源文件
参数:backgroundcolor 属性:任何用符号“#”开头的16进制数值或是任何预定义的颜色作用:指定图像窗口的背景颜色
参数:center 属性:True或是False 作用:指定片断使用初始编码大小播放,并且在图像窗口的中央。
参数:classid 属性:"clsid:CFCDAA03-8BE4-1lcf-B84B0020AFBBCCFA:** 作用:用于指定ActiveX控件的唯一的字符串标示,可以认出嵌入的RealPalyer播放器。
参数:console 属性:任何字符串作用:可以将各种不同的RealPlayer控制聚集在网页上,这样它们可以交互使用或是保持独立,而且互相不影响
参数:controls 属性:ImageWindow,All,ControlPanel,PlavButton,PlayOnlyButton, PauseButton,StopButton,FFCtrl,RWCtrl,MuteCtrl,MuteVolume,VolumeSlider,PositionSlider,TACCtrl,HomeCtrl,InfoVolumePanel,InfoPanel,StatusBar,StatusField,PositionField 作用:可以让你指定那些控制是可见的。
参数:height 属性:任何整数值作用:指定RealPlayer元素的高度,单位:像素
参数:id 属性:任何字符串作用:为标签中的RealPlayer元素指定名字。
参数:imagestatus 属性:True或是False 作用:指定是否在图像窗口中显示状态信息,默认值是true
参数:loop 属性:True或是False 作用:可以让你指定片断是否无限循环
参数:maintainaspect 属性:True或是False 作用:默认RealPlayer拉伸所有的片断来充满整个图像窗口。
参数:name 属性:任何字符串作用:为标签中的RealPlayer元素指定名字(在标签中使用id)
参数:nojava 属性:True或是False 作用:避免启动Java虚拟机
参数:nolabels 属性:True或是False 作用:可以禁止显示标题或是版权信息(realplayer5.0以上时,它是垃圾...)
参数:nologo 属性:True或是False 作用:避免RealPlayer启动时在图像窗口中显示
参数:numloop 属性:任何整数值作用:让你能够指定文件片循环的次数,不需要参数loop
参数:prefetch 属性:True或是False 作用:指定在播放前,RealPlayer是否可以获得流描述信息,默认值是False
参数:region 属性:任何字符串作用:同SMIL一起使用。允许你指定使用HTML代替SMIL
参数:scriptcallbacks 属性:用逗号分割的列表作用:指定浏览器的回调监控(好高级的东东!)
参数:shuffle 属性:True或是False 作用:同多文件片的ram文件或是SMIL文件一起使用。可以让RealPlayer随机播放列表中的文件
参数:src 属性:任何合法的相对或是完整的URL 作用:指定播放的文件或是源文件的地址
参数:type 属性:字符串作用:为嵌入插件指定MIME类型
参数:width 属性:任何整数值作用:指定RealPlayer元素的宽度

RealPlayer的一些函数、方法和过程

这是 Real Player ActiveX Control Library (Version 1.0) 的所有函数与方法,有兴趣可以研究一下。

function GetSource: WideString;
procedure SetSource(const lpszNewValue: WideString);
function GetConsole: WideString;
procedure SetConsole(const lpszNewValue: WideString);
function GetControls: WideString;
procedure SetControls(const lpszNewValue: WideString);
function GetNoLabels: WordBool;
procedure SetNoLabels(bNewValue: WordBool);
function GetAutoStart: WordBool;
procedure SetAutoStart(bNewValue: WordBool);
function GetAutoGotoURL: WordBool;
procedure SetAutoGotoURL(bNewValue: WordBool);
function GetVolume: Smallint;
procedure SetVolume(nVol: Smallint);
function GetMute: WordBool;
procedure SetMute(bMute: WordBool);
function GetLoop: WordBool;
procedure SetLoop(bVal: WordBool);
function GetImageStatus: WordBool;
procedure SetImageStatus(bEnable: WordBool);
function GetPacketsTotal: Integer;
function GetPacketsReceived: Integer;
function GetPacketsOutOfOrder: Integer;
function GetPacketsMissing: Integer;
function GetPacketsEarly: Integer;
function GetPacketsLate: Integer;
function GetBandwidthAverage: Integer;
function GetBandwidthCurrent: Integer;
procedure DoPlayPause;
procedure DoStop;
procedure DoNextItem;
procedure DoPrevItem;
function CanPlayPause: WordBool;
function CanStop: WordBool;
function HasNextItem: WordBool;
function HasPrevItem: WordBool;
function HasNextEntry: WordBool;
function HasPrevEntry: WordBool;
procedure DoNextEntry;
procedure DoPrevEntry;
procedure AboutBox;
procedure EditPreferences;
procedure HideShowStatistics;
function IsStatisticsVisible: WordBool;
procedure DoGotoURL(const url: WideString; const target: WideString);
procedure DoPlay;
procedure DoPause;
function GetPosition: Integer;
function GetPlayState: Integer;
function GetLength: Integer;
function GetTitle: WideString;
function GetAuthor: WideString;
function GetCopyright: WideString;
function GetClipWidth: Integer;
function GetClipHeight: Integer;
function CanPlay: WordBool;
function CanPause: WordBool;
procedure SetPosition(lPosition: Integer);
function GetNumLoop: Integer;
procedure SetNumLoop(lVal: Integer);
function GetCenter: WordBool;
procedure SetCenter(bVal: WordBool);
function GetNoLogo: WordBool;
procedure SetNoLogo(bVal: WordBool);
function GetMaintainAspect: WordBool;
procedure SetMaintainAspect(bVal: WordBool);
function GetBackgroundColor: WideString;
procedure SetBackgroundColor(const pVal: WideString);
function GetStereoState: WordBool;
function GetLiveState: WordBool;
function GetShowStatistics: WordBool;
procedure SetShowStatistics(bVal: WordBool);
function GetShowPreferences: WordBool;
procedure SetShowPreferences(bVal: WordBool);
function GetShowAbout: WordBool;
procedure SetShowAbout(bVal: WordBool);
function GetOriginalSize: WordBool;
procedure SetOriginalSize;
function GetDoubleSize: WordBool;
procedure SetDoubleSize;
function GetFullScreen: WordBool;
procedure SetFullScreen;
function GetEnableContextMenu: WordBool;
procedure SetEnableContextMenu(bVal: WordBool);
function GetEnableOriginalSize: WordBool;
procedure SetEnableOriginalSize(bVal: WordBool);
function GetEnableDoubleSize: WordBool;
procedure SetEnableDoubleSize(bVal: WordBool);
function GetEnableFullScreen: WordBool;
procedure SetEnableFullScreen(bVal: WordBool);
function GetEnableMessageBox: WordBool;
procedure SetEnableMessageBox(bVal: WordBool);
procedure SetTitle(const pVal: WideString);
procedure SetAuthor(const pVal: WideString);
procedure SetCopyright(const pVal: WideString);
function GetWantKeyboardEvents: WordBool;
procedure SetWantKeyboardEvents(bWantsEvents: WordBool);
function GetWantMouseEvents: WordBool;
procedure SetWantMouseEvents(bWantsEvents: WordBool);
function GetNumEntries: Smallint;
function GetCurrentEntry: Smallint;
function GetEntryTitle(uEntryIndex: Smallint): WideString;
function GetEntryAuthor(uEntryIndex: Smallint): WideString;
function GetEntryCopyright(uEntryIndex: Smallint): WideString;
function GetEntryAbstract(uEntryIndex: Smallint): WideString;
procedure SetCanSeek(bCanSeek: WordBool);
function GetCanSeek: WordBool;
function GetBufferingTimeElapsed: Integer;
function GetBufferingTimeRemaining: Integer;
function GetConnectionBandwidth: Integer;
function GetPreferedLanguageString: WideString;
function GetPreferedLanguageID: Integer;
function GetUserCountryID: Integer;
function GetNumSources: Smallint;
function GetSourceTransport(nSourceNum: Smallint): WideString;
function GetWantErrors: WordBool;
procedure SetWantErrors(bVal: WordBool);
function GetShuffle: WordBool;
procedure SetShuffle(bVal: WordBool);
function GetVersionInfo: WideString;
function GetLastMessage: WideString;
function GetLastErrorSeverity: Integer;
function GetLastErrorRMACode: Integer;
function GetLastErrorUserCode: Integer;
function GetLastErrorUserString: WideString;
function GetLastErrorMoreInfoURL: WideString;
procedure SetPreFetch(bVal: WordBool);
function GetPreFetch: WordBool;
procedure SetRegion(const pVal: WideString);
function GetRegion: WideString;
function GetIsPlus: WordBool;
function GetConsoleEvents: WordBool;
procedure SetConsoleEvents(bVal: WordBool);
function GetDRMInfo(const pVal: WideString): WideString;
property ControlInterface: IRealAudio read GetControlInterface;
property DefaultInterface: IRealAudio read GetControlInterface;

网页中WMP视频控制之使用指南

□播放方法和属性
除播放、暂停、停止外,媒体播放器还可用下面的属性:
扫描(Scanning)——与录像机的快进快倒功能类似;
搜索(Seeking)——直接移到剪辑中标记的特定上演时间;

□播放
媒体播放器提供了两种技术来指定要播放的媒体名称(剪辑),你可以设置 FileName 属性,或调用 Open 方法。 如果 AutoStart 属性的值是 true 的话,当 FileName 属性被设置成该电影剪辑的 URL 后,该电影剪辑将开始播放;否则电影剪辑不会开始播放, 除非你调用 Play 方法。 Open 方法开始播放是异步的,它不象 Play 方法要等到其他的进程结束后才开始播放。
媒体播放器提供了下面的类似录像机的属性和方法来控制流媒体的播放:
Play、Stop、Pause 方法,来开始、停止、暂停流媒体。
PlayCount 属性,设置文件播放的次数。
AutoRewind 属性,确定当停止播放时是否返回到电影剪辑的开始部分。

□音频控制
媒体播放器提供了下面的属性来管理音频:
Balance 属性,确定左右音箱的声音平衡;
Volume 属性,用来加大或降低音量;
Mute 属性,用来关闭或打开声音;
※你可以将 ShowAudioControls 属性设为 true 来在控制栏添加处理声音的控件。

□扫描
媒体播放器提供了下面的属性用以扫描:
FastForward 方法,快进;
FastReverse 方法,快倒;
Rate属性,改变播放速率;
※要想使电影剪辑能被扫描,必须将 CanScan 和 AllowScan 属性设为 true。

□搜索
用于搜索的属性有:
MarkerCount 属性,指剪辑中标记的总数量;
CurrentMaker、GetMarkerName、GetMarkerTime 方法,用于返回标记信息;
MarkerHit 事件,当遇到标记时触发;
CurrentPosition 属性,当前位置(用秒度量),可用来将播放头移到剪辑中指定的点;
PositionChange 事件,当设置 CurrentPosition 属性时触发;
※要想搜索到任意的时间,必须将 CanSeek 属性设为 true,要想搜索到标记点,必须将 CanSeekToMarkers 属性设为 true。

□媒体播放器的外观界面
在网页中,你可以通过相关属性来控制媒体播放器的哪些部分出现,哪些部分不出现。
媒体播放器包括如下元素:
Video Display Panel:视频显示面板;
Video Border:视频边框;
Closed Captioning Display Panel;字幕显示面板;
Track Bar;搜索栏;
Control Bar with Audio and Position Controls:带有声音和位置控制的控制栏;
Go To Bar:转到栏;
Display Panel:显示面板;
Status Bar:状态栏;
下面的属性用来决定显示哪一个元素:
ShowControls 属性:是否显示控制栏(包括播放控件及可选的声音和位置控件);
ShowAudioControls 属性:是否在控制栏显示声音控件(静音按钮和音量滑块);
ShowPositionControls 属性:是否在控制栏显示位置控件(包括向后跳进、快退、快进、向前跳进、预览播放列表中的每个剪辑);
ShowTracker 属性:是否显示搜索栏;
ShowDisplay 属性:是否显示显示面板(用来提供节目与剪辑的信息);
ShowCaptioning 属性:是否显示字幕显示面板;
ShowGotoBar 属性:是否显示转到栏;
ShowStatusBar 属性:是否显示状态栏;

□播放列表
媒体播放器提供下面的方法来访问播放列表中的剪辑:
Next 方法,跳到节目(播放列表)中的下一个剪辑;
Previous 方法,跳回到节目中的上一个剪辑;
媒体播放器的一个特性是能够预览节目中的每一个剪辑,使用如下属性:
PreviewMode 属性,决定媒体播放器当前是否处于预览模式;
CanPreview 属性,决定媒体播放器能否处于预览模式;
在windows 媒体元文件中,可以为每一个剪辑指定预览时间——PREVIEWDURATION,如果没有指定,那么默认的预览时间是10秒钟。
你也可以用Windows 媒体元文件来添加 watermarks 与 banners,元文件也支持插入广告时的无间隙流切换。

□节目信息
使用 GetMediaInfoString 方法可以返回相关剪辑或节目的如下信息:
文件名:File name
标题:Title
描述:Description
作者:Author
版权:Copyright
级别:Rating
URLs:logo icon、watermark、banner的地址
剪辑信息可以放在媒体文件中,也可以放在Windows 媒体元文件中,或者两者都放。如果在元文件中指定了剪辑信息,那么用 GetMediaInfoString 方法返回的就是元文件中的信息,而不会返回剪辑中包含的信息。
在元文件中,附加信息可以放置在每一个剪辑或节目的 PARAM标签中。你可以为每个剪辑添加任意多个 PARAM 标签,用来存储自定义的信息或链接到相关站点。在 PARAM 标签中的信息可以通过 GetMediaParameter 方法来访问。
下面的属性返回有关大小和时间的信息:
ImageSourceHeight、ImageSourceWidth:返回图像窗口的显示尺寸;
Duration 属性,返回剪辑的长度(秒), 要检测这个属性是否包含有效的数值,请检查IsDurationValid 属性。(对于广播的视频,其长度是不可预知的)。

□字幕
你可以用 .smi 文件来为你的节目添加字幕。媒体播放器支持下面的属性来处理字幕:
SAMIFileName 属性,指定 .smi 文件的名字;
SAMILang 属性,指定字幕的语言(如果没有指定则使用第一种语言);
SAMIStyle 属性,指定字幕的文字大小和样式;
ShowCaptioning 属性,决定是否显示字幕显示面板;

□脚本命令
伴随音频、视频流,你可以在流媒体文件中加入脚本命令。脚本命令是多媒体流中与特定时间同步的多对Unicode串。第一个串标识待发命令的类型,第二个串指定要执行的命令。
当流播放到与脚本相关的时间时,控件会向网页发送一个 scriptCommand事件,然后由事件处理进程来响应这个事件。脚本命令字符串会作为脚本命令事件的参数传递给事件处理器。
媒体播放器会自动处理下面类型的内嵌脚本命令:
1)URL型命令:当媒体播放器控件收到一个URL型的命令时,指定的URL会被装载到用户的默认浏览器。如果媒体播放器嵌在一个分帧的HTML文件中,URL页可以装载到由脚本命令指定的帧内。如果脚本命令没有指定一个帧,将由 DefaultFrame 属性决定将 URL 页装入哪一帧。
你可以通过设置 InvokeURLs 属性来决定是否自动处理 URL 型的脚本命令。如果这个属性的值为 false ,媒体播放器控件将忽视 URL型命令。但是脚本命令事件仍会触发,这就允许你有选择地处理 URL 型命令。
URL 型命令指定的是 URL 的相对地址。基地址是由 BaseURL属性指定的。媒体播放器控件传送的脚本命令事件的命令参数是链接好的地址。
2)FILENAME型命令:当媒体播放器控件收到一个FILENAME型的命令时,它将 FileName属性设置为脚本命令提供的文件,之后媒体播放器会打开这个文件开始播放。 媒体播放器控件总是自动处理 FILENAME 型命令,不象 URL 型命令,它们不能被禁止。
3)TEXT型命令:当媒体播放器控件收到一个 TEXT型的命令时,它会将命令的内容显示在控件的字幕窗口。内容可以是纯文本的,也可以是 HTML。
4)EVENT型命令:当媒体播放器控件收到一个 EVENT型的命令时,它会在媒体元文件中搜索 EVENT 元素的 NAME 属性。如果 NAME 属性与脚本命令中的第二个字符串匹配,媒体播放器控件就执行包含在 EVENT 元素中的条目。
5)OPENEVENT型命令:当媒体播放器控件收到一个 OPENEVENT型的命令时,它会在媒体元文件中检查 EVENT 元素,并打开匹配的标题,但不播放,直到收到来自 EVENT型命令的同名真实事件。

□捕捉键盘和鼠标事件
EnableContextMenu 与 ClickToPlay 属性为用户提供了在图像窗口进行操作的方法。
如果 EnableContextMenu 属性为 true ,在图像窗口右击鼠标可以打开关联菜单,如果将ClickToPlay 属性设为 true ,用户可以单击图像窗口进行播放与暂停的切换。
要接收鼠标移动和单击事件,请将 SendMouseMoveEvents 和 SendMouseClickEvents 属性设为 true 。鼠标事件有:
MouseDown,当用户按下鼠标时产生;
MouseUp,当用户释放鼠标时产生;
MouseMove,当用户移动鼠标时产生;
Click,当用户在媒体播放器上单击鼠标按钮时产生;
DbClick,当用户在媒体播放器上双击鼠标按钮时产生;
要接收键盘事件,请将 SendKeyboardEvents 属性设为 true 。键盘事件有:
KeyDown,当用户按下一个键时产生;
KeyUp,当用户释放一个键时产生;
KeyPress,当用户按下并释放一个键时产生;

□监测流状态与网络链接
流状态属性包括:
PlayState:播放状态;
OpenState:打开状态;
Bandwidth:带宽;
支持的事件有:
OpenStateChange:打开状态改变(仅当SendOpenStateChangeEvents属性为true时触发)
PlayStateChange:播放状态改变(仅当SendPlayStateChangeEvents属性为true时触发)
EndOfStream:流结束时触发;
NewStream:打开新流时触发;
网络接收属性包括:
ReceptionQuality:接收质量;
ReceivedPackets:已经收到的包;
LostPackets:丢失的包;
监测缓冲的属性有:
BufferingTime:缓冲时间;
BufferingCount:缓冲次数;
BufferingProgress:缓冲进程;
Buffering:缓冲事件;

□错误处理
媒体播放器提供了内建的错误处理功能——在对话框或状态栏显示错误信息。 另外,你可以自己添加错误处理程序。如果 SendErrorEvents 属性设置为 true,将不会显示错误框,而是发送错误事件;如果 SendErrorEvents 属性设置为 false,将显示错误框,而是发送错误事件。
媒体播放器支持下面的错误处理事件:
Error 事件,指有危险性错误发生;
Warning 事件,指发生了非危险性的错误;
当你的应用程序接收到一个错误事件,你可以检测下面的属性来确定具体的错误信息:
HasError:检测目前的媒体播放器是否有错误;
ErrorCode:提供与该类型错误相关的代码值;
ErrorDescription:提供错误的描述信息;
ErrorCorrection:指定媒体播放器对该类型的错误进行校正;

□播放CD
媒体播放器将 CD 看作(在每个音轨的开头具有标记的)单一音频流。 要在网页中使用CD,你需要将 FileName 属性设为 CDAUDIO:,必须带有冒号,如下所示:
<HTML>
<HEAD><TITLE>CD Audio Playback Example</TITLE></HEAD>
<BODY>
<OBJECT ID="MediaPlayer"
CLASSID="CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95"
WIDTH="320"
HEIGHT="508"
STYLE="position:absolute; left:0px; top:70px;" >
<PARAM NAME="FileName" value="/cdaudio:">
<PARAM NAME="AutoStart" VALUE="0">
<PARAM NAME="ShowControls" VALUE="1">
<PARAM NAME="ShowStatusBar" VALUE="1">
<PARAM NAME="ShowDisplay" VALUE="1">
</OBJECT>
</BODY>
</HTML>
之后你可以用 Play 方法播放 CD 了。
你可以在 windows 媒体元文件中指定播放顺序以及每个音轨的信息。示例如下:

<ASX VERSION="3.0">
<TITLE>CD Audio with the Media Player</TITLE>
<AUTHOR>Windows Media Technologies</AUTHOR>
<COPYRIGHT>(c) 1999, Microsoft, Inc.</COPYRIGHT>

<ENTRY>
<TITLE>Track 1: Title 1</TITLE>
<REF href="/cdaudio:" />
<STARTMARKER NUMBER="1"/>
<ENDMARKER NUMBER="2"/>
</ENTRY>

<ENTRY>
<TITLE>Track 2: Title 2</TITLE>
<REF href="/cdaudio:" />
<STARTMARKER NUMBER="2"/>
<ENDMARKER NUMBER="3"/>
</ENTRY>

<ENTRY>
<TITLE>Track 3: Title 3</TITLE>
<REF href="/cdaudio:" />
<STARTMARKER NUMBER="3"/>
<ENDMARKER NUMBER="4"/>
</ENTRY>

<ENTRY>
<TITLE>Track 4: Title 4</TITLE>
<REF href="/cdaudio:" />
<STARTMARKER NUMBER="4"/>
</ENTRY>
</ASX> 
网页上嵌入视频播放器 jquery
播放: MediaPlayer.Play()
暂停: MediaPlayer.Pause()
定位: MediaPlayer.SetCurrentEntry(lWhichEntry)
MediaPlayer.Next()
MediaPlayer.Previous()
循环: MediaPlayer.PlayCount = 0
0:the clip plays repeatedly
1:once
停止: MediaPlayer.Stop()
==================================
AllowChangeDisplaySize 返回或设置最终用户是否能设置显示尺寸(逻辑型)
AllowScan 返回或设置是否允许扫描(逻辑型)
AnimationAtStart 返回或设置控件开始播放之前是否先播放一个动画序列(逻辑型)
AudioStream 返回或设置音频流的编号(用于多音频流的剪辑,默认为-1)(长整型)
AutoRewind 返回或设置媒体文件播放完毕后是否自动回绕(逻辑型)
AutoSize 返回或设置是否自动调整控件大小来适应载入的媒体(逻辑型)
AutoStart 返回或设置在载入媒体文件之后是否自动开始播放(逻辑型)
Balance 返回或设置指定立体声媒体文件的播放声道(-10000为左声道,10000为右声道,0为立体声)(长整型)
Bandwidth 返回或设置当前文件的带宽(长整型)
BaseURL 返回基本的 HTTP URL(字符串)
BufferingCount 返回媒体文件回放时缓冲的总时间(长整型)
BufferingProgress 返回缓冲完成的百分比(长整型)
BufferingTime 返回缓冲的时间(双精度型)
CanPreview 返回或设置当前显示的剪辑是能否被预览(逻辑型)
CanScan 返回或设置当前文件是否支持快进或快退(逻辑型)
CanSeek 返回或设置当前文件是否能搜索并定位到某个时间(逻辑型)
CanSeekToMarkers 返回或设置文件是否支持搜索到标签(逻辑型)
CaptioningID 返回在标题中显示的帧或控件的名称(字符串)
ChannelDescription 返回电台的描述(字符串)
ChannelName 返回电台的名称(字符串)
ChannelURL 返回电台的元文件的位置(字符串)
ClickToPlay 返回或设置是否可以通过点击图像暂停或播放剪辑(逻辑型)
ClientID 返回客户端唯一的标识符(字符串)
CodecCount 返回文件使用的可安装的 codecs 的个数(长整型)
ContactAddress 返回电台的联系地址(字符串)
ContactEmail 返回电台的联系电子邮件地址(字符串)
ContactPhone 返回电台的联系电话(字符串)
CreationDate 返回剪辑的创建日期(日期型)
CurrentMarker 返回或设置当前书签号码(长整型)
CurrentPosition 返回或设置剪辑的当前位置(双精度型)
CursorType 返回或设置指针类型(长整型)
DefaultFrame 返回或设置控件的默认目标 Http 帧(字符串)
DisplayBackColor 返回或设置显示面板的背景色(OLE_COLOR 值)
DisplayForeColor 返回或设置显示面板的前景色(OLE_COLOR 值)
DisplayMode 返回或设置显示面板是否用秒或帧的形式显示当前位置(MPDisplayModeConstants 值)
DisplaySize 返回或设置图像显示窗口的大小(MPDisplaySizeConstant 值)
Duration 返回或设置剪辑剪辑的播放时间(双精度型)
EnableContextMenu 返回或设置是否允许使用上下文菜单(逻辑型)
Enabled 返回或设置控件是否可用(逻辑型)
EnableFullScreenControls 返回或设置全屏幕控制是否可用(逻辑型)
EnablePositionControls 返回或设置位置控制是否可用(逻辑型)
EnableTracker 返回或设置搜索栏控制是否可用(逻辑型)
ErrorCode 返回当前错误代码(长整型)
ErrorCorrection 返回当前剪辑的错误修正类型(长整型)
ErrorDescription 返回当前错误的描述(字符串)
FileName 返回或设置要播放的剪辑的文件名称(字符串)
HasError 返回控件是否发生错误(逻辑型)
HasMultipleItems 返回或设置控件是否包含某些多重项目的内容(逻辑型)
ImageSourceHeight 返回或设置当前剪辑的原始图像高度(长整型)
ImageSourceWidth 返回或设置当前剪辑的原始图像宽度(长整型)
InvokeURLs 返回或设置 URL 是否自动发送请求(逻辑型)
IsBroadcast 返回或设置源是否进行广播(逻辑型)
IsDurationValid 返回或设置持续时间值是否有效(逻辑型)
Language 返回或设置用于本地化语言支持的当前区域语言(长整型)
LostPackets 返回丢失的数据包数量(长整型)
MarkerCount 返回文件书签的数量(长整型)
Mute 返回或设置控件是否播放声音(逻辑型)
OpenState 返回控件的内容源状态(长整型)
PlayCount 返回或设置一个剪辑播放的次数(长整型)
PlayState 返回控件的当前操作状态(长整型)
PreviewMode 返回或设置控件是否处在预览模式(逻辑型)
Rate 返回或设置回放帧频(双精度型)
ReadyState 返回控件是否准备就绪(ReadyStateConstant 值)
ReceivedPackets 返回已接收到的数据包的数量(长整型)
ReceptionQuality 返回最后 30 秒接收到的数据包的百分比(长整型)
RecoveredPackets 返回已转换的数据包的数量(长整型)
SAMIFileName 返回或设置 closed-captioning 文件名(字符串)
SAMILang 返回或设置 closed captioning 语言(字符串)
SAMIStyle 返回或设置 closed captioning 风格(字符串)
SelectionEnd 返回或设置流的结束位置(双精度型)
SelectionStart 返回或设置流的起始位置(双精度型)
SendErrorEvents 返回或设置控件是否发送错误事件(逻辑型)
SendKeyboardEvents 返回或设置控件是否发送键盘事件(逻辑型)
SendMouseClickEvents 返回或设置控件是否发送鼠标单击事件(逻辑型)
SendMouseMoveEvents 返回或设置控件是否发送鼠标移动事件(逻辑型)
SendOpenStateChangeEvents 返回或设置控件是否发送打开状态改变事件(逻辑型)
SendPlayStateChangeEvents 返回或设置控件是否发送播放状态改变事件(逻辑型)
SendWarningEvents 返回或设置控件是否发送警告事件(逻辑型)
ShowAudioControls 返回或设置是否显示音频控制(逻辑型)
ShowCaptioning 返回或设置是否显示字幕(逻辑型)
ShowControls 返回或设置控制面板是否可见(逻辑型)
ShowDisplay 返回或设置是否显示显示面板(逻辑型)
ShowGotoBar 返回或设置是否显示跳转栏(逻辑型)
ShowPositionControls 返回或设置是否显示位置控制(逻辑型)
ShowStatusBar 返回或设置是否显示状态栏(逻辑型)
ShowTracker 返回或设置是否显示搜索栏(逻辑型)
SourceLink 返回内容文件的路径(字符串)
SourceProtocol 返回用于接收数据的协议(长整型)
StreamCount 返回媒体帧的数量(长整型)
TransparentAtStart 返回或设置在开始播放之前和停止之后控件是否透明(逻辑型)
VideoBorder3D 返回或设置视频边框是否显示为 3D 效果(逻辑型)
VideoBorderColor 返回或设置视频边框的颜色(OLE_颜色)
VideoBorderWidth 返回或设置视频边框的宽度(长整型)
Volume 返回或设置音量(长整型)
==============================================
Mediaplayer属性和方法

属性/方法名:说明:
[基本属性]  
URL:String; 指定媒体位置,本机或网络地址
uiMode:String; 播放器界面模式,可为Full, Mini, None, Invisible
playState:integer; 播放状态,1=停止,2=暂停,3=播放,6=正在缓冲,9=正在连接,10=准备就绪
enableContextMenu:Boolean; 启用/禁用右键菜单
fullScreen:boolean; 是否全屏显示
[controls] wmp.controls //播放器基本控制
controls.play; 播放
controls.pause; 暂停
controls.stop; 停止
controls.currentPosition:double; 当前进度
controls.currentPositionString:string; 当前进度,字符串格式。如“00:23”
controls.fastForward; 快进
controls.fastReverse; 快退
controls.next; 下一曲
controls.previous; 上一曲
[settings] wmp.settings //播放器基本设置
settings.volume:integer; 音量,0-100
settings.autoStart:Boolean; 是否自动播放
settings.mute:Boolean; 是否静音
settings.playCount:integer; 播放次数
[currentMedia] wmp.currentMedia //当前媒体属性
currentMedia.duration:double; 媒体总长度
currentMedia.durationString:string; 媒体总长度,字符串格式。如“03:24”
currentMedia.getItemInfo (const string); 获取当前媒体信息"Title"=媒体标题,"Author"=艺术家,"Copyright"=版权信息, "Description"=媒体内容描述,"Duration"=持续时间(秒),"FileSize"=文件大小,"FileType"=文件类型, "sourceURL"=原始地址
currentMedia.setItemInfo(const string); 通过属性名设置媒体信息
currentMedia.name:string; 同 currentMedia.getItemInfo("Title")
[currentPlaylist] wmp.currentPlaylist //当前播放列表属性
currentPlaylist.count:integer; 当前播放列表所包含媒体数
currentPlaylist.Item[integer]; 获取或设置指定项目媒体信息,其子属性同wmp.currentMedia
以下是realplayer:

<object ID="javademo" CLASSID="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" HEIGHT="280" WIDTH="200">
<param name="AUTOSTART" value="0">
<param name="SHUFFLE" value="0">
<param name="PREFETCH" value="0">
<param name="NOLABELS" value="0">
<param name="SRC" value="yy.wma">
<param name="CONTROLS" value="ImageWindow">
<param name="CONSOLE" value="Clip1">
<param name="LOOP" value="0">
<param name="NUMLOOP" value="0">
<param name="CENTER" value="0">
<param name="MAINTAINASPECT" value="0">
<param name="BACKGROUNDCOLOR" value="#000000">
</object>


//控制开始
<input type=button value=play onclick="javademo.DoPlay()">
<input type=button value=pause onclick="javademo.DoPause()">
<input type=button value=stop onclick="javademo.DoStop()">
REAL PLAYER控制
player.DoPlay() 播放
player.DoPause() 暂停
player.DoStop() 停止
player.GetLength() 返回播放文件的总长度(以毫秒为单位)
player.GetPosition() 返回播放文件的当前时间位置(以毫秒为单位)
player.GetPlayState() 返回播放器状态(0:停止,1:连接,2:缓冲,3:播放,4:暂停,5:寻找)
player.SetPosition(n) 时间位置跳到n的地方(n取值以毫秒为单位)
player.SetVolume(n) 设置音量(n取值为0到100)
player.SetMute(s) 静音(s取值为true和false)
player.SetFullScreen() 全屏
player.setSource() 设置文件来源
media player控制
player.controls.play(); 播放
player.controls.stop(); 停止
player.controls.pause(); 暂停
player.controls.currentPosition 返回播放文件的当前时间位置(以秒为单位)
player.controls.currentPositionString 时间格式的字符串 "0:32"
player.currentMedia.duration 返回播放文件的总长度(以秒为单位)
player.currentMedia.durationString 时间格式的字符串 "4:34"
player.settings.volume 音量 (0-100)
player.settings.balance 声道,通过它应该可以进行立体声、左声道、右声道的控制。但对应的取值尚不清楚。
player.settings.mute = s 静音(s取值为true和false)
player.closedCaption.CaptioningID 网页中出现字幕的容器的ID
player.closedCaption.SAMIFileName 字幕文件地址
player.playState 返回播放器状态( 3:正在播放,2:暂停1:已停止)
属性
all
显示全部功能表
controlpanel
显示控制面版
infovolumepanel
显示声音面版
infopanel
显示信息面版
statusbar
显示状态列
playbutton
显示开始鈕
stopbutton
显示停止鈕
volumeslider
显示声音调整按钮
posititonslider
显示位置调整按钮
positionfield
显示位置区
statusfield
显示状态列
src
声音來源位置
autostart
是否自动播放
nolabels
是否显示title、author与coptright等信息
autogotourl
是否自动传送url事件
方法
aboutbox
显示about对话框
canplaypause
是否可以暂停、重播
canstop
是否能停止realaudio
doplaypause
设定播放或暂停
dostop
停止播放
donextitem
播放下一个声音文件
doprevitem
播放上一个声音文件
editpreferences
开启设定的对话框
hasnextitem
检查是否有下一个声音文件
hasprevitem
检查是否有上一个声音文件
hideshowstatistics
设定开启或关闭连线统计的对话框
isstatisticsvisible
检查是否已开启连线统计的对话框
dogotourl
载入设定的的realaudio声音文件
事件
onclipopened
开启realaudio声音文件时触发的事件
onclipclosed
关闭realaudio声音文件时触发的事件
onshowstatus
状态列文字改变时触发的事件
ongotourl
播放音效时,遇到url事件时,触发的事件
默认0为否,-1或1为是)
<object classid="clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95" id="MediaPlayer1" width="286" height="225">
<param name="AudioStream" value="/-1">
<param name="AutoSize" value="-1">
<!--是否自动调整播放大小-->
<param name="AutoStart" value="-1">
<!--是否自动播放-->
<param name="AnimationAtStart" value="-1">
<param name="AllowScan" value="-1">
<param name="AllowChangeDisplaySize" value="-1">
<param name="AutoRewind" value="0">
<param name="Balance" value="0">
<!--左右声道平衡,最左-9640,最右9640-->
<param name="BaseURL" value>
<param name="BufferingTime" value="15">
<!--缓冲时间-->
<param name="CaptioningID" value>
<param name="ClickToPlay" value="-1">
<param name="CursorType" value="0">
<param name="CurrentPosition" value="0">
<!--当前播放进度 -1 表示不变,0表示开头单位是秒,比如10表示从第10秒处开始播放,值必须是-1.0或大于等于0-->
<param name="CurrentMarker" value="0">
<param name="DefaultFrame" value>
<param name="DisplayBackColor" value="0">
<param name="DisplayForeColor" value="16777215">
<param name="DisplayMode" value="0">
<param name="DisplaySize" value="0">
<!--视频1-50%, 0-100%, 2-200%,3-全屏其它的值作0处理,小数则采用四舍五入然后按前的处理-->
<param name="Enabled" value="-1">
<param name="EnableContextMenu" value="-1">
<!-是否用右键弹出菜单控制-->
<param name="EnablePositionControls" value="-1">
<param name="EnableFullScreenControls 
" value="-1">
<param name="EnableTracker" value="-1">
<!--是否允许拉动播放进度条到任意地方播放-->
<param name="Filename" value="01.wma" valuetype="ref">
<!--播放的文件地址-->
<param name="InvokeURLs" value="-1">
<param name="Language" value="-1">
<param name="Mute" value="0">
<!--是否静音-->
<param name="PlayCount" value="10">
<!--重复播放次数,0为始终重复-->
<param name="PreviewMode" value="-1">
<param name="Rate" value="1">
<!--播放速率控制,1为正常,允许小数-->
<param name="SAMIStyle" value>
<!--SAMI样式-->
<param name="SAMILang" value>
<!--SAMI语言-->
<param name="SAMIFilename" value>
<!--字幕ID-->
<param name="SelectionStart" value="-1">
<param name="SelectionEnd" value="-1">
<param name="SendOpenStateChangeEvents" value="-1">
<param name="SendWarningEvents" value="-1">
<param name="SendErrorEvents" value="-1">
<param name="SendKeyboardEvents" value="0">
<param name="SendMouseClickEvents" value="0">
<param name="SendMouseMoveEvents" value="0">
<param name="SendPlayStateChangeEvents" value="-1">
<param name="ShowCaptioning" value="0">
<!--是否显示字幕,为一块黑色,下面会有一大块黑色,一般不显示-->
<param name="ShowControls" value="-1">
<!--是否显示控制,比如播放,停止,暂停-->
<param name="ShowAudioControls" value="-1">
<!--是否显示音量控制-->
<param name="ShowDisplay" value="0">
<!--显示节目信息,比如版权等-->
<param name="ShowGotoBar" value="0">
<!--是否启用上下文菜单-->
<param name="ShowPositionControls" value="-1">
<!--是否显示往前往后及列表,如果显示一般也都是灰色不可控制-->
<param name="ShowStatusBar" value="-1">
<!--当前播放信息,显示是否正在播放,及总播放时间和当前播放到的时间-->
<param name="ShowTracker" value="-1">
<!--是否显示当前播放跟踪条,即当前的播放进度条-->
<param name="TransparentAtStart" value="-1">
<param name="VideoBorderWidth" value="0">
<!--显示部的宽部,如果小于视频宽,则最小为视频宽,或者加大到指定值,并自动加大高度.此改变只改变四周的黑框大小,不改变视频大小 -->
<param name="VideoBorderColor" value="0">
<!--显示黑色框的颜色, 为RGB值,比如ffff00为黄色-->
<param name="VideoBorder3D" value="0">
<param name="Volume" value="0">
<!--音量大小,负值表示是当前音量的减值,值自动会取绝对值,最大为0,最小为-9640-->
<param name="WindowlessVideo" value="0">
<!--如果是0可以允许全屏,否则只能在窗口中查看-->
</object>
上面的这个播放器是老式的那种,6.4版本!新式播放器是在MediaPlayer9.0以后出现的,也就是说只有装了9.0或9.0以上的播放器才能正 常使用的。
-------------------------------------------------------------------------------
下面是新式播放器代码,相对以前的来说要简单很多:
<object id="player" height="64" width="260" classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6">
<param NAME="AutoStart" VALUE="-1">
<!--是否自动播放-->
<param NAME="Balance" VALUE="0">
<!--调整左右声道平衡,同上面旧播放器代码-->
<param name="enabled" value="-1">
<!--播放器是否可人为控制-->
<param NAME="EnableContextMenu" VALUE="-1">
<!--是否启用上下文菜单-->
<param NAME="url" VALUE="1.wma">
<!--播放的文件地址-->
<param NAME="PlayCount" VALUE="1">
<!--播放次数控制,为整数-->
<param name="rate" value="1">
<!--播放速率控制,1为正常,允许小数,1.0-2.0-->
<param name="currentPosition" value="0">
<!--控件设置:当前位置-->
<param name="currentMarker" value="0">
<!--控件设置:当前标记-->
<param name="defaultFrame" value="">
<!--显示默认框架-->
<param name="invokeURLs" value="0">
<!--脚本命令设置:是否调用URL-->
<param name="baseURL" value="">
<!--脚本命令设置:被调用的URL-->
<param name="stretchToFit" value="0">
<!--是否按比例伸展-->
<param name="volume" value="50">
<!--默认声音大小0%-100%,50则为50%-->
<param name="mute" value="0">
<!--是否静音-->
<param name="uiMode" value="mini">
<!--播放器显示模式:Full显示全部;mini最简化;None不显示播放控制,只显示视频窗口;invisible全部不显示-->
<param name="windowlessVideo" value="0">
<!--如果是0可以允许全屏,否则只能在窗口中查看-->
<param name="fullScreen" value="0">
<!--开始播放是否自动全屏-->
<param name="enableErrorDialogs" value="-1">
<!--是否启用错误提示报告-->
<param name="SAMIStyle" value>
<!--SAMI样式-->
<param name="SAMILang" value>
<!--SAMI语言-->
<param name="SAMIFilename" value>
<!--字幕ID-->
</object>

请大家多多照顾,更多免费小说: 飞翔鸟中文

haotingplay.playState //播放机状态,共有1-10个状态,具体如下 --------------------------------

haotingplay.playState==1 //停止播放 
haotingplay.playState==2 //暂停播放 
haotingplay.playState==3 //正常播放 
haotingplay.playState==4 //向前搜索 
haotingplay.playState==5 //向后搜索 
haotingplay.playState==6 //缓冲处理 
haotingplay.playState==7 //等待反应 
haotingplay.playState==8 //播放完毕 
haotingplay.playState==9 //连接媒体 
haotingplay.playState==10 //准备就绪

播放器----------------------------------------

<object id="haotingplay" width="360px" height="64px" border="0" type="application/x-oleobject" CLASSID="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6">
<param NAME="AutoStart" VALUE="true">
<!--是否自动播放-->
<param NAME="Balance" VALUE="0">
<!--调整左右声道平衡,同上面旧播放器代码-->
<param name="enabled" value="-1">
<!--播放器是否可人为控制-->
<param NAME="EnableContextMenu" VALUE="-1">
<!--是否启用上下文菜单-->
<param NAME="url" VALUE="">
<!--播放的文件地址-->
<param NAME="PlayCount" VALUE="1">
<!--播放次数控制,为整数-->
<param name="rate" value="1">
<!--播放速率控制,1为正常,允许小数,1.0-2.0-->
<param name="currentPosition" value="0">
<!--控件设置:当前位置-->
<param name="currentMarker" value="0">
<!--控件设置:当前标记-->
<param name="defaultFrame" value="">
<!--显示默认框架-->
<param name="invokeURLs" value="0">
<!--脚本命令设置:是否调用URL-->
<param name="baseURL" value="">
<!--脚本命令设置:被调用的URL-->
<param name="stretchToFit" value="0">
<!--是否按比例伸展-->
<param name="volume" value="50">
<!--默认声音大小0%-100%,50则为50%-->
<param name="mute" value="0">
<!--是否静音-->
<param name="uiMode" value="mini">
<!--播放器显示模式:Full显示全部;mini最简化;None不显示播放控制,只显示视频窗口;invisible全部不显示-->
<param name="windowlessVideo" value="0">
<!--如果是0可以允许全屏,否则只能在窗口中查看-->
<param name="fullScreen" value="0">
<!--开始播放是否自动全屏-->
<param name="enableErrorDialogs" value="-1">
<!--是否启用错误提示报告-->
<param name="SAMIStyle" value>
<!--SAMI样式-->
<param name="SAMILang" value>
<!--SAMI语言-->
<param name="SAMIFilename" value>
<!--字幕ID-->
</object>

自动连续播放---------------------------------------------------------

//每1秒执行这个paly()方法

setInterval("play()",1000);

function play(){
  
   if(haotingplay.playState==1){
    //播放下一首歌的方法

   }

}
java去除字符串中的空格、回车、换行符、制表符 java
java去除字符串中的空格、回车、换行符、制表符
01
import java.util.regex.Matcher;
02

import java.util.regex.Pattern;
03
04
05
06

/**
07

 * @author lei
08

 * 2011-9-2
09

 */
10

public class StringUtils {
11

12

    public static String replaceBlank(String str) {
13

        String dest = "";

14

        if (str!=null) {
15

            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
16

            Matcher m = p.matcher(str);
17

            dest = m.replaceAll("");
18

        }

19

        return dest;
20

    }
21

    public static void main(String[] args) {
22

        System.out.println(StringUtils.replaceBlank("just do it!"));
23

    }
24

    /*-----------------------------------


    笨方法:String s = "你要去除的字符串";

 

            1.去除空格:s = s.replace('\\s','');





29

 





30

            2.去除回车:s = s.replace('\n','');





31

 





32

    这样也可以把空格和回车去掉,其他也可以照这样做。





33

 





34

    注:\n 回车(\u000a) 





35

    \t 水平制表符(\u0009) 





36

    \s 空格(\u0008) 





37

    \r 换行(\u000d)*/





38

}
网页嵌入realplayer例子 jquery
<html>
<head>
		<script>
		function doFull()
		{
			alert(1);
		  var objId = "RVOCX";
		  var funcStr = 'document.'+objId + ".SetFullScreen()";
		  eval(funcStr);
		}
		</script>

		
</head>
<body>
<input type="button" value="全屏" onclick="doFull();" style="postion:absolute;top:0px;left:0px;"> 
<div style="position:absolute;top:0px;left:0px;z-index:9;">
<object id="RVOCX" classid="CLSID:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="320" height="240">
  <param name="SRC" value="1">
  <param name="CONTROLS" value="ImageWindow">
  <param name="CONSOLE" value="cons">
  <embed src="1" type="audio/x-pn-realaudio-plugin" width="320" height="240" controls="ImageWindow" console="cons"> 
  </embed> </object>
<br/>
<object id="RVOCX1" classid="CLSID:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA" width="320" height="50"  >
  <param name="SRC" value="1">
  <param name="CONTROLS" value="StatusBar,ControlPanel">
  <param name="CONSOLE" value="cons">
  <embed src="1" type="audio/x-pn-realaudio-plugin" width="375" height="100" controls="StatusBar,ControlPanel" console="cons" > 
  </embed> 
</object>
</div>

</body>

</html>
realplayer网页参数说明 jquery
autostart 
属性:true或是false 
作用:指定是否自动播放指定的源文件。 
backgroundcolor 
属性:任何用符号“#”开头的16进制数值或是任何预定义的颜色 
作用:指定图像窗口的背景颜色。 
center 
属性:true或是false 
作用:指定片断使用初始编码大小播放,并且在图像窗口的中央。 
classid 
属性:clsid:cfcdaa03-8be4-1lcf-b84b0020afbbccfa:** 
作用:用于指定activex控件的唯一的字符串标示,可以认出嵌入的realpalyer播放器。 
console 
属性:任何字符串 
作用:可以将各种不同的realplayer控制聚集在网页上,这样它们可以交互使用或是保持独立,而且互相不影响。 
controls 
属性:imagewindow,all,controlpanel,plavbutton,playonlybutton, pausebutton,stopbutton,ffctrl,rwctrl,mutectrl,mutevolume,volumeslider, positionslider,tacctrl,homectrl,infovolumepanel,infopanel,statusbar, statusfield,positionfield 
作用:可以让你指定那些控制是可见的。 
height 
属性:任何整数值 
作用:指定realplayer元素的高度,单位:像素。 
id 
属性:任何字符串 
作用:为标签中的realplayer元素指定名字。 
imagestatus 
属性:true或是false 
作用:指定是否在图像窗口中显示状态信息,默认值是true 
loop 
属性:true或是false 
作用:可以让你指定片断是否无限循环 
maintainaspect 
属性:true或是false 
作用:默认realplayer拉伸所有的片断来充满整个图像窗口。 
name 
属性:任何字符串 
作用:为标签中的realplayer元素指定名字(在标签中使用id)。 
nojava 
属性:true或是false 
作用:避免启动java虚拟机。 
nolabels 
属性:true或是false 
作用:可以禁止显示标题或是版权信息。 
nologo 
属性:true或是false 
作用:避免realplayer启动时在图像窗口中显示。 
numloop 
属性:任何整数值 
作用:让你能够指定文件片循环的次数,不需要参数loop。 
prefetch 
属性:true或是false 
作用:指定在播放前,realplayer是否可以获得流描述信息,默认值是false。 
region 
属性:任何字符串 
作用:同smil一起使用,允许你指定使用html代替smil。 
scriptcallbacks 
属性:用逗号分割的列表 
作用:指定浏览器的回调监控。 
shuffle 
属性:true或是false 
作用:同多文件片的ram文件或是smil文件一起使用,可以让realplayer随机播放列表中的文件。 
src 
属性:任何合法的相对或是完整的url 
作用:指定播放的文件或是源文件的地址。 
type 
属性:字符串 
作用:为嵌入插件指定mime类型。 
width 
属性:任何整数值 
作用:指定realplayer元素的宽度。 
网页嵌入播放器 jquery
例如:网络上最流行的windows media流(asf,wma,wmv格式...),Real流(rm,rmvb...),还有MPEG系列编码格式(MP4/MP3格式...) 
Windows Media Video 是微软推出的一种流媒体格式,它是在“同门”的ASF(Advanced Stream Format)格式升级延伸来得.在同等视频质量下,WMV格式的体积非常小,因此很适合在网上播放和传输。Windows Media Player9兼容所有格式的WMV,官方编码器是Windows Media Encoder ,但是如果你想转制 高质量的wmv文件,那您一定要有超大的内存来处理数据... 
无意中发现CASTPOST的播放器可以自己定义大小,对于WMV格式的在线播放可以说已经足够快了,然后就费了好大劲把一些精彩的短片和一些经典的MTV转化WMV格式放了上来,尽管现在不能下载了,但是只要不是连接人数过多,播放起来还是很流畅的^_^ 

WMP加入了ActiveX解码器控件,不仅可以放曲子,还能放Flash和其它视频文件 
复制代码 代码如下:
<object align=center classid="CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95" class=OBJECT id=MediaPlayer width=196 height=196> 
<param name=ShowStatusBar value=0> 
<param name=Filename value="http://www.jb51.net/b.wmv"> 
<embed type=application/x-oleobject codebase="http://activex.microsoft.com/activex/con ... n/nsmp2inf.cab#Version=5,1,52,701" > 
</embed> 
</object> 

想用WMP连续播放请参照ASX元文件使用讲解:使用ASX播放列表吧 

上面的这个播放器是老式的那种,6.4版本!新式播放器是在MediaPlayer9.0以后出现的,也就是说只有装了9.0或9.0以上的播放器才能正常使用的。 


下面是新式播放器代码,相对以前的来说要简单很多: 
复制代码 代码如下:
<object id="player" height="64" width="260" classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6"> 
<param NAME="AutoStart" VALUE="-1"> 
<!--是否自动播放--> 
<param NAME="Balance" VALUE="0"> 
<!--调整左右声道平衡,同上面旧播放器代码--> 
<param name="enabled" value="-1"> 
<!--播放器是否可人为控制--> 
<param NAME="EnableContextMenu" VALUE="-1"> 
<!--是否启用上下文菜单--> 
<param NAME="url" value="/blog/1.wma"> 
<!--播放的文件地址--> 
<param NAME="PlayCount" VALUE="1"> 
<!--播放次数控制,为整数--> 
<param name="rate" value="1"> 
<!--播放速率控制,1为正常,允许小数,1.0-2.0--> 
<param name="currentPosition" value="0"> 
<!--控件设置:当前位置--> 
<param name="currentMarker" value="0"> 
<!--控件设置:当前标记--> 
<param name="defaultFrame" value=""> 
<!--显示默认框架--> 
<param name="invokeURLs" value="0"> 
<!--脚本命令设置:是否调用URL--> 
<param name="baseURL" value=""> 
<!--脚本命令设置:被调用的URL--> 
<param name="stretchToFit" value="0"> 
<!--是否按比例伸展--> 
<param name="volume" value="50"> 
<!--默认声音大小0%-100%,50则为50%--> 
<param name="mute" value="0"> 
<!--是否静音--> 
<param name="uiMode" value="mini"> 
<!--播放器显示模式:Full显示全部;mini最简化;None不显示播放控制,只显示视频窗口;invisible全部不显示--> 
<param name="windowlessVideo" value="0"> 
<!--如果是0可以允许全屏,否则只能在窗口中查看--> 
<param name="fullScreen" value="0"> 
<!--开始播放是否自动全屏--> 
<param name="enableErrorDialogs" value="-1"> 
<!--是否启用错误提示报告--> 
<param name="SAMIStyle" value> 
<!--SAMI样式--> 
<param name="SAMILang" value> 
<!--SAMI语言--> 
<param name="SAMIFilename" value> 
<!--字幕ID--> 
</object> 


RealOne播放器代码: 
复制代码 代码如下:
<object id="vid" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width=427 height=300> 
<param name="_ExtentX" value="11298"> 
<param name="_ExtentY" value="7938"> 
<param name="AUTOSTART" value="-1"> 
<param name="SHUFFLE" value="0"> 
<param name="PREFETCH" value="0"> 
<param name="NOLABELS" value="-1"> 
<param name="SRC" value="rtsp://211.89.225.1/encoder/cnr3";> 
<param name="CONTROLS" value="Imagewindow"> 
<param name="CONSOLE" value="clip1"> 
<param name="LOOP" value="0"> 
<param name="NUMLOOP" value="0"> 
<param name="CENTER" value="0"> 
<param name="MAINTAINASPECT" value="0"> 
<param name="BACKGROUNDCOLOR" value="#000000"> 
</object> <object id="vid2" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width=427 height=30> 
<param name="_ExtentX" value="11298"> 
<param name="_ExtentY" value="794"> 
<param name="AUTOSTART" value="-1"> 
<param name="SHUFFLE" value="0"> 
<param name="PREFETCH" value="0"> 
<param name="NOLABELS" value="-1"> 
<param name="SRC" value="rtsp://211.89.225.1/encoder/cnr3";> 
<param name="CONTROLS" value="ControlPanel"> 
<param name="CONSOLE" value="clip1"> 
<param name="LOOP" value="0"> 
<param name="NUMLOOP" value="0"> 
<param name="CENTER" value="0"> 
<param name="MAINTAINASPECT" value="0"> 
<param name="BACKGROUNDCOLOR" value="#000000"> 
</object> 


看着很多很复杂的样子,其实就是多了些播放器参数,可以不要... 
Real Player 网页播放器 参数含义 

参数:autostart 属性:True或是False 作用:指定是否自动播放指定的源文件 
参数:backgroundcolor 属性:任何用符号“#”开头的16进制数值或是任何预定义的颜色作用:指定图像窗口的背景颜色 
参数:center 属性:True或是False 作用:指定片断使用初始编码大小播放,并且在图像窗口的中央。 
参数:classid 属性:"clsid:CFCDAA03-8BE4-1lcf-B84B0020AFBBCCFA:** 作用:用于指定ActiveX控件的唯一的字符串标示,可以认出嵌入的RealPalyer播放器。 
参数:console 属性:任何字符串作用:可以将各种不同的RealPlayer控制聚集在网页上,这样它们可以交互使用或是保持独立,而且互相不影响 
参数:controls 属性:ImageWindow,All,ControlPanel,PlavButton,PlayOnlyButton, PauseButton,StopButton,FFCtrl,RWCtrl,MuteCtrl,MuteVolume,VolumeSlider,PositionSlider,TACCtrl,HomeCtrl,InfoVolumePanel,InfoPanel,StatusBar,StatusField,PositionField 作用:可以让你指定那些控制是可见的。 
参数:height 属性:任何整数值作用:指定RealPlayer元素的高度,单位:像素 
参数:id 属性:任何字符串作用:为标签中的RealPlayer元素指定名字。 
参数:imagestatus 属性:True或是False 作用:指定是否在图像窗口中显示状态信息,默认值是true 
参数:loop 属性:True或是False 作用:可以让你指定片断是否无限循环 
参数:maintainaspect 属性:True或是False 作用:默认RealPlayer拉伸所有的片断来充满整个图像窗口。 
参数:name 属性:任何字符串作用:为标签中的RealPlayer元素指定名字(在标签中使用id) 
参数:nojava 属性:True或是False 作用:避免启动Java虚拟机 
参数:nolabels 属性:True或是False 作用:可以禁止显示标题或是版权信息(realplayer5.0以上时,它是垃圾...) 
参数:nologo 属性:True或是False 作用:避免RealPlayer启动时在图像窗口中显示 
参数:numloop 属性:任何整数值作用:让你能够指定文件片循环的次数,不需要参数loop 
参数:prefetch 属性:True或是False 作用:指定在播放前,RealPlayer是否可以获得流描述信息,默认值是False 
参数:region 属性:任何字符串作用:同SMIL一起使用。允许你指定使用HTML代替SMIL 
参数:scriptcallbacks 属性:用逗号分割的列表作用:指定浏览器的回调监控(好高级的东东!) 
参数:shuffle 属性:True或是False 作用:同多文件片的ram文件或是SMIL文件一起使用。可以让RealPlayer随机播放列表中的文件 
参数:src 属性:任何合法的相对或是完整的URL 作用:指定播放的文件或是源文件的地址 
参数:type 属性:字符串作用:为嵌入插件指定MIME类型 
参数:width 属性:任何整数值作用:指定RealPlayer元素的宽度 


RealPlayer的一些函数、方法和过程 

这是 Real Player ActiveX Control Library (Version 1.0) 的所有函数与方法,有兴趣可以研究一下。 
复制代码 代码如下:
function GetSource: WideString; 
procedure SetSource(const lpszNewValue: WideString); 
function GetConsole: WideString; 
procedure SetConsole(const lpszNewValue: WideString); 
function GetControls: WideString; 
procedure SetControls(const lpszNewValue: WideString); 
function GetNoLabels: WordBool; 
procedure SetNoLabels(bNewValue: WordBool); 
function GetAutoStart: WordBool; 
procedure SetAutoStart(bNewValue: WordBool); 
function GetAutoGotoURL: WordBool; 
procedure SetAutoGotoURL(bNewValue: WordBool); 
function GetVolume: Smallint; 
procedure SetVolume(nVol: Smallint); 
function GetMute: WordBool; 
procedure SetMute(bMute: WordBool); 
function GetLoop: WordBool; 
procedure SetLoop(bVal: WordBool); 
function GetImageStatus: WordBool; 
procedure SetImageStatus(bEnable: WordBool); 
function GetPacketsTotal: Integer; 
function GetPacketsReceived: Integer; 
function GetPacketsOutOfOrder: Integer; 
function GetPacketsMissing: Integer; 
function GetPacketsEarly: Integer; 
function GetPacketsLate: Integer; 
function GetBandwidthAverage: Integer; 
function GetBandwidthCurrent: Integer; 
procedure DoPlayPause; 
procedure DoStop; 
procedure DoNextItem; 
procedure DoPrevItem; 
function CanPlayPause: WordBool; 
function CanStop: WordBool; 
function HasNextItem: WordBool; 
function HasPrevItem: WordBool; 
function HasNextEntry: WordBool; 
function HasPrevEntry: WordBool; 
procedure DoNextEntry; 
procedure DoPrevEntry; 
procedure AboutBox; 
procedure EditPreferences; 
procedure HideShowStatistics; 
function IsStatisticsVisible: WordBool; 
procedure DoGotoURL(const url: WideString; const target: WideString); 
procedure DoPlay; 
procedure DoPause; 
function GetPosition: Integer; 
function GetPlayState: Integer; 
function GetLength: Integer; 
function GetTitle: WideString; 
function GetAuthor: WideString; 
function GetCopyright: WideString; 
function GetClipWidth: Integer; 
function GetClipHeight: Integer; 
function CanPlay: WordBool; 
function CanPause: WordBool; 
procedure SetPosition(lPosition: Integer); 
function GetNumLoop: Integer; 
procedure SetNumLoop(lVal: Integer); 
function GetCenter: WordBool; 
procedure SetCenter(bVal: WordBool); 
function GetNoLogo: WordBool; 
procedure SetNoLogo(bVal: WordBool); 
function GetMaintainAspect: WordBool; 
procedure SetMaintainAspect(bVal: WordBool); 
function GetBackgroundColor: WideString; 
procedure SetBackgroundColor(const pVal: WideString); 
function GetStereoState: WordBool; 
function GetLiveState: WordBool; 
function GetShowStatistics: WordBool; 
procedure SetShowStatistics(bVal: WordBool); 
function GetShowPreferences: WordBool; 
procedure SetShowPreferences(bVal: WordBool); 
function GetShowonmouseover WordBool; 
procedure SetShowAbout(bVal: WordBool); 
function GetOriginalSize: WordBool; 
procedure SetOriginalSize; 
function GetDoubleSize: WordBool; 
procedure SetDoubleSize; 
function GetFullScreen: WordBool; 
procedure SetFullScreen; 
function GetEnableContextMenu: WordBool; 
procedure SetEnableContextMenu(bVal: WordBool); 
function GetEnableOriginalSize: WordBool; 
procedure SetEnableOriginalSize(bVal: WordBool); 
function GetEnableDoubleSize: WordBool; 
procedure SetEnableDoubleSize(bVal: WordBool); 
function GetEnableFullScreen: WordBool; 
procedure SetEnableFullScreen(bVal: WordBool); 
function GetEnableMessageBox: WordBool; 
procedure SetEnableMessageBox(bVal: WordBool); 
procedure SetTitle(const pVal: WideString); 
procedure SetAuthor(const pVal: WideString); 
procedure SetCopyright(const pVal: WideString); 
function GetWantKeyboardEvents: WordBool; 
procedure SetWantKeyboardEvents(bWantsEvents: WordBool); 
function GetWantMouseEvents: WordBool; 
procedure SetWantMouseEvents(bWantsEvents: WordBool); 
function GetNumEntries: Smallint; 
function GetCurrentEntry: Smallint; 
function GetEntryTitle(uEntryIndex: Smallint): WideString; 
function GetEntryAuthor(uEntryIndex: Smallint): WideString; 
function GetEntryCopyright(uEntryIndex: Smallint): WideString; 
function GetEntryAbstract(uEntryIndex: Smallint): WideString; 
procedure SetCanSeek(bCanSeek: WordBool); 
function GetCanSeek: WordBool; 
function GetBufferingTimeElapsed: Integer; 
function GetBufferingTimeRemaining: Integer; 
function GetConnectionBandwidth: Integer; 
function GetPreferedLanguageString: WideString; 
function GetPreferedLanguageID: Integer; 
function GetUserCountryID: Integer; 
function GetNumSources: Smallint; 
function GetSourceTransport(nSourceNum: Smallint): WideString; 
function GetWantErrors: WordBool; 
procedure SetWantErrors(bVal: WordBool); 
function GetShuffle: WordBool; 
procedure SetShuffle(bVal: WordBool); 
function GetVersionInfo: WideString; 
function GetLastMessage: WideString; 
function GetLastErrorSeverity: Integer; 
function GetLastErrorRMACode: Integer; 
function GetLastErrorUserCode: Integer; 
function GetLastErrorUserString: WideString; 
function GetLastErrorMoreInfoURL: WideString; 
procedure SetPreFetch(bVal: WordBool); 
function GetPreFetch: WordBool; 
procedure SetRegion(const pVal: WideString); 
function GetRegion: WideString; 
function GetIsPlus: WordBool; 
function GetConsoleEvents: WordBool; 
procedure SetConsoleEvents(bVal: WordBool); 
function GetDRMInfo(const pVal: WideString): WideString; 
property ControlInterface: IRealAudio read GetControlInterface; 
property DefaultInterface: IRealAudio read GetControlInterface; 

网页中wmp视频控制之使用指南 

//wmplayer.controls.currentPosition = wmplayer.currentMedia.duration * (i / 100) 
// 
// 
// 
□播放方法和属性 
除播放、暂停、停止外,媒体播放器还可用下面的属性: 
扫描(Scanning)——与录像机的快进快倒功能类似; 
搜索(Seeking)——直接移到剪辑中标记的特定上演时间; 

□播放 
媒体播放器提供了两种技术来指定要播放的媒体名称(剪辑),你可以设置 FileName 属性,或调用 Open 方法。 如果 AutoStart 属性的值是 true 的话,当 FileName 属性被设置成该电影剪辑的 URL 后,该电影剪辑将开始播放;否则电影剪辑不会开始播放, 除非你调用 Play 方法。 Open 方法开始播放是异步的,它不象 Play 方法要等到其他的进程结束后才开始播放。 
媒体播放器提供了下面的类似录像机的属性和方法来控制流媒体的播放: 
Play、Stop、Pause 方法,来开始、停止、暂停流媒体。 
PlayCount 属性,设置文件播放的次数。 
AutoRewind 属性,确定当停止播放时是否返回到电影剪辑的开始部分。 

□音频控制 
媒体播放器提供了下面的属性来管理音频: 
Balance 属性,确定左右音箱的声音平衡; 
Volume 属性,用来加大或降低音量; 
Mute 属性,用来关闭或打开声音; 
※你可以将 ShowAudioControls 属性设为 true 来在控制栏添加处理声音的控件。 

□扫描 
媒体播放器提供了下面的属性用以扫描: 
FastForward 方法,快进; 
FastReverse 方法,快倒; 
Rate属性,改变播放速率; 
※要想使电影剪辑能被扫描,必须将 CanScan 和 AllowScan 属性设为 true。 

□搜索 
用于搜索的属性有: 
MarkerCount 属性,指剪辑中标记的总数量; 
CurrentMaker、GetMarkerName、GetMarkerTime 方法,用于返回标记信息; 
MarkerHit 事件,当遇到标记时触发; 
CurrentPosition 属性,当前位置(用秒度量),可用来将播放头移到剪辑中指定的点; 
PositionChange 事件,当设置 CurrentPosition 属性时触发; 
※要想搜索到任意的时间,必须将 CanSeek 属性设为 true,要想搜索到标记点,必须将 CanSeekToMarkers 属性设为 true。 

□媒体播放器的外观界面 
在网页中,你可以通过相关属性来控制媒体播放器的哪些部分出现,哪些部分不出现。 
媒体播放器包括如下元素: 
Video Display Panel:视频显示面板; 
Video Border:视频边框; 
Closed Captioning Display Panel;字幕显示面板; 
Track Bar;搜索栏; 
Control Bar with Audio and Position Controls:带有声音和位置控制的控制栏; 
Go To Bar:转到栏; 
Display Panel:显示面板; 
Status Bar:状态栏; 
下面的属性用来决定显示哪一个元素: 
ShowControls 属性:是否显示控制栏(包括播放控件及可选的声音和位置控件); 
ShowAudioControls 属性:是否在控制栏显示声音控件(静音按钮和音量滑块); 
ShowPositionControls 属性:是否在控制栏显示位置控件(包括向后跳进、快退、快进、向前跳进、预览播放列表中的每个剪辑); 
ShowTracker 属性:是否显示搜索栏; 
ShowDisplay 属性:是否显示显示面板(用来提供节目与剪辑的信息); 
ShowCaptioning 属性:是否显示字幕显示面板; 
ShowGotoBar 属性:是否显示转到栏; 
ShowStatusBar 属性:是否显示状态栏; 

□播放列表 
媒体播放器提供下面的方法来访问播放列表中的剪辑: 
Next 方法,跳到节目(播放列表)中的下一个剪辑; 
Previous 方法,跳回到节目中的上一个剪辑; 
媒体播放器的一个特性是能够预览节目中的每一个剪辑,使用如下属性: 
PreviewMode 属性,决定媒体播放器当前是否处于预览模式; 
CanPreview 属性,决定媒体播放器能否处于预览模式; 
在windows 媒体元文件中,可以为每一个剪辑指定预览时间——PREVIEWDURATION,如果没有指定,那么默认的预览时间是10秒钟。 
你也可以用Windows 媒体元文件来添加 watermarks 与 banners,元文件也支持插入广告时的无间隙流切换。 

□节目信息 
使用 GetMediaInfoString 方法可以返回相关剪辑或节目的如下信息: 
文件名:File name 
标题:Title 
描述:Description 
作者:Author 
版权:Copyright 
级别:Rating 
URLs:logo icon、watermark、banner的地址 
剪辑信息可以放在媒体文件中,也可以放在Windows 媒体元文件中,或者两者都放。 

剪辑信息可以放在媒体文件中,也可以放在windows 媒体元文件中,或者两者都放。如果在元文件中指定了剪辑信息,那么用 GetMediaInfoString 方法返回的就是元文件中的信息,而不会返回剪辑中包含的信息。 
在元文件中,附加信息可以放置在每一个剪辑或节目的 PARAM标签中。你可以为每个剪辑添加任意多个 PARAM 标签,用来存储自定义的信息或链接到相关站点。在 PARAM 标签中的信息可以通过 GetMediaParameter 方法来访问。 
下面的属性返回有关大小和时间的信息: 
ImageSourceHeight、ImageSourceWidth:返回图像窗口的显示尺寸; 
Duration 属性,返回剪辑的长度(秒), 要检测这个属性是否包含有效的数值,请检查IsDurationValid 属性。(对于广播的视频,其长度是不可预知的)。 
详细出处参考:http://www.jb51.net/article/21413.htm
字符集
正则表达式语法   
  
正则表达式是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”)。模式描述在搜索文本时要匹配的一个或多个字符串。   
  
正则表达式示例   
  
表达式 匹配     
/^\s*$/   
匹配空行。   
  
/\d{2}-\d{5}/   
验证由两位数字、一个连字符再加 5 位数字组成的 ID 号。   
  
/<\s*(\S+)(\s[^>]*)?>[\s\S]*<\s*\/\1\s*>/   
匹配 HTML 标记。   
  
下表包含了元字符的完整列表以及它们在正则表达式上下文中的行为:   
  
  
字符 说明     
\   
将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,“n”匹配字符“n”。“\n”匹配换行符。序列“\\”匹配“\”,“\(”匹配“(”。   
  
^   
匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与“\n”或“\r”之后的位置匹配。   
  
$   
匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与“\n”或“\r”之前的位置匹配。   
  
*   
零次或多次匹配前面的字符或子表达式。例如,zo* 匹配“z”和“zoo”。* 等效于 {0,}。   
  
+   
一次或多次匹配前面的字符或子表达式。例如,“zo+”与“zo”和“zoo”匹配,但与“z”不匹配。+ 等效于 {1,}。   
  
?   
零次或一次匹配前面的字符或子表达式。例如,“do(es)?”匹配“do”或“does”中的“do”。? 等效于 {0,1}。   
  
{n}   
n 是非负整数。正好匹配 n 次。例如,“o{2}”与“Bob”中的“o”不匹配,但与“food”中的两个“o”匹配。   
  
{n,}   
n 是非负整数。至少匹配 n 次。例如,“o{2,}”不匹配“Bob”中的“o”,而匹配“foooood”中的所有 o。“o{1,}”等效于“o+”。“o{0,}”等效于“o*”。   
  
{n,m}   
M 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,“o{1,3}”匹配“fooooood”中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。   
  
?   
当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是“非贪心的”。“非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的“贪心的”模式匹配搜索到的、尽可能长的字符串。例如,在字符串“oooo”中,“o+?”只匹配单个“o”,而“o+”匹配所有“o”。   
  
.   
匹配除“\n”之外的任何单个字符。若要匹配包括“\n”在内的任意字符,请使用诸如“[\s\S]”之类的模式。   
  
(pattern)   
匹配 pattern 并捕获该匹配的子表达式。可以使用 $0…$9 属性从结果“匹配”集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用“\(”或者“\)”。   
  
(?:pattern)   
匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用“or”字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经济的表达式。   
  
(?=pattern)   
执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配“Windows 2000”中的“Windows”,但不匹配“Windows 3.1”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。   
  
(?!pattern)   
执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配“Windows 3.1”中的 “Windows”,但不匹配“Windows 2000”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。   
  
x|y   
匹配 x 或 y。例如,'z|food' 匹配“z”或“food”。'(z|f)ood' 匹配“zood”或“food”。    
  
[xyz]   
字符集。匹配包含的任一字符。例如,“[abc]”匹配“plain”中的“a”。    
  
[^xyz]   
反向字符集。匹配未包含的任何字符。例如,“[^abc]”匹配“plain”中的“p”。    
  
[a-z]   
字符范围。匹配指定范围内的任何字符。例如,“[a-z]”匹配“a”到“z”范围内的任何小写字母。    
  
[^a-z]   
反向范围字符。匹配不在指定的范围内的任何字符。例如,“[^a-z]”匹配任何不在“a”到“z”范围内的任何字符。    
  
\b   
匹配一个字边界,即字与空格间的位置。例如,“er\b”匹配“never”中的“er”,但不匹配“verb”中的“er”。    
  
\B   
非字边界匹配。“er\B”匹配“verb”中的“er”,但不匹配“never”中的“er”。    
  
\cx   
匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回车符。x 的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是“c”字符本身。    
  
\d   
数字字符匹配。等效于 [0-9]。    
  
\D   
非数字字符匹配。等效于 [^0-9]。    
  
\f   
换页符匹配。等效于 \x0c 和 \cL。   
  
\n   
换行符匹配。等效于 \x0a 和 \cJ。   
  
\r   
匹配一个回车符。等效于 \x0d 和 \cM。   
  
\s   
匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。   
  
\S   
匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。    
  
\t   
制表符匹配。与 \x09 和 \cI 等效。   
  
\v   
垂直制表符匹配。与 \x0b 和 \cK 等效。   
  
\w   
匹配任何字类字符,包括下划线。与“[A-Za-z0-9_]”等效。    
  
\W   
与任何非单词字符匹配。与“[^A-Za-z0-9_]”等效。    
  
\xn   
匹配 n,此处的 n 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,“\x41”匹配“A”。“\x041”与“\x04”&“1”等效。允许在正则表达式中使用 ASCII 代码。   
  
\num   
匹配 num,此处的 num 是一个正整数。到捕获匹配的反向引用。例如,“(.)\1”匹配两个连续的相同字符。    
  
\n   
标识一个八进制转义码或反向引用。如果 \n 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八进制数 (0-7),那么 n 是八进制转义码。   
  
\nm   
标识一个八进制转义码或反向引用。如果 \nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用。如果 \nm 前面至少有 n 个捕获,则 n 是反向引用,后面跟有字符 m。如果两种前面的情况都不存在,则 \nm 匹配八进制值 nm,其中 n 和 m 是八进制数字 (0-7)。   
  
\nml   
当 n 是八进制数 (0-3),m 和 l 是八进制数 (0-7) 时,匹配八进制转义码 nml。   
  
\un   
匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (?)。 
FileUtils_Ver1.0 io-(文件删除、查找)
package day0703;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.List;

/**
 * 提供文件操作的方法(1、删除目录中的所有内容,2、查找用户需要的文件)
 * 
 * @author Administrator
 * 
 */
public class FileUtils {
	/**
	 * 删除指定目录中的所有内容
	 * 
	 * @param dir
	 *				将要删除的目录
	 * @return 
	 * 				true删除成功 false删除失败---递归尽量写成private方法,public去调用它
	 */
	public static boolean deleteDir(File dir){
		if(dir==null ||!dir.exists()||!dir.isDirectory()){
			throw new IllegalArgumentException("目录必须有效");//抛出错误的参数异常
		}
		return doDelete(dir);
	}
	/**
	 * 
	 * deleteDir()重载
	 * 
	 * @param pathname 
	 * 					将要删除的目录路径
	 * @return 
	 * 					返回deleteDir()的返回值,true:删除成功,false:删除失败
	 */
	public static boolean deleteDir(String pathname){
		File file=new File(pathname);
		return deleteDir(file);
	}
	/**
	 * 递归删除某目录下的所有内容
	 * 
	 * @param dir
	 * 					将要删除的目录		
	 * @return
	 * 					true:删除成功,false:删除失败
	 */
	private static boolean doDelete(File dir) {
		File[] subs= dir.listFiles();
		//delete subs
		for(File sub:subs){
			if(sub.isDirectory()){
				doDelete(sub);//递归
			}else{
				sub.delete();
			}
		}
		return dir.delete();
	}
	/**
	 * 
	 * @param dir
	 *				要查找的目录
	 * @param ff
	 *				符合用户需要的文件过滤器
	 * @return 
	 * 				若没有找到匹配的文件,则返回null;若找到,则返回数组(包含所有找到的匹配文件)
	 */
	public static File[] findFiles(File dir, FileFilter ff) {
		List<File> fileList = new ArrayList<File>();
		//把找到的文件放入fileList
		doFind(fileList,dir,ff);
		return fileList.toArray(new File[]{});
		//把集合变成数组
		
	}
	/**
	 * findFiles()重载
	 * @param dir
	 * 					要查找的目录
	 * @param suffix
	 * 					
	 * @return
	 */
	public static File[] findFiles(File dir,final String suffix){
		return findFiles(dir,new FileFilter(){
			public boolean accept(File pathname){
				if(pathname.isFile()&&pathname.getName().endsWith(suffix)){
					return true;
				}
				return false;
			}
		});
	}
	/**
	 * 
	 * @param fileList
	 * 					将查找到的文件放入此容器中
	 * @param dir
	 * 					将要查找的目录
	 * @param ff
	 * 					符合需要的文件过滤器		
	 */
	private static void doFind(List<File> fileList,File dir,FileFilter ff){
		File[] subs=dir.listFiles();
		for(File sub:subs){
			if(sub.isDirectory()){
				doFind(fileList,sub,ff);
			}else{
				if(ff.accept(sub)){
					fileList.add(sub);
				}
			}
		}
		
	}
	public static void main(String[] args) {
		
		File[] file = findFiles(new File("F:/a"), new MyFileFilter());
	}

}
filechooser&& FileFilter 文件过滤器
教你用Java用JFileChooser设置FileFilter文件过滤 
2009-08-27 20:09
Java 设置文件过率比较麻烦,不像C#直接过滤语句就行。 下面是一个简单示例,让大家明白原理

首先创建一个java 类 继承 FileFilter 例如:

import java.io.File;

import javax.swing.filechooser.FileFilter;


public class MyFileFilter extends FileFilter{

   public boolean accept(File f) {
   String tmp=f.getName().toLowerCase();
//   显示文件夹
         if(f.isDirectory()){
             return true; 
         }
//       循环过滤文件过滤
        if(tmp.endsWith(".jpg") || tmp.endsWith(".gif")   ){
           return true;
        }
         return false;
}

public String getDescription() {
       return "图像文件(*.jpg,*gif)";
   } 

}


使用方法:

JFileChooser fileDialog=new JFileChooser("D:");

MyFileFilter mf=new MyFileFilter();

this.fileDialog.setFileFilter(mf);

------------------------------------------------------------------------------------------------------------------------------

我以前写写了一个FileFilter类,把功能增强一点,蛮好用的。由于自己主要搞WEB项目所以很少用。下面是源码。 很简单的,如果不想打代码,直接拿去用也可以。

import java.io.File;

import javax.swing.filechooser.FileFilter;


public class MyFileFilter extends FileFilter{

private int length;
private String[] filters=new String[100];
private String desc;

public MyFileFilter(){

}

public MyFileFilter(String str){
   this.filters[length]=str;
   length++;
}
public MyFileFilter(String str,String desc){
   this.filters[length]=str;
   this.desc=desc;
   length++;
}




public boolean accept(File f) {
   String tmp=f.getName().toLowerCase();
//   如果小于0 显示所有文件
   if(length==0){
    return true; 
   }
//   显示文件夹
         if(f.isDirectory()){
             return true; 
         }
//       循环过滤文件过滤
         for(int i=0;i<length;i++){
        if(tmp.endsWith(this.filters[i])){
           return true;
        }
         }
         return false;
}

/**
* 
* @param str 过滤器名称 例如:".jpg"
*/
public void addFilter(String str){
   this.filters[length]=str;
   this.length++;
}
/**
* 
* @param str 过滤器名称 例如:".jpg"
* @param desc @param desc 此过滤器的描述。例如:"JPG and GIF Images" 
*/
public void addFilter(String str,String desc){
   this.filters[length]=str;
   this.desc=desc;
   length++;
}

/** 
* @param str 传递多个 过滤器名称 例如:{".jpg",".gif"}
* @param desc 此过滤器的描述。例如:"JPG and GIF Images" 
*/
public void addFilter(String[] str,String desc){
   this.filters=str;
   this.desc=desc;
   this.length=str.length;
}

/**
* 
* @param desc 此过滤器的描述。例如:"JPG and GIF Images" 
*/
public void setDesc(String desc) {
   this.desc=desc;
} 


@Override
public String getDescription() {
   return desc;
} 
}




public boolean accept(File f) {
   String tmp=f.getName().toLowerCase();

         if(f.isDirectory() || tmp.endsWith(".jpg") || tmp.endsWith(".gif")    ){
             return true;
         }
         return false;
}

}


public String getDescription() {
   return "图像文件(*.jpg,* .gif)";
}
}


这样算是完成了,比较简单。

使用方法

JFileChooser fileDialog=new JFileChooser("D:");

MyFileFilter mf=new MyFileFilter();
   mf.addFilter(".jpg");
   mf.addFilter(".gif");
   mf.setDesc("选择文件(*.jpg,*.gif)");

// this.fileDialog.setFileFilter(mf);
   this.fileDialog.addChoosableFileFilter(mf);

   fileDialog.addChoosableFileFilter(new MyFileFilter(".jpg","图像文件(*.jpg)"));
   fileDialog.addChoosableFileFilter(new MyFileFilter(".gif","图像文件(*.gif)"));

上面代码完成了 大家自己去试下吧


Global site tag (gtag.js) - Google Analytics