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        }