KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > mx > remoting > MBeanServerClientInvokerProxy


1 /***************************************
2  * *
3  * JBoss: The OpenSource J2EE WebOS *
4  * *
5  * Distributable under LGPL license. *
6  * See terms of license at gnu.org. *
7  * *
8  ***************************************/

9 package org.jboss.mx.remoting;
10
11 import java.lang.reflect.InvocationHandler JavaDoc;
12 import java.lang.reflect.Method JavaDoc;
13 import java.lang.reflect.Proxy JavaDoc;
14 import java.lang.reflect.UndeclaredThrowableException JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.Map JavaDoc;
19 import java.util.Set JavaDoc;
20 import java.util.Timer JavaDoc;
21 import java.util.TimerTask JavaDoc;
22 import javax.management.MBeanException JavaDoc;
23 import javax.management.MBeanServer JavaDoc;
24 import javax.management.NotificationFilter JavaDoc;
25 import javax.management.NotificationListener JavaDoc;
26 import javax.management.ObjectName JavaDoc;
27 import javax.management.ReflectionException JavaDoc;
28 import org.jboss.logging.Logger;
29 import org.jboss.remoting.Client;
30 import org.jboss.remoting.ConnectionFailedException;
31 import org.jboss.remoting.InvokerLocator;
32 import org.jboss.remoting.Subsystem;
33 import org.jboss.remoting.invocation.NameBasedInvocation;
34
35 /**
36  * MBeanServerClientInvokerProxy is an MBeanServer dynamic proxy that will forward all
37  * MBeanServer requests to a remote MBeanServer via a RemoteClientInvoker.
38  *
39  * @author <a HREF="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
40  * @version $Revision: 40917 $
41  */

