/*
* Copyright (C) 2009 Google Inc.
*
* 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.
*/
package org.waveprotocol.wave.examples.fedone.rpc;
import com.google.protobuf.Message;
import com.google.protobuf.UnknownFieldSet;
import java.nio.channels.ByteChannel;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* A tiny manager for a SequencedProtoChannel that allows incoming messages to
* be waited for and returned cleanly.
*
*
*/
public class FakeProtoChannelManager {
public final SequencedProtoChannel channel;
private final BlockingQueue<SequencedObject<?>> queue =
new LinkedBlockingQueue<SequencedObject<?>>();
/**
* A container class for a sequence numbed and protocol buffer message pair.
*/
public static class SequencedObject<V> {
final public long sequenceNo;
final public V message;
private SequencedObject(long sequenceNo, V message) {
this.sequenceNo = sequenceNo;
this.message = message;
}
/**
* Helper method to instantiate new SequencedObject instances.
* @param <T>
*/
public static <T> SequencedObject<T> of(long sequenceNo, T message) {
return new SequencedObject<T>(sequenceNo, message);
}
@Override
public boolean equals(Object thatObject) {
if (!(thatObject instanceof SequencedObject)) {
return false;
} else {
SequencedObject<?> that = (SequencedObject<?>) thatObject;
return that.sequenceNo == this.sequenceNo && that.message.equals(this.message);
}
}
@Override
public int hashCode() {
return (31*17 + (int) sequenceNo) * 31 + message.hashCode();
}
}
public FakeProtoChannelManager(ByteChannel channel) {
this.channel = new SequencedProtoChannel(channel, new ProtoCallback() {
@Override
public void message(long sequenceNo, Message message) {
queue.add(SequencedObject.of(sequenceNo, message));
}
@Override
public void unknown(long sequenceNo, String messageType, UnknownFieldSet message) {
queue.add(SequencedObject.of(sequenceNo, message));
}
@Override
public void unknown(long sequenceNo, String messageType, String message) {
queue.add(SequencedObject.of(sequenceNo, message));
}
});
this.channel.startAsyncRead();
}
/**
* Wait for any incoming message and return it. If this is called from
* multiple threads, each thread will be handed a different message from the
* queue.
*
* @param timeout the timeout, in seconds, to wait for messages
* @return an incoming message, or null if none were found
*/
public SequencedObject<?> waitForMessage(int timeout) {
try {
return queue.poll(timeout, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new IllegalStateException("Operation should not be interrupted", e);
}
}
}