1
18 package net.bull.javamelody.internal.web;
19
20 import java.io.IOException;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Locale;
24 import java.util.Map;
25
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28
29 import net.bull.javamelody.SessionListener;
30 import net.bull.javamelody.internal.common.HttpParameter;
31 import net.bull.javamelody.internal.common.HttpPart;
32 import net.bull.javamelody.internal.common.I18N;
33 import net.bull.javamelody.internal.model.Action;
34 import net.bull.javamelody.internal.model.Collector;
35 import net.bull.javamelody.internal.model.CollectorServer;
36 import net.bull.javamelody.internal.model.Counter;
37 import net.bull.javamelody.internal.model.CounterRequestContext;
38 import net.bull.javamelody.internal.model.DatabaseInformations;
39 import net.bull.javamelody.internal.model.HeapHistogram;
40 import net.bull.javamelody.internal.model.JavaInformations;
41 import net.bull.javamelody.internal.model.JndiBinding;
42 import net.bull.javamelody.internal.model.MBeanNode;
43 import net.bull.javamelody.internal.model.MBeans;
44 import net.bull.javamelody.internal.model.ProcessInformations;
45 import net.bull.javamelody.internal.model.Range;
46 import net.bull.javamelody.internal.model.SamplingProfiler.SampledMethod;
47 import net.bull.javamelody.internal.model.SessionInformations;
48 import net.bull.javamelody.internal.model.VirtualMachine;
49 import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestAttribute;
50 import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestParameter;
51 import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestPart;
52 import net.bull.javamelody.internal.web.pdf.PdfOtherReport;
53 import net.bull.javamelody.internal.web.pdf.PdfReport;
54
55
59 class PdfController {
60 private static final String RANGE_KEY = "range";
61 private static final String JAVA_INFORMATIONS_LIST_KEY = "javaInformationsList";
62 private static final RequestToMethodMapper<PdfController> REQUEST_TO_METHOD_MAPPER = new RequestToMethodMapper<>(
63 PdfController.class);
64 private final HttpCookieManager httpCookieManager = new HttpCookieManager();
65 private final Collector collector;
66 private final CollectorServer collectorServer;
67 private PdfOtherReport pdfOtherReport;
68
69 PdfController(Collector collector, CollectorServer collectorServer) {
70 super();
71 assert collector != null;
72 this.collector = collector;
73 this.collectorServer = collectorServer;
74 }
75
76 void doPdf(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
77 List<JavaInformations> javaInformationsList) throws IOException {
78 addPdfContentTypeAndDisposition(httpRequest, httpResponse);
79 try {
80 final String part = HttpParameter.PART.getParameterFrom(httpRequest);
81 final Range range = httpCookieManager.getRange(httpRequest, httpResponse);
82 if (PdfReport.shouldUseEnglishInsteadOfUkrainian()) {
83 I18N.bindLocale(Locale.ENGLISH);
84 }
85 if (part == null) {
86 if (!isFromCollectorServer() && !collector.isStopped()) {
87
88
89
90
91 collector.collectLocalContextWithoutErrors();
92 }
93
94 final PdfReport pdfReport = new PdfReport(collector, isFromCollectorServer(),
95 javaInformationsList, range, httpResponse.getOutputStream());
96 pdfReport.toPdf();
97 } else {
98 this.pdfOtherReport = new PdfOtherReport(getApplication(),
99 httpResponse.getOutputStream());
100
101
102 httpRequest.setAttribute(RANGE_KEY, range);
103 httpRequest.setAttribute(JAVA_INFORMATIONS_LIST_KEY, javaInformationsList);
104 REQUEST_TO_METHOD_MAPPER.invoke(httpRequest, this);
105 }
106 } finally {
107 httpResponse.getOutputStream().flush();
108 }
109 }
110
111 @RequestPart(HttpPart.CURRENT_REQUESTS)
112 void doCurrentRequests(
113 @RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List<JavaInformations> javaInformationsList,
114 @RequestAttribute(RANGE_KEY) Range range) throws IOException {
115 final List<Counter> counters = collector.getRangeCountersToBeDisplayed(range);
116 final Map<JavaInformations, List<CounterRequestContext>> currentRequests;
117 if (!isFromCollectorServer()) {
118
119 assert collectorServer == null;
120 assert javaInformationsList.size() == 1;
121 final JavaInformations javaInformations = javaInformationsList.get(0);
122 final List<CounterRequestContext> rootCurrentContexts = collector
123 .getRootCurrentContexts(counters);
124 currentRequests = Collections.singletonMap(javaInformations, rootCurrentContexts);
125 } else {
126 currentRequests = collectorServer.collectCurrentRequests(collector.getApplication());
127 }
128 final long timeOfSnapshot = System.currentTimeMillis();
129 pdfOtherReport.writeAllCurrentRequestsAsPart(currentRequests, collector, counters,
130 timeOfSnapshot);
131 }
132
133 @RequestPart(HttpPart.THREADS)
134 void doThreads(
135 @RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List<JavaInformations> javaInformationsList)
136 throws IOException {
137 pdfOtherReport.writeThreads(javaInformationsList);
138 }
139
140 @RequestPart(HttpPart.RUNTIME_DEPENDENCIES)
141 void doRuntimeDependencies(@RequestAttribute(RANGE_KEY) Range range,
142 @RequestParameter(HttpParameter.COUNTER) String counterName) throws IOException {
143 final Counter counter = collector.getRangeCounter(range, counterName);
144 pdfOtherReport.writeRuntimeDependencies(counter, range);
145 }
146
147 @RequestPart(HttpPart.COUNTER_SUMMARY_PER_CLASS)
148 void doCounterSummaryPerClass(@RequestAttribute(RANGE_KEY) Range range,
149 @RequestParameter(HttpParameter.GRAPH) String requestId,
150 @RequestParameter(HttpParameter.COUNTER) String counterName) throws IOException {
151 final Counter counter = collector.getRangeCounter(range, counterName);
152 pdfOtherReport.writeCounterSummaryPerClass(collector, counter, requestId, range);
153 }
154
155 @RequestPart(HttpPart.GRAPH)
156 void doRequestAndGraphDetail(@RequestAttribute(RANGE_KEY) Range range,
157 @RequestParameter(HttpParameter.GRAPH) String requestId) throws IOException {
158 pdfOtherReport.writeRequestAndGraphDetail(collector, collectorServer, range, requestId);
159 }
160
161 @RequestPart(HttpPart.SESSIONS)
162 void doSessions() throws IOException {
163
164 Action.checkSystemActionsEnabled();
165 final List<SessionInformations> sessionsInformations;
166 if (!isFromCollectorServer()) {
167 sessionsInformations = SessionListener.getAllSessionsInformations();
168 } else {
169 sessionsInformations = collectorServer.collectSessionInformations(getApplication(),
170 null);
171 }
172 pdfOtherReport.writeSessionInformations(sessionsInformations);
173 }
174
175 @RequestPart(HttpPart.HOTSPOTS)
176 void doHotspots() throws IOException {
177
178 Action.checkSystemActionsEnabled();
179 if (!isFromCollectorServer()) {
180 final List<SampledMethod> hotspots = collector.getHotspots();
181 pdfOtherReport.writeHotspots(hotspots);
182 } else {
183 final List<SampledMethod> hotspots = collectorServer.collectHotspots(getApplication());
184 pdfOtherReport.writeHotspots(hotspots);
185 }
186 }
187
188 @RequestPart(HttpPart.PROCESSES)
189 void doProcesses() throws IOException {
190
191 Action.checkSystemActionsEnabled();
192 if (!isFromCollectorServer()) {
193 final List<ProcessInformations> processInformations = ProcessInformations
194 .buildProcessInformations();
195 pdfOtherReport.writeProcessInformations(processInformations);
196 } else {
197 final Map<String, List<ProcessInformations>> processesByTitle = collectorServer
198 .collectProcessInformations(getApplication());
199 pdfOtherReport.writeProcessInformations(processesByTitle);
200 }
201 }
202
203 @RequestPart(HttpPart.DATABASE)
204 void doDatabase(@RequestParameter(HttpParameter.REQUEST) String requestIndex) throws Exception {
205
206 Action.checkSystemActionsEnabled();
207 final int index = DatabaseInformations.parseRequestIndex(requestIndex);
208 final DatabaseInformations databaseInformations;
209 if (!isFromCollectorServer()) {
210 databaseInformations = new DatabaseInformations(index);
211 } else {
212 databaseInformations = collectorServer.collectDatabaseInformations(getApplication(),
213 index);
214 }
215 pdfOtherReport.writeDatabaseInformations(databaseInformations);
216 }
217
218 @RequestPart(HttpPart.JNDI)
219 void doJndi(@RequestParameter(HttpParameter.PATH) String path) throws Exception {
220
221 Action.checkSystemActionsEnabled();
222 final List<JndiBinding> jndiBindings;
223 if (!isFromCollectorServer()) {
224 jndiBindings = JndiBinding.listBindings(path);
225 } else {
226 jndiBindings = collectorServer.collectJndiBindings(getApplication(), path);
227 }
228 pdfOtherReport.writeJndi(jndiBindings, JndiBinding.normalizePath(path));
229 }
230
231 @RequestPart(HttpPart.MBEANS)
232 void doMBeans() throws Exception {
233
234 Action.checkSystemActionsEnabled();
235 if (!isFromCollectorServer()) {
236 final List<MBeanNode> nodes = MBeans.getAllMBeanNodes();
237 pdfOtherReport.writeMBeans(nodes);
238 } else {
239 final Map<String, List<MBeanNode>> allMBeans = collectorServer
240 .collectMBeans(getApplication());
241 pdfOtherReport.writeMBeans(allMBeans);
242 }
243 }
244
245 @RequestPart(HttpPart.HEAP_HISTO)
246 void doHeapHisto() throws Exception {
247
248 Action.checkSystemActionsEnabled();
249 final HeapHistogram heapHistogram;
250 if (!isFromCollectorServer()) {
251 heapHistogram = VirtualMachine.createHeapHistogram();
252 } else {
253 heapHistogram = collectorServer.collectHeapHistogram(getApplication());
254 }
255 pdfOtherReport.writeHeapHistogram(heapHistogram);
256 }
257
258 void addPdfContentTypeAndDisposition(HttpServletRequest httpRequest,
259 HttpServletResponse httpResponse) {
260 httpResponse.setContentType("application/pdf");
261 final String contentDisposition = encodeFileNameToContentDisposition(httpRequest,
262 PdfReport.getFileName(getApplication()));
263
264 httpResponse.addHeader("Content-Disposition",
265 contentDisposition.replace('\n', '_').replace('\r', '_'));
266 }
267
268 private String getApplication() {
269 return collector.getApplication();
270 }
271
272 private boolean isFromCollectorServer() {
273 return collectorServer != null;
274 }
275
276
284 private static String encodeFileNameToContentDisposition(HttpServletRequest httpRequest,
285 String fileName) {
286 assert fileName != null;
287 final String userAgent = httpRequest.getHeader("user-agent");
288 if (userAgent != null && userAgent.contains("MSIE")) {
289 return "attachment;filename=" + fileName;
290 }
291 return encodeFileNameToStandardContentDisposition(fileName);
292 }
293
294 private static String encodeFileNameToStandardContentDisposition(String fileName) {
295 final int length = fileName.length();
296 final StringBuilder sb = new StringBuilder(length + length / 4);
297
298
299 sb.append("attachment;filename=\"");
300 char c;
301 for (int i = 0; i < length; i++) {
302 c = fileName.charAt(i);
303 if (isEncodingNotNeeded(c)) {
304 sb.append(c);
305 } else {
306 sb.append('%');
307 if (c < 16) {
308 sb.append('0');
309 }
310 sb.append(Integer.toHexString(c));
311 }
312 }
313 sb.append('"');
314 return sb.toString();
315 }
316
317 private static boolean isEncodingNotNeeded(char c) {
318 return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '.'
319 || c == '_';
320 }
321 }
322