001    /*
002     * NodeNameRegistrar.java
003     *
004     * Developed for the "Rethinking CS101" project. See http://www.cs101.org, the
005     * CS101 homepage or email las@olin.edu.
006     *
007     * Created on February 19, 2003, 2:32 PM
008     */
009    
010    package nodenet.registrar;
011    
012    import nodenet.Node;
013    import nodenet.NodeConfigurationBean;
014    
015    import java.io.Serializable;
016    import java.io.ObjectStreamException;
017    import java.util.List;
018    import java.util.Vector;
019    import java.beans.VetoableChangeListener;
020    import java.beans.PropertyChangeEvent;
021    import java.beans.PropertyVetoException;
022    
023    /**
024     * Tracks all nodes and ensures that they have legal names. Besides providing
025     * legal names via the {@link #nameAndRegister} method it listens for changes to
026     * node names and vetos any attempt to create am illegal node name. To avoid
027     * multiple registry problems, this class is uses the sigleton pattern, and
028     * the one true <code>NodeNameRegistrar</code> can be obtained via the
029     * static method {@link #getInstance()}.
030     *
031     * <p>This implementation only checks for uniquenes of names, and
032     * accomodates up to Integer.MAX_VALUE nodes, wich is
033     * limited by the Vector class used to store node references. There is also
034     * a limit of Integer.MAX_VALUE name requests during the lifetime of this
035     * registrar object, as unique names are generated in the format "Node X"
036     * where X is replaced by an integer. <strong>If a node name is changed to
037     * "Node Y" where Y is an integer.
038     *
039     * @author  Patrick G. Heck, gus.heck@olin.edu
040     * @version $Id: NodeNameRegistry.java,v 1.5 2004/01/14 21:04:16 gus Exp $
041     */
042    public final class NodeNameRegistry implements NodeNameRegistrar {
043        
044        private static final NodeNameRegistry SINGLETON = new NodeNameRegistry();
045        
046        private NodeNameRegistrar policy;
047        
048        /** Prevent independant instantiation */
049        private NodeNameRegistry() {
050        }
051        
052        /**
053         * Factory method to get the one true <code>NodeNameRegistrar</code>.
054         *
055         * @return the registrar object.
056         */
057        public static NodeNameRegistry getInstance() {
058            return SINGLETON;
059        }
060        
061        /**
062         * Set the registrartion policy one time only. This method may not be
063         * invoked more than once. Subsequent invocations will throw an
064         * <code>IllegalStateException</code>. This is necessary to prevent
065         * the registered nodes from being lost. In the future a changePolicy
066         * method may be provided for "graceful" transition between policies.
067         * Note that "graceful" may include deletion of existing nodes.
068         *
069         * @param policy  An naming policy implemented as a NodeNameRegistrar.
070         */
071        public void setPolicy(NodeNameRegistrar policy) {
072            if (!isPolicySet()) {
073                this.policy = policy;
074            } else {
075                throw new IllegalStateException("Policy already set!");
076            }
077        }
078        
079        /**
080         * Check to see if the policy has been set for this registry.
081         *
082         * @return <code>true</code> if the policy has been set, <code>false</code>
083         *         otherwise
084         */
085        
086        public boolean isPolicySet() {
087            return (policy == null) ? false : true;
088        }
089        
090        // This allows us to deserialize objects containing a reference to the
091        // NodeNameRegistry and never cause duplicate registries. This technique
092        // means that when nodes are deserialized they need to re-register
093        // with the existing registry.
094        private Object readResolve() throws ObjectStreamException {
095            return SINGLETON;
096        }
097        
098        public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
099            if (!isPolicySet()) {
100                throw new IllegalStateException("Policy not set! (method:vetoableChange)");
101            }
102            
103            if (pce.getPropertyName().equals(NodeConfigurationBean.PROP_NODE_NAME)) {
104                this.policy.vetoableChange(pce);
105            }
106        }
107        
108        /* (Copy of original doc. Generated version is inherited)
109         *
110         * Provides a name that is currently legal. The legality of the name is
111         * not guaranteed to remain legal for all time. This is particularly true
112         * if the policy implemented by the <code>Registrar</code> requires names
113         * to be unique. The implementation of this method should make a reasonable
114         * attempt not to give out names that will become illegal quickly, either
115         * due to temporal relevance of the name, or due to copetition between
116         * names given out to the clients. For example, this method should not
117         * give out the same node name on consecutive invocations if uniqueness is
118         * a qualification for legality.
119         *
120         * @returns A name that is currently legal, and should remain legal for a
121         *          reasonable period of time, if possible.
122         */
123        public String getLegalName() {
124            if (!isPolicySet()) {
125                throw new IllegalStateException("Policy not set!(method:getLegalName)");
126            }
127            return this.policy.getLegalName();
128        }
129        
130        /* (Copy of original doc. Generated version is inherited)
131         *
132         * Provides access to nodes via their registred name. This method is
133         * optional, but if it is not implemented it should throw an
134         * <code>UnsupportedOperationException</code> rather than returning
135         * a bogus node or null.
136         *
137         * @param aName   The name of the node to look for.
138         * @return        A reference to the node if found, null otherwise.
139         */
140        public Node getNode(String aName) throws UnsupportedOperationException {
141            if (!isPolicySet()) {
142                throw new IllegalStateException("Policy not set!(method:getNode(String))");
143            }
144            return this.policy.getNode(aName);
145        }
146        
147        /* (Copy of original doc. Generated version is inherited)
148         *
149         * Provides a list of currently registered node names. This method is
150         * optional, but if it is not implemented it should throw an
151         * <code>UnsupportedOperationException</code> rather than returning
152         * a bogus list or null.
153         *
154         * @returns An array of node names currently in use.
155         */
156        public String[] getRegisteredNames() throws UnsupportedOperationException {
157            if (!isPolicySet()) {
158                throw new IllegalStateException("Policy not set!(method:getRegisteredNames)");
159            }
160            return this.policy.getRegisteredNames();
161        }
162        
163        /* (Copy of original doc. Generated version is inherited)
164         *
165         * Tests a name to see if it is currently legal. The legality of the name is
166         * not guaranteed to remain invariant for all time. This is particularly
167         * true if the policy implemented by the <code>Registrar</code> requires
168         * names to be unique.
169         *
170         * @param   aName   The node name to be tested.
171         * @returns <code>true</code> if the name is legal, <code>false</code>
172         *          otherwise.
173         */
174        public boolean isLegalName(String aName) {
175            if (!isPolicySet()) {
176                throw new IllegalStateException("Policy not set!(method:isLegalName)");
177            }
178            return this.policy.isLegalName(aName);
179        }
180        
181        /* (Copy of original doc. Generated version is inherited)
182         *
183         * Tests to see if a name belongs to a registered node. For obvious reasons
184         * the results of this test are only valid while the client has a lock
185         * on the registry.
186         *
187         * @param The node name to test
188         * @return <code>true</code> if the name is registered <code>false</code>
189         *         otherwise.
190         */
191        public boolean isRegistered(String aName) {
192            if (!isPolicySet()) {
193                throw new IllegalStateException("Policy not set!(method:isRegistered)");
194            }
195            return this.policy.isRegistered(aName);
196        }
197        
198        /* (Copy of original doc. Generated version is inherited)
199         *
200         * Tests to see if a node has been registered.
201         *
202         * @param The node to test
203         * @return <code>true</code> if the node is registered <code>false</code>
204         *         otherwise.
205         */
206        public boolean isRegistered(Node aNode) {
207            if (!isPolicySet()) {
208                throw new IllegalStateException("Policy not set!(method:isRegistered)");
209            }
210            return this.policy.isRegistered(aNode);
211        }
212        
213        /* (Copy of original doc. Generated version is inherited)
214         *
215         * Gives the node a legal name and registers the node in one step. This
216         * method guarantees that the node will be registered, and no exceptions
217         * should be thrown. The node to be registered must expose a method
218         * with the signature setName(String).
219         */
220        public void nameAndRegister(Node aNode) {
221            if (!isPolicySet()) {
222                throw new IllegalStateException("Policy not set!(method:nameAndRegister)");
223            }
224            this.policy.nameAndRegister(aNode);
225        }
226        
227        /* (Copy of original doc. Generated version is inherited)
228         *
229         * Attempt to register a node. This method proposes to register a node
230         * with an existing name. The node will be rejected if and only if it
231         * contains a name that would currently generate a false response
232         * from {@link #isLegalName(String)}. If the name contained in the node
233         * is rejected, The node is not registered or tracked in any way and an
234         * <code>IllegalArgumentException</code> will be thrown.
235         *
236         * @param aNode The node that wishes to register itself.
237         */
238        public void register(Node aNode) throws IllegalArgumentException {
239            if (!isPolicySet()) {
240                throw new IllegalStateException("Policy not set!(method:register)");
241            }
242            this.policy.register(aNode);
243        }
244        
245        /* (Copy of original doc. Generated version is inherited)
246         *
247         * Ensures that the specified node is nolonger registered. If the node
248         * was never registred, it simply returns with no side effects.
249         *
250         * @param aNode the node to unregister
251         */
252        public void unregister(Node aNode) {
253            if (!isPolicySet()) {
254                throw new IllegalStateException("Policy not set!(method:unregister)");
255            }
256            this.policy.unregister(aNode);
257        }
258        
259    }
260    
261    /*
262     * $Log: NodeNameRegistry.java,v $
263     * Revision 1.5  2004/01/14 21:04:16  gus
264     * More javadoc fixes
265     *
266     * Revision 1.4  2004/01/14 20:23:21  gus
267     * Javadoc and comment cleanup
268     *
269     * Revision 1.3  2003/02/25 21:42:31  gus
270     * Improve error messages to specify which method generated them
271     *
272     * Revision 1.2  2003/02/24 15:54:01  gus
273     * Add support for configuring the destination of generated packets
274     *
275     * Revision 1.1  2003/02/21 17:47:06  gus
276     * A set of classes to support name checking  and generation schemes for nodes.
277     *
278     */