xbird.xquery.dm.value.sequence.MarshalledSequence.java Source code

Java tutorial

Introduction

Here is the source code for xbird.xquery.dm.value.sequence.MarshalledSequence.java

Source

/*
 * @(#)$Id: codetemplate_xbird.xml 943 2006-09-13 07:03:37Z yui $
 *
 * Copyright 2006-2008 Makoto YUI
 *
 * Licensed 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.
 * 
 * Contributors:
 *     Makoto YUI - initial implementation
 */
package xbird.xquery.dm.value.sequence;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import xbird.config.Settings;
import xbird.util.io.FastByteArrayInputStream;
import xbird.util.io.FastByteArrayOutputStream;
import xbird.util.io.FastMultiByteArrayInputStream;
import xbird.util.io.FastMultiByteArrayOutputStream;
import xbird.util.io.NoHeaderObjectInputStream;
import xbird.util.io.NoHeaderObjectOutputStream;
import xbird.util.io.TeeOutputStream;
import xbird.util.lang.PrintUtils;
import xbird.xquery.XQueryException;
import xbird.xquery.dm.coder.XDMTreeBuilder;
import xbird.xquery.dm.coder.XQEventDecoder;
import xbird.xquery.dm.coder.XQEventEncoder;
import xbird.xquery.dm.value.AbstractSequence;
import xbird.xquery.dm.value.Item;
import xbird.xquery.dm.value.Sequence;
import xbird.xquery.meta.DynamicContext;
import xbird.xquery.meta.IFocus;
import xbird.xquery.type.Type;

/**
 * 
 * <DIV lang="en"></DIV>
 * <DIV lang="ja"></DIV>
 * 
 * @author Makoto YUI (yuin405+xbird@gmail.com)
 */
public final class MarshalledSequence extends AbstractSequence<Item> implements Externalizable {
    private static final long serialVersionUID = 296919228793372877L;
    private static final int BUFFERING_BLOCK_SIZE = 16384;
    private static final boolean ENV_SER_PIPED = Boolean
            .parseBoolean(Settings.get("xbird.remote.ser.piped", "true"));
    private static final boolean DEBUG_DESER_SPEED = System.getProperty("xbird.debug.deser_speed") != null;
    private static final Log LOG = LogFactory.getLog("xbird.remote.ser");

    private/* final */Type _type;
    private/* final */Sequence<Item> _entity;

    private boolean _redirectable = false;
    private boolean _piped = ENV_SER_PIPED;
    private boolean _reaccessable = false; // TODO REVIEWME
    private boolean _remotePaging = false;

    public MarshalledSequence(Sequence<? extends Item> seq, DynamicContext dynEnv) {
        super(dynEnv);
        if (seq == null) {
            throw new IllegalArgumentException();
        }
        if (DEBUG_DESER_SPEED) {
            this._entity = new ValueSequence(seq.materialize(), dynEnv);
        } else {
            this._entity = (Sequence<Item>) seq;
        }
        this._type = seq.getType();
    }

    public MarshalledSequence() { // for Externalizable
        super(DynamicContext.DUMMY);
    }

    public void setRedirectable(boolean redirectable) {
        this._redirectable = redirectable;
    }

    public boolean isRedirectable() {
        return _redirectable;
    }

    public void setPiped(boolean piped) {
        this._piped = piped;
    }

    public void setReaccessable(boolean reaccessable) {
        this._reaccessable = reaccessable;
    }

