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