KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > remoting > detection > jndi > JNDIDetector


1 /*
2  * JBoss, the OpenSource J2EE webOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.remoting.detection.jndi;
8
9 import java.net.InetAddress JavaDoc;
10 import java.util.Properties JavaDoc;
11 import javax.naming.Binding JavaDoc;
12 import javax.naming.Context JavaDoc;
13 import javax.naming.InitialContext JavaDoc;
14 import javax.naming.NameAlreadyBoundException JavaDoc;
15 import javax.naming.NamingEnumeration JavaDoc;
16 import javax.naming.NamingException JavaDoc;
17 import org.jboss.logging.Logger;
18 import org.jboss.remoting.InvokerLocator;
19 import org.jboss.remoting.InvokerRegistry;
20 import org.jboss.remoting.detection.AbstractDetector;
21 import org.jboss.remoting.detection.Detection;
22 import org.jboss.remoting.ident.Identity;
23 import org.jboss.remoting.transport.PortUtil;
24 import org.jnp.interfaces.NamingContextFactory;
25 import org.jnp.server.Main;
26
27 /**
28  * This is a remoting detector for the remoting package which uses a JNDI server to
29  * maintain the registeries for remote invoker servers (stored as Detection messages).
30  * This detector is intended to be used in conjuntion with an external JNDI server that
31  * is already running. This is done by passing all the information needed to connect
32  * to the remote JNDI server via the setter methods. This can also be done within
33  * the jboss-service.xml. An example of the entry is as follows:<p>
34  * &lt;mbean code="org.jboss.remoting.detection.jndi.JNDIDetector" name="jboss.remoting:service=Detector,transport=jndi"&gt;<br>
35  * &lt;attribute name="Port"&gt;5555&lt;/attribute&gt;<br>
36  * &lt;attribute name="Host"&gt;foo.bar.com&lt;/attribute&gt;<br>
37  * &lt;attribute name="ContextFactory"&gt;org.jnp.interfaces.NamingContextFactory&lt;/attribute&gt;<br>
38  * &lt;attribute name="URLPackage"&gt;org.jboss.naming:org.jnp.interfaces&lt;/attribute&gt;<br>
39  * &lt;/mbean&gt;<br><p>
40  * Note: The above xml is for the JBoss JNP JNDI server, and has not be tested (just an example).<p>
41  * Be aware that just because this detector is stopped (and the entry removed from the JNDI server)
42  * remote JNDIDetectors may not recognize that the invoker servers are not available. This is because
43  * once remote invoker servers (connectors) are detected, they will be pinged directly to determine
44  * if they are no longer available. However, no new JNDIDetectors will detect your server once stopped.
45  * Also, please note that currently the detection registries are bound at the root context and
46  * not a sub context (which is on the todo list, but you know how that goes).<p>
47  * Important to also note that if any of the above attributes are set once the detector has
48  * started, they will not be used in connecting to the JNDI server until the detector is stopped
49  * and re-started (they do not change the JNDI server connection dynamically).<p>
50  *
51  * @author <a HREF="mailto:telrod@e2technologies.net">Tom Elrod</a>
52  */

