Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » web » [javadoc | source]
    1   /*
    2    * JBoss, the OpenSource J2EE WebOS
    3    *
    4    * Distributable under LGPL license.
    5    * See terms of license at gnu.org.
    6    */
    7   
    8   package org.jboss.web;
    9   
   10   import java.io.ByteArrayInputStream;
   11   import java.io.ByteArrayOutputStream;
   12   import java.io.File;
   13   import java.io.FileInputStream;
   14   import java.io.InputStream;
   15   import java.io.InputStream;
   16   import java.lang.reflect.Method;
   17   import java.net.MalformedURLException;
   18   import java.net.URL;
   19   import java.net.URLClassLoader;
   20   import java.util.ArrayList;
   21   import java.util.HashMap;
   22   import java.util.HashSet;
   23   import java.util.Iterator;
   24   import java.util.Set;
   25   import javax.naming.Context;
   26   import javax.naming.InitialContext;
   27   import javax.naming.LinkRef;
   28   import javax.naming.NamingException;
   29   
   30   import org.jboss.deployment.DeploymentException;
   31   import org.jboss.deployment.DeploymentInfo;
   32   import org.jboss.deployment.SubDeployerSupport;
   33   import org.jboss.ejb.EjbUtil;
   34   import org.jboss.metadata.EjbLocalRefMetaData;
   35   import org.jboss.metadata.EjbRefMetaData;
   36   import org.jboss.metadata.EnvEntryMetaData;
   37   import org.jboss.metadata.ResourceEnvRefMetaData;
   38   import org.jboss.metadata.ResourceRefMetaData;
   39   import org.jboss.metadata.WebMetaData;
   40   import org.jboss.metadata.XmlFileLoader;
   41   import org.jboss.metadata.MetaData;
   42   import org.jboss.mx.loading.LoaderRepositoryFactory;
   43   import org.jboss.mx.loading.LoaderRepositoryFactory.LoaderRepositoryConfig;
   44   import org.jboss.naming.Util;
   45   import org.jboss.security.plugins.NullSecurityManager;
   46   import org.jboss.util.file.JarUtils;
   47   
   48   import org.w3c.dom.Document;
   49   import org.w3c.dom.Element;
   50   
   51   /** A template pattern class for web container integration into JBoss. This class
   52   should be subclasses by web container providers wishing to integrate their
   53   container into a JBoss server.
   54   
   55   It provides support for mapping the following web-app.xml/jboss-web.xml elements
   56   into the JBoss server JNDI namespace:
   57   - env-entry
   58   - resource-ref
   59   - resource-env-ref
   60   - ejb-ref
   61   - ejb-local-ref
   62   - security-domain
   63   
   64   Subclasses need to implement the {@link #performDeploy(WebApplication, String,
   65    WebDescriptorParser) performDeploy()}
   66   and {@link #performUndeploy(String) performUndeploy()} methods to perform the
   67   container specific steps and return the web application info required by the
   68   AbstractWebContainer class.
   69   
   70   Integration with the JBossSX security framework is based on the establishment
   71   of a java:comp/env/security context as described in the
   72   {@link linkSecurityDomain(String, Context) linkSecurityDomain } comments.
   73   The security context provides access to the JBossSX security mgr interface
   74   implementations for use by subclass request interceptors. A outline of the
   75   steps for authenticating a user is:
   76   <code>
   77      // Get the username & password from the request context...
   78      String username = f(request);
   79      String password = f(request);
   80      // Get the JBoss security manager from the ENC context
   81      InitialContext iniCtx = new InitialContext();
   82      SecurityManager securityMgr = (SecurityManager) iniCtx.lookup("java:comp/env/security/securityMgr");
   83      SimplePrincipal principal = new SimplePrincipal(username);
   84      if( securityMgr.isValid(principal, password) )
   85      {
   86      // Indicate the user is allowed access to the web content...
   87   
   88      // Propagate the user info to JBoss for any calls into made by the servlet
   89      SecurityAssociation.setPrincipal(principal);
   90      SecurityAssociation.setCredential(password.toCharArray());
   91      }
   92      else
   93      {
   94      // Deny access...
   95      }
   96   </code>
   97   
   98   An outline of the steps for authorizing the user is:
   99   <code>
  100      // Get the username & required roles from the request context...
  101      String username = f(request);
  102      String[] roles = f(request);
  103      // Get the JBoss security manager from the ENC context
  104      InitialContext iniCtx = new InitialContext();
  105      RealmMapping securityMgr = (RealmMapping) iniCtx.lookup("java:comp/env/security/realmMapping");
  106      SimplePrincipal principal = new SimplePrincipal(username);
  107      Set requiredRoles = new HashSet(Arrays.asList(roles));
  108      if( securityMgr.doesUserHaveRole(principal, requiredRoles) )
  109      {
  110      // Indicate the user has the required roles for the web content...
  111      }
  112      else
  113      {
  114      // Deny access...
  115      }
  116   </code>
  117   
  118   The one thing to be aware of is the relationship between the thread context
  119   class loader and the JNDI ENC context. Any method that attempts to access
  120   the JNDI ENC context must have the ClassLoader in the WebApplication returned
  121   from the {@link #performDeploy(String, String) performDeploy} as its thread
  122   context ClassLoader or else the lookup for java:comp/env will fail with a
  123   name not found exception, or worse, it will receive some other web application
  124   ENC context. If your adapting a web container that is trying be compatible with
  125   both 1.1 and 1.2 Java VMs this is something you need to pay special attention
  126   to. For example, I have seen problems a request interceptor that was handling
  127   the authentication/authorization callouts in tomcat3.2.1 not having the same
  128   thread context ClassLoader as was used to dispatch the http service request.
  129   
  130   For a complete example see the
  131   {@link org.jboss.web.catalina.EmbeddedCatalinaServiceSX EmbeddedCatalinaServiceSX}
  132   in the catalina module.
  133   
  134   @see #performDeploy(WebApplication webApp, String warUrl,
  135           WebDescriptorParser webAppParser)
  136   @see #performUndeploy(String)
  137   @see #parseWebAppDescriptors(ClassLoader, WebMetaData)
  138   @see #linkSecurityDomain(String, Context)
  139   @see org.jboss.security.SecurityManager;
  140   @see org.jboss.security.RealmMapping;
  141   @see org.jboss.security.SimplePrincipal;
  142   @see org.jboss.security.SecurityAssociation;
  143   
  144   @jmx:mbean extends="org.jboss.deployment.SubDeployerMBean"
  145   
  146   @author  Scott.Stark@jboss.org
  147   @version $Revision: 1.51.2.25 $
  148   */
  149   public abstract class AbstractWebContainer
  150      extends SubDeployerSupport
  151      implements AbstractWebContainerMBean
  152   {
  153      public static interface WebDescriptorParser
  154      {
  155         /** This method is called as part of subclass performDeploy() method implementations
  156         to parse the web-app.xml and jboss-web.xml deployment descriptors from a
  157         war deployment. The method creates the ENC(java:comp/env) env-entry,
  158         resource-ref, ejb-ref, etc element values. The creation of the env-entry
  159         values does not require a jboss-web.xml descriptor. The creation of the
  160         resource-ref and ejb-ref elements does require a jboss-web.xml descriptor
  161         for the JNDI name of the deployed resources/EJBs.
  162   
  163         Because the ENC context is private to the web application, the web
  164         application class loader is used to identify the ENC. The class loader
  165         is used because each war typically requires a unique class loader to
  166         isolate the web application classes/resources. This means that the
  167         ClassLoader passed to this method must be the thread context ClassLoader
  168         seen by the server/jsp pages during init/destroy/service/etc. method
  169         invocations if these methods interace with the JNDI ENC context.
  170   
  171         @param loader, the ClassLoader for the web application. May not be null.
  172         @param metaData, the WebMetaData from the WebApplication object passed to
  173          the performDeploy method.
  174         */
  175         public void parseWebAppDescriptors(ClassLoader loader, WebMetaData metaData) throws Exception;
  176   
  177         /** Get the DeploymentInfo for the war the triggered the deployment process.
  178          * The returned reference may be updated to affect the state of the
  179          * JBoss DeploymentInfo object. This can be used to assign ObjectNames
  180          * of MBeans created by the container.
  181          * @return The DeploymentInfo for the war being deployed.
  182          */
  183         public DeploymentInfo getDeploymentInfo();
  184      }
  185   
  186      /** A mapping of deployed warUrl strings to the WebApplication object */
  187      protected HashMap deploymentMap = new HashMap();
  188      /** The parent class loader first model flag */
  189      protected boolean java2ClassLoadingCompliance = false;
  190      /** A flag indicating if war archives should be unpacked */
  191      protected boolean unpackWars = true;
  192   
  193      /** If true, ejb-links that don't resolve don't cause an error (fallback to jndi-name) */
  194      protected boolean lenientEjbLink = false;
  195   
  196      public AbstractWebContainer()
  197      {
  198      }
  199   
  200      /** Get the flag indicating if the normal Java2 parent first class loading
  201       * model should be used over the servlet 2.3 web container first model.
  202       * @return true for parent first, false for the servlet 2.3 model
  203       * @jmx:managed-attribute
  204       */
  205      public boolean getJava2ClassLoadingCompliance()
  206      {
  207         return java2ClassLoadingCompliance;
  208      }
  209      /** Set the flag indicating if the normal Java2 parent first class loading
  210       * model should be used over the servlet 2.3 web container first model.
  211       * @param flag true for parent first, false for the servlet 2.3 model
  212       * @jmx:managed-attribute
  213       */
  214      public void setJava2ClassLoadingCompliance(boolean flag)
  215      {
  216         java2ClassLoadingCompliance = flag;
  217      }
  218   
  219      /** Set the flag indicating if war archives should be unpacked. This may
  220       * need to be set to false as long extraction paths under deploy can
  221       * show up as deployment failures on some platforms.
  222       * 
  223       * @jmx:managed-attribute
  224       * @return true is war archives should be unpacked
  225       */ 
  226      public boolean getUnpackWars()
  227      {
  228         return unpackWars;
  229      }
  230      /** Get the flag indicating if war archives should be unpacked. This may
  231       * need to be set to false as long extraction paths under deploy can
  232       * show up as deployment failures on some platforms.
  233       * 
  234       * @jmx:managed-attribute
  235       * @param flag , true is war archives should be unpacked
  236       */ 
  237      public void setUnpackWars(boolean flag)
  238      {
  239         this.unpackWars = flag;
  240      }
  241   
  242   
  243       /**
  244        * Get the flag indicating if ejb-link errors should be ignored
  245        * in favour of trying the jndi-name in jboss-web.xml
  246        * @return a <code>boolean</code> value
  247        *    
  248        * @jmx:managed-attribute
  249        */
  250       public boolean getLenientEjbLink ()
  251       {
  252           return lenientEjbLink;
  253       }
  254       
  255       /**
  256        * Set the flag indicating if ejb-link errors should be ignored
  257        * in favour of trying the jndi-name in jboss-web.xml
  258        * @return a <code>boolean</code> value
  259        *    
  260        * @jmx:managed-attribute
  261        */    
  262       public void setLenientEjbLink (boolean flag)
  263       {
  264           lenientEjbLink = flag;
  265       }
  266       
  267       public boolean accepts(DeploymentInfo sdi)
  268       {
  269           String warFile = sdi.url.getFile();
  270           return warFile.endsWith("war") || warFile.endsWith("war/");
  271       }
  272   
  273      public synchronized void init(DeploymentInfo di)
  274         throws DeploymentException
  275      {
  276         log.debug("Begin init");
  277         try
  278         {
  279            if (di.url.getPath().endsWith("/"))
  280            {
  281               // the URL is a unpacked collection, watch the deployment descriptor
  282               di.watch = new URL(di.url, "WEB-INF/web.xml");
  283            }
  284            else
  285            {
  286               // just watch the original URL
  287               di.watch = di.url;
  288            }
  289   
  290            // Make sure the war is unpacked if unpackWars is true
  291            File warFile = new File(di.localUrl.getFile());
  292            if( warFile.isDirectory() == false && unpackWars == true )
  293            {
  294               File tmp = new File(warFile.getAbsolutePath()+".tmp");
  295               if( warFile.renameTo(tmp) == false )
  296                  throw new DeploymentException("Was unable to move war to: "+tmp);
  297               if( warFile.mkdir() == false )
  298                  throw new DeploymentException("Was unable to mkdir: "+warFile);            
  299               log.debug("Unpacking war to: "+warFile);
  300               FileInputStream fis = new FileInputStream(tmp);
  301               JarUtils.unjar(fis, warFile);
  302               fis.close();
  303               log.debug("Replaced war with unpacked contents");
  304               if( tmp.delete() == false )
  305                  log.debug("Was unable to delete war tmp file");
  306               else
  307                  log.debug("Deleted war archive");
  308               // Reset the localUrl to end in a '/'
  309               di.localUrl = warFile.toURL();
  310               // Reset the localCl to point to the file
  311               URL[] localCP = {di.localUrl};
  312               di.localCl = new URLClassLoader(localCP);
  313            }
  314   
  315            WebMetaData metaData = new WebMetaData();
  316            metaData.setJava2ClassLoadingCompliance(this.java2ClassLoadingCompliance);
  317            di.metaData = metaData;
  318            // Check for a loader-repository
  319            XmlFileLoader xfl = new XmlFileLoader();
  320            InputStream in = di.localCl.getResourceAsStream("WEB-INF/jboss-web.xml");
  321            if( in != null )
  322            {
  323               Element jbossWeb = xfl.getDocument(in, "WEB-INF/jboss-web.xml").getDocumentElement();
  324               in.close();
  325               // Check for a war level class loading config
  326               Element classLoading = MetaData.getOptionalChild(jbossWeb, "class-loading");
  327               if( classLoading != null )
  328               {
  329                  String flagString = classLoading.getAttribute("java2ClassLoadingCompliance");
  330                  if( flagString.length() == 0 )
  331                     flagString = "true";
  332                  boolean flag = Boolean.valueOf(flagString).booleanValue();
  333                  metaData.setJava2ClassLoadingCompliance(flag);
  334                  // Check for a loader-repository for scoping
  335                  Element loader = MetaData.getOptionalChild(classLoading, "loader-repository");
  336                  if( loader != null )
  337                  {
  338                     LoaderRepositoryConfig config = LoaderRepositoryFactory.parseRepositoryConfig(loader);
  339                     di.setRepositoryInfo(config);
  340                  }
  341               }
  342            }
  343   
  344            // Generate an event for the initialization
  345            super.init(di);
  346         }
  347         catch (Exception e)
  348         {
  349            log.error("Problem in init ", e);
  350            throw new DeploymentException(e);
  351         }
  352   
  353         log.debug("End init");
  354      }
  355   
  356      /** WARs do not have nested deployments
  357       * @param di
  358       */
  359      protected void processNestedDeployments(DeploymentInfo di)
  360      {
  361      }
  362   
  363      /** A template pattern implementation of the deploy() method. This method
  364      calls the {@link #performDeploy(String, String) performDeploy()} method to
  365      perform the container specific deployment steps and registers the
  366      returned WebApplication in the deployment map. The steps performed are:
  367   
  368         ClassLoader appClassLoader = thread.getContextClassLoader();
  369         URLClassLoader warLoader = URLClassLoader.newInstance(empty, appClassLoader);
  370         thread.setContextClassLoader(warLoader);
  371         WebDescriptorParser webAppParser = ...;
  372         WebMetaData metaData = di.metaData;
  373         parseMetaData(ctxPath, warUrl, metaData);
  374         WebApplication warInfo = new WebApplication(metaData);
  375         performDeploy(warInfo, warUrl, webAppParser);
  376         deploymentMap.put(warUrl, warInfo);
  377         thread.setContextClassLoader(appClassLoader);
  378   
  379      The subclass performDeploy() implementation needs to invoke
  380      webAppParser.parseWebAppDescriptors(loader, warInfo) to have the JNDI
  381      java:comp/env namespace setup before any web app component can access
  382      this namespace.
  383   
  384       Also, an MBean for each servlet deployed should be created and its
  385       JMX ObjectName placed into the DeploymentInfo.mbeans list so that the
  386       JSR77 layer can create the approriate model view. The servlet MBean
  387       needs to provide access to the min, max and total time in milliseconds.
  388       Expose this information via MinServiceTime, MaxServiceTime and TotalServiceTime
  389       attributes to integrate seemlessly with the JSR77 factory layer.
  390   
  391      @param di, The deployment info that contains the context-root element value
  392       from the J2EE application/module/web application.xml descriptor. This may
  393       be null if war was is not being deployed as part of an enterprise application.
  394       It also contains the URL of the web application war.
  395      */
  396      public synchronized void start(DeploymentInfo di) throws DeploymentException
  397      {
  398         Thread thread = Thread.currentThread();
  399         ClassLoader appClassLoader = thread.getContextClassLoader();
  400         try
  401         {
  402            // Create a classloader for the war to ensure a unique ENC
  403            URL[] empty = {};
  404            URLClassLoader warLoader = URLClassLoader.newInstance(empty, di.ucl);
  405            thread.setContextClassLoader(warLoader);
  406            WebDescriptorParser webAppParser = new DescriptorParser(di);
  407            String webContext = di.webContext;
  408            if( webContext != null )
  409            {
  410               if( webContext.length() > 0 && webContext.charAt(0) != '/' )
  411                  webContext = "/" + webContext;
  412            }
  413            // Get the war URL
  414            URL warURL = di.localUrl != null ? di.localUrl : di.url;
  415   
  416            if (log.isDebugEnabled())
  417            {
  418               log.debug("webContext: " + webContext);
  419               log.debug("warURL: " + warURL);
  420               log.debug("webAppParser: " + webAppParser);
  421            }
  422   
  423            // Parse the web.xml and jboss-web.xml descriptors
  424            WebMetaData metaData = (WebMetaData) di.metaData;
  425            parseMetaData(webContext, warURL, di.shortName, metaData);
  426            WebApplication warInfo = new WebApplication(metaData);
  427            warInfo.setDeploymentInfo(di);
  428            performDeploy(warInfo, warURL.toString(), webAppParser);
  429            deploymentMap.put(warURL.toString(), warInfo);
  430   
  431            // Generate an event for the startup
  432            super.start(di);
  433         }
  434         catch(DeploymentException e)
  435         {
  436            throw e;
  437         }
  438         catch(Exception e)
  439         {
  440            throw new DeploymentException("Error during deploy", e);
  441         }
  442         finally
  443         {
  444            thread.setContextClassLoader(appClassLoader);
  445         }
  446      }
  447   
  448      /** This method is called by the deploy() method template and must be overriden by
  449      subclasses to perform the web container specific deployment steps.
  450      @param webApp, The web application information context. This contains the
  451       metadata such as the context-root element value from the J2EE
  452      application/module/web application.xml descriptor and virtual-host.
  453      @param warUrl, The string for the URL of the web application war.
  454      @param webAppParser, The callback interface the web container should use to
  455      setup the web app JNDI environment for use by the web app components. This
  456      needs to be invoked after the web app class loader is known, but before
  457      and web app components attempt to access the java:comp/env JNDI namespace.
  458      @return WebApplication, the web application information required by the
  459      AbstractWebContainer class to track the war deployment status.
  460      */
  461      protected abstract void performDeploy(WebApplication webApp, String warUrl,
  462         WebDescriptorParser webAppParser) throws Exception;
  463   
  464      /** A template pattern implementation of the undeploy() method. This method
  465      calls the {@link #performUndeploy(String) performUndeploy()} method to
  466      perform the container specific undeployment steps and unregisters the
  467      the warUrl from the deployment map.
  468      */
  469      public synchronized void stop(DeploymentInfo di)
  470         throws DeploymentException
  471      {
  472         URL warURL = di.localUrl != null ? di.localUrl : di.url;
  473         String warUrl = warURL.toString();
  474         try
  475         {
  476            performUndeploy(warUrl);
  477            // Remove the web application ENC...
  478            deploymentMap.remove(warUrl);
  479   
  480            // Generate an event for the stop
  481            super.stop(di);
  482         }
  483         catch(DeploymentException e)
  484         {
  485            throw e;
  486         }
  487         catch(Exception e)
  488         {
  489            throw new DeploymentException("Error during deploy", e);
  490         }
  491      }
  492   
  493      /** Called as part of the undeploy() method template to ask the
  494      subclass for perform the web container specific undeployment steps.
  495      */
  496      protected abstract void performUndeploy(String warUrl) throws Exception;
  497   
  498      /** See if a war is deployed.
  499        @jmx:managed-operation
  500      */
  501      public boolean isDeployed(String warUrl)
  502      {
  503         return deploymentMap.containsKey(warUrl);
  504      }
  505   
  506      /** Get the WebApplication object for a deployed war.
  507      @param warUrl, the war url string as originally passed to deploy().
  508      @return The WebApplication created during the deploy step if the
  509      warUrl is valid, null if no such deployment exists.
  510      */
  511      public WebApplication getDeployedApp(String warUrl)
  512      {
  513         WebApplication appInfo = (WebApplication) deploymentMap.get(warUrl);
  514         return appInfo;
  515      }
  516   
  517      /** Returns the applications deployed by the web container subclasses.
  518      @jmx:managed-attribute
  519      @return An Iterator of WebApplication objects for the deployed wars.
  520      */
  521      public Iterator getDeployedApplications()
  522      {
  523         return deploymentMap.values().iterator();
  524      }
  525   
  526      /** An accessor for any configuration element set via setConfig. This
  527      method always returns null and must be overriden by subclasses to
  528      return a valid value.
  529      @jmx:managed-attribute
  530      */
  531      public Element getConfig()
  532      {
  533         return null;
  534      }
  535      /** This method is invoked to import an arbitrary XML configuration tree.
  536      Subclasses should override this method if they support such a configuration
  537      capability. This implementation does nothing.
  538      @jmx:managed-attribute
  539      */
  540      public void setConfig(Element config)
  541      {
  542      }
  543   
  544      /** This method is invoked from within subclass performDeploy() method
  545      implementations when they invoke WebDescriptorParser.parseWebAppDescriptors().
  546   
  547      @param loader, the ClassLoader for the web application. May not be null.
  548      @param metaData, the WebMetaData from the WebApplication object passed to
  549       the performDeploy method.
  550      */
  551      protected void parseWebAppDescriptors(DeploymentInfo di, ClassLoader loader,
  552         WebMetaData metaData)
  553         throws Exception
  554      {
  555         log.debug("AbstractWebContainer.parseWebAppDescriptors, Begin");
  556         InitialContext iniCtx = new InitialContext();
  557         Context envCtx = null;
  558         Thread currentThread = Thread.currentThread();
  559         ClassLoader currentLoader = currentThread.getContextClassLoader();
  560         try
  561         {
  562            // Create a java:comp/env environment unique for the web application
  563            log.debug("Creating ENC using ClassLoader: "+loader);
  564            ClassLoader parent = loader.getParent();
  565            while( parent != null )
  566            {
  567               log.debug(".."+parent);
  568               parent = parent.getParent();
  569            }
  570            currentThread.setContextClassLoader(loader);
  571            metaData.setENCLoader(loader);
  572            envCtx = (Context) iniCtx.lookup("java:comp");
  573   
  574            // Add a link to the global transaction manager
  575            envCtx.bind("UserTransaction", new LinkRef("UserTransaction"));
  576            log.debug("Linked java:comp/UserTransaction to JNDI name: UserTransaction");
  577            envCtx = envCtx.createSubcontext("env");
  578         }
  579         finally
  580         {
  581            currentThread.setContextClassLoader(currentLoader);
  582         }
  583   
  584         Iterator envEntries = metaData.getEnvironmentEntries();
  585         log.debug("addEnvEntries");
  586         addEnvEntries(envEntries, envCtx);
  587         Iterator resourceEnvRefs = metaData.getResourceEnvReferences();
  588         log.debug("linkResourceEnvRefs");
  589         linkResourceEnvRefs(resourceEnvRefs, envCtx);
  590         Iterator resourceRefs = metaData.getResourceReferences();
  591         log.debug("linkResourceRefs");
  592         linkResourceRefs(resourceRefs, envCtx);
  593         Iterator ejbRefs = metaData.getEjbReferences();
  594         log.debug("linkEjbRefs");
  595         linkEjbRefs(ejbRefs, envCtx, di);
  596         Iterator ejbLocalRefs = metaData.getEjbLocalReferences();
  597         log.debug("linkEjbLocalRefs");
  598         linkEjbLocalRefs(ejbLocalRefs, envCtx, di);
  599         String securityDomain = metaData.getSecurityDomain();
  600         log.debug("linkSecurityDomain");
  601         linkSecurityDomain(securityDomain, envCtx);
  602         log.debug("AbstractWebContainer.parseWebAppDescriptors, End");
  603      }
  604   
  605      protected void addEnvEntries(Iterator envEntries, Context envCtx)
  606         throws ClassNotFoundException, NamingException
  607      {
  608         while( envEntries.hasNext() )
  609         {
  610            EnvEntryMetaData entry = (EnvEntryMetaData) envEntries.next();
  611               log.debug("Binding env-entry: "+entry.getName()+" of type: " +
  612                         entry.getType()+" to value:"+entry.getValue());
  613            EnvEntryMetaData.bindEnvEntry(envCtx, entry);
  614         }
  615      }
  616   
  617      protected void linkResourceEnvRefs(Iterator resourceEnvRefs, Context envCtx)
  618         throws NamingException
  619      {
  620         while( resourceEnvRefs.hasNext() )
  621         {
  622            ResourceEnvRefMetaData ref = (ResourceEnvRefMetaData) resourceEnvRefs.next();
  623            String resourceName = ref.getJndiName();
  624            String refName = ref.getRefName();
  625            if( ref.getType().equals("java.net.URL") )
  626            {
  627                try
  628                {
  629                    log.debug("Binding '"+refName+"' to URL: "+resourceName);
  630                    URL url = new URL(resourceName);
  631                    Util.bind(envCtx, refName, url);
  632                }
  633                catch(MalformedURLException e)
  634                {
  635                    throw new NamingException("Malformed URL:"+e.getMessage());
  636                }
  637            }
  638            else if( resourceName != null )
  639            {
  640               log.debug("Linking '"+refName+"' to JNDI name: "+resourceName);
  641               Util.bind(envCtx, refName, new LinkRef(resourceName));
  642            }
  643            else
  644            {
  645               throw new NamingException("resource-env-ref: "+refName
  646                  +" has no valid JNDI binding. Check the jboss-web/resource-env-ref.");
  647            }
  648         }
  649      }
  650   
  651      protected void linkResourceRefs(Iterator resourceRefs, Context envCtx)
  652         throws NamingException
  653      {
  654         while( resourceRefs.hasNext() )
  655         {
  656            ResourceRefMetaData ref = (ResourceRefMetaData) resourceRefs.next();
  657            String jndiName = ref.getJndiName();
  658            String refName = ref.getRefName();
  659            if( ref.getType().equals("java.net.URL") )
  660            {
  661                try
  662                {
  663                    log.debug("Binding '"+refName+"' to URL: "+jndiName);
  664                    URL url = new URL(jndiName);
  665                    Util.bind(envCtx, refName, url);
  666                }
  667                catch(MalformedURLException e)
  668                {
  669                    throw new NamingException("Malformed URL:"+e.getMessage());
  670                }
  671            }
  672            else if( jndiName != null )
  673            {
  674                log.debug("Linking '"+refName+"' to JNDI name: "+jndiName);
  675                Util.bind(envCtx, refName, new LinkRef(jndiName));
  676            }
  677            else
  678            {
  679               throw new NamingException("resource-ref: "+refName
  680                  +" has no valid JNDI binding. Check the jboss-web/resource-ref.");
  681            }
  682         }
  683      }
  684   
  685      protected void linkEjbRefs(Iterator ejbRefs, Context envCtx, DeploymentInfo di)
  686         throws NamingException
  687      {
  688         while( ejbRefs.hasNext() )
  689         {
  690            EjbRefMetaData ejb = (EjbRefMetaData) ejbRefs.next();
  691            String name = ejb.getName();
  692            String linkName = ejb.getLink();
  693            String jndiName = null;
  694   
  695            //use ejb-link if it is specified
  696            if ( linkName != null )
  697            {
  698                jndiName = EjbUtil.findEjbLink(server, di, linkName);
  699                
  700                //if flag does not allow misconfigured ejb-links, it is an error
  701                if ( ( jndiName == null ) && !(getLenientEjbLink()) )
  702                    throw new NamingException("ejb-ref: "+name+", no ejb-link match");
  703            }
  704   
  705            
  706            //fall through to the jndiName
  707            if ( jndiName == null )
  708            { 
  709                jndiName = ejb.getJndiName();
  710                if (jndiName == null )
  711                    throw new NamingException("ejb-ref: "+name+", no ejb-link in web.xml and no jndi-name in jboss-web.xml");
  712            }
  713            
  714            log.debug("Linking ejb-ref: "+name+" to JNDI name: "+jndiName);
  715            Util.bind(envCtx, name, new LinkRef(jndiName));
  716         }
  717      }
  718   
  719      protected void linkEjbLocalRefs(Iterator ejbRefs, Context envCtx, DeploymentInfo di)
  720         throws NamingException
  721      {
  722         while( ejbRefs.hasNext() )
  723         {
  724            EjbLocalRefMetaData ejb = (EjbLocalRefMetaData) ejbRefs.next();
  725            String name = ejb.getName();
  726            String linkName = ejb.getLink();
  727            String jndiName = null;
  728   
  729            //use the ejb-link field if it is specified
  730            if ( linkName != null )
  731            {
  732                jndiName = EjbUtil.findLocalEjbLink(server, di, linkName);
  733                
  734                //if flag does not allow misconfigured ejb-links, it is an error    
  735                if ( ( jndiName == null ) && !(getLenientEjbLink()) )
  736                    throw new NamingException("ejb-ref: "+name+", no ejb-link match");
  737            }
  738   
  739            
  740            if (jndiName == null)
  741            {
  742                jndiName = ejb.getJndiName();
  743                if ( jndiName == null )
  744                {
  745                   String msg = null;
  746                   if( linkName == null )
  747                   {
  748                     msg = "ejb-local-ref: '"+name+"', no ejb-link in web.xml and "
  749                      + "no local-jndi-name in jboss-web.xml";
  750                   }
  751                   else
  752                   {
  753                      msg = "ejb-local-ref: '"+name+"', with web.xml ejb-link: '"
  754                      + linkName + "' failed to resolve to an ejb with a LocalHome";
  755                   }
  756                   throw new NamingException(msg);
  757                }
  758            }
  759   
  760            log.debug("Linking ejb-local-ref: "+name+" to JNDI name: "+jndiName);
  761            Util.bind(envCtx, name, new LinkRef(jndiName));
  762         }
  763      }
  764   
  765      /** This creates a java:comp/env/security context that contains a
  766      securityMgr binding pointing to an AuthenticationManager implementation
  767      and a realmMapping binding pointing to a RealmMapping implementation.
  768      If the jboss-web.xml descriptor contained a security-domain element
  769      then the bindings are LinkRefs to the jndi name specified by the
  770      security-domain element. If there was no security-domain element then
  771      the bindings are to NullSecurityManager instance which simply allows
  772      all access.
  773      */
  774      protected void linkSecurityDomain(String securityDomain, Context envCtx)
  775         throws NamingException
  776      {
  777         if( securityDomain == null )
  778         {
  779            log.debug("Binding security/securityMgr to NullSecurityManager");
  780            Object securityMgr = new NullSecurityManager("java:/jaas/null");
  781            Util.bind(envCtx, "security/securityMgr", securityMgr);
  782            Util.bind(envCtx, "security/realmMapping", securityMgr);
  783            Util.bind(envCtx, "security/security-domain", new LinkRef("java:/jaas/null"));
  784            Util.bind(envCtx, "security/subject", new LinkRef("java:/jaas/null/subject"));
  785         }
  786         else
  787         {
  788            log.debug("Linking security/securityMgr to JNDI name: "+securityDomain);
  789            Util.bind(envCtx, "security/securityMgr", new LinkRef(securityDomain));
  790            Util.bind(envCtx, "security/realmMapping", new LinkRef(securityDomain));
  791            Util.bind(envCtx, "security/security-domain", new LinkRef(securityDomain));
  792            Util.bind(envCtx, "security/subject", new LinkRef(securityDomain+"/subject"));
  793         }
  794      }
  795   
  796      /** A utility method that searches the given loader for the
  797       resources: "javax/servlet/resources/web-app_2_3.dtd",
  798       "org/apache/jasper/resources/jsp12.dtd", and "javax/ejb/EJBHome.class"
  799       and returns an array of URL strings. Any jar: urls are reduced to the
  800       underlying <url> portion of the 'jar:<url>!/{entry}' construct.
  801       */
  802      public String[] getStandardCompileClasspath(ClassLoader loader)
  803      {
  804         String[] jspResources = {
  805            "javax/servlet/resources/web-app_2_3.dtd",
  806            "org/apache/jasper/resources/jsp12.dtd",
  807            "javax/ejb/EJBHome.class"
  808         };
  809         ArrayList tmp = new ArrayList();
  810         for(int j = 0; j < jspResources.length; j ++)
  811         {
  812            URL rsrcURL = loader.getResource(jspResources[j]);
  813            if( rsrcURL != null )
  814            {
  815               String url = rsrcURL.toExternalForm();
  816               if( rsrcURL.getProtocol().equals("jar") )
  817               {
  818                  // Parse the jar:<url>!/{entry} URL
  819                  url = url.substring(4);
  820                  int seperator = url.indexOf('!');
  821                  url = url.substring(0, seperator);
  822               }
  823               tmp.add(url);
  824            }
  825            else
  826            {
  827               log.warn("Failed to fin jsp rsrc: "+jspResources[j]);
  828            }
  829         }
  830         log.trace("JSP StandardCompileClasspath: " + tmp);
  831         String[] cp = new String[tmp.size()];
  832         tmp.toArray(cp);
  833         return cp;
  834      }
  835   
  836      /** A utility method that walks up the ClassLoader chain starting at
  837       the given loader and queries each ClassLoader for a 'URL[] getURLs()'
  838       method from which a complete classpath of URL strings is built.
  839       */
  840      public String[] getCompileClasspath(ClassLoader loader)
  841      {
  842         HashSet tmp = new HashSet();
  843         ClassLoader cl = loader;
  844         while( cl != null )
  845         {
  846            URL[] urls = getClassLoaderURLs(cl);
  847            addURLs(tmp, urls);
  848            cl = cl.getParent();
  849         }
  850         try
  851         {
  852            URL[] globalUrls = (URL[])getServer().getAttribute(LoaderRepositoryFactory.DEFAULT_LOADER_REPOSITORY,
  853                                                            "URLs");
  854            addURLs(tmp, globalUrls);
  855         }
  856         catch (Exception e)
  857         {
  858            log.warn("Could not get global URL[] from default loader repository!");
  859         } // end of try-catch
  860         log.trace("JSP CompileClasspath: " + tmp);
  861         String[] cp = new String[tmp.size()];
  862         tmp.toArray(cp);
  863         return cp;
  864      }
  865   
  866      private void addURLs(Set urlSet, URL[] urls)
  867      {
  868         for(int u = 0; u < urls.length; u ++)
  869         {
  870            URL url = urls[u];
  871            urlSet.add(url.toExternalForm());
  872         }
  873      }
  874   
  875      /** Use reflection to access a URL[] getURLs method so that non-URLClassLoader
  876       *class loaders that support this method can provide info.
  877       */
  878      protected URL[] getClassLoaderURLs(ClassLoader cl)
  879      {
  880         URL[] urls = {};
  881         try
  882         {
  883            Class returnType = urls.getClass();
  884            Class[] parameterTypes = {};
  885            Method getURLs = cl.getClass().getMethod("getURLs", parameterTypes);
  886            if( returnType.isAssignableFrom(getURLs.getReturnType()) )
  887            {
  888               Object[] args = {};
  889               urls = (URL[]) getURLs.invoke(cl, args);
  890            }
  891            if( urls == null || urls.length == 0 )
  892            {
  893               getURLs = cl.getClass().getMethod("getAllURLs", parameterTypes);
  894               if( returnType.isAssignableFrom(getURLs.getReturnType()) )
  895               {
  896                  Object[] args = {};
  897                  urls = (URL[]) getURLs.invoke(cl, args);
  898               }
  899            }
  900         }
  901         catch(Exception ignore)
  902         {
  903         }
  904         return urls;
  905      }
  906   
  907      /** This method creates a context-root string from either the
  908         WEB-INF/jboss-web.xml context-root element is one exists, or the
  909         filename portion of the warURL. It is called if the DeploymentInfo
  910         webContext value is null which indicates a standalone war deployment.
  911         A war name of ROOT.war is handled as a special case of a war that
  912         should be installed as the default web context.
  913       */
  914      protected void parseMetaData(String ctxPath, URL warURL, String warName,
  915         WebMetaData metaData)
  916         throws DeploymentException
  917      {
  918         InputStream jbossWebIS = null;
  919         InputStream webIS = null;
  920   
  921         // Parse the war deployment descriptors, web.xml and jboss-web.xml
  922         try
  923         {
  924            // See if the warUrl is a directory
  925            File warDir = new File(warURL.getFile());
  926            if( warURL.getProtocol().equals("file") && warDir.isDirectory() == true )
  927            {
  928               File webDD = new File(warDir, "WEB-INF/web.xml");
  929               if( webDD.exists() == true )
  930                  webIS = new FileInputStream(webDD);
  931               File jbossWebDD = new File(warDir, "WEB-INF/jboss-web.xml");
  932               if( jbossWebDD.exists() == true )
  933                  jbossWebIS = new FileInputStream(jbossWebDD);
  934            }
  935            else
  936            {
  937               // First check for a WEB-INF/web.xml and a WEB-INF/jboss-web.xml
  938               InputStream warIS = warURL.openStream();
  939               java.util.zip.ZipInputStream zipIS = new java.util.zip.ZipInputStream(warIS);
  940               java.util.zip.ZipEntry entry;
  941               byte[] buffer = new byte[512];
  942               int bytes;
  943               while( (entry = zipIS.getNextEntry()) != null )
  944               {
  945                  if( entry.getName().equals("WEB-INF/web.xml") )
  946                  {
  947                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
  948                     while( (bytes = zipIS.read(buffer)) > 0 )
  949                     {
  950                        baos.write(buffer, 0, bytes);
  951                     }
  952                     webIS = new ByteArrayInputStream(baos.toByteArray());
  953                  }
  954                  else if( entry.getName().equals("WEB-INF/jboss-web.xml") )
  955                  {
  956                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
  957                     while( (bytes = zipIS.read(buffer)) > 0 )
  958                     {
  959                        baos.write(buffer, 0, bytes);
  960                     }
  961                     jbossWebIS = new ByteArrayInputStream(baos.toByteArray());
  962                  }
  963               }
  964               zipIS.close();
  965            }
  966   
  967            XmlFileLoader xmlLoader = new XmlFileLoader();
  968            String warURI = warURL.toExternalForm();
  969            try
  970            {
  971               if( webIS != null )
  972               {
  973                  Document webDoc = xmlLoader.getDocument(webIS, warURI+"/WEB-INF/web.xml");
  974                  Element web = webDoc.getDocumentElement();
  975                  metaData.importXml(web);
  976               }
  977            }
  978            catch(Exception e)
  979            {
  980               throw new DeploymentException("Failed to parse WEB-INF/web.xml", e);
  981            }
  982            try
  983            {
  984               if( jbossWebIS != null )
  985               {
  986                  Document jbossWebDoc = xmlLoader.getDocument(jbossWebIS, warURI+"/WEB-INF/jboss-web.xml");
  987                  Element jbossWeb = jbossWebDoc.getDocumentElement();
  988                  metaData.importXml(jbossWeb);
  989               }
  990            }
  991            catch(Exception e)
  992            {
  993               throw new DeploymentException("Failed to parse WEB-INF/jboss-web.xml", e);
  994            }
  995   
  996         }
  997         catch(Exception e)
  998         {
  999            log.warn("Failed to parse descriptors for war("+warURL+")", e);
 1000         }
 1001   
 1002         // Build a war root context from the war name if one was not specified
 1003         String webContext = ctxPath;
 1004         if( webContext == null )
 1005            webContext = metaData.getContextRoot();
 1006         if( webContext == null )
 1007         {
 1008            // Build the context from the war name, strip the .war suffix
 1009            webContext = warName;
 1010            webContext = webContext.replace('\\', '/');
 1011            if( webContext.endsWith("/") )
 1012               webContext = webContext.substring(0, webContext.length()-1);
 1013            int prefix = webContext.lastIndexOf('/');
 1014            if( prefix > 0 )
 1015               webContext = webContext.substring(prefix+1);
 1016            int suffix = webContext.lastIndexOf(".war");
 1017            if( suffix > 0 )
 1018               webContext = webContext.substring(0, suffix);
 1019             // Strip any '<int-value>.' prefix
 1020             int index = 0;
 1021             for(; index < webContext.length(); index ++)
 1022             {
 1023                char c = webContext.charAt(index);
 1024                if( Character.isDigit(c) == false && c != '.' )
 1025                   break;
 1026             }
 1027             webContext = webContext.substring(index);
 1028         }
 1029   
 1030         // Servlet containers are anal about the web context starting with '/'
 1031         if( webContext.length() > 0 && webContext.charAt(0) != '/' )
 1032            webContext = "/" + webContext;
 1033         // And also the default root context must be an empty string, not '/'
 1034         else if( webContext.equals("/") )
 1035            webContext = "";
 1036         metaData.setContextRoot(webContext);
 1037      }
 1038   
 1039      /** An inner class that maps the WebDescriptorParser.parseWebAppDescriptors()
 1040      onto the protected parseWebAppDescriptors() AbstractWebContainer method.
 1041      */
 1042      private class DescriptorParser implements WebDescriptorParser
 1043      {
 1044         DeploymentInfo di;
 1045         DescriptorParser(DeploymentInfo di)
 1046         {
 1047            this.di = di;
 1048         }
 1049         public void parseWebAppDescriptors(ClassLoader loader, WebMetaData metaData) throws Exception
 1050         {
 1051            AbstractWebContainer.this.parseWebAppDescriptors(di, loader, metaData);
 1052         }
 1053         public DeploymentInfo getDeploymentInfo()
 1054         {
 1055            return di;
 1056         }
 1057      }
 1058   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » web » [javadoc | source]