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.ArrayList;
25  import java.util.Collection;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.concurrent.ConcurrentHashMap;
31  
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  import org.eclipse.jetty.util.resource.EmptyResource;
35  import org.eclipse.jetty.util.resource.Resource;
36  
37  /**
38   * MetaInfConfiguration
39   *
40   * Scan META-INF of jars to find:
41   * <ul>
42   * <li>tlds
43   * <li>web-fragment.xml
44   * <li>resources
45   * </ul>
46   * 
47   * The jars which are scanned are:
48   * <ol>
49   * <li>those from the container classpath whose pattern matched the WebInfConfiguration.CONTAINER_JAR_PATTERN</li>
50   * <li>those from WEB-INF/lib</li>
51   * </ol>
52   */
53  public class MetaInfConfiguration extends AbstractConfiguration
54  {
55      private static final Logger LOG = Log.getLogger(MetaInfConfiguration.class);
56  
57      public static final String USE_CONTAINER_METAINF_CACHE = "org.eclipse.jetty.metainf.useCache";
58      public static final boolean DEFAULT_USE_CONTAINER_METAINF_CACHE = true;
59      public static final String CACHED_CONTAINER_TLDS = "org.eclipse.jetty.tlds.cache";
60      public static final String CACHED_CONTAINER_FRAGMENTS = FragmentConfiguration.FRAGMENT_RESOURCES+".cache";
61      public static final String CACHED_CONTAINER_RESOURCES = WebInfConfiguration.RESOURCE_DIRS+".cache";
62      public static final String METAINF_TLDS = "org.eclipse.jetty.tlds";
63      public static final String METAINF_FRAGMENTS = FragmentConfiguration.FRAGMENT_RESOURCES;
64      public static final String METAINF_RESOURCES = WebInfConfiguration.RESOURCE_DIRS;
65  
66      @Override
67      public void preConfigure(final WebAppContext context) throws Exception
68      {        
69          boolean useContainerCache = DEFAULT_USE_CONTAINER_METAINF_CACHE;
70          Boolean attr = (Boolean)context.getServer().getAttribute(USE_CONTAINER_METAINF_CACHE);
71          if (attr != null)
72              useContainerCache = attr.booleanValue();
73          
74          if (LOG.isDebugEnabled()) LOG.debug("{} = {}", USE_CONTAINER_METAINF_CACHE, useContainerCache);
75          scanJars(context, context.getMetaData().getContainerResources(), useContainerCache);
76          scanJars(context, context.getMetaData().getWebInfJars(), false);
77      }
78  
79      /**
80       * Look into the jars to discover info in META-INF. If useCaches == true, then we will
81       * cache the info discovered indexed by the jar in which it was discovered: this speeds
82       * up subsequent context deployments.
83       * 
84       * @param context
85       * @param jars
86       * @param useCaches
87       * @throws Exception
88       */
89      public void scanJars (final WebAppContext context, Collection<Resource> jars, boolean useCaches)
90      throws Exception
91      {
92          ConcurrentHashMap<Resource, Resource> metaInfResourceCache = null;       
93          ConcurrentHashMap<Resource, Resource> metaInfFragmentCache = null;
94          ConcurrentHashMap<Resource, Collection<URL>> metaInfTldCache = null;
95          if (useCaches)
96          {
97              metaInfResourceCache = (ConcurrentHashMap<Resource, Resource>)context.getServer().getAttribute(CACHED_CONTAINER_RESOURCES);
98              if (metaInfResourceCache == null)
99              {
100                 metaInfResourceCache = new ConcurrentHashMap<Resource,Resource>();
101                 context.getServer().setAttribute(CACHED_CONTAINER_RESOURCES, metaInfResourceCache);
102             }
103             metaInfFragmentCache = (ConcurrentHashMap<Resource, Resource>)context.getServer().getAttribute(CACHED_CONTAINER_FRAGMENTS);
104             if (metaInfFragmentCache == null)
105             {
106                 metaInfFragmentCache = new ConcurrentHashMap<Resource,Resource>();
107                 context.getServer().setAttribute(CACHED_CONTAINER_FRAGMENTS, metaInfFragmentCache);
108             }
109             metaInfTldCache = (ConcurrentHashMap<Resource, Collection<URL>>)context.getServer().getAttribute(CACHED_CONTAINER_TLDS);
110             if (metaInfTldCache == null)
111             {
112                 metaInfTldCache = new ConcurrentHashMap<Resource,Collection<URL>>(); 
113                 context.getServer().setAttribute(CACHED_CONTAINER_TLDS, metaInfTldCache);
114             }
115         }
116         
117         //Scan jars for META-INF information
118         if (jars != null)
119         {
120             for (Resource r : jars)
121             {
122                 
123                scanForResources(context, r, metaInfResourceCache);
124                scanForFragment(context, r, metaInfFragmentCache);
125                scanForTlds(context, r, metaInfTldCache);
126             }
127         }
128     }
129     
130     /**
131      * Scan for META-INF/resources dir in the given jar.
132      * 
133      * @param context
134      * @param jar
135      * @param cache
136      * @throws Exception
137      */
138     public void scanForResources (WebAppContext context, Resource jar, ConcurrentHashMap<Resource,Resource> cache)
139     throws Exception
140     {
141         Resource resourcesDir = null;
142         if (cache != null && cache.containsKey(jar))
143         {
144             resourcesDir = cache.get(jar);  
145             if (resourcesDir == EmptyResource.INSTANCE)
146             {
147                 if (LOG.isDebugEnabled()) LOG.debug(jar+" cached as containing no META-INF/resources");
148                 return;    
149             }
150             else
151                 if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/resources found in cache ");
152         }
153         else
154         {
155             //not using caches or not in the cache so check for the resources dir
156             if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/resources checked");
157             URI uri = jar.getURI();
158             resourcesDir = Resource.newResource("jar:"+uri+"!/META-INF/resources");
159             if (!resourcesDir.exists() || !resourcesDir.isDirectory())
160                 resourcesDir = EmptyResource.INSTANCE;
161 
162             if (cache != null)
163             {               
164                 Resource old  = cache.putIfAbsent(jar, resourcesDir);
165                 if (old != null)
166                     resourcesDir = old;
167                 else
168                     if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/resources cache updated");
169             }
170 
171             if (resourcesDir == EmptyResource.INSTANCE)
172                 return;
173         }
174 
175         //add it to the meta inf resources for this context
176         Set<Resource> dirs = (Set<Resource>)context.getAttribute(METAINF_RESOURCES);
177         if (dirs == null)
178         {
179             dirs = new HashSet<Resource>();
180             context.setAttribute(METAINF_RESOURCES, dirs);
181         }
182         if (LOG.isDebugEnabled()) LOG.debug(resourcesDir+" added to context");
183         dirs.add(resourcesDir);
184     }
185     
186     /**
187      * Scan for META-INF/web-fragment.xml file in the given jar.
188      * 
189      * @param context
190      * @param jar
191      * @param cache
192      * @throws Exception
193      */
194     public void scanForFragment (WebAppContext context, Resource jar, ConcurrentHashMap<Resource,Resource> cache)
195     throws Exception
196     {
197         Resource webFrag = null;
198         if (cache != null && cache.containsKey(jar))
199         {
200             webFrag = cache.get(jar);  
201             if (webFrag == EmptyResource.INSTANCE)
202             {
203                 if (LOG.isDebugEnabled()) LOG.debug(jar+" cached as containing no META-INF/web-fragment.xml");
204                 return;     
205             }
206             else
207                 if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/web-fragment.xml found in cache ");
208         }
209         else
210         {
211             //not using caches or not in the cache so check for the web-fragment.xml
212             if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/web-fragment.xml checked");
213             URI uri = jar.getURI();
214             webFrag = Resource.newResource("jar:"+uri+"!/META-INF/web-fragment.xml");
215             if (!webFrag.exists() || webFrag.isDirectory())
216                 webFrag = EmptyResource.INSTANCE;
217             
218             if (cache != null)
219             {
220                 //web-fragment.xml doesn't exist: put token in cache to signal we've seen the jar               
221                 Resource old = cache.putIfAbsent(jar, webFrag);
222                 if (old != null)
223                     webFrag = old;
224                 else
225                     if (LOG.isDebugEnabled()) LOG.debug(jar+" META-INF/web-fragment.xml cache updated");
226             }
227             
228             if (webFrag == EmptyResource.INSTANCE)
229                 return;
230         }
231 
232         Map<Resource, Resource> fragments = (Map<Resource,Resource>)context.getAttribute(METAINF_FRAGMENTS);
233         if (fragments == null)
234         {
235             fragments = new HashMap<Resource, Resource>();
236             context.setAttribute(METAINF_FRAGMENTS, fragments);
237         }
238         fragments.put(jar, webFrag);   
239         if (LOG.isDebugEnabled()) LOG.debug(webFrag+" added to context");
240     }
241     
242     
243     /**
244      * Discover META-INF/*.tld files in the given jar
245      * 
246      * @param context
247      * @param jar
248      * @param cache
249      * @throws Exception
250      */
251     public void scanForTlds (WebAppContext context, Resource jar, ConcurrentHashMap<Resource, Collection<URL>> cache)
252     throws Exception
253     {
254         Collection<URL> tlds = null;
255         
256         if (cache != null && cache.containsKey(jar))
257         {
258             Collection<URL> tmp = cache.get(jar);
259             if (tmp.isEmpty())
260             {
261                 if (LOG.isDebugEnabled()) LOG.debug(jar+" cached as containing no tlds");
262                 return;
263             }
264             else
265             {
266                 tlds = tmp;
267                 if (LOG.isDebugEnabled()) LOG.debug(jar+" tlds found in cache ");
268             }
269         }
270         else
271         {
272             //not using caches or not in the cache so find all tlds
273             URI uri = jar.getURI();
274             Resource metaInfDir = Resource.newResource("jar:"+uri+"!/META-INF/");
275 
276             //find any *.tld files inside META-INF or subdirs
277             tlds = new HashSet<URL>();      
278             Collection<Resource> resources = metaInfDir.getAllResources();
279             for (Resource t:resources)
280             {
281                 String name = t.toString();
282                 if (name.endsWith(".tld"))
283                 {
284                     if (LOG.isDebugEnabled()) LOG.debug(t+" tld discovered");
285                     tlds.add(t.getURL());
286                 }
287             }
288             if (cache != null)
289             {  
290                 if (LOG.isDebugEnabled()) LOG.debug(jar+" tld cache updated");
291                 Collection<URL> old = (Collection<URL>)cache.putIfAbsent(jar, tlds);
292                 if (old != null)
293                     tlds = old;
294             }
295             
296             if (tlds.isEmpty())
297                 return;
298         }
299 
300         Collection<URL> tld_resources=(Collection<URL>)context.getAttribute(METAINF_TLDS);
301         if (tld_resources == null)
302         {
303             tld_resources = new HashSet<URL>();
304             context.setAttribute(METAINF_TLDS, tld_resources);
305         }
306         tld_resources.addAll(tlds);  
307         if (LOG.isDebugEnabled()) LOG.debug("tlds added to context");
308     }
309     
310    
311     @Override
312     public void postConfigure(WebAppContext context) throws Exception
313     {
314         context.setAttribute(METAINF_FRAGMENTS, null); 
315         context.setAttribute(METAINF_RESOURCES, null);
316         context.setAttribute(METAINF_TLDS, null);
317     }
318 }