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