2005年7月19日
#
看上了google的blogger.com,终于搬家了!
新的地址是:
http://www.soapui.cn
2005年7月9日
#
记录一下,省得忘了。
先要增加bonding的支持,改/etc/module.conf(redhat as4上是 cat /etc/modprobe.conf)
alias bond0 bonding
options bond0 miimon=100 mode=0 (mode=1是主备)
[root@work network-scripts]# cat ifcfg-bond0
DEVICE=bond0
BOOTPROTO=none
IPADDR=220.165.4.182
NETMASK=255.255.255.0
ONBOOT=yes
TYPE=Ethernet
GATEWAY=220.165.4.1
[root@work network-scripts]# cat ifcfg-eth0
DEVICE=eth0
USERCTL=no
ONBOOT=yes
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
[root@work network-scripts]# cat ifcfg-eth1
DEVICE=eth1
USERCTL=no
ONBOOT=yes
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
网卡要一样,要不然就会warning,好象有的机器还会崩掉
2005年6月27日
#
继续试用O/R Broker,发现一个NULL造成的问题
我用2.0.2的Broker,数据库是oracle8i,使用apache common dbcp,假定有一个对象
class A{
private String name;
}
在Save这个A对象的时候,根据orbroker.xml文件的定义,会调用
INSERT INTO A(name) VALUES(?);
来处理处理,如果name属性为null,根据jdbc开发的经验,应该是这样
stmt.setNull(1,Types.VARCHAR);
但是,O/R Broker运行的结果却是报“net.sourceforge.orbroker.QueryException: 无效的列类型”,在eclipse 中通过debug功能,将断点设在
public void setNull(int parameterIndex, int sqlType) throws SQLException
{ checkOpen(); try { _stmt.setNull(parameterIndex,sqlType); } catch (SQLException e) { handleException(e); } }
通过debug发现,这儿的sqlType设的是0,也就是java.sqlTypes类中定义的
public final static int NULL = 0;
而不是我们期望的
public final static int VARCHAR = 12;
在ibatis里面,可以通过对result-object每个属性强行指令sql-type或是jdbctype来控制,但是在O/R Broker里面,我没有发现哪份文档有这个介绍。
继续测试,O/R Broker的这个bug带来一系列的问题。
象这一段配置
<property name="owner">
<sub-query sql-statement="findAccountByName">
<set-parameter name="name" from-column="OWNER" />
</sub-query>
</property>
如果检索到的OWNER字段不为NULL,一点问题都没有,如果为NULL,你又会看到可爱的“net.sourceforge.orbroker.QueryException: 无效的列类型”了。
1、sub-query
Catalog对象有个owner的属性,类定义
private Account owner;
Account的findAccountByXXX的sql-statement定义如下:
<sql-statement id="findAccount" result-object="Account">
SELECT * FROM market_user_index t
<append-statement id-suffix="ByUUID">
WHERE t.uuid=:uuid
</append-statement>
<append-statement id-suffix="ByName">
WHERE t.usr=:name
</append-statement>
</sql-statement>
定义Catalog的result-object。
<result-object id="Catalog"
<property name="owner">
<sub-query sql-statement="findAccountByName">
<set-parameter name="name" from-column="OWNER" />
</sub-query>
</property>
2、pl/sql
<sql-statement id="syncAccount"
external-source="/com/yninfo/market/services/orbroker/syncAccount.sql" />
syncAccount.sql文件内容如下:
DECLARE
v_account MARKET_USER_INDEX%ROWTYPE;
BEGIN
SELECT * INTO v_account
FROM MARKET_USER_INDEX t
WHERE t.usr=:account.loginName;
IF v_account.uuid is null THEN
UPDATE
MARKET_USER_INDEX t
SET
t.uuid=:account.id
WHERE
t.time=v_account.time;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
BEGIN
INSERT INTO
MARKET_USER_INDEX(TIME,USR,UUID)
VALUES(
to_char(sysdate,'yyyymmddhh24misssss'),
:account.loginName,
:account.id);
END;
END;
2005年6月26日
#
webalizer的安装很简单,网上一堆的教程,或者是configura的时候--help一下,或者是看看带着的INSTALL文档,轻轻松松装上。
问题是,我的日志文件是做了轮循配置的,而webalizer里面,只能指定一个logfile,流量分析,自然是要几个月的并一起对比分析了,怎么办呢?
TransferLog "|/www/apache_1.3.33/cronolog-1.6.2/sbin/cronolog /www/apache_1.3.33/logs/access_%Y%m%d.log"
ErrorLog "|/www/apache_1.3.33/cronolog-1.6.2/sbin/cronolog /www/apache_1.3.33/logs/errors_%Y%m%d.log"
看了一下webalizer的FAQ,说是要跟mergelog配合着用。使用管道来处理“mergelog logfile logfile1 logfile2。。。|webalizer”。问题解决。
写了段解决的解本,偷偷懒,省得自己每次敲半天键盘(要真正偷懒的话,不如放/etc/crontab里面)
[root@localhost ~]# cat /www/webalizer.sh
#!/bin/sh
/www/mergelog-4.5/bin/mergelog /www/apache_1.3.33/logs/access_$1*.log |/www/webalizer-2.01-10-linuxelf-x86/webalizer -o /www/apache_1.3.33/htdocs/logs -c /www/webalizer-2.01-10-linuxelf-x86/work.conf -n xxx.domain.com
注:
webalizer可以从http://www.webalizer.org/获得
mergelog可以从http://mergelog.sourceforge.net/获得
在我测试时用的一套老系统的数据库中,有个fee的字段,存放格式为“fee1,fee2,fee3....”,我定义PO时,这样定义
private String[] fee;
public void setFee(String[] );
public String[] getFee();
再定义了个parseFee(String fee)的方法(最先是定义成setFee(String fee),结果运行的时候说是“More than one method setFee(?). Please use attribute 'class' to narrow.”,只好改成parseFee)
public void parseFee(String fee) {
this.fee = fee == null
? null
: fee.split(",");
}
在result-object中,这样定义fee。
<method name="parseFee">
<argument>
<column name="FEE" />
</argument>
</method>
前段时间白衣在QQ群、blog上推荐了O/R Broker ,趁着周末,写了些小例子,Hello了一把。
同ibatis相比较,orbroker显得不是那么成熟,比如:
简单试用下来,相对ibatis,orbroker还是有不少新意的。
1、轻巧。只需要一个90.6k的orbroker-2.0.2.jar文件就行,依赖库也没有。
2、sql-statement提供了external-source属性,可以将具体的sql语句放在一个一个单独的文本文件里面。
3、在sql中允许使用velocity、freemarker,哈哈!这个功能帅呆了,远比ibatis里面通过那么几个xml标签来动态处理sql语句的灵活!比如我用freemarker做的例子:
例1:
select
t.time,
t.usr,
t.pass,
t.ip
from market_user_index t
where
rownum=1
<#if name?exists>
and
t.usr= :name
</#if>
<#if id?exists>
and
t.time = :id
</#if>#IF>
例2:
UPDATE
market_user_index t
SET
t.usr=:user.name,
t.pass=:user.password,
t.ip=:user.ipAddress
<#if time?exists>
,
t.login=<#--:time-->
<#assign temp=["1","2","3"]/>
<#list temp as a>
<#if a_index > 0>||</#if>'${a}'
</#list>
</#if>
WHERE
t.time=:user.id
2005年5月27日
#
spring部分:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!--
加入spring后,把dataSource定义交给hibernate.cfb.xml,感觉hibernat部分要完整些,与没加入spring基本没区别(不知道是好事还是坏事)
<property name="dataSource">
<ref bean="dataSource" />
</property>
-->
<property name="configLocation">
<value>
classpath:/mapping/hibernate.cfg.xml</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="AbstractDAOProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="UserDAO"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
parent="AbstractDAOProxy">
<property name="target">
<!--
DAO直接定义在这儿,省得单独定义一个bean id="xxxxDAO"
由于是测试工程,就直接由测试代码操作DAO,不写services了
-->
<bean class="dao.UserDAOHibernate">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
</property>
</bean>
</beans>
Hibernate.cfb.xml 部分(与没使用spring相同)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE
Hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" >
<hibernate-configuration>
<session-factory name="spring_hibernate">
<property name="hibernate.connection.driver_class">
oracle.jdbc.driver.OracleDriver
</property>
<property name="hibernate.connection.url">
jdbc:oracle:thin:@(DESCRIPTION = (ADDRESS_LIST = (ADDRESS =
(PROTOCOL = TCP)(HOST = xxx.xxx.xxx.xxx)(PORT = 1521)) )
(CONNECT_DATA = (SID = xx)(SERVER = DEDICATED)) )
</property>
<property name="hibernate.connection.username">xxxx</property>
<property name="hibernate.connection.password">
xxxxxxxxxxxx
</property>
<property name="hibernate.dbcp.maxActive">100</property>
<property name="hibernate.dbcp.whenExhaustedAction">1</property>
<property name="hibernate.dbcp.maxWait">120000</property>
<property name="hibernate.dbcp.maxIdle">10</property>
<property name="hibernate.dialect">
org.hibernate.dialect.OracleDialect
</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">create-drop</property>
<property name="hibernate.cache.provider_class">
org.hibernate.cache.OSCacheProvider
</property>
<mapping resource="mapping/People.hbm.xml" />
<mapping resource="mapping/Lob.hbm.xml" />
<class-cache usage="read-only" class="domain.People" />
</session-factory>
</hibernate-configuration>
2005年4月22日
#
URIEncoding |
This specifies the character encoding used to decode the URI bytes,
after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
|
useBodyEncodingForURI |
This specifies if the encoding specified in contentType should be used
for URI query parameters, instead of using the URIEncoding. This
setting is present for compatibility with Tomcat 4.1.x, where the
encoding specified in the contentType, or explicitely set using
Request.setCharacterEncoding method was also used for the parameters from
the URL. The default value is false.
|
在网上google到的资料,大多是叫你改Tomcat5
Connector处的URIEncoding参数的
2005年4月21日
#
对于没有参数的情况,比较好解决
假定properties里面这样写
test=测试信息在ftl中可以使用ww的taglib,也可以使用ftl语法
${action.getText("test")}<@ww.text name="'test'" />对于有参数的,就比较麻烦了,比如properties定义为
test=测试信息{0},{1}用taglib是没问题的,跟jsp中间差不多
<@ww.text name="'test'" > <@ww.param>WebWork</@ww.param> <@ww.param>FreeMarker</@ww.param></@ww.text>用ftl语法就没办法了,google半天,最后发现一个变通的方法(实际上还是用了taglib)
<@ww.set name="list" value="{'WebWork','Freemarker'}" scope="page"/>
${action.getText("test",list)}参考:
http://forums.opensymphony.com/message.jspa?messageID=3628
2005年4月19日
#
以前是一直抵触检验器的,非要继承那个ActionSupport,心里觉得烦,就一直没用。这次拿来正儿八经做项目,才发现自己写检验的那个痛苦啊!if else的弄半天,晕死
首先把原来的Action全加上extends ActionSupport这么一堆,然后放一个classpath:validators.xml 的文件,内容很简单,就是定义哪个检验器叫什么名字,可以抄webwork自带的,也可以自己写。
<validators>
<validator name="email" class="com.opensymphony.xwork.validator.validators.EmailValidator"/>
<validator name="myRequiredString" class="com.yninfo.move.util.MyRequiredStringValidator"/>
<validator name="requiredString" class="com.opensymphony.xwork.validator.validators.RequiredStringValidator"/>
<validator name="stringLength" class="com.opensymphony.xwork.validator.validators.StringLengthFieldValidator"/>
<validator name="required" class="com.opensymphony.xwork.validator.validators.RequiredFieldValidator"/>
<validator name="intRange" class="com.opensymphony.xwork.validator.validators.IntRangeFieldValidator"/>
<validator name="myIntRange" class="com.yninfo.move.util.MyIntRangeFieldValidator"/>
<validator name="fieldEq" class="com.yninfo.move.util.MyFieldEqualsValidator"/>
</validators>
完了以后在Action所在的包下面放一个ActionName-validation.xml文件,ww会来读这个文件,看看要对哪些field检验,检验以后显示什么东东。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd" >
<validators>
<field name="model.workParameter.password">
<field-validator type="myRequiredString">
<param name="field">hasWorkAccount</param>
<param name="trim">true</param>
<message>登录密码不能为空</message>
</field-validator>
</field>
</validators>
自然少不了view上的东西,我一直用freemarker做view,先是试了一下ww提供的taglib,惨不忍睹啊!不能用simple的template,只能用xhtml的,页面撑得死去活来,一气之下,写了个freemarker的宏来干活。
<#macro showErrorMessage local>
<#if
fieldErrors?has_content &&
fieldErrors[local]?exists
>
<br>
<font color="red">
<#--
${fieldErrors[local][0]}
-->
<#list fieldErrors[local] as error>
${error}
</#list>
</font>
</#if>
</#macro>
问题1:ww检验器是按fieldname把errormessage扔进fieldErrors这个map里面,在freemarker里面根据fieldname(也就是local参数)读出来的是个集合,由于一开始没用short-circuit="true" ,结果ww把一个field所有的field-validator定义的检验都弄了一遍,比如用户名这儿,显示出来就是”用户名不能为空 用户名必须为2至8个字符 提示信息3 提示信息4 。。。“这类,又难看又不好又浪费。加上short-circuit="true" 以后,安静多了。
问题2:用户第一次访问的时候,ww也来这么检验一次,自然了,什么信息都是null的,满屏提示xxx不能为空、xxx必须填写之类的,估计用户得把人给吓倒。好歹你第一次的时候别显示,用户提交了再显示这些错误啊。
不对ww动手术就解决问题,估计希望不大,反正将就着还过得去就行,所以我就用了个歪招。
加了个token的属性,用户一进来,token肯定是null的,就用freemarker做了下手脚,要是token是null的,有错误也不给显示(后台照样在检验一回,只是门面上没显示出来罢了)。
webwork.xml上也要改改,不能把检验的拦截器给扔了
<interceptor-ref name="validation" />
<interceptor-ref name="workflow" />
自己写了几个检验器
1、myRequiredString、myIntRange
先判断一个field B是否为true,为true才判断字段B是不是存在。ww自带的检验器就没法干这个活了。
类代码:public class MyRequiredStringValidator extends RequiredStringValidator {
private String field;
public void validate(Object object) throws ValidationException {
Object obj = this.getFieldValue(getField(), object);
if (obj instanceof Boolean) {
if (((Boolean) obj).booleanValue()) super.validate(object);
}
else if (obj instanceof String) {
if (new Boolean((String) obj).booleanValue()) super.validate(object);
}
else {
throw new ValidationException("UnSupport Validation field Type");
}
}
检验配置: <field name="model.workParameter.userName">
<field-validator type="myRequiredString"> <param name="field">hasWorkAccount</param> <param name="trim">true</param> <message>登录用户名不能为空</message> </field-validator> </field>
2、fieldEq
就是比较两个Field是不是相等,比如两次输密码是不是一样,就用这个了。
程序代码:
public class MyFieldEqualsValidator extends RequiredStringValidator {
private String field;
/* (non-Javadoc)
* @see com.opensymphony.xwork.validator.Validator#validate(java.lang.Object)
*/
public void validate(Object object) throws ValidationException {
Object v1 = getFieldValue(getField(), object);
Object v2 = getFieldValue(getFieldName(), object);
if (v1 != null && v2 != null && !v1.equals(v2))
addFieldError(getFieldName(), object);
}
配置文件: <field name="model.authPasswordAgain">
<field-validator type="requiredString" short-circuit="true">
<param name="trim">true</param>
<message>密码验证不能为空</message>
</field-validator>
<field-validator type="stringLength" short-circuit="true">
<param name="maxLength">20</param>
<param name="minLength">4</param>
<message>验证密码必须在${minLength}和${maxLength}个字符之间</message>
</field-validator>
<field-validator type="fieldEq" short-circuit="true"> <param name="field">model.authParameter.password</param> <message>两次密码输入不匹配</message> </field-validator> </field>
2005年4月4日
#
这两天在找3des算法的api,结果发现这么个东东:http://www.java-encryption.com/
按它讲的,是“Java encryption API without JCE by DES,3DES,AES.”,使用也很简单,主要是三个类
两个流,使用起来跟普通的I/O stream没什么区别,构造式是
DecryptInputStream(int algorithm,byte[] password,java.io.InputStream in)
EncryptOutputStream(int algorithm,byte[] password,java.io.OutputStream out)
一个Seal类,定义了AES、DES、TripleDES三个常量,decrypt、encrypt两个方法用于加/解密。
测试代码如下,非常简单:
try {
Seal seal = new Seal(Seal.TripleDES, SourceKey.getBytes());
byte[] s = seal.encrypt(CryptSourcedata.getBytes());
System.out.println(Hex.encodeHex(s));
System.out.println(ByteToHex.byte2hex(s));
System.out.println(new String(seal.decrypt(s)));
CopyUtils.copy(new FileInputStream("g:/trustix-2.2.i586.iso"),
new EncryptOutputStream(Seal.TripleDES, SourceKey.getBytes(),
new FileOutputStream("C:/b.iso")));
CopyUtils.copy(new DecryptInputStream(Seal.TripleDES, SourceKey
.getBytes(), new FileInputStream("C:/b.iso")), new FileOutputStream(
"C:/c.iso"));
}
catch (EncryptException e) {
e.printStackTrace();
}
对License的声明
One software license is required per Developer.
Run-time royalty free.
大致应该是开发者要买一个许可,运行是免费的。不过我用下来,好象开发的时候也没有提示什么注册之类的信息,或是有功能限制,先将就着用吧
一直以为apache的common codec是用于做音、视频编/解码的,没想到居然还有别的东东,看来俺又可以扔掉一些东西了,有现成的轮子,何必再自己造?或是买些劣品来用?
|
Base64
|
Provides Base64 content-transfer-encoding as defined in
RFC 2045
. This
encoder can optionally create 76 character chunked output
for strict adherence to RFC 2045.
|
|
Hex
|
Converts an array of bytes into an array of characters representing the
hexadecimal values of each byte in order
|
|
BinaryCodec
|
Translates between byte arrays and strings of "0"s and "1"s.
|
|
Soundex
|
Implementation of the Soundex algorithm.
|
|
Metaphone
|
Implementation of the Metaphone algorithm.
|
|
Refined Soundex
|
Alternative implementation of the Soundex algorithm.
|
|
Double Metaphone
|
Alternative implementation of the Metaphone algorithm.
|
|
DigestUtils
|
Provides simple static methods for creating a SHA or MD5 digest.
|
|
URLCodec
|
Implements the www-form-urlencoded encoding scheme, also misleadingly known as URL encoding.
|
|
QuotedPrintableCodec
|
Codec for RFC 1521 MIME (Multipurpose Internet Mail Extensions) Part
One. Rules #3, #4, and #5 of the quoted-printable spec are not
implemented yet |
|
BCodec
|
Identical to the Base64 encoding defined by
RFC 1521
and allows a character set to be specified.
|
|
QCodec
|
Similar to the Quoted-Printable content-transfer-encoding defined in
RFC 1521
and designed to allow text containing mostly ASCII
characters to be decipherable on an ASCII terminal without decoding. |
公司使用的jims平台,MT调用需要使用http GET或是http PostXML两种接口方式。调GET我通常是使用java.net.URL.openStream()来处理,而POST,我就只好自己写socket来处理了。
周末在家闲得没事,为这次开发UA的迁移向导做一些前期准备。迁移向导与UA是走webservices,与各应用程序通讯则是走http GET方式。以前听农民提了一下http client,这次特意看了一下。
http client使用比较简单,看看文档,api,参考一下自带的几个example,如FormLoginDemo.java、PostXML.java、
ProxyTunnelDemo.java,差不多就能上手了。
HttpClient client = new HttpClient();
client.getHostConfiguration().setHost("localhost", 8080, "http");
HttpMethod method = new GetMethod("/abcd");
NameValuePair[] params = new NameValuePair[]{
new NameValuePair("action", "login"),
new NameValuePair("url", "/index.html"),
new NameValuePair("UserId", "用户名"),
new NameValuePair("Password", "password")};
if (method instanceof GetMethod)
((GetMethod)
method).setQueryString(params);
else if (method instanceof PostMethod)
((PostMethod)
method).setRequestBody(params);
int status =
client.executeMethod(method);
System.err.println(status);
System.err.println(method.getResponseBodyAsString());
//method.getResponseHeaders()
method.releaseConnection();
使用HttpClient,下次调jims的时候,不用再傻不拉几的写socket,再考虑代理啊,header、算content-length之类的情况,省事省心
2005年3月23日
#
REMOTEADDR: ${req.remoteAddr}<br>ServletPath: ${req.servletPath}<br><#list req.getHeaderNames() as names> ${names}:${req.getHeader(names)}<br></#list>结果:
REMOTEADDR:61.138.209.5
ServletPath:/admin/Test.action
HOST:61.138.209.5
USER-AGENT:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1
ACCEPT:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
ACCEPT-LANGUAGE:en-us,en;q=0.7,zh-cn;q=0.3
ACCEPT-ENCODING:gzip,deflate
ACCEPT-CHARSET:GB2312,utf-8;q=0.7,*;q=0.7
KEEP-ALIVE:300
CONNECTION:keep-alive
REFERER:http://61.138.209.5/rules/fulltext/FulltextIndex.action;jsessionid=ACJKEHICDPNP
COOKIE:JSESSIONID=ACJKEHICDPNP