【Zookeeper】原始碼分析之序列化

leesf發表於2017-01-12

一、前言

  在完成了前面的理論學習後,現在可以從原始碼角度來解析Zookeeper的細節,首先筆者想從序列化入手,因為在網路通訊、資料儲存中都用到了序列化,下面開始分析。

二、序列化

  序列化主要在zookeeper.jute包中,其中涉及的主要介面如下

    · InputArchive

    · OutputArchive

    · Index

    · Record

  2.1 InputArchive

  其是所有反序列化器都需要實現的介面,其方法如下 

public interface InputArchive {
    // 讀取byte型別
    public byte readByte(String tag) throws IOException;
    // 讀取boolean型別
    public boolean readBool(String tag) throws IOException;
    // 讀取int型別
    public int readInt(String tag) throws IOException;
    // 讀取long型別
    public long readLong(String tag) throws IOException;
    // 讀取float型別
    public float readFloat(String tag) throws IOException;
    // 讀取double型別
    public double readDouble(String tag) throws IOException;
    // 讀取String型別
    public String readString(String tag) throws IOException;
    // 通過緩衝方式讀取
    public byte[] readBuffer(String tag) throws IOException;
    // 開始讀取記錄
    public void readRecord(Record r, String tag) throws IOException;
    // 開始讀取記錄
    public void startRecord(String tag) throws IOException;
    // 結束讀取記錄
    public void endRecord(String tag) throws IOException;
    // 開始讀取向量
    public Index startVector(String tag) throws IOException;
    // 結束讀取向量
    public void endVector(String tag) throws IOException;
    // 開始讀取Map
    public Index startMap(String tag) throws IOException;
    // 結束讀取Map
    public void endMap(String tag) throws IOException;
}

  InputArchive的類結構如下

  

  1. BinaryInputArchive  

public class BinaryInputArchive implements InputArchive {
    // DataInput介面,用於從二進位制流中讀取位元組
    private DataInput in;
    
    // 靜態方法,用於獲取Archive
    static public BinaryInputArchive getArchive(InputStream strm) {
        return new BinaryInputArchive(new DataInputStream(strm));
    }
    
    // 內部類,對應BinaryInputArchive索引
    static private class BinaryIndex implements Index {
        private int nelems;
        BinaryIndex(int nelems) {
            this.nelems = nelems;
        }
        public boolean done() {
            return (nelems <= 0);
        }
        public void incr() {
            nelems--;
        }
    }
    /** Creates a new instance of BinaryInputArchive */
    // 建構函式
    public BinaryInputArchive(DataInput in) {
        this.in = in;
    }
    
    // 讀取位元組
    public byte readByte(String tag) throws IOException {
        return in.readByte();
    }
    
    // 讀取boolean型別
    public boolean readBool(String tag) throws IOException {
        return in.readBoolean();
    }
    
    // 讀取int型別
    public int readInt(String tag) throws IOException {
        return in.readInt();
    }
    
    // 讀取long型別
    public long readLong(String tag) throws IOException {
        return in.readLong();
    }
    
    // 讀取float型別
    public float readFloat(String tag) throws IOException {
        return in.readFloat();
    }
    
    // 讀取double型別
    public double readDouble(String tag) throws IOException {
        return in.readDouble();
    }
    
    // 讀取String型別
    public String readString(String tag) throws IOException {
        // 確定長度
        int len = in.readInt();
        if (len == -1) return null;
        byte b[] = new byte[len];
        // 從輸入流中讀取一些位元組,並將它們儲存在緩衝區陣列b中
        in.readFully(b);
        return new String(b, "UTF8");
    }
    
    // 最大緩衝值
    static public final int maxBuffer = Integer.getInteger("jute.maxbuffer", 0xfffff);

    // 讀取緩衝
    public byte[] readBuffer(String tag) throws IOException {
        // 確定長度
        int len = readInt(tag);
        if (len == -1) return null;
        // Since this is a rough sanity check, add some padding to maxBuffer to
        // make up for extra fields, etc. (otherwise e.g. clients may be able to
        // write buffers larger than we can read from disk!)
        if (len < 0 || len > maxBuffer + 1024) { // 檢查長度是否合理
            throw new IOException("Unreasonable length = " + len);
        }
        byte[] arr = new byte[len];
        // 從輸入流中讀取一些位元組,並將它們儲存在緩衝區陣列arr中
        in.readFully(arr);
        return arr;
    }
    
    // 讀取記錄
    public void readRecord(Record r, String tag) throws IOException {
        // 反序列化,動態呼叫
        r.deserialize(this, tag);
    }
    
    // 開始讀取記錄,實現為空
    public void startRecord(String tag) throws IOException {}
    
    // 結束讀取記錄,實現為空
    public void endRecord(String tag) throws IOException {}
    
    // 開始讀取向量
    public Index startVector(String tag) throws IOException {
        // 確定長度
        int len = readInt(tag);
        if (len == -1) {
            return null;
        }
        // 返回索引
        return new BinaryIndex(len);
    }
    
