/*
* Copyright (c) 2009-present the original author or authors.
*
* Licensed 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 com.planet57.gshell.commands.standard;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import com.google.common.base.CharMatcher;
import com.planet57.gshell.branding.Branding;
import com.planet57.gshell.branding.License;
import com.planet57.gshell.command.Command;
import com.planet57.gshell.command.CommandContext;
import com.planet57.gshell.command.CommandActionSupport;
import com.planet57.gshell.util.io.IO;
import com.planet57.gshell.util.cli2.Argument;
import com.planet57.gshell.util.cli2.Option;
import com.planet57.gshell.util.i18n.I18N;
import com.planet57.gshell.util.i18n.MessageBundle;
import com.planet57.gshell.util.pref.Preference;
import com.planet57.gshell.util.pref.Preferences;
import javax.annotation.Nonnull;
//
// Based on info command from Apache Felix
//
/**
* Display shell information.
*
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @since 2.5
*/
@Command(name = "info", description = "Display information about the shell and environment")
@Preferences(path = "commands/info")
public class InfoAction
extends CommandActionSupport
{
private static final NumberFormat FMTI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH));
private static final NumberFormat FMTD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH));
private interface Messages
extends MessageBundle
{
@DefaultMessage("Shell")
String headerShell();
@DefaultMessage("Terminal")
String headerTerminal();
@DefaultMessage("JVM")
String headerJvm();
@DefaultMessage("Threads")
String headerThreads();
@DefaultMessage("Memory")
String headerMemory();
@DefaultMessage("Classes")
String headerClasses();
@DefaultMessage("Operating System")
String headerOs();
@DefaultMessage("License")
String headerLicense();
}
private static Messages messages = I18N.create(Messages.class);
public enum Section
{
SHELL,
TERMINAL,
JVM,
THREADS,
MEMORY,
CLASSES,
OS,
LICENSE,
}
@Preference
@Argument(description = "Display information about specific sections", token = "(section)*")
private List<Section> sections;
@Option(name = "a", longName = "all", description = "Display all sections")
private boolean all;
@Override
public Object execute(@Nonnull final CommandContext context) throws Exception {
IO io = context.getIo();
Branding branding = context.getShell().getBranding();
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
ThreadMXBean threads = ManagementFactory.getThreadMXBean();
MemoryMXBean mem = ManagementFactory.getMemoryMXBean();
ClassLoadingMXBean cl = ManagementFactory.getClassLoadingMXBean();
if (all) {
sections = Arrays.asList(Section.values());
}
if (sections == null) {
sections = Collections.singletonList(Section.SHELL);
}
StringWriter buff = new StringWriter();
PrintWriter writer = io.out;
// TODO: i18n
for (Section section : sections) {
switch (section) {
case SHELL:
printlnHeader(writer, messages.headerShell());
println(writer, "Display Name", branding.getDisplayName());
println(writer, "Program Name", branding.getProgramName());
println(writer, "License", branding.getLicense());
println(writer, "Version", branding.getVersion());
println(writer, "Home Dir", branding.getShellHomeDir());
println(writer, "Context Dir", branding.getShellContextDir());
println(writer, "User Home Dir", branding.getUserHomeDir());
println(writer, "User Context Dir", branding.getUserContextDir());
println(writer, "Script Extension", branding.getScriptExtension());
println(writer, "Preference Path", branding.getPreferencesBasePath());
println(writer, "Profile Script", branding.getProfileScriptName());
println(writer, "Interactive Script", branding.getInteractiveScriptName());
println(writer, "History File", branding.getHistoryFileName());
break;
case TERMINAL: {
printlnHeader(writer, messages.headerTerminal());
println(writer, "Name", io.terminal.getName());
println(writer, "Type", io.terminal.getType());
println(writer, "Echo", io.terminal.echo());
println(writer, "Height", io.terminal.getHeight());
println(writer, "Width", io.terminal.getWidth());
println(writer, "Mouse support", io.terminal.hasMouseSupport());
// TODO: attributes/capabilities?
break;
}
case JVM:
printlnHeader(writer, messages.headerJvm());
println(writer, "Java Virtual Machine", runtime.getVmName() + " version " + runtime.getVmVersion());
println(writer, "Vendor", runtime.getVmVendor());
println(writer, "Uptime", printDuration(runtime.getUptime()));
try {
println(writer, "Process CPU time", printDuration(getSunOsValueAsLong(os, "getProcessCpuTime") / 1000000));
}
catch (Throwable t) {
// ignore
}
println(writer, "Total compile time",
printDuration(ManagementFactory.getCompilationMXBean().getTotalCompilationTime()));
break;
case THREADS:
printlnHeader(writer, messages.headerThreads());
println(writer, "Live threads", Integer.toString(threads.getThreadCount()));
println(writer, "Daemon threads", Integer.toString(threads.getDaemonThreadCount()));
println(writer, "Peak", Integer.toString(threads.getPeakThreadCount()));
println(writer, "Total started", Long.toString(threads.getTotalStartedThreadCount()));
break;
case MEMORY:
printlnHeader(writer, messages.headerMemory());
println(writer, "Current heap size", printSizeInKb(mem.getHeapMemoryUsage().getUsed()));
println(writer, "Maximum heap size", printSizeInKb(mem.getHeapMemoryUsage().getMax()));
println(writer, "Committed heap size", printSizeInKb(mem.getHeapMemoryUsage().getCommitted()));
println(writer, "Pending objects", Integer.toString(mem.getObjectPendingFinalizationCount()));
for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
String val = "Name = '" + gc.getName() + "', Collections = " + gc.getCollectionCount() + ", Time = " +
printDuration(gc.getCollectionTime());
println(writer, "Garbage collector", val);
}
break;
case CLASSES:
printlnHeader(writer, messages.headerClasses());
println(writer, "Current classes loaded", printLong(cl.getLoadedClassCount()));
println(writer, "Total classes loaded", printLong(cl.getTotalLoadedClassCount()));
println(writer, "Total classes unloaded", printLong(cl.getUnloadedClassCount()));
break;
case OS:
printlnHeader(writer, messages.headerOs());
println(writer, "Name", os.getName() + " version " + os.getVersion());
println(writer, "Architecture", os.getArch());
println(writer, "Processors", Integer.toString(os.getAvailableProcessors()));
try {
println(writer, "Total physical memory", printSizeInKb(getSunOsValueAsLong(os, "getTotalPhysicalMemorySize")));
println(writer, "Free physical memory", printSizeInKb(getSunOsValueAsLong(os, "getFreePhysicalMemorySize")));
println(writer, "Committed virtual memory",
printSizeInKb(getSunOsValueAsLong(os, "getCommittedVirtualMemorySize")));
println(writer, "Total swap space", printSizeInKb(getSunOsValueAsLong(os, "getTotalSwapSpaceSize")));
println(writer, "Free swap space", printSizeInKb(getSunOsValueAsLong(os, "getFreeSwapSpaceSize")));
}
catch (Throwable t) {
// ignore
}
break;
case LICENSE:
License lic = branding.getLicense();
printlnHeader(writer, messages.headerLicense());
println(writer, "Name", lic.getName());
println(writer, "URL", lic.getUrl());
writer.println("----8<----");
writer.println(lic.getContent());
writer.println("---->8----");
break;
}
}
writer.flush();
// HACK: trip off any trailing whitespace
String info = CharMatcher.whitespace().trimTrailingFrom(buff.toString());
io.println(info);
return null;
}
private void printlnHeader(final PrintWriter writer, final String name) {
writer.format("@|bold,green %s|@%n", name);
}
private long getSunOsValueAsLong(final OperatingSystemMXBean os, final String name) throws Exception {
Method mth = os.getClass().getMethod(name);
return (Long) mth.invoke(os);
}
private String printLong(final long i) {
return FMTI.format(i);
}
private String printSizeInKb(final double size) {
return FMTI.format((long) (size / 1024)) + " kbytes";
}
private String printDuration(double uptime) {
uptime /= 1000;
if (uptime < 60) {
return FMTD.format(uptime) + " seconds";
}
uptime /= 60;
if (uptime < 60) {
long minutes = (long) uptime;
return FMTI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
}
uptime /= 60;
if (uptime < 24) {
long hours = (long) uptime;
long minutes = (long) ((uptime - hours) * 60);
String s = FMTI.format(hours) + (hours > 1 ? " hours" : " hour");
if (minutes != 0) {
s += " " + FMTI.format(minutes) + (minutes > 1 ? " minutes" : "minute");
}
return s;
}
uptime /= 24;
long days = (long) uptime;
long hours = (long) ((uptime - days) * 60);
String s = FMTI.format(days) + (days > 1 ? " days" : " day");
if (hours != 0) {
s += " " + FMTI.format(hours) + (hours > 1 ? " hours" : "hour");
}
return s;
}
private void println(final PrintWriter writer, final String name, final Object value) {
writer.format(" @|bold %s|@: %s%n", name, value);
}
}