View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.tools;
12  
13  import static org.junit.jupiter.api.Assertions.assertEquals;
14  import static org.junit.jupiter.api.Assertions.assertNotNull;
15  import static org.junit.jupiter.api.Assertions.assertNull;
16  import static org.junit.jupiter.api.Assertions.assertThrows;
17  import static org.junit.jupiter.api.Assertions.assertTrue;
18  import static org.mockito.Mockito.mock;
19  import static org.mockito.Mockito.when;
20  
21  import jakarta.servlet.ServletContext;
22  import jakarta.servlet.http.HttpSession;
23  
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Locale;
27  
28  import javax.naming.NamingException;
29  
30  import org.apache.catalina.Container;
31  import org.apache.catalina.Context;
32  import org.apache.catalina.Manager;
33  import org.apache.catalina.Session;
34  import org.apache.catalina.Wrapper;
35  import org.apache.catalina.core.StandardWrapper;
36  import org.junit.jupiter.api.Test;
37  
38  import psiprobe.beans.ContainerWrapperBean;
39  import psiprobe.beans.ResourceResolver;
40  import psiprobe.model.Application;
41  import psiprobe.model.ApplicationResource;
42  import psiprobe.model.ApplicationSession;
43  import psiprobe.model.DataSourceInfo;
44  import psiprobe.model.ServletInfo;
45  import psiprobe.model.ServletMapping;
46  
47  /**
48   * Tests for {@link ApplicationUtils}.
49   */
50  class ApplicationUtilsTest {
51  
52    @Test
53    void getApplicationSessionReturnsNullForInvalidSession() {
54      Session session = mock(Session.class);
55      when(session.isValid()).thenReturn(false);
56  
57      assertNull(ApplicationUtils.getApplicationSession(session, false, false));
58    }
59  
60    @Test
61    void getApplicationSessionCollectsAttributesAndMetadata() {
62      Session session = mock(Session.class);
63      Manager manager = mock(Manager.class);
64      HttpSession httpSession = mock(HttpSession.class);
65  
66      when(session.isValid()).thenReturn(true);
67      when(session.getId()).thenReturn("s-1");
68      when(session.getCreationTime()).thenReturn(1_000L);
69      when(session.getLastAccessedTime()).thenReturn(2_000L);
70      when(session.getMaxInactiveInterval()).thenReturn(60);
71      when(session.getManager()).thenReturn(manager);
72      when(session.getSession()).thenReturn(httpSession);
73  
74      when(httpSession.getAttributeNames()).thenReturn(Collections.enumeration(List.of("name",
75          ApplicationSession.LAST_ACCESSED_BY_IP, ApplicationSession.LAST_ACCESSED_LOCALE)));
76      when(httpSession.getAttribute("name")).thenReturn("value");
77      when(httpSession.getAttribute(ApplicationSession.LAST_ACCESSED_BY_IP)).thenReturn("127.0.0.1");
78      when(httpSession.getAttribute(ApplicationSession.LAST_ACCESSED_LOCALE)).thenReturn(Locale.US);
79  
80      ApplicationSession result = ApplicationUtils.getApplicationSession(session, false, true);
81  
82      assertNotNull(result);
83      assertEquals("s-1", result.getId());
84      assertEquals(3, result.getObjectCount());
85      assertTrue(result.isSerializable());
86      assertEquals("127.0.0.1", result.getLastAccessedIp());
87      assertEquals(Locale.US, result.getLastAccessedIpLocale());
88      assertEquals(3, result.getAttributes().size());
89    }
90  
91    @Test
92    void getApplicationSessionHandlesInvalidatedHttpSessionGracefully() {
93      Session session = mock(Session.class);
94      Manager manager = mock(Manager.class);
95      HttpSession httpSession = mock(HttpSession.class);
96  
97      when(session.isValid()).thenReturn(true);
98      when(session.getId()).thenReturn("s-2");
99      when(session.getCreationTime()).thenReturn(1_000L);
100     when(session.getLastAccessedTime()).thenReturn(2_000L);
101     when(session.getMaxInactiveInterval()).thenReturn(60);
102     when(session.getManager()).thenReturn(manager);
103     when(session.getSession()).thenReturn(httpSession);
104 
105     when(httpSession.getAttributeNames()).thenThrow(new IllegalStateException("invalidated"));
106 
107     ApplicationSession result = ApplicationUtils.getApplicationSession(session, false, true);
108 
109     assertNotNull(result);
110     assertEquals(0, result.getObjectCount());
111     assertEquals(0, result.getAttributes().size());
112   }
113 
114   @Test
115   void collectApplicationServletStatsAggregatesStandardWrappers() {
116     Context context = mock(Context.class);
117     StandardWrapper sw1 = mock(StandardWrapper.class);
118     StandardWrapper sw2 = mock(StandardWrapper.class);
119     Container other = mock(Container.class);
120 
121     when(context.findChildren()).thenReturn(new Container[] {sw1, other, sw2});
122 
123     when(sw1.getRequestCount()).thenReturn(5);
124     when(sw1.getErrorCount()).thenReturn(1);
125     when(sw1.getProcessingTime()).thenReturn(100L);
126     when(sw1.getMinTime()).thenReturn(10L);
127     when(sw1.getMaxTime()).thenReturn(50L);
128 
129     when(sw2.getRequestCount()).thenReturn(2);
130     when(sw2.getErrorCount()).thenReturn(0);
131     when(sw2.getProcessingTime()).thenReturn(200L);
132     when(sw2.getMinTime()).thenReturn(5L);
133     when(sw2.getMaxTime()).thenReturn(80L);
134 
135     Application app = new Application();
136     ApplicationUtils.collectApplicationServletStats(context, app);
137 
138     assertEquals(2, app.getServletCount());
139     assertEquals(7L, app.getRequestCount());
140     assertEquals(1L, app.getErrorCount());
141     assertEquals(300L, app.getProcessingTime());
142     assertEquals(5L, app.getMinTime());
143     assertEquals(80L, app.getMaxTime());
144   }
145 
146   @Test
147   void getApplicationDataSourceUsageScoresUsesMaximumAcrossResources() throws NamingException {
148     Context context = mock(Context.class);
149     ResourceResolver resolver = mock(ResourceResolver.class);
150     ContainerWrapperBean containerWrapper = mock(ContainerWrapperBean.class);
151 
152     ApplicationResource r1 = new ApplicationResource();
153     DataSourceInfo ds1 = new DataSourceInfo();
154     ds1.setMaxConnections(100);
155     ds1.setBusyConnections(40);
156     ds1.setEstablishedConnections(50);
157     r1.setDataSourceInfo(ds1);
158 
159     ApplicationResource r2 = new ApplicationResource();
160     DataSourceInfo ds2 = new DataSourceInfo();
161     ds2.setMaxConnections(200);
162     ds2.setBusyConnections(160);
163     ds2.setEstablishedConnections(100);
164     r2.setDataSourceInfo(ds2);
165 
166     ApplicationResource r3 = new ApplicationResource();
167 
168     when(resolver.getApplicationResources(context, containerWrapper))
169         .thenReturn(List.of(r1, r2, r3));
170 
171     int[] scores =
172         ApplicationUtils.getApplicationDataSourceUsageScores(context, resolver, containerWrapper);
173 
174     assertEquals(Math.max(ds1.getBusyScore(), ds2.getBusyScore()), scores[0]);
175     assertEquals(Math.max(ds1.getEstablishedScore(), ds2.getEstablishedScore()), scores[1]);
176   }
177 
178   @Test
179   void getApplicationDataSourceUsageScoresWrapsNamingException() throws NamingException {
180     Context context = mock(Context.class);
181     ResourceResolver resolver = mock(ResourceResolver.class);
182     ContainerWrapperBean containerWrapper = mock(ContainerWrapperBean.class);
183 
184     when(resolver.getApplicationResources(context, containerWrapper))
185         .thenThrow(new NamingException("boom"));
186 
187     assertThrows(RuntimeException.class, () -> ApplicationUtils
188         .getApplicationDataSourceUsageScores(context, resolver, containerWrapper));
189   }
190 
191   @Test
192   void getApplicationAttributesCollectsServletContextAttributes() {
193     Context context = mock(Context.class);
194     ServletContext servletContext = mock(ServletContext.class);
195 
196     when(context.getServletContext()).thenReturn(servletContext);
197     when(servletContext.getAttributeNames())
198         .thenReturn(Collections.enumeration(List.of("a1", "a2")));
199     when(servletContext.getAttribute("a1")).thenReturn("v1");
200     when(servletContext.getAttribute("a2")).thenReturn(42);
201 
202     assertEquals(2, ApplicationUtils.getApplicationAttributes(context).size());
203   }
204 
205   @Test
206   void getApplicationServletReturnsNullWhenChildIsNotWrapper() {
207     Context context = mock(Context.class);
208     Container container = mock(Container.class);
209 
210     when(context.findChild("x")).thenReturn(container);
211 
212     assertNull(ApplicationUtils.getApplicationServlet(context, "x"));
213   }
214 
215   @Test
216   void getApplicationServletAndServletsBuildServletInfoFromWrappers() {
217     Context context = mock(Context.class);
218     Wrapper wrapper = mock(Wrapper.class);
219     Container other = mock(Container.class);
220 
221     when(context.getName()).thenReturn("/app");
222     when(context.findChild("w")).thenReturn(wrapper);
223     when(wrapper.getName()).thenReturn("w");
224     when(wrapper.getServletClass()).thenReturn("com.example.Servlet");
225     when(wrapper.isUnavailable()).thenReturn(false);
226     when(wrapper.getLoadOnStartup()).thenReturn(1);
227     when(wrapper.getRunAs()).thenReturn("runAs");
228     when(wrapper.findMappings()).thenReturn(new String[] {"/w"});
229 
230     ServletInfo servletInfo = ApplicationUtils.getApplicationServlet(context, "w");
231 
232     assertNotNull(servletInfo);
233     assertEquals("w", servletInfo.getServletName());
234 
235     when(context.findChildren()).thenReturn(new Container[] {wrapper, other});
236     assertEquals(1, ApplicationUtils.getApplicationServlets(context).size());
237   }
238 
239   @Test
240   void getApplicationServletMapsSkipsNullMappingsAndAddsWrapperMetadata() {
241     Context context = mock(Context.class);
242     Wrapper wrapper = mock(Wrapper.class);
243 
244     when(context.getName()).thenReturn("/app");
245     when(context.findServletMappings()).thenReturn(new String[] {"/x", null, "/y"});
246     when(context.findServletMapping("/x")).thenReturn("s1");
247     when(context.findServletMapping("/y")).thenReturn(null);
248     when(context.findChild("s1")).thenReturn(wrapper);
249     when(wrapper.getServletClass()).thenReturn("com.example.S1");
250     when(wrapper.isUnavailable()).thenReturn(false);
251 
252     List<ServletMapping> mappings = ApplicationUtils.getApplicationServletMaps(context);
253 
254     assertEquals(1, mappings.size());
255     assertEquals("/x", mappings.get(0).getUrl());
256     assertEquals("s1", mappings.get(0).getServletName());
257     assertEquals("com.example.S1", mappings.get(0).getServletClass());
258   }
259 }