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