Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.htrace.core; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; import java.util.Locale; import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * <p>The HTrace tracer ID.</p> * * <p>HTrace tracer IDs are created from format strings. * Format strings contain variables which the TracerId class will * replace with the correct values at runtime.</p> * * <ul> * <li>%{tname}: the tracer name supplied when creating the Tracer.</li> * <li>%{pname}: the process name obtained from the JVM.</li> * <li>%{ip}: will be replaced with an ip address.</li> * <li>%{pid}: the numerical process ID from the operating system.</li> * </ul> * * <p>For example, the string "%{pname}/%{ip}" will be replaced with something * like: DataNode/192.168.0.1, assuming that the process' name is DataNode * and its IP address is 192.168.0.1.</p> * * ID strings can contain backslashes as escapes. * For example, "\a" will map to "a". "\%{ip}" will map to the literal * string "%{ip}", not the IP address. A backslash itself can be escaped by a * preceding backslash. */ public final class TracerId { private static final Log LOG = LogFactory.getLog(TracerId.class); /** * The configuration key to use for process id */ public static final String TRACER_ID_KEY = "tracer.id"; /** * The default tracer ID to use if no other ID is configured. */ private static final String DEFAULT_TRACER_ID = "%{tname}/%{ip}"; private final String tracerName; private final String tracerId; public TracerId(HTraceConfiguration conf, String tracerName) { this.tracerName = tracerName; String fmt = conf.get(TRACER_ID_KEY, DEFAULT_TRACER_ID); StringBuilder bld = new StringBuilder(); StringBuilder varBld = null; boolean escaping = false; int varSeen = 0; for (int i = 0, len = fmt.length(); i < len; i++) { char c = fmt.charAt(i); if (c == '\\') { if (!escaping) { escaping = true; continue; } } switch (varSeen) { case 0: if (c == '%') { if (!escaping) { varSeen = 1; continue; } } escaping = false; varSeen = 0; bld.append(c); break; case 1: if (c == '{') { if (!escaping) { varSeen = 2; varBld = new StringBuilder(); continue; } } escaping = false; varSeen = 0; bld.append("%").append(c); break; default: if (c == '}') { if (!escaping) { String var = varBld.toString(); bld.append(processShellVar(var)); varBld = null; varSeen = 0; continue; } } escaping = false; varBld.append(c); varSeen++; break; } } if (varSeen > 0) { LOG.warn("Unterminated process ID substitution variable at the end " + "of format string " + fmt); } this.tracerId = bld.toString(); if (LOG.isTraceEnabled()) { LOG.trace("ProcessID(fmt=" + fmt + "): computed process ID of \"" + this.tracerId + "\""); } } private String processShellVar(String var) { if (var.equals("tname")) { return tracerName; } else if (var.equals("pname")) { return getProcessName(); } else if (var.equals("ip")) { return getBestIpString(); } else if (var.equals("pid")) { return Long.valueOf(getOsPid()).toString(); } else { LOG.warn("unknown ProcessID variable " + var); return ""; } } static String getProcessName() { String cmdLine = System.getProperty("sun.java.command"); if (cmdLine != null && !cmdLine.isEmpty()) { String fullClassName = cmdLine.split("\\s+")[0]; String[] classParts = fullClassName.split("\\."); cmdLine = classParts[classParts.length - 1]; } return (cmdLine == null || cmdLine.isEmpty()) ? "Unknown" : cmdLine; } /** * <p>Get the best IP address that represents this node.</p> * * This is complicated since nodes can have multiple network interfaces, * and each network interface can have multiple IP addresses. What we're * looking for here is an IP address that will serve to identify this node * to HTrace. So we prefer site-local addresess (i.e. private ones on the * LAN) to publicly routable interfaces. If there are multiple addresses * to choose from, we select the one which comes first in textual sort * order. This should ensure that we at least consistently call each node * by a single name. */ static String getBestIpString() { Enumeration<NetworkInterface> ifaces; try { ifaces = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { LOG.error("Error getting network interfaces", e); return "127.0.0.1"; } TreeSet<String> siteLocalCandidates = new TreeSet<String>(); TreeSet<String> candidates = new TreeSet<String>(); while (ifaces.hasMoreElements()) { NetworkInterface iface = ifaces.nextElement(); for (Enumeration<InetAddress> addrs = iface.getInetAddresses(); addrs.hasMoreElements();) { InetAddress addr = addrs.nextElement(); if (!addr.isLoopbackAddress()) { if (addr.isSiteLocalAddress()) { siteLocalCandidates.add(addr.getHostAddress()); } else { candidates.add(addr.getHostAddress()); } } } } if (!siteLocalCandidates.isEmpty()) { return siteLocalCandidates.first(); } if (!candidates.isEmpty()) { return candidates.first(); } return "127.0.0.1"; } /** * <p>Get the process id from the operating system.</p> * * Unfortunately, there is no simple method to get the process id in Java. * The approach we take here is to use the shell method (see * {TracerId#getOsPidFromShellPpid}) unless we are on Windows, where the * shell is not available. On Windows, we use * {TracerId#getOsPidFromManagementFactory}, which depends on some * undocumented features of the JVM, but which doesn't require a shell. */ static long getOsPid() { if ((System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH)).contains("windows")) { return getOsPidFromManagementFactory(); } else { return getOsPidFromShellPpid(); } } /** * <p>Get the process ID by executing a shell and printing the PPID (parent * process ID).</p> * * This method of getting the process ID doesn't depend on any undocumented * features of the virtual machine, and should work on almost any UNIX * operating system. */ private static long getOsPidFromShellPpid() { Process p = null; StringBuilder sb = new StringBuilder(); try { p = new ProcessBuilder("/usr/bin/env", "sh", "-c", "echo $PPID").redirectErrorStream(true).start(); BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line.trim()); } int exitVal = p.waitFor(); if (exitVal != 0) { throw new IOException("Process exited with error code " + Integer.valueOf(exitVal).toString()); } } catch (InterruptedException e) { LOG.error("Interrupted while getting operating system pid from " + "the shell.", e); return 0L; } catch (IOException e) { LOG.error("Error getting operating system pid from the shell.", e); return 0L; } finally { if (p != null) { p.destroy(); } } try { return Long.parseLong(sb.toString()); } catch (NumberFormatException e) { LOG.error("Error parsing operating system pid from the shell.", e); return 0L; } } /** * <p>Get the process ID by looking at the name of the managed bean for the * runtime system of the Java virtual machine.</p> * * Although this is undocumented, in the Oracle JVM this name is of the form * [OS_PROCESS_ID]@[HOSTNAME]. */ private static long getOsPidFromManagementFactory() { try { return Long.parseLong(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); } catch (NumberFormatException e) { LOG.error("Failed to get the operating system process ID from the name " + "of the managed bean for the JVM.", e); return 0L; } } public String get() { return tracerId; } }