42 public class MBeanServerClientInvokerProxy implements InvocationHandler JavaDoc
43 {
44    private static final Logger log = Logger.getLogger(MBeanServerClientInvokerProxy.class.getName());
45
46    private final String JavaDoc serverId;
47    private final String JavaDoc localJmxId;
48    private final transient InvokerLocator locator;
49    private final transient Client client;
50    private final transient Map JavaDoc paramMap = new HashMap JavaDoc();
51    private NotificationPoller poller = new NotificationPoller();
52    private Timer JavaDoc pollTimer = new Timer JavaDoc(true);
53    private static transient Map JavaDoc proxies = new HashMap JavaDoc();
54    private transient Set JavaDoc listeners = new HashSet JavaDoc();
55
56    private MBeanServerClientInvokerProxy(ClassLoader JavaDoc cl, InvokerLocator invoker, String JavaDoc localJmxId, String JavaDoc id)
57          throws Exception JavaDoc
58    {
59       this.localJmxId = localJmxId;
60       this.serverId = id;
61       if(this.serverId == null)
62       {
63          throw new IllegalArgumentException JavaDoc("MBeanServer ID not found - make sure the NetworkRegistry MBean is running");
64       }
65       this.client = new Client(cl, invoker, Subsystem.JMX, null);
66       this.locator = new InvokerLocator(invoker.getLocatorURI());
67       this.proxies.put(id, this);
68       setupPollingTimer();
69    }
70
71    /**
72     * remove the proxy for a given JMX id
73     *
74     * @param id
75     * @return
76     */

77    public static synchronized MBeanServerClientInvokerProxy remove(String JavaDoc id)
78    {
79       return (MBeanServerClientInvokerProxy) proxies.remove(id);
80    }
81
82    /**
83     * get a proxy for a given JMX Id
84     *
85     * @param id
86     * @return
87     */

88    public static synchronized MBeanServerClientInvokerProxy get(String JavaDoc id)
89    {
90       return (MBeanServerClientInvokerProxy) proxies.get(id);
91    }
92
93    /**
94     * setup the polling based on the <tt>pollinterval</tt> locator attribute. <P>
95     * <p/>
96     * For example, to set the pollinterval to every 2.5 seconds, you would configure
97     * the client invoker locator to be:
98     * <p/>
99     * <CODE><PRE>
100     * soap://192.168.10.1/pollinterval=2500
101     * </PRE></CODE>
102     * <p/>
103     * The default interval if not specified is 1000, for every 1 second. You can
104     * disable polling by setting the interval to <tt>&lt;=0</tt>.
105     */

106    protected void setupPollingTimer()
107    {
108       Map JavaDoc map = this.locator.getParameters();
109       long delay = 1000; // default
110
if(map != null)
111       {
112          String JavaDoc ds = (String JavaDoc) map.get("pollinterval");
113          if(ds != null)
114          {
115             delay = Integer.parseInt(ds);
116          }
117       }
118       if(delay > 0)
119       {
120          this.pollTimer.scheduleAtFixedRate(poller, 2000, delay);
121       }
122    }
123
124    /**
125     * return the remote JMX id
126     *
127     * @return
128     */

129    public synchronized String JavaDoc getServerId()
130    {
131       return this.serverId;
132    }
133
134    /**
135     * return the invoker locator
136     *
137     * @return
138     */

139    public InvokerLocator getLocator()
140    {
141       return locator;
142    }
143
144    public static synchronized MBeanServer JavaDoc create(InvokerLocator locator, String JavaDoc localJmxId, String JavaDoc jmxId) throws Exception JavaDoc
145    {
146       // always use the same one previously registered if possible
147
MBeanServer JavaDoc mbeanserver = MBeanServerRegistry.getMBeanServerFor(locator);
148       if(mbeanserver != null)
149       {
150          return mbeanserver;
151       }
152       ClassLoader JavaDoc cl = Thread.currentThread().getContextClassLoader();
153       if(cl == null)
154       {
155          cl = MBeanServerClientInvokerProxy.class.getClassLoader();
156       }
157       MBeanServerClientInvokerProxy handler = new MBeanServerClientInvokerProxy(cl, locator, localJmxId, jmxId);
158       mbeanserver = (MBeanServer JavaDoc) Proxy.newProxyInstance(MBeanServerClientInvokerProxy.class.getClassLoader(), new Class JavaDoc[]{MBeanServer JavaDoc.class}, handler);
159       MBeanServerRegistry.register(mbeanserver, handler);
160       log.debug("created MBeanServer proxy to remote mbeanserver at: " + locator + " for JMX Id: " + jmxId + " from our JMX id: " + localJmxId);
161       return mbeanserver;
162    }
163
164    /**
165     * called to destroy the proxy and the invoker
166     */

167    public synchronized void destroy()
168    {
169       if(log.isTraceEnabled())
170       {
171          log.trace("destroy called on: " + this + " for locator: " + locator + ", and serverid: " + serverId);
172       }
173       client.disconnect();
174       MBeanServerRegistry.unregister(this);
175       cancelPoller();
176       removeListeners();
177       paramMap.clear();
178       proxies.remove(serverId);
179    }
180
181    /**
182     * cancel the running poller
183     */

184    private synchronized void cancelPoller()
185    {
186       if(poller != null)
187       {
188          poller.cancel();
189          poller = null;
190       }
191       if(pollTimer != null)
192       {
193          pollTimer.cancel();
194          pollTimer = null;
195       }
196    }
197
198    private synchronized void removeListeners()
199    {
200       Iterator JavaDoc iter = listeners.iterator();
201       while(iter.hasNext())
202       {
203          Object JavaDoc key = iter.next();
204          ClientListener.remove(key);
205          iter.remove();
206       }
207    }
208
209    private void addNotificationListener(Method JavaDoc m, Object JavaDoc args[], String JavaDoc sig[], Map JavaDoc payload)
210          throws Throwable JavaDoc
211    {
212       String JavaDoc methodName = m.getName();
213       Object JavaDoc key = ClientListener.register(serverId, (ObjectName JavaDoc) args[0], args[1], (NotificationFilter JavaDoc) args[2], args[3]);
214       listeners.add(key);
215       Object JavaDoc a[] = new Object JavaDoc[]{args[0], null, args[2], key};
216
217       // make sure we pass our local id as session id
218
client.setSessionId(serverId);
219       client.invoke(new NameBasedInvocation(methodName, a, sig),
220                     payload);
221    }
222
223    private void removeNotificationListener(Method JavaDoc m, Object JavaDoc args[], String JavaDoc sig[], Map JavaDoc payload)
224          throws Throwable JavaDoc
225    {
226       String JavaDoc methodName = m.getName();
227       Object JavaDoc id = ClientListener.makeId(serverId, (ObjectName JavaDoc) args[0], args[1]);
228       listeners.remove(id);
229       ClientListener cl = ClientListener.remove(id);
230       Object JavaDoc a[] = new Object JavaDoc[]{args[0], null, id};
231       if(cl != null)
232       {
233          // make sure we pass our local id as session id
234
client.setSessionId(serverId);
235          client.invoke(new NameBasedInvocation(methodName,
236                                                a,
237                                                new String JavaDoc[]{ObjectName JavaDoc.class.getName(),
238                                                             sig[1],
239                                                             Integer JavaDoc.class.getName()}),
240                        payload);
241       }
242    }
243
244
245    private boolean proxyEquals(Object JavaDoc proxy)
246    {
247       return (proxy.getClass() == this.getClass() && ((MBeanServerClientInvokerProxy) proxy).serverId.equals(serverId));
248    }
249
250    private Integer JavaDoc proxyHashcode()
251    {
252       return new Integer JavaDoc(client.hashCode() + serverId.hashCode());
253    }
254
255    public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args)
256          throws Throwable JavaDoc
257    {
258
259       String JavaDoc methodName = method.getName();
260
261       // handle Object.class methods, so we don't go across the wire
262
if(method.getDeclaringClass() == Object JavaDoc.class)
263       {
264          if(methodName.equals("equals"))
265          {
266             return new Boolean JavaDoc(proxyEquals(args[0]));
267          }
268          else if(methodName.equals("hashCode"))
269          {
270             return proxyHashcode();
271          }
272          else if(methodName.equals("toString"))
273          {
274             return "MBeanServerClientInvokerProxy [serverid:" + serverId + ",locator:" + client.getInvoker().getLocator() + "]";
275          }
276       }
277
278       String JavaDoc sig[] = getMethodSignature(method);
279       Map JavaDoc payload = new HashMap JavaDoc(1);
280       if(methodName.equals("addNotificationListener"))
281       {
282          addNotificationListener(method, args, sig, payload);
283          return null;
284       }
285       else if(methodName.equals("removeNotificationListener"))
286       {
287          removeNotificationListener(method, args, sig, payload);
288          return null;
289       }
290       Object JavaDoc value = null;
291       try
292       {
293          // make sure we pass our local id as session id
294
client.setSessionId(serverId);
295          value = client.invoke(new NameBasedInvocation(methodName, args, sig),
296                                payload);
297       }
298       catch(Throwable JavaDoc throwable)
299       {
300          if(log.isTraceEnabled())
301          {
302             log.trace("remote invocation failed for method: " + methodName + " to: " + serverId, throwable);
303          }
304          if(throwable instanceof ConnectionFailedException)
305          {
306             destroy();
307          }
308          rethrowMBeanException(throwable);
309       }
310       finally
311       {
312          if(payload != null)
313          {
314             // if the payload isn't null
315
NotificationQueue queue = (NotificationQueue) payload.get("notifications");
316             if(queue != null && queue.isEmpty() == false)
317             {
318                deliverNotifications(queue, false);
319             }
320          }
321       }
322
323       return value;
324    }
325
326    private void rethrowMBeanException(Throwable JavaDoc throwable) throws MBeanException JavaDoc, ReflectionException JavaDoc
327    {
328       if(throwable instanceof MBeanException JavaDoc)
329       {
330          throw (MBeanException JavaDoc) throwable;
331       }
332       else if(throwable instanceof ReflectionException JavaDoc)
333       {
334          throw (ReflectionException JavaDoc) throwable;
335       }
336       else
337       {
338          if(throwable instanceof UndeclaredThrowableException JavaDoc)
339          {
340             UndeclaredThrowableException JavaDoc ut = (UndeclaredThrowableException JavaDoc) throwable;
341             if(ut instanceof Exception JavaDoc)
342             {
343                throw new MBeanException JavaDoc((Exception JavaDoc) ut.getUndeclaredThrowable(), ut.getMessage());
344             }
345             else
346             {
347                throw new MBeanException JavaDoc(new Exception JavaDoc(ut.getUndeclaredThrowable().getMessage()));
348             }
349          }
350          else
351          {
352             if(throwable instanceof Exception JavaDoc)
353             {
354                throw new MBeanException JavaDoc((Exception JavaDoc) throwable, throwable.getMessage());
355             }
356             throw new MBeanException JavaDoc(new Exception JavaDoc(throwable.getMessage()));
357          }
358       }
359    }
360
361    public void deliverNotifications(NotificationQueue queue, boolean async) throws InterruptedException JavaDoc
362    {
363       if(async && poller != null)
364       {
365          // we're receiving async, kill the poller thread, no need for it
366
cancelPoller();
367       }
368       if(queue == null)
369       {
370          return;
371       }
372
373       Iterator JavaDoc iter = queue.iterator();
374       while(iter.hasNext())
375       {
376          final NotificationEntry entry = (NotificationEntry) iter.next();
377          final ClientListener listener = ClientListener.get(entry.getHandBack());
378          if(listener != null)
379          {
380             if(listener.listener instanceof NotificationListener JavaDoc)
381             {
382                if(log.isTraceEnabled())
383                {
384                   log.trace("sending notification for entry: " + entry + " to: " + listener.listener);
385                }
386                try
387                {
388                   ((NotificationListener JavaDoc) listener.listener).handleNotification(entry.getNotification(), listener.handback);
389                }
390                catch(Throwable JavaDoc ex)
391                {
392                   log.error("Error sending notification: " + entry.getNotification() + " to listener: " + listener);
393                }
394             }
395             else
396             {
397                //ObjectName l = (ObjectName)listener.listener;
398
//TODO: implement
399
log.error("called unimplemented addListener method", new Exception JavaDoc());
400             }
401          }
402          else
403          {
404             log.warn("couldn't find client listener for handback: " + entry.getHandBack() + "\nentry:" + entry + "\nqueue: " + queue + "\ndump: " + ClientListener.dump());
405          }
406       }
407    }
408
409    public String JavaDoc[] getMethodSignature(Method JavaDoc method)
410    {
411       if(paramMap.containsKey(method))
412       {
413          return (String JavaDoc[]) paramMap.get(method);
414       }
415       Class JavaDoc paramTypes[] = method.getParameterTypes();
416       String JavaDoc sig[] = (paramTypes == null) ? null : new String JavaDoc[paramTypes.length];
417       if(paramTypes != null)
418       {
419          for(int c = 0; c < sig.length; c++)
420          {
421             sig[c] = paramTypes[c].getName();
422          }
423       }
424       paramMap.put(method, sig);
425       return sig;
426    }
427
428    /**
429     * notification pooler is a timer task that will poll a remote server invoker for
430     * notifications and re-dispatch them locally
431     */

432    private final class NotificationPoller extends TimerTask JavaDoc
433    {
434       public void run()
435       {
436          try
437          {
438             // we only poll if we're connected, and we have active listeners
439
// attached remotely
440
if(client.isConnected() && ClientListener.hasListeners())
441             {
442                Map JavaDoc payload = new HashMap JavaDoc(1);
443                // transport the special method that will just return null, but will also return
444
// the notification queue for the session in the payload
445
client.setSessionId(serverId);
446                Boolean JavaDoc continuePolling = (Boolean JavaDoc) client.invoke(new NameBasedInvocation("$GetNotifications$",
447                                                                                          new Object JavaDoc[]{},
448                                                                                          new String JavaDoc[]{}),
449                                                                  payload);
450                NotificationQueue queue = (NotificationQueue) payload.get("notifications");
451                if(queue != null && queue.isEmpty() == false)
452                {
453                   // we have notifications, deliver locally,
454
deliverNotifications(queue, false);
455                }
456                if(continuePolling.booleanValue() == false)
457                {
458                   cancelPoller();
459                }
460             }
461          }
462          catch(ConnectionFailedException cnf)
463          {
464             // remove ourself
465
destroy();
466          }
467          catch(Throwable JavaDoc ex)
468          {
469             //FIXME - what to do?
470
ex.printStackTrace();
471          }
472       }
473    }
474 }
475
Popular Tags