    public void setRemotePaging(boolean enable) {
        this._remotePaging = enable;
    }

    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(_type);
        final boolean reaccessable = _reaccessable;
        final boolean redirectable = _redirectable;
        out.writeBoolean(redirectable);
        if (redirectable) {
            out.writeObject(_entity);
        } else {
            if (_entity instanceof IncrDecodedSequnece) {
                out.writeBoolean(true);
                IncrDecodedSequnece incr = ((IncrDecodedSequnece) _entity);
                incr._reaccessable = reaccessable;
                incr.writeExternal(out);
            } else {
                out.writeBoolean(false);
                if (_piped) {
                    out.writeBoolean(true);
                    pipedOut(out, _entity, _remotePaging);
                } else {
                    out.writeBoolean(false);
                    bulkOut(out, _entity, _remotePaging);
                }
            }
        }
        if (!reaccessable) {
            this._entity = null;
        }
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        final Type type = (Type) in.readObject();
        this._type = type;
        final boolean redirectable = in.readBoolean();
        if (redirectable) {
            this._redirectable = true;
            this._entity = (Sequence<Item>) in.readObject();
        } else {
            this._redirectable = false; // just for readability
            final boolean isDecodedSeq = in.readBoolean();
            if (isDecodedSeq) {
                IncrDecodedSequnece entity = IncrDecodedSequnece.readFrom(in);
                entity._type = type;
                this._entity = entity;
            } else {
                final boolean piped = in.readBoolean();
                final XQEventDecoder decoder;
                if (piped) {
                    this._piped = true;
                    decoder = pipedIn(in, _reaccessable);
                } else {
                    this._piped = false;
                    decoder = bulkIn(in); // This is required for nested Object serialization/deserialization                    
                }
                if (DEBUG_DESER_SPEED) {
                    try {
                        this._entity = decoder.decode();
                    } catch (XQueryException e) {
                        throw new IllegalStateException("failed decoding", e);
                    }
                } else {
                    final IncrDecodedSequnece entity = new IncrDecodedSequnece(decoder, type);
                    entity._piped = piped;
                    this._entity = entity;
                }
            }
        }
    }

    @Override
    public IFocus<Item> iterator() {
        IFocus<Item> focus = _entity.iterator();
        return focus;
    }

    public boolean next(IFocus<Item> focus) throws XQueryException {
        final boolean hasNext = _entity.next(focus);
        if (!hasNext) {
            //focus.close();    // may already be closed
            if (!_reaccessable) {
                this._entity = null;
            }
            return false;
        }
        return true;
    }

    public Type getType() {
        return _type;
    }

    private static final class IncrDecodedSequnece extends AbstractSequence<Item> implements Externalizable {
        private static final long serialVersionUID = 5692780446373650820L;
        private static final float DECODE_UNIT_GROWTH = 1.4f;

        private List<Item> _decodedItems = new ArrayList<Item>(256);
        private final XDMTreeBuilder _treeBuilder;

        private/* final */XQEventDecoder _decoder; // may be null if decoding is already finished
        private/* final */Type _type; // not null

        private transient boolean _decodeFinished = false;
        private transient boolean _piped = false;
        private transient boolean _reaccessable = true;

        private transient int _decodeUnit = 32;

        public IncrDecodedSequnece(XQEventDecoder decoder, Type type) {
            super(DynamicContext.DUMMY);
            this._treeBuilder = new XDMTreeBuilder();
            this._decoder = decoder;
            this._type = type;
        }

        public IncrDecodedSequnece() {
            super(DynamicContext.DUMMY);
            this._treeBuilder = new XDMTreeBuilder();
        }

        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            //this._type = (Type) in.readObject();
            final List<Item> decodedItems = _decodedItems;
            final int numDecoded = in.readInt();
            for (int i = 0; i < numDecoded; i++) {
                Item item = (Item) in.readObject();
                decodedItems.add(item);
            }
            final boolean decodeFinished = in.readBoolean();
            if (decodeFinished) {
                this._decodeFinished = true;
            } else {
                this._decodeFinished = false;
                final boolean piped = in.readBoolean();
                if (piped) {
                    this._piped = true;
                    // avoid readStreamHeader()
                    //ObjectInputStream ois = (ObjectInputStream) in; //TODO REVIEWME too hacky
                    //this._decoder = pipedIn(new NoHeaderObjectInputStream(ois), _reaccessable);
                    this._decoder = pipedIn(in, _reaccessable);
                } else {
                    this._piped = false;
                    this._decoder = bulkIn(in);
                }
            }
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            //out.writeObject(_type);
            final List<Item> decodedItems = _decodedItems;
            final int numDecoded = decodedItems.size();
            out.writeInt(numDecoded);
            for (int i = 0; i < numDecoded; i++) {
                Item item = decodedItems.get(i);
                out.writeObject(item);
            }
            final boolean decodeFinished = _decodeFinished;
            if (decodeFinished) {
                out.writeBoolean(true);
            } else {
                out.writeBoolean(false);
                if (_piped && out instanceof ObjectOutputStream) {
                    out.writeBoolean(true);
                    if (_reaccessable) {
                        incrPipedOutReaccessable((ObjectOutputStream) out);
                    } else {
                        incrPipedOut((ObjectOutputStream) out);
                    }
                } else {// rarely comes here
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Unusually piped in/out streams in BULK mode.");
                    }
                    out.writeBoolean(false);
                    incrBulkOut(out);
                }
            }
        }

        private void incrPipedOut(final ObjectOutputStream out) throws IOException {
            assert (!_reaccessable);
            final XQEventDecoder decoder = _decoder;
            decoder.redirectTo(out);
            decoder.close();
        }

        /**
         * @link http://java.sun.com/javase/technologies/core/basic/serializationFAQ.jsp#appendSerialStream
         */
        private void incrPipedOutReaccessable(final ObjectOutputStream out) throws IOException {
            assert (_reaccessable);
            final FastMultiByteArrayOutputStream bufOut = new FastMultiByteArrayOutputStream(BUFFERING_BLOCK_SIZE);
            final TeeOutputStream tee = new TeeOutputStream(out, bufOut);
            final ObjectOutputStream objectOut = new NoHeaderObjectOutputStream(tee);
            final XQEventDecoder decoder = _decoder;
            decoder.redirectTo(objectOut);
            decoder.close();
            objectOut.flush();

            // Because input of decoder fully read, this section required for re-object serialization, etc.
            final byte[][] buf = bufOut.toMultiByteArray();
            final int bufTotalSize = bufOut.size();
            bufOut.clear();
            final FastMultiByteArrayInputStream inputBuf = new FastMultiByteArrayInputStream(buf,
                    BUFFERING_BLOCK_SIZE, bufTotalSize);
            if (!_reaccessable) {
                inputBuf.setCleanable(true);
            }
            final ObjectInputStream objInput = new NoHeaderObjectInputStream(inputBuf);
            this._decoder = new XQEventDecoder(objInput); // replace old Decoder with fresh Decoder
        }

        private void incrBulkOut(final ObjectOutput out) throws IOException {
            final FastByteArrayOutputStream bufOut = new FastByteArrayOutputStream(16384);
            final ObjectOutputStream objectOut = new ObjectOutputStream(bufOut);
            final XQEventDecoder decoder = _decoder;
            decoder.redirectTo(objectOut);
            decoder.close();
            objectOut.flush();
            final byte[] buf = bufOut.toByteArray();
            bufOut.clear();

            // Because input of decoder fully read, this section required for re-object serialization, etc.
            if (_reaccessable) {
                final FastByteArrayInputStream inputBuf = new FastByteArrayInputStream(buf);
                final ObjectInputStream objectInput = new ObjectInputStream(inputBuf);
                this._decoder = new XQEventDecoder(objectInput); // replace old Decoder with fresh Decoder
            }

            final int buflen = buf.length;
            if (LOG.isDebugEnabled()) {
                LOG.debug("encoded sequence size: " + buflen);
            }
            out.writeInt(buflen);
            out.write(buf);
        }

        public boolean next(IFocus<Item> focus) throws XQueryException {
            if (focus.reachedEnd()) {
                return false;
            }

            final List<Item> decodedItems = _decodedItems;
            final int decodedCount = decodedItems.size();
            final int curPos = focus.getContextPosition();
            if (curPos < decodedCount) {
                final Item it = decodedItems.get(curPos);
                if (it == null) {
                    throw new IllegalStateException();
                }
                focus.setContextItem(it);
                return true;
            }

            if (_decodeFinished) {
                focus.setReachedEnd(true);
                focus.closeQuietly();
                if (!_reaccessable) {//TODO
                    this._decodedItems = Collections.emptyList();
                }
                return false;
            }
            final XDMTreeBuilder treeBuilder = _treeBuilder;
            final int last = _decodeUnit - 1;
            for (int i = 0; i < _decodeUnit; i++) {
                final Item item;
                try {
                    item = _decoder.decodeItem(treeBuilder);
                } catch (IOException e) {
                    throw new XQueryException("failed decoding an Item", e);
                } finally {
                    treeBuilder.reset();
                }
                if (item != null) {
                    decodedItems.add(item);
                    if (i == 0) {
                        focus.setContextItem(item);
                    } else if (i == last) {
                        _decodeUnit *= DECODE_UNIT_GROWTH;
                        return true;
                    }
                } else {
                    this._decodeFinished = true;
                    _decoder.close();
                    if (i > 0) {
                        return true;
                    } else {
                        break;
                    }
                }
            }
            focus.setReachedEnd(true);
            focus.closeQuietly();
            return false;
        }

        public Type getType() {
            return _type;
        }

        public static IncrDecodedSequnece readFrom(ObjectInput in) throws IOException, ClassNotFoundException {
            final IncrDecodedSequnece seq = new IncrDecodedSequnece();
            seq.readExternal(in);
            return seq;
        }
    }

    private static XQEventDecoder pipedIn(ObjectInput in, boolean reaccessable) throws IOException {
        final FastMultiByteArrayOutputStream bufOut = new FastMultiByteArrayOutputStream(BUFFERING_BLOCK_SIZE);
        final ObjectOutputStream objectOut = new ObjectOutputStream(bufOut);
        final XQEventDecoder decoder = new XQEventDecoder(in);
        decoder.redirectTo(objectOut);
        objectOut.flush();

        final byte[][] buf = bufOut.toMultiByteArray();
        final int totalBufSize = bufOut.size();
        bufOut.clear();

        // Because input of decoder fully read, this section required for re-object serialization, etc.
        final FastMultiByteArrayInputStream inputBuf = new FastMultiByteArrayInputStream(buf, BUFFERING_BLOCK_SIZE,
                totalBufSize);
        if (!reaccessable) {
            inputBuf.setCleanable(true);
        }
        final ObjectInputStream objectInput = new ObjectInputStream(inputBuf);
        final XQEventDecoder newDecoder = new XQEventDecoder(objectInput); // replace old Decoder with fresh Decoder
        return newDecoder;
    }

    private static void pipedOut(ObjectOutput out, Sequence<Item> entity, boolean remotePaing) throws IOException {
        final XQEventEncoder encoder = new XQEventEncoder(out);
        if (remotePaing) {
            encoder.setRemotePaging(true);
        }
        try {
            encoder.emit(entity);
        } catch (XQueryException xqe) {
            throw new IllegalStateException("failed encoding", xqe);
        } catch (Throwable e) {
            LOG.fatal(PrintUtils.prettyPrintStackTrace(e));
            throw new IllegalStateException("failed encoding", e);
        }
    }

    private static XQEventDecoder bulkIn(final ObjectInput input) throws IOException {
        final int inputLen = input.readInt();
        final byte[] buf = new byte[inputLen];
        input.readFully(buf);
        if (LOG.isDebugEnabled()) {
            LOG.debug("decoding sequence size: " + inputLen);
        }
        FastByteArrayInputStream inputBuf = new FastByteArrayInputStream(buf);
        ObjectInputStream objInput = new ObjectInputStream(inputBuf);
        XQEventDecoder decoder = new XQEventDecoder(objInput);
        return decoder;
    }

    private static void bulkOut(ObjectOutput out, Sequence<Item> entity, boolean remotePaing) throws IOException {
        final FastByteArrayOutputStream bufOut = new FastByteArrayOutputStream(16384);
        final ObjectOutputStream objOut = new ObjectOutputStream(bufOut);
        final XQEventEncoder encoder = new XQEventEncoder(objOut);
        if (remotePaing) {
            encoder.setRemotePaging(true);
        }
        try {
            encoder.emit(entity);
            objOut.flush();
        } catch (XQueryException xqe) {
            throw new IllegalStateException("failed encoding", xqe);
        } catch (Throwable e) {
            LOG.fatal(e);
            throw new IllegalStateException("failed encoding", e);
        }
        final byte[] buf = bufOut.toByteArray();
        bufOut.clear();
        final int buflen = buf.length;
        if (LOG.isDebugEnabled()) {
            LOG.debug("encoded sequence size: " + buflen);
        }
        out.writeInt(buflen);
        out.write(buf);
    }

}