View Javadoc

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