View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.webapp;
20  
21  
22  import java.net.URI;
23  import java.net.URL;
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.concurrent.ConcurrentHashMap;
30  
31  import org.eclipse.jetty.util.log.Log;
32  import org.eclipse.jetty.util.log.Logger;
33  import org.eclipse.jetty.util.resource.EmptyResource;
34  import org.eclipse.jetty.util.resource.Resource;
35  
36  /**
37   * MetaInfConfiguration
38   *
39   * Scan META-INF of jars to find:
40   * <ul>
41   * <li>tlds
42   * <li>web-fragment.xml
43   * <li>resources
44   * </ul>
45   * 
46   * The jars which are scanned are:
47   * <ol>
48   * <li>those from the container classpath whose pattern matched the WebInfConfiguration.CONTAINER_JAR_PATTERN</li>
49   * <li>those from WEB-INF/lib</li>
50   * </ol>
51   */
52  public class MetaInfConfiguration extends AbstractConfiguration
53  {
54      private static final Logger LOG = Log.getLogger(MetaInfConfiguration.class);
55  
56      public static final String USE_CONTAINER_METAINF_CACHE = "org.eclipse.jetty.metainf.useCache";
57      public static final boolean DEFAULT_USE_CONTAINER_METAINF_CACHE = true;
58      public static final String CACHED_CONTAINER_TLDS = "org.eclipse.jetty.tlds.cache";
59      public static final String CACHED_CONTAINER_FRAGMENTS = FragmentConfiguration.FRAGMENT_RESOURCES+".cache";
60      public static final String CACHED_CONTAINER_RESOURCES = WebInfConfiguration.RESOURCE_DIRS+".cache";
61      public static final String METAINF_TLDS = "org.eclipse.jetty.tlds";
62      public static final String METAINF_FRAGMENTS = FragmentConfiguration.FRAGMENT_RESOURCES;
63      public static final String METAINF_RESOURCES = WebInfConfiguration.RESOURCE_DIRS;
64  
65      @Override
66      public void preConfigure(final WebAppContext context) throws Exception
67      {        
68          boolean useContainerCache = DEFAULT_USE_CONTAINER_METAINF_CACHE;
69          Boolean attr = (Boolean)context.getServer().getAttribute(USE_CONTAINER_METAINF_CACHE);
70          if (attr != null)
71              useContainerCache = attr.booleanValue();
72          
73          if (LOG.isDebugEnabled()) LOG.debug("{} = {}", USE_CONTAINER_METAINF_CACHE, useContainerCache);
74          
75          //pre-emptively create empty lists for tlds, fragments and resources as context attributes
76          //this signals that this class has been called. This differentiates the case where this class
77          //has been called but finds no META-INF data from the case where this class was never called
78          if (context.getAttribute(METAINF_TLDS) == null)
79              context.setAttribute(METAINF_TLDS, new HashSet<URL>());
80          if (context.getAttribute(METAINF_RESOURCES) == null)
81              context.setAttribute(METAINF_RESOURCES, new HashSet<Resource>());
82          if (context.getAttribute(METAINF_FRAGMENTS) == null)
83              context.setAttribute(METAINF_FRAGMENTS, new HashMap<Resource, Resource>());
84         
85          scanJars(context, context.getMetaData().getContainerResources(), useContainerCache);
86          scanJars(context, context.getMetaData().getWebInfJars(), false);
87      }
88  
89      /**
90       * Look into the jars to discover info in META-INF. If useCaches == true, then we will
91       * cache the info discovered indexed by the jar in which it was discovered: this speeds
92       * up subsequent context deployments.
93       * 
94       * @param context
95       * @param jars
96       * @param useCaches
97       * @throws Exception
98       */
99      public void scanJars (final WebAppContext context, Collection<Resource> jars, boolean useCaches)
100     throws Exception
101     {
102         ConcurrentHashMap<Resource, Resource> metaInfResourceCache = null;       
103         ConcurrentHashMap<Resource, Resource> metaInfFragmentCache = null;
104         ConcurrentHashMap<Resource, Collection<URL>> metaInfTldCache = null;
105         if (useCaches)
106         {
107             metaInfResourceCache = (ConcurrentHashMap<Resource, Resource>)context.getServer().getAttribute(CACHED_CONTAINER_RESOURCES);
108             if (metaInfResourceCache == null)
109             {
110                 metaInfResourceCache = new ConcurrentHashMap<Resource,Resource>();
111                 context.getServer().setAttribute(CACHED_CONTAINER_RESOURCES, metaInfResourceCache);
112             }
113             metaInfFragmentCache = (ConcurrentHashMap<Resource, Resource>)context.getServer().getAttribute(CACHED_CONTAINER_FRAGMENTS);
114             if (metaInfFragmentCache == null)
115             {
116                 metaInfFragmentCache = new ConcurrentHashMap<Resource,Resource>();
117                 context.getServer().setAttribute(CACHED_CONTAINER_FRAGMENTS, metaInfFragmentCache);
118             }
119             metaInfTldCache = (ConcurrentHashMap<Resource, Collection<URL>>)context.getServer().getAttribute(CACHED_CONTAINER_TLDS);
120             if (metaInfTldCache == null)
121             {
122                 metaInfTldCache = new ConcurrentHashMap<Resource,Collection<URL>>(); 
123                 context.getServer().setAttribute(CACHED_CONTAINER_TLDS, metaInfTldCache);
124             }
125         }
126         
127         //Scan jars for META-INF information
128         if (jars != null)
129         {
130             for (Resource r : jars)
131             {
132                 
133                scanForResources(context, r, metaInfResourceCache);
134                scanForFragment(context, r, metaInfFragmentCache);
135                scanForTlds(context, r, metaInfTldCache);
136             }
137         }
138     }
139     
140     /**
141      * Scan for META-INF/resources dir in the given jar.
142      * 
143      * @param context
144      * @param target
145      * @param cache
146      * @throws Exception
147      */
148     public void scanForResources (WebAppContext context, Resource target, ConcurrentHashMap<Resource,Resource> cache)
149     throws Exception
150     {
151         Resource resourcesDir = null;
152         if (cache != null && cache.containsKey(target))
153         {
154             resourcesDir = cache.get(target);  
155             if (resourcesDir == EmptyResource.INSTANCE)
156             {
157                 if (LOG.isDebugEnabled()) LOG.debug(target+" cached as containing no META-INF/resources");
158                 return;    
159             }
160             else
161                 if (LOG.isDebugEnabled()) LOG.debug(target+" META-INF/resources found in cache ");
162         }
163         else
164         {
165             //not using caches or not in the cache so check for the resources dir
166             if (LOG.isDebugEnabled()) LOG.debug(target+" META-INF/resources checked");
167             if (target.isDirectory())
168             {
169                 //TODO think  how to handle an unpacked jar file (eg for osgi)
170                 resourcesDir = target.addPath("/META-INF/resources");
171             }
172             else
173             {
174                 //Resource represents a packed jar
175                 URI uri = target.getURI();
176                 resourcesDir = Resource.newResource("jar:"+uri+"!/META-INF/resources");
177             }
178             if (!resourcesDir.exists() || !resourcesDir.isDirectory())
179                 resourcesDir = EmptyResource.INSTANCE;
180 
181             if (cache != null)
182             {               
183                 Resource old  = cache.putIfAbsent(target, resourcesDir);
184                 if (old != null)
185                     resourcesDir = old;
186                 else
187                     if (LOG.isDebugEnabled()) LOG.debug(target+" META-INF/resources cache updated");
188             }
189 
190             if (resourcesDir == EmptyResource.INSTANCE)
191                 return;
192         }
193 
194         //add it to the meta inf resources for this context
195         Set<Resource> dirs = (Set<Resource>)context.getAttribute(METAINF_RESOURCES);
196         if (dirs == null)
197         {
198             dirs = new HashSet<Resource>();
199             context.setAttribute(METAINF_RESOURCES, dirs);
200         }
201         if (LOG.isDebugEnabled()) LOG.debug(resourcesDir+" added to context");
202         dirs.add(resourcesDir);
203     }
204     
205     /**
206      * Scan for META-INF/web-fragment.xml file in the given jar.
207      * 
208      * @param context
209      * @param jar
210      * @param cache
211      * @throws Exception
212      */
213     public void scanForFragment (WebAppContext context, Resource jar, ConcurrentHashMap<Resource,Resource> cache)
214     throws Exception
215     {
216         Resource webFrag = null;
217         if (cache != null && cache.containsKey(jar))
218         {
219             webFrag = cache.get(jar);  
220             if (webFrag == EmptyResource.INSTANCE)
221             {
222                 if (LOG.isDebugEnabled()) LOG.debug(jar+" cached as containing no META-INF/web-fragment.xml");
223                 return;     
224             }
225             else
226                 if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/web-fragment.xml found in cache ");
227         }
228         else
229         {
230             //not using caches or not in the cache so check for the web-fragment.xml
231             if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/web-fragment.xml checked");
232             if (jar.isDirectory())
233             {
234                 //TODO   ????
235                 webFrag = jar.addPath("/META-INF/web-fragment.xml");
236             }
237             else
238             {
239                 URI uri = jar.getURI();
240                 webFrag = Resource.newResource("jar:"+uri+"!/META-INF/web-fragment.xml");
241             }
242             if (!webFrag.exists() || webFrag.isDirectory())
243                 webFrag = EmptyResource.INSTANCE;
244             
245             if (cache != null)
246             {
247                 //web-fragment.xml doesn't exist: put token in cache to signal we've seen the jar               
248                 Resource old = cache.putIfAbsent(jar, webFrag);
249                 if (old != null)
250                     webFrag = old;
251                 else
252                     if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/web-fragment.xml cache updated");
253             }
254             
255             if (webFrag == EmptyResource.INSTANCE)
256                 return;
257         }
258 
259         Map<Resource, Resource> fragments = (Map<Resource,Resource>)context.getAttribute(METAINF_FRAGMENTS);
260         if (fragments == null)
261         {
262             fragments = new HashMap<Resource, Resource>();
263             context.setAttribute(METAINF_FRAGMENTS, fragments);
264         }
265         fragments.put(jar, webFrag);   
266         if (LOG.isDebugEnabled()) LOG.debug(webFrag+" added to context");
267     }
268     
269     
270     /**
271      * Discover META-INF/*.tld files in the given jar
272      * 
273      * @param context
274      * @param jar
275      * @param cache
276      * @throws Exception
277      */
278     public void scanForTlds (WebAppContext context, Resource jar, ConcurrentHashMap<Resource, Collection<URL>> cache)
279     throws Exception
280     {
281         Collection<URL> tlds = null;
282         
283         if (cache != null && cache.containsKey(jar))
284         {
285             Collection<URL> tmp = cache.get(jar);
286             if (tmp.isEmpty())
287             {
288                 if (LOG.isDebugEnabled()) LOG.debug(jar+" cached as containing no tlds");
289                 return;
290             }
291             else
292             {
293                 tlds = tmp;
294                 if (LOG.isDebugEnabled()) LOG.debug(jar+" tlds found in cache ");
295             }
296         }
297         else
298         {
299             //not using caches or not in the cache so find all tlds
300             Resource metaInfDir = null;
301             if (jar.isDirectory())
302             {
303                 //TODO ??????
304                 metaInfDir = jar.addPath("/META-INF/");
305             }
306             else
307             {
308                 URI uri = jar.getURI();
309                 metaInfDir = Resource.newResource("jar:"+uri+"!/META-INF/");
310             }
311 
312             //find any *.tld files inside META-INF or subdirs
313             tlds = new HashSet<URL>();      
314             Collection<Resource> resources = metaInfDir.getAllResources();
315             for (Resource t:resources)
316             {
317                 String name = t.toString();
318                 if (name.endsWith(".tld"))
319                 {
320                     if (LOG.isDebugEnabled()) LOG.debug(t+" tld discovered");
321                     tlds.add(t.getURL());
322                 }
323             }
324             if (cache != null)
325             {  
326                 if (LOG.isDebugEnabled()) LOG.debug(jar+" tld cache updated");
327                 Collection<URL> old = (Collection<URL>)cache.putIfAbsent(jar, tlds);
328                 if (old != null)
329                     tlds = old;
330             }
331             
332             if (tlds.isEmpty())
333                 return;
334         }
335 
336         Collection<URL> tld_resources=(Collection<URL>)context.getAttribute(METAINF_TLDS);
337         if (tld_resources == null)
338         {
339             tld_resources = new HashSet<URL>();
340             context.setAttribute(METAINF_TLDS, tld_resources);
341         }
342         tld_resources.addAll(tlds);  
343         if (LOG.isDebugEnabled()) LOG.debug("tlds added to context");
344     }
345     
346    
347     @Override
348     public void postConfigure(WebAppContext context) throws Exception
349     {
350         context.setAttribute(METAINF_FRAGMENTS, null); 
351         context.setAttribute(METAINF_RESOURCES, null);
352         context.setAttribute(METAINF_TLDS, null);
353     }
354 }