1
18 package net.bull.javamelody.internal.web.html;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.Reader;
25 import java.io.StringWriter;
26 import java.io.Writer;
27 import java.nio.charset.StandardCharsets;
28 import java.security.CodeSource;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.List;
33 import java.util.zip.ZipEntry;
34 import java.util.zip.ZipFile;
35
36 import net.bull.javamelody.internal.common.HttpPart;
37 import net.bull.javamelody.internal.model.MavenArtifact;
38
39
43 class HtmlSourceReport extends HtmlAbstractReport {
44 private static final File JDK_SRC_FILE = getJdkSrcFile();
45 private static final List<String> TOMCAT_PACKAGES = Collections.unmodifiableList(
46 Arrays.asList("org.apache.tomcat", "org.apache.catalina", "org.apache.coyote",
47 "org.apache.jasper", "org.apache.el", "org.apache.juli", "org.apache.naming"));
48
49 private final String source;
50
51 HtmlSourceReport(String className, Writer writer) throws IOException {
52 super(writer);
53 this.source = getSource(normalizeClassName(className));
54 }
55
56 private static String normalizeClassName(String className) {
57 String temp = className;
58 while (temp.lastIndexOf('$') != -1) {
59
60 temp = temp.substring(0, temp.lastIndexOf('$'));
61 }
62 if (temp.lastIndexOf('/') != -1) {
63
64 temp = temp.substring(temp.lastIndexOf('/') + 1);
65 }
66 return temp;
67 }
68
69 private String getSource(String className) throws IOException {
70 final Class<?> clazz;
71 try {
72 clazz = Class.forName(className);
73 } catch (final ClassNotFoundException e) {
74 return null;
75 }
76
77 final CodeSource codeSource = clazz.getProtectionDomain().getCodeSource();
78 final String sourceFilePath = clazz.getName().replace('.', '/') + ".java";
79 if (clazz.getName().startsWith("java.")
80 || clazz.getName().startsWith("javax.") && codeSource == null) {
81 if (JDK_SRC_FILE != null) {
82 return getSourceFromZip(sourceFilePath, JDK_SRC_FILE);
83 }
84 } else if (codeSource != null) {
85 final File sourceJarFile = MavenArtifact.getSourceJarFile(codeSource.getLocation());
86 if (sourceJarFile != null) {
87 return getSourceFromZip(sourceFilePath, sourceJarFile);
88 }
89 }
90 if (clazz.getName().startsWith("org.apache.")) {
91 for (final String tomcatPackage : TOMCAT_PACKAGES) {
92 if (clazz.getName().startsWith(tomcatPackage + '.')) {
93 final File tomcatSrcFile = MavenArtifact.getTomcatSrcZipFile();
94 if (tomcatSrcFile != null) {
95 assert tomcatSrcFile.getName().endsWith(".zip");
96 final String entryName = tomcatSrcFile.getName().substring(0,
97 tomcatSrcFile.getName().length() - ".zip".length()) + "/java/"
98 + sourceFilePath;
99 return getSourceFromZip(entryName, tomcatSrcFile);
100 }
101 }
102 }
103 }
104 return null;
105 }
106
107 private static File getJdkSrcFile() {
108 File file = new File(System.getProperty("java.home"));
109 if ("jre".equalsIgnoreCase(file.getName())) {
110 file = file.getParentFile();
111 }
112 File srcZipFile = new File(file, "src.zip");
113 if (srcZipFile.exists()) {
114 return srcZipFile;
115 }
116
117 srcZipFile = new File(file, "lib/src.zip");
118 if (srcZipFile.exists()) {
119 return srcZipFile;
120 }
121 return null;
122 }
123
124 private String getSourceFromZip(String entryName, File srcJarFile) throws IOException {
125 try (ZipFile zipFile = new ZipFile(srcJarFile)) {
126 ZipEntry entry = zipFile.getEntry(entryName);
127 if (entry == null) {
128
129 final Enumeration<? extends ZipEntry> entries = zipFile.entries();
130 while (entries.hasMoreElements()) {
131 entry = entries.nextElement();
132 if (entry.getName().endsWith(entryName)) {
133 break;
134 }
135 }
136 if (!entry.getName().endsWith(entryName)) {
137 return null;
138 }
139 }
140 final StringWriter writer = new StringWriter();
141 try (InputStream inputStream = zipFile.getInputStream(entry)) {
142 try (Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
143 final char[] chars = new char[1024];
144 int read = reader.read(chars);
145 while (read != -1) {
146 writer.write(chars, 0, read);
147 read = reader.read(chars);
148 }
149 }
150 }
151 return writer.toString();
152 }
153 }
154
155 @Override
156 void toHtml() throws IOException {
157 if (source != null) {
158 String html = JavaHTMLizer.htmlize(source);
159 html = JavaHTMLizer.addLineNumbers(html);
160 writeDirectly("<code>");
161 writeDirectly(html);
162 writeDirectly("</code>");
163 } else {
164 write("#source_not_found#");
165 }
166 }
167
168 static String htmlEncodeStackTraceElement(String element) {
169 if (element.endsWith(")") && !element.endsWith("(Native Method)")
170 && !element.endsWith("(Unknown Source)")) {
171 final int index3 = element.lastIndexOf(':');
172 final int index2 = element.lastIndexOf('(');
173 final int index1 = element.lastIndexOf('.', index2);
174 final int index0 = element.lastIndexOf(' ', index1);
175 if (index1 > index0 && index2 != -1 && index3 > index2) {
176 final String classNameEncoded = urlEncode(element.substring(index0 + 1, index1));
177 return htmlEncodeButNotSpace(element.substring(0, index2 + 1)) + "<a href='?part="
178 + HttpPart.SOURCE + "&class=" + classNameEncoded + '#'
179 + urlEncode(element.substring(index3 + 1, element.length() - 1))
180 + "' class='lightwindow' type='external' title='" + classNameEncoded + "'>"
181 + htmlEncode(element.substring(index2 + 1, element.length() - 1)) + "</a>)";
182 }
183 }
184 return htmlEncodeButNotSpace(element);
185 }
186
187 static String htmlEncodeStackTraceElementAndTabs(String element) {
188 return htmlEncodeStackTraceElement(element).replace("\t",
189 " ");
190 }
191
192 static String addLinkToClassName(String className) {
193 String cleanClassName = className;
194 if (cleanClassName.endsWith("[]")) {
195 cleanClassName = cleanClassName.substring(0, cleanClassName.length() - 2);
196 }
197 return "<a href='?part=" + HttpPart.SOURCE + "&class=" + cleanClassName
198 + "' class='lightwindow' type='external' title='" + cleanClassName + "'>"
199 + className + "</a>";
200 }
201 }
202