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.beans;
12  
13  import jakarta.inject.Inject;
14  
15  import java.util.ArrayList;
16  import java.util.Collection;
17  import java.util.List;
18  import java.util.Map;
19  
20  import javax.naming.NamingException;
21  
22  import org.apache.catalina.Context;
23  import org.apache.catalina.Wrapper;
24  import org.apache.catalina.util.ServerInfo;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  import org.springframework.beans.factory.annotation.Value;
28  
29  import psiprobe.TomcatContainer;
30  import psiprobe.model.ApplicationResource;
31  
32  /**
33   * This class wires support for Tomcat "privileged" context functionality into Spring. If
34   * application context is privileged Tomcat would always call servlet.setWrapper method on each
35   * request. ContainerWrapperBean wires the passed wrapper to the relevant Tomcat container adapter
36   * class, which in turn helps the Probe to interpret the wrapper.
37   */
38  public class ContainerWrapperBean {
39  
40    /** The Constant logger. */
41    private static final Logger logger = LoggerFactory.getLogger(ContainerWrapperBean.class);
42  
43    /** The tomcat container. */
44    private volatile TomcatContainer tomcatContainer;
45  
46    /** The lock. */
47    private final Object lock = new Object();
48  
49    /** List of class names to adapt particular Tomcat implementation to TomcatContainer interface. */
50    @Inject
51    private List<String> adapterClasses;
52  
53    /** The resource resolver. */
54    private ResourceResolver resourceResolver;
55  
56    /** The force first adapter. */
57    private boolean forceFirstAdapter;
58  
59    /** The resource resolvers. */
60    @Inject
61    private Map<String, ResourceResolver> resourceResolvers;
62  
63    /**
64     * Checks if is force first adapter.
65     *
66     * @return true, if is force first adapter
67     */
68    public boolean isForceFirstAdapter() {
69      return forceFirstAdapter;
70    }
71  
72    /**
73     * Sets the force first adapter. Setting this property to true will override the server polling
74     * each adapter performs to test for compatibility. Instead, it will use the first one in the
75     * adapterClasses list.
76     *
77     * @param forceFirstAdapter the new force first adapter
78     */
79    // TODO We should make this configurable
80    @Value("false")
81    public void setForceFirstAdapter(boolean forceFirstAdapter) {
82      this.forceFirstAdapter = forceFirstAdapter;
83    }
84  
85    /**
86     * Sets the wrapper.
87     *
88     * @param wrapper the new wrapper
89     */
90    public void setWrapper(Wrapper wrapper) {
91      if (tomcatContainer == null) {
92  
93        synchronized (lock) {
94  
95          if (tomcatContainer == null) {
96  
97            String serverInfo = ServerInfo.getServerInfo();
98            logger.info("Server info: {}", serverInfo);
99            for (String className : adapterClasses) {
100             try {
101               Object obj = Class.forName(className).getDeclaredConstructor().newInstance();
102               logger.debug("Testing container adapter: {}", className);
103               if (obj instanceof TomcatContainer) {
104                 if (forceFirstAdapter || ((TomcatContainer) obj).canBoundTo(serverInfo)) {
105                   logger.info("Using {}", className);
106                   tomcatContainer = (TomcatContainer) obj;
107                   tomcatContainer.setWrapper(wrapper);
108                   break;
109                 }
110                 logger.debug("Cannot bind {} to {}", className, serverInfo);
111               } else {
112                 logger.error("{} does not implement {}", className,
113                     TomcatContainer.class.getName());
114               }
115             } catch (Exception e) {
116               logger.debug("", e);
117               logger.info("Failed to load {}", className);
118             }
119           }
120 
121           if (tomcatContainer == null) {
122             logger.error("No suitable container adapter found!");
123           }
124         }
125       }
126     }
127 
128     try {
129       if (tomcatContainer != null && wrapper == null) {
130         logger.info("Unregistering container adapter");
131         tomcatContainer.setWrapper(null);
132       }
133     } catch (Exception e) {
134       logger.error("Could not unregister container adapter", e);
135     }
136   }
137 
138   /**
139    * Gets the tomcat container.
140    *
141    * @return the tomcat container
142    */
143   public TomcatContainer getTomcatContainer() {
144     return tomcatContainer;
145   }
146 
147   /**
148    * Gets the adapter classes.
149    *
150    * @return the adapter classes
151    */
152   public List<String> getAdapterClasses() {
153     return adapterClasses;
154   }
155 
156   /**
157    * Sets the adapter classes.
158    *
159    * @param adapterClasses the new adapter classes
160    */
161   public void setAdapterClasses(List<String> adapterClasses) {
162     this.adapterClasses = adapterClasses;
163   }
164 
165   /**
166    * Gets the resource resolver.
167    *
168    * @return the resource resolver
169    */
170   public ResourceResolver getResourceResolver() {
171     if (resourceResolver == null) {
172       if (System.getProperty("jboss.server.name") != null) {
173         resourceResolver = resourceResolvers.get("jboss");
174         logger.info("Using JBOSS resource resolver");
175       } else {
176         resourceResolver = resourceResolvers.get("default");
177         logger.info("Using DEFAULT resource resolver");
178       }
179     }
180     return resourceResolver;
181   }
182 
183   /**
184    * Gets the resource resolvers.
185    *
186    * @return the resource resolvers
187    */
188   public Map<String, ResourceResolver> getResourceResolvers() {
189     return resourceResolvers;
190   }
191 
192   /**
193    * Sets the resource resolvers.
194    *
195    * @param resourceResolvers the resource resolvers
196    */
197   public void setResourceResolvers(Map<String, ResourceResolver> resourceResolvers) {
198     this.resourceResolvers = resourceResolvers;
199   }
200 
201   /**
202    * Gets the data sources.
203    *
204    * @return the data sources
205    *
206    * @throws NamingException the naming exception
207    */
208   public List<ApplicationResource> getDataSources() throws NamingException {
209     List<ApplicationResource> resources = new ArrayList<>(getPrivateDataSources());
210     resources.addAll(getGlobalDataSources());
211     return resources;
212   }
213 
214   /**
215    * Gets the private data sources.
216    *
217    * @return the private data sources
218    *
219    * @throws NamingException the naming exception
220    */
221   public List<ApplicationResource> getPrivateDataSources() throws NamingException {
222     List<ApplicationResource> resources = new ArrayList<>();
223     if (tomcatContainer != null && getResourceResolver().supportsPrivateResources()) {
224       for (Context app : getTomcatContainer().findContexts()) {
225         List<ApplicationResource> appResources =
226             getResourceResolver().getApplicationResources(app, this);
227         // add only those resources that have data source info
228         filterDataSources(appResources, resources);
229       }
230     }
231     return resources;
232   }
233 
234   /**
235    * Gets the global data sources.
236    *
237    * @return the global data sources
238    *
239    * @throws NamingException the naming exception
240    */
241   public List<ApplicationResource> getGlobalDataSources() throws NamingException {
242     List<ApplicationResource> resources = new ArrayList<>();
243     if (getResourceResolver().supportsGlobalResources()) {
244       List<ApplicationResource> globalResources = getResourceResolver().getApplicationResources();
245       // add only those resources that have data source info
246       filterDataSources(globalResources, resources);
247     }
248     return resources;
249   }
250 
251   /**
252    * Filter data sources.
253    *
254    * @param resources the resources
255    * @param dataSources the data sources
256    */
257   protected void filterDataSources(Iterable<ApplicationResource> resources,
258       Collection<ApplicationResource> dataSources) {
259 
260     for (ApplicationResource res : resources) {
261       if (res.getDataSourceInfo() != null) {
262         dataSources.add(res);
263       }
264     }
265   }
266 
267 }