/*
* Copyright (c) 2010-2012 Grid Dynamics Consulting Services, Inc, All Rights Reserved
* http://www.griddynamics.com
*
* This library is free software; you can redistribute it and/or modify it under the terms of
* the Apache License; either
* version 2.0 of the License, or any later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.griddynamics.jagger.agent.impl;
import com.google.common.collect.Maps;
import com.griddynamics.jagger.diagnostics.thread.sampling.ThreadInfoProvider;
import com.griddynamics.jagger.util.ConfigurableExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import javax.management.JMException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
/**
* @author Alexey Kiselyov
* Date: 30.08.11
*/
public class RemoteMultiThreadInfoProvider implements ThreadInfoProvider {
private static final Logger log = LoggerFactory.getLogger(RemoteMultiThreadInfoProvider.class);
private ConfigurableExecutor executor;
private JmxConnector jmxConnector;
private Future<Map<String, MBeanServerConnection>> future;
private Map<String, MBeanServerConnection> connections = Maps.newConcurrentMap();
@Override
public Set<String> getIdentifiersSuT() {
return connections.keySet();
}
@Override
public Map<String, ThreadInfo[]> getThreadInfo() {
if (connections.size() == 0) {
connections = getEstablishedJmxConnections();
}
if (connections.size() > 0) {
long startTimeLog = System.currentTimeMillis();
Map<String, ThreadInfo[]> result = Maps.newHashMap();
for (String serviceURL : this.connections.keySet()) {
try {
ObjectName srvThrdName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
long[] threadIDs = (long[]) this.connections.get(serviceURL).getAttribute(srvThrdName, "AllThreadIds");
MBeanServerConnection mBeanServerConnection = this.connections.get(serviceURL);
CompositeData[] compositeDatas = (CompositeData[]) (mBeanServerConnection.invoke(srvThrdName,
"getThreadInfo", new Object[]{threadIDs, Integer.MAX_VALUE}, new String[]{"[J", "int"}));
ThreadInfo[] threadInfos = new ThreadInfo[compositeDatas.length];
for (int i = 0; i < compositeDatas.length; i++) {
threadInfos[i] = ThreadInfo.from(compositeDatas[i]);
}
result.put(serviceURL, threadInfos);
} catch (JMException e) {
log.error("JMException", e);
} catch (IOException e) {
log.error("IOException", e);
}
}
log.debug("collected threadInfos through jmx for profiling on agent: time {} ms", System.currentTimeMillis() - startTimeLog);
return result;
}
else {
log.warn("JMX connection is not initialized. Skip");
return Collections.emptyMap();
}
}
public void init(){
future = executor.submit(new Callable<Map<String, MBeanServerConnection>>() {
@Override
public Map<String, MBeanServerConnection> call() throws Exception {
return jmxConnector.connect("");
}
});
}
private Map<String, MBeanServerConnection> getEstablishedJmxConnections() {
Map<String, MBeanServerConnection> result;
if ((future == null) || (!future.isDone())) {
return Collections.emptyMap();
}
try {
result = future.get();
} catch (Exception ex) {
// connection failed
future = null;
log.error("Failed to establish JMX connection");
return Collections.emptyMap();
}
return result;
}
@Required
public void setExecutor(ConfigurableExecutor executor) {
this.executor = executor;
}
@Required
public void setJmxConnector(JmxConnector jmxConnector) {
this.jmxConnector = jmxConnector;
}
}