2007年10月28日星期日

Spring 2.5新特性――使用注解方式的Ioc

ApplicationContext.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=" http://www.springframework.org/schema/beans"
       xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context "
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd" default-lazy-init="true">
          
    <context:annotation-config />
    <context:component-scan base-package="com.xued.techevaluate.ioc,com.xued.techevaluate" />
   
</beans>

除了要注入一些外部资源,基本上不再需要配置<bean/>项。

两个bean的代码,实现同一个接口

@Component(value="Bloomberg")
public class BloombergDataService implements MarketDataService{

    @PostConstruct
    private void init(){
        System.out.println ("Bloomberg service initiallized");
    }
       
    public String getStockData(String stockCode) {
        StringBuilder sb = new StringBuilder();
        sb.append("/*************************************************************\n" +
                       "*             Bloomberg market Data Provider                 *\n" +
                       "*************************************************************/\n");
        sb.append("StockCode      Company      Price      Amount      Time      ");
        sb.append("\n");
        sb.append(stockCode + "      长江实业      136.60      200000      "+ new Date());
        return sb.toString();
    }

}

@Component(value="Reuters")
public class ReutersDataService implements MarketDataService{

    @PostConstruct
    private void init(){
        System.out.println("Reuters service initiallized");
    }
   
    public String getStockData(String stockCode) {
        StringBuilder sb = new StringBuilder();
        sb.append("/*************************************************************\n" +
                       "*             Reuters market Data Provider                   *\n" +
                       "*************************************************************/\n");
        sb.append("StockCode      Company      Price      Amount      Time      ");
        sb.append ("\n");
        sb.append(stockCode + "      长江实业      136.60      200000      "+ new Date());
        return sb.toString();
    }

}

注入的代码:

@Component(value="dataMiner")
public class DataMiner {

    @Resource(name="Reuters")
    private MarketDataService dataService;

    public String queryByCode(String stockCode){
        return dataService.getStockData(stockCode);
    }
}

采用传统方式取得bean引用:

        ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        instance = (DataMiner)ctx.getBean("dataMiner");

Java实现UCS-2/ISO10646转UTF-8

ISO10646/UCS与UTF-8的对应关系:

U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

-----------------------------------------------------------------------------------------------------------
| UCS-2                                         | UTF-8                                        |
|----------------------------------------------------------------------------------------------------------
| | code                                        | 1st Byte   | 2nd byte   | 3rd Byte   |
|--------------------------------------------------------------------------------------------------------
| 000000000aaaaaaa | 0000 - 007F | 0aaaaaaa | | |
|--------------------------------------------------------------------------------------------------------
| 00000bbbbbaaaaaa | 0080 - 07FF | 110bbbbb | 10aaaaaa | |
|--------------------------------------------------------------------------------------------------------
| ccccbbbbbbaaaaaa | 0800 - FFFF | 1110cccc | 10bbbbbb | 10aaaaaa |
|--------------------------------------------------------------------------------------------------------

public class ISO10646Decoder {

    public String decode(char[] rawData) {
        StringBuilder sb = new StringBuilder();
        try {
            for (char c : rawData) {
                sb.append(new String(UCS2toUTF8Code((short) c), "UTF-8"));
            }
        } catch (UnsupportedEncodingException unsupportedEncodingException) {
            return null; //can't reach here
        }
        return sb.toString();
    }

    public String decode(byte[] rawData) {
        if(rawData.length == 0 || rawData.length % 2 != 0)
            return "Not UCS-2 Code";
        StringBuilder sb = new StringBuilder();
        try {
            for (int i = 0; i < rawData.length; i += 2) {
                sb.append(new String(UCS2toUTF8Code(encodeUCS2FromByte(rawData[i], rawData[i + 1])), "UTF-8"));
            }
        } catch (UnsupportedEncodingException unsupportedEncodingException) {
            return null; //can't reach here;
        }
        return sb.toString();
    }

    private byte[] UCS2toUTF8Code(short ucs2Code) {
        byte[] utf8Code = null;
        if (ucs2Code < 0 || ucs2Code > (short) 0x0800) {
            utf8Code = new byte[3];
            utf8Code[0] = (byte) ((convertShortToInt(ucs2Code) >>> 12) | 0xe0);
            utf8Code[1] = (byte) ((convertShortToInt((short) (ucs2Code & 0x0fc0)) >>> 6) | 0x80);
            utf8Code[2] = (byte) ((ucs2Code & 0x003f) | 0x80);
        } else if ((short) 0x0080 > ucs2Code) {
            utf8Code = new byte[1];
            utf8Code[0] = (byte) ucs2Code;
        } else {
            utf8Code = new byte[2];
            utf8Code[0] = (byte) ((ucs2Code >>> 6) | 0xc0);
            utf8Code[1] = (byte) ((ucs2Code & 0x003f) | 0x80);
        }
        return utf8Code;
    }