    // 結束讀取向量
    public void endVector(String tag) throws IOException {}
    
    // 開始讀取Map
    public Index startMap(String tag) throws IOException {
        // 返回索引
        return new BinaryIndex(readInt(tag));
    }
    
    // 結束讀取Map,實現為空
    public void endMap(String tag) throws IOException {}
    
}
View Code

  2. CsvInputArchive 

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jute;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackReader;
import java.io.UnsupportedEncodingException;

/**
 *
 */
class CsvInputArchive implements InputArchive {
    // 推回位元組流
    private PushbackReader stream;
   
    // 內部類,對應CsvInputArchive索引
    private class CsvIndex implements Index {
        public boolean done() {
            char c = '\0';
            try {
                c = (char) stream.read();
                stream.unread(c);
            } catch (IOException ex) {
            }
            return (c == '}') ? true : false;
        }
        public void incr() {}
    }
    
    // 私有方法,丟擲異常
    private void throwExceptionOnError(String tag) throws IOException {
        throw new IOException("Error deserializing "+tag);
    }
    
    // 私有方法,讀取欄位
    private String readField(String tag) throws IOException {
        try {
            StringBuilder buf = new StringBuilder();
            while (true) { 
                // 讀取並轉化為字元
                char c = (char) stream.read();
                switch (c) { // 判斷字元
                    case ',':
                        // 讀取欄位完成,可直接返回
                        return buf.toString();
                    case '}':
                    case '\n':
                    case '\r':
                        // 推回緩衝區
                        stream.unread(c);
                        return buf.toString();
                    default: // 預設新增至buf中
                        buf.append(c);
                }
            }
        } catch (IOException ex) {
            throw new IOException("Error reading "+tag);
        }
    }
    
    // 獲取CsvInputArchive
    static CsvInputArchive getArchive(InputStream strm)
    throws UnsupportedEncodingException {
        return new CsvInputArchive(strm);
    }
    
    /** Creates a new instance of CsvInputArchive */
    // 建構函式
    public CsvInputArchive(InputStream in)
    throws UnsupportedEncodingException {
        // 初始化stream屬性
        stream = new PushbackReader(new InputStreamReader(in, "UTF-8"));
    }
    
    // 讀取byte型別
    public byte readByte(String tag) throws IOException {
        return (byte) readLong(tag);
    }
    
    // 讀取boolean型別
    public boolean readBool(String tag) throws IOException {
        String sval = readField(tag);
        return "T".equals(sval) ? true : false;
    }
    
    // 讀取int型別
    public int readInt(String tag) throws IOException {
        return (int) readLong(tag);
    }
    
    // 讀取long型別
    public long readLong(String tag) throws IOException {
        // 讀取欄位
        String sval = readField(tag);
        try {
            // 轉化
            long lval = Long.parseLong(sval);
            return lval;
        } catch (NumberFormatException ex) {
            throw new IOException("Error deserializing "+tag);
        }
    }
    
    // 讀取float型別
    public float readFloat(String tag) throws IOException {
        return (float) readDouble(tag);
    }
    
    // 讀取double型別
    public double readDouble(String tag) throws IOException {
        // 讀取欄位
        String sval = readField(tag);
        try {
            // 轉化
            double dval = Double.parseDouble(sval);
            return dval;
        } catch (NumberFormatException ex) {
            throw new IOException("Error deserializing "+tag);
        }
    }
    
    // 讀取String型別
    public String readString(String tag) throws IOException {
        // 讀取欄位
        String sval = readField(tag);
        // 轉化
        return Utils.fromCSVString(sval);
        
    }
    
    // 讀取緩衝型別
    public byte[] readBuffer(String tag) throws IOException {
        // 讀取欄位
        String sval = readField(tag);
        // 轉化
        return Utils.fromCSVBuffer(sval);
    }
    
    // 讀取記錄
    public void readRecord(Record r, String tag) throws IOException {
        // 反序列化
        r.deserialize(this, tag);
    }
    
    // 開始讀取記錄
    public void startRecord(String tag) throws IOException {
        if (tag != null && !"".equals(tag)) { 
            // 讀取並轉化為字元
            char c1 = (char) stream.read();
            // 讀取並轉化為字元
            char c2 = (char) stream.read();
            if (c1 != 's' || c2 != '{') { // 進行判斷
                throw new IOException("Error deserializing "+tag);
            }
        }
    }
    
    // 結束讀取記錄
    public void endRecord(String tag) throws IOException {
        // 讀取並轉化為字元
        char c = (char) stream.read();
        if (tag == null || "".equals(tag)) {
            if (c != '\n' && c != '\r') { // 進行判斷
                throw new IOException("Error deserializing record.");
            } else {
                return;
            }
        }
        
        if (c != '}') { // 進行判斷
            throw new IOException("Error deserializing "+tag);
        }
        // 讀取並轉化為字元
        c = (char) stream.read();
        if (c != ',') { 
            // 推回緩衝區
            stream.unread(c);
        }
        
        return;
    }
    
