// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.webapp; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletContext; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.EmptyResource; import org.eclipse.jetty.util.resource.Resource; /** * MetaData * * All data associated with the configuration and deployment of a web application. */ public class MetaData { private static final Logger LOG = Log.getLogger(MetaData.class); public static final String VALIDATE_XML = "org.eclipse.jetty.webapp.validateXml"; public static final String ORDERED_LIBS = "javax.servlet.context.orderedLibs"; public static final Resource NON_FRAG_RESOURCE = EmptyResource.INSTANCE; protected Map<String, OriginInfo> _origins =new HashMap<String,OriginInfo>(); protected WebDescriptor _webDefaultsRoot; protected WebDescriptor _webXmlRoot; protected final List<WebDescriptor> _webOverrideRoots=new ArrayList<WebDescriptor>(); protected boolean _metaDataComplete; protected final List<DescriptorProcessor> _descriptorProcessors = new ArrayList<DescriptorProcessor>(); protected final List<FragmentDescriptor> _webFragmentRoots = new ArrayList<FragmentDescriptor>(); protected final Map<String,FragmentDescriptor> _webFragmentNameMap = new HashMap<String,FragmentDescriptor>(); protected final Map<Resource, FragmentDescriptor> _webFragmentResourceMap = new HashMap<Resource, FragmentDescriptor>(); protected final Map<Resource, List<DiscoveredAnnotation>> _annotations = new HashMap<Resource, List<DiscoveredAnnotation>>(); protected final List<Resource> _webInfClasses = new ArrayList<Resource>(); protected final List<Resource> _webInfJars = new ArrayList<Resource>(); protected final List<Resource> _orderedContainerResources = new ArrayList<Resource>(); protected final List<Resource> _orderedWebInfResources = new ArrayList<Resource>(); protected Ordering _ordering;//can be set to RelativeOrdering by web-default.xml, web.xml, web-override.xml protected boolean _allowDuplicateFragmentNames = false; protected boolean _validateXml = false; public static class OriginInfo { private final String name; private final Origin origin; private final Descriptor descriptor; private final Annotation annotation; private final Class<?> annotated; public OriginInfo (String n, Annotation a,Class<?> ac) { name=n; origin=Origin.Annotation; descriptor=null; annotation=a; annotated=ac; } public OriginInfo (String n, Descriptor d) { name = n; descriptor = d; annotation=null; annotated=null; if (d == null) throw new IllegalArgumentException("No descriptor"); if (d instanceof FragmentDescriptor) origin = Origin.WebFragment; else if (d instanceof OverrideDescriptor) origin = Origin.WebOverride; else if (d instanceof DefaultsDescriptor) origin = Origin.WebDefaults; else origin = Origin.WebXml; } public OriginInfo(String n) { name = n; origin = Origin.API; annotation=null; descriptor=null; annotated=null; } public String getName() { return name; } public Origin getOriginType() { return origin; } public Descriptor getDescriptor() { return descriptor; } public String toString() { if (descriptor!=null) return descriptor.toString(); if (annotation!=null) return "@"+annotation.annotationType().getSimpleName()+"("+annotated.getName()+")"; return origin.toString(); } } public MetaData () { } /** * Empty ready for reuse */ public void clear () { _webDefaultsRoot = null; _origins.clear(); _webXmlRoot = null; _webOverrideRoots.clear(); _metaDataComplete = false; _annotations.clear(); _descriptorProcessors.clear(); _webFragmentRoots.clear(); _webFragmentNameMap.clear(); _webFragmentResourceMap.clear(); _annotations.clear(); _webInfJars.clear(); _orderedWebInfResources.clear(); _orderedContainerResources.clear(); _ordering = null; _allowDuplicateFragmentNames = false; } public void setDefaults (Resource webDefaults) throws Exception { _webDefaultsRoot = new DefaultsDescriptor(webDefaults); _webDefaultsRoot.setValidating(isValidateXml()); _webDefaultsRoot.parse(); if (_webDefaultsRoot.isOrdered()) { Ordering ordering = getOrdering(); if (ordering == null) ordering = new AbsoluteOrdering(this); List<String> order = _webDefaultsRoot.getOrdering(); for (String s:order) { if (s.equalsIgnoreCase("others")) ((AbsoluteOrdering)ordering).addOthers(); else ((AbsoluteOrdering)ordering).add(s); } //(re)set the ordering to cause webinf jar order to be recalculated setOrdering(ordering); } } public void setWebXml (Resource webXml) throws Exception { _webXmlRoot = new WebDescriptor(webXml); _webXmlRoot.setValidating(isValidateXml()); _webXmlRoot.parse(); _metaDataComplete=_webXmlRoot.getMetaDataComplete() == MetaDataComplete.True; if (_webXmlRoot.isOrdered()) { Ordering ordering = getOrdering(); if (ordering == null) ordering = new AbsoluteOrdering(this); List<String> order = _webXmlRoot.getOrdering(); for (String s:order) { if (s.equalsIgnoreCase("others")) ((AbsoluteOrdering)ordering).addOthers(); else ((AbsoluteOrdering)ordering).add(s); } //(re)set the ordering to cause webinf jar order to be recalculated setOrdering(ordering); } } public void addOverride (Resource override) throws Exception { OverrideDescriptor webOverrideRoot = new OverrideDescriptor(override); webOverrideRoot.setValidating(false); webOverrideRoot.parse(); switch(webOverrideRoot.getMetaDataComplete()) { case True: _metaDataComplete=true; break; case False: _metaDataComplete=false; break; case NotSet: break; } if (webOverrideRoot.isOrdered()) { Ordering ordering = getOrdering(); if (ordering == null) ordering = new AbsoluteOrdering(this); List<String> order = webOverrideRoot.getOrdering(); for (String s:order) { if (s.equalsIgnoreCase("others")) ((AbsoluteOrdering)ordering).addOthers(); else ((AbsoluteOrdering)ordering).add(s); } //set or reset the ordering to cause the webinf jar ordering to be recomputed setOrdering(ordering); } _webOverrideRoots.add(webOverrideRoot); } /** * Add a web-fragment.xml * * @param jarResource the jar the fragment is contained in * @param xmlResource the resource representing the xml file * @throws Exception if unable to add fragment */ public void addFragment (Resource jarResource, Resource xmlResource) throws Exception { if (_metaDataComplete) return; //do not process anything else if web.xml/web-override.xml set metadata-complete //Metadata-complete is not set, or there is no web.xml FragmentDescriptor descriptor = new FragmentDescriptor(xmlResource); _webFragmentResourceMap.put(jarResource, descriptor); _webFragmentRoots.add(descriptor); descriptor.setValidating(isValidateXml()); descriptor.parse(); if (descriptor.getName() != null) { Descriptor existing = _webFragmentNameMap.get(descriptor.getName()); if (existing != null && !isAllowDuplicateFragmentNames()) { throw new IllegalStateException("Duplicate fragment name: "+descriptor.getName()+" for "+existing.getResource()+" and "+descriptor.getResource()); } else _webFragmentNameMap.put(descriptor.getName(), descriptor); } //only accept an ordering from the fragment if there is no ordering already established if (_ordering == null && descriptor.isOrdered()) { setOrdering(new RelativeOrdering(this)); return; } //recompute the ordering with the new fragment name orderFragments(); } /** * Annotations not associated with a WEB-INF/lib fragment jar. * These are from WEB-INF/classes or the ??container path?? * @param annotations the list of discovered annotations to add */ public void addDiscoveredAnnotations(List<DiscoveredAnnotation> annotations) { if (annotations == null) return; for (DiscoveredAnnotation a:annotations) { addDiscoveredAnnotation(a); } } /** * Add an annotation that has been discovered on a class, method or field within a resource * eg a jar or dir. * * This method is synchronized as it is anticipated that it may be called by many threads * during the annotation scanning phase. * * @param annotation the discovered annotation */ public synchronized void addDiscoveredAnnotation (DiscoveredAnnotation annotation) { if (annotation == null) return; //if no resource associated with an annotation or the resource is not one of the WEB-INF/lib jars, //map it to empty resource Resource resource = annotation.getResource(); if (resource == null || !_webInfJars.contains(resource)) resource = EmptyResource.INSTANCE; List<DiscoveredAnnotation> list = _annotations.get(resource); if (list == null) { list = new ArrayList<DiscoveredAnnotation>(); _annotations.put(resource, list); } list.add(annotation); } public void addDescriptorProcessor(DescriptorProcessor p) { _descriptorProcessors.add(p); } public void removeDescriptorProcessor(DescriptorProcessor p) { _descriptorProcessors.remove(p); } public void orderFragments () { _orderedWebInfResources.clear(); if (getOrdering() != null) _orderedWebInfResources.addAll(getOrdering().order(_webInfJars)); } /** * Resolve all servlet/filter/listener metadata from all sources: descriptors and annotations. * * @param context the context to resolve servlets / filters / listeners metadata from * @throws Exception if unable to resolve metadata */ public void resolve (WebAppContext context) throws Exception { LOG.debug("metadata resolve {}",context); //Ensure origins is fresh _origins.clear(); // Set the ordered lib attribute List<Resource> orderedWebInfJars = null; if (getOrdering() != null) { orderedWebInfJars = getOrderedWebInfJars(); List<String> orderedLibs = new ArrayList<String>(); for (Resource webInfJar:orderedWebInfJars) { //get just the name of the jar file String fullname = webInfJar.getName(); int i = fullname.indexOf(".jar"); int j = fullname.lastIndexOf("/", i); orderedLibs.add(fullname.substring(j+1,i+4)); } context.setAttribute(ServletContext.ORDERED_LIBS, orderedLibs); } // set the webxml version if (_webXmlRoot != null) { context.getServletContext().setEffectiveMajorVersion(_webXmlRoot.getMajorVersion()); context.getServletContext().setEffectiveMinorVersion(_webXmlRoot.getMinorVersion()); } for (DescriptorProcessor p:_descriptorProcessors) { p.process(context,getWebDefault()); p.process(context,getWebXml()); for (WebDescriptor wd : getOverrideWebs()) { LOG.debug("process {} {}",context,wd); p.process(context,wd); } } //get an apply the annotations that are not associated with a fragment (and hence for //which no ordering applies List<DiscoveredAnnotation> nonFragAnnotations = _annotations.get(NON_FRAG_RESOURCE); if (nonFragAnnotations != null) { for (DiscoveredAnnotation a:nonFragAnnotations) { LOG.debug("apply {}",a); a.apply(); } } //apply the annotations that are associated with a fragment, according to the //established ordering List<Resource> resources = null; if (getOrdering() != null) resources = orderedWebInfJars; else resources = getWebInfJars(); for (Resource r:resources) { FragmentDescriptor fd = _webFragmentResourceMap.get(r); if (fd != null) { for (DescriptorProcessor p:_descriptorProcessors) { LOG.debug("process {} {}",context,fd); p.process(context,fd); } } List<DiscoveredAnnotation> fragAnnotations = _annotations.get(r); if (fragAnnotations != null) { for (DiscoveredAnnotation a:fragAnnotations) { LOG.debug("apply {}",a); a.apply(); } } } } public boolean isDistributable () { boolean distributable = ( (_webDefaultsRoot != null && _webDefaultsRoot.isDistributable()) || (_webXmlRoot != null && _webXmlRoot.isDistributable())); for (WebDescriptor d : _webOverrideRoots) distributable&=d.isDistributable(); if (getOrdering() != null) { List<Resource> orderedResources = getOrderedWebInfJars(); for (Resource r: orderedResources) { FragmentDescriptor d = _webFragmentResourceMap.get(r); if (d!=null) distributable = distributable && d.isDistributable(); } } return distributable; } public WebDescriptor getWebXml () { return _webXmlRoot; } public List<WebDescriptor> getOverrideWebs () { return _webOverrideRoots; } public WebDescriptor getWebDefault () { return _webDefaultsRoot; } public List<FragmentDescriptor> getFragments () { return _webFragmentRoots; } public List<Resource> getOrderedWebInfJars() { return _orderedWebInfResources; } public List<FragmentDescriptor> getOrderedFragments () { List<FragmentDescriptor> list = new ArrayList<FragmentDescriptor>(); if (getOrdering() == null) return list; for (Resource r:getOrderedWebInfJars()) { FragmentDescriptor fd = _webFragmentResourceMap.get(r); if (fd != null) list.add(fd); } return list; } public Ordering getOrdering() { return _ordering; } public void setOrdering (Ordering o) { _ordering = o; orderFragments(); } public FragmentDescriptor getFragment (Resource jar) { return _webFragmentResourceMap.get(jar); } public FragmentDescriptor getFragment(String name) { return _webFragmentNameMap.get(name); } public Resource getJarForFragment (String name) { FragmentDescriptor f = getFragment(name); if (f == null) return null; Resource jar = null; for (Resource r: _webFragmentResourceMap.keySet()) { if (_webFragmentResourceMap.get(r).equals(f)) jar = r; } return jar; } public Map<String,FragmentDescriptor> getNamedFragments () { return Collections.unmodifiableMap(_webFragmentNameMap); } public Origin getOrigin (String name) { OriginInfo x = _origins.get(name); if (x == null) return Origin.NotSet; return x.getOriginType(); } public OriginInfo getOriginInfo (String name) { OriginInfo x = _origins.get(name); if (x == null) return null; return x; } public Descriptor getOriginDescriptor (String name) { OriginInfo o = _origins.get(name); if (o == null) return null; return o.getDescriptor(); } public void setOrigin (String name, Descriptor d) { OriginInfo x = new OriginInfo (name, d); _origins.put(name, x); } public void setOrigin (String name, Annotation annotation, Class<?> annotated) { if (name == null) return; OriginInfo x = new OriginInfo (name, annotation, annotated); _origins.put(name, x); } public void setOriginAPI(String name) { if (name == null) return; OriginInfo x = new OriginInfo (name); _origins.put(name, x); } public boolean isMetaDataComplete() { return _metaDataComplete; } public void addWebInfJar(Resource newResource) { _webInfJars.add(newResource); } public List<Resource> getWebInfJars() { return Collections.unmodifiableList(_webInfJars); } public List<Resource> getContainerResources() { return _orderedContainerResources; } public void addContainerResource(Resource jar) { _orderedContainerResources.add(jar); } public void setWebInfClassesDirs (List<Resource> dirs) { _webInfClasses.addAll(dirs); } public List<Resource> getWebInfClassesDirs () { return _webInfClasses; } public boolean isAllowDuplicateFragmentNames() { return _allowDuplicateFragmentNames; } public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames) { this._allowDuplicateFragmentNames = allowDuplicateFragmentNames; } /** * @return the validateXml */ public boolean isValidateXml() { return _validateXml; } /** * @param validateXml the validateXml to set */ public void setValidateXml(boolean validateXml) { _validateXml = validateXml; } public Map<String,OriginInfo> getOrigins() { return Collections.unmodifiableMap(_origins); } }