/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive.jmx; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import org.ow2.proactive.authentication.Authentication; import org.ow2.proactive.jmx.naming.JMXTransportProtocol; import org.ow2.proactive.jmx.provider.JMXProviderUtils; /** * This helper class provides a way to establish a JMX connection over RMI. * If it fails this helper will try over RO (Remote Object). * It can be reused if each open connection is closed with {@link #disconnect()} method. * * @author <a href="mailto:support@activeeon.com">ActiveEon Team</a> */ public final class JMXClientHelper { /** The name of the java property that defines the {@link JMXTransportProtocol} used by JMX client */ public static final String PA_JMX_CLIENT_PROTOCOL = "pa.jmx.client.protocol"; /** The authentication interface */ private final Authentication auth; /** The environment that may contains credentials */ private final Map<String, Object> env; /** To know if this helper is connected */ private boolean isConnected; // false by default /** The JMX connector that provides a JMX connection */ private JMXConnector jmxConnector; /** The last happened exception*/ private Exception lastException; /** * Creates a new instance of this helper and puts the credentials in the environment * used by the JMXConnector. * * @param auth the authentication interface * @param creds the array that contains credentials */ public JMXClientHelper(final Authentication auth, final Object[] creds) { this.auth = auth; final HashMap<String, Object> environment = new HashMap<>(2); // 2 because creds + portential RO fallback environment.put(JMXConnector.CREDENTIALS, creds); this.env = environment; } /** * Creates a new instance of this helper. * * @param auth the authentication interface * @param env the environment used by the JMXConnector */ public JMXClientHelper(final Authentication auth, final Map<String, Object> env) { this.auth = auth; this.env = env; } /** * Establishes the connection to the connector server by calling {@link #connect(JMXTransportProtocol)} * using the JMXTransportProtocol specified by the {@link #PA_JMX_CLIENT_PROTOCOL} property * that can be {@link JMXTransportProtocol#RMI} or {@link JMXTransportProtocol#RO}. * If the property is not defined the {@link JMXTransportProtocol#RMI} protocol is used. * * @return <code>true</code> if this helper is connected otherwise return <code>false</code> */ public boolean connect() { JMXTransportProtocol protocol = JMXTransportProtocol.RMI; final String property = System.getProperty(JMXClientHelper.PA_JMX_CLIENT_PROTOCOL); if (property != null && JMXTransportProtocol.valueOf(property.toUpperCase()) == JMXTransportProtocol.RO) { protocol = JMXTransportProtocol.RO; } return this.connect(protocol); } /** * Establishes the connection to the connector server. * Handles exception and returns boolean value if success or not. * * @param protocol the {@link JMXTransportProtocol} * @return <code>true</code> if this helper is connected and <code>false</code> otherwise */ public boolean connect(final JMXTransportProtocol protocol) { if (this.isConnected) { return true; } try { if (protocol == JMXTransportProtocol.RO) { this.jmxConnector = JMXClientHelper.tryJMXoverRO(auth, env); } else { this.jmxConnector = JMXClientHelper.tryJMXoverRMI(auth, env); } this.isConnected = true; } catch (Exception e) { this.lastException = e; } return this.isConnected; } /** * Returns true if this client is connected. * * @return <code>true</code> if this client helper is connected otherwise return <code>false</code> */ public boolean isConnected() { return this.isConnected; } /** * Disconnects the connector of this helper. */ public void disconnect() { try { this.isConnected = false; if (this.jmxConnector != null) { this.jmxConnector.close(); } this.jmxConnector = null; } catch (Exception e) { this.lastException = e; } finally { this.env.remove(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES); } } /** * Returns the connected JMXConnector. * * @return the connected connector (may be null) */ public JMXConnector getConnector() { return this.jmxConnector; } /** * Returns the last happened exception. * * @return the last exception occurred during precedent operation */ public Exception getLastException() { return this.lastException; } /** * Tries to create a a JMXConnector over RMI, with a fall back solution over RO. * * @param auth the authentication interface * @param env the environment that may contains credentials * @return a new connected JMXConnector */ public static JMXConnector tryJMXoverRMI(final Authentication auth, final Map<String, Object> env) { JMXServiceURL jmxRmiServiceURL = null; try { final String url = auth.getJMXConnectorURL(JMXTransportProtocol.RMI); jmxRmiServiceURL = new JMXServiceURL(url); } catch (Exception e) { // At this point the JMX-RMI infrastructure was not started, try JMX over RO return tryJMXoverRO(auth, env); } // Try to connect using RMI try { return JMXConnectorFactory.connect(jmxRmiServiceURL, env); } catch (IOException e) { // At this point there was a communication problem, try to connect over RO return tryJMXoverRO(auth, env); } } /** * Tries to create a a JMXConnector over RO. Throws a RuntimeException in case of failure. * * @param auth the authentication interface * @param env the environment that may contains credentials * @return a new connected JMXConnector */ public static JMXConnector tryJMXoverRO(final Authentication auth, final Map<String, Object> env) { env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, JMXProviderUtils.RO_PROVIDER_PKGS); JMXServiceURL jmxRoServiceURL = null; try { final String url = auth.getJMXConnectorURL(JMXTransportProtocol.RO); jmxRoServiceURL = new JMXServiceURL(url); } catch (Exception e) { // At this point the JMX-RO infrastructure was not started throw an Exception throw new RuntimeException("Unable to obtain the URL of the JMX-RO connector server due to " + e.getMessage(), e); } try { return JMXConnectorFactory.connect(jmxRoServiceURL, env); } catch (IOException e) { throw new RuntimeException("Unable to connect to the JMX-RO connector server", e); } } }