    // 開始讀取vector
    public Index startVector(String tag) throws IOException {
        char c1 = (char) stream.read();
        char c2 = (char) stream.read();
        if (c1 != 'v' || c2 != '{') {
            throw new IOException("Error deserializing "+tag);
        }
        return new CsvIndex();
    }
    
    // 結束讀取vector
    public void endVector(String tag) throws IOException {
        char c = (char) stream.read();
        if (c != '}') {
            throw new IOException("Error deserializing "+tag);
        }
        c = (char) stream.read();
        if (c != ',') {
            stream.unread(c);
        }
        return;
    }
    
    // 開始讀取Map
    public Index startMap(String tag) throws IOException {
        char c1 = (char) stream.read();
        char c2 = (char) stream.read();
        if (c1 != 'm' || c2 != '{') {
            throw new IOException("Error deserializing "+tag);
        }
        return new CsvIndex();
    }
    
    // 結束讀取Map
    public void endMap(String tag) throws IOException {
        char c = (char) stream.read();
        if (c != '}') {
            throw new IOException("Error deserializing "+tag);
        }
        c = (char) stream.read();
        if (c != ',') {
            stream.unread(c);
        }
        return;
    }
}
View Code

  3. XmlInputArchive 

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jute;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
 *
 */
class XmlInputArchive implements InputArchive {
    // 內部類,值(包含型別和值)
    static private class Value {
        private String type;
        private StringBuffer sb;
        
        public Value(String t) {
            type = t;
            sb = new StringBuffer();
        }
        
        // 新增chars
        public void addChars(char[] buf, int offset, int len) {
            sb.append(buf, offset, len);
        }
        
        // 返回value
        public String getValue() { return sb.toString(); }
        
        // 返回type
        public String getType() { return type; }
    }
    
    // 內部類,XML解析器
    private static class XMLParser extends DefaultHandler {
        private boolean charsValid = false;
        
        private ArrayList<Value> valList;
        
        private XMLParser(ArrayList<Value> vlist) {
            valList = vlist;
        }
        
        // 文件開始,空的實現
        public void startDocument() throws SAXException {}
        
        // 文件結束,空的實現
        public void endDocument() throws SAXException {}
        
        // 開始解析元素
        public void startElement(String ns,
                String sname,
                String qname,
                Attributes attrs) throws SAXException {
            // 
            charsValid = false;
            if ("boolean".equals(qname) ||        // boolean型別
                    "i4".equals(qname) ||        // 四個位元組
                    "int".equals(qname) ||        // int型別
                    "string".equals(qname) ||    // String型別
                    "double".equals(qname) ||    // double型別
                    "ex:i1".equals(qname) ||    // 一個位元組
                    "ex:i8".equals(qname) ||    // 八個位元組
                    "ex:float".equals(qname)) { // 基本型別
                // 
                charsValid = true;
                // 新增至列表
                valList.add(new Value(qname));
            } else if ("struct".equals(qname) ||
                "array".equals(qname)) { // 結構體或陣列型別
                // 新增至列表
                valList.add(new Value(qname));
            }
        }
        
        // 結束解析元素
        public void endElement(String ns,
                String sname,
                String qname) throws SAXException {
            charsValid = false;
            if ("struct".equals(qname) ||
                    "array".equals(qname)) { // 結構體或陣列型別
                // 新增至列表
                valList.add(new Value("/"+qname));
            }
        }
        
        public void characters(char buf[], int offset, int len)
        throws SAXException {
            if (charsValid) { // 是否合法
                // 從列表獲取value
                Value v = valList.get(valList.size()-1);
                // 將buf新增至value
                v.addChars(buf, offset,len);
            }
        }
        
    }
    
    // 內部類,對應XmlInputArchive
    private class XmlIndex implements Index {
        // 是否已經完成
        public boolean done() {
            // 根據索引獲取value
            Value v = valList.get(vIdx);
            if ("/array".equals(v.getType())) { // 型別為/array
                // 設定開索引值為null
                valList.set(vIdx, null);
                // 增加索引值
                vIdx++;
                return true;
            } else {
                return false;
            }
        }
        // 增加索引值,空的實現
        public void incr() {}
    }
    
    // 值列表
    private ArrayList<Value> valList;
    // 值長度
    private int vLen;
    // 索引
    private int vIdx;
    
    // 下一項
    private Value next() throws IOException {
        if (vIdx < vLen) { // 當前索引值小於長度
            // 獲取值
            Value v = valList.get(vIdx);
            // 設定索引值為null
            valList.set(vIdx, null);
            // 增加索引值
            vIdx++;
            return v;
        } else {
            throw new IOException("Error in deserialization.");
        }
    }
        
    // 獲取XmlInputArchive
    static XmlInputArchive getArchive(InputStream strm)
    throws ParserConfigurationException, SAXException, IOException {
        return new XmlInputArchive(strm);
    }
    