53 public class JNDIDetector extends AbstractDetector implements JNDIDetectorMBean
54 {
55    private int port;
56    private String JavaDoc host;
57    private String JavaDoc contextFactory = NamingContextFactory.class.getName();;
58    private String JavaDoc urlPackage = "org.jboss.naming:org.jnp.interfaces";;
59
60    private Identity id;
61    private Context JavaDoc context;
62    private int cleanDetectionCount = 0;
63
64    public static final String JavaDoc DETECTION_SUBCONTEXT_NAME = "detection";
65
66    /**
67     * Indicates the number of time will detect before doing check to see if server still alive.
68     */

69    private int detectionNumber = 5;
70
71    protected final Logger log = Logger.getLogger(getClass());
72
73    /**
74     * Gets the port used to connect to the JNDI Server.
75     *
76     * @return
77     */

78    public int getPort()
79    {
80       return port;
81    }
82
83    /**
84     * Sets the port to use when connecting to JNDI server
85     *
86     * @param port
87     */

88    public void setPort(int port)
89    {
90       this.port = port;
91    }
92
93    /**
94     * Gets the host to use when connecting to JNDI server
95     *
96     * @return
97     */

98    public String JavaDoc getHost()
99    {
100       return host;
101    }
102
103    /**
104     * Sets the host to use when connecting to JNDI server
105     *
106     * @param host
107     */

108    public void setHost(String JavaDoc host)
109    {
110       this.host = host;
111    }
112
113    /**
114     * The context factory string used when connecting to the JNDI server
115     *
116     * @return
117     */

118    public String JavaDoc getContextFactory()
119    {
120       return contextFactory;
121    }
122
123    /**
124     * The context factory string to use when connecting to the JNDI server.
125     * Should be a qualified class name for JNDI client.
126     *
127     * @param contextFactory
128     */

129    public void setContextFactory(String JavaDoc contextFactory)
130    {
131       this.contextFactory = contextFactory;
132    }
133
134    /**
135     * The url package string used when connecting to JNDI server
136     *
137     * @return
138     */

139    public String JavaDoc getURLPackage()
140    {
141       return urlPackage;
142    }
143
144    /**
145     * The url package string to use when connecting to the JNDI server.
146     *
147     * @param urlPackage
148     */

149    public void setURLPackage(String JavaDoc urlPackage)
150    {
151       this.urlPackage = urlPackage;
152    }
153
154    /**
155     * Will establish the connection to the JNDI server and start detection of other servers.
156     *
157     * @throws Exception
158     */

159    public void start() throws Exception JavaDoc
160    {
161       createContext();
162       id = Identity.get(mbeanserver);
163       super.start();
164    }
165
166    /**
167     * Creates connection to JNDI server (which should have already happened in start()
168     * method) and will begin checking for remote servers as well as registering itself
169     * so will be visible by remote detectors.
170     */

171    protected void heartbeat()
172    {
173       try
174       {
175          //Need to establish connection to server
176
if(context == null)
177          {
178             createContext();
179          }
180          checkRemoteDetectionMsg();
181       }
182       catch(NamingException JavaDoc nex)
183       {
184          log.error("Can not connect to JNDI server to register local connectors.", nex);
185       }
186    }
187
188    /**
189     * Gets the number of detection iterations before manually pinging remote
190     * server to make sure still alive.
191     *
192     * @return
193     */

194    public int getCleanDetectionNumber()
195    {
196       return detectionNumber;
197    }
198
199    /**
200     * Sets the number of detection iterations before manually pinging remote
201     * server to make sure still alive. This is needed since remote server
202     * could crash and yet still have an entry in the JNDI server, thus
203     * making it appear that it is still there.
204     *
205     * @param cleanDetectionNumber
206     */

207    public void setCleanDetectionNumber(int cleanDetectionNumber)
208    {
209       detectionNumber = cleanDetectionNumber;
210    }
211
212    private void checkRemoteDetectionMsg()
213    {
214       try
215       {
216          boolean localFound = false;
217          cleanDetectionCount++;
218          boolean cleanDetect = cleanDetectionCount > detectionNumber;
219          String JavaDoc bindName = "";
220          NamingEnumeration JavaDoc enumeration = context.listBindings(bindName);
221          while(enumeration.hasMore())
222          {
223             Binding JavaDoc binding = (Binding JavaDoc) enumeration.next();
224             Detection regMsg = (Detection) binding.getObject();
225             // No need to detect myself here
226
if(isRemoteDetection(regMsg))
227             {
228                log.debug("Detected id: " + regMsg.getIdentity().getInstanceId() + ", message: " + regMsg);
229
230                if(cleanDetect)
231                {
232                   if(log.isTraceEnabled())
233                   {
234                      log.trace("Doing clean detection.");
235                   }
236                   // Need to actually detect if servers registered in JNDI server
237
// are actually there (since could die before unregistering)
238
ClassLoader JavaDoc cl = JNDIDetector.this.getClass().getClassLoader();
239                   if(!checkInvokerServer(regMsg, cl))
240                   {
241                      unregisterDetection(regMsg.getIdentity().getInstanceId());
242                   }
243                }
244                else
245                {
246                   // Let parent handle detection
247
detect(regMsg);
248                }
249             }
250             else
251             {
252                //verify local detection message is correct
253
if(!verifyLocalDetectionMsg(regMsg))
254                {
255                   addLocalDetectionMsg();
256                }
257                localFound = true;
258             }
259          }
260          if(cleanDetect)
261          {
262             // did clean detect, now need to reset.
263
cleanDetectionCount = 0;
264          }
265          if(!localFound)
266          {
267             // never found local detection message in list, so add it
268
addLocalDetectionMsg();
269          }
270       }
271       catch(NamingException JavaDoc e)
272       {
273          log.error("Exception getting detection messages from JNDI server.", e);
274       }
275    }
276
277    private boolean verifyLocalDetectionMsg(Detection regMsg) throws NamingException JavaDoc
278    {
279       boolean verified = false;
280
281       InvokerLocator[] locators = InvokerRegistry.getRegisteredServerLocators();
282       Detection msg = createDetection();
283       String JavaDoc sId = id.getInstanceId();
284       InvokerLocator[] invokers = regMsg.getLocators();
285
286       // first do sanity check to make sure even local detection msg (just in case)
287
if(sId.equals(regMsg.getIdentity().getInstanceId()))
288       {
289
290          // now see if invoker list changed
291
boolean changed = false;
292          if(locators.length != invokers.length)
293          {
294             changed = true;
295          }
296          else
297          {
298             // now need to make sure all the invokers are same now as in old detection msg
299
// not the most efficient (or elegant) way to do this, but list is short
300
boolean found = false; // flag for if current invoker in list found in old list
301
for(int i = 0; i < locators.length; i++)
302             {
303                found = false;
304                for(int x = 0; x < invokers.length; x++)
305                {
306                   if(locators[i].equals(invokers[x]))
307                   {
308                      found = true;
309                      break;
310                   }
311                }
312                if(!found)
313                {
314                   break;
315                }
316             }
317             if(!found)
318             {
319                changed = true;
320             }
321          }
322          if(changed)
323          {
324             registerDetectionMsg(sId, msg);
325          }
326          // are sure that local detection is correct in JNDI server now
327
verified = true;
328       }
329       return verified;
330    }
331
332    private void addLocalDetectionMsg() throws NamingException JavaDoc
333    {
334       Detection msg = createDetection();
335       String JavaDoc sId = id.getInstanceId();
336       registerDetectionMsg(sId, msg);
337    }
338
339    private void registerDetectionMsg(String JavaDoc sId, Detection msg) throws NamingException JavaDoc
340    {
341       try
342       {
343          context.bind(sId, msg);
344          log.info("Added " + sId + " to registry.");
345       }
346       catch(NameAlreadyBoundException JavaDoc nabex)
347       {
348          if(log.isTraceEnabled())
349          {
350             log.trace(sId + " already bound to server.");
351          }
352       }
353    }
354
355    /**
356     * Convience method to see if given proper configuration to connect to an
357     * existing JNDI server. If not, will create one via JBoss JNP. Should
358     * really only be needed for testing.
359     */

360    private void verifyJNDIServer()
361    {
362       if(host == null || host.length() == 0)
363       {
364          try
365          {
366             log.info("JNDI Server configuration information not present so will create a local server.");
367             port = PortUtil.findFreePort();
368             host = InetAddress.getLocalHost().getHostName();
369
370             log.info("Remoting JNDI detector starting JNDI server instance since none where specified via configuration.");
371             log.info("Remoting JNDI server started on host + " + host + " and port " + port);
372
373             //If no server information provided, then start one of our own by default
374
Main server = new Main();
375             server.setPort(port);
376             server.setBindAddress(host);
377             server.start();
378
379             contextFactory = NamingContextFactory.class.getName();
380             urlPackage = "org.jboss.naming:org.jnp.interfaces";
381          }
382          catch(Exception JavaDoc e)
383          {
384             log.error("Error starting up JNDI server since none was specified via configuration.", e);
385          }
386       }
387    }
388
389    /**
390     * Will try to establish the initial context to the JNDI server based
391     * on the configuration properties set.
392     *
393     * @throws NamingException
394     */

395    private void createContext() throws NamingException JavaDoc
396    {
397       verifyJNDIServer();
398
399       Properties JavaDoc env = new Properties JavaDoc();
400
401       env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
402       env.put(Context.PROVIDER_URL, host + ":" + port);
403       env.put(Context.URL_PKG_PREFIXES, urlPackage);
404
405       InitialContext JavaDoc initialContext = new InitialContext JavaDoc(env);
406       try
407       {
408          context = (Context JavaDoc) initialContext.lookup(DETECTION_SUBCONTEXT_NAME);
409       }
410       catch(NamingException JavaDoc e)
411       {
412          try
413          {
414             context = initialContext.createSubcontext(DETECTION_SUBCONTEXT_NAME);
415          }
416          catch(NameAlreadyBoundException JavaDoc e1)
417          {
418             log.debug("The sub context " + DETECTION_SUBCONTEXT_NAME + " was created before we could.");
419             context = (Context JavaDoc) initialContext.lookup(DETECTION_SUBCONTEXT_NAME);
420          }
421       }
422    }
423
424    public void stop() throws Exception JavaDoc
425    {
426       try
427       {
428          super.stop();
429       }
430       finally // Need to cleanup JNDI, even if super's stop throws exception
431
{
432          String JavaDoc sId = id.getInstanceId();
433          try
434          {
435             unregisterDetection(sId);
436          }
437          catch(NamingException JavaDoc e)
438          {
439             log.warn("Could not unregister " + sId + " before shutdown. " +
440                      "Root cause is " + e.getMessage());
441          }
442       }
443    }
444
445    private void unregisterDetection(String JavaDoc sId) throws NamingException JavaDoc
446    {
447       if(log.isTraceEnabled())
448       {
449          log.trace("unregistering detector " + sId);
450       }
451       context.unbind(sId);
452    }
453 }
454
Popular Tags