001/*
002 * Copyright (C) 2015-2023 The Prometheus jmx_exporter Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package io.prometheus.jmx;
018
019import io.prometheus.client.CollectorRegistry;
020import io.prometheus.client.exporter.HTTPServer;
021import io.prometheus.client.hotspot.DefaultExports;
022import io.prometheus.jmx.common.http.ConfigurationException;
023import io.prometheus.jmx.common.http.HTTPServerFactory;
024import java.io.File;
025import java.lang.instrument.Instrumentation;
026import java.net.InetSocketAddress;
027import java.util.regex.Matcher;
028import java.util.regex.Pattern;
029
030public class JavaAgent {
031
032    public static final String CONFIGURATION_REGEX =
033            "^(?:((?:[\\w.-]+)|(?:\\[.+])):)?"
034                    + // host name, or ipv4, or ipv6 address in brackets
035                    "(\\d{1,5}):"
036                    + // port
037                    "(.+)"; // config file
038
039    static HTTPServer server;
040
041    public static void agentmain(String agentArgument, Instrumentation instrumentation)
042            throws Exception {
043        premain(agentArgument, instrumentation);
044    }
045
046    public static void premain(String agentArgument, Instrumentation instrumentation)
047            throws Exception {
048        // Bind to all interfaces by default (this includes IPv6).
049        String host = "0.0.0.0";
050
051        try {
052            Config config = parseConfig(agentArgument, host);
053
054            new BuildInfoCollector().register();
055            new JmxCollector(new File(config.file), JmxCollector.Mode.AGENT).register();
056            DefaultExports.initialize();
057
058            server =
059                    new HTTPServerFactory()
060                            .createHTTPServer(
061                                    config.socket,
062                                    CollectorRegistry.defaultRegistry,
063                                    true,
064                                    new File(config.file));
065        } catch (ConfigurationException e) {
066            System.err.println("Configuration Exception : " + e.getMessage());
067            System.exit(1);
068        } catch (IllegalArgumentException e) {
069            System.err.println(
070                    "Usage: -javaagent:/path/to/JavaAgent.jar=[host:]<port>:<yaml configuration"
071                            + " file> "
072                            + e.getMessage());
073            System.exit(1);
074        }
075    }
076
077    /**
078     * Parse the Java Agent configuration. The arguments are typically specified to the JVM as a
079     * javaagent as {@code -javaagent:/path/to/agent.jar=<CONFIG>}. This method parses the {@code
080     * <CONFIG>} portion.
081     *
082     * @param args provided agent args
083     * @param ifc default bind interface
084     * @return configuration to use for our application
085     */
086    public static Config parseConfig(String args, String ifc) {
087        Pattern pattern = Pattern.compile(CONFIGURATION_REGEX);
088
089        Matcher matcher = pattern.matcher(args);
090        if (!matcher.matches()) {
091            throw new IllegalArgumentException("Malformed arguments - " + args);
092        }
093
094        String givenHost = matcher.group(1);
095        String givenPort = matcher.group(2);
096        String givenConfigFile = matcher.group(3);
097
098        int port = Integer.parseInt(givenPort);
099
100        InetSocketAddress socket;
101        if (givenHost != null && !givenHost.isEmpty()) {
102            socket = new InetSocketAddress(givenHost, port);
103        } else {
104            socket = new InetSocketAddress(ifc, port);
105            givenHost = ifc;
106        }
107
108        return new Config(givenHost, port, givenConfigFile, socket);
109    }
110
111    static class Config {
112        String host;
113        int port;
114        String file;
115        InetSocketAddress socket;
116
117        Config(String host, int port, String file, InetSocketAddress socket) {
118            this.host = host;
119            this.port = port;
120            this.file = file;
121            this.socket = socket;
122        }
123    }
124}