Something went wrong on our end
-
Brutzman, Don authoredBrutzman, Don authored
DisThreadedNetworkInterface.java 33.46 KiB
/**
* Copyright (c) 2008-2021, MOVES Institute, Naval Postgraduate School (NPS). All rights reserved.
* This work is provided under a BSD open-source license, see project license.html and license.txt
*/
package edu.nps.moves.dis7.utilities;
import edu.nps.moves.dis7.enumerations.DisPduType;
import edu.nps.moves.dis7.pdus.DisTime;
import edu.nps.moves.dis7.pdus.EntityStatePdu;
import edu.nps.moves.dis7.pdus.Pdu;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This is a thread-safe, multicast DIS network interface class.
*
* @author Mike Bailey, jmbailey@nps.edu
* @since Jul 29, 2019
*/
public class DisThreadedNetworkInterface
{
/** ********** Begin class ************** */
/**
* MTU 8192: TODO this has actually been superseded by a larger buffer size,
* but good enough for now
*/
public static final int MAX_DIS_PDU_SIZE = 8192;
/**
* MTU 1500: size of an Ethernet frame, common value to avoid packet
* segmentation
*/
public static final int MAX_TRANSMISSION_UNIT_SIZE = 1500;
// independently available parameters for each DisThreadedNetworkInterface object
private String disAddress;
private int disPort;
private boolean killed = false; // thread loop sentinel
private InetAddress inetAddress;
private InetSocketAddress inetSocket;
private NetworkInterface networkInterface;
private DatagramSocket datagramSocket;
/** Default multicast group address <code>239.1.2.3</code> for send and receive connections.
* @see <a href="https://en.wikipedia.org/wiki/Multicast_address">https://en.wikipedia.org/wiki/Multicast_address</a> */
public static String DEFAULT_DIS_ADDRESS = "239.1.2.3";
/** Default socket port <code>3000</code>, matches Wireshark DIS capture default
* @see <a href="https://en.wikipedia.org/wiki/Port_(computer_networking)">https://en.wikipedia.org/wiki/Port_(computer_networking)</a> */
public static int DEFAULT_DIS_PORT = 3000;
private String descriptor = new String();
private String TRACE_PREFIX = "[" + (this.getClass().getSimpleName() + " " + getDescriptor()).trim() + "] ";
private boolean verbose = true;
private boolean verboseReceipt = true;
private boolean verboseSending = true;
private boolean verboseIncludesTimestamp = false;
/* *********** queues and lists and public methods ************** */
private final List<PduListener> everyTypeListeners = new ArrayList<>();
private final Map<DisPduType, List<PduListener>> typeListeners = new HashMap<>();
private final List<RawPduListener> rawListeners = new ArrayList<>();
// https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/LinkedBlockingQueue.html
private final LinkedBlockingQueue<Pdu> pdus2send = new LinkedBlockingQueue<>(); // FIFO
ByteArrayOutputStream baos;
DataOutputStream dos;
DatagramPacket packet;
/**
* Pdu listener class and interface
*/
public interface PduListener
{
/**
* Callback method
* @param pdu received pdu
*/
void incomingPdu(Pdu pdu);
}
/**
* Raw pdu listener class and interface
*/
public interface RawPduListener
{
/**
* Callback method
* @param bAndL exposed buffer to receive incoming pdu
*/
void incomingPdu(ByteArrayBufferAndLength bAndL);
}
/**
* Stores data for further processing
*/
public class ByteArrayBufferAndLength
{
/**
* Active ByteArray buffer
*/
public byte[] bufferByteArray;
/**
* Active ByteArray buffer length
*/
public int length;
/**
* Default constructor for data storage
*
* @param bufferByteArray the data buffer to store
* @param length the length of the data buffer
*/
public ByteArrayBufferAndLength(byte[] bufferByteArray, int length)
{
this.bufferByteArray = bufferByteArray;
this.length = length;
}
}
/**
* Object constructor using default multicast address and port, no descriptor
*/
public DisThreadedNetworkInterface()
{
this(DEFAULT_DIS_ADDRESS, DEFAULT_DIS_PORT, "");
}
/**
* Object constructor with descriptor, using default multicast address and port
* @param newDescriptor simple descriptor name for this interface
*/
public DisThreadedNetworkInterface(String newDescriptor)
{
this(DEFAULT_DIS_ADDRESS, DEFAULT_DIS_PORT, newDescriptor);
}
/**
* Object constructor using specified multicast address and port
* @param address the multicast group or unicast address to utilize
* @param port the multicast port to utilize
*/
public DisThreadedNetworkInterface(String address, int port)
{
this(address, port, "");
}
/**
* Object constructor using specified multicast address and port, plus descriptor.
* @param address the multicast group or unicast address to utilize
* @param port the multicast port to utilize
* @param newDescriptor simple descriptor name for this interface
*/
public DisThreadedNetworkInterface(String address, int port, String newDescriptor)
{
if (newDescriptor == null)
descriptor = "";
else descriptor = newDescriptor;
TRACE_PREFIX = "[" + (DisThreadedNetworkInterface.class.getSimpleName() + " " + descriptor).trim() + "] ";
disAddress = address;
disPort = port;
try
{
inetAddress = InetAddress.getByName(disAddress);
}
catch (UnknownHostException ex)
{
Logger.getLogger(DisThreadedNetworkInterface.class.getName()).log(Level.SEVERE, null, ex);
}
inetSocket = new InetSocketAddress(inetAddress, disPort); // tests that accessor methods are working as set
networkInterface = findIpv4Interface();
begin();
}
/**
* Add a listener to accept only pdus of a given type
* @param newListener listener instance implementing the RawPduListener interface
* @param disPduType Pdu type
*/
public void addListener(PduListener newListener, DisPduType disPduType)
{
if (disPduType == null)
{
addListener(newListener);
}
else
{
List<PduListener> arLis = typeListeners.get(disPduType);
if (arLis == null)
{
arLis = new ArrayList<>();
typeListeners.put(disPduType, arLis);
}
arLis.add(newListener);
}
}
/**
* Add a listener to accept all pdu types
* @param newListener listener instance implementing the RawPduListener interface
*/
public void addListener(PduListener newListener)
{
everyTypeListeners.add(newListener);
}
/**
* Remove previously added listener
*
* @param priorListener listener instance implementing the RawPduListener interface
*/
public void removeListener(PduListener priorListener)
{
everyTypeListeners.remove(priorListener);
typeListeners.entrySet().forEach(entry ->
{
List<PduListener> arLis = entry.getValue();
if (arLis.contains(priorListener))
{
arLis.remove(priorListener);
}
});
// additional sleep, hopefully allowing teardown to proceed to completion
sleep(100l); // TODO needed?
}
/**
* Add a listener to accept pdus of all types in the form of a byte array
*
* @param rawPduListener listener instance implementing the RawPduListener interface
*/
public void addRawListener(RawPduListener rawPduListener)
{
rawListeners.add(rawPduListener);
}
/**
* Remove previously added raw listener
*
* @param rawPduListener listener instance implementing the RawPduListener interface
*/
public void removeRawListener(RawPduListener rawPduListener)
{
rawListeners.remove(rawPduListener);
// additional sleep, hopefully allowing teardown to proceed to completion
sleep(100l); // TODO needed?
}
/**
* Method renamed as <code>getAddress()</code> so use that method instead.
* @return current multicast address value
*/
@Deprecated
public String getMulticastGroup()
{
return getAddress();
}
/**
* Get current multicast (or unicast) network address for send and receive connections.
* @see <a href="https://en.wikipedia.org/wiki/Multicast_address">https://en.wikipedia.org/wiki/Multicast_address</a>
* @return current multicast address value
*/
public String getAddress()
{
return this.disAddress;
}
/**
* Network address for send and receive connections.
* @see <a href="https://en.wikipedia.org/wiki/Multicast_address">https://en.wikipedia.org/wiki/Multicast_address</a>
* @param newAddress the new network address to set
*/
public void setAddress(String newAddress) {
this.disAddress = newAddress;
}
/**
* Send the given pdu to the network using the IP address and port given to the constructor
* @param pdu the pdu to send
*/
public void send(Pdu pdu)
{
pdus2send.add(pdu);
}
/* *************** networking i/o ************* */
private PduFactory pduFactory = new PduFactory();
private Thread sendingThread;
private Thread receiveThread;
/** Initialization of threads, otherwise no action that while they remain running */
private synchronized void createThreads()
{
if (receiveThread == null)
{
// System.out.println("DisThreadedNetworkInterface createThreads() start receiveThread...");
receiveThread = new Thread(receiveThreadRunnable, "receiveThread");
// https://stackoverflow.com/questions/2213340/what-is-a-daemon-thread-in-java
receiveThread.setDaemon(false); // user thread, not system thread
receiveThread.setPriority(Thread.NORM_PRIORITY);
receiveThread.start();
}
System.out.println("DisThreadedNetworkInterface createThreads() receiveThread.isAlive()=" + receiveThread.isAlive());
if (sendingThread == null)
{
// System.out.println("DisThreadedNetworkInterface createThreads() start sendingThread...");
sendingThread = new Thread(sendingThreadRunnable, "sendingThread");
// https://stackoverflow.com/questions/2213340/what-is-a-daemon-thread-in-java
sendingThread.setDaemon(false); // user thread, not system thread
sendingThread.setPriority(Thread.NORM_PRIORITY);
sendingThread.start();
}
System.out.println("DisThreadedNetworkInterface createThreads() sendingThread.isAlive()=" + receiveThread.isAlive());
}
/**
* Can be used to restart DisThreadedNetworkInterface if closed.
* Create datagram socket if not already available; can also be invoked by
* either sender or receiver thread to ensure datagram socket is open.
* Method was originally named start().
*/
public void begin()
{
createDatagramSocket(); // common asset, synchronized to prevent interleaved reentry
createThreads();
}
/**
* Create datagram socket if not already available; can also be invoked by
* either sender or receiver thread to ensure datagram socket is open.
* Synchronized method to prevent interleaved reentry.
* @see <a href="https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html">Java Tutorials: Synchronized Methods</a>
*/
// @SuppressWarnings("SleepWhileHoldingLock") // intentional
private synchronized void createDatagramSocket()
{
// The capacity could go up to MAX_DIS_PDU_SIZE, but this should be good for now
baos = new ByteArrayOutputStream(MAX_TRANSMISSION_UNIT_SIZE);
dos = new DataOutputStream(baos);
packet = new DatagramPacket(baos.toByteArray(), baos.size(), inetSocket);
String message = TRACE_PREFIX;
if ((datagramSocket != null))
{
if (datagramSocket.isConnected())
{
System.out.println(message + "datagramSocket is already connected...");
}
else if (datagramSocket.isClosed())
{
datagramSocket = null;
System.err.println(" *** " + TRACE_PREFIX + "datagramSocket was previously used but closed, resetting...");
}
}
if (datagramSocket == null) // may have just been reset
{
try
{
// System.out.println(message + "datagramSocket is getting connected...");
// The initial value of the SO_BROADCAST socket option is FALSE
datagramSocket = new MulticastSocket(getPort());
datagramSocket.setSoTimeout(1000); // msec timeout on reading and then continue looping in order to avoid blocking
((MulticastSocket) datagramSocket).joinGroup(inetSocket, networkInterface);
Thread.sleep (100L); // allow threads, streams to catch up
}
catch (InterruptedException ex)
{
System.err.println(" *** " + TRACE_PREFIX + "InterruptedException in DisThreadedNetworkInterface createDatagramSocket(): " + ex.getLocalizedMessage());
}
catch (IOException ex)
{
System.err.println(" *** " + TRACE_PREFIX + "IOException in DisThreadedNetworkInterface createDatagramSocket(): " + ex.getLocalizedMessage());
}
}
if (hasVerboseOutput())
{
// if (hasVerboseOutputIncludesTimestamp())
// message += " (timestamp " + getTimestamp()); // TODO
message += "datagramSocket.joinGroup address=" + inetSocket.getHostString() + " port=" + inetSocket.getPort() +
" isConnected()=" + datagramSocket.isConnected() + " createDatagramSocket() complete.";
System.out.println(message);
System.out.flush();
}
}
@SuppressWarnings("SleepWhileInLoop") // intentional
private Runnable receiveThreadRunnable = () -> {
int pduReceiptCounter = 0;
// The capacity could go up to MAX_DIS_PDU_SIZE, but this should be good for now
// The raw listeners will strip off any extra padding and process what is required
ByteBuffer byteBuffer = ByteBuffer.allocate(MAX_TRANSMISSION_UNIT_SIZE);
DatagramPacket receivedPacket = new DatagramPacket(byteBuffer.array(), byteBuffer.capacity());
while (!killed && !Thread.currentThread().isInterrupted()) // loop until terminated
{
try
{
datagramSocket.receive(receivedPacket); // blocks here waiting for next DIS pdu to be received on multicast IP and specified port
toRawListeners(receivedPacket.getData(), receivedPacket.getLength());
Pdu nextPdu = pduFactory.createPdu(byteBuffer);
if (nextPdu != null)
{
pduReceiptCounter++; // TODO experimental, add to generator as a commented-out diagnostic; consider adding diagnostic mode
if (hasVerboseOutput() && hasVerboseReceipt())
{
String pad = "";
if (pduReceiptCounter < 10)
pad = " ";
String message = TRACE_PREFIX + "[receipt " + pad + pduReceiptCounter + "] " + nextPdu.getPduType().toString();
if (hasVerboseOutputIncludesTimestamp())
message += " (timestamp " + DisTime.timeStampToString(nextPdu.getTimestamp());
if (nextPdu.getPduType() == DisPduType.ENTITY_STATE)
message += " " + String.format("%11s", ((EntityStatePdu)nextPdu).getMarkingString());
message += ", size " + nextPdu.getMarshalledSize() + " bytes)";
System.err.flush();
System.out.println(message);
System.out.flush();
}
toListeners(nextPdu);
}
byteBuffer.clear();
}
catch (IOException ex)
{
// read commonly times out while waiting for PDUs to arrive, and so this trace statement is for debugging
// System.err.println(TRACE_PREFIX + "Exception in DisThreadedNetworkInterface receiveThread: " + ex.getLocalizedMessage());
}
}
// returning kills thread, do not put any other steps here
};
private final Runnable sendingThreadRunnable = () -> {
int pduSentCounter = 0;
// The capacity could go up to MAX_DIS_PDU_SIZE, but this should be good for now
baos = new ByteArrayOutputStream(MAX_TRANSMISSION_UNIT_SIZE);
dos = new DataOutputStream(baos);
packet = new DatagramPacket(baos.toByteArray(), baos.size(), inetSocket);
while (!killed && !Thread.currentThread().isInterrupted()) // loop until terminated
{
try
{
Pdu nextPdu = pdus2send.take();
nextPdu.marshal(dos);
packet.setData(baos.toByteArray());
datagramSocket.send(packet);
pduSentCounter++;
if (hasVerboseOutput() && hasVerboseSending())
{
String pad = "";
if (pduSentCounter < 10)
pad = " ";
String message = TRACE_PREFIX + "[sending " + pad + pduSentCounter + "] " + nextPdu.getPduType().toString();
if (hasVerboseOutputIncludesTimestamp())
message += " (timestamp " + DisTime.timeStampToString(nextPdu.getTimestamp());
if (nextPdu.getPduType() == DisPduType.ENTITY_STATE)
message += " " + String.format("%11s", ((EntityStatePdu)nextPdu).getMarkingString());
message += ", size " + nextPdu.getMarshalledSize() + " bytes)";
System.err.flush();
System.out.println(message);
System.out.flush();
}
dos.flush(); // immediately force pdu write
baos.reset(); // prepare for next send
}
catch (InterruptedException ex)
{
// no response, expected termination
}
catch (Exception ex)
{
System.err.println(TRACE_PREFIX + "Exception in DisThreadedNetworkInterface sendingThread: " + ex.getLocalizedMessage());
ex.printStackTrace();
}
}
// returning kills thread, do not put any other steps here
};
private void toListeners(Pdu pdu)
{
if (everyTypeListeners.isEmpty()) {
return;
}
if (pdu != null) {
everyTypeListeners.forEach(lis -> lis.incomingPdu(pdu));
if (typeListeners.isEmpty()) {
return;
}
List<PduListener> arLis = typeListeners.get(pdu.getPduType());
if (arLis != null) {
arLis.forEach(lis -> lis.incomingPdu(pdu));
}
}
}
private void toRawListeners(byte[] data, int len)
{
if(rawListeners.isEmpty())
return;
ByteArrayBufferAndLength bl = new ByteArrayBufferAndLength(data, len);
rawListeners.forEach(lis->lis.incomingPdu(bl));
}
/** Method renamed as <code>close()</code> so use that method instead.
*/
@Deprecated
public void kill()
{
setKillSentinelAndInterrupts();
}
/** Tell sendingThread and receiveThread to stop. */
public void setKillSentinelAndInterrupts() // TODO rename AndInterrupts
{
killed = true; // set loop sentinel for threads to finish
// https://stackoverflow.com/questions/26647840/how-do-i-interrupt-kill-a-hung-thread-in-java
sendingThread.interrupt();
receiveThread.interrupt();
System.out.println ("*** setKillSentinelAndInterrupts() killed=" + killed +
" sendingThread.isInterrupted()=" + sendingThread.isInterrupted() +
" receiveThread.isInterrupted()=" + receiveThread.isInterrupted());
}
/** Terminate the instance after completion of pending send/receive activity.
* Synchronized to prevent interleaved invocation. */
public synchronized void close()
{
try
{
if (!killed)
{
setKillSentinelAndInterrupts(); // killed = true;
Thread.sleep(100l); // let sendingThread and receiveThread stop
}
pdus2send.clear(); // all stop
dos.flush(); // immediately force pdu write, if any remain
baos.close();
dos.close();
System.out.println ("*** DisThreadedNetworkInterface close():" +
" pdus2send.size()=" + pdus2send.size() +
" baos.size()=" + baos.size() + " dos.size()=" + dos.size());
// now close socket (after killing threads so that hopefully the socket doesn't lock them)
if (datagramSocket != null && !datagramSocket.isClosed())
{
try {
((MulticastSocket)datagramSocket).leaveGroup(inetSocket, networkInterface);
} catch (IOException ex) {
Logger.getLogger(DisThreadedNetworkInterface.class.getName()).log(Level.SEVERE, null, ex);
}
datagramSocket.close();
sleep(100l); // TODO needed?
if (hasVerboseOutput())
{
String message = TRACE_PREFIX;
// if (hasVerboseOutputIncludesTimestamp())
// message += " (timestamp " + getTimestamp()); // TODO
message += "datagramSocket.leaveGroup address=" + getAddress() + " port=" + getPort() +
" isClosed()=" + datagramSocket.isClosed() + " close() complete.";
System.out.println(message);
System.out.flush();
}
}
killThread (sendingThread); // making sure
killThread (receiveThread); // making sure
Thread.sleep(500l);
reportThreadStatus();
}
catch (Exception e)
{
System.err.println ("*** DisThreadedNetworkInterface close()() unexpected exception!");
}
}
public void killThread(Thread threadToKill)
{
try { // join and kill threadToKill
if (threadToKill != null)
{
threadToKill.interrupt();
threadToKill.join(4000); // wait for thread to die, msec max duration
Thread.sleep(100l);
}
}
catch (InterruptedException ie)
{
System.err.println ("*** DisThreadedNetworkInterface threadToKill join() failed to wait for threadToKill to die");
System.err.flush();
ie.printStackTrace(System.err);
}
String threadAlive = "null";
if (threadToKill != null)
threadAlive = String.valueOf(threadToKill.isAlive());
System.out.println ("*** killThread() status: " + threadToKill.getName() + ".isAlive()=" + threadAlive +
" " + threadToKill.getName() + ".isInterrupted()=" + threadToKill.isInterrupted());
System.err.flush(); // ensure all output sent
System.out.flush(); // ensure all output sent
}
/** Report whether sendingThread and receiveThread are alive */
public void reportThreadStatus()
{
// report if successful
String sendingThreadAlive = "null";
String receiveThreadAlive = "null";
if (sendingThread != null)
sendingThreadAlive = String.valueOf( sendingThread.isAlive());
if (receiveThread != null)
receiveThreadAlive = String.valueOf(receiveThread.isAlive());
System.out.println ("*** Thread close status: sendingThread.isAlive()=" + sendingThreadAlive +
" receiveThread.isAlive()=" + receiveThreadAlive);
System.err.flush(); // ensure all output sent
System.out.flush(); // ensure all output sent
}
/** Thread sleep for indicated interval
* @param duration milliseconds */
private void sleep(long duration)
{
try {
Thread.sleep(duration);
}
catch (InterruptedException ie)
{
System.err.println ("*** " + getClass().getName() + ".sleep(" + duration + ") failed to sleep");
System.err.flush();
ie.printStackTrace(System.err);
}
}
/**
* Find proper IPV4 interface on this computer for use with the djs address
*
* @return a network interface to use to join a multicast group
*/
public static NetworkInterface findIpv4Interface()
{
try {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces != null && networkInterfaces.hasMoreElements())
{
NetworkInterface networkInterface = networkInterfaces.nextElement();
if (networkInterface.isUp())
{
// now check available addresses available on this running interface
Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
while (addresses.hasMoreElements())
{
InetAddress nextAddress = addresses.nextElement();
if (nextAddress instanceof Inet4Address && !nextAddress.isLoopbackAddress() && !nextAddress.isLinkLocalAddress())
{
// can't use object descriptor in static context
System.out.println("[" + DisThreadedNetworkInterface.class.getSimpleName() + "] " + "using network interface " + networkInterface.getDisplayName());
return networkInterface;
}
}
}
}
} catch (SocketException ex) {
Logger.getLogger(DisThreadedNetworkInterface.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
/**
* Set whether or not trace statements are provided when packets are sent or received.
* @param newValue the verbose status to set. Also resets verboseReceipt and verboseSending to match.
* @see verboseReceipt
* @see verboseSending
*/
public void setVerbose(boolean newValue)
{
this.verbose = newValue;
this.verboseReceipt = newValue;
this.verboseSending = newValue;
}
/**
* Whether or not trace statements are provided when packets are sent or received.
* @return the verbose status
* @see verboseReceipt
* @see verboseSending
*/
public boolean hasVerboseOutput()
{
return verbose;
}
/**
* Set whether or not trace statements are provided when packets are received.
* @param newValue the verboseReceipt status to set
* @see verbose
* @see verboseSending
*/
public void setVerboseReceipt(boolean newValue)
{
this.verboseReceipt = newValue;
verbose = (verboseReceipt || verboseSending);
}
/**
* Whether or not trace statements are provided when packets are received.
* @return the verboseReceipt status
*/
public boolean hasVerboseReceipt()
{
return verboseReceipt;
}
/**
* Set whether or not trace statements are provided when packets are sent.
* @param newValue the verboseSending status to set
* @see verbose
* @see verboseReceipt
*/
public void setVerboseSending(boolean newValue)
{
this.verboseSending = newValue;
verbose = (verboseReceipt || verboseSending);
}
/**
* Whether or not trace statements are provided when packets are sent.
* @return the verboseSending status
*/
public boolean hasVerboseSending()
{
return verboseSending;
}
/**
* Whether or not trace statements include timestamp values.
* @return the verboseIncludesTimestamp value
*/
public boolean hasVerboseOutputIncludesTimestamp()
{
return verboseIncludesTimestamp;
}
/**
* Set whether or not trace statements include timestamp values.
* @param verboseIncludesTimestamp the value to set
*/
public void setVerboseIncludesTimestamp(boolean verboseIncludesTimestamp)
{
this.verboseIncludesTimestamp = verboseIncludesTimestamp;
}
/** Method renamed as <code>getPort()</code> so use that method instead.
* @return current port value
*/
@Deprecated
public int getDisPort()
{
return getPort();
}
/** Get network port used, multicast or unicast.
* @see <a href="https://en.wikipedia.org/wiki/Port_(computer_networking)">https://en.wikipedia.org/wiki/Port_(computer_networking)</a>
* @return current port value
*/
public int getPort()
{
return this.disPort;
}
/**
/** Set network port used, multicast or unicast.
* @see <a href="https://en.wikipedia.org/wiki/Port_(computer_networking)">https://en.wikipedia.org/wiki/Port_(computer_networking)</a>
* @param newPortValue the disPort value to set
*/
public void setPort(int newPortValue)
{
this.disPort = newPortValue;
}
/**
* Get simple descriptor (such as parent class name) for this network interface, used in trace statements
* @return simple descriptor name
*/
public String getDescriptor()
{
return descriptor;
}
/**
* Set new simple descriptor (such as parent class name) for this network interface, used in trace statements
* @param newDescriptor simple descriptor name for this interface
*/
public void setDescriptor(String newDescriptor)
{
this.descriptor = newDescriptor;
TRACE_PREFIX = "[" + (DisThreadedNetworkInterface.class.getSimpleName() + " " + descriptor).trim() + "] ";
}
/**
* Main method for testing.
* @see <a href="https://docs.oracle.com/javase/tutorial/getStarted/application/index.html">Java Tutorials: A Closer Look at the "Hello World!" Application</a>
* @param args [address, port, descriptor] command-line arguments are an array of optional String parameters that are passed from execution environment during invocation
*/
public static void main(String[] args)
{
DisThreadedNetworkInterface disThreadedNetworkInterface;
System.out.println("*** DisThreadedNetworkInterface main() self test started...");
String selfTestDescriptor = "main() self test";
if ((args != null) && args.length == 3)
selfTestDescriptor = args[2];
if ((args == null) || (args.length == 0))
disThreadedNetworkInterface = new DisThreadedNetworkInterface( /* default address, port */ selfTestDescriptor);
else disThreadedNetworkInterface = new DisThreadedNetworkInterface(args[0], Integer.parseInt(args[1]), selfTestDescriptor);
System.out.println("*** DisThreadedNetworkInterface main() self test initialized...");
try
{
EntityStatePdu espdu = new EntityStatePdu();
espdu.setMarking("self test");
disThreadedNetworkInterface.setVerbose(true);
DisThreadedNetworkInterface.PduListener pduListener;
pduListener = new DisThreadedNetworkInterface.PduListener()
{
/** Callback handler for listener */
@Override
public void incomingPdu(Pdu newPdu)
{
System.out.println( "received newPdu " + newPdu.getPduType().toString());
System.out.flush();
}
};
disThreadedNetworkInterface.addListener(pduListener);
System.out.println("*** DisThreadedNetworkInterface main() self test sending espdu...");
disThreadedNetworkInterface.send(espdu);
// briefly wait get response from threaded receiver
Thread.sleep(500L);
System.out.flush();
System.err.flush();
// all done, close() in finally block
}
catch(InterruptedException ex)
{
System.out.println("*** DisThreadedNetworkInterface main() self test InterruptedException " + ex.getMessage());
}
finally // clean up following regular completion and any interruptions
{
if (disThreadedNetworkInterface != null)
disThreadedNetworkInterface.close();
System.out.flush();
System.err.flush();
System.out.println("*** DisThreadedNetworkInterface main() self test complete.");
}
}
}