1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.monitoring.reporting.web;
18
19 import org.apache.commons.monitoring.reporting.web.handler.FilteringEndpoints;
20 import org.apache.commons.monitoring.reporting.web.handler.HomeEndpoint;
21 import org.apache.commons.monitoring.reporting.web.handler.internal.EndpointInfo;
22 import org.apache.commons.monitoring.reporting.web.handler.internal.Invoker;
23 import org.apache.commons.monitoring.reporting.web.plugin.PluginRepository;
24 import org.apache.commons.monitoring.reporting.web.template.MapBuilder;
25 import org.apache.commons.monitoring.reporting.web.template.Templates;
26
27 import javax.servlet.Filter;
28 import javax.servlet.FilterChain;
29 import javax.servlet.FilterConfig;
30 import javax.servlet.ServletException;
31 import javax.servlet.ServletRequest;
32 import javax.servlet.ServletResponse;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpServletResponse;
35 import java.io.ByteArrayInputStream;
36 import java.io.ByteArrayOutputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.PrintStream;
40 import java.io.PrintWriter;
41 import java.io.StringWriter;
42 import java.lang.reflect.InvocationHandler;
43 import java.lang.reflect.Method;
44 import java.lang.reflect.Proxy;
45 import java.util.HashMap;
46 import java.util.Map;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49
50 public class MonitoringController implements Filter {
51 private final Map<String, byte[]> cachedResources = new HashMap<String, byte[]>();
52 private final Map<Pattern, Invoker> invokers = new HashMap<Pattern, Invoker>();
53 private String mapping;
54 private ClassLoader classloader;
55 private Invoker defaultInvoker;
56
57 @Override
58 public void init(final FilterConfig config) throws ServletException {
59 classloader = Thread.currentThread().getContextClassLoader();
60 initMapping(config);
61 initHandlers();
62 Templates.init(config.getServletContext().getContextPath(), mapping);
63 }
64
65 private void initHandlers() {
66
67 invokers.putAll(EndpointInfo.build(HomeEndpoint.class, null, "").getInvokers());
68 defaultInvoker = invokers.values().iterator().next();
69
70
71 invokers.putAll(EndpointInfo.build(FilteringEndpoints.class, null, "").getInvokers());
72
73
74 for (final PluginRepository.PluginInfo plugin : PluginRepository.PLUGIN_INFO) {
75 for (final Map.Entry<Pattern, Invoker> invoker : plugin.getInvokers().entrySet()) {
76 invokers.put(invoker.getKey(), invoker.getValue());
77 }
78 }
79 }
80
81 private void initMapping(FilterConfig config) {
82 mapping = config.getInitParameter("monitoring-mapping");
83 if (mapping == null) {
84 mapping = "";
85 } else if (!mapping.startsWith("/")) {
86 mapping = "/" + mapping;
87 }
88 if (mapping.endsWith("/")) {
89 mapping = mapping.substring(0, mapping.length() - 1);
90 }
91 }
92
93 @Override
94 public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
95 if (!HttpServletRequest.class.isInstance(request)) {
96 chain.doFilter(request, response);
97 return;
98 }
99
100 final HttpServletRequest httpRequest = HttpServletRequest.class.cast(request);
101 final HttpServletResponse httpResponse = HttpServletResponse.class.cast(response);
102
103 String path = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length() + mapping.length());
104 if (!path.startsWith("/")) {
105 path = "/" + path;
106 }
107
108
109 Invoker invoker = defaultInvoker;
110 Matcher matcher = null;
111 for (final Map.Entry<Pattern, Invoker> entry : invokers.entrySet()) {
112 matcher = entry.getKey().matcher(path);
113 if (matcher.matches()) {
114 invoker = entry.getValue();
115 break;
116 }
117 }
118
119
120 if (path.startsWith("/resources/")) {
121 byte[] bytes = cachedResources.get(path);
122 if (bytes == null) {
123 final InputStream is;
124 if (invoker != defaultInvoker) {
125 final StringWriter writer = new StringWriter();
126 final PrintWriter printWriter = new PrintWriter(writer);
127 invoker.invoke(httpRequest, HttpServletResponse.class.cast(Proxy.newProxyInstance(classloader, new Class<?>[] { HttpServletResponse.class }, new InvocationHandler() {
128 @Override
129 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
130 if ("getWriter".equals(method.getName())) {
131 return printWriter;
132 }
133 return method.invoke(httpResponse, args);
134 }
135 })), null);
136 is = new ByteArrayInputStream(writer.toString().getBytes());
137 } else {
138 is = classloader.getResourceAsStream(path.substring(1));
139 }
140
141 if (is != null) {
142 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
143 int i;
144 while ((i = is.read()) != -1) {
145 baos.write(i);
146 }
147
148 bytes = baos.toByteArray();
149 cachedResources.put(path, bytes);
150 }
151 }
152 if (bytes != null) {
153 httpResponse.getOutputStream().write(bytes);
154 return;
155 }
156 }
157
158
159 if (invoker == null) {
160 error(response, null);
161 } else {
162 try {
163 invoker.invoke(httpRequest, httpResponse, matcher);
164 } catch (final Exception e) {
165 error(response, e);
166 }
167 }
168 }
169
170 private void error(final ServletResponse response, final Exception e) throws IOException {
171 final String exception;
172 if (e != null) {
173 final ByteArrayOutputStream err = new ByteArrayOutputStream();
174 e.printStackTrace(new PrintStream(err));
175 exception = new String(err.toByteArray());
176 } else {
177 exception = "No matcher found";
178 }
179 Templates.htmlRender(response.getWriter(), "error.vm", new MapBuilder<String, Object>().set("exception", exception).build());
180 }
181
182 @Override
183 public void destroy() {
184 invokers.clear();
185 }
186 }