1
18 package net.bull.javamelody.internal.web;
19
20 import java.io.IOException;
21 import java.io.Serializable;
22 import java.sql.SQLException;
23 import java.text.DateFormat;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.LinkedHashMap;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.TreeMap;
33
34 import javax.management.JMException;
35 import javax.naming.NamingException;
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
38
39 import net.bull.javamelody.JdbcWrapper;
40 import net.bull.javamelody.SessionListener;
41 import net.bull.javamelody.internal.common.HttpParameter;
42 import net.bull.javamelody.internal.common.HttpPart;
43 import net.bull.javamelody.internal.common.I18N;
44 import net.bull.javamelody.internal.model.Action;
45 import net.bull.javamelody.internal.model.Collector;
46 import net.bull.javamelody.internal.model.Counter;
47 import net.bull.javamelody.internal.model.CounterRequest;
48 import net.bull.javamelody.internal.model.CounterRequestAggregation;
49 import net.bull.javamelody.internal.model.CounterRequestContext;
50 import net.bull.javamelody.internal.model.DatabaseInformations;
51 import net.bull.javamelody.internal.model.HsErrPid;
52 import net.bull.javamelody.internal.model.JRobin;
53 import net.bull.javamelody.internal.model.JavaInformations;
54 import net.bull.javamelody.internal.model.JndiBinding;
55 import net.bull.javamelody.internal.model.MBeans;
56 import net.bull.javamelody.internal.model.MavenArtifact;
57 import net.bull.javamelody.internal.model.Period;
58 import net.bull.javamelody.internal.model.ProcessInformations;
59 import net.bull.javamelody.internal.model.Range;
60 import net.bull.javamelody.internal.model.TransportFormat;
61 import net.bull.javamelody.internal.model.VirtualMachine;
62 import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestAttribute;
63 import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestHeader;
64 import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestParameter;
65 import net.bull.javamelody.internal.web.RequestToMethodMapper.RequestPart;
66
67
71 public class SerializableController {
72 private static final String RANGE_KEY = "range";
73 private static final String JAVA_INFORMATIONS_LIST_KEY = "javaInformationsList";
74 private static final String MESSAGE_FOR_REPORT_KEY = "messageForReport";
75 private static final RequestToMethodMapper<SerializableController> REQUEST_TO_METHOD_MAPPER = new RequestToMethodMapper<>(
76 SerializableController.class);
77 private final Collector collector;
78
79 public SerializableController(Collector collector) {
80 super();
81 assert collector != null;
82 this.collector = collector;
83 }
84
85 void doSerializable(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
86 Serializable serializable) throws IOException {
87
88
89
90 final String format = HttpParameter.FORMAT.getParameterFrom(httpRequest);
91 final TransportFormat transportFormat = TransportFormat.valueOfIgnoreCase(format);
92
93 transportFormat.checkDependencies();
94 httpResponse.setContentType(transportFormat.getMimeType());
95 final String fileName = "JavaMelody_" + getApplication().replace(' ', '_').replace("/", "")
96 + '_' + I18N.getCurrentDate().replace('/', '_') + '.' + transportFormat.getCode();
97 final String contentDisposition = "inline;filename=" + fileName;
98
99 httpResponse.addHeader("Content-Disposition",
100 contentDisposition.replace('\n', '_').replace('\r', '_'));
101
102 transportFormat.writeSerializableTo(serializable, httpResponse.getOutputStream());
103 }
104
105 public Serializable createSerializable(HttpServletRequest httpRequest,
106 List<JavaInformations> javaInformationsList, String messageForReport)
107 throws IOException {
108 final Range range = getRangeForSerializable(httpRequest);
109 if (HttpParameter.PART.getParameterFrom(httpRequest) != null) {
110 httpRequest.setAttribute(JAVA_INFORMATIONS_LIST_KEY, javaInformationsList);
111 httpRequest.setAttribute(RANGE_KEY, range);
112 httpRequest.setAttribute(MESSAGE_FOR_REPORT_KEY, messageForReport);
113
114 return (Serializable) REQUEST_TO_METHOD_MAPPER.invokeAndReturn(httpRequest, this);
115 } else if (HttpParameter.JMX_VALUE.getParameterFrom(httpRequest) != null) {
116
117 Action.checkSystemActionsEnabled();
118 final String jmxValue = HttpParameter.JMX_VALUE.getParameterFrom(httpRequest);
119 return MBeans.getConvertedAttributes(jmxValue);
120 }
121
122 return createDefaultSerializable(javaInformationsList, range, messageForReport);
123 }
124
125 @RequestPart(HttpPart.THREADS)
126 Serializable createThreadsSerializable(
127 @RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List<JavaInformations> javaInformationsList) {
128 return new ArrayList<>(javaInformationsList.get(0).getThreadInformationsList());
129 }
130
131 @RequestPart(HttpPart.COUNTER_SUMMARY_PER_CLASS)
132 Serializable createCounterSummaryPerClassSerializable(@RequestAttribute(RANGE_KEY) Range range,
133 @RequestParameter(HttpParameter.COUNTER) String counterName,
134 @RequestParameter(HttpParameter.GRAPH) String requestId) throws IOException {
135 final Counter counter = collector.getRangeCounter(range, counterName).clone();
136 final List<CounterRequest> requestList = new CounterRequestAggregation(counter)
137 .getRequestsAggregatedOrFilteredByClassName(requestId);
138 return new ArrayList<>(requestList);
139 }
140
141 @RequestPart(HttpPart.CURRENT_REQUESTS)
142 Serializable createCurrentRequestsSerializable(
143 @RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List<JavaInformations> javaInformationsList) {
144 final Map<JavaInformations, List<CounterRequestContext>> result = new HashMap<>();
145 result.put(javaInformationsList.get(0), getCurrentRequests());
146 return (Serializable) result;
147 }
148
149 @RequestPart(HttpPart.DEFAULT_WITH_CURRENT_REQUESTS)
150 @SuppressWarnings("unchecked")
151 Serializable createDefaultWithCurrentRequestsSerializable(
152 @RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List<JavaInformations> javaInformationsList,
153 @RequestAttribute(MESSAGE_FOR_REPORT_KEY) String messageForReport,
154 @RequestAttribute(RANGE_KEY) Range range) throws IOException {
155 final List<Serializable> result = new ArrayList<>(
156 (List<Serializable>) createDefaultSerializable(javaInformationsList, range,
157 messageForReport));
158 result.addAll(getCurrentRequests());
159 return (Serializable) result;
160 }
161
162 @RequestPart(HttpPart.JVM)
163 Serializable createJvmSerializable(
164 @RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List<JavaInformations> javaInformationsList) {
165 return new ArrayList<>(javaInformationsList);
166 }
167
168 @RequestPart(HttpPart.SESSIONS)
169 Serializable createSessionsSerializable(
170 @RequestParameter(HttpParameter.SESSION_ID) String sessionId) {
171
172 Action.checkSystemActionsEnabled();
173 if (sessionId == null) {
174 return new ArrayList<>(SessionListener.getAllSessionsInformations());
175 }
176 return SessionListener.getSessionInformationsBySessionId(sessionId);
177 }
178
179 @RequestPart(HttpPart.HOTSPOTS)
180 Serializable createHotspotsSerializable() {
181
182 Action.checkSystemActionsEnabled();
183 return new ArrayList<>(collector.getHotspots());
184 }
185
186 @RequestPart(HttpPart.HEAP_HISTO)
187 Serializable createHeapHistoSerializable() throws Exception {
188
189 Action.checkSystemActionsEnabled();
190 return VirtualMachine.createHeapHistogram();
191 }
192
193 @RequestPart(HttpPart.PROCESSES)
194 Serializable createProcessesSerializable() throws IOException {
195
196 Action.checkSystemActionsEnabled();
197 return new ArrayList<>(ProcessInformations.buildProcessInformations());
198 }
199
200 @RequestPart(HttpPart.JNDI)
201 Serializable createJndiSerializable(@RequestParameter(HttpParameter.PATH) String path)
202 throws NamingException {
203
204 Action.checkSystemActionsEnabled();
205 return new ArrayList<>(JndiBinding.listBindings(path));
206 }
207
208 @RequestPart(HttpPart.MBEANS)
209 Serializable createMBeansSerializable() throws JMException {
210
211 Action.checkSystemActionsEnabled();
212 return new ArrayList<>(MBeans.getAllMBeanNodes());
213 }
214
215 @RequestPart(HttpPart.DEPENDENCIES)
216 Serializable createDependenciesSerializable() throws IOException {
217
218 Action.checkSystemActionsEnabled();
219 final Map<String, MavenArtifact> webappDependencies = MavenArtifact.getWebappDependencies();
220 for (final MavenArtifact dependency : webappDependencies.values()) {
221 if (dependency != null) {
222
223 dependency.getLicenseUrlsByName();
224 }
225 }
226 return new TreeMap<>(webappDependencies);
227 }
228
229 @RequestPart(HttpPart.LAST_VALUE)
230 Serializable createLastValueSerializable(@RequestParameter(HttpParameter.GRAPH) String graph)
231 throws IOException {
232 if (graph != null) {
233 final JRobin jrobin = collector.getJRobin(graph);
234 final double lastValue;
235 if (jrobin == null) {
236 lastValue = -1;
237 } else {
238 lastValue = jrobin.getLastValue();
239 }
240 return lastValue;
241 }
242 final Collection<JRobin> jrobins = collector.getDisplayedCounterJRobins();
243 final Map<String, Double> lastValues = new LinkedHashMap<>(jrobins.size());
244 for (final JRobin jrobin : jrobins) {
245 lastValues.put(jrobin.getName(), jrobin.getLastValue());
246 }
247 return (Serializable) lastValues;
248 }
249
250 @RequestPart(HttpPart.DATABASE)
251 Serializable createDatabaseSerializable(
252 @RequestParameter(HttpParameter.REQUEST) String requestIndex)
253 throws SQLException, NamingException {
254
255 Action.checkSystemActionsEnabled();
256 final int index = DatabaseInformations.parseRequestIndex(requestIndex);
257 return new DatabaseInformations(index);
258 }
259
260 @RequestPart(HttpPart.CONNECTIONS)
261 Serializable createConnectionsSerializable() {
262
263 Action.checkSystemActionsEnabled();
264 return new ArrayList<>(JdbcWrapper.getConnectionInformationsList());
265 }
266
267 @RequestPart(HttpPart.WEBAPP_VERSIONS)
268 Serializable createWebappVersionsSerializable() {
269 return new LinkedHashMap<>(collector.getDatesByWebappVersions());
270 }
271
272 @RequestPart(HttpPart.GRAPH)
273 Serializable getCounterRequestById(@RequestParameter(HttpParameter.GRAPH) String requestId,
274 @RequestAttribute(RANGE_KEY) Range range) throws IOException {
275 for (final Counter counter : collector.getCounters()) {
276 if (counter.isRequestIdFromThisCounter(requestId)) {
277 final Counter rangeCounter = collector.getRangeCounter(range, counter.getName())
278 .clone();
279 for (final CounterRequest request : rangeCounter.getRequests()) {
280 if (requestId.equals(request.getId())) {
281 return request;
282 }
283 }
284 }
285 }
286
287 return null;
288 }
289
290 @RequestPart(HttpPart.JROBINS)
291 Serializable getJRobinsImages(@RequestAttribute(RANGE_KEY) Range range,
292 @RequestParameter(HttpParameter.WIDTH) String width,
293 @RequestParameter(HttpParameter.HEIGHT) String height,
294 @RequestParameter(HttpParameter.GRAPH) String graphName) throws IOException {
295
296 final int myWidth = Integer.parseInt(width);
297 final int myHeight = Integer.parseInt(height);
298 if (graphName != null) {
299 final JRobin jrobin = collector.getJRobin(graphName);
300 if (jrobin != null) {
301 return jrobin.graph(range, myWidth, myHeight);
302 }
303 return null;
304 }
305 final Collection<JRobin> jrobins = collector.getDisplayedCounterJRobins();
306 return (Serializable) convertJRobinsToImages(jrobins, range, myWidth, myHeight);
307 }
308
309 @RequestPart(HttpPart.OTHER_JROBINS)
310 Serializable getOtherJRobinsImages(@RequestAttribute(RANGE_KEY) Range range,
311 @RequestParameter(HttpParameter.WIDTH) String width,
312 @RequestParameter(HttpParameter.HEIGHT) String height) throws IOException {
313
314 final Collection<JRobin> jrobins = collector.getDisplayedOtherJRobins();
315 return (Serializable) convertJRobinsToImages(jrobins, range, Integer.parseInt(width),
316 Integer.parseInt(height));
317 }
318
319 @RequestPart(HttpPart.EXPLAIN_PLAN)
320 Serializable createExplainPlanSerializableFor(@RequestHeader("request") String sqlRequest) {
321
322 assert sqlRequest != null;
323 try {
324
325 return DatabaseInformations.explainPlanFor(sqlRequest);
326 } catch (final Exception ex) {
327 return ex.toString();
328 }
329 }
330
331 private List<CounterRequestContext> getCurrentRequests() {
332 final List<Counter> counters = collector.getCounters();
333 final List<Counter> newCounters = new ArrayList<>();
334 for (final Counter counter : counters) {
335 final Counter cloneLight = new Counter(counter.getName(), counter.getStorageName(),
336 counter.getIconName(), counter.getChildCounterName());
337 newCounters.add(cloneLight);
338 }
339
340
341 return collector.getRootCurrentContexts(newCounters);
342 }
343
344 @RequestPart(HttpPart.CRASHES)
345 Serializable createCrashesSerializable(
346 @RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List<JavaInformations> javaInformationsList) {
347 return new ArrayList<>(HsErrPid.getHsErrPidList(javaInformationsList));
348 }
349
350 private Map<String, byte[]> convertJRobinsToImages(Collection<JRobin> jrobins, Range range,
351 int width, int height) throws IOException {
352 final Map<String, byte[]> images = new LinkedHashMap<>(jrobins.size());
353 for (final JRobin jrobin : jrobins) {
354 final byte[] image = jrobin.graph(range, width, height);
355 images.put(jrobin.getName(), image);
356 }
357 return images;
358 }
359
360 public Serializable createDefaultSerializable(List<JavaInformations> javaInformationsList,
361 Range range, String messageForReport) throws IOException {
362 final List<Counter> counters = collector.getRangeCounters(range);
363 final List<Serializable> serialized = new ArrayList<>(
364 counters.size() + javaInformationsList.size());
365
366 for (final Counter counter : counters) {
367 serialized.add(counter.clone());
368 }
369 serialized.addAll(javaInformationsList);
370 if (messageForReport != null) {
371 serialized.add(messageForReport);
372 }
373 return (Serializable) serialized;
374 }
375
376 public Range getRangeForSerializable(HttpServletRequest httpRequest) {
377 final Range range;
378 final String period = HttpParameter.PERIOD.getParameterFrom(httpRequest);
379 if (period == null) {
380
381 range = Period.TOUT.getRange();
382 } else {
383 final DateFormat dateFormat;
384 final String pattern = HttpParameter.PATTERN.getParameterFrom(httpRequest);
385 if (pattern == null) {
386 dateFormat = I18N.createDateFormat();
387 } else {
388 dateFormat = new SimpleDateFormat(pattern, Locale.US);
389 }
390 range = Range.parse(period, dateFormat);
391 }
392 return range;
393 }
394
395 private String getApplication() {
396 return collector.getApplication();
397 }
398 }
399