/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Mar 24, 2008
*/
package com.bigdata;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.util.Collections;
import java.util.Date;
import java.util.Formatter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.system.SystemUtil;
import com.bigdata.util.Depends;
import com.bigdata.util.InnerCause;
import com.bigdata.util.Depends.Dependency;
import com.bigdata.util.config.LogUtil;
import com.bigdata.util.config.NicUtil;
/**
* Class has a static method which writes a copyright banner on stdout once per
* JVM. This method is invoked from several core classes in order to ensure that
* the copyright banner is always written out on bigdata startup.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public class Banner {
/**
* The logger for <em>this</em> class.
*/
private static final Logger log = Logger.getLogger("com.bigdata.Banner");
private final static AtomicBoolean didBanner = new AtomicBoolean(false);
private final static String fullyQualifiedHostName;
/**
* Returns fully qualified host name from static initialization.
*
* Moved from AbstractStatisticsCollector for BLZG-1497.
*
* @return
*/
public static String getFullyqualifiedhostname() {
return fullyQualifiedHostName;
}
/**
* Environment variables understood by the {@link Banner} class.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public interface Options {
/**
* This may be used to suppress the banner text.
*/
String QUIET = "com.bigdata.Banner.quiet";
/**
* Suppress the installation of a default
* {@link UncaughtExceptionHandler}.
*/
String NOCATCH = "com.bigdata.Banner.nocatch";
/**
* This may be used to disable JMX MBeans which self-report on the log4j
* properties.
*/
String LOG4J_MBEANS_DISABLE = "com.bigdata.jmx.log4j.disable";
}
/**
* This static code block is responsible obtaining the canonical hostname.
*
* @see <a href="http://trac.blazegraph.com/ticket/886" >Provide workaround for
* bad reverse DNS setups</a>
*/
static {
String s = System.getProperty(BigdataStatics.HOSTNAME);
if (s != null) {
// Trim whitespace.
s = s.trim();
}
if (s != null && s.length() != 0) {
log.warn("Hostname override: hostname=" + s);
} else {
try {
/*
* Note: This should be the host *name* NOT an IP address of a
* preferred Ethernet adaptor.
*/
s = InetAddress.getLocalHost().getCanonicalHostName();
// s = NicUtil.getIpAddress("default.nic", "default", false);
} catch (Throwable t) {
// fall back
log.error("Could not resolve name for host: " + t);
s = NicUtil.getIpAddressByLocalHost();
log.warn("Falling back to " + s);
}
}
fullyQualifiedHostName = s;
}
/**
* Display the banner, dependencies, etc.
*/
static public void banner() {
if(didBanner.compareAndSet(false/*expect*/, true/*update*/)) {
final boolean nocatch = Boolean.getBoolean(Options.NOCATCH);
if (!nocatch) {
/*
* Set a logger for any uncaught exceptions.
*/
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
public void uncaughtException(final Thread t,
final Throwable e) {
log.error("Uncaught exception in thread", e);
}
});
}
if (!quiet) {
final StringBuilder sb = new StringBuilder(banner);
// Add in the dependencies.
{
int maxNameLen = 0, maxProjectLen = 0, maxLicenseLen = 0;
for (Dependency dep : com.bigdata.util.Depends.depends()) {
if (dep.getName().length() > maxNameLen)
maxNameLen = dep.getName().length();
if (dep.projectURL().length() > maxProjectLen)
maxProjectLen = dep.projectURL().length();
if (dep.licenseURL().length() > maxLicenseLen)
maxLicenseLen = dep.licenseURL().length();
}
maxNameLen = Math.min(80, maxNameLen);
maxProjectLen = Math.min(80, maxProjectLen);
maxLicenseLen = Math.min(80, maxLicenseLen);
final Formatter f = new Formatter(sb);
try {
final String fmt1 = "" //
+ "%-" + maxNameLen + "s"//
// + " %-" + maxProjectLen + "s" //
+ " %-" + maxLicenseLen + "s"//
+ "\n";
f.format(fmt1, "Dependency", "License");
for (Dependency dep : com.bigdata.util.Depends.depends()) {
f.format(fmt1, //
dep.getName(),//
// dep.projectURL(),//
dep.licenseURL()//
);
}
} finally {
f.close();
}
}
System.out.println(sb);
}
/*
* Note: I have modified this to test for disabled registration and
* to use reflection in order to decouple the JMX dependency for
* anzo.
*/
if (!Boolean.getBoolean(Options.LOG4J_MBEANS_DISABLE)) {
try {
final Class<?> cls = Class
.forName("com.bigdata.jmx.JMXLog4jMBeanUtil");
final Method m = cls.getMethod("registerLog4jMBeans",
new Class[] {});
// Optionally register a log4j MBean.
m.invoke(null/* obj */);
// JMXLog4jMBeanUtil.registerLog4jMBeans();
} catch (Throwable t) {
log.info("Problem registering log4j mbean?", t);
}
}
}
}
/**
* If logging is not configured for [com.bigdata] then we set a default log
* level @ WARN. This is critical for good performance.
*/
private static void setDefaultLogLevel(final boolean quiet) {
final Logger defaultLog =
LogUtil.getLog4jLogger("com.bigdata");
// Logger.getLogger("com.bigdata");
if (defaultLog.getLevel() == null) {
/*
* Since there is no default for com.bigdata, default to WARN.
*/
try {
defaultLog.setLevel(Level.WARN);
if (!quiet)
log.warn("Defaulting log level to WARN: "
+ defaultLog.getName());
} catch (Throwable t) {
/*
* Note: The SLF4J bridge can cause a NoSuchMethodException to
* be thrown out of Logger.setLevel(). We trap this exception
* and log a message @ ERROR. It is critical that bigdata
* logging is properly configured as logging at INFO for
* com.bigdata will cause a tremendous loss of performance.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/362
*/
if (InnerCause.isInnerCause(t, NoSuchMethodError.class)) {
log.error("Unable to raise the default log level to WARN."
+ " Logging is NOT properly configured."
+ " Severe performance penalty will result.");
} else {
// Something else that we are not expecting.
throw new RuntimeException(t);
}
}
} // if(log.getLevel() == null)
}
/**
* An interface which declares the keys for the map returned by
* {@link Banner#getBuildInfo()} .
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan
* Thompson</a>
*/
public interface BuildInfoMeta {
/** The bigdata release version. */
static final String buildVersion = "buildVersion";
/** The source code revision. */
static final String svnRevision = "svnRevision";
/** The source code repository URL for the branch. */
static final String svnURL = "svnURL";
/** The timestamp of the build. */
static final String buildTimestamp = "buildTimestamp";
/** The username that performed the build. */
static final String buildUser = "buildUser";
/** The hostname on which the build was performed. */
static final String buildHost = "buildHost";
/** The OS architecture on which the build was performed. */
static final String osArch = "osArch";
/** The OS name on which the build was performed. */
static final String osName = "osName";
/** The OS version on which the build was performed. */
static final String osVersion = "osVersion";
/** The string representing the git build branch. */
static final String gitBranch = "gitBranch";
/**
* The string representing the git build commit.
*
* This is the output of git git rev-parse --verify HEAD
*
* See BLZG-1688
*
*/
static final String gitCommit = "gitCommit";
}
/**
* Method used to discover and report on the bigdata build information. A
* <code>com.bigdata.BuildInfo</code> class is built when the JAR is
* created. However, it may not be present when running under an IDE from
* the source code and, therefore, there MUST NOT be any compile time
* references to the <code>com.bigdata.BuildInfo</code> class. This method
* uses reflection to avoid a compile time dependency.
* <p>
* Note: This method works fine. However, people running from an IDE will
* observe <em>stale</em> data from old <code>com.bigdata.BuildInfo</code>
* class files left from a previous build of a JAR. This makes the
* information good for deployed versions of the JAR but potentially
* misleading when people are running under an IDE.
*
* @return Build info metadata iff available.
*
* @see BuildInfoMeta
*/
public synchronized static Map<String,String> getBuildInfo() {
final String BUILD_INFO = "com.bigdata.BuildInfo";
if (buildInfoRef.get() == null) {
final Map<String,String> map = getStaticVariables(BUILD_INFO);
// set at most once.
buildInfoRef.compareAndSet(null/* expect */,
Collections.unmodifiableMap(map)/* update */);
}
return buildInfoRef.get();
}
/**
* Utility class to get the static string variables for a given class name.
*
* @param className
* @return
*/
public synchronized static Map<String,String> getStaticVariables(final String className) {
final Map<String,String> map = new LinkedHashMap<String, String>();
try {
final Class<?> cls = Class.forName(className);
for (Field f : cls.getFields()) {
final String name = f.getName();
final int mod = f.getModifiers();
if (!Modifier.isStatic(mod))
continue;
if (!Modifier.isPublic(mod))
continue;
if (!Modifier.isFinal(mod))
continue;
try {
final Object obj = f.get(null/* staticField */);
if (obj != null) {
map.put(name, "" + obj);
}
} catch (IllegalArgumentException e) {
log.warn("Field: " + name + " : " + e);
} catch (IllegalAccessException e) {
log.warn("Field: " + name + " : " + e);
}
}
} catch (ClassNotFoundException e) {
log.warn("Not found: " + className);
} catch (Throwable t) {
log.error(t, t);
}
return map;
}
private final static AtomicReference<Map<String, String>> buildInfoRef = new AtomicReference<Map<String, String>>();
private final static String getBuildString() {
if (getBuildInfo().isEmpty())
return "";
final StringBuilder s = new StringBuilder();
s.append("\nbuildVersion=" + getBuildInfo().get(BuildInfoMeta.buildVersion));
//BLZG-1688
if(getBuildInfo().get(BuildInfoMeta.gitCommit) != null) {
s.append("\ngitCommit=" + getBuildInfo().get(BuildInfoMeta.gitCommit));
}
return s.toString();
}
/**
* Attempts to return the build version (aka the release version) from the
* <code>com.bigdata.BuildInfo</code> class. This class is generated by
* <code>build.xml</code> and is NOT available from the IDE. It is correct
* discovered using reflection.
*
* @return Build version if available and <code>null</code> otherwise.
*
* @see #getBuildInfo()
*/
public final static String getVersion() {
if (getBuildInfo().isEmpty()) {
return null;
}
return getBuildInfo().get(BuildInfoMeta.buildVersion);
}
/**
* Return the banner.
*/
public static String getBanner() {
return banner;
}
/**
* Outputs the banner and exits.
*
* @param args
* Ignored.
*/
public static void main(final String[] args) {
banner();
}
private static final String banner;
private static final boolean quiet;
static {
quiet = Boolean.getBoolean(Options.QUIET);
/*
* If logging is not configured for [com.bigdata] then we set a
* default log level @ WARN. This is critical for good performance.
*/
setDefaultLogLevel(quiet);
banner =
"\nBlazeGraph(TM) Graph Engine"+//
"\n"+//
"\n Flexible"+//
"\n Reliable"+//
"\n Affordable"+//
"\n Web-Scale Computing for the Enterprise"+//
"\n"+//
"\nCopyright SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved."+//
"\n"+//
"\n"+fullyQualifiedHostName+//
"\n"+new Date()+//
"\n"+SystemUtil.operatingSystem() + "/" + SystemUtil.osVersion()
+ " " + SystemUtil.architecture() + //
"\n"+SystemUtil.cpuInfo() + " #CPU="+SystemUtil.numProcessors() +//
"\n"+System.getProperty("java.vendor")+" "+System.getProperty("java.version")+
"\nfreeMemory="+Runtime.getRuntime().freeMemory()+//
getBuildString()+ // Note: Will add its own newline if non-empty.
"\n\n"
;
}
}