[jboss-cvs] JBossAS SVN: r93222 - trunk/server/src/main/org/jboss/web/deployers.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sat Sep 5 14:48:59 EDT 2009


Author: remy.maucherat at jboss.com
Date: 2009-09-05 14:48:59 -0400 (Sat, 05 Sep 2009)
New Revision: 93222

Modified:
   trunk/server/src/main/org/jboss/web/deployers/MergedJBossWebMetaDataDeployer.java
Log:
- Add the fragment order resolution code from Catalina.

Modified: trunk/server/src/main/org/jboss/web/deployers/MergedJBossWebMetaDataDeployer.java
===================================================================
--- trunk/server/src/main/org/jboss/web/deployers/MergedJBossWebMetaDataDeployer.java	2009-09-05 17:11:23 UTC (rev 93221)
+++ trunk/server/src/main/org/jboss/web/deployers/MergedJBossWebMetaDataDeployer.java	2009-09-05 18:48:59 UTC (rev 93222)
@@ -21,17 +21,33 @@
  */
 package org.jboss.web.deployers;
 
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
 import org.jboss.deployers.spi.DeploymentException;
 import org.jboss.deployers.spi.deployer.DeploymentStages;
 import org.jboss.deployers.spi.deployer.helpers.AbstractDeployer;
 import org.jboss.deployers.structure.spi.DeploymentUnit;
+import org.jboss.deployers.vfs.spi.structure.VFSDeploymentUnit;
 import org.jboss.deployment.AnnotationMetaDataDeployer;
 import org.jboss.metadata.ear.jboss.JBossAppMetaData;
+import org.jboss.metadata.javaee.spec.SecurityRolesMetaData;
+import org.jboss.metadata.web.jboss.JBossWebMetaData;
+import org.jboss.metadata.web.spec.AbsoluteOrderingMetaData;
 import org.jboss.metadata.web.spec.AnnotationMergedView;
-import org.jboss.metadata.web.jboss.JBossWebMetaData;
+import org.jboss.metadata.web.spec.OrderingElementMetaData;
 import org.jboss.metadata.web.spec.Web25MetaData;
+import org.jboss.metadata.web.spec.Web30MetaData;
+import org.jboss.metadata.web.spec.WebFragmentMetaData;
 import org.jboss.metadata.web.spec.WebMetaData;
