WARDeployer.java

/*
 * IronJacamar, a Java EE Connector Architecture implementation
 * Copyright 2016, Red Hat Inc, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the Eclipse Public License 1.0 as
 * published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse
 * Public License for more details.
 *
 * You should have received a copy of the Eclipse Public License 
 * along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.ironjacamar.web;

import java.io.File;
import java.io.IOException;
import java.net.URL;

import org.jboss.logging.Logger;

import com.github.fungal.api.util.FileUtil;
import com.github.fungal.spi.deployers.Context;
import com.github.fungal.spi.deployers.DeployException;
import com.github.fungal.spi.deployers.Deployer;
import com.github.fungal.spi.deployers.Deployment;
import org.eclipse.jetty.webapp.WebAppClassLoader;
import org.eclipse.jetty.webapp.WebAppContext;

/**
 * The WAR deployer for JCA/SJC
 * @author <a href="mailto:jesper.pedersen@ironjacamar.org">Jesper Pedersen</a>
 */
public class WARDeployer implements Deployer
{
   private static Logger log = Logger.getLogger(WARDeployer.class);

   private WebServer webServer;
       
   /**
    * Constructor
    */
   public WARDeployer()
   {
      this.webServer = null;
   }

   /**
    * Get the web server
    * @return The server
    */
   public WebServer getWebServer()
   {
      return webServer;
   }

   /**
    * Set the web server
    * @param server The server
    */
   public void setWebServer(WebServer server)
   {
      this.webServer = server;
   }

   /**
    * {@inheritDoc}
    */
   public boolean accepts(URL url)
   {
      if (url == null || !(url.toExternalForm().endsWith(".war") || url.toExternalForm().endsWith(".war/")))
         return false;

      return true;
   }

   /**
    * {@inheritDoc}
    */
   public int getOrder()
   {
      return Integer.MIN_VALUE;
   }

   /**
    * {@inheritDoc}
    */
   public synchronized Deployment deploy(URL url, Context context, ClassLoader parent) throws DeployException
   {
      if (log.isDebugEnabled())
      {
         log.debug("Deploying: " + url.toExternalForm());
      }
      try
      {
         String path = url.toExternalForm();

         // Extract context path based on .war name
         String contextPath = "/";
         if (!path.endsWith("/"))
         {
            contextPath += path.substring(path.lastIndexOf('/') + 1, path.lastIndexOf('.'));
         }
         else
         {
            int lastIndex = path.lastIndexOf('/');

            int index = path.indexOf('/');
            boolean done = false;

            while (!done)
            {
               if (index + 1 <= path.length())
               {
                  int nextIndex = path.indexOf('/', index + 1);
                  if (nextIndex == lastIndex)
                  {
                     done = true;
                  }
                  else
                  {
                     index = nextIndex;
                  }
               }
               else
               {
                  done = true;
               }
            }

            contextPath += path.substring(index + 1, path.lastIndexOf("."));
         }

         String tmpPath = "/web";
         // Map ROOT.war to /
         if ("/ROOT".equalsIgnoreCase(contextPath))
         {
            contextPath = "/";
            tmpPath += "/root"; 
         }
         else
         {
            tmpPath += contextPath;
         }

         // Setup temporary work directory
         File tmp = new File(SecurityActions.getSystemProperty("ironjacamar.home"), "/tmp/");
         File tmpDeployment = new File(tmp, tmpPath);

         if (tmpDeployment.exists())
         {
            FileUtil fileUtil = new FileUtil();
            fileUtil.delete(tmpDeployment);
         }

         if (!tmpDeployment.mkdirs())
            throw new IOException("Unable to create " + tmpDeployment);

         log.debugf("ContextPath=%s", contextPath);
         log.debugf("TmpPath=%s", tmpPath);

         WebAppContext webapp = new WebAppContext();
         webapp.setContextPath(contextPath);
         webapp.setWar(url.toString());
         webapp.setTempDirectory(tmpDeployment);

         ClassLoader webappCL = SecurityActions.createWARClassLoader(context.getKernel(), parent);
         WebAppClassLoader wal = SecurityActions.createWebAppClassLoader(webappCL, webapp);

         if (wal == null)
            throw new DeployException("WebAppClassLoader is null");

         webapp.setClassLoader(wal);

         webapp.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
            ".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$");

         // Hack to shut up AnnotationConfiguration
         webapp.setAttribute(org.eclipse.jetty.annotations.AnnotationConfiguration.CLASS_INHERITANCE_MAP,
                             new org.eclipse.jetty.annotations.AnnotationConfiguration.ClassInheritanceMap());
         
         webServer.addHandler(webapp);

         log.info("Deployed: " + url.toExternalForm());

         return new WARDeployment(url, webapp, tmpDeployment, webappCL);
      }
      catch (DeployException de)
      {
         throw de;
      }
      catch (Throwable t)
      {
         throw new DeployException(t.getMessage(), t);
      }
   }
}