Skip to content

Commit

Permalink
added: Display webapp dependencies in a page, including name, url, li…
Browse files Browse the repository at this point in the history
…cense
  • Loading branch information
evernat committed Mar 31, 2017
1 parent 388c1f9 commit e7607c1
Show file tree
Hide file tree
Showing 15 changed files with 539 additions and 89 deletions.
Expand Up @@ -26,6 +26,7 @@
import static net.bull.javamelody.HttpParameters.COUNTER_SUMMARY_PER_CLASS_PART;
import static net.bull.javamelody.HttpParameters.CURRENT_REQUESTS_PART;
import static net.bull.javamelody.HttpParameters.DATABASE_PART;
import static net.bull.javamelody.HttpParameters.DEPENDENCIES_PART;
import static net.bull.javamelody.HttpParameters.EXPLAIN_PLAN_PART;
import static net.bull.javamelody.HttpParameters.FORMAT_PARAMETER;
import static net.bull.javamelody.HttpParameters.GRAPH_PARAMETER;
Expand Down Expand Up @@ -209,7 +210,8 @@ private void doPart(HttpServletRequest req, HttpServletResponse resp, String app
MonitoringController monitoringController, String partParameter)
throws IOException, ServletException {
if (WEB_XML_PART.equalsIgnoreCase(partParameter)
|| POM_XML_PART.equalsIgnoreCase(partParameter)) {
|| POM_XML_PART.equalsIgnoreCase(partParameter)
|| DEPENDENCIES_PART.equalsIgnoreCase(partParameter)) {
noCache(resp);
doProxy(req, resp, application, partParameter);
} else if (SOURCE_PART.equalsIgnoreCase(partParameter)) {
Expand Down
Expand Up @@ -23,6 +23,7 @@
import static net.bull.javamelody.HttpParameters.COUNTER_SUMMARY_PER_CLASS_PART;
import static net.bull.javamelody.HttpParameters.CURRENT_REQUESTS_PART;
import static net.bull.javamelody.HttpParameters.DATABASE_PART;
import static net.bull.javamelody.HttpParameters.DEPENDENCIES_PART;
import static net.bull.javamelody.HttpParameters.FORMAT_PARAMETER;
import static net.bull.javamelody.HttpParameters.GRAPH_PARAMETER;
import static net.bull.javamelody.HttpParameters.GRAPH_PART;
Expand Down Expand Up @@ -141,6 +142,8 @@ private void doHtmlPart(HttpServletRequest httpRequest, String part, HtmlReport
} else if (SOURCE_PART.equalsIgnoreCase(part)) {
final String className = httpRequest.getParameter(CLASS_PARAMETER);
htmlReport.writeSource(className);
} else if (DEPENDENCIES_PART.equalsIgnoreCase(part)) {
htmlReport.writeDependencies();
} else {
doHtmlPartForSystemActions(httpRequest, part, htmlReport);
}
Expand Down
@@ -0,0 +1,109 @@
/*
* 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.io.IOException;
import java.io.Writer;
import java.util.Map;

/**
* Rapport html pour afficher la liste des dépendances dans WEB-INF/lib, avec noms, urls, licences.
* @author Emeric Vernat
*/
class HtmlDependenciesReport extends HtmlAbstractReport {
private final Map<String, MavenArtifact> dependencies;

HtmlDependenciesReport(Map<String, MavenArtifact> dependencies, Writer writer) {
super(writer);
assert dependencies != null;
this.dependencies = dependencies;
}

@Override
void toHtml() throws IOException {
writeBackLink();
writeln("<br/>");

if (dependencies.isEmpty()) {
writeln("#Aucune_dependance#");
return;
}
writeTitle("beans.png", getString("Dependencies"));
final HtmlTable table = new HtmlTable();
table.beginTable(getString("Dependencies"));
// table of dependencies inspired by Jenkins "/about"
write("<th>Artifact</th><th>#Nom#</th><th>Maven ID</th><th>#Licence#</th>");
for (final Map.Entry<String, MavenArtifact> entry : dependencies.entrySet()) {
final String jarFilename = entry.getKey();
final MavenArtifact dependency = entry.getValue();
table.nextRow();
writeDependency(jarFilename, dependency);
}
table.endTable();
writeln("<div align='right'>" + getFormattedString("nb_dependencies", dependencies.size())
+ "</div>");
}

private void writeBackLink() throws IOException {
writeln("<div class='noPrint'>");
writeln("<a href='javascript:history.back()'>");
writeln("<img src='?resource=action_back.png' alt='#Retour#'/> #Retour#</a>");
writeln("</div>");
}

private void writeDependency(String jarFilename, MavenArtifact dependency) throws IOException {
write("<td>");
writeDirectly(htmlEncodeButNotSpace(jarFilename));
write("</td><td>");
if (dependency == null) {
write("</td><td>");
write("</td><td>");
} else {
if (dependency.getName() != null) {
if (dependency.getUrl() == null) {
writeDirectly(htmlEncodeButNotSpace(dependency.getName()));
} else {
writeDirectly("<a href='" + urlEncode(dependency.getUrl()) + "'>"
+ htmlEncodeButNotSpace(dependency.getName()) + "</a>");
}
}
write("</td><td>");
writeDirectly(dependency.getGroupId() + ':' + dependency.getArtifactId() + ':' + "<b>"
+ dependency.getVersion() + "</b>");
write("</td><td>");
boolean firstLicense = true;
for (final Map.Entry<String, String> entry : dependency.getLicenseUrlsByName()
.entrySet()) {
final String licenseName = entry.getKey();
final String licenseUrl = entry.getValue();
if (!firstLicense) {
write("<br/>");
}
if (licenseUrl == null || !licenseUrl.startsWith("http")) {
writeDirectly(htmlEncodeButNotSpace(licenseName));
} else {
writeDirectly("<a href='" + urlEncode(licenseUrl) + "'>"
+ htmlEncodeButNotSpace(licenseName) + "</a>");
}
firstLicense = false;
}
}

write("</td>");
}
}
Expand Up @@ -40,7 +40,6 @@ class HtmlJavaInformationsReport extends HtmlAbstractReport {
private static final int FULL_BLOCKS = 10;
private static final double UNIT_SIZE = (MAX_VALUE - MIN_VALUE)
/ (FULL_BLOCKS * PARTIAL_BLOCKS);
private static int uniqueByPageSequence;

private final boolean noDatabase = Parameters.isNoDatabase();
private final DecimalFormat integerFormat = I18N.createIntegerFormat();
Expand Down Expand Up @@ -200,11 +199,10 @@ private void writeDetails(JavaInformations javaInformations, boolean repeatHost)

writeDatabaseVersionAndDataSourceDetails(javaInformations);

if (javaInformations.isDependenciesEnabled()) {
writeln("<tr><td valign='top'>#Dependencies#: </td><td>");
writeDependencies(javaInformations);
writeln(columnEnd);
}
writeln("<tr><td valign='top'>#Dependencies#: </td><td>");
writeDependencies(javaInformations);
writeln(columnEnd);

writeln("</table>");
}

Expand Down Expand Up @@ -341,21 +339,12 @@ private void writeMemoryInformations(MemoryInformations memoryInformations) thro
}

private void writeDependencies(JavaInformations javaInformations) throws IOException {
final int nbDependencies = javaInformations.getDependenciesList().size();
writeln(getFormattedString("nb_dependencies", nbDependencies));
if (nbDependencies > 0) {
uniqueByPageSequence++;
writeln(" ; &nbsp;&nbsp;&nbsp;");
writeShowHideLink("detailsDependencies" + uniqueByPageSequence, "#Details#");
if (javaInformations.doesPomXmlExists() && Parameters.isSystemActionsEnabled()) {
writeln("&nbsp;&nbsp;&nbsp;<a href='?part=pom.xml' class='noPrint'>");
writeln("<img src='?resource=xml.png' width='14' height='14' alt=\"#pom.xml#\"/> #pom.xml#</a>");
}
writeln("<br/>");
writeln("<div id='detailsDependencies" + uniqueByPageSequence
+ "' style='display: none;'><div>");
writeln(htmlEncodeButNotSpace(javaInformations.getDependencies()));
writeln("</div></div>");
writeln("<a href='?part=dependencies' class='noPrint'>");
writeln("<img src='?resource=beans.png' width='14' height='14' alt='#Dependencies#'> #Dependencies#</a>");
if (javaInformations.doesPomXmlExists() && Parameters.isSystemActionsEnabled()) {
writeln("&nbsp;&nbsp;&nbsp;");
writeln("<a href='?part=pom.xml' class='noPrint'>");
writeln("<img src='?resource=xml.png' width='14' height='14' alt=\"#pom.xml#\"/> #pom.xml#</a>");
}
}

Expand Down
Expand Up @@ -133,6 +133,13 @@ void writeSource(String className) throws IOException {
writeHtmlFooter();
}

void writeDependencies() throws IOException {
writeHtmlHeader();
final Map<String, MavenArtifact> dependencies = MavenArtifact.getWebappDependencies();
new HtmlDependenciesReport(dependencies, getWriter()).toHtml();
writeHtmlFooter();
}

void writeHtmlHeader() throws IOException {
writeHtmlHeader(false, false);
}
Expand Down
Expand Up @@ -76,6 +76,7 @@ final class HttpParameters {
static final String EXPLAIN_PLAN_PART = "explainPlan";
static final String APPLICATIONS_PART = "applications";
static final String SOURCE_PART = "source";
static final String DEPENDENCIES_PART = "dependencies";

/**
* Constructeur privé: pas d'instance.
Expand Down
Expand Up @@ -33,7 +33,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.sql.DataSource;
Expand Down Expand Up @@ -92,8 +91,6 @@ class JavaInformations implements Serializable { // NOPMD
private final List<CacheInformations> cacheInformationsList;
@SuppressWarnings("all")
private final List<JobInformations> jobInformationsList;
@SuppressWarnings("all")
private final List<String> dependenciesList;
private final boolean webXmlExists = localWebXmlExists;
private final boolean pomXmlExists = localPomXmlExists;

Expand Down Expand Up @@ -159,12 +156,10 @@ public int compare(JobInformations job1, JobInformations job2) {
serverInfo = null;
contextPath = null;
contextDisplayName = null;
dependenciesList = null;
} else {
serverInfo = servletContext.getServerInfo();
contextPath = Parameters.getContextPath(servletContext);
contextDisplayName = servletContext.getServletContextName();
dependenciesList = buildDependenciesList(servletContext);
}
startDate = START_DATE;
jvmArguments = buildJvmArguments();
Expand Down Expand Up @@ -461,30 +456,6 @@ private static String buildDataSourceDetails() {
return sb.toString();
}

private static List<String> buildDependenciesList(ServletContext servletContext) {
final String directory = "/WEB-INF/lib/";

final Set<String> dependencies;
try {
dependencies = servletContext.getResourcePaths(directory);
} catch (final Exception e) {
// Tomcat 8 can throw "IllegalStateException: The resources may not be accessed if they are not currently started"
// for some ServletContext states (issue 415)
return Collections.emptyList();
}
if (dependencies == null || dependencies.isEmpty()) {
return Collections.emptyList();
}
final List<String> result = new ArrayList<String>(dependencies.size());
for (final String dependency : dependencies) {
if (dependency.endsWith(".jar") || dependency.endsWith(".JAR")) {
result.add(dependency.substring(directory.length()));
}
}
Collections.sort(result);
return result;
}

private static boolean isSunOsMBean(OperatingSystemMXBean operatingSystem) {
// on ne teste pas operatingSystem instanceof com.sun.management.OperatingSystemMXBean
// car le package com.sun n'existe à priori pas sur une jvm tierce
Expand Down Expand Up @@ -682,32 +653,6 @@ int getCurrentlyExecutingJobCount() {
return result;
}

boolean isDependenciesEnabled() {
return dependenciesList != null && !dependenciesList.isEmpty();
}

List<String> getDependenciesList() {
if (dependenciesList != null) {
return Collections.unmodifiableList(dependenciesList);
}
return Collections.emptyList();
}

String getDependencies() {
if (!isDependenciesEnabled()) {
return null;
}
final StringBuilder sb = new StringBuilder();
for (final String dependency : getDependenciesList()) {
sb.append(dependency);
sb.append(",\n");
}
if (sb.length() >= 2) {
sb.delete(sb.length() - 2, sb.length());
}
return sb.toString();
}

boolean isStackTraceEnabled() {
for (final ThreadInformations threadInformations : threadInformationsList) {
final List<StackTraceElement> stackTrace = threadInformations.getStackTrace();
Expand Down

0 comments on commit e7607c1

Please sign in to comment.