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  import java.lang.annotation.Annotation;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.servlet.ServletContext;
29  
30  import org.eclipse.jetty.util.log.Log;
31  import org.eclipse.jetty.util.log.Logger;
32  import org.eclipse.jetty.util.resource.EmptyResource;
33  import org.eclipse.jetty.util.resource.Resource;
34  
35  
36  
37  
38  /**
39   * MetaData
40   *
41   * All data associated with the configuration and deployment of a web application.
42   */
43  public class MetaData
44  {
45      private static final Logger LOG = Log.getLogger(MetaData.class);
46  
47      public static final String ORDERED_LIBS = "javax.servlet.context.orderedLibs";
48      public static final Resource NON_FRAG_RESOURCE = EmptyResource.INSTANCE;
49  
50      protected Map<String, OriginInfo> _origins  =new HashMap<String,OriginInfo>();
51      protected WebDescriptor _webDefaultsRoot;
52      protected WebDescriptor _webXmlRoot;
53      protected final List<WebDescriptor> _webOverrideRoots=new ArrayList<WebDescriptor>();
54      protected boolean _metaDataComplete;  
55      protected final List<DescriptorProcessor> _descriptorProcessors = new ArrayList<DescriptorProcessor>();
56      protected final List<FragmentDescriptor> _webFragmentRoots = new ArrayList<FragmentDescriptor>();
57      protected final Map<String,FragmentDescriptor> _webFragmentNameMap = new HashMap<String,FragmentDescriptor>();
58      protected final Map<Resource, FragmentDescriptor> _webFragmentResourceMap = new HashMap<Resource, FragmentDescriptor>();
59      protected final Map<Resource, List<DiscoveredAnnotation>> _annotations = new HashMap<Resource, List<DiscoveredAnnotation>>();
60      protected final List<Resource> _webInfClasses = new ArrayList<Resource>();
61      protected final List<Resource> _webInfJars = new ArrayList<Resource>();
62      protected final List<Resource> _orderedContainerResources = new ArrayList<Resource>();
63      protected final List<Resource> _orderedWebInfResources = new ArrayList<Resource>();
64      protected Ordering _ordering;//can be set to RelativeOrdering by web-default.xml, web.xml, web-override.xml
65      protected boolean allowDuplicateFragmentNames = false;
66  
67  
68  
69  
70  
71      public static class OriginInfo
72      {
73          private final String name;
74          private final Origin origin;
75          private final Descriptor descriptor;
76          private final Annotation annotation;
77          private final Class<?> annotated;
78  
79          public OriginInfo (String n, Annotation a,Class<?> ac)
80          {
81              name=n;
82              origin=Origin.Annotation;
83              descriptor=null;
84              annotation=a;
85              annotated=ac;
86          }
87          
88          public OriginInfo (String n, Descriptor d)
89          {
90              name = n;
91              descriptor = d;
92              annotation=null;
93              annotated=null;
94              if (d == null)
95                  throw new IllegalArgumentException("No descriptor");
96              if (d instanceof FragmentDescriptor)
97                  origin = Origin.WebFragment;
98              else if (d instanceof OverrideDescriptor)
99                  origin =  Origin.WebOverride;
100             else if (d instanceof DefaultsDescriptor)
101                 origin =  Origin.WebDefaults;
102             else
103                 origin = Origin.WebXml;
104         }
105 
106         public OriginInfo(String n)
107         {
108             name = n;
109             origin = Origin.API;
110             annotation=null;
111             descriptor=null;
112             annotated=null;
113         }
114 
115         public String getName()
116         {
117             return name;
118         }
119 
120         public Origin getOriginType()
121         {
122             return origin;
123         }
124 
125         public Descriptor getDescriptor()
126         {
127             return descriptor;
128         }
129         
130         public String toString()
131         {
132             if (descriptor!=null)
133                 return descriptor.toString();
134             if (annotation!=null)
135                 return "@"+annotation.annotationType().getSimpleName()+" on "+annotated.getName();
136             return origin.toString();
137         }
138     }
139 
140     public MetaData ()
141     {
142     }
143 
144     /**
145      * Empty ready for reuse
146      */
147     public void clear ()
148     {
149         _webDefaultsRoot = null;
150         _origins.clear();
151         _webXmlRoot = null;
152         _webOverrideRoots.clear();
153         _metaDataComplete = false;
154         _annotations.clear();
155         _descriptorProcessors.clear();
156         _webFragmentRoots.clear();
157         _webFragmentNameMap.clear();
158         _webFragmentResourceMap.clear();
159         _annotations.clear();
160         _webInfJars.clear();
161         _orderedWebInfResources.clear();
162         _orderedContainerResources.clear();
163         _ordering = null;
164         allowDuplicateFragmentNames = false;
165     }
166 
167     public void setDefaults (Resource webDefaults)
168     throws Exception
169     {
170         _webDefaultsRoot =  new DefaultsDescriptor(webDefaults);
171         _webDefaultsRoot.parse();
172         if (_webDefaultsRoot.isOrdered())
173         {
174             Ordering ordering = getOrdering();
175             if (ordering == null)
176                ordering = new Ordering.AbsoluteOrdering(this);
177 
178             List<String> order = _webDefaultsRoot.getOrdering();
179             for (String s:order)
180             {
181                 if (s.equalsIgnoreCase("others"))
182                     ((Ordering.AbsoluteOrdering)ordering).addOthers();
183                 else
184                     ((Ordering.AbsoluteOrdering)ordering).add(s);
185             }
186             
187             //(re)set the ordering to cause webinf jar order to be recalculated
188             setOrdering(ordering);
189         }
190     }
191 
192     public void setWebXml (Resource webXml)
193     throws Exception
194     {
195         _webXmlRoot = new WebDescriptor(webXml);
196         _webXmlRoot.parse();
197         _metaDataComplete=_webXmlRoot.getMetaDataComplete() == MetaDataComplete.True;
198 
199         if (_webXmlRoot.isOrdered())
200         {
201             Ordering ordering = getOrdering();
202             if (ordering == null)
203                 ordering = new Ordering.AbsoluteOrdering(this);
204 
205             List<String> order = _webXmlRoot.getOrdering();
206             for (String s:order)
207             {
208                 if (s.equalsIgnoreCase("others"))
209                     ((Ordering.AbsoluteOrdering)ordering).addOthers();
210                 else
211                     ((Ordering.AbsoluteOrdering)ordering).add(s);
212             }
213             
214             //(re)set the ordering to cause webinf jar order to be recalculated
215             setOrdering(ordering);
216         }
217     }
218 
219     public void addOverride (Resource override)
220     throws Exception
221     {
222         OverrideDescriptor webOverrideRoot = new OverrideDescriptor(override);
223         webOverrideRoot.setValidating(false);
224         webOverrideRoot.parse();
225 
226         switch(webOverrideRoot.getMetaDataComplete())
227         {
228             case True:
229                 _metaDataComplete=true;
230                 break;
231             case False:
232                 _metaDataComplete=false;
233                 break;
234             case NotSet:
235                 break;
236         }
237 
238         if (webOverrideRoot.isOrdered())
239         {
240             Ordering ordering = getOrdering();
241             
242             if (ordering == null)
243                ordering = new Ordering.AbsoluteOrdering(this);
244 
245             List<String> order = webOverrideRoot.getOrdering();
246             for (String s:order)
247             {
248                 if (s.equalsIgnoreCase("others"))
249                     ((Ordering.AbsoluteOrdering)ordering).addOthers();
250                 else
251                     ((Ordering.AbsoluteOrdering)ordering).add(s);
252             }
253             
254             //set or reset the ordering to cause the webinf jar ordering to be recomputed
255             setOrdering(ordering);
256         }
257         _webOverrideRoots.add(webOverrideRoot);
258     }
259 
260 
261     /**
262      * Add a web-fragment.xml
263      *
264      * @param jarResource the jar the fragment is contained in
265      * @param xmlResource the resource representing the xml file
266      * @throws Exception
267      */
268     public void addFragment (Resource jarResource, Resource xmlResource)
269     throws Exception
270     {
271         if (_metaDataComplete)
272             return; //do not process anything else if web.xml/web-override.xml set metadata-complete
273 
274         //Metadata-complete is not set, or there is no web.xml
275         FragmentDescriptor descriptor = new FragmentDescriptor(xmlResource);
276         _webFragmentResourceMap.put(jarResource, descriptor);
277         _webFragmentRoots.add(descriptor);
278 
279         descriptor.parse();
280 
281         if (descriptor.getName() != null)
282         {
283             Descriptor existing = _webFragmentNameMap.get(descriptor.getName());
284             if (existing != null && !isAllowDuplicateFragmentNames())
285             {
286                 throw new IllegalStateException("Duplicate fragment name: "+descriptor.getName()+" for "+existing.getResource()+" and "+descriptor.getResource());
287             }
288             else
289                 _webFragmentNameMap.put(descriptor.getName(), descriptor);
290         }
291 
292 
293         //only accept an ordering from the fragment if there is no ordering already established
294         if (_ordering == null && descriptor.isOrdered())
295         {
296             setOrdering(new Ordering.RelativeOrdering(this));
297             return;
298         }
299         
300         //recompute the ordering with the new fragment name
301         orderFragments();
302     }
303 
304     /**
305      * Annotations not associated with a WEB-INF/lib fragment jar.
306      * These are from WEB-INF/classes or the ??container path??
307      * @param annotations
308      */
309     public void addDiscoveredAnnotations(List<DiscoveredAnnotation> annotations)
310     {
311         if (annotations == null)
312             return;
313         for (DiscoveredAnnotation a:annotations)
314         {
315             addDiscoveredAnnotation(a);       
316         }
317     }
318 
319     
320     /**
321      * Add an annotation that has been discovered on a class, method or field within a resource
322      * eg a jar or dir.
323      * 
324      * This method is synchronized as it is anticipated that it may be called by many threads
325      * during the annotation scanning phase.
326      * 
327      * @param annotation
328      */
329     public synchronized void addDiscoveredAnnotation (DiscoveredAnnotation annotation)
330     {
331         if (annotation == null)
332             return;
333         
334         //if no resource associated with an annotation or the resource is not one of the WEB-INF/lib jars,
335         //map it to empty resource
336         Resource resource = annotation.getResource();
337         if (resource == null ||  !_webInfJars.contains(resource))
338             resource = EmptyResource.INSTANCE;
339 
340         List<DiscoveredAnnotation> list = _annotations.get(resource);
341         if (list == null)
342         {
343             list = new ArrayList<DiscoveredAnnotation>();
344             _annotations.put(resource, list);
345         }
346         list.add(annotation);           
347     }
348 
349 
350     public void addDescriptorProcessor(DescriptorProcessor p)
351     {
352         _descriptorProcessors.add(p);
353     }
354     
355     public void removeDescriptorProcessor(DescriptorProcessor p)
356     {
357         _descriptorProcessors.remove(p);
358     }
359 
360     
361     public void orderFragments ()
362     {
363         _orderedWebInfResources.clear();
364         if (getOrdering() != null)
365             _orderedWebInfResources.addAll(getOrdering().order(_webInfJars));
366     }
367 
368 
369     /**
370      * Resolve all servlet/filter/listener metadata from all sources: descriptors and annotations.
371      *
372      */
373     public void resolve (WebAppContext context)
374     throws Exception
375     {
376         LOG.debug("metadata resolve {}",context);
377 
378         //Ensure origins is fresh
379         _origins.clear();
380 
381         // Set the ordered lib attribute
382         List<Resource> orderedWebInfJars = null;
383         if (getOrdering() != null)
384         {
385             orderedWebInfJars = getOrderedWebInfJars();
386             List<String> orderedLibs = new ArrayList<String>();
387             for (Resource webInfJar:orderedWebInfJars)
388             {
389                 //get just the name of the jar file
390                 String fullname = webInfJar.getName();
391                 int i = fullname.indexOf(".jar");
392                 int j = fullname.lastIndexOf("/", i);
393                 orderedLibs.add(fullname.substring(j+1,i+4));
394             }
395             context.setAttribute(ServletContext.ORDERED_LIBS, orderedLibs);
396         }
397 
398         // set the webxml version
399         if (_webXmlRoot != null)
400         {
401             context.getServletContext().setEffectiveMajorVersion(_webXmlRoot.getMajorVersion());
402             context.getServletContext().setEffectiveMinorVersion(_webXmlRoot.getMinorVersion());
403         }
404 
405         for (DescriptorProcessor p:_descriptorProcessors)
406         {
407             p.process(context,getWebDefault());
408             p.process(context,getWebXml());
409             for (WebDescriptor wd : getOverrideWebs())
410             {
411                 LOG.debug("process {} {}",context,wd);
412                 p.process(context,wd);
413             }
414         }
415 
416         //get an apply the annotations that are not associated with a fragment (and hence for
417         //which no ordering applies
418         List<DiscoveredAnnotation> nonFragAnnotations = _annotations.get(NON_FRAG_RESOURCE);
419         if (nonFragAnnotations != null)
420         {
421             for (DiscoveredAnnotation a:nonFragAnnotations)
422             {
423                 LOG.debug("apply {}",a);
424                 a.apply();
425             }
426         }
427       
428         //apply the annotations that are associated with a fragment, according to the 
429         //established ordering
430         List<Resource> resources = null;
431         
432         if (getOrdering() != null)
433             resources = orderedWebInfJars;
434         else
435             resources = getWebInfJars();
436         
437         for (Resource r:resources)
438         {
439             FragmentDescriptor fd = _webFragmentResourceMap.get(r);
440             if (fd != null)
441             {
442                 for (DescriptorProcessor p:_descriptorProcessors)
443                 {
444                     LOG.debug("process {} {}",context,fd);
445                     p.process(context,fd);
446                 }
447             }
448 
449             List<DiscoveredAnnotation> fragAnnotations = _annotations.get(r);
450             if (fragAnnotations != null)
451             {
452                 for (DiscoveredAnnotation a:fragAnnotations)
453                 {
454                     LOG.debug("apply {}",a);
455                     a.apply();
456                 }
457             }
458         }
459 
460     }
461 
462     public boolean isDistributable ()
463     {
464         boolean distributable = (
465                 (_webDefaultsRoot != null && _webDefaultsRoot.isDistributable())
466                 || (_webXmlRoot != null && _webXmlRoot.isDistributable()));
467 
468         for (WebDescriptor d : _webOverrideRoots)
469             distributable&=d.isDistributable();
470 
471         if (getOrdering() != null)
472         {
473             List<Resource> orderedResources = getOrderedWebInfJars();
474             for (Resource r: orderedResources)
475             {
476                 FragmentDescriptor d = _webFragmentResourceMap.get(r);
477                 if (d!=null)
478                     distributable = distributable && d.isDistributable();
479             }
480         }
481         return distributable;
482     }
483 
484 
485     public WebDescriptor getWebXml ()
486     {
487         return _webXmlRoot;
488     }
489 
490     public List<WebDescriptor> getOverrideWebs ()
491     {
492         return _webOverrideRoots;
493     }
494 
495     public WebDescriptor getWebDefault ()
496     {
497         return _webDefaultsRoot;
498     }
499 
500     public List<FragmentDescriptor> getFragments ()
501     {
502         return _webFragmentRoots;
503     }
504 
505     public List<Resource> getOrderedWebInfJars()
506     {
507         return _orderedWebInfResources;
508     }
509 
510     public List<FragmentDescriptor> getOrderedFragments ()
511     {
512         List<FragmentDescriptor> list = new ArrayList<FragmentDescriptor>();
513         if (getOrdering() == null)
514             return list;
515 
516         for (Resource r:getOrderedWebInfJars())
517         {
518             FragmentDescriptor fd = _webFragmentResourceMap.get(r);
519             if (fd != null)
520                 list.add(fd);
521         }
522         return list;
523     }
524 
525     public Ordering getOrdering()
526     {
527         return _ordering;
528     }
529 
530     public void setOrdering (Ordering o)
531     {    
532         _ordering = o;
533         orderFragments();
534     }
535 
536     public FragmentDescriptor getFragment (Resource jar)
537     {
538         return _webFragmentResourceMap.get(jar);
539     }
540 
541     public FragmentDescriptor getFragment(String name)
542     {
543         return _webFragmentNameMap.get(name);
544     }
545 
546     public Resource getJarForFragment (String name)
547     {
548         FragmentDescriptor f = getFragment(name);
549         if (f == null)
550             return null;
551 
552         Resource jar = null;
553         for (Resource r: _webFragmentResourceMap.keySet())
554         {
555             if (_webFragmentResourceMap.get(r).equals(f))
556                 jar = r;
557         }
558         return jar;
559     }
560 
561     public Map<String,FragmentDescriptor> getNamedFragments ()
562     {
563         return Collections.unmodifiableMap(_webFragmentNameMap);
564     }
565 
566 
567     public Origin getOrigin (String name)
568     {
569         OriginInfo x =  _origins.get(name);
570         if (x == null)
571             return Origin.NotSet;
572 
573         return x.getOriginType();
574     }
575 
576     public OriginInfo getOriginInfo (String name)
577     {
578         OriginInfo x =  _origins.get(name);
579         if (x == null)
580             return null;
581 
582         return x;
583     }
584 
585     public Descriptor getOriginDescriptor (String name)
586     {
587         OriginInfo o = _origins.get(name);
588         if (o == null)
589             return null;
590         return o.getDescriptor();
591     }
592 
593     public void setOrigin (String name, Descriptor d)
594     {
595         OriginInfo x = new OriginInfo (name, d);
596         _origins.put(name, x);
597     }
598 
599     public void setOrigin (String name, Annotation annotation, Class<?> annotated)
600     {
601         if (name == null)
602             return;
603 
604         OriginInfo x = new OriginInfo (name, annotation, annotated);
605         _origins.put(name, x);
606     }
607     
608     public void setOriginAPI(String name)
609     {
610         if (name == null)
611             return;
612        
613         OriginInfo x = new OriginInfo (name);
614         _origins.put(name, x);
615     }
616 
617     public boolean isMetaDataComplete()
618     {
619         return _metaDataComplete;
620     }
621 
622 
623     public void addWebInfJar(Resource newResource)
624     {
625         _webInfJars.add(newResource);
626     }
627 
628     public List<Resource> getWebInfJars()
629     {
630         return Collections.unmodifiableList(_webInfJars);
631     }
632 
633     public List<Resource> getContainerResources()
634     {
635         return _orderedContainerResources;
636     }
637 
638     public void addContainerResource(Resource jar)
639     {
640         _orderedContainerResources.add(jar);
641     }
642     
643     public void setWebInfClassesDirs (List<Resource> dirs)
644     {
645         _webInfClasses.addAll(dirs);
646     }
647     
648     public List<Resource> getWebInfClassesDirs ()
649     {
650         return _webInfClasses;
651     }
652     
653     public boolean isAllowDuplicateFragmentNames()
654     {
655         return allowDuplicateFragmentNames;
656     }
657 
658     public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames)
659     {
660         this.allowDuplicateFragmentNames = allowDuplicateFragmentNames;
661     }
662 
663     public Map<String,OriginInfo> getOrigins()
664     {
665         return Collections.unmodifiableMap(_origins);
666     }
667 }