    private short convertByteToShort(byte byteValue) {
        return byteValue < 0 ? (short) (byteValue ^ 0xff00) : (short) byteValue;
    }

    private int convertShortToInt(short shortValue) {
        return shortValue < 0 ? shortValue ^ 0xffff0000 : shortValue;
    }

    private short encodeUCS2FromByte(byte highByte, byte lowByte) {
        return (short) ((short) (convertByteToShort(highByte) << 8) | convertByteToShort(lowByte));
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        byte[] tmp = {0x4f, 0x60, 0x59, 0x7d, (byte)0xff, 0x0c, 0x53, 0x17, 0x4e,
                            (byte)0xac, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38};
        String utfStr = new ISO10646Decoder().decode(tmp);
        System.out.println(utfStr);
    }

}

2007年10月16日星期二

使用Ant和JUnit自动构建测试


一个简化的自动化构建过程包括:
  1. 从存储库取得最新的代码
  2. 构建代码
  3. 记录构建版本号
  4. 运行单元测试
  5. 上传部署版本
  6. 发送电子邮件
以上步骤简化了部署前单元测试、部署及部署后单元测试三个过程。
  1. 从存储库取得最新的代码
     <cvs cvsRoot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
    package="moduleName"
    dest="${ws.dir}"
    /> <!-- 执行cvs check out-->
    <cvs dest="${ws.dir}" command="update"/> <!-- 执行cvs update -->

  2. 构建代码
    执行自定义Ant构建任务。

  3. 记录构建版本号
     <buildnumber file="mybuild.number"/>

    <target name="baseline" description="Record build information"> 
    <!-- The name of the file that holds the build information. If no such file exists, a new
    one gets created. -->
    <propertyfile file="${destination.dir}/build.info">
    <!-- Initial build number is 0001. Then, any subsequent build increments
    this number by one each time. -->

    <entry default="0001" key="build.number" operation="+" pattern="0000" type="int" />
    <!-- Records the current time to the same file. -->
    <entry default="now" key="build.time" pattern=" yyyy.MM.dd-HH:mm" type="date" />

    </propertyfile>

    </target>


  4. 运行单元测试
    <!-- ========================================
                  target: auto test all test case and output report file                     
                  ===================================== -->
        <target name="junit and report" depends="test init, all compile">
            <junit printsummary="on" fork="true" showoutput="true">
                <classpath>
                    <fileset dir="lib" includes="**/*.jar" />
                    <pathelement path="${output.folder}" />
                    <pathelement path="${basedir}/src/resource" />
                </classpath>
                <formatter type="xml" />
                <batchtest todir="${report.folder}">
                    <fileset dir="${output.folder}">
                        <include name="**/*Test.*" />
                    </fileset>
                </batchtest>
            </junit>
            <junitreport todir="${report.folder}">
                <fileset dir="${report.folder}">
                    <include name="TEST-*.xml" />
                </fileset>
                <report format="noframes" todir="${ report.folder}" />
            </junitreport>
        </target>

  5. 上传部署版本
    <target name="uploadbuild" description="Upload build to an FTP server"> 
    <!-- Upload everything under the destination.dir to the FTP server. -->
    <ftp server="${ftp.hostname}" remotedir="/" userid="${ftp.username}"
    password="${ ftp.userpassword}" separator="\" verbose="yes" binary="yes">
    <fileset dir="${destination.dir}">
    <include name="**/*.*" />
    </fileset>
    </ftp>
    </target>

  6. 发送电子邮件
    <target name="notifyteam" description="Notify testing team of the new build"> 
    <!-- Read build information from the build.info file. -->
    <property file="${destination.dir}/build.info" />
    <!-- Send a mail to the testing team. -->
    <mail mailhost="${smtp.hostname}" mailport="${smtp.hostport}"
    user="${mail.user}"
    password="${mail.password}"
    subject="Test build #${ build.number}"
    from="${smtp.from}" tolist="${smtp.tolist}">
    <message>The build #${build.number} is now available for testing.</message>
    </mail>
    </target>
    需要javaMail.jar