001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.management.remote.connect;
006:
007: import com.tc.async.api.AbstractEventHandler;
008: import com.tc.async.api.EventContext;
009: import com.tc.async.api.EventHandlerException;
010: import com.tc.logging.TCLogger;
011: import com.tc.logging.TCLogging;
012: import com.tc.management.TerracottaMBean;
013: import com.tc.management.TerracottaManagement;
014: import com.tc.management.remote.protocol.ProtocolProvider;
015: import com.tc.management.remote.protocol.terracotta.ClientProvider;
016: import com.tc.management.remote.protocol.terracotta.TunnelingMessageConnection;
017: import com.tc.management.remote.protocol.terracotta.ClientTunnelingEventHandler.L1ConnectionMessage;
018: import com.tc.net.TCSocketAddress;
019: import com.tc.net.protocol.tcm.MessageChannel;
020:
021: import java.io.IOException;
022: import java.net.MalformedURLException;
023: import java.util.ArrayList;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.Set;
029:
030: import javax.management.MBeanServer;
031: import javax.management.MBeanServerConnection;
032: import javax.management.MBeanServerInvocationHandler;
033: import javax.management.Notification;
034: import javax.management.NotificationFilter;
035: import javax.management.NotificationListener;
036: import javax.management.ObjectName;
037: import javax.management.StandardMBean;
038: import javax.management.remote.JMXConnectionNotification;
039: import javax.management.remote.JMXConnector;
040: import javax.management.remote.JMXConnectorFactory;
041: import javax.management.remote.JMXServiceURL;
042:
043: public class ClientConnectEventHandler extends AbstractEventHandler {
044:
045: private static final class ConnectorClosedFilter implements
046: NotificationFilter {
047:
048: public boolean isNotificationEnabled(
049: final Notification notification) {
050: boolean enabled = false;
051: if (notification instanceof JMXConnectionNotification) {
052: final JMXConnectionNotification jmxcn = (JMXConnectionNotification) notification;
053: enabled = jmxcn.getType().equals(
054: JMXConnectionNotification.CLOSED);
055: }
056: return enabled;
057: }
058:
059: }
060:
061: private static final class ConnectorClosedListener implements
062: NotificationListener {
063:
064: private static final TCLogger logger = TCLogging
065: .getLogger(ConnectorClosedListener.class);
066:
067: private final MBeanServerConnection mBeanServerConnection;
068:
069: ConnectorClosedListener(
070: final MBeanServerConnection mBeanServerConnection) {
071: this .mBeanServerConnection = mBeanServerConnection;
072: }
073:
074: final public void handleNotification(
075: final Notification notification, final Object context) {
076: for (Iterator beanNames = ((List) context).iterator(); beanNames
077: .hasNext();) {
078: final ObjectName dsoClientBeanName = (ObjectName) beanNames
079: .next();
080: try {
081: mBeanServerConnection
082: .unregisterMBean(dsoClientBeanName);
083: } catch (Exception e) {
084: logger.warn("Unable to unregister DSO client bean["
085: + dsoClientBeanName + "]", e);
086: }
087: }
088: }
089: }
090:
091: private static final TCLogger logger = TCLogging
092: .getLogger(ClientConnectEventHandler.class);
093:
094: public void handleEvent(EventContext context)
095: throws EventHandlerException {
096: L1ConnectionMessage msg = (L1ConnectionMessage) context;
097: if (msg.isConnectingMsg()) {
098: addJmxConnection(msg);
099: } else {
100: removeJmxConnection(msg);
101: }
102: }
103:
104: private void addJmxConnection(final L1ConnectionMessage msg) {
105: final MessageChannel channel = msg.getChannel();
106: final TCSocketAddress remoteAddress = channel != null ? channel
107: .getRemoteAddress() : null;
108: if (remoteAddress == null) {
109: return;
110: }
111:
112: final MBeanServer l2MBeanServer = msg.getMBeanServer();
113: final Map channelIdToJmxConnector = msg
114: .getChannelIdToJmxConnector();
115: final Map channelIdToMsgConnection = msg
116: .getChannelIdToMsgConnector();
117: synchronized (channelIdToJmxConnector) {
118: if (!channelIdToJmxConnector.containsKey(channel
119: .getChannelID())) {
120: JMXServiceURL serviceURL;
121: try {
122: serviceURL = new JMXServiceURL(
123: "terracotta",
124: remoteAddress.getAddress().getHostAddress(),
125: remoteAddress.getPort());
126: } catch (MalformedURLException murle) {
127: logger
128: .error(
129: "Unable to construct a JMX service URL using DSO client channel from host["
130: + channel
131: .getRemoteAddress()
132: + "]; tunneled JMX connection will not be established",
133: murle);
134: return;
135: }
136: Map environment = new HashMap();
137: ProtocolProvider.addTerracottaJmxProvider(environment);
138: environment.put(ClientProvider.JMX_MESSAGE_CHANNEL,
139: channel);
140: environment.put(ClientProvider.CONNECTION_LIST,
141: channelIdToMsgConnection);
142: final JMXConnector jmxConnector;
143: try {
144: jmxConnector = JMXConnectorFactory.connect(
145: serviceURL, environment);
146:
147: final MBeanServerConnection l1MBeanServerConnection = jmxConnector
148: .getMBeanServerConnection();
149: Set mBeans = l1MBeanServerConnection.queryNames(
150: null, TerracottaManagement
151: .matchAllTerracottaMBeans());
152: List modifiedObjectNames = new ArrayList();
153: for (Iterator iter = mBeans.iterator(); iter
154: .hasNext();) {
155: ObjectName objName = (ObjectName) iter.next();
156: try {
157: TerracottaMBean mBeanProxy = (TerracottaMBean) MBeanServerInvocationHandler
158: .newProxyInstance(
159: l1MBeanServerConnection,
160: objName,
161: TerracottaMBean.class,
162: false);
163: ObjectName modifiedObjName = TerracottaManagement
164: .addNodeInfo(objName, channel
165: .getRemoteAddress());
166: Class interfaceClass = Class
167: .forName(mBeanProxy
168: .getInterfaceClassName());
169: Object obj = MBeanServerInvocationHandler
170: .newProxyInstance(
171: l1MBeanServerConnection,
172: objName,
173: interfaceClass,
174: mBeanProxy
175: .isNotificationBroadcaster());
176: l2MBeanServer.registerMBean(
177: new StandardMBean(obj,
178: interfaceClass),
179: modifiedObjName);
180: modifiedObjectNames.add(modifiedObjName);
181: } catch (Exception e) {
182: logger
183: .error(
184: "Unable to register remote DSO client MBean["
185: + objName
186: .getCanonicalName()
187: + "] for host["
188: + channel
189: .getRemoteAddress()
190: + "], this bean will not show up in monitoring tools!!",
191: e);
192: }
193: }
194: try {
195: jmxConnector.addConnectionNotificationListener(
196: new ConnectorClosedListener(
197: l2MBeanServer),
198: new ConnectorClosedFilter(),
199: modifiedObjectNames);
200: } catch (Exception e) {
201: logger
202: .error(
203: "Unable to register a JMX connection listener for the DSO client["
204: + channel
205: .getRemoteAddress()
206: + "], if the DSO client disconnects the then its (dead) beans will not be unregistered",
207: e);
208: }
209: } catch (IOException ioe) {
210: logger
211: .error(
212: "Unable to create tunneled JMX connection to the DSO client on host["
213: + channel
214: .getRemoteAddress()
215: + "], this DSO client will not show up in monitoring tools!!",
216: ioe);
217: return;
218: }
219: channelIdToJmxConnector.put(channel.getChannelID(),
220: jmxConnector);
221: } else {
222: logger
223: .warn("We are trying to create a new tunneled JMX connection but already have one for channel["
224: + channel.getRemoteAddress()
225: + "], ignoring new connection message");
226: }
227: }
228: }
229:
230: private void removeJmxConnection(final L1ConnectionMessage msg) {
231: final MessageChannel channel = msg.getChannel();
232: final Map channelIdToJmxConnector = msg
233: .getChannelIdToJmxConnector();
234: final Map channelIdToMsgConnection = msg
235: .getChannelIdToMsgConnector();
236:
237: synchronized (channelIdToMsgConnection) {
238: final TunnelingMessageConnection tmc = (TunnelingMessageConnection) channelIdToMsgConnection
239: .remove(channel.getChannelID());
240: if (tmc != null) {
241: try {
242: tmc.close();
243: } catch (IOException ioe) {
244: logger.warn(
245: "Unable to close JMX tunneling message connection to DSO client["
246: + channel + "]", ioe);
247: }
248: }
249: }
250:
251: synchronized (channelIdToJmxConnector) {
252: if (channelIdToJmxConnector.containsKey(channel
253: .getChannelID())) {
254: final JMXConnector jmxConnector = (JMXConnector) channelIdToJmxConnector
255: .remove(channel.getChannelID());
256: if (jmxConnector != null) {
257: try {
258: jmxConnector.close();
259: } catch (IOException ioe) {
260: logger.debug(
261: "Unable to close JMX connector to DSO client["
262: + channel + "]", ioe);
263: }
264: }
265: } else {
266: logger
267: .warn("DSO client channel closed without a corresponding tunneled JMX connection");
268: }
269: }
270: }
271:
272: }
|