/*******************************************************************************
* Copyright (c) 2012 VMware, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.beans.ui.livegraph.model;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.json.JSONException;
import org.springframework.context.support.LiveBeansViewMBean;
import org.springframework.ide.eclipse.beans.ui.livegraph.LiveGraphUiPlugin;
import org.springsource.ide.eclipse.commons.core.StatusHandler;
/**
* Loads an MBean exposed by the Spring Framework and generates a
* {@link LiveBeansModel} from the JSON contained within.
*
* @author Leo Dos Santos
*/
public class LiveBeansModelGenerator {
/**
* This method will not attempt to close the given {@link JMXConnector}. If
* the connection has failed, clients may capture the thrown
* {@link CoreException} and inform the user. This method is not UI safe,
* and may block the UI with network operations. Clients will need to call
* this method from a non-blocking {@link Job}.
*
* @param connector
* @param appName
* @return A valid {@link LiveBeansModel} model, or <code>null</code> if
* connection has failed
* @throws CoreException
*/
public static LiveBeansModel connectToModel(JMXConnector connector, LiveBeansSession session) throws CoreException {
try {
String appName = session.getApplicationName();
if (connector != null) {
ObjectName name;
if (appName == null || appName.length() == 0) {
// Standalone apps like spring-boot will have an empty app
// name. Deal with that situation.
name = ObjectName.getInstance("", "application", "");
}
else {
name = ObjectName.getInstance("", "application", "/".concat(appName));
}
MBeanServerConnection connection = connector.getMBeanServerConnection();
// Test the MBean's existence before proceeding. Will throw
// InstanceNotFoundException
connection.getObjectInstance(name);
LiveBeansViewMBean mbean = MBeanServerInvocationHandler.newProxyInstance(connection, name,
LiveBeansViewMBean.class, false);
return generateModel(mbean, session);
}
}
catch (MalformedObjectNameException e) {
throw new CoreException(new Status(IStatus.ERROR, LiveGraphUiPlugin.PLUGIN_ID,
"An error occurred while connecting to server. Please check that the application name is correct.",
e));
}
catch (InstanceNotFoundException e) {
throw new CoreException(new Status(IStatus.ERROR, LiveGraphUiPlugin.PLUGIN_ID,
"An error occurred while connecting to server. Please check that the application name is correct.",
e));
}
catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR, LiveGraphUiPlugin.PLUGIN_ID,
"An error occurred while connecting to server.", e));
}
return null;
}
/**
* This method will attempt to create a {@link JMXConnector} from the given
* parameters and will close it when it is finished. If the connection has
* failed, clients may capture the thrown {@link CoreException} and inform
* the user. This method is UI safe, and will not block the UI with network
* operations.
*
* @param serviceUrl
* @param username
* @param password
* @param appName
* @return A valid {@link LiveBeansModel} model, or <code>null</code> if
* connection has failed
* @throws CoreException
*/
public static LiveBeansModel connectToModel(final String serviceUrl, final String username, final String password,
final String appName) throws CoreException {
return connectToModel(serviceUrl, username, password, appName, null);
}
/**
* This method will attempt to create a {@link JMXConnector} from the given
* parameters and will close it when it is finished. If the connection has
* failed, clients may capture the thrown {@link CoreException} and inform
* the user. This method is UI safe, and will not block the UI with network
* operations.
*
* @param serviceUrl
* @param username
* @param password
* @param appName
* @param project
* @return A valid {@link LiveBeansModel} model, or <code>null</code> if
* connection has failed
* @throws CoreException
*/
public static LiveBeansModel connectToModel(final String serviceUrl, final String username, final String password,
final String appName, final IProject project) throws CoreException {
final CountDownLatch latch = new CountDownLatch(1);
final LiveBeansModel[] result = new LiveBeansModel[1];
final CoreException[] status = new CoreException[1];
Job jmxOperation = new Job("Executing Server Command") {
@Override
protected IStatus run(IProgressMonitor monitor) {
JMXConnector connector = null;
try {
connector = setupConnector(serviceUrl, username, password);
result[0] = connectToModel(connector, new LiveBeansSession(serviceUrl, username, password, appName, project));
}
catch (CoreException e) {
status[0] = e;
}
finally {
latch.countDown();
if (connector != null) {
try {
connector.close();
}
catch (IOException e) {
StatusHandler.log(new Status(IStatus.ERROR, LiveGraphUiPlugin.PLUGIN_ID,
"An error occurred while closing connection to server.", e));
}
}
}
return Status.OK_STATUS;
}
};
jmxOperation.schedule();
try {
if (latch.await(30, TimeUnit.SECONDS)) {
if (status[0] != null) {
throw status[0];
}
return result[0];
}
}
catch (InterruptedException e) {
// swallowed
}
return null;
}
private static LiveBeansModel generateModel(LiveBeansViewMBean mbean, LiveBeansSession session)
throws CoreException {
try {
if (mbean != null) {
String json = mbean.getSnapshotAsJson();
LiveBeansJsonParser parser = new LiveBeansJsonParser(session, json);
LiveBeansModel model = parser.parse();
// add model to collection
LiveBeansModelCollection.getInstance().addModel(model);
return model;
}
}
catch (JSONException e) {
throw new CoreException(new Status(IStatus.ERROR, LiveGraphUiPlugin.PLUGIN_ID,
"An error occurred while generating graph model.", e));
}
return null;
}
/**
* This method will attempt to produce an up to date {@link LiveBeansModel}
* from the connection information in the given model. Will return the
* original model if there is a failure.
*
* @param originalModel
* @return {@link LiveBeansModel}
* @throws CoreException
*/
public static LiveBeansModel refreshModel(LiveBeansModel originalModel) throws CoreException {
LiveBeansSession session = originalModel.getSession();
if (session != null) {
LiveBeansModel model = connectToModel(session.getServiceUrl(), session.getUsername(),
session.getPassword(), session.getApplicationName(), session.getProject());
if (model != null) {
return model;
}
}
return originalModel;
}
private static JMXConnector setupConnector(String serviceUrl, String username, String password)
throws CoreException {
try {
if (serviceUrl != null && serviceUrl.length() > 0) {
Map<String, String[]> env = new HashMap<String, String[]>();
if (username != null && password != null) {
String[] creds = new String[] { username, password };
env.put(JMXConnector.CREDENTIALS, creds);
}
return JMXConnectorFactory.connect(new JMXServiceURL(serviceUrl), env);
}
}
catch (MalformedURLException e) {
throw new CoreException(new Status(IStatus.ERROR, LiveGraphUiPlugin.PLUGIN_ID,
"An error occurred while connecting to server. Please check that the service URL is correct.", e));
}
catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR, LiveGraphUiPlugin.PLUGIN_ID,
"An error occurred while connecting to server.", e));
}
return null;
}
}