001 package org.hd.d.pg2k.svrCore;
002
003 import java.io.InvalidObjectException;
004 import java.io.Serializable;
005
006 /**Immutable stratum and other details of this instance/server and upstream. */
007 public final class Stratum implements Serializable
008 {
009 /**Maximum length of upstream short (unique) name; strictly positive. */
010 public static final int MAX_UPSTREAM_NAME = 16; // 6 is enough for all current purposes as of 2010...
011
012 /**Maximum root delay; strictly positive (and no higher than Short.MAX_VALUE).
013 * Forced to this for an unknown stratum.
014 */
015 public static final short MAX_ROOT_DELAY = Short.MAX_VALUE;
016
017 /**Maximum permissible stratum; strictly positive (and much less than Byte.MAX_VALUE).
018 * This determines the maximum distance that any (leaf) node can be from the root;
019 * 1 would indicate that all nodes must connect to the root/master directly
020 * and higher values (2 or more) allow more decentralised structures.
021 */
022 public static final byte MAX_STRATUM = 2; // Minimum value that allows some load to move from the root; may increase later.
023
024 /**Our stratum, in range [0,MAX_STRATUM] with 0 indicating master/root, or -1 for unknown. */
025 private final byte stratum;
026
027 /**Root delay in ms capped to Short.MAX_VALUE; non-negative.
028 * Is forced to the MAX_ROOT_DELAY for an unknown stratum.
029 */
030 private final short rootDelay;
031
032 /**Upstream server short, unique, printable-ASCII name, "" for root/master/unknown else mirror tag like "cc-xxx"; never null. */
033 private final String upstreamName;
034
035 /**True if upstream is in a power-conserving state or unknown (as fail-safe). */
036 private final boolean upstreamConserving;
037
038 /**Construct an instance with int-valued numeric arguments for convenience.
039 * In passing this silently caps the rootDelay to the maximum permitted value.
040 */
041 public Stratum(final int stratum,
042 final int rootDelay,
043 final String upstreamName,
044 final boolean upstreamConserving)
045 {
046 this((byte)stratum, (short)Math.min(rootDelay, MAX_ROOT_DELAY), upstreamName, upstreamConserving);
047 // Just make sure that bad values didn't get missed due to truncation...
048 if((stratum < -1) || (stratum > MAX_STRATUM)) { throw new IllegalArgumentException("bad stratum"); }
049 if(rootDelay < 0) { throw new IllegalArgumentException("bad root delay"); }
050 }
051
052 /**Construct an instance. */
053 public Stratum(final byte stratum,
054 final short rootDelay,
055 final String upstreamName,
056 final boolean upstreamConserving)
057 {
058 this.stratum = stratum;
059 this.upstreamName = MemoryTools.intern(upstreamName); // Should only be small number of distinct instances...
060 this.rootDelay = (stratum < 0) ? MAX_ROOT_DELAY : rootDelay; // MAX delay for unknown stratum.
061 this.upstreamConserving = (stratum < 0) ? true : upstreamConserving; // Conserve by default for unknown stratum.
062
063 // Verify object state.
064 try { validateObject(); }
065 catch(final InvalidObjectException e)
066 { throw new IllegalArgumentException(e.getMessage(), e); }
067 }
068
069 /**Unique serialisation ID. */
070 private static final long serialVersionUID = 654463415117548565L;
071
072 /**Deserialise: use constructor for validation, defensive copying, etc. */
073 protected Object readResolve()
074 // throws ObjectStreamException
075 {
076 // Construct new instance of object in normal defensive way.
077 return(new Stratum(stratum, rootDelay, upstreamName, upstreamConserving));
078 }
079
080 /**Validate fields/state.
081 * Called in the constructor and possibly after de-serialising.
082 * <p>
083 * Barf if something bad is found.
084 * (Maybe allow some extra info in debug version.)
085 */
086 public void validateObject()
087 throws InvalidObjectException
088 {
089 // Check that all components are sane and safe.
090 if((stratum < -1) || (stratum > MAX_STRATUM)) { throw new InvalidObjectException("bad stratum"); }
091 if((rootDelay < 0) || (rootDelay > MAX_ROOT_DELAY)) { throw new InvalidObjectException("bad root delay"); }
092 if(null == upstreamName) { throw new InvalidObjectException("null upstream server name"); }
093 if(getUpstreamName().length() > MAX_UPSTREAM_NAME) { throw new InvalidObjectException("over-long upstream server name"); }
094 // Check extra constraints on UNKNOWN value.
095 if(stratum < 0)
096 {
097 if(rootDelay != MAX_ROOT_DELAY) { throw new InvalidObjectException("bad stratum for UNKNOWN"); }
098 if(!upstreamConserving) { throw new InvalidObjectException("bad upstreamConserving for UNKNOWN"); }
099 }
100 }
101
102 /**Our stratum, in range [0,MAX_STRATUM] with 0 indicating master/root, -1 unknown. */
103 public byte getStratum() { return(stratum); }
104
105 /**True if stratum is unknown. */
106 public boolean isUnknownStratum() { return(stratum < 0); }
107
108 /**Root delay in ms capped to Short.MAX_VALUE; non-negative. */
109 public short getRootDelay() { return(rootDelay); }
110
111 /**Upstream server short name, "" for root/master/unknown else like "cc-xxx"; never null. */
112 public String getUpstreamName() { return(upstreamName); }
113
114 /**True if upstream is in a power-conserving state. */
115 public boolean isUpstreamConserving() { return(upstreamConserving); }
116
117 /**Value for use at the root (master) node; non-null.
118 * The root is at stratum zero with a delay of zero.
119 * <p>
120 * It has no upstream node so the upstream name is "" and the conserving flag is false.
121 */
122 public static final Stratum ROOT = new Stratum((byte) 0, (short) 0, "", true);
123
124 /**Value for use when upstream node is knot known; non-null.
125 * The root is at stratum zero with a delay of zero.
126 * <p>
127 * The upstream node is not known so the upstream name is "" and the conserving flag is false.
128 */
129 public static final Stratum UNKNOWN = new Stratum((byte) -1, (short) MAX_ROOT_DELAY, "", false);
130 }