/*
* @(#)$Id: RemoteFocus.java 3619 2008-03-26 07:23:03Z 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.engine.remote;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import xbird.config.Settings;
import xbird.engine.request.QueryRequest.FetchMethod;
import xbird.util.collections.ArrayQueue;
import xbird.util.compress.LZFInputStream;
import xbird.util.io.FastBufferedInputStream;
import xbird.util.io.FastByteArrayInputStream;
import xbird.util.io.RemoteInputStream;
import xbird.util.primitive.Primitives;
import xbird.xquery.XQRTException;
import xbird.xquery.dm.value.Item;
import xbird.xquery.dm.value.sequence.SingleCollection;
import xbird.xquery.meta.IFocus;
/**
*
* <DIV lang="en"></DIV>
* <DIV lang="ja"></DIV>
*
* @author Makoto YUI (yuin405+xbird@gmail.com)
*/
public final class RemoteFocus implements IFocus<Item>, Externalizable {
private static final long serialVersionUID = 6866110139682464788L;
private static final Log LOG = LogFactory.getLog(RemoteFocus.class);
public static final int DEFAULT_FETCH_SIZE;
public static final float DEFAULT_FETCH_GROWFACTOR;
static {
DEFAULT_FETCH_SIZE = Primitives.parseInt(Settings.get("xbird.remote.fetchsize"), 256);
DEFAULT_FETCH_GROWFACTOR = Primitives.parseFloat(Settings.get("xbird.remote.fetchgrow"), 1.3f);
}
//--------------------------------------------
// shipped stuffs
private IRemoteFocusProxy _proxy;
private int _fetchSize;
private float _fetchGrow;
/** fetch-size watermark to prevent fetch-size growing */
private int _fetchWatermark;
private FetchMethod _fetchMethod;
//--------------------------------------------
// prefetch items
private final ArrayQueue<Item> _fetchedQueue;
private boolean _nomoreFetch = false;
//--------------------------------------------
// Formal Semantics variables
private Item _citem = null;
private int _cpos = 0;
//-------------------------------------------
// auxiliary items
private boolean _reachedEnd = false;
private int _position = 0;
private int _last = -1;
private final AtomicBoolean _closed = new AtomicBoolean(false);
public RemoteFocus() {//Externalizable
this._fetchedQueue = new ArrayQueue<Item>(512);
}
public RemoteFocus(IRemoteFocusProxy proxy, int fetchSize, float fetchGrow, FetchMethod fetchMethod) {
if(fetchSize < 1) {
throw new IllegalArgumentException("Illegal fetch size: " + fetchSize);
}
this._proxy = proxy;
this._fetchSize = fetchSize;
this._fetchGrow = fetchGrow;
this._fetchWatermark = (int) (fetchSize * Math.pow(fetchGrow, 19)); // 256*1.3^20=48652.7073, 256*1.3^19=37425.1594
this._fetchedQueue = new ArrayQueue<Item>(512);
this._fetchMethod = fetchMethod;
}
public IRemoteFocusProxy getProxy() {
return _proxy;
}
public <T extends Item> Iterator<T> getBaseFocus() {
try {
return _proxy.getBaseFocus();
} catch (RemoteException e) {
throw new XQRemoteException(e);
}
}
public boolean hasNext() {
if(_reachedEnd) {
return false;
}
if(!_fetchedQueue.isEmpty()) {
return true;
}
if(_nomoreFetch) {
this._reachedEnd = true;
close(true);
return false;
}
int fetchedLength = fetch();
if(fetchedLength == 0) {
this._reachedEnd = true;
close(true);
return false;
}
return true;
}
public Item next() {
if(_reachedEnd) {
return null;
}
int rest = _fetchedQueue.size();
if(rest > 0) {
Item head = _fetchedQueue.poll();
this._citem = head;
++_cpos;
return head;
}
if(_nomoreFetch) {
this._reachedEnd = true;
close(true);
return null;
}
int fetchedLength = fetch();
if(fetchedLength == 0) {
this._reachedEnd = true;
close(true);
return null;
}
Item head = _fetchedQueue.peek();
this._citem = head;
++_cpos;
return head;
}
private int fetch() {
final int fetched;
switch(_fetchMethod) {
case syncStream:
fetched = fetchSyncStream();
break;
case bytes:
fetched = fetchBytes(false);
break;
case compressed_bytes:
fetched = fetchBytes(true);
break;
case asyncStream: // TODO FetchMethod.asyncStream
throw new UnsupportedOperationException("FetchMethod.asyncStream is not supported yet.");
default:
throw new IllegalStateException("Illegal fetch method: " + _fetchMethod);
}
if(fetched < _fetchSize) {
this._nomoreFetch = true;
} else if(_fetchSize < _fetchWatermark) {
this._fetchSize = (int) (_fetchSize * _fetchGrow);
}
if(LOG.isDebugEnabled()) {
LOG.debug("fetched " + fetched + " items");
}
return fetched;
}
private int fetchSyncStream() {
final RemoteInputStream remoteIs;
try {
remoteIs = _proxy.fetch(_fetchSize);
} catch (RemoteException e) {
throw new XQRemoteException(e);
}
final FastBufferedInputStream bufferedIs = new FastBufferedInputStream(remoteIs);
final int fetched = fetchInternal(bufferedIs);
return fetched;
}
@Deprecated
private int fetchAsyncStream() {
final RemoteInputStream remoteIs;
try {
remoteIs = _proxy.asyncFetch(_fetchSize);
} catch (RemoteException e) {
throw new XQRemoteException(e);
}
final FastBufferedInputStream bufferedIs = new FastBufferedInputStream(remoteIs);
final ObjectInputStream ois;
try {
ois = new ObjectInputStream(bufferedIs);
} catch (IOException e) {
throw new IllegalStateException(e);
}
int count = 0;
try {
while(true) {
boolean hasmore = ois.readBoolean();
if(!hasmore) {
break;
}
++count;
Object ro = ois.readObject();
Item fetched = (Item) ro;
_fetchedQueue.offer(fetched);
if(count == _fetchSize) {
break;
}
}
} catch (IOException ioe) {
throw new XQRTException("failed to deserialize the fetched items", ioe);
} catch (ClassNotFoundException cnf) {
throw new XQRTException("failed to deserialize the fetched items", cnf);
}
return count;
}
private int fetchBytes(boolean compressed) {
assert (!_nomoreFetch);
assert (_fetchedQueue.isEmpty()) : _fetchedQueue;
final byte[] fetchedData;
try {
fetchedData = _proxy.fetchBytes(_fetchSize, compressed);
} catch (RemoteException e) {
throw new XQRemoteException(e);
}
final FastByteArrayInputStream fis = new FastByteArrayInputStream(fetchedData);
final InputStream is;
try {
is = compressed ? new LZFInputStream(fis) : fis;
} catch (IOException e) {
throw new IllegalStateException(e);
}
int fetched = fetchInternal(is);
return fetched;
}
private int fetchInternal(InputStream is) {
final ObjectInputStream ois;
final int fetchedLength;
try {
ois = new ObjectInputStream(is);
fetchedLength = ois.readInt();
} catch (IOException ioe) {
throw new XQRTException("failed to deserialize the fetched items", ioe);
}
try {
for(int i = 0; i < fetchedLength; i++) {
Object ro = ois.readObject();
Item fetched = (Item) ro;
_fetchedQueue.offer(fetched);
}
} catch (IOException ioe) {
throw new XQRTException("failed to deserialize the fetched items", ioe);
} catch (ClassNotFoundException cnf) {
throw new XQRTException("failed to deserialize the fetched items", cnf);
}
return fetchedLength;
}
public void offerItem(Item item) {
_fetchedQueue.offer(item);
}
public Item pollItem() {
return _fetchedQueue.poll();
}
public void remove() {
throw new UnsupportedOperationException();
}
public Item getContextItem() {
return _citem;
}
public Item setContextItem(Item item) {
assert (item != null);
if(item instanceof SingleCollection) {
final IFocus<Item> focus = item.iterator();
item = focus.next();
assert (!focus.hasNext());
}
Item prev = _citem;
this._citem = item;
return prev;
}
public int getContextPosition() {
return _cpos;
}
public void setContextPosition(int pos) {
this._cpos = pos;
}
public boolean reachedEnd() {
return _reachedEnd;
}
public void setReachedEnd(boolean end) {
this._reachedEnd = end;
}
public int getPosition() {
return _position;
}
public int incrPosition() {
return ++_position;
}
public int getLast() {
return _last;
}
public void setLast(int last) {
this._last = last;
}
public final RemoteFocus iterator() {
return this;
}
public void close() {
close(true);
}
public void closeQuietly() {
close(true);
}
private void close(final boolean force) {
if(_closed.compareAndSet(false, true)) {
final IRemoteFocusProxy proxy = _proxy;
if(proxy != null) {
try {
proxy.close(force);
} catch (RemoteException e) {
LOG.warn("failed terminating remote focus: " + proxy, e);
} finally {
this._proxy = null;
}
}
}
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(_proxy);
out.writeInt(_fetchSize);
out.writeFloat(_fetchGrow);
out.writeInt(_fetchWatermark);
out.writeObject(_fetchMethod);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this._proxy = (IRemoteFocusProxy) in.readObject();
this._fetchSize = in.readInt();
this._fetchGrow = in.readFloat();
this._fetchWatermark = in.readInt();
this._fetchMethod = (FetchMethod) in.readObject();
}
}