-import org.jboss.metadata.javaee.spec.SecurityRolesMetaData;
+import org.jboss.virtual.VirtualFile;
 
 /**
  * A deployer that merges annotation metadata, xml metadata, and jboss metadata
@@ -45,6 +61,7 @@
 public class MergedJBossWebMetaDataDeployer extends AbstractDeployer
 {
    public static final String WEB_MERGED_ATTACHMENT_NAME = "merged."+JBossWebMetaData.class.getName();
+   public static final String WEB_ORDER_ATTACHMENT_NAME = "order."+WebMetaData.class.getName();
 
    /**
     * Create a new MergedJBossWebMetaDataDeployer.
@@ -62,6 +79,7 @@
       setOutput(JBossWebMetaData.class);
       // 
       addOutput(WEB_MERGED_ATTACHMENT_NAME);
+      addOutput(WEB_ORDER_ATTACHMENT_NAME);
    }
 
    public void deploy(DeploymentUnit unit) throws DeploymentException
@@ -71,9 +89,194 @@
       if(specMetaData == null && metaData == null)
          return;
 
-      // FIXME: Check for a deployment order
+      // FIXME: Check metadata-complete (see AnnotationMetaDataDeployer)
       
+      // Find all fragments that have been processed by deployers, and place them in a map keyed by location
+      LinkedList<String> order = new LinkedList<String>();
+      List<WebOrdering> orderings = new ArrayList<WebOrdering>();
+      HashSet<String> jarsSet = new HashSet<String>();
+      VirtualFile webInfLib = null;
+      boolean fragmentFound = false;
+      HashMap<String, WebFragmentMetaData> webFragments = new HashMap<String, WebFragmentMetaData>();
+      if (unit instanceof VFSDeploymentUnit)
+      {
+         VFSDeploymentUnit vfsUnit = (VFSDeploymentUnit) unit;
+         webInfLib = vfsUnit.getFile("WEB-INF/lib");
+         if (webInfLib != null)
+         {
+            try
+            {
+               List<VirtualFile> jars = webInfLib.getChildren();
+               for (VirtualFile jar : jars)
+               {
+                  jarsSet.add(jar.getName());
+               }
+            }
+            catch (IOException e)
+            {
+            }
+         }
 
+         String base = unit.getName();
+         int pos = base.indexOf(':');
+         if (pos > 0)
+         {
+            base = base.substring(pos);
+         }
+
+         Iterator<String> attachementNames = unit.getAttachments().keySet().iterator();
+         HashSet<String> jarsWithoutFragmentsSet = (HashSet<String>) jarsSet.clone();
+         while (attachementNames.hasNext())
+         {
+            String location = attachementNames.next();
+            Object attachement = unit.getAttachment(location);
+            if (attachement != null && attachement instanceof WebFragmentMetaData)
+            {
+               pos = location.indexOf(':');
+               if (pos > 0)
+               {
+                  location = location.substring(pos);
+               }
+               if (!location.startsWith(base))
+               {
+                  // If there is only one fragment, it will also get mapped as this attachement
+                  continue;
+               }
+               String relativeLocation = "/" + location.substring(base.length());
+               String jarName = null;
+               if (relativeLocation.startsWith("/WEB-INF/lib/"))
+               {
+                  jarName = relativeLocation.substring("/WEB-INF/lib/".length());
+               }
+               if (jarName == null)
+               {
+                  continue;
+               }
+               fragmentFound = true;
+               WebFragmentMetaData fragmentMetaData = (WebFragmentMetaData) attachement;
+               webFragments.put(jarName, fragmentMetaData);
+               WebOrdering webOrdering = new WebOrdering();
+               webOrdering.setName(fragmentMetaData.getName());
+               webOrdering.setJar(jarName);
+               jarsWithoutFragmentsSet.remove(jarName);
+               if (fragmentMetaData.getOrdering() != null)
+               {
+                  if (fragmentMetaData.getOrdering().getAfter() != null)
+                  {
+                     for (OrderingElementMetaData orderingElementMetaData : 
+                        fragmentMetaData.getOrdering().getAfter().getOrdering())
+                     {
+                        if (orderingElementMetaData.isOthers())
+                        {
+                           webOrdering.setAfterOthers(true);
+                        }
+                        else
+                        {
+                           webOrdering.addAfter(orderingElementMetaData.getName());
+                        }
+                     }
+                  }
+                  if (fragmentMetaData.getOrdering().getBefore() != null)
+                  {
+                     for (OrderingElementMetaData orderingElementMetaData : 
+                        fragmentMetaData.getOrdering().getBefore().getOrdering())
+                     {
+                        if (orderingElementMetaData.isOthers())
+                        {
+                           webOrdering.setBeforeOthers(true);
+                        }
+                        else
+                        {
+                           webOrdering.addBefore(orderingElementMetaData.getName());
+                        }
+                     }
+                  }
+               }
+               orderings.add(webOrdering);
+            }
+         }
+         // If there is no fragment, still consider it for ordering as a
+         // fragment specifying no name and no order
+         for (String jarName : jarsWithoutFragmentsSet)
+         {
+            WebOrdering ordering = new WebOrdering();
+            ordering.setJar(jarName);
+            orderings.add(ordering);
+         }
+      }
+      
+      if (!fragmentFound)
+      {
+         // Drop the order as there is no fragment in the webapp
+         orderings.clear();
+      }
+
+      // Generate web fragments parsing order
+      AbsoluteOrderingMetaData absoluteOrderingMetaData = null;
+      if (specMetaData instanceof Web30MetaData)
+      {
+         absoluteOrderingMetaData = ((Web30MetaData) specMetaData).getAbsoluteOrdering();
+      }
+      if (absoluteOrderingMetaData != null) {
+         // Absolute ordering from web.xml, any relative fragment ordering is ignored
+         int otherPos = -1;
+         int i = 0;
+         for (OrderingElementMetaData orderingElementMetaData : absoluteOrderingMetaData.getOrdering())
+         {
+            if (orderingElementMetaData.isOthers())
+            {
+               if (otherPos >= 0) {
+                  throw new DeploymentException("Duplicate others in absolute ordering"); 
+              }
+              otherPos = i;
+            }
+            else
+            {
+               for (WebOrdering ordering : orderings)
+               {
+                  if (ordering.getName().equals(orderingElementMetaData.getName())) {
+                     order.add(ordering.getJar());
+                     jarsSet.remove(ordering.getJar());
+                     break;
+                 }
+               }
+            }
+            i++;
+         }
+         if (otherPos >= 0)
+         {
+            order.addAll(otherPos, jarsSet);
+         }
+      }
+      else if (orderings.size() > 0)
+      {
+         // Resolve relative ordering
+         try
+         {
+            resolveOrder(orderings, order);
+         }
+         catch (IllegalStateException e)
+         {
+            DeploymentException.rethrowAsDeploymentException("Invalid ordering", e);
+         }
+      }
+      else
+      {
+         // No order specified
+         order.addAll(jarsSet);
+      }
+
+      unit.addAttachment(WEB_ORDER_ATTACHMENT_NAME, order);
+      
+      // The fragments and corresponding annotations will need to be merged in order
+      // 1: Merge specMetaData into mergedMetaData
+      // 2: For each JAR in the order: 
+      // 2.1: Merge the fragment metadata into specMetaData, according to the Servlet 3.0 overriding rules
+      // 2.1: Merge the annotation metadata into specMetaData, according to the Servlet 3.0 overriding rules
+      // 3: Merge the Servlet annotations for all declared Servlets ?
+      // 4: Override with metaData (JBossWebMetaData)
+      // FIXME: Implement
+
       // Check for an annotated view
       String key = AnnotationMetaDataDeployer.WEB_ANNOTATED_ATTACHMENT_NAME;
       Web25MetaData annotatedMetaData = unit.getAttachment(key, Web25MetaData.class);
@@ -123,4 +326,354 @@
       unit.getTransientManagedObjects().addAttachment(JBossWebMetaData.class, mergedMetaData);
    }
 
+   
+   /**
+    * Utility class to associate the logical name with the JAR name, needed during the
+    * order resolving.
+    * @author remm
+    */
+   protected class WebOrdering implements Serializable {
+
+      private static final long serialVersionUID = 5603203103871892211L;
+
+      protected String jar = null;
+      protected String name = null;
+      protected List<String> after = new ArrayList<String>();
+      protected List<String> before = new ArrayList<String>();
+      protected boolean afterOthers = false;
+      protected boolean beforeOthers = false;
+      
+      public String getName() {
+          return name;
+      }
+      
+      public void setName(String name) {
+          this.name = name;
+      }
+
+      public List<String> getAfter() {
+          return after;
+      }
+
+      public void addAfter(String name) {
+          after.add(name);
+      }
+
+      public List<String> getBefore() {
+          return before;
+      }
+
+      public void addBefore(String name) {
+          before.add(name);
+      }
+
+      public String getJar() {
+          return jar;
+      }
+      
+      public void setJar(String jar) {
+          this.jar = jar;
+      }
+      
+      public boolean isAfterOthers() {
+          return afterOthers;
+      }
+      
+      public void setAfterOthers(boolean afterOthers) {
+          this.afterOthers = afterOthers;
+      }
+      
+      public boolean isBeforeOthers() {
+          return beforeOthers;
+      }
+      
+      public void setBeforeOthers(boolean beforeOthers) {
+          this.beforeOthers = beforeOthers;
+      }
+
+  }
+
+   protected static class Ordering {
+      protected WebOrdering ordering;
+      protected Set<Ordering> after = new HashSet<Ordering>();
+      protected Set<Ordering> before = new HashSet<Ordering>();
+      protected boolean afterOthers = false;
+      protected boolean beforeOthers = false;
+      
+      public boolean addAfter(Ordering ordering) {
+          return after.add(ordering);
+      }
+      
+      public boolean addBefore(Ordering ordering) {
+          return before.add(ordering);
+      }
+      
+      public void validate() {
+          isBefore(new Ordering());
+          isAfter(new Ordering());
+      }
+      
+      /**
+       * Check (recursively) if a fragment is before the specified fragment.
+       * 
+       * @param ordering
+       * @return
+       */
+      public boolean isBefore(Ordering ordering) {
+          return isBeforeInternal(ordering, new HashSet<Ordering>());
+      }
+      
+      protected boolean isBeforeInternal(Ordering ordering, Set<Ordering> checked) {
+          checked.add(this);
+          if (before.contains(ordering)) {
+              return true;
+          }
+          Iterator<Ordering> beforeIterator = before.iterator();
+          while (beforeIterator.hasNext()) {
+              Ordering check = beforeIterator.next();
+              if (checked.contains(check)) {
+                  //throw new IllegalStateException(sm.getString("ordering.orderConflict", this.ordering.getJar()));
+                  throw new IllegalStateException("Ordering conflict with JAR: " + this.ordering.getJar());
+              }
+              if (check.isBeforeInternal(ordering, checked)) {
+                  return false;
+              }
+          }
+          return false;
+      }
+      
+      /**
+       * Check (recursively) if a fragment is after the specified fragment.
+       * 
+       * @param ordering
+       * @return
+       */
+      public boolean isAfter(Ordering ordering) {
+          return isAfterInternal(ordering, new HashSet<Ordering>());
+      }
+      
+      protected boolean isAfterInternal(Ordering ordering, Set<Ordering> checked) {
+          checked.add(this);
+          if (after.contains(ordering)) {
+              return true;
+          }
+          Iterator<Ordering> afterIterator = after.iterator();
+          while (afterIterator.hasNext()) {
+              Ordering check = afterIterator.next();
+              if (checked.contains(check)) {
+                  //throw new IllegalStateException(sm.getString("ordering.orderConflict", this.ordering.getJar()));
+                 throw new IllegalStateException("Ordering conflict with JAR: " + this.ordering.getJar());
+              }
+              if (check.isAfterInternal(ordering, checked)) {
+                  return false;
+              }
+          }
+          return false;
+      }
+      
+      /**
+       * Check is a fragment marked as before others is after a fragment that is not.
+       * 
+       * @return true if a fragment marked as before others is after a fragment that is not
+       */
+      public boolean isLastBeforeOthers() {
+          if (!beforeOthers) {
+              throw new IllegalStateException();
+          }
+          Iterator<Ordering> beforeIterator = before.iterator();
+          while (beforeIterator.hasNext()) {
+              Ordering check = beforeIterator.next();
+              if (!check.beforeOthers) {
+                  return true;
+              } else if (check.isLastBeforeOthers()) {
+                  return true;
+              }
+          }
+          return false;
+      }
+
+      /**
+       * Check is a fragment marked as after others is before a fragment that is not.
+       * 
+       * @return true if a fragment marked as after others is before a fragment that is not
+       */
+      public boolean isFirstAfterOthers() {
+          if (!afterOthers) {
+              throw new IllegalStateException();
+          }
+          Iterator<Ordering> afterIterator = after.iterator();
+          while (afterIterator.hasNext()) {
+              Ordering check = afterIterator.next();
+              if (!check.afterOthers) {
+                  return true;
+              } else if (check.isFirstAfterOthers()) {
+                  return true;
+              }
+          }
+          return false;
+      }
+      
+  }
+
+  /**
+   * Generate the Jar processing order.
+   * 
+   * @param webOrderings The list of orderings, as parsed from the fragments
+   * @param order The generated order list
+   */
+  protected static void resolveOrder(List<WebOrdering> webOrderings, List<String> order) {
+      List<Ordering> work = new ArrayList<Ordering>();
+      
+      // Populate the work Ordering list
+      Iterator<WebOrdering> webOrderingsIterator = webOrderings.iterator();
+      while (webOrderingsIterator.hasNext()) {
+          WebOrdering webOrdering = webOrderingsIterator.next();
+          Ordering ordering = new Ordering();
+          ordering.ordering = webOrdering;
+          ordering.afterOthers = webOrdering.isAfterOthers();
+          ordering.beforeOthers = webOrdering.isBeforeOthers();
+          if (ordering.afterOthers && ordering.beforeOthers) {
+              // Cannot be both after and before others
+              //throw new IllegalStateException(sm.getString("ordering.afterAndBeforeOthers", webOrdering.getJar()));
+              throw new IllegalStateException("Ordering includes both before and after others in JAR: " + webOrdering.getJar());
+          }
+          work.add(ordering);
+      }
+      
+      // Create double linked relationships between the orderings,
+      // and resolve names
+      Iterator<Ordering> workIterator = work.iterator();
+      while (workIterator.hasNext()) {
+          Ordering ordering = workIterator.next();
+          WebOrdering webOrdering = ordering.ordering;
+          Iterator<String> after = webOrdering.getAfter().iterator();
+          while (after.hasNext()) {
+              String name = after.next();
+              Iterator<Ordering> workIterator2 = work.iterator();
+              boolean found = false;
+              while (workIterator2.hasNext()) {
+                  Ordering ordering2 = workIterator2.next();
+                  if (name.equals(ordering2.ordering.getName())) {
+                      if (found) {
+                          // Duplicate name
+                          //throw new IllegalStateException(sm.getString("ordering.duplicateName", webOrdering.getJar()));
+                         throw new IllegalStateException("Duplicate name declared in JAR: " + webOrdering.getJar());
+                      }
+                      ordering.addAfter(ordering2);
+                      ordering2.addBefore(ordering);
+                      found = true;
+                  }
+              }
+              if (!found) {
+                  // Unknown name
+                  //throw new IllegalStateException(sm.getString("ordering.unkonwnName", webOrdering.getJar()));
+                 throw new IllegalStateException("Unknown name declared in JAR: " + webOrdering.getJar());
+              }
+          }
+          Iterator<String> before = webOrdering.getBefore().iterator();
+          while (before.hasNext()) {
+              String name = before.next();
+              Iterator<Ordering> workIterator2 = work.iterator();
+              boolean found = false;
+              while (workIterator2.hasNext()) {
+                  Ordering ordering2 = workIterator2.next();
+                  if (name.equals(ordering2.ordering.getName())) {
+                      if (found) {
+                          // Duplicate name
+                          //throw new IllegalStateException(sm.getString("ordering.duplicateName", webOrdering.getJar()));
+                         throw new IllegalStateException("Duplicate name declared in JAR: " + webOrdering.getJar());
+                      }
+                      ordering.addBefore(ordering2);
+                      ordering2.addAfter(ordering);
+                      found = true;
+                  }
+              }
+              if (!found) {
+                  // Unknown name
+                  //throw new IllegalStateException(sm.getString("ordering.unkonwnName", webOrdering.getJar()));
+                 throw new IllegalStateException("Unknown name declared in JAR: " + webOrdering.getJar());
+              }
+          }
+      }
+      
+      // Validate ordering
+      workIterator = work.iterator();
+      while (workIterator.hasNext()) {
+          workIterator.next().validate();
+      }
+      
+      // Create three ordered lists that will then be merged
+      List<Ordering> tempOrder = new ArrayList<Ordering>();
+
+      // Create the ordered list of fragments which are before others
+      workIterator = work.iterator();
+      while (workIterator.hasNext()) {
+          Ordering ordering = workIterator.next();
+          if (ordering.beforeOthers) {
+              // Insert at the first possible position
+              int insertAfter = -1;
+              boolean last = ordering.isLastBeforeOthers();
+              int lastBeforeOthers = -1;
+              for (int i = 0; i < tempOrder.size(); i++) {
+                  if (ordering.isAfter(tempOrder.get(i))) {
+                      insertAfter = i;
+                  }
+                  if (tempOrder.get(i).beforeOthers) {
+                      lastBeforeOthers = i;
+                  }
+              }
+              int pos = insertAfter;
+              if (last && lastBeforeOthers > insertAfter) {
+                  pos = lastBeforeOthers;
+              }
+              tempOrder.add(pos + 1, ordering);
+          } else if (ordering.afterOthers) {
+              // Insert at the last possible element
+              int insertBefore = tempOrder.size();
+              boolean first = ordering.isFirstAfterOthers();
+              int firstAfterOthers = tempOrder.size();
+              for (int i = tempOrder.size() - 1; i >= 0; i--) {
+                  if (ordering.isBefore(tempOrder.get(i))) {
+                      insertBefore = i;
+                  }
+                  if (tempOrder.get(i).afterOthers) {
+                      firstAfterOthers = i;
+                  }
+              }
+              int pos = insertBefore;
+              if (first && firstAfterOthers < insertBefore) {
+                  pos = firstAfterOthers;
+              }
+              tempOrder.add(pos, ordering);
+          } else {
+              // Insert according to other already inserted elements
+              int insertAfter = -1;
+              int insertBefore = tempOrder.size();
+              for (int i = 0; i < tempOrder.size(); i++) {
+                  if (ordering.isAfter(tempOrder.get(i)) || tempOrder.get(i).beforeOthers) {
+                      insertAfter = i;
+                  }
+                  if (ordering.isBefore(tempOrder.get(i)) || tempOrder.get(i).afterOthers) {
+                      insertBefore = i;
+                  }
+              }
+              if (insertAfter > insertBefore) {
+                  // Conflicting order (probably caught earlier)
+                  //throw new IllegalStateException(sm.getString("ordering.orderConflict", ordering.ordering.getJar()));
+                 throw new IllegalStateException("Fragment ordering conflict with JAR: " + ordering.ordering.getJar());
+              }
+              // Insert somewhere in the range
+              tempOrder.add(insertAfter + 1, ordering);
+          }
+      }
+      
+      // Create the final ordered list
+      Iterator<Ordering> tempOrderIterator = tempOrder.iterator();
+      while (tempOrderIterator.hasNext()) {
+          Ordering ordering = tempOrderIterator.next();
+          order.add(ordering.ordering.getJar());
+      }
+      
+  }
+   
 }




More information about the jboss-cvs-commits mailing list