org.eclipse.jetty.webapp.WebXmlProcessor Maven / Gradle / Ivy
// ========================================================================
// Copyright (c) 2009 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.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlParser;
/**
* WebXmlProcessor
*
*
*/
public class WebXmlProcessor
{
public static final String WEB_PROCESSOR = "org.eclipse.jetty.webProcessor";
public static final String METADATA_COMPLETE = "org.eclipse.jetty.metadataComplete";
public static final String WEBXML_MAJOR_VERSION = "org.eclipse.jetty.webXmlMajorVersion";
public static final String WEBXML_MINOR_VERSION = "org.eclipse.jetty.webXmlMinorVersion";
public static final String WEBXML_CLASSNAMES = "org.eclipse.jetty.webXmlClassNames";
public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment};
protected WebAppContext _context;
protected Map _origins = new HashMap();
protected Descriptor _webDefaultsRoot;
protected Descriptor _webXmlRoot;
protected Descriptor _webOverrideRoot;
protected List _webFragmentRoots = new ArrayList();
protected Map _webFragmentNameMap = new HashMap();
protected List _orderedFragments = new LinkedList();
protected XmlParser _parser;
protected Ordering _ordering;//can be set to RelativeOrdering by web-default.xml, web.xml, web-override.xml
protected StandardDescriptorProcessor _standardDescriptorProcessor;
public static XmlParser newParser()
throws ClassNotFoundException
{
XmlParser xmlParser=new XmlParser();
//set up cache of DTDs and schemas locally
URL dtd22=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_2.dtd",true);
URL dtd23=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_3.dtd",true);
URL j2ee14xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/j2ee_1_4.xsd",true);
URL webapp24xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_4.xsd",true);
URL webapp25xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_5.xsd",true);
URL webapp30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_3_0.xsd",true);
URL webcommon30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-common_3_0.xsd",true);
URL webfragment30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-fragment_3_0.xsd",true);
URL schemadtd=Loader.getResource(Servlet.class,"javax/servlet/resources/XMLSchema.dtd",true);
URL xmlxsd=Loader.getResource(Servlet.class,"javax/servlet/resources/xml.xsd",true);
URL webservice11xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/j2ee_web_services_client_1_1.xsd",true);
URL webservice12xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/javaee_web_services_client_1_2.xsd",true);
URL datatypesdtd=Loader.getResource(Servlet.class,"javax/servlet/resources/datatypes.dtd",true);
URL jsp20xsd = null;
URL jsp21xsd = null;
try
{
Class jsp_page = Loader.loadClass(WebXmlConfiguration.class, "javax.servlet.jsp.JspPage");
jsp20xsd = jsp_page.getResource("/javax/servlet/resources/jsp_2_0.xsd");
jsp21xsd = jsp_page.getResource("/javax/servlet/resources/jsp_2_1.xsd");
}
catch (Exception e)
{
Log.ignore(e);
}
finally
{
if (jsp20xsd == null) jsp20xsd = Loader.getResource(Servlet.class, "javax/servlet/resources/jsp_2_0.xsd", true);
if (jsp21xsd == null) jsp21xsd = Loader.getResource(Servlet.class, "javax/servlet/resources/jsp_2_1.xsd", true);
}
redirect(xmlParser,"web-app_2_2.dtd",dtd22);
redirect(xmlParser,"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",dtd22);
redirect(xmlParser,"web.dtd",dtd23);
redirect(xmlParser,"web-app_2_3.dtd",dtd23);
redirect(xmlParser,"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",dtd23);
redirect(xmlParser,"XMLSchema.dtd",schemadtd);
redirect(xmlParser,"http://www.w3.org/2001/XMLSchema.dtd",schemadtd);
redirect(xmlParser,"-//W3C//DTD XMLSCHEMA 200102//EN",schemadtd);
redirect(xmlParser,"jsp_2_0.xsd",jsp20xsd);
redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/jsp_2_0.xsd",jsp20xsd);
redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/jsp_2_1.xsd",jsp21xsd);
redirect(xmlParser,"j2ee_1_4.xsd",j2ee14xsd);
redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd",j2ee14xsd);
redirect(xmlParser,"web-app_2_4.xsd",webapp24xsd);
redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd",webapp24xsd);
redirect(xmlParser,"web-app_2_5.xsd",webapp25xsd);
redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd",webapp25xsd);
redirect(xmlParser,"web-app_3_0.xsd",webapp30xsd);
redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd",webapp30xsd);
redirect(xmlParser,"web-common_3_0.xsd",webcommon30xsd);
redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-common_3_0.xsd",webcommon30xsd);
redirect(xmlParser,"web-fragment_3_0.xsd",webfragment30xsd);
redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd",webfragment30xsd);
redirect(xmlParser,"xml.xsd",xmlxsd);
redirect(xmlParser,"http://www.w3.org/2001/xml.xsd",xmlxsd);
redirect(xmlParser,"datatypes.dtd",datatypesdtd);
redirect(xmlParser,"http://www.w3.org/2001/datatypes.dtd",datatypesdtd);
redirect(xmlParser,"j2ee_web_services_client_1_1.xsd",webservice11xsd);
redirect(xmlParser,"http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd",webservice11xsd);
redirect(xmlParser,"javaee_web_services_client_1_2.xsd",webservice12xsd);
redirect(xmlParser,"http://www.ibm.com/webservices/xsd/javaee_web_services_client_1_2.xsd",webservice12xsd);
return xmlParser;
}
protected static void redirect(XmlParser parser, String resource, URL source)
{
if (source != null) parser.redirectEntity(resource, source);
}
/**
* Ordering
*
*
*/
public interface Ordering
{
public List order();
public boolean isAbsolute ();
}
/**
* AbsoluteOrdering
*
* An element in web.xml
*/
public class AbsoluteOrdering implements Ordering
{
public static final String OTHER = "@@-OTHER-@@";
protected List _order = new ArrayList();
protected boolean _hasOther = false;
public List order()
{
List orderedList = new ArrayList();
//1. put everything into the list of named others, and take the named ones out of there,
//assuming we will want to use the clause
Map others = new HashMap(getNamedFragments());
//2. for each name, take out of the list of others, add to tail of list
int index = -1;
for (String item:_order)
{
if (!item.equals(OTHER))
{
Fragment f = others.remove(item);
if (f != null)
orderedList.add(f); //take from others and put into final list in order, ignoring duplicate names
}
else
index = orderedList.size(); //remember the index at which we want to add in all the others
}
//3. if was specified, insert the leftovers
orderedList.addAll(index, others.values());
return orderedList;
}
public boolean isAbsolute()
{
return true;
}
public void add (String name)
{
_order.add(name);
}
public void addOthers ()
{
if (_hasOther)
throw new IllegalStateException ("Duplicate element in absolute ordering");
_hasOther = true;
_order.add(OTHER);
}
}
/**
* RelativeOrdering
*
* A set of elements in web-fragment.xmls.
*/
public class RelativeOrdering implements Ordering
{
protected LinkedList _beforeOthers = new LinkedList();
protected LinkedList _afterOthers = new LinkedList();
protected LinkedList _noOthers = new LinkedList();
public List order()
{
List orderedList = new ArrayList();
int maxIterations = 2;
boolean done = false;
do
{
//1. order the before-others according to any explicit before/after relationships
done = orderList(_beforeOthers);
//2. order the after-others according to any explicit before/after relationships
done = orderList(_afterOthers);
//3. order the no-others according to their explicit before/after relationships
done = orderList(_noOthers);
}
while (!done && (--maxIterations >0));
//5. merge before-others + no-others +after-others
if (!done)
throw new IllegalStateException("Circular references for fragments");
for (String s: _beforeOthers)
orderedList.add(getFragment(s));
for (String s: _noOthers)
orderedList.add(getFragment(s));
for(String s: _afterOthers)
orderedList.add(getFragment(s));
return orderedList;
}
public boolean isAbsolute ()
{
return false;
}
public void addBeforeOthers (Fragment d)
{
_beforeOthers.addLast(d.getName());
}
public void addAfterOthers (Fragment d)
{
_afterOthers.addLast(d.getName());
}
public void addNoOthers (Fragment d)
{
_noOthers.addLast(d.getName());
}
protected boolean orderList (LinkedList list)
{
//Take a copy of the list so we can iterate over it and at the same time do random insertions
boolean noChanges = true;
List iterable = new ArrayList(list);
Iterator itor = iterable.iterator();
while (itor.hasNext())
{
String name = itor.next();
Fragment f = _webFragmentNameMap.get(name);
if (f == null)
throw new IllegalStateException ("No fragment matching name "+name);
//Handle any explicit relationships for the fragment we're considering
List befores = f.getBefores();
if (befores != null && !befores.isEmpty())
{
for (String b: befores)
{
//Fragment we're considering must be before this name
//Check that we are already before it, if not, move us so that we are.
//If the name does not exist in our list, then get it out of the no-other list
if (!isBefore(list, name, b))
{
//b is not already before name, move it so that it is
int idx1 = list.indexOf(name);
int idx2 = list.indexOf(b);
//if b is not in the same list
if (idx2 < 0)
{
// must be in the noOthers list or it would have been an error
_noOthers.remove(b);
//If its in the no-others list, insert into this list so that we are before it
insert(list, idx1+1, b);
noChanges = false;
}
else
{
//b is in the same list but b is before name, so swap it around
list.remove(name);
insert(list, idx2, name);
noChanges = false;
}
}
}
}
//Handle any explicit relationships
List afters = f.getAfters();
if (afters != null && !afters.isEmpty())
{
for (String a: afters)
{
//Check that name is after a, moving it if possible if its not
if (!isAfter(list, name, a))
{
//name is not after a, move it
int idx1 = list.indexOf(name);
int idx2 = list.indexOf(a);
//if a is not in the same list as name
if (idx2 < 0)
{
//take it out of the noOthers list and put it in the right place in this list
_noOthers.remove(a);
insert(list,idx1, a);
noChanges = false;
}
else
{
//a is in the same list as name, but in the wrong place, so move it
list.remove(a);
insert(list,idx1, a);
noChanges = false;
}
}
//Name we're considering must be after this name
//Check we're already after it, if not, move us so that we are.
//If the name does not exist in our list, then get it out of the no-other list
}
}
}
return noChanges;
}
/**
* Is a before b?
* @param list
* @param a
* @param b
* @return
*/
protected boolean isBefore (List list, String a, String b)
{
//check if a and b are already in the same list, and b is already
//before a
int idxa = list.indexOf(a);
int idxb = list.indexOf(b);
if (idxb >=0 && idxb < idxa)
{
//a and b are in the same list but a is not before b
return false;
}
if (idxb < 0)
{
//a and b are not in the same list, but it is still possible that a is before
//b, depending on which list we're examining
if (list == _beforeOthers)
{
//The list we're looking at is the beforeOthers.If b is in the _afterOthers or the _noOthers, then by
//definition a is before it
return true;
}
else if (list == _afterOthers)
{
//The list we're looking at is the afterOthers, then a will be the tail of
//the final list. If b is in the beforeOthers list, then b will be before a and an error.
if (_beforeOthers.contains(b))
throw new IllegalStateException("Incorrect relationship: "+a+" before "+b);
else
return false; //b could be moved to the list
}
}
//a and b are in the same list and a is already before b
return true;
}
/**
* Is a after b?
* @param list
* @param a
* @param b
* @return
*/
protected boolean isAfter(List list, String a, String b)
{
int idxa = list.indexOf(a);
int idxb = list.indexOf(b);
if (idxb >=0 && idxa < idxb)
{
//a and b are both in the same list, but a is before b
return false;
}
if (idxb < 0)
{
//a and b are in different lists. a could still be after b depending on which list it is in.
if (list == _afterOthers)
{
//The list we're looking at is the afterOthers. If b is in the beforeOthers or noOthers then
//by definition a is after b because a is in the afterOthers list.
return true;
}
else if (list == _beforeOthers)
{
//The list we're looking at is beforeOthers, and contains a and will be before
//everything else in the final ist. If b is in the afterOthers list, then a cannot be before b.
if (_afterOthers.contains(b))
throw new IllegalStateException("Incorrect relationship: "+b+" after "+a);
else
return false; //b could be moved from noOthers list
}
}
return true; //a and b in the same list, a is after b
}
protected void insert(List list, int index, String element)
{
if (index > list.size())
list.add(element);
else
list.add(index, element);
}
}
public WebXmlProcessor (WebAppContext context) throws ClassNotFoundException
{
_context = context;
_parser = newParser();
}
public WebAppContext getContext()
{
return _context;
}
public XmlParser getParser()
{
return _parser;
}
public void parseDefaults (Resource webDefaults)
throws Exception
{
_webDefaultsRoot = new DefaultsDescriptor(webDefaults, this);
_webDefaultsRoot.parse();
if (_webDefaultsRoot.isOrdered())
{
if (_ordering == null)
_ordering = new AbsoluteOrdering();
List order = _webDefaultsRoot.getOrdering();
for (String s:order)
{
if (s.equalsIgnoreCase("others"))
((AbsoluteOrdering)_ordering).addOthers();
else
((AbsoluteOrdering)_ordering).add(s);
}
}
}
public void parseWebXml (Resource webXml)
throws Exception
{
_webXmlRoot = new Descriptor(webXml, this);
_webXmlRoot.parse();
_webXmlRoot.processClassNames();
if (_webXmlRoot.getMetaDataComplete() == Descriptor.MetaDataComplete.True)
_context.setAttribute(METADATA_COMPLETE, Boolean.TRUE);
else
_context.setAttribute(METADATA_COMPLETE, Boolean.FALSE);
_context.getServletContext().setEffectiveMajorVersion(_webXmlRoot.getMajorVersion());
_context.getServletContext().setEffectiveMinorVersion(_webXmlRoot.getMinorVersion());
_context.setAttribute(WEBXML_CLASSNAMES, _webXmlRoot.getClassNames());
if (_webXmlRoot.isOrdered())
{
if (_ordering == null)
_ordering = new AbsoluteOrdering();
List order = _webXmlRoot.getOrdering();
for (String s:order)
{
if (s.equalsIgnoreCase("others"))
((AbsoluteOrdering)_ordering).addOthers();
else
((AbsoluteOrdering)_ordering).add(s);
}
}
}
public void parseOverride (Resource override)
throws Exception
{
_webOverrideRoot = new OverrideDescriptor(override, this);
_webOverrideRoot.setValidating(false);
_webOverrideRoot.parse();
if (_webOverrideRoot.getMetaDataComplete() == Descriptor.MetaDataComplete.True)
_context.setAttribute(METADATA_COMPLETE, Boolean.TRUE);
else if (_webOverrideRoot.getMetaDataComplete() == Descriptor.MetaDataComplete.False)
_context.setAttribute(METADATA_COMPLETE, Boolean.FALSE);
if (_webOverrideRoot.isOrdered())
{
if (_ordering == null)
_ordering = new AbsoluteOrdering();
List order = _webOverrideRoot.getOrdering();
for (String s:order)
{
if (s.equalsIgnoreCase("others"))
((AbsoluteOrdering)_ordering).addOthers();
else
((AbsoluteOrdering)_ordering).add(s);
}
}
}
public void parseFragment (Resource fragment)
throws Exception
{
Boolean metaComplete = (Boolean)_context.getAttribute(METADATA_COMPLETE);
if (metaComplete != null && metaComplete.booleanValue())
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
Fragment frag = new Fragment(fragment, this);
frag.parse();
_webFragmentRoots.add(frag);
if (frag.getName() != null)
_webFragmentNameMap.put(frag.getName(), frag);
if (frag.isOrdered())
{
if (frag.isOrdered())
{
if (_ordering == null)
_ordering = new RelativeOrdering();
switch (frag.getOtherType())
{
case None:
{
((RelativeOrdering)_ordering).addNoOthers(frag);
break;
}
case Before:
{
((RelativeOrdering)_ordering).addBeforeOthers(frag);
break;
}
case After:
{
((RelativeOrdering)_ordering).addAfterOthers(frag);
break;
}
}
}
}
}
public void orderFragments ()
{
if (_ordering != null)
{
_orderedFragments = _ordering.order();
List orderedJars = new ArrayList();
for (Descriptor frag: _orderedFragments)
{
//get just the name of the jar file
String fullname = frag.getResource().getName();
int i = fullname.indexOf(".jar");
int j = fullname.lastIndexOf("/", i);
orderedJars.add(fullname.substring(j+1,i+4));
}
_context.setAttribute(ServletContext.ORDERED_LIBS, orderedJars);
}
else
_orderedFragments = _webFragmentRoots;
}
public void processFragments ()
throws Exception
{
//Servlet Spec 3.0 p.74 says all descriptors must say distributable
boolean distributable = ((_webDefaultsRoot != null && _webDefaultsRoot.isDistributable())
|| (_webXmlRoot != null && _webXmlRoot.isDistributable())
|| (_webOverrideRoot != null && _webOverrideRoot.isDistributable()));
for (Fragment frag : _orderedFragments)
{
process(frag);
distributable = distributable && frag.isDistributable();
}
_context.setDistributable(distributable);
}
public Descriptor getWebXml ()
{
return _webXmlRoot;
}
public Descriptor getOverrideWeb ()
{
return _webOverrideRoot;
}
public Descriptor getWebDefault ()
{
return _webDefaultsRoot;
}
public List getFragments ()
{
return _webFragmentRoots;
}
public List getOrderedFragments ()
{
return _orderedFragments;
}
public Ordering getOrdering()
{
return _ordering;
}
public void setOrdering (Ordering o)
{
_ordering = o;
}
public Fragment getFragment(String name)
{
return _webFragmentNameMap.get(name);
}
public Map getNamedFragments ()
{
return Collections.unmodifiableMap(_webFragmentNameMap);
}
/**
* Convenience method. Process the standard elements of the web descriptor.
* @param descriptor
* @throws Exception
*/
public void process (Descriptor descriptor)
throws Exception
{
if (descriptor != null)
{
initStandardDescriptorProcessor();
process(descriptor, _standardDescriptorProcessor);
}
}
/**
* Process the elements of the Descriptor according to the
* given DescriptorProcessor.
* @param descriptor
* @param processor
* @throws Exception
*/
public void process (Descriptor descriptor, DescriptorProcessor processor)
throws Exception
{
if (descriptor != null && processor != null)
processor.process(descriptor);
}
public Origin getOrigin (String name)
{
Descriptor d = _origins.get(name);
if (d == null)
return Origin.NotSet;
if (d instanceof Fragment)
return Origin.WebFragment;
if (d instanceof OverrideDescriptor)
return Origin.WebOverride;
if (d instanceof DefaultsDescriptor)
return Origin.WebDefaults;
return Origin.WebXml;
}
public Descriptor getOriginDescriptor (String name)
{
return _origins.get(name);
}
public void setOrigin (String name, Descriptor d)
{
_origins.put(name, d);
}
public void initStandardDescriptorProcessor ()
{
if (_standardDescriptorProcessor == null)
_standardDescriptorProcessor = new StandardDescriptorProcessor(this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy