1
2
3
4
5
6
7
8
9
10
11 package psiprobe.beans;
12
13 import java.lang.management.ManagementFactory;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Set;
17
18 import javax.inject.Inject;
19 import javax.management.AttributeNotFoundException;
20 import javax.management.InstanceNotFoundException;
21 import javax.management.MBeanException;
22 import javax.management.MBeanServer;
23 import javax.management.MalformedObjectNameException;
24 import javax.management.ObjectName;
25 import javax.management.ReflectionException;
26 import javax.naming.InitialContext;
27 import javax.naming.NamingException;
28 import javax.sql.DataSource;
29
30 import org.apache.catalina.Context;
31 import org.apache.catalina.Server;
32 import org.apache.catalina.core.StandardServer;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 import psiprobe.beans.accessors.DatasourceAccessor;
37 import psiprobe.model.ApplicationResource;
38 import psiprobe.model.DataSourceInfo;
39
40
41
42
43 public class ResourceResolverBean implements ResourceResolver {
44
45
46 private static final Logger logger = LoggerFactory.getLogger(ResourceResolverBean.class);
47
48
49
50
51 public static final String DEFAULT_GLOBAL_RESOURCE_PREFIX = "";
52
53
54
55
56
57 public static final String DEFAULT_RESOURCE_PREFIX =
58 DEFAULT_GLOBAL_RESOURCE_PREFIX + "java:comp/env/";
59
60
61 @Inject
62 private List<String> datasourceMappers;
63
64 @Override
65 public List<ApplicationResource> getApplicationResources() throws NamingException {
66 logger.debug("Reading GLOBAL resources");
67 List<ApplicationResource> resources = new ArrayList<>();
68
69 MBeanServer server = getMBeanServer();
70 if (server != null) {
71 try {
72 Set<ObjectName> dsNames =
73 server.queryNames(new ObjectName("Catalina:type=Resource,resourcetype=Global,*"), null);
74 for (ObjectName objectName : dsNames) {
75 ApplicationResource resource = new ApplicationResource();
76
77 logger.debug("reading resource: {}", objectName);
78 resource.setName(getStringAttribute(server, objectName, "name"));
79 resource.setType(getStringAttribute(server, objectName, "type"));
80 resource.setScope(getStringAttribute(server, objectName, "scope"));
81 resource.setAuth(getStringAttribute(server, objectName, "auth"));
82 resource.setDescription(getStringAttribute(server, objectName, "description"));
83
84 lookupResource(resource, true, true);
85
86 resources.add(resource);
87 }
88 } catch (Exception e) {
89 logger.error("There was an error querying JMX server:", e);
90 }
91 }
92 return resources;
93 }
94
95 @Override
96 public synchronized List<ApplicationResource> getApplicationResources(Context context,
97 ContainerWrapperBean containerWrapper) throws NamingException {
98
99 List<ApplicationResource> resourceList = new ArrayList<>();
100
101 boolean contextAvailable = containerWrapper.getTomcatContainer().getAvailable(context);
102 if (contextAvailable) {
103
104 logger.debug("Reading CONTEXT {}", context.getName());
105
106 boolean contextBound = false;
107
108 try {
109 containerWrapper.getTomcatContainer().bindToContext(context);
110 contextBound = true;
111 } catch (NamingException e) {
112 logger.error("Cannot bind to context. useNaming=false ?");
113 logger.debug("", e);
114 }
115
116 try {
117 containerWrapper.getTomcatContainer().addContextResource(context, resourceList);
118
119 containerWrapper.getTomcatContainer().addContextResourceLink(context, resourceList);
120
121 for (ApplicationResource resourceList1 : resourceList) {
122 lookupResource(resourceList1, contextBound, false);
123 }
124
125 } finally {
126 if (contextBound) {
127 containerWrapper.getTomcatContainer().unbindFromContext(context);
128 }
129 }
130 }
131
132 return resourceList;
133 }
134
135
136
137
138
139
140
141
142 public void lookupResource(ApplicationResource resource, boolean contextBound, boolean global) {
143 DataSourceInfo dataSourceInfo = null;
144 if (contextBound) {
145 try {
146 javax.naming.Context ctx = !global ? new InitialContext() : getGlobalNamingContext();
147 if (ctx == null) {
148 logger.error(
149 "Unable to find context. This may indicate invalid setup. Check global resources versus requested resources");
150 resource.setLookedUp(false);
151 return;
152 }
153 String jndiName = resolveJndiName(resource.getName(), global);
154 logger.debug("reading resource jndi name: {}", jndiName);
155 Object obj = ctx.lookup(jndiName);
156 resource.setLookedUp(true);
157 for (String accessorString : datasourceMappers) {
158 logger.debug("Looking up datasource adapter: {}", accessorString);
159 DatasourceAccessor accessor = Class.forName(accessorString)
160 .asSubclass(DatasourceAccessor.class).getDeclaredConstructor().newInstance();
161 dataSourceInfo = accessor.getInfo(obj);
162 if (dataSourceInfo != null) {
163 break;
164 }
165 }
166 } catch (Exception e) {
167 resource.setLookedUp(false);
168 dataSourceInfo = null;
169 logger.error("Failed to lookup: '{}'", resource.getName(), e);
170 }
171 } else {
172 resource.setLookedUp(false);
173 }
174
175 if (resource.isLookedUp() && dataSourceInfo != null) {
176 resource.setDataSourceInfo(dataSourceInfo);
177 }
178 }
179
180 @Override
181 public synchronized boolean resetResource(final Context context, String resourceName,
182 ContainerWrapperBean containerWrapper) throws NamingException {
183
184 if (context != null) {
185 containerWrapper.getTomcatContainer().bindToContext(context);
186 }
187 try {
188 javax.naming.Context ctx = context != null ? new InitialContext() : getGlobalNamingContext();
189 String jndiName = resolveJndiName(resourceName, context == null);
190 try {
191 for (String accessorString : datasourceMappers) {
192 logger.debug("Resetting datasource adapter: {}", accessorString);
193 DatasourceAccessor accessor = Class.forName(accessorString)
194 .asSubclass(DatasourceAccessor.class).getDeclaredConstructor().newInstance();
195 if (ctx == null) {
196 return false;
197 }
198 Object obj = ctx.lookup(jndiName);
199 if (accessor.reset(obj)) {
200 return true;
201 }
202 }
203 return false;
204 } catch (Exception e) {
205 logger.trace("", e);
206 return false;
207 }
208 } finally {
209 if (context != null) {
210 containerWrapper.getTomcatContainer().unbindFromContext(context);
211 }
212 }
213 }
214
215 @Override
216 public synchronized DataSource lookupDataSource(final Context context, String resourceName,
217 ContainerWrapperBean containerWrapper) throws NamingException {
218
219 if (context != null) {
220 containerWrapper.getTomcatContainer().bindToContext(context);
221 }
222 try {
223 javax.naming.Context ctx = context != null ? new InitialContext() : getGlobalNamingContext();
224 String jndiName = resolveJndiName(resourceName, context == null);
225 if (ctx == null) {
226 return null;
227 }
228 Object obj = ctx.lookup(jndiName);
229
230 if (obj instanceof DataSource) {
231 return (DataSource) obj;
232 }
233 return null;
234 } finally {
235 if (context != null) {
236 containerWrapper.getTomcatContainer().unbindFromContext(context);
237 }
238 }
239 }
240
241
242
243
244
245
246 public List<String> getDatasourceMappers() {
247 return datasourceMappers;
248 }
249
250
251
252
253
254
255 public void setDatasourceMappers(List<String> datasourceMappers) {
256 this.datasourceMappers = datasourceMappers;
257 }
258
259 @Override
260 public boolean supportsPrivateResources() {
261 return true;
262 }
263
264 @Override
265 public boolean supportsGlobalResources() {
266 return true;
267 }
268
269 @Override
270 public boolean supportsDataSourceLookup() {
271 return true;
272 }
273
274 @Override
275 public MBeanServer getMBeanServer() {
276 return ManagementFactory.getPlatformMBeanServer();
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290 protected static String resolveJndiName(String name, boolean global) {
291 return (global ? DEFAULT_GLOBAL_RESOURCE_PREFIX : DEFAULT_RESOURCE_PREFIX) + name;
292 }
293
294
295
296
297
298
299
300
301
302
303 private String getStringAttribute(MBeanServer server, ObjectName objectName,
304 String attributeName) {
305
306 try {
307 return (String) server.getAttribute(objectName, attributeName);
308 } catch (Exception e) {
309 logger.error("Error getting attribute '{}' from '{}'", attributeName, objectName, e);
310 return null;
311 }
312 }
313
314
315
316
317
318
319 public static javax.naming.Context getGlobalNamingContext() {
320
321 javax.naming.Context globalContext = null;
322 MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
323 if (mbeanServer != null) {
324 for (String domain : mbeanServer.getDomains()) {
325
326 ObjectName name;
327 try {
328 name = new ObjectName(domain + ":type=Server");
329 } catch (MalformedObjectNameException e) {
330 logger.error("", e);
331 return null;
332 }
333
334 Server server = null;
335 try {
336 server = (Server) mbeanServer.getAttribute(name, "managedResource");
337 } catch (AttributeNotFoundException | InstanceNotFoundException | MBeanException
338 | ReflectionException e) {
339 logger.trace("JMX objectName {} does not contain any managedResource", name, e);
340 }
341
342
343 if (server instanceof StandardServer) {
344 globalContext = server.getGlobalNamingContext();
345 break;
346 }
347 }
348 }
349
350 return globalContext;
351 }
352 }