blob: 522cf36e709d5ce4bc3f5c325031d64c425481a4 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2016 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 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;
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()+" on "+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.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.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.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;
}
public Map<String,OriginInfo> getOrigins()
{
return Collections.unmodifiableMap(_origins);
}
}