Answer: Something like:
public void transmitPacket( InputChannelVector inputChannels, OutputChannelVector outputChannels ) { try { Object myObject = inputChannels.firstElement().readObject(); } catch( ChannelEmptyException cee ) { } catch( ChannelDisabledException cde ) { } }
This works under the assumption that inputChannels has a first element, and that there is a packet to be read. To make it more robust, we could put the try-catch in an if-statement, checking to see if there were transmitPacketually any elements in inputChannels (see next exercise), or we could specific that the next channel should be read, if one of the exceptions are thrown (see answers to the In-Lab part for more detail).
Question 2: Write a statement that could appear inside a nodeBehavior's transmitPacket method and that determines whether there are any output channels available for writing. A channel is available if it exists, is not disabled, and is not full.
Answer: This question is a bit more complicated. One possible way of implementing this: first, check and make sure that outputChannels has at least one channel in it. Then, try to write to the channel. If you can't write to it, try to write to the next one, until you've hit the last channel (remember, the first element of outputChannels is at place '0', so the last channel will be the size() of outputChannels - 1). One way of doing it:
public void transmitPacket( InputChannelVector inputChannels, OutputChannelVector outputChannels ) { try { Object packetToSend = inputChannels.firstElement().readObject(); } catch( ChannelEmptyException cee ) { } catch( ChannelDisabledException cde ) { } int outSize() = outputChannels.size(); if( outSize() > 0 ) { for( int i = 0; i < outSize(); i ++ ) { try { outputChannels.elementAt( i ).writeObject( packetToSend ); break; } catch( ChannelEmptyException cee ) { } } catch( ChannelDisabledException cde ) { } } } }
A for loop is sort of like a glorified, condensed while loop -- another way of writing for( int i = 0; i < outSize(); i++ ) is
int i = 0; while( i < outSize() ) { // try, blah blah i++; }
Just as with a while loop, we can use break; to stop the loop.
Question 3: Chapter 9, exercise 2. (Hint: Console also has a readln() method.) [Define a class that periodically reads from the Console and writes the value back to the Console.]
Answer: To read and write from the Console, we can use cs101.io.Console -- to do something periodically, we can implement cs101.lang.Animate and use an AnimatorThread. So:
import cs101.io.Console; import cs101.lang.*; public class ReaderWriter implements Animate { AnimatorThread myThread; public ReaderWriter( ) { this.myThread = new AnimatorThread( this ); this.myThread.startExecution( ); } public void transmitPacket( ) { String myString = Console.readln(); Console.println( myString ); } }
You should be familiar with how to use AnimatorThread and Console.
package nodeNet;
public BoringNodeBehavior implements NodeBehavior {
public void transmitPacket(InputChannelVector inputChannels,
OutputChannelVector outputChannels) {
// transmitPacket is empty.
}
}
package nodeNet;Alas, we find that while we seem to do okay in certain circumstances, our code can crash, eat packets, or create them. How can we fix this? Well, the first problem, our code can crash, is caused by the lines where we access the 1st element of the ChannelVector (either the input or the output) without ensuring that that channel even exists! Imagine the situation where there are no inputs or outputs. Does it make sense to access the first (non-existent) element? We remedy the situation with the following modifications:
public Strategy1_1 implements NodeBehavior {Object o; // storage for the object we will read/write
public void transmitPacket(InputChannelVector inputChannels,
OutputChannelVector outputChannels) {
// first we try to read a packet, catching the
// necesssary exceptions
try {
o = inputChannel.firstElement().readObject();
} catch (ChannelEmptyException e1) {
} catch (ChannelDisabledException e2) {}// now we have a packet, so we try to write it.
try {
outputChannels.firstElement().writeObject(o);
} catch (ChannelFullException e3) {
} catch (ChannelDisabledException e4) {
}
}
package nodeNet;Now we need to take care of the problems where we create and destroy packets. How do we create packets? Well, suppose we have read a packet and successfully written it. Our transmitPacket method is called again and this time we fail to successfully read a packet, but Object o is still successfully written to the output channel. Oops! We need to prevent this double (or triple, or quadruple, or...) writing somehow. The following does the trick. Now whenever we write a packet we prevent ourselves from ever writing it again by unsticking our label and checking to make sure we only write packets if Object o is non-null.
public Strategy1_2 implements NodeBehavior {Object o; // storage for the object we will read/write
public void transmitPacket(InputChannelVector inputChannels,
OutputChannelVector outputChannels) {
// first we try to read a packet, catching the
// necesssary exceptions
try {
// check for at least one input
if (inputChannels.size() != 0) {
o = inputChannel.firstElement().readObject();
}
} catch (ChannelEmptyException e1) {
} catch (ChannelDisabledException e2) {}// now we have a packet, so we try to write it.
try {
// check for at least one output
if (outputChannels.size() != 0) {
outputChannels.firstElement().writeObject(o);
}
} catch (ChannelFullException e3) {
} catch (ChannelDisabledException e4) {
}
}
package nodeNet;The final problem we face is that our node sometimes eats packets, meaning that not every packet that enters via one of the inputChannels makes it to an OutputChannel. Borrowing from the above code, though, we can see that the problem is that we read a packet, regardless of whether or not we wrote the one we already had. To prevent this, only do a readObject if o is null (has been written). This final implementation fo our first strategy is thus:
public Strategy1_3 implements NodeBehavior {Object o; // storage for the object we will read/write
public void transmitPacket(InputChannelVector inputChannels,
OutputChannelVector outputChannels) {
// first we try to read a packet, catching the
// necesssary exceptions
try {
// check for at least one input
if (inputChannels.size() != 0) {
o = inputChannel.firstElement().readObject();
}
} catch (ChannelEmptyException e1) {
} catch (ChannelDisabledException e2) {}// now we have a packet, so we try to write it.
try {
// check for at least one output, and check to make sure
// we have a packet ready to write.
if ((outputChannels.size() != 0) && (o != null)) {
outputChannels.firstElement().writeObject(o);
// if no exception is thrown, we have successfully written
// the packet, so unstick the label from the Object.
o = null;
}
} catch (ChannelFullException e3) {
} catch (ChannelDisabledException e4) {
}
}
package nodeNet;
public Strategy1_4 implements NodeBehavior {Object o; // storage for the object we will read/write
public void transmitPacket(InputChannelVector inputChannels,
OutputChannelVector outputChannels) {
// first we try to read a packet, catching the
// necesssary exceptions
try {
// check for at least one input, and make sure we're not
// already holding an Object.
if ((inputChannels.size() != 0) && (o == null)) {
o = inputChannel.firstElement().readObject();
}
} catch (ChannelEmptyException e1) {
} catch (ChannelDisabledException e2) {}// now we have a packet, so we try to write it.
try {
// check for at least one output, and check to make sure
// we have a packet ready to write.
if ((outputChannels.size() != 0) && (o != null)) {
outputChannels.firstElement().writeObject(o);
// if no exception is thrown, we have successfully written
// the packet, so unstick the label from the Object.
o = null;
}
} catch (ChannelFullException e3) {
} catch (ChannelDisabledException e4) {
}
}
// counters to determine which input/output
to write to first
private int nextInIndex, nextOutIndex;
// the packet we read/write
private Object packetToBeSent;
private void readFromChannel(InputChannelVector
inputChannels)
{
// if there's no input channel, exit right
away. Note that this
// is not strictly necessary (the for loop
is correct even for
// the case where we have size() ==
0.)
if (inputChannels.size() == 0) return;
// try to read from each InputChannel.
Stop when either:
// 1. a packet has been successfully read,
or
// 2. when we have tried each input channel
at least once.
for (int i = 0; i < inputChannels.size() &&
!readToWrite; i++)
{
try
{
packetToBeSent =
inputChannels.elementAt( nextInIndex
).readObject();
readyToWrite = true;
}
catch(ChannelEmptyException exc) {}
catch(ChannelDisabledException exc2) {}
// Increment the channel we will
read from, modulo size()
nextInIndex = (nextInIndex + 1) % inputChannels.size();
}
}
private void writeToChannel(OutputChannelVector outputChannels)
{
// If there's no out channel, exit right
away. As above, this
// is not stricly necessary.
if (outputChannels.size() == 0) return;
// try to write to each OutputChannel.
Stop when either:
// 1. a packet has been successfully
written, or
// 2. when we have tried each out channel
at least once.
for (int i = 0; i < outputChannels.size() &&
readToWrite; i++)
{
try
{
outputChannels.elementAt( nextOutIndex ).writeObject(
packetToBeSent );
readyToWrite = false;
}
catch(ChannelFullException exc) {}
catch(ChannelDisabledException exc2) {}
// Increment the channel we will
write to, modulo size();
nextOutIndex = ++nextOutIndex % outputChannels.size();
}
}
public void transmitPacket(InputChannelVector inputs,
OutputChannelVector outputs)
{
if (readToWrite) { writeToChannel( outputs ); }
else /* not readToWrite */ {
readFromChannel( inputs ); }
}
}
Important Note:
IntermediateNodeBehavior is not necessarily the best, most efficient, or most "correct" implementation of NodeBehavior. Many of you have code that is as effective in moving packets. Your code is correct too. The code we have given you here is one possible solution, and probably could be improved upon. Indeed, some of the things included in our code are unnessary for correctness, but were included to make what was happening more clear. For instance, we do not really need to have the lines:
if (inputChannels.size() == 0) return;becuase the for loop that follows would correctly exit without ever attempting a read/writeObject. So, if you think your implementation is better than ours, let us know and we'll post it to the homepage.
if (outputChannels.size() == 0) return;
This course is a part of Lynn Andrea Stein's Rethinking CS101 project at
the MIT AI Lab and the Department of Electrical Engineering and
Computer Science at the Massachusetts
Institute of Technology.