用perl脚本解决ant…非法字符:\65279…的问题
用ant管理项目,编译时发现有些utf8编码的java文件无法编译,
报错是…非法字符:\65279…。
在网上查N多与ant有关的文章,但是大多数的解决方案都不是很理想。因为文件比较多,而且我的
机器上也没有装editplus。
索性自己用perl写了一个脚本
如下:
#remove the utf-8 BOM for Ant
sub remove_bom {
#open( my $in, "+<", "src/com/umpay/wap20/mobileProxy/MobileProxy.java" );
foreach my $filename (@_) {
print $filename,"\n";
open( my $in, "+<", $filename );
@lines = <$in>;
$line1 = $lines[0];
$t_chk1 = substr( $line1, 0, 1 ); #0xef -> 239
$t_chk2 = substr( $line1, 1, 1 ); #0xbb -> 187
$t_chk3 = substr( $line1, 2, 1 ); #0xbf ->191
if ( ord($t_chk1) == 239 && ord($t_chk2) == 187 && ord($t_chk3) == 191 )
{
$line1 = substr( $line1, 3 );
seek( $in, 0, 0 );
print $in $line1;
}
close $in;
}
}
&remove_bom("src/com/umpay/wap20/mobileProxy/MobileProxy.java"
,"src/com/umpay/wap20/security/GeneralKeyPairs.java"
,"src/com/umpay/wap20/security/RSAUtil.java"
,"src/com/umpay/wap/pages/WML.java");
其中remove_bom方法就是处理有问题的文件的方法。
把你的项目中出现问题的文件名称一个一个的放到remove_bom的参数列表中就可以了。ant,BOM,ant,BOM
最后运行这个perl文件。之后再运行ant就可以了。
这次春节回家,什么资料也没带,刚好机器里也没什么东东,只有之前装的perl,索性翻了翻perl的文档,发现和ruby最相似的语言原来是perl,应该是ruby抄袭的perl。
Perl的语法要求:每一句后最后都要有分号,最后一句可以省略分号。
Perl 的数据类型包括:Scalars,arrays,hashes。
Scalar:就是变量
Scalar 值可以是字符串,整形或者浮点型。Perl会自动转型,换句话说,perl是若类型语言。但是如果你要定义一个变量,需要使用my这个关键字。使用起来也极其简单。
Scalar values can be used in various ways:
my $animal = "camel";
my $answer = 42;
print $animal;
print "The animal is $animal\n";
print "The square of $answer is ", $answer * $answer, "\n";
一般脚本语言都会预定义一些变量。如果你用过ruby或者python,那你一定深有体会。
在perl中也有一些预定义的变量,比如$_就是预定义值。
试一下这两句,你就知道了。
说实话,这种预定义的特性,对于System Administrator来说是一个好用的属性,但是对于Developer来说,真是很痛苦的一个特性。
Array:就是数组
my @animals = ("camel", "llama", "owl");
my @numbers = (23, 42, 69);
my @mixed = ("camel", 42, 1.23);
print $animals[0]; # prints "camel"
print $animals[1]; # prints "llama"
$#array 为最后一个数组元素的下标。例如:
print $mixed[$#mixed]; # last element, prints 1.23
之前就听说perl的语法及其诡异,但是当我真正用的时候,才发现太诡异了。看下面两句:
if (@animals < 5) { ... }#这一句可以判断@animals中元素的个数。
print @animals;#这一句会打印出@animals中的所有元素。
还有下面这个,和ruby很相似的“范围”语法
@animals[0,1]; # gives ("camel", "llama");
@animals[0..2]; # gives ("camel", "llama", "owl");
@animals[1..$#animals]; # gives all except the first element
This is called an “array slice”.
You can do various useful things to lists:
my @sorted = sort @animals;
my @backwards = reverse @numbers;
当然,这里还有很多特殊的数组, 比如@ARGV(the command line arguments to your script) 还有@_(the arguments passed to a subroutine). 后面会有单独的文章介绍。
Hashes
不多说了,一种键值对的数据结构,附带说两句,其实这个数据结构名叫哈希,其实并不是我们熟悉的hash数据结构,因为正统的hash结构其键值之间应该有一种字面意义上的对应关系,特别是应该有一种hash key 生成体制。Perl中的hash没有这个设计。:
my %fruit_color = ("apple", "red", "banana", "yellow");
You can use whitespace and the => operator to lay them out more nicely:
my %fruit_color = (
apple => "red",
banana => "yellow",
);
#To get at hash elements:
$fruit_color{"apple"}; # gives "red"
#You can get at lists of keys and values with keys() and values().
my @fruits = keys %fruit_colors;
my @colors = values %fruit_colors;
如果要使用hash中的一个元素,则用$符号,如果使用整个hash则使用%符号。例如:
Print $fruit_color{"apple"}; # gives "red"
my @fruits = keys %fruit_colors;
变量作用域:
定义一个变量有两种方式,分别为一下两行:
my $var = "value";
$var = "value";
但是,第二行定义的变量将是一个全局变量,而第一行创建的变量是block级别的变量,这一点和JavaScript的语法非常相似。
如下:
my $x = "foo";
my $some_condition = 1;
if ($some_condition) {
my $y = "bar";
print $x; # prints "foo"
print $y; # prints "bar"
}
print $x; # prints "foo"
print $y; # prints nothing; $y has fallen out of scope
条件语句:
这个与ruby几乎一摸一样:
If与unless
if ( condition ) {
...
} elsif ( other condition ) {
...
} else {
...
}
unless:
unless ( condition ) {
...
}
当然也可以写在一行:
print "Yow!" if $zippy;
print "We have no bananas" unless $bananas;
普通循环:
while ( condition ) {
...
}
until ( condition ) {
...
}
print "LA LA LA\n" while 1; # loops forever
for循环:
for ($i = 0; $i <= $max; $i++) {
...
}
foreach循环:
看例子:
#Hash:
my %fruit_color=("apple"=>"red","banana"=>"yellow");
foreach my $key(keys %fruit_color){
print "The value of $key is $fruit_color{$key}","\n";
}
#Array:
my @fruit_arr=("apple","banana","capp","del");
foreach (@fruit_arr){
print $_,"\n";
}
foreach my $i(@fruit_arr){
print $i,"\n";
}
#还可以写到一行,类似于ruby的语法:
print $_,"\n" foreach (@fruit_arr);
#还可以使用范围:
#比如:
print $list[$_] foreach 0 .. $max;
perl中的变量都是预定义的变量。
比如
my $a="";#如果是字符串类型的变量,””等同于false,其他是true
if($a){
print "true";
}else{
print "false";
}
my $a=0;#如果是数字类型的变量,0等同于false,其他是true
if($a){
print "true";
}else{
print "false";
}
my $b;#如果你定义一个变量,没有赋值,那么当你对它操作的时候会自动赋值,如果需要变量为数字型,就赋值为0,如果需要是字符串型,则赋值为””。
$b++;
print($b);
作为一个SIP Servlet Tutorial,这个文档主要描述了在JavaEE平台下如何开发基于SIP协议的应用服务。当然这个教程也包含了如何将JavaEE技术与SIP应用程序集成。
这个教程主要涉及到的软件有:
1. JavaSE 5.0
2. Glassfish and Sailfin(这是目前为止,我知道的唯一的一个开源的java sip servlet 容器)
3. Netbeans IDE(主要是在sailfin的安装包中有Netbeans的插件,所以使用NB)
4. X-Lite Soft Phone 或者MyFreesipphone
5. Apache Ant
这里就不多介绍SIP协议了,直接进入主题。
熟悉web开发的人基本都知道Http request 和 http Response 两个对象。
对应的,SIP也有对应的对象。
SIP Request
常见SIP Request Action
INVITE 请求初始化一个session
ACK 回应INVITE请求,是(acknowledges的简写)
BYE 请求关闭连接
CANCEL 取消所有未处理的action,但是不释放连接
REGISTER 向服务器注册一个地址,或者说,客户端向服务器发送一个注册请求
什么是SIP Servlets?
SIP Servlet 是一个 基于Java 语言的 server-side 组件,就类似于Java Servlet 一样,只不过SIP Servlet 需要运行在 SIP Servlet 容器中。
而且SIP Servlet主要用来处理SIP请求,而Servlet用来处理HTTP请求。
HTTP Servlet 与 SIP Servlet的不同之处:
1. HTTP Servlet 都会有一个context对象,也就是每个Servlet都有一个上下文的概念。而SIP Servlet没有。
2. HTTP Servlet 通常会返回一个HTML 页面或者某种形式的字符串(比如JSON),而SIP请求通常只是起到连接服务器与客户端的作用。
3. SIP 是一个P2P的协议,所以服务器端同样可以向客户端发送SIP请求。
4. SIP Servlet通常只是扮演一个代理的角色,最终的请求(客户端的请求)会发送到另一个已注册的SIP客户端。而HTTP Servlet会返回给客户端一个HTTP response。
5. SIP协议类似于长连接,但是一个无状态的链接,所以它可以返回个客户端多个相互独立的response,而HTTP是一个无状态的协议,一个Request对一个Response。
6. SIP 请求可以是一个异步的请求。这一点取决于上一个特性。
关于SIP Servlet的annotation。
Annotation 描述
@SipServlet 标示这是一个SIP Servlet
@SipListener 标示这个一个Sip Listener
@SipApplication 用来定义一组SIP Servlets
@SipApplicationKey 为一个SIP请求或者一个SIP session分配一个SipApplicationSession
比如:
@SipServlet
public class MyServlet extends SipServlet{
…
}
@SipApplication 这个注释一般都用在包上。
比如
@SipApplication(name=”MySipApp”)
Package com.example.sip;
而@SipApplicationKey这个注释需要使用在方法上,而且这个方法需要满足以下条件:
1. Public method
2. Static method
3. Return a String
4. 方法的参数必须是SipServletRequest 类型
5. 传入的参数不可变
例如:
@SipApplication(name=”MySipApp”)
Package com.example.sip;
public class MySipApp{
@SipApplicationKey
public static String sessionKey(SipServletRequest request){
Return hash(request.getRequestURI()+getDomain(request.getForm()));
}
}
在一个Sip application中只能有一个SipApplicationKey。
SIP Factory
SIP Factory 是一个Servlet Factory,也就是说所有的SIP请求进入SIP容器后,都要通过SIP Factory来创建一个Servlet实例。所以SIP Factory是整个SIP容器的入口。如果想调用容器中的其他资源,可以通过SIP Factory来调用。而调用的方法包括依赖注入和”查找”两种方式。
看例子:
//通过注释来调用
@Resource
SipFactory sf;
//通过查找的方式
SipFactory sf=(SipFactory)getServletContext().getAttribute(“javax.servlet.sip.SipFactory”);
SIP Session
SIP协议是一个无状态协议,所以一个response只能对应一个request(虽然一个request可以对应多个response)。也就是说多个request之间无法共享数据。
但是SIP Session提供了一种比较便捷的方式,用来存储request之间的共享数据。而这种方式非常类似于HTTP servlet中的Session对象。
但是与HTTP Session不同的是,在SIP应用中还有一个SIP ApplicationSession对象,这个东西很强大,它可以关于整个应用的session信息。
除此之外,还有个SipSessionUtil可以使用。使用方法和SIP Factory类似:
@Resource
SipSessionUtil sessionUtil;
//或者
SipSessionUtil sessionUtil=(SipSessionUtil)getServletContext().getAttribute(“javax.servlet.sip.SipSessionUtil”);
SIP Listeners
SIP application Listeners 就是用来监听SIP相关事件的Java Servlet listeners。它需要实现SIPServletListener接口。同时在其类上标注上Annotation用于简化部署。
比如:
@SipListener
Public class MyListener implements SipServletListener{
…..
}
Back-to-Back User Agent Applications
一个back-to-back user agent(B2BUA)本身就是一个应用程序,同时它还是一个工具,很类似于代理。用于将一个客户端请求,转发给另一个客户端。而javax.servlet.sip.B2buaHelper就是一个实现这种功能的帮助类。
以上内容翻译自SUN的SIP Servlet Tutorial文档。
下面是我自己写的一个简单的sip程序。
package com.ohacker.sip.proxy;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.sip.*;
import javax.servlet.sip.Address;
/**
*
* @author O!Hacker
*/
@javax.servlet.sip.annotation.SipServlet
public class RegistratorServlet extends javax.servlet.sip.SipServlet {
public static ArrayList<Address> CLIENT_LIST=new ArrayList<Address>();
@Override
protected void doInvite(SipServletRequest req) throws ServletException, IOException {
System.err.println("doInvite action: "+ req.getCallId());
Proxy proxy=req.getProxy();
System.err.println(req.getTo().getURI().toString());
proxy.proxyTo(req.getTo().getURI());
}
@Override
protected void doRegister(SipServletRequest req) throws ServletException, IOException {
System.err.println("doRegister action: "+ req.getCallId());
System.err.println("doRegister action: "+ req.getRequestURI());
SipServletResponse resp=null;
Address address=req.getTo();
if(!RegistratorServlet.CLIENT_LIST.contains(address)){
CLIENT_LIST.add(address);
}
System.err.println(CLIENT_LIST.size());
resp=req.createResponse(SipServletResponse.SC_OK);
resp.send();
}
}
Ant 小记
传统的ant 打包的项目需要处理文件和路径。特别是烦人的classpath。Ant内置了很多处理文件和路径的数据类型。比如fileset和path
Fileset可以枚举文件。
比如:
<fileset id=”source.fileset” dir=”src” includes=”**/*.java”/>
其中id是一个引用。其他的操作,可以通过id引用的方式调用它。比如:
<copy todir=”backup”>
<fileset refid=”source.fileset”/>
</copy>
javac
Debugging info
Javac中常用到的选项
Debug=”true” or debug=”false” debu=”true” debuglevel=”lines,vars,source”
Nowarn=”true” verbose=”true” verbose是一个有意思的选项,它会在编译的时候打印出一些编译信息。当然还有classpath。
比如:
<javac destdir="${build.classes.dir}" debug="true" verbose="true" srcdir="src">
<classpath refid="compile.classpath"/>
</javac>
Src也可以用子元素来表示
比如:
<javac destdir="${build.classes.dir}" debug="true" verbose="true">
<classpath refid="compile.classpath"/>
<src path=”src”/>
</javac>
如果源文件分散在多个目录中可以使用引用的方式
比如:
<path id="src.dir">
<pathelement path="src:conf"/>
</path>
<target name="mkdir">
<mkdir dir="${build.classes.dir}"/>
</target>
<target name="compile" depends="mkdir">
<javac destdir="${build.classes.dir}">
<classpath refid="compile.classpath"/>
<src refid="src.dir"/>
</javac>
</target>
还有一种方法,比较土
例子:
<javac destdir="${build.classes.dir}">
<classpath refid="compile.classpath"/>
<src refid="src.dir"/>
<src path=”test”/>
</javac>
但是需要注意的是src指的是一个目录。
在path或者fileset这类标签中常常会遇到类似以”**/*.jsp”这样的匹配模式。下面就讲讲在如何匹配。这种匹配方式好像应该叫做wildcard路径匹配方式。是目前比较常见的3中路径匹配风格之一,(题外话,另两种是mod_rewrite风格和perl 正则风格的路径匹配)
“*”表示一个或多个字符
“?”表示一个字符
“**”表示零个或者多个目录
如果以”/”or”\”结尾,那么就相当于”**”
常见例子:
**/*~ linux 下的备份文件
**/CVS/ cvs元文件
**/.cvsignore cvs文件
**/.svn/ SVN元文件目录
比如在javac中的使用
<javac srcdir=”src” destdir=”build/classes”>
<include name=”org/mama/**/*.java”/>
<exclude name=”org/mama/papa/*.java”/>
</javac>
下面是一个完整的java 项目的build文件
<?xml version="1.0"?>
<project name="java_project" default="archive">
<property file="build.properties"/>
<path id="compile.classpath">
<pathelement location="lib/*.jar"/>
</path>
<path id="src.dir">
<pathelement path="src/java"/>
<pathelement path="src/conf"/>
</path>
<target name="init">
<mkdir dir="${build.classes.dir}"/>
<mkdir dir="dist"/>
</target>
<target name="compile" depends="init">
<javac destdir="${build.classes.dir}">
<classpath refid="compile.classpath"/>
<src refid="src.dir"/>
</javac>
</target>
<target name="archive" depends="compile">
<jar destfile="dist/${project.name}.jar" basedir="build/classes"/>
</target>
<target name="clear" depends="init">
<delete dir="build"/>
<delete dir="dist"/>
</target>
</project>
对应的build.properties文件也很简单
project.name=default
build.classes.dir=build/classes
Glassfish sip server(sailfin) install 安装glassfish sip server
第一步:安装JDK,Ant
第二步:下载sailfin:下载地址
第三步:如果你的机器上正跑着glassfish,先关掉。在你的下载目录下运行:
>java -Xmx256m -jar filename.jar
等解压完后,再运行
>cd sailfin
>ant -f setup.xml
第四步:之后将生成的目录加进环境变量中的Path下。
第五步:在环境变量中加入AS_ADMIN_USER 值为admin
第六步:从新开一个cmd console,运行
asadmin start-domain domain1
第六步:在浏览器中打开http://localhost:4848/
第七步:输入admin/adminadmin
大功告成