    /** Creates a new instance of BinaryInputArchive */
    // 建構函式
    public XmlInputArchive(InputStream in)
    throws ParserConfigurationException, SAXException, IOException {
        // 初始化XmlInputArchive的相應欄位
        valList = new ArrayList<Value>();
        DefaultHandler handler = new XMLParser(valList);
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        parser.parse(in, handler);
        vLen = valList.size();
        vIdx = 0;
    }
    
    // 讀取byte型別
    public byte readByte(String tag) throws IOException {
        Value v = next();
        if (!"ex:i1".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return Byte.parseByte(v.getValue());
    }
    
    // 讀取Boolean型別
    public boolean readBool(String tag) throws IOException {
        Value v = next();
        if (!"boolean".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return "1".equals(v.getValue());
    }
    
    // 讀取int型別
    public int readInt(String tag) throws IOException {
        Value v = next();
        if (!"i4".equals(v.getType()) &&
                !"int".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return Integer.parseInt(v.getValue());
    }
    
    // 讀取long型別
    public long readLong(String tag) throws IOException {
        Value v = next();
        if (!"ex:i8".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return Long.parseLong(v.getValue());
    }
    
    // 讀取float型別
    public float readFloat(String tag) throws IOException {
        Value v = next();
        if (!"ex:float".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return Float.parseFloat(v.getValue());
    }
    
    // 讀取double型別
    public double readDouble(String tag) throws IOException {
        Value v = next();
        if (!"double".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return Double.parseDouble(v.getValue());
    }
    
    // 讀取String型別
    public String readString(String tag) throws IOException {
        Value v = next();
        if (!"string".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return Utils.fromXMLString(v.getValue());
    }
    
    // 讀取Buffer型別
    public byte[] readBuffer(String tag) throws IOException {
        Value v = next();
        if (!"string".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return Utils.fromXMLBuffer(v.getValue());
    }
    
    // 讀取Record型別
    public void readRecord(Record r, String tag) throws IOException {
        r.deserialize(this, tag);
    }
    
    // 開始讀取Record
    public void startRecord(String tag) throws IOException {
        Value v = next();
        if (!"struct".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
    }
    
    // 結束讀取Record
    public void endRecord(String tag) throws IOException {
        Value v = next();
        if (!"/struct".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
    }
    
    // 開始讀取vector
    public Index startVector(String tag) throws IOException {
        Value v = next();
        if (!"array".equals(v.getType())) {
            throw new IOException("Error deserializing "+tag+".");
        }
        return new XmlIndex();
    }
    
    // 結束讀取vector
    public void endVector(String tag) throws IOException {}
    
    // 開始讀取Map
    public Index startMap(String tag) throws IOException {
        return startVector(tag);
    }
    
    // 停止讀取Map
    public void endMap(String tag) throws IOException { endVector(tag); }

}
View Code

  2.2 OutputArchive

  其是所有序列化器都需要實現此介面,其方法如下。  

public interface OutputArchive {
    // 寫Byte型別
    public void writeByte(byte b, String tag) throws IOException;
    // 寫boolean型別
    public void writeBool(boolean b, String tag) throws IOException;
    // 寫int型別
    public void writeInt(int i, String tag) throws IOException;
    // 寫long型別
    public void writeLong(long l, String tag) throws IOException;
    // 寫float型別
    public void writeFloat(float f, String tag) throws IOException;
    // 寫double型別
    public void writeDouble(double d, String tag) throws IOException;
    // 寫String型別
    public void writeString(String s, String tag) throws IOException;
    // 寫Buffer型別
    public void writeBuffer(byte buf[], String tag)
        throws IOException;
    // 寫Record型別
    public void writeRecord(Record r, String tag) throws IOException;
    // 開始寫Record
    public void startRecord(Record r, String tag) throws IOException;
    // 結束寫Record
    public void endRecord(Record r, String tag) throws IOException;
    // 開始寫Vector
    public void startVector(List v, String tag) throws IOException;
    // 結束寫Vector
    public void endVector(List v, String tag) throws IOException;
    // 開始寫Map
    public void startMap(TreeMap v, String tag) throws IOException;
    // 結束寫Map
    public void endMap(TreeMap v, String tag) throws IOException;

}

  OutputArchive的類結構如下

  

  1. BinaryOutputArchive 

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jute;

import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.TreeMap;

/**
 *
 */
public class BinaryOutputArchive implements OutputArchive {
    // 位元組緩衝
    private ByteBuffer bb = ByteBuffer.allocate(1024);
    // DataInput介面,用於從二進位制流中讀取位元組
    private DataOutput out;
    
    // 靜態方法,用於獲取Archive
    public static BinaryOutputArchive getArchive(OutputStream strm) {
        return new BinaryOutputArchive(new DataOutputStream(strm));
    }
    
    /** Creates a new instance of BinaryOutputArchive */
    // 建構函式
    public BinaryOutputArchive(DataOutput out) {
        this.out = out;
    }
    
    // 寫Byte型別
    public void writeByte(byte b, String tag) throws IOException {
        out.writeByte(b);
    }
    
    // 寫boolean型別
    public void writeBool(boolean b, String tag) throws IOException {
        out.writeBoolean(b);
    }
    
    // 寫int型別
    public void writeInt(int i, String tag) throws IOException {
        out.writeInt(i);
    }
    
    // 寫long型別
    public void writeLong(long l, String tag) throws IOException {
        out.writeLong(l);
    }
    
    // 寫float型別
    public void writeFloat(float f, String tag) throws IOException {
        out.writeFloat(f);
    }
    
    // 寫double型別
    public void writeDouble(double d, String tag) throws IOException {
        out.writeDouble(d);
    }
    
    /**
     * create our own char encoder to utf8. This is faster 
     * then string.getbytes(UTF8).
     * @param s the string to encode into utf8
     * @return utf8 byte sequence.
     */
    // 將String型別轉化為ByteBuffer型別
    final private ByteBuffer stringToByteBuffer(CharSequence s) {
        // 清空ByteBuffer
        bb.clear();
        // s的長度
        final int len = s.length();
        for (int i = 0; i < len; i++) { // 遍歷s
            if (bb.remaining() < 3) { // ByteBuffer剩餘大小小於3
                // 再進行一次分配(擴大一倍)
                ByteBuffer n = ByteBuffer.allocate(bb.capacity() << 1);
                // 切換方式
                bb.flip();
                // 寫入bb
                n.put(bb);
                bb = n;
            }
            char c = s.charAt(i);
            if (c < 0x80) { // 小於128,直接寫入
                bb.put((byte) c);
            } else if (c < 0x800) { // 小於2048,則進行相應處理
                bb.put((byte) (0xc0 | (c >> 6)));
                bb.put((byte) (0x80 | (c & 0x3f)));
            } else { // 大於2048,則進行相應處理
                bb.put((byte) (0xe0 | (c >> 12)));
                bb.put((byte) (0x80 | ((c >> 6) & 0x3f)));
                bb.put((byte) (0x80 | (c & 0x3f)));
            }
        }
        // 切換方式
        bb.flip();
        return bb;
    }

    // 寫String型別
    public void writeString(String s, String tag) throws IOException {
        if (s == null) {
            writeInt(-1, "len");
            return;
        }
        ByteBuffer bb = stringToByteBuffer(s);
        writeInt(bb.remaining(), "len");
        out.write(bb.array(), bb.position(), bb.limit());
    }
    
    // 寫Buffer型別
    public void writeBuffer(byte barr[], String tag)
    throws IOException {
        if (barr == null) {
            out.writeInt(-1);
            return;
        }
        out.writeInt(barr.length);
        out.write(barr);
    }
    
    // 寫Record型別
    public void writeRecord(Record r, String tag) throws IOException {
        r.serialize(this, tag);
    }
    
    // 開始寫Record
    public void startRecord(Record r, String tag) throws IOException {}
    
    // 結束寫Record
    public void endRecord(Record r, String tag) throws IOException {}
    
    // 開始寫Vector
    public void startVector(List v, String tag) throws IOException {
        if (v == null) {
            writeInt(-1, tag);
            return;
        }
        writeInt(v.size(), tag);
    }
    
    // 結束寫Vector
    public void endVector(List v, String tag) throws IOException {}
    
    // 開始寫Map
    public void startMap(TreeMap v, String tag) throws IOException {
        writeInt(v.size(), tag);
    }
    
    // 結束寫Map
    public void endMap(TreeMap v, String tag) throws IOException {}
    
}
View Code

  2. CsvOutputArchive 

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jute;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.TreeMap;

/**
 *
 */
public class CsvOutputArchive implements OutputArchive {
    // PrintStream為其他輸出流新增了功能,使它們能夠方便地列印各種資料值表示形式
    private PrintStream stream;
    // 預設為第一次
    private boolean isFirst = true;
    
    // 獲取Archive
    static CsvOutputArchive getArchive(OutputStream strm)
    throws UnsupportedEncodingException {
        return new CsvOutputArchive(strm);
    }
    
    // 私有函式,丟擲異常
    private void throwExceptionOnError(String tag) throws IOException {
        if (stream.checkError()) {
            throw new IOException("Error serializing "+tag);
        }
    }
 
    // 私有函式,除第一次外,均列印","
    private void printCommaUnlessFirst() {
        if (!isFirst) {
            stream.print(",");
        }
        isFirst = false;
    }
    
    /** Creates a new instance of CsvOutputArchive */
    // 建構函式
    public CsvOutputArchive(OutputStream out)
    throws UnsupportedEncodingException {
        stream = new PrintStream(out, true, "UTF-8");
    }
    
    // 寫Byte型別
    public void writeByte(byte b, String tag) throws IOException {
        writeLong((long)b, tag);
    }
    
    // 寫boolean型別
    public void writeBool(boolean b, String tag) throws IOException {
        // 列印","
        printCommaUnlessFirst();
        String val = b ? "T" : "F";
        // 列印值
        stream.print(val);
        // 丟擲異常
        throwExceptionOnError(tag);
    }

    // 寫int型別
    public void writeInt(int i, String tag) throws IOException {
        writeLong((long)i, tag);
    }
    
    // 寫long型別
    public void writeLong(long l, String tag) throws IOException {
        printCommaUnlessFirst();
        stream.print(l);
        throwExceptionOnError(tag);
    }
    
    // 寫float型別
    public void writeFloat(float f, String tag) throws IOException {
        writeDouble((double)f, tag);
    }
    
    // 寫double型別
    public void writeDouble(double d, String tag) throws IOException {
        printCommaUnlessFirst();
        stream.print(d);
        throwExceptionOnError(tag);
    }
    
    // 寫String型別
    public void writeString(String s, String tag) throws IOException {
        printCommaUnlessFirst();
        stream.print(Utils.toCSVString(s));
        throwExceptionOnError(tag);
    }
    
    // 寫Buffer型別
    public void writeBuffer(byte buf[], String tag)
    throws IOException {
        printCommaUnlessFirst();
        stream.print(Utils.toCSVBuffer(buf));
        throwExceptionOnError(tag);
    }
    
    // 寫Record型別
    public void writeRecord(Record r, String tag) throws IOException {
        if (r == null) {
            return;
        }
        r.serialize(this, tag);
    }
    
    // 開始寫Record
    public void startRecord(Record r, String tag) throws IOException {
        if (tag != null && !"".equals(tag)) {
            printCommaUnlessFirst();
            stream.print("s{");
            isFirst = true;
        }
    }
    
    // 結束寫Record
    public void endRecord(Record r, String tag) throws IOException {
        if (tag == null || "".equals(tag)) {
            stream.print("\n");
            isFirst = true;
        } else {
            stream.print("}");
            isFirst = false;
        }
    }
    
    // 開始寫Vector
    public void startVector(List v, String tag) throws IOException {
        printCommaUnlessFirst();
        stream.print("v{");
        isFirst = true;
    }
    
    // 結束寫Vector
    public void endVector(List v, String tag) throws IOException {
        stream.print("}");
        isFirst = false;
    }
    
    // 開始寫Map
    public void startMap(TreeMap v, String tag) throws IOException {
        printCommaUnlessFirst();
        stream.print("m{");
        isFirst = true;
    }
    
    // 結束寫Map
    public void endMap(TreeMap v, String tag) throws IOException {
        stream.print("}");
        isFirst = false;
    }
}
View Code

  3. XmlOutputArchive

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jute;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Stack;
import java.util.TreeMap;

/**
 *
 */
class XmlOutputArchive implements OutputArchive {
    // PrintStream為其他輸出流新增了功能,使它們能夠方便地列印各種資料值表示形式
    private PrintStream stream;
    
    // 縮排個數
    private int indent = 0;
    
    // 棧結構
    private Stack<String> compoundStack;
    
    // 存放縮排
    private void putIndent() {
        StringBuilder sb = new StringBuilder("");
        for (int idx = 0; idx < indent; idx++) {
            sb.append("  ");
        }
        stream.print(sb.toString());
    }
    
    // 新增縮排
    private void addIndent() {
        indent++;
    }
    
    // 減少縮排
    private void closeIndent() {
        indent--;
    }
    
    // 列印檔案頭格式
    private void printBeginEnvelope(String tag) {
        if (!compoundStack.empty()) {
            String s = compoundStack.peek();
            if ("struct".equals(s)) {
                putIndent();
                stream.print("<member>\n");
                addIndent();
                putIndent();
                stream.print("<name>"+tag+"</name>\n");
                putIndent();
                stream.print("<value>");
            } else if ("vector".equals(s)) {
                stream.print("<value>");
            } else if ("map".equals(s)) {
                stream.print("<value>");
            }
        } else {
            stream.print("<value>");
        }
    }
    
    // 列印檔案尾格式
    private void printEndEnvelope(String tag) {
        if (!compoundStack.empty()) {
            String s = compoundStack.peek();
            if ("struct".equals(s)) {
                stream.print("</value>\n");
                closeIndent();
                putIndent();
                stream.print("</member>\n");
            } else if ("vector".equals(s)) {
                stream.print("</value>\n");
            } else if ("map".equals(s)) {
                stream.print("</value>\n");
            }
        } else {
            stream.print("</value>\n");
        }
    }
    
    // 
    private void insideVector(String tag) {
        printBeginEnvelope(tag);
        compoundStack.push("vector");
    }
    
    private void outsideVector(String tag) throws IOException {
        String s = compoundStack.pop();
        if (!"vector".equals(s)) {
            throw new IOException("Error serializing vector.");
        }
        printEndEnvelope(tag);
    }
    
    private void insideMap(String tag) {
        printBeginEnvelope(tag);
        compoundStack.push("map");
    }
    
    private void outsideMap(String tag) throws IOException {
        String s = compoundStack.pop();
        if (!"map".equals(s)) {
            throw new IOException("Error serializing map.");
        }
        printEndEnvelope(tag);
    }
    
    private void insideRecord(String tag) {
        printBeginEnvelope(tag);
        compoundStack.push("struct");
    }
    
    private void outsideRecord(String tag) throws IOException {
        String s = compoundStack.pop();
        if (!"struct".equals(s)) {
            throw new IOException("Error serializing record.");
        }
        printEndEnvelope(tag);
    }
    
    // 獲取Archive
    static XmlOutputArchive getArchive(OutputStream strm) {
        return new XmlOutputArchive(strm);
    }
    
    /** Creates a new instance of XmlOutputArchive */
    // 建構函式
    public XmlOutputArchive(OutputStream out) {
        stream = new PrintStream(out);
        compoundStack = new Stack<String>();
    }
    
    // 寫Byte型別
    public void writeByte(byte b, String tag) throws IOException {
        printBeginEnvelope(tag);
        stream.print("<ex:i1>");
        stream.print(Byte.toString(b));
        stream.print("</ex:i1>");
        printEndEnvelope(tag);
    }
    
    // 寫boolean型別
    public void writeBool(boolean b, String tag) throws IOException {
        printBeginEnvelope(tag);
        stream.print("<boolean>");
        stream.print(b ? "1" : "0");
        stream.print("</boolean>");
        printEndEnvelope(tag);
    }
    
    // 寫int型別
    public void writeInt(int i, String tag) throws IOException {
        printBeginEnvelope(tag);
        stream.print("<i4>");
        stream.print(Integer.toString(i));
        stream.print("</i4>");
        printEndEnvelope(tag);
    }
    
    // 寫long型別
    public void writeLong(long l, String tag) throws IOException {
        printBeginEnvelope(tag);
        stream.print("<ex:i8>");
        stream.print(Long.toString(l));
        stream.print("</ex:i8>");
        printEndEnvelope(tag);
    }
    
    // 寫float型別
    public void writeFloat(float f, String tag) throws IOException {
        printBeginEnvelope(tag);
        stream.print("<ex:float>");
        stream.print(Float.toString(f));
        stream.print("</ex:float>");
        printEndEnvelope(tag);
    }
    
    // 寫double型別
    public void writeDouble(double d, String tag) throws IOException {
        printBeginEnvelope(tag);
        stream.print("<double>");
        stream.print(Double.toString(d));
        stream.print("</double>");
        printEndEnvelope(tag);
    }
    
    // 寫String型別
    public void writeString(String s, String tag) throws IOException {
        printBeginEnvelope(tag);
        stream.print("<string>");
        stream.print(Utils.toXMLString(s));
        stream.print("</string>");
        printEndEnvelope(tag);
    }
    
    // 寫Buffer型別
    public void writeBuffer(byte buf[], String tag)
    throws IOException {
        printBeginEnvelope(tag);
        stream.print("<string>");
        stream.print(Utils.toXMLBuffer(buf));
        stream.print("</string>");
        printEndEnvelope(tag);
    }
    
    // 寫Record型別
    public void writeRecord(Record r, String tag) throws IOException {
        r.serialize(this, tag);
    }
    
    // 開始寫Record型別
    public void startRecord(Record r, String tag) throws IOException {
        insideRecord(tag);
        stream.print("<struct>\n");
        addIndent();
    }
    
    // 結束寫Record型別
    public void endRecord(Record r, String tag) throws IOException {
        closeIndent();
        putIndent();
        stream.print("</struct>");
        outsideRecord(tag);
    }
    
    // 開始寫Vector型別
    public void startVector(List v, String tag) throws IOException {
        insideVector(tag);
        stream.print("<array>\n");
        addIndent();
    }
    
    // 結束寫Vector型別
    public void endVector(List v, String tag) throws IOException {
        closeIndent();
        putIndent();
        stream.print("</array>");
        outsideVector(tag);
    }
    
    // 開始寫Map型別
    public void startMap(TreeMap v, String tag) throws IOException {
        insideMap(tag);
        stream.print("<array>\n");
        addIndent();
    }
    
    // 結束寫Map型別
    public void endMap(TreeMap v, String tag) throws IOException {
        closeIndent();
        putIndent();
        stream.print("</array>");
        outsideMap(tag);
    }

}
View Code

  2.3 Index

  其用於迭代反序列化器的迭代器。  

public interface Index {
    // 是否已經完成
    public boolean done();
    // 下一項
    public void incr();
}

  Index的類結構如下

  

  1. BinaryIndex 

    static private class BinaryIndex implements Index {
        // 元素個數
        private int nelems;
        // 建構函式
        BinaryIndex(int nelems) {
            this.nelems = nelems;
        }
        // 是否已經完成
        public boolean done() {
            return (nelems <= 0);
        }
        // 移動一項
        public void incr() {
            nelems--;
        }
    }

  2. CsxIndex 

    private class CsvIndex implements Index {
        // 是否已經完成
        public boolean done() {
            char c = '\0';
            try {
                // 讀取字元
                c = (char) stream.read();
                // 推回緩衝區 
                stream.unread(c);
            } catch (IOException ex) {
            }
            return (c == '}') ? true : false;
        }
        // 什麼都不做
        public void incr() {}
    }

  3. XmlIndex 

    private class XmlIndex implements Index {
        // 是否已經完成
        public boolean done() {
            // 根據索引獲取值
            Value v = valList.get(vIdx);
            if ("/array".equals(v.getType())) { // 判斷是否值的型別是否為/array
                // 設定索引的值
                valList.set(vIdx, null);
                // 索引加1
                vIdx++;
                return true;
            } else {
                return false;
            }
        }
        // 什麼都不做
        public void incr() {}
    }

  2.4 Record

  所有用於網路傳輸或者本地儲存的型別都實現該介面,其方法如下  

public interface Record {
    // 序列化
    public void serialize(OutputArchive archive, String tag)
        throws IOException;
    // 反序列化
    public void deserialize(InputArchive archive, String tag)
        throws IOException;
}

  所有的實現類都需要實現seriallize和deserialize方法。 

三、示例

  下面通過一個示例來理解OutputArchive和InputArchive的搭配使用。 

package com.leesf.zookeeper_samples;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.TreeMap;

import org.apache.jute.BinaryInputArchive;
import org.apache.jute.BinaryOutputArchive;
import org.apache.jute.Index;
import org.apache.jute.InputArchive;
import org.apache.jute.OutputArchive;
import org.apache.jute.Record;

public class ArchiveTest {
    public static void main( String[] args ) throws IOException {
        String path = "F:\\test.txt";
        // write operation
        OutputStream outputStream = new FileOutputStream(new File(path));
        BinaryOutputArchive binaryOutputArchive = BinaryOutputArchive.getArchive(outputStream);
        
        binaryOutputArchive.writeBool(true, "boolean");
        byte[] bytes = "leesf".getBytes();
        binaryOutputArchive.writeBuffer(bytes, "buffer");
        binaryOutputArchive.writeDouble(13.14, "double");
        binaryOutputArchive.writeFloat(5.20f, "float");
        binaryOutputArchive.writeInt(520, "int");
        Person person = new Person(25, "leesf");
        binaryOutputArchive.writeRecord(person, "leesf");
        TreeMap<String, Integer> map = new TreeMap<String, Integer>();
        map.put("leesf", 25);
        map.put("dyd", 25);
        Set<String> keys = map.keySet();
        binaryOutputArchive.startMap(map, "map");
        int i = 0;
        for (String key: keys) {
            String tag = i + "";
            binaryOutputArchive.writeString(key, tag);
            binaryOutputArchive.writeInt(map.get(key), tag);
            i++;
        }
        
        binaryOutputArchive.endMap(map, "map");
        
        
        // read operation
        InputStream inputStream = new FileInputStream(new File(path));
        BinaryInputArchive binaryInputArchive = BinaryInputArchive.getArchive(inputStream);
        
        System.out.println(binaryInputArchive.readBool("boolean"));
        System.out.println(new String(binaryInputArchive.readBuffer("buffer")));
        System.out.println(binaryInputArchive.readDouble("double"));
        System.out.println(binaryInputArchive.readFloat("float"));
        System.out.println(binaryInputArchive.readInt("int"));
        Person person2 = new Person();
        binaryInputArchive.readRecord(person2, "leesf");
        System.out.println(person2);       
        
        Index index = binaryInputArchive.startMap("map");
        int j = 0;
        while (!index.done()) {
            String tag = j + "";
            System.out.println("key = " + binaryInputArchive.readString(tag) 
                + ", value = " + binaryInputArchive.readInt(tag));
            index.incr();
            j++;
        }
    }
    
    static class Person implements Record {
        private int age;
        private String name;
        
        public Person() {
            
        }
        
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }

        public void serialize(OutputArchive archive, String tag) throws IOException {
            archive.startRecord(this, tag);
            archive.writeInt(age, "age");
            archive.writeString(name, "name");
            archive.endRecord(this, tag);
        }

        public void deserialize(InputArchive archive, String tag) throws IOException {
            archive.startRecord(tag);
            age = archive.readInt("age");
            name = archive.readString("name");
            archive.endRecord(tag);            
        }    
        
        public String toString() {
            return "age = " + age + ", name = " + name;
        }
    }
}

  執行結果:  

true
leesf
13.14
5.2
520
age = 25, name = leesf
key = dyd, value = 25
key = leesf, value = 25

四、總結

  本篇博文分析了序列化中涉及到的類,主要是org.zookeeper.jute包下的類,相對來說還是相對簡單,也謝謝各位園友的觀看~

相關文章