KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > server > jetty > CustomWebApplicationContext


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.sslexplorer.server.jetty;
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.lang.reflect.Method JavaDoc;
25 import java.net.URL JavaDoc;
26 import java.net.URLClassLoader JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.Collections JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.mortbay.http.ResourceCache;
38 import org.mortbay.jetty.servlet.WebApplicationContext;
39 import org.mortbay.util.Resource;
40
41 import com.sslexplorer.server.Main;
42
43 /**
44  * <p>
45  * An extension to the standard Jetty
46  * {@link org.mortbay.jetty.servlet.WebApplicationContext} that allows resources
47  * to be loaded from multiple {@link org.mortbay.http.ResourceCache}s.
48  *
49  * <p>
50  * This is necessary for the plugin architecture so that plugins may register
51  * their own <b>webapp</b> directories which can then be overlaid onto the
52  * namespace of the main SSL-Explorer webapp.
53  *
54  * <p>
55  * Other SSL-Explorer specific webapp configuration is also performed here,
56  * including setting up the special /defaultStyle.css alias that is used to
57  * workaround the change to the way the CSS file is load. If this alias was not
58  * set up, upgraders would have lost their CSS as /defaultStyle.css no longer
59  * really exists.
60  *
61  * <p>
62  * Plugins also register new classpaths here so that any Java bytecode they may
63  * require (be it in .CLASS format or .JAR format) may be loaded in the same
64  * Class Loader as the main webapp.
65  *
66  * @author Brett Smith <brett@3sp.com>
67  */

68 public class CustomWebApplicationContext extends WebApplicationContext {
69
70     final static Log log = LogFactory.getLog(CustomWebApplicationContext.class);
71     private List JavaDoc<ResourceCache> resourceCaches;
72     private String JavaDoc additionalClasspath;
73     private List JavaDoc<ResourceCache> reverseCaches;
74     private Map JavaDoc<String JavaDoc, ResourceCache> resourceCacheMap;
75     private ResourceCache mainWebappResourceCache;
76     private Map JavaDoc<String JavaDoc, CacheState> cacheState;
77
78     /**
79      * Constructor
80      *
81      * @param useDevConfig <code>true</code> if running in development mode
82      * @throws Exception on any error
83      */

84     public CustomWebApplicationContext(boolean useDevConfig) throws Exception JavaDoc {
85         super("webapp");
86         additionalClasspath = "";
87         resourceCacheMap = new HashMap JavaDoc<String JavaDoc, ResourceCache>();
88         reverseCaches = new ArrayList JavaDoc<ResourceCache>();
89         cacheState = new HashMap JavaDoc<String JavaDoc, CacheState>();
90         setContextPath("/");
91         setDefaultsDescriptor("/com/sslexplorer/boot/webdefault.xml");
92         setDisplayName("SSL-Explorer");
93         setTempDirectory(new File JavaDoc("tmp"));
94
95         setResourceAlias("/defaultStyle.css", "/css/defaultStyle.jsp");
96
97         if (useDevConfig) {
98             setClassLoader(Main.class.getClassLoader());
99         }
100         setWelcomeFiles(new String JavaDoc[] { "showHome.do" });
101         setConfigurationClassNames(new String JavaDoc[]{"org.mortbay.jetty.servlet.XMLConfiguration", "org.mortbay.jetty.servlet.JettyWebConfiguration",
102             "com.sslexplorer.server.jetty.JspPrecompileConfiguration" });
103         resourceCaches = new ArrayList JavaDoc<ResourceCache>();
104     }
105     
106     /**
107      * Get all resource caches
108      *
109      * @return resource caches
110      */

111     public Collection JavaDoc<ResourceCache> getResourceCaches() {
112         return resourceCaches;
113     }
114
115     /**
116      * <p>
117      * Add a new Resource Cache. Whenever a resource is requested, this handler
118      * will search all registered resource caches until one can locate it.
119      *
120      * <p>
121      * This shouldn't be called directly, but through
122      * {@link com.sslexplorer.boot.Context#addResourceBase(URL)}
123      *
124      * @param cache cache to add
125      */

126     public void addResourceCache(ResourceCache cache) {
127         resourceCaches.add(cache);
128         reverseCaches.clear();
129         cacheState.clear();
130         reverseCaches.addAll(resourceCaches);
131         Collections.reverse(reverseCaches);
132     }
133
134     /**
135      * <p>
136      * Remove a Resrouce Cache. Whenever a resource is requested, this handler
137      * will no longer use this cache.
138      *
139      * <p>
140      * This shouldn't be called directly, but through
141      * {@link com.sslexplorer.boot.Context#removeResourceBase(URL)}
142      *
143      * @param cache cache to remove
144      */

145     public void removeResourceCache(ResourceCache cache) {
146         resourceCaches.remove(cache);
147         reverseCaches.clear();
148         cacheState.clear();
149         reverseCaches.addAll(resourceCaches);
150         Collections.reverse(reverseCaches);
151     }
152     
153     protected void addComponent(Object JavaDoc o)
154     {
155         if(o instanceof ResourceCache) {
156             mainWebappResourceCache = ((ResourceCache)o);
157         }
158         super.addComponent(o);
159     }
160     
161     /*
162      * (non-Javadoc)
163      *
164      * @see org.mortbay.http.ResourceCache#getResource(java.lang.String)
165      */

166     public Resource getResource(String JavaDoc pathInContext) throws IOException JavaDoc {
167         boolean fullResourceCache = System.getProperty("sslexplorer.fullResourceCache",
168                     String.valueOf(!(System.getProperty("sslexplorer.useDevConfig", "false").equals("true")))).equals("true");
169         Resource r;
170         if (log.isDebugEnabled())
171             log.debug("Request for " + pathInContext);
172         
173         // This is a work around to prevent WEB-INF getting listed by using the path //WEB-INF
174
if(pathInContext.indexOf("//WEB-INF") != -1) {
175             return null;
176         }
177         
178         /*
179          * When in 'Full resource cache' mode, check if we already
180          * have cached the resource
181          */

182         if(fullResourceCache && cacheState.containsKey(pathInContext)) {
183             r = cacheState.get(pathInContext).getResource();
184             if (log.isDebugEnabled())
185                 if(r == null)
186                     log.debug("Resource " + pathInContext + " is permanently as missing.");
187                 else
188                     log.debug("Resource " + pathInContext + " found in permanent cache");
189             return r;
190         }
191
192         /*
193          * Determine if the resource has already been found in a
194          * resource cache (be it an extensions resource cache or
195          * the cores)
196          */

197         
198         ResourceCache o = fullResourceCache ? null : (ResourceCache) resourceCacheMap.get(pathInContext);
199         if (o == null) {
200             
201             /*
202              * The existence of the resource has not yet been determined.
203              * Search all resource caches in reverse until the extension
204              * is found. When found, store which cache it was found in
205              * for quick look up in the future.
206              */

207             if (log.isDebugEnabled())
208                 log.debug("Resource " + pathInContext + " not found in any resource cache, checking in plugins");
209             
210             for (Iterator JavaDoc i = reverseCaches.iterator();i.hasNext();) {
211                 ResourceCache cache = (ResourceCache)i.next();
212                 r = cache.getResource(pathInContext);
213                 if(r != null && r.exists() && !r.isDirectory()) {
214                     if(fullResourceCache) {
215                         if (log.isDebugEnabled())
216                             log.debug(" Found in " + cache.getBaseResource().toString());
217                         cacheState.put(pathInContext, new CacheState(CacheState.FOUND, pathInContext, r));
218                     }
219                     else {
220                         if (log.isDebugEnabled())
221                             log.debug(" Found in " + cache.getBaseResource().toString());
222                         resourceCacheMap.put(pathInContext, cache);
223                     }
224                     return r;
225                 }
226             }
227             
228             /*
229              * The resource cannot be found in this caches base directory
230              */

231             if (log.isDebugEnabled())
232                 log.debug(" Not found");
233         } else {
234             /*
235              * We know what cache the resource came from so check it still exists and
236              * return. This will only happen when not in full cache mode
237              */

238             r = o.getResource(pathInContext);
239             if (r != null && r.exists() && !r.isDirectory() ) {
240                 if (log.isDebugEnabled())
241                     log.debug(" Found in " + o.getBaseResource().toString());
242                 return r;
243             }
244         }
245
246         if (log.isDebugEnabled())
247             log.debug("Checking for alias in plugins");
248         String JavaDoc resourceAlias = getResourceAlias(pathInContext);
249         if (resourceAlias != null) {
250
251             /*
252              * The resource was not found with its real name in any
253              * caches base directory, so repeat the operation but look
254              * for the alias
255              */

256             
257             if (log.isDebugEnabled())
258                 log.debug(" Found alias of " + resourceAlias + ", checking in plugins");
259             for (Iterator JavaDoc i = reverseCaches.iterator();i.hasNext();) {
260                 ResourceCache cache = (ResourceCache)i.next();
261                 r = cache.getResource(resourceAlias);
262                 
263                 /* When checking for resource modification, check for existence of file. This
264                  * allows file to be removed at runtime without adding
265                  * overhead when used on deployed server
266                  */

267
268                 if(r !=null && r.exists() && !r.isDirectory()) {
269                     if(fullResourceCache) {
270                         if (log.isDebugEnabled())
271                             log.debug(" Found in " + cache.getBaseResource().toString());
272                         cacheState.put(pathInContext, new CacheState(CacheState.FOUND, pathInContext, r));
273                         return r;
274                     }
275                     else {
276                         if (log.isDebugEnabled())
277                             log.debug(" Found in " + cache.getBaseResource().toString());
278                         resourceCacheMap.put(pathInContext, cache);
279                         return r;
280                     }
281                 }
282             }
283             if (log.isDebugEnabled())
284                 log.debug(" Not found");
285         }
286         
287         /*
288          * The resource could not be found in any caches base
289          * directory, so pass to the main webapp
290          */

291
292         if (log.isDebugEnabled())
293             log.debug("Passing to main webapp");
294         r = super.getResource(pathInContext);
295         if(r != null && r.exists() && !r.isDirectory()) {
296             
297             /*
298              * The resource has been found in the main webapps base directory, store
299              * where it was found for quick lookup in future requests
300              */

301             
302             if (log.isDebugEnabled())
303                 log.debug(" Found in main webapp");
304
305             if(fullResourceCache) {
306                 cacheState.put(pathInContext, new CacheState(CacheState.FOUND, pathInContext, r));
307             }
308             else {
309                 resourceCacheMap.put(pathInContext, mainWebappResourceCache);
310             }
311             return r;
312         }
313         else {
314             if(fullResourceCache) {
315                 if (log.isDebugEnabled())
316                     log.debug(" Not found, caching as missing");
317                 cacheState.put(pathInContext, new CacheState(CacheState.MISSING, pathInContext, r));
318             }
319         }
320         
321         /* Not found at all */
322         
323         if (log.isDebugEnabled())
324             log.debug(" Found in main webapp");
325         
326         return r;
327     }
328
329     /**
330      * <p>
331      * Add a new location to the webapp Class Loader. This is used when plugins
332      * are loaded and they have one or more JARS or CLASS directories containing
333      * any bytecode they require.
334      *
335      * <p>
336      * The URL supplied should have a protocol of <b>file</code>.
337      *
338      * <p>
339      * This should be called directly, but through
340      * {@link com.sslexplorer.boot.Context#addContextLoaderURL(URL)}.
341      *
342      * @param url url to add
343      */

344     public void addContextLoaderURL(URL JavaDoc url) {
345         if (url.getProtocol().equals("file")) {
346             additionalClasspath = additionalClasspath
347                             + (additionalClasspath.length() != 0 ? System.getProperty("path.separator") : "") + url.getFile();
348         }
349         doAddContextLoaderURL(url);
350     }
351
352     private void doAddContextLoaderURL(URL JavaDoc u) {
353         try {
354             URLClassLoader JavaDoc sysloader = (URLClassLoader JavaDoc) getClassLoader();
355             Class JavaDoc sysclass = URLClassLoader JavaDoc.class;
356             Method JavaDoc method = sysclass.getDeclaredMethod("addURL", new Class JavaDoc[] { URL JavaDoc.class });
357             method.setAccessible(true);
358             method.invoke(sysloader, new Object JavaDoc[] { u });
359             if(log.isInfoEnabled())
360                 log.info(u.toExternalForm() + " added to context classloader");
361         } catch (Exception JavaDoc e) {
362             log.error("Failed to add to classpath.", e);
363         }
364     }
365 }
Popular Tags