View Javadoc

1   /*
2    * Copyright (c) 1998-2002 The Jgroup Team.
3    *
4    * This program is free software; you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser General Public License version 2 as
6    * published by the Free Software Foundation.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * GNU Lesser General Public License for more details.
12   *
13   * You should have received a copy of the GNU Lesser General Public License
14   * along with this program; if not, write to the Free Software
15   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16   *
17   */
18  
19  package jgroup.relacs.simulator;
20  
21  import java.net.InetAddress;
22  import java.net.UnknownHostException;
23  import java.rmi.ConnectException;
24  import java.rmi.NotBoundException;
25  import java.rmi.Remote;
26  import java.rmi.RemoteException;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Random;
33  
34  import jgroup.core.registry.BootstrapRegistry;
35  import jgroup.util.log.Eventlogger;
36  import net.jini.export.Exporter;
37  import net.jini.jeri.BasicILFactory;
38  import net.jini.jeri.BasicJeriExporter;
39  import net.jini.jeri.tcp.TcpServerEndpoint;
40  
41  import org.apache.log4j.Logger;
42  
43  /**
44   *  Implementation of the <code>SocketStatus</code> interface,
45   *  allowing an external entity (the simulator) to set the status
46   *  of particular links from the JVM hosting this instance of the
47   *  SocketStatusImpl.  Note that objects within the local JVM may
48   *  obtain a reference to a "singlton" instance of this class, and
49   *  thus query the link connectivity map through the
50   *  <code>isReliable()</code> method.
51   *
52   *  @author Alberto Montresor
53   *  @author Hein Meling
54   *  @since Jgroup 0.5
55   */
56  public class SocketStatusImpl
57    implements SocketStatus
58  {
59  
60    ////////////////////////////////////////////////////////////////////////////////////////////
61    // Logger
62    ////////////////////////////////////////////////////////////////////////////////////////////
63  
64    /** Obtain logger for this class */
65    private static final Logger log = Logger.getLogger(SocketStatusImpl.class);
66  
67  
68    ////////////////////////////////////////////////////////////////////////////////////////////
69    // Static fields
70    ////////////////////////////////////////////////////////////////////////////////////////////
71  
72    /** Random number generator */
73    private static Random random = new Random();
74  
75    /** The local socket status object */
76    private static SocketStatusImpl status = null;
77  
78  
79    ////////////////////////////////////////////////////////////////////////////////////////////
80    // Fields
81    ////////////////////////////////////////////////////////////////////////////////////////////
82  
83    /** Connectivity map */
84    private transient Map<InetAddress,Integer> links = new HashMap<InetAddress, Integer>(20);
85  
86    /** Connectivity map */
87    private transient Map<InetAddress,Integer> committedLinks = null;
88  
89    /** SocketStatus objects listening for changes in the reachability patteren */
90    private transient List<SocketStatus> listeners = new ArrayList<SocketStatus>(3);
91  
92  
93    ////////////////////////////////////////////////////////////////////////////////////////////
94    // Constructor
95    ////////////////////////////////////////////////////////////////////////////////////////////
96  
97    /**
98     * Constructs an instance of the <code>SocketStatusImpl</code> class.
99     * It is a private constructor to avoid construction from the outside.
100    * Use the <code>instance()</code> method to obtain the local single
101    * instance of the socket status class.  This construtor is used when
102    * we know that there is no other <code>SocketStatus</code> instances
103    * on the local machine, and we can safely export and bind this socket
104    * status instance.
105    */
106   private SocketStatusImpl()
107     throws RemoteException
108   {
109     Exporter exporter =
110       new BasicJeriExporter(TcpServerEndpoint.getInstance(0), new BasicILFactory());
111     Remote socketStatusProxy = exporter.export(this);
112     try {
113       BootstrapRegistry.bind(SOCKET_STATUS, socketStatusProxy);
114       if (log.isDebugEnabled())
115         log.debug("Primary SocketStatus exported and bound...");
116     } catch (Exception e) {
117       log.warn("Failed to initialize the SocketStatusImpl class", e);
118       exporter.unexport(true);
119     }
120   }
121 
122   /**
123    * This constructor is used when there exists another instance of the
124    * <code>SocketStatus</code>, possibly within another JVM on this machine.
125    * This instance of <code>SocketStatus</code> will export itself and
126    * register with the existing <code>SocketStatus</code> in order to be
127    * notified of changes to the reachability.
128    * 
129    * @param st
130    *   Reference to the existing <code>SocketStatus</code> remote object.
131    * @throws RemoteException
132    */
133   private SocketStatusImpl(SocketStatus st)
134     throws RemoteException
135   {
136     Exporter exporter =
137       new BasicJeriExporter(TcpServerEndpoint.getInstance(0), new BasicILFactory());
138     Remote ssProxy = exporter.export(this);
139     st.register((SocketStatus) ssProxy);
140     committedLinks = st.getStatus();
141     if (log.isDebugEnabled())
142       log.debug("Secondary SocketStatus object registered with the primary SocketStatus...");
143   }
144 
145   /**
146    *  Obtain the local instance of the socket status object.
147    */
148   public static SocketStatusImpl instance()
149   {
150     if (status != null) {
151       return status;
152     }
153     try {
154       try {
155         SocketStatus st = (SocketStatus) BootstrapRegistry.lookup(SOCKET_STATUS);
156         status = new SocketStatusImpl(st);
157       } catch (NotBoundException e) {
158         // Since we are the first SocketStatus object available on this host.
159         status = new SocketStatusImpl();
160       }
161     } catch (RemoteException e) {
162       log.info("Partition simulator not available", e);
163       return null;
164     }
165     log.info("Partition simulator activated");
166     return status;
167   }
168 
169   ////////////////////////////////////////////////////////////////////////////////////////////
170   // Public access methods for the SocketStatusImpl class
171   ////////////////////////////////////////////////////////////////////////////////////////////
172 
173   /**
174    *  Check if the link to the given internet address is considered
175    *  reliable.
176    */
177   public boolean isReliable(InetAddress address)
178   {
179     if (committedLinks == null) {
180       // no link probabilities have yet been committed
181       return true;
182     }
183     Integer prob = (Integer) committedLinks.get(address);
184     int val = (int) (random.nextFloat() * BASE);
185     return (prob == null || val < prob.intValue());
186   }
187 
188 
189   /**
190    *  Check if the link to the given internet host is considered
191    *  reliable.
192    */
193   public boolean isReliable(String hostname)
194     throws UnknownHostException
195   {
196     InetAddress inetAdr = InetAddress.getByName(hostname);
197     return isReliable(inetAdr);
198   }
199 
200 
201   ////////////////////////////////////////////////////////////////////////////////////////////
202   // Remote methods from the SocketStatus interface
203   ////////////////////////////////////////////////////////////////////////////////////////////
204 
205   /* (non-Javadoc)
206    * @see jgroup.relacs.simulator.SocketStatus#register(jgroup.relacs.simulator.SocketStatus)
207    */
208   public void register(SocketStatus sStatus)
209     throws RemoteException
210   {
211     if (!listeners.contains(sStatus))
212       listeners.add(sStatus);
213   }
214 
215   /* (non-Javadoc)
216    * @see jgroup.relacs.simulator.SocketStatus#commit()
217    */
218   public void commit()
219     throws RemoteException
220   {
221     if (log.isDebugEnabled())
222       log.debug("commit received");
223     doCommit();
224   }
225 
226   private void doCommit()
227     throws RemoteException
228   {
229     committedLinks = new HashMap<InetAddress,Integer>(links);
230     for (Iterator<SocketStatus> iter = listeners.iterator(); iter.hasNext();) {
231       SocketStatus st = iter.next();
232       try {
233         st.notifyStatus(committedLinks);
234       } catch (ConnectException e) {
235         log.warn("Remote SocketStatus object no longer present", e);
236         /*
237          * Since the remote (machine local) socket status object appears
238          * to have crashed, we remove it from the set of listeners.
239          */
240         iter.remove();
241       }
242     }
243   }
244 
245   /* (non-Javadoc)
246    * @see jgroup.relacs.simulator.SocketStatus#notifyStatus(java.util.HashMap)
247    */
248   public void notifyStatus(Map<InetAddress,Integer> committedLinks)
249     throws RemoteException
250   {
251     if (log.isDebugEnabled())
252       log.debug("notifyStatus received: " + committedLinks);
253     this.committedLinks = committedLinks;
254     // The following may occur multiple times depending on the number of
255     // listeners registered for link status events.
256     if (Eventlogger.ENABLED)
257       Eventlogger.logEventFlush("LinksCommitted");
258   }
259 
260   /* (non-Javadoc)
261    * @see jgroup.relacs.simulator.SocketStatus#getStatus()
262    */
263   public Map<InetAddress,Integer> getStatus()
264     throws RemoteException
265   {
266     if (log.isDebugEnabled())
267       log.debug("getStatus received: " + committedLinks);
268     return committedLinks;
269   }
270 
271   /* (non-Javadoc)
272    * @see jgroup.relacs.simulator.SocketStatus#setStatus(java.net.InetAddress, int)
273    */
274   public void setStatus(InetAddress inet, int prob)
275     throws RemoteException
276   {
277     if (log.isDebugEnabled())
278       log.debug("changed the reachability: "+inet+" "+prob);
279     if (prob >= BASE)
280       links.remove(inet);
281     else
282       links.put(inet, prob);
283   }
284 
285 
286   ////////////////////////////////////////////////////////////////////////////////////////////
287   // Object methods
288   ////////////////////////////////////////////////////////////////////////////////////////////
289 
290   public String toString()
291   {
292     StringBuilder buf = new StringBuilder("[SocketStatusImpl: ");
293     buf.append("links.size=" + links.size());
294     if (committedLinks != null) {
295       buf.append(", committedLinks.size=" + committedLinks.size());
296     }
297     buf.append("]");
298     return buf.toString();
299   }
300 
301 } // END SocketStatusImpl