Skip to content

Commit

Permalink
Allow to connect to a local pid
Browse files Browse the repository at this point in the history
  • Loading branch information
henri-tremblay committed Aug 21, 2015
1 parent dc14a55 commit c3c1c89
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 40 deletions.
43 changes: 43 additions & 0 deletions jmxtrans-core/pom.xml
Expand Up @@ -111,6 +111,13 @@
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.6.0</version>
<scope>system</scope>
<systemPath>${toolsjar}</systemPath>
</dependency>
</dependencies>

<build>
Expand All @@ -132,4 +139,40 @@
</plugins>
</build>

<profiles>
<profile>
<id>windows_profile</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<toolsjar>${java.home}/../lib/tools.jar</toolsjar>
</properties>
</profile>
<profile>
<id>unix_profile</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<properties>
<toolsjar>${java.home}/../lib/tools.jar</toolsjar>
</properties>
</profile>
<profile>
<id>osx_profile</id>
<activation>
<activeByDefault>false</activeByDefault>
<os>
<family>mac</family>
</os>
</activation>
<properties>
<toolsjar>${java.home}/../lib/tools.jar</toolsjar>
</properties>
</profile>
</profiles>
</project>
Expand Up @@ -3,6 +3,7 @@
import com.googlecode.jmxtrans.connections.JMXConnectionParams;
import com.googlecode.jmxtrans.jmx.JmxUtils;
import com.googlecode.jmxtrans.model.Server;
import com.sun.tools.attach.VirtualMachine;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.quartz.Job;
import org.quartz.JobDataMap;
Expand All @@ -13,6 +14,8 @@

import javax.inject.Inject;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXServiceURL;
import java.io.File;

/**
* This is a quartz job that is responsible for executing a Server object on a
Expand All @@ -21,6 +24,9 @@
* @author jon
*/
public class ServerJob implements Job {

private static final String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";

private static final Logger log = LoggerFactory.getLogger(ServerJob.class);

private final GenericKeyedObjectPool<JMXConnectionParams, JMXConnector> jmxPool;
Expand All @@ -41,8 +47,17 @@ public void execute(JobExecutionContext context) throws JobExecutionException {

JMXConnector conn = null;
JMXConnectionParams connectionParams = null;

try {
connectionParams = new JMXConnectionParams(server.getJmxServiceURL(), server.getEnvironment());
JMXServiceURL jmxUrl;
if(server.getPid() != null) {
jmxUrl = extractJMXServiceURLFromPid(server.getPid());
}
else {
jmxUrl = new JMXServiceURL(server.getUrl());
}

connectionParams = new JMXConnectionParams(jmxUrl, server.getEnvironment());
if (!server.isLocal()) {
conn = jmxPool.borrowObject(connectionParams);
}
Expand All @@ -60,4 +75,32 @@ public void execute(JobExecutionContext context) throws JobExecutionException {

log.debug("+++++ Finished server job: {}", server);
}

private JMXServiceURL extractJMXServiceURLFromPid(String pid) throws Exception
{
// attach to the target application
VirtualMachine vm = VirtualMachine.attach(pid );

try {
// get the connector address
String connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);

// no connector address, so we start the JMX agent
if (connectorAddress == null) {
String agent = vm.getSystemProperties().getProperty("java.home") +
File.separator + "lib" + File.separator + "management-agent.jar";
vm.loadAgent(agent);

// agent is started, get the connector address
connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
}

// establish connection to connector server
return new JMXServiceURL(connectorAddress);
}
finally
{
vm.detach();
}
}
}
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang.builder.EqualsBuilder;
Expand All @@ -18,18 +19,16 @@
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.management.ManagementFactory;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion.NON_NULL;
import static com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion.*;
import static com.google.common.collect.ImmutableSet.copyOf;
import static com.googlecode.jmxtrans.model.PropertyResolver.resolveProps;
import static java.util.Arrays.asList;
import static javax.management.remote.JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES;
import static javax.naming.Context.SECURITY_CREDENTIALS;
import static javax.naming.Context.SECURITY_PRINCIPAL;
import static com.googlecode.jmxtrans.model.PropertyResolver.*;
import static java.util.Arrays.*;
import static javax.management.remote.JMXConnectorFactory.*;
import static javax.naming.Context.*;

/**
* Represents a jmx server that we want to connect to. This also stores the
Expand All @@ -41,6 +40,7 @@
@JsonPropertyOrder(value = {
"alias",
"local",
"pid",
"host",
"port",
"username",
Expand All @@ -57,6 +57,7 @@ public class Server {
private static final String BACK = "/jmxrmi";

private final String alias;
private final String pid;
private final String host;
private final String port;
private final String username;
Expand All @@ -74,6 +75,7 @@ public class Server {
@JsonCreator
public Server(
@JsonProperty("alias") String alias,
@JsonProperty("pid") String pid,
@JsonProperty("host") String host,
@JsonProperty("port") String port,
@JsonProperty("username") String username,
Expand All @@ -85,6 +87,7 @@ public Server(
@JsonProperty("local") boolean local,
@JsonProperty("queries") List<Query> queries) {
this.alias = resolveProps(alias);
this.pid = resolveProps(pid);
this.host = resolveProps(host);
this.port = resolveProps(port);
this.username = resolveProps(username);
Expand All @@ -95,6 +98,9 @@ public Server(
this.numQueryThreads = numQueryThreads;
this.local = local;
this.queries = copyOf(queries);

Preconditions.checkArgument(getPid() != null || getUrl() != null, "You must provide the pid or the [url|host and port]");
Preconditions.checkArgument(!(getPid() != null && getUrl() != null), "You must provide the pid OR the url, not both");
}

/**
Expand Down Expand Up @@ -148,10 +154,21 @@ public String getAlias() {
return this.alias;
}

/**
* Returns the pid of the local process jmxtrans will attach to
*
* @return pid of the java process
*/
public String getPid() {
if(this.pid == null) {
return null;
}
return this.pid;
}

public String getHost() {
if (host == null && url == null) {
// TODO: shouldn't we just return a null in this case ?
throw new IllegalStateException("host is null and url is null. Cannot construct host dynamically.");
return null;
}

if (host != null) {
Expand All @@ -168,7 +185,7 @@ public String getHost() {

public String getPort() {
if (port == null && url == null) {
throw new IllegalStateException("port is null and url is null. Cannot construct port dynamically.");
return null;
}
if (this.port != null) {
return port;
Expand Down Expand Up @@ -213,18 +230,13 @@ public ImmutableSet<Query> getQueries() {
public String getUrl() {
if (this.url == null) {
if ((this.host == null) || (this.port == null)) {
throw new RuntimeException("url is null and host or port is null. cannot construct url dynamically.");
return null;
}
return FRONT + this.host + ":" + this.port + BACK;
}
return this.url;
}

@JsonIgnore
public JMXServiceURL getJmxServiceURL() throws MalformedURLException {
return new JMXServiceURL(getUrl());
}

@JsonIgnore
public boolean isQueriesMultiThreaded() {
return (this.numQueryThreads != null) && (this.numQueryThreads > 0);
Expand All @@ -249,7 +261,14 @@ public String getCronExpression() {

@Override
public String toString() {
return "Server [host=" + this.host + ", port=" + this.port + ", url=" + this.url + ", cronExpression=" + this.cronExpression
String msg;
if(pid != null) {
msg = "pid=" + pid;
}
else {
msg = "host=" + this.host + ", port=" + this.port + ", url=" + this.url;
}
return "Server [" + msg + ", cronExpression=" + this.cronExpression
+ ", numQueryThreads=" + this.numQueryThreads + "]";
}

Expand All @@ -274,6 +293,7 @@ public boolean equals(Object o) {
return new EqualsBuilder()
.append(this.getHost(), other.getHost())
.append(this.getPort(), other.getPort())
.append(this.getPid(), other.getPid())
.append(this.getNumQueryThreads(), other.getNumQueryThreads())
.append(this.getCronExpression(), other.getCronExpression())
.append(this.getAlias(), other.getAlias())
Expand All @@ -287,6 +307,7 @@ public int hashCode() {
return new HashCodeBuilder(13, 21)
.append(this.getHost())
.append(this.getPort())
.append(this.getPid())
.append(this.getNumQueryThreads())
.append(this.getCronExpression())
.append(this.getAlias())
Expand Down Expand Up @@ -318,6 +339,7 @@ public static Builder builder(Server server) {
@NotThreadSafe
public static final class Builder {
private String alias;
private String pid;
private String host;
private String port;
private String username;
Expand All @@ -333,6 +355,7 @@ private Builder() {}

private Builder(Server server) {
this.alias = server.alias;
this.pid = server.pid;
this.host = server.host;
this.port = server.port;
this.username = server.username;
Expand All @@ -350,6 +373,11 @@ public Builder setAlias(String alias) {
return this;
}

public Builder setPid(String pid) {
this.pid = pid;
return this;
}

public Builder setHost(String host) {
this.host = host;
return this;
Expand Down Expand Up @@ -413,6 +441,7 @@ public Builder addQueries(Set<Query> queries) {
public Server build() {
return new Server(
alias,
pid,
host,
port,
username,
Expand Down

0 comments on commit c3c1c89

Please sign in to comment.