/*
* Copyright 2008-2017 by Emeric Vernat
*
* This file is part of Java Melody.
*
* 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 net.bull.javamelody;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.List;
import javax.imageio.ImageIO;
import com.lowagie.text.Anchor;
import com.lowagie.text.BadElementException;
import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPTable;
/**
* Partie du rapport pdf pour les informations systèmes sur le serveur.
* @author Emeric Vernat
*/
class PdfJavaInformationsReport extends PdfAbstractReport {
private static final String DIVIDE = " / ";
private static final String BAR_SEPARATOR = " ";
private final boolean noDatabase = Parameters.isNoDatabase();
private final DecimalFormat decimalFormat = I18N.createPercentFormat();
private final DecimalFormat integerFormat = I18N.createIntegerFormat();
private final List<JavaInformations> javaInformationsList;
private final Font cellFont = PdfFonts.TABLE_CELL.getFont();
private final Font boldCellFont = PdfFonts.BOLD_CELL.getFont();
private PdfPTable currentTable;
static final class Bar {
// constantes pour l'affichage d'une barre avec pourcentage
private static final double MIN_VALUE = 0;
private static final double MAX_VALUE = 100;
private static final int PARTIAL_BLOCKS = 5;
private static final int FULL_BLOCKS = 10;
private static final double UNIT_SIZE = (MAX_VALUE - MIN_VALUE)
/ (FULL_BLOCKS * PARTIAL_BLOCKS);
private final double percentValue;
private final BufferedImage image;
private final Graphics graphics;
private int x; // initialisé à 0
private final boolean alertOnHighUsage;
private Bar(double percentValue, boolean alertOnHighUsage) {
super();
this.percentValue = percentValue;
this.alertOnHighUsage = alertOnHighUsage;
if (alertOnHighUsage) {
this.image = new BufferedImage(130, 14, BufferedImage.TYPE_INT_ARGB);
} else {
this.image = new BufferedImage(106, 10, BufferedImage.TYPE_INT_ARGB);
}
this.graphics = image.getGraphics();
}
static BufferedImage toBar(double percentValue) throws IOException {
final Bar bar = new Bar(percentValue, false);
bar.draw();
bar.graphics.dispose();
return bar.image;
}
static BufferedImage toBarWithAlert(double percentValue) throws IOException {
final Bar bar = new Bar(percentValue,
percentValue >= JavaInformations.HIGH_USAGE_THRESHOLD_IN_PERCENTS);
bar.draw();
bar.graphics.dispose();
return bar.image;
}
// méthode inspirée de VisualScoreTag dans LambdaProbe/JStripe (Licence GPL)
private void draw() throws IOException { // NOPMD
assert x == 0;
final double myPercent = Math.max(Math.min(percentValue, 100d), 0d);
final int fullBlockCount = (int) Math.floor(myPercent / (UNIT_SIZE * PARTIAL_BLOCKS));
final int partialBlockIndex = (int) Math
.floor((myPercent - fullBlockCount * UNIT_SIZE * PARTIAL_BLOCKS) / UNIT_SIZE);
addImage(getBarImage(fullBlockCount > 0 || partialBlockIndex > 0 ? "a" : "a0"));
final BufferedImage fullBody = getBarImage(String.valueOf(PARTIAL_BLOCKS));
for (int i = 0; i < fullBlockCount; i++) {
addImage(fullBody);
}
if (partialBlockIndex > 0) {
final BufferedImage partialBody = getBarImage(String.valueOf(partialBlockIndex));
addImage(partialBody);
}
final int emptyBlocks = FULL_BLOCKS - fullBlockCount - (partialBlockIndex > 0 ? 1 : 0);
final BufferedImage emptyBody = getBarImage(String.valueOf(0));
for (int i = 0; i < emptyBlocks; i++) {
addImage(emptyBody);
}
addImage(getBarImage(fullBlockCount == FULL_BLOCKS ? "b" : "b0"));
if (alertOnHighUsage) {
x += 8;
graphics.drawImage(getImage("alert.png"), x, 0, null);
}
}
private void addImage(BufferedImage img) {
graphics.drawImage(img, x, alertOnHighUsage ? 4 : 0, null);
x += img.getWidth();
}
private static BufferedImage getBarImage(String key) throws IOException {
return getImage("bar/rb_" + key + ".gif");
}
private static BufferedImage getImage(String fileName) throws IOException {
// ici, ne pas utiliser Toolkit.createImage et surtout pas ImageIcon (sur un serveur)
return ImageIO.read(Bar.class.getResource(Parameters.getResourcePath(fileName)));
}
}
PdfJavaInformationsReport(List<JavaInformations> javaInformationsList, Document document) {
super(document);
assert javaInformationsList != null;
this.javaInformationsList = javaInformationsList;
}
@Override
void toPdf() throws DocumentException, IOException {
for (final JavaInformations javaInformations : javaInformationsList) {
currentTable = createJavaInformationsTable();
writeSummary(javaInformations);
addCell("");
addCell("");
addToDocument(currentTable);
}
}
private static PdfPTable createJavaInformationsTable() throws DocumentException {
final PdfPTable table = new PdfPTable(2);
table.setHorizontalAlignment(Element.ALIGN_LEFT);
table.setWidthPercentage(100);
table.setWidths(new int[] { 2, 8 });
table.getDefaultCell().setBorder(0);
return table;
}
private void writeSummary(JavaInformations javaInformations)
throws BadElementException, IOException {
addCell(getString("Host") + ':');
currentTable.addCell(new Phrase(javaInformations.getHost(), boldCellFont));
addCell(getString("memoire_utilisee") + ':');
final MemoryInformations memoryInformations = javaInformations.getMemoryInformations();
final long usedMemory = memoryInformations.getUsedMemory();
final long maxMemory = memoryInformations.getMaxMemory();
final Phrase memoryPhrase = new Phrase(integerFormat.format(usedMemory / 1024 / 1024) + ' '
+ getString("Mo") + DIVIDE + integerFormat.format(maxMemory / 1024 / 1024) + ' '
+ getString("Mo") + BAR_SEPARATOR, cellFont);
final Image memoryImage = Image.getInstance(
Bar.toBarWithAlert(memoryInformations.getUsedMemoryPercentage()), null);
memoryImage.scalePercent(50);
memoryPhrase.add(new Chunk(memoryImage, 0, 0));
currentTable.addCell(memoryPhrase);
if (javaInformations.getSessionCount() >= 0) {
addCell(getString("nb_sessions_http") + ':');
addCell(integerFormat.format(javaInformations.getSessionCount()));
}
addCell(getString("nb_threads_actifs") + "\n(" + getString("Requetes_http_en_cours")
+ "):");
addCell(integerFormat.format(javaInformations.getActiveThreadCount()));
if (!noDatabase) {
addCell(getString("nb_connexions_actives") + ':');
addCell(integerFormat.format(javaInformations.getActiveConnectionCount()));
addCell(getString("nb_connexions_utilisees") + "\n(" + getString("ouvertes") + "):");
final int usedConnectionCount = javaInformations.getUsedConnectionCount();
final int maxConnectionCount = javaInformations.getMaxConnectionCount();
if (maxConnectionCount <= 0) {
addCell(integerFormat.format(usedConnectionCount));
} else {
final Phrase usedConnectionCountPhrase = new Phrase(
integerFormat.format(usedConnectionCount) + DIVIDE
+ integerFormat.format(maxConnectionCount) + BAR_SEPARATOR,
cellFont);
final Image usedConnectionCountImage = Image.getInstance(
Bar.toBarWithAlert(javaInformations.getUsedConnectionPercentage()), null);
usedConnectionCountImage.scalePercent(50);
usedConnectionCountPhrase.add(new Chunk(usedConnectionCountImage, 0, 0));
currentTable.addCell(usedConnectionCountPhrase);
}
}
if (javaInformations.getSystemLoadAverage() >= 0) {
addCell(getString("Charge_systeme") + ':');
addCell(decimalFormat.format(javaInformations.getSystemLoadAverage()));
}
if (javaInformations.getSystemCpuLoad() >= 0) {
addCell(getString("systemCpuLoad") + ':');
final Phrase systemCpuLoadPhrase = new Phrase(
decimalFormat.format(javaInformations.getSystemCpuLoad()) + BAR_SEPARATOR,
cellFont);
final Image systemCpuLoadImage = Image
.getInstance(Bar.toBarWithAlert(javaInformations.getSystemCpuLoad()), null);
systemCpuLoadImage.scalePercent(50);
systemCpuLoadPhrase.add(new Chunk(systemCpuLoadImage, 0, 0));
currentTable.addCell(systemCpuLoadPhrase);
}
}
void writeInformationsDetails() throws DocumentException, IOException {
for (final JavaInformations javaInformations : javaInformationsList) {
currentTable = createJavaInformationsTable();
writeSummary(javaInformations);
writeDetails(javaInformations);
addToDocument(currentTable);
}
}
private void writeDetails(JavaInformations javaInformations)
throws BadElementException, IOException {
addCell(getString("OS") + ':');
final Phrase osPhrase = new Phrase("", cellFont);
final String osIconName = HtmlJavaInformationsReport
.getOSIconName(javaInformations.getOS());
final String separator = " ";
if (osIconName != null) {
final Image osImage = PdfDocumentFactory.getImage("servers/" + osIconName);
osImage.scalePercent(40);
osPhrase.add(new Chunk(osImage, 0, 0));
osPhrase.add(separator);
}
osPhrase.add(javaInformations.getOS() + " (" + javaInformations.getAvailableProcessors()
+ ' ' + getString("coeurs") + ')');
currentTable.addCell(osPhrase);
addCell(getString("Java") + ':');
addCell(javaInformations.getJavaVersion());
addCell(getString("JVM") + ':');
final Phrase jvmVersionPhrase = new Phrase(javaInformations.getJvmVersion(), cellFont);
if (javaInformations.getJvmVersion().contains("Client")) {
jvmVersionPhrase.add(separator);
final Image alertImage = PdfDocumentFactory.getImage("alert.png");
alertImage.scalePercent(50);
jvmVersionPhrase.add(new Chunk(alertImage, 0, -2));
}
currentTable.addCell(jvmVersionPhrase);
addCell(getString("PID") + ':');
addCell(javaInformations.getPID());
if (javaInformations.getUnixOpenFileDescriptorCount() >= 0) {
writeFileDescriptorCounts(javaInformations);
}
final String serverInfo = javaInformations.getServerInfo();
if (serverInfo != null) {
writeServerInfo(serverInfo);
addCell(getString("Contexte_webapp") + ':');
addCell(javaInformations.getContextPath());
}
addCell(getString("Demarrage") + ':');
addCell(I18N.createDateAndTimeFormat().format(javaInformations.getStartDate()));
addCell(getString("Arguments_JVM") + ':');
addCell(javaInformations.getJvmArguments());
if (javaInformations.getSessionCount() >= 0) {
addCell(getString("httpSessionsMeanAge") + ':');
addCell(integerFormat.format(javaInformations.getSessionMeanAgeInMinutes()));
}
writeTomcatInformations(javaInformations.getTomcatInformationsList());
addCell(getString("Gestion_memoire") + ':');
writeMemoryInformations(javaInformations.getMemoryInformations());
if (javaInformations.getFreeDiskSpaceInTemp() >= 0) {
// on considère que l'espace libre sur le disque dur est celui sur la partition du répertoire temporaire
addCell(getString("Free_disk_space") + ':');
addCell(integerFormat.format(javaInformations.getFreeDiskSpaceInTemp() / 1024 / 1024)
+ ' ' + getString("Mo"));
}
writeDatabaseVersionAndDataSourceDetails(javaInformations);
addCell("");
addCell("");
}
private void writeServerInfo(String serverInfo) throws BadElementException, IOException {
addCell(getString("Serveur") + ':');
final Phrase serverInfoPhrase = new Phrase("", cellFont);
final String applicationServerIconName = HtmlJavaInformationsReport
.getApplicationServerIconName(serverInfo);
if (applicationServerIconName != null) {
final Image applicationServerImage = PdfDocumentFactory
.getImage("servers/" + applicationServerIconName);
applicationServerImage.scalePercent(40);
serverInfoPhrase.add(new Chunk(applicationServerImage, 0, 0));
serverInfoPhrase.add(" ");
}
serverInfoPhrase.add(serverInfo);
currentTable.addCell(serverInfoPhrase);
}
private void writeFileDescriptorCounts(JavaInformations javaInformations)
throws BadElementException, IOException {
final long unixOpenFileDescriptorCount = javaInformations.getUnixOpenFileDescriptorCount();
final long unixMaxFileDescriptorCount = javaInformations.getUnixMaxFileDescriptorCount();
addCell(getString("nb_fichiers") + ':');
final Phrase fileDescriptorCountPhrase = new Phrase(
integerFormat.format(unixOpenFileDescriptorCount) + DIVIDE
+ integerFormat.format(unixMaxFileDescriptorCount) + BAR_SEPARATOR,
cellFont);
final Image fileDescriptorCountImage = Image.getInstance(
Bar.toBarWithAlert(javaInformations.getUnixOpenFileDescriptorPercentage()), null);
fileDescriptorCountImage.scalePercent(50);
fileDescriptorCountPhrase.add(new Chunk(fileDescriptorCountImage, 0, 0));
currentTable.addCell(fileDescriptorCountPhrase);
}
private void writeDatabaseVersionAndDataSourceDetails(JavaInformations javaInformations) {
if (!noDatabase && javaInformations.getDataBaseVersion() != null) {
addCell(getString("Base_de_donnees") + ':');
addCell(javaInformations.getDataBaseVersion());
}
if (javaInformations.getDataSourceDetails() != null) {
addCell(getString("DataSource_jdbc") + ':');
addCell(javaInformations.getDataSourceDetails());
addCell("");
final Anchor anchor = new Anchor("DataSource reference", PdfFonts.BLUE.getFont());
anchor.setName("DataSource reference");
anchor.setReference(
"http://commons.apache.org/dbcp/apidocs/org/apache/commons/dbcp/BasicDataSource.html");
currentTable.addCell(anchor);
}
}
private void writeTomcatInformations(List<TomcatInformations> tomcatInformationsList)
throws BadElementException, IOException {
for (final TomcatInformations tomcatInformations : tomcatInformationsList) {
if (tomcatInformations.getRequestCount() <= 0) {
continue;
}
addCell("Tomcat " + tomcatInformations.getName() + ':');
// rq: on n'affiche pas pour l'instant getCurrentThreadCount
final int currentThreadsBusy = tomcatInformations.getCurrentThreadsBusy();
final String equal = " = ";
final Phrase phrase = new Phrase(getString("busyThreads") + equal
+ integerFormat.format(currentThreadsBusy) + DIVIDE
+ integerFormat.format(tomcatInformations.getMaxThreads()) + BAR_SEPARATOR,
cellFont);
final Image threadsImage = Image.getInstance(Bar.toBarWithAlert(
100d * currentThreadsBusy / tomcatInformations.getMaxThreads()), null);
threadsImage.scalePercent(50);
phrase.add(new Chunk(threadsImage, 0, 0));
phrase.add(new Chunk('\n' + getString("bytesReceived") + equal
+ integerFormat.format(tomcatInformations.getBytesReceived()) + '\n'
+ getString("bytesSent") + equal
+ integerFormat.format(tomcatInformations.getBytesSent()) + '\n'
+ getString("requestCount") + equal
+ integerFormat.format(tomcatInformations.getRequestCount()) + '\n'
+ getString("errorCount") + equal
+ integerFormat.format(tomcatInformations.getErrorCount()) + '\n'
+ getString("processingTime") + equal
+ integerFormat.format(tomcatInformations.getProcessingTime()) + '\n'
+ getString("maxProcessingTime") + equal
+ integerFormat.format(tomcatInformations.getMaxTime())));
currentTable.addCell(phrase);
}
}
private void writeMemoryInformations(MemoryInformations memoryInformations)
throws BadElementException, IOException {
addCell(memoryInformations.getMemoryDetails().replace(" Mo", ' ' + getString("Mo")));
final long usedPermGen = memoryInformations.getUsedPermGen();
if (usedPermGen > 0) {
// perm gen est à 0 sous jrockit
final long maxPermGen = memoryInformations.getMaxPermGen();
addCell(getString("Memoire_Perm_Gen") + ':');
if (maxPermGen > 0) {
final Phrase permGenPhrase = new Phrase(
integerFormat.format(usedPermGen / 1024 / 1024) + ' ' + getString("Mo")
+ DIVIDE + integerFormat.format(maxPermGen / 1024 / 1024) + ' '
+ getString("Mo") + BAR_SEPARATOR,
cellFont);
final Image permGenImage = Image.getInstance(
Bar.toBarWithAlert(memoryInformations.getUsedPermGenPercentage()), null);
permGenImage.scalePercent(50);
permGenPhrase.add(new Chunk(permGenImage, 0, 0));
currentTable.addCell(permGenPhrase);
} else {
addCell(integerFormat.format(usedPermGen / 1024 / 1024) + ' ' + getString("Mo"));
}
}
}
private void addCell(String string) {
currentTable.addCell(new Phrase(string, cellFont));
}
}