View Javadoc

1   /* $Id: RemoteMBeanInvocationHandler.java 4010 2005-12-14 02:07:16Z stack-sf $
2    *
3    * Created on Dec 12, 2005
4    *
5    * Copyright (C) 2005 Internet Archive.
6    *  
7    * This file is part of the Heritrix Cluster Controller (crawler.archive.org).
8    *  
9    * HCC is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser Public License as published by
11   * the Free Software Foundation; either version 2.1 of the License, or
12   * any later version.
13   * 
14   * Heritrix is distributed in the hope that it will be useful, 
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU Lesser Public License for more details.
18   * 
19   * You should have received a copy of the GNU Lesser Public License
20   * along with Heritrix; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22   */
23  package org.archive.hcc;
24  
25  import java.lang.reflect.InvocationHandler;
26  import java.lang.reflect.Method;
27  import java.util.HashMap;
28  import java.util.Map;
29  import java.util.logging.Level;
30  import java.util.logging.Logger;
31  
32  import javax.management.Attribute;
33  import javax.management.AttributeList;
34  import javax.management.MBeanServerConnection;
35  import javax.management.Notification;
36  import javax.management.NotificationFilter;
37  import javax.management.NotificationListener;
38  import javax.management.ObjectName;
39  
40  /***
41   * This invocation handler is responsible for proxying calls to and
42   * remote notifications from remote mbean instances.
43   * @author Daniel Bernstein (dbernstein@archive.org)
44   */
45  public class RemoteMBeanInvocationHandler implements
46          InvocationHandler {
47      
48      private static Logger log =
49          Logger.getLogger(RemoteMBeanInvocationHandler.class.getName());
50  
51      private ObjectName remoteObjectName;
52  
53      private MBeanServerConnection connection;
54  
55      private ObjectName proxyObjectName;
56  
57      private NotificationListener spyListener;
58  
59      private Map<NotificationListener, NotificationListener> notificationProxyMap = 
60          new HashMap<NotificationListener, NotificationListener>();
61  
62      /***
63       * Constructs a remote mbean invocation handler.
64       * @param remoteObjectName 
65       * @param proxyObjectName
66       * @param connection 
67       * @param notificationInterceptor
68       */
69      public RemoteMBeanInvocationHandler(
70              ObjectName remoteObjectName,
71              ObjectName proxyObjectName,
72              MBeanServerConnection connection,
73              NotificationListener notificationInterceptor) {
74          this.remoteObjectName = remoteObjectName;
75          this.proxyObjectName = proxyObjectName;
76          this.connection = connection;
77          this.spyListener = notificationInterceptor;
78      }
79  
80      public ObjectName getRemoteObjectName() {
81          return remoteObjectName;
82      }
83  
84      /***
85       * Invokes the specified method on the remote mbean. If the method doesn't
86       * match a method on the DynamicMBean or NotificationEmitter, the method is
87       * invoked on the remote invocation handler directly (ie equals and
88       * hashCode);
89       * @param proxy 
90       * @param method 
91       * @param args 
92       * @return Result object.
93       * @throws Throwable 
94       */
95      public Object invoke(Object proxy, Method method, Object[] args)
96              throws Throwable {
97  
98          if (log.isLoggable(Level.FINER)) {
99              log.finer("method=" + method);
100         }
101         if (method.getName().equals("invoke")) {
102             return this.connection.invoke(
103                     this.remoteObjectName,
104                     (String) args[0],
105                     (Object[]) args[1],
106                     (String[]) args[2]);
107         } else if (method.getName().equals("getMBeanInfo")) {
108             return this.connection.getMBeanInfo(this.remoteObjectName);
109         } else if (method.getName().equals("getAttributes")) {
110             return this.connection.getAttributes(
111                     this.remoteObjectName,
112                     (String[]) args[0]);
113         } else if (method.getName().equals("getAttribute")) {
114             return this.connection.getAttribute(
115                     this.remoteObjectName,
116                     (String) args[0]);
117         } else if (method.getName().equals("setAttributes")) {
118             return this.connection.setAttributes(
119                     this.remoteObjectName,
120                     (AttributeList) args[0]);
121         } else if (method.getName().equals("setAttribute")) {
122             this.connection.setAttribute(
123                     this.remoteObjectName,
124                     (Attribute) args[0]);
125             return null;
126         } else if (method.getName().equals("addNotificationListener")) {
127             
128             //reference the notification listener argument.
129             final NotificationListener clientListener = 
130                 (NotificationListener) args[0];
131 
132             //wrap the listener with a interceptor
133             //that translates the proxy object name
134             NotificationListener interceptingListener = 
135                 new NotificationListener() {
136                  public void handleNotification(
137                          javax.management.Notification notification,
138                          Object handback) {
139                      notification.setSource(getProxyObjectName());
140                      // notify the spy listener
141                      spyListener.handleNotification(notification, handback);
142  
143                      // forward to registered listeners.
144                      clientListener.handleNotification(notification, handback);
145                  };
146             };
147 
148             this.notificationProxyMap.put(clientListener, interceptingListener);
149 
150             //proxy the notification filter if 
151             //the second argument is not null,
152             //a filter that translates teh remote object name.
153             NotificationFilter interceptingFilter = null;
154 
155             if (args[1] != null) {
156                 final NotificationFilter clientFilter = 
157                     (NotificationFilter) args[1];
158                 interceptingFilter = 
159                     new NotificationFilter() {
160                      public boolean isNotificationEnabled(
161                              Notification notification) {
162                          Notification n = new Notification(notification
163                                  .getType(), proxyObjectName, notification
164                                  .getSequenceNumber(), notification
165                                  .getTimeStamp(), notification.getMessage());
166  
167                          n.setUserData(notification.getUserData());
168                          return clientFilter.isNotificationEnabled(n);
169                      }
170                 };
171             }
172 
173             this.connection.addNotificationListener(
174                     this.remoteObjectName,
175                     interceptingListener,
176                     interceptingFilter,
177                     (Object) args[2]);
178             return null;
179             
180         } else if (method.getName().equals("removeNotificationListener")) {
181             final NotificationListener clientListener = 
182                     (NotificationListener) args[0];
183             this.notificationProxyMap.remove(clientListener);
184             this.connection.removeNotificationListener(
185                     this.remoteObjectName,
186                     clientListener);
187         } else if (method.getName().equals("getNotificationInfo()")) {
188             return this.connection
189                     .getMBeanInfo(this.remoteObjectName)
190                     .getNotifications();
191         }
192 
193         Method m = this.getClass().getMethod(
194                 method.getName(),
195                 method.getParameterTypes());
196         return m.invoke(this, args);
197     }
198 
199     public ObjectName getProxyObjectName() {
200         return proxyObjectName;
201     }
202 }