
com.googlecode.jmxtrans.model.Server Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmxtrans-core Show documentation
Show all versions of jmxtrans-core Show documentation
This module contains most of the core logic of JmxTrans.
/**
* The MIT License
* Copyright (c) 2010 JmxTrans team
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.googlecode.jmxtrans.model;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.name.Named;
import com.googlecode.jmxtrans.connections.JMXConnection;
import com.googlecode.jmxtrans.connections.JmxConnectionProvider;
import com.sun.tools.attach.VirtualMachine;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.apache.commons.pool.KeyedObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import javax.annotation.concurrent.ThreadSafe;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion.NON_NULL;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.ImmutableSet.copyOf;
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;
/**
* Represents a jmx server that we want to connect to. This also stores the
* queries that we want to execute against the server.
*
* @author jon
*/
@JsonSerialize(include = NON_NULL)
@JsonPropertyOrder(value = {
"alias",
"local",
"pid",
"host",
"port",
"username",
"password",
"cronExpression",
"numQueryThreads",
"protocolProviderPackages"
})
@Immutable
@ThreadSafe
@EqualsAndHashCode(exclude = {"queries", "pool", "outputWriters", "outputWriterFactories"})
@ToString(of = {"pid", "host", "port", "url", "cronExpression", "numQueryThreads"})
public class Server implements JmxConnectionProvider {
private static final String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";
private static final String FRONT = "service:jmx:rmi:///jndi/rmi://";
private static final String BACK = "/jmxrmi";
private static final Logger logger = LoggerFactory.getLogger(Server.class);
/**
* Some writers (GraphiteWriter) use the alias in generation of the unique
* key which references this server.
*/
@Getter private final String alias;
/** Returns the pid of the local process jmxtrans will attach to. */
@Getter private final String pid;
private final String host;
private final String port;
@Getter private final String username;
@Getter private final String password;
/**
* This is some obtuse shit for enabling weblogic support.
*
* http://download.oracle.com/docs/cd/E13222_01/wls/docs90/jmx/accessWLS.
* html
*
* You'd set this to: weblogic.management.remote
*/
@Getter private final String protocolProviderPackages;
private final String url;
/**
* Each server can set a cronExpression for the scheduler. If the
* cronExpression is null, then the job is run immediately and once.
* Otherwise, it is added to the scheduler for immediate execution and run
* according to the cronExpression.
*
* @deprecated use runPeriodSeconds instead
*/
@Deprecated
@Getter @Nullable private final String cronExpression;
@Getter @Nullable private final Integer runPeriodSeconds;
/** The number of query threads for this server. */
@Getter private final int numQueryThreads;
/**
* Whether the current local Java process should be used or not (useful for
* polling the embedded JVM when using JmxTrans inside a JVM to poll JMX
* stats and push them remotely)
*/
@Getter private final boolean local;
@Getter private final ImmutableSet queries;
@Nonnull @Getter private final Iterable outputWriters;
private final KeyedObjectPool pool;
@Nonnull @Getter private final ImmutableList outputWriterFactories;
@JsonCreator
public Server(
@JsonProperty("alias") String alias,
@JsonProperty("pid") String pid,
@JsonProperty("host") String host,
@JsonProperty("port") String port,
@JsonProperty("username") String username,
@JsonProperty("password") String password,
@JsonProperty("protocolProviderPackages") String protocolProviderPackages,
@JsonProperty("url") String url,
@JsonProperty("cronExpression") String cronExpression,
@JsonProperty("runPeriodSeconds") Integer runPeriodSeconds,
@JsonProperty("numQueryThreads") Integer numQueryThreads,
@JsonProperty("local") boolean local,
@JsonProperty("queries") List queries,
@JsonProperty("outputWriters") List outputWriters,
@JacksonInject @Named("mbeanPool") KeyedObjectPool pool) {
checkArgument(pid != null || url != null || host != null,
"You must provide the pid or the [url|host and port]");
checkArgument(!(pid != null && (url != null || host != null)),
"You must provide the pid OR the url, not both");
this.alias = alias;
this.pid = pid;
this.port = port;
this.username = username;
this.password = password;
this.protocolProviderPackages = protocolProviderPackages;
this.url = url;
this.cronExpression = cronExpression;
if (!isNullOrEmpty(cronExpression)) {
logger.warn("cronExpression is deprecated, please use runPeriodSeconds instead.");
}
this.runPeriodSeconds = runPeriodSeconds;
this.numQueryThreads = firstNonNull(numQueryThreads, 0);
this.local = local;
this.queries = copyOf(queries);
// when connecting in local, we cache the host after retrieving it from the network card
if(pid != null) {
try {
this.host = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
// should work, so just throw a runtime if it doesn't
throw new RuntimeException(e);
}
}
else {
this.host = host;
}
this.pool = pool;
this.outputWriterFactories = ImmutableList.copyOf(firstNonNull(outputWriters, ImmutableList.of()));
this.outputWriters = createOutputWriters(firstNonNull(outputWriters, ImmutableList.of()));
}
@Nonnull
private ImmutableList createOutputWriters(@Nonnull Iterable outputWriters) {
return FluentIterable
.from(outputWriters)
.transform(new Function() {
@Nullable
@Override
public OutputWriter apply(OutputWriterFactory input) {
return input.create();
}
})
.toList();
}
public Iterable execute(Query query) throws Exception {
JMXConnection jmxConnection = pool.borrowObject(this);
try {
ImmutableList.Builder results = ImmutableList.builder();
MBeanServerConnection connection = jmxConnection.getMBeanServerConnection();
for (ObjectName queryName : query.queryNames(connection)) {
results.addAll(query.fetchResults(connection, queryName));
}
pool.returnObject(this, jmxConnection);
return results.build();
} catch (Exception e) {
pool.invalidateObject(this, jmxConnection);
throw e;
}
}
/**
* Generates the proper username/password environment for JMX connections.
*/
@JsonIgnore
public ImmutableMap getEnvironment() {
if (getProtocolProviderPackages() != null && getProtocolProviderPackages().contains("weblogic")) {
ImmutableMap.Builder environment = ImmutableMap.builder();
if ((username != null) && (password != null)) {
environment.put(PROTOCOL_PROVIDER_PACKAGES, getProtocolProviderPackages());
environment.put(SECURITY_PRINCIPAL, username);
environment.put(SECURITY_CREDENTIALS, password);
}
return environment.build();
}
ImmutableMap.Builder environment = ImmutableMap.builder();
if ((username != null) && (password != null)) {
String[] credentials = new String[] {
username,
password
};
environment.put(JMXConnector.CREDENTIALS, credentials);
}
return environment.build();
}
/**
* Helper method for connecting to a Server. You need to close the resulting
* connection.
*/
@Override
@JsonIgnore
public JMXConnector getServerConnection() throws IOException {
JMXServiceURL url = new JMXServiceURL(getUrl());
return JMXConnectorFactory.connect(url, this.getEnvironment());
}
@Override
@JsonIgnore
public MBeanServer getLocalMBeanServer() {
// Getting the platform MBean server is cheap (expect for th first call) no need to cache it.
return ManagementFactory.getPlatformMBeanServer();
}
public String getHost() {
if (host == null && url == null) {
return null;
}
if (host != null) {
return host;
}
// removed the caching of the extracted host as it is a very minor
// optimization we should probably pre compute it in the builder and
// throw exception at construction if both url and host are set
// we might also be able to use java.net.URI to parse the URL, but I'm
// not familiar enough with JMX URLs to think of the test cases ...
return url.substring(url.lastIndexOf("//") + 2, url.lastIndexOf(':'));
}
public String getSource() {
if (alias != null) {
return alias;
}
return this.getHost();
}
public String getPort() {
if (port == null && url == null) {
return null;
}
if (this.port != null) {
return port;
}
return extractPortFromUrl(url);
}
private static String extractPortFromUrl(String url) {
String computedPort = url.substring(url.lastIndexOf(':') + 1);
if (computedPort.contains("/")) {
computedPort = computedPort.substring(0, computedPort.indexOf('/'));
}
return computedPort;
}
/**
* The jmx url to connect to. If null, it builds this from host/port with a
* standard configuration. Other JVM's may want to set this value.
*/
public String getUrl() {
if (this.url == null) {
if ((this.host == null) || (this.port == null)) {
return null;
}
return FRONT + this.host + ":" + this.port + BACK;
}
return this.url;
}
@JsonIgnore
public JMXServiceURL getJmxServiceURL() throws IOException {
if(this.pid != null) {
return JMXServiceURLFactory.extractJMXServiceURLFromPid(this.pid);
}
return new JMXServiceURL(getUrl());
}
@JsonIgnore
public boolean isQueriesMultiThreaded() {
return numQueryThreads > 0;
}
public void runOutputWriters(Query query, Iterable results) throws Exception {
for (OutputWriter writer : outputWriters) {
writer.doWrite(this, query, results);
}
logger.debug("Finished running outputWriters for query: {}", query);
}
/**
* Factory to create a JMXServiceURL from a pid. Inner class to prevent class
* loader issues when tools.jar isn't present.
*/
private static class JMXServiceURLFactory {
public static JMXServiceURL extractJMXServiceURLFromPid(String pid) throws IOException {
try {
VirtualMachine vm = VirtualMachine.attach(pid);
try {
String connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
if (connectorAddress == null) {
String agent = vm.getSystemProperties().getProperty("java.home") +
File.separator + "lib" + File.separator + "management-agent.jar";
vm.loadAgent(agent);
connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
}
return new JMXServiceURL(connectorAddress);
} finally {
vm.detach();
}
}
catch(Exception e) {
throw new IOException(e);
}
}
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(Server server) {
return new Builder(server);
}
@NotThreadSafe
@Accessors(chain = true)
public static final class Builder {
@Setter private String alias;
@Setter private String pid;
@Setter private String host;
@Setter private String port;
@Setter private String username;
@Setter private String password;
@Setter private String protocolProviderPackages;
@Setter private String url;
@Setter private String cronExpression;
@Setter private Integer runPeriodSeconds;
@Setter private Integer numQueryThreads;
@Setter private boolean local;
private final List outputWriters = new ArrayList();
private final List queries = new ArrayList();
@Setter private KeyedObjectPool pool;
private Builder() {}
private Builder(Server server) {
this.alias = server.alias;
this.pid = server.pid;
this.host = (server.pid != null ? null : server.host); // let the host be deduced in the constructor
this.port = server.port;
this.username = server.username;
this.password = server.password;
this.protocolProviderPackages = server.protocolProviderPackages;
this.url = server.url;
this.cronExpression = server.cronExpression;
this.runPeriodSeconds = server.runPeriodSeconds;
this.numQueryThreads = server.numQueryThreads;
this.local = server.local;
this.queries.addAll(server.queries);
this.pool = server.pool;
}
public Builder clearQueries() {
queries.clear();
return this;
}
public Builder addQuery(Query query) {
this.queries.add(query);
return this;
}
public Builder addQueries(Query... queries) {
this.queries.addAll(asList(queries));
return this;
}
public Builder addQueries(Set queries) {
this.queries.addAll(queries);
return this;
}
public Builder addOutputWriter(OutputWriterFactory outputWriter) {
this.outputWriters.add(outputWriter);
return this;
}
public Builder addOutputWriters(OutputWriterFactory... outputWriters) {
this.outputWriters.addAll(asList(outputWriters));
return this;
}
public Builder addOutputWriters(Collection outputWriters) {
this.outputWriters.addAll(outputWriters);
return this;
}
public Server build() {
return new Server(
alias,
pid,
host,
port,
username,
password,
protocolProviderPackages,
url,
cronExpression,
runPeriodSeconds,
numQueryThreads,
local,
queries,
outputWriters,
pool);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy