| // |
| // ======================================================================== |
| // Copyright (c) 1995-2014 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.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.jetty.util.resource.Resource; |
| |
| |
| /** |
| * Ordering |
| * |
| * Ordering options for jars in WEB-INF lib. |
| */ |
| public interface Ordering |
| { |
| |
| public List<Resource> order(List<Resource> fragments); |
| public boolean isAbsolute (); |
| public boolean hasOther(); |
| |
| |
| /** |
| * AbsoluteOrdering |
| * |
| * An <absolute-order> element in web.xml |
| */ |
| public static class AbsoluteOrdering implements Ordering |
| { |
| public static final String OTHER = "@@-OTHER-@@"; |
| protected List<String> _order = new ArrayList<String>(); |
| protected boolean _hasOther = false; |
| protected MetaData _metaData; |
| |
| public AbsoluteOrdering (MetaData metaData) |
| { |
| _metaData = metaData; |
| } |
| |
| /** |
| * Order the list of jars in WEB-INF/lib according to the ordering declarations in the descriptors |
| * @see org.eclipse.jetty.webapp.Ordering#order(java.util.List) |
| */ |
| @Override |
| public List<Resource> order(List<Resource> jars) |
| { |
| List<Resource> orderedList = new ArrayList<Resource>(); |
| List<Resource> tmp = new ArrayList<Resource>(jars); |
| |
| //1. put everything into the list of named others, and take the named ones out of there, |
| //assuming we will want to use the <other> clause |
| Map<String,FragmentDescriptor> others = new HashMap<String,FragmentDescriptor>(_metaData.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)) |
| { |
| FragmentDescriptor f = others.remove(item); |
| if (f != null) |
| { |
| Resource jar = _metaData.getJarForFragment(item); |
| orderedList.add(jar); //take from others and put into final list in order, ignoring duplicate names |
| //remove resource from list for resource matching name of descriptor |
| tmp.remove(jar); |
| } |
| } |
| else |
| index = orderedList.size(); //remember the index at which we want to add in all the others |
| } |
| |
| //3. if <other> was specified, insert rest of the fragments |
| if (_hasOther) |
| { |
| orderedList.addAll((index < 0? 0: index), tmp); |
| } |
| |
| return orderedList; |
| } |
| |
| @Override |
| public boolean isAbsolute() |
| { |
| return true; |
| } |
| |
| public void add (String name) |
| { |
| _order.add(name); |
| } |
| |
| public void addOthers () |
| { |
| if (_hasOther) |
| throw new IllegalStateException ("Duplicate <other> element in absolute ordering"); |
| |
| _hasOther = true; |
| _order.add(OTHER); |
| } |
| |
| @Override |
| public boolean hasOther () |
| { |
| return _hasOther; |
| } |
| } |
| /** |
| * RelativeOrdering |
| * |
| * A set of <order> elements in web-fragment.xmls. |
| */ |
| public static class RelativeOrdering implements Ordering |
| { |
| protected MetaData _metaData; |
| protected LinkedList<Resource> _beforeOthers = new LinkedList<Resource>(); |
| protected LinkedList<Resource> _afterOthers = new LinkedList<Resource>(); |
| protected LinkedList<Resource> _noOthers = new LinkedList<Resource>(); |
| |
| public RelativeOrdering (MetaData metaData) |
| { |
| _metaData = metaData; |
| } |
| /** |
| * Order the list of jars according to the ordering declared |
| * in the various web-fragment.xml files. |
| * @see org.eclipse.jetty.webapp.Ordering#order(java.util.List) |
| */ |
| @Override |
| public List<Resource> order(List<Resource> jars) |
| { |
| //for each jar, put it into the ordering according to the fragment ordering |
| for (Resource jar:jars) |
| { |
| //check if the jar has a fragment descriptor |
| FragmentDescriptor descriptor = _metaData.getFragment(jar); |
| if (descriptor != null) |
| { |
| switch (descriptor.getOtherType()) |
| { |
| case None: |
| { |
| ((RelativeOrdering)_metaData.getOrdering()).addNoOthers(jar); |
| break; |
| } |
| case Before: |
| { |
| ((RelativeOrdering)_metaData.getOrdering()).addBeforeOthers(jar); |
| break; |
| } |
| case After: |
| { |
| ((RelativeOrdering)_metaData.getOrdering()).addAfterOthers(jar); |
| break; |
| } |
| } |
| } |
| else |
| { |
| //jar fragment has no descriptor, but there is a relative ordering in place, so it must be part of the others |
| ((RelativeOrdering)_metaData.getOrdering()).addNoOthers(jar); |
| } |
| } |
| |
| //now apply the ordering |
| List<Resource> orderedList = new ArrayList<Resource>(); |
| int maxIterations = 2; |
| boolean done = false; |
| do |
| { |
| //1. order the before-others according to any explicit before/after relationships |
| boolean changesBefore = orderList(_beforeOthers); |
| |
| //2. order the after-others according to any explicit before/after relationships |
| boolean changesAfter = orderList(_afterOthers); |
| |
| //3. order the no-others according to their explicit before/after relationships |
| boolean changesNone = orderList(_noOthers); |
| |
| //we're finished on a clean pass through with no ordering changes |
| done = (!changesBefore && !changesAfter && !changesNone); |
| } |
| while (!done && (--maxIterations >0)); |
| |
| //4. merge before-others + no-others +after-others |
| if (!done) |
| throw new IllegalStateException("Circular references for fragments"); |
| |
| for (Resource r: _beforeOthers) |
| orderedList.add(r); |
| for (Resource r: _noOthers) |
| orderedList.add(r); |
| for(Resource r: _afterOthers) |
| orderedList.add(r); |
| |
| return orderedList; |
| } |
| |
| @Override |
| public boolean isAbsolute () |
| { |
| return false; |
| } |
| |
| @Override |
| public boolean hasOther () |
| { |
| return !_beforeOthers.isEmpty() || !_afterOthers.isEmpty(); |
| } |
| |
| public void addBeforeOthers (Resource r) |
| { |
| _beforeOthers.addLast(r); |
| } |
| |
| public void addAfterOthers (Resource r) |
| { |
| _afterOthers.addLast(r); |
| } |
| |
| public void addNoOthers (Resource r) |
| { |
| _noOthers.addLast(r); |
| } |
| |
| protected boolean orderList (LinkedList<Resource> list) |
| { |
| //Take a copy of the list so we can iterate over it and at the same time do random insertions |
| boolean changes = false; |
| List<Resource> iterable = new ArrayList<Resource>(list); |
| Iterator<Resource> itor = iterable.iterator(); |
| |
| while (itor.hasNext()) |
| { |
| Resource r = itor.next(); |
| FragmentDescriptor f = _metaData.getFragment(r); |
| if (f == null) |
| { |
| //no fragment for this resource so cannot have any ordering directives |
| continue; |
| } |
| |
| //Handle any explicit <before> relationships for the fragment we're considering |
| List<String> befores = f.getBefores(); |
| if (befores != null && !befores.isEmpty()) |
| { |
| for (String b: befores) |
| { |
| //Fragment we're considering must be before b |
| //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, f.getName(), b)) |
| { |
| //b is not already before name, move it so that it is |
| int idx1 = getIndexOf(list, f.getName()); |
| int idx2 = getIndexOf(list, b); |
| |
| //if b is not in the same list |
| if (idx2 < 0) |
| { |
| changes = true; |
| // must be in the noOthers list or it would have been an error |
| Resource bResource = _metaData.getJarForFragment(b); |
| if (bResource != null) |
| { |
| //If its in the no-others list, insert into this list so that we are before it |
| if (_noOthers.remove(bResource)) |
| { |
| insert(list, idx1+1, b); |
| |
| } |
| } |
| } |
| else |
| { |
| //b is in the same list but b is before name, so swap it around |
| list.remove(idx1); |
| insert(list, idx2, f.getName()); |
| changes = true; |
| } |
| } |
| } |
| } |
| |
| //Handle any explicit <after> relationships |
| List<String> afters = f.getAfters(); |
| if (afters != null && !afters.isEmpty()) |
| { |
| for (String a: afters) |
| { |
| //Check that fragment we're considering is after a, moving it if possible if its not |
| if (!isAfter(list, f.getName(), a)) |
| { |
| //name is not after a, move it |
| int idx1 = getIndexOf(list, f.getName()); |
| int idx2 = getIndexOf(list, a); |
| |
| //if a is not in the same list as name |
| if (idx2 < 0) |
| { |
| changes = true; |
| //take it out of the noOthers list and put it in the right place in this list |
| Resource aResource = _metaData.getJarForFragment(a); |
| if (aResource != null) |
| { |
| if (_noOthers.remove(aResource)) |
| { |
| insert(list,idx1, aResource); |
| } |
| } |
| } |
| else |
| { |
| //a is in the same list as name, but in the wrong place, so move it |
| list.remove(idx2); |
| insert(list,idx1, a); |
| changes = true; |
| } |
| } |
| //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 changes; |
| } |
| |
| /** |
| * Is fragment with name a before fragment with name b? |
| * @param list |
| * @param fragNameA |
| * @param fragNameB |
| * @return true if fragment name A is before fragment name B |
| */ |
| protected boolean isBefore (List<Resource> list, String fragNameA, String fragNameB) |
| { |
| //check if a and b are already in the same list, and b is already |
| //before a |
| int idxa = getIndexOf(list, fragNameA); |
| int idxb = getIndexOf(list, fragNameB); |
| |
| |
| 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(fragNameB)) |
| throw new IllegalStateException("Incorrect relationship: "+fragNameA+" before "+fragNameB); |
| 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 fragment name "a" after fragment name "b"? |
| * @param list |
| * @param fragNameA |
| * @param fragNameB |
| * @return true if fragment name A is after fragment name B |
| */ |
| protected boolean isAfter(List<Resource> list, String fragNameA, String fragNameB) |
| { |
| int idxa = getIndexOf(list, fragNameA); |
| int idxb = getIndexOf(list, fragNameB); |
| |
| 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(fragNameB)) |
| throw new IllegalStateException("Incorrect relationship: "+fragNameB+" after "+fragNameA); |
| else |
| return false; //b could be moved from noOthers list |
| } |
| } |
| |
| return true; //a and b in the same list, a is after b |
| } |
| |
| /** |
| * Insert the resource matching the fragName into the list of resources |
| * at the location indicated by index. |
| * |
| * @param list |
| * @param index |
| * @param fragName |
| */ |
| protected void insert(List<Resource> list, int index, String fragName) |
| { |
| Resource jar = _metaData.getJarForFragment(fragName); |
| if (jar == null) |
| throw new IllegalStateException("No jar for insertion"); |
| |
| insert(list, index, jar); |
| } |
| |
| protected void insert(List<Resource> list, int index, Resource resource) |
| { |
| if (list == null) |
| throw new IllegalStateException("List is null for insertion"); |
| |
| //add it at the end |
| if (index > list.size()) |
| list.add(resource); |
| else |
| list.add(index, resource); |
| } |
| |
| protected void remove (List<Resource> resources, Resource r) |
| { |
| if (resources == null) |
| return; |
| resources.remove(r); |
| } |
| |
| protected int getIndexOf(List<Resource> resources, String fragmentName) |
| { |
| FragmentDescriptor fd = _metaData.getFragment(fragmentName); |
| if (fd == null) |
| return -1; |
| |
| |
| Resource r = _metaData.getJarForFragment(fragmentName); |
| if (r == null) |
| return -1; |
| |
| return resources.indexOf(r); |
| } |
| } |
| |
| } |