Coverage Report - org.mule.transport.servlet.jetty.JettyHttpConnector
 
Classes in this File Line Coverage Branch Coverage Complexity
JettyHttpConnector
0%
0/153
0%
0/42
0
JettyHttpConnector$1
0%
0/13
0%
0/6
0
JettyHttpConnector$2
0%
0/11
0%
0/4
0
JettyHttpConnector$DummyJndiConfiguration
0%
0/4
N/A
0
JettyHttpConnector$MuleReceiverConnectorHolder
0%
0/22
0%
0/8
0
 
 1  
 /*
 2  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 3  
  * The software in this package is published under the terms of the CPAL v1.0
 4  
  * license, a copy of which has been included with this distribution in the
 5  
  * LICENSE.txt file.
 6  
  */
 7  
 package org.mule.transport.servlet.jetty;
 8  
 
 9  
 import org.mule.api.MuleContext;
 10  
 import org.mule.api.MuleException;
 11  
 import org.mule.api.MuleRuntimeException;
 12  
 import org.mule.api.config.MuleProperties;
 13  
 import org.mule.api.construct.FlowConstruct;
 14  
 import org.mule.api.context.notification.MuleContextNotificationListener;
 15  
 import org.mule.api.endpoint.ImmutableEndpoint;
 16  
 import org.mule.api.endpoint.InboundEndpoint;
 17  
 import org.mule.api.lifecycle.InitialisationException;
 18  
 import org.mule.api.lifecycle.LifecycleException;
 19  
 import org.mule.api.transport.MessageReceiver;
 20  
 import org.mule.api.transport.ReplyToHandler;
 21  
 import org.mule.config.i18n.CoreMessages;
 22  
 import org.mule.context.notification.MuleContextNotification;
 23  
 import org.mule.context.notification.NotificationException;
 24  
 import org.mule.transport.AbstractConnector;
 25  
 import org.mule.transport.servlet.JarResourceServlet;
 26  
 import org.mule.transport.servlet.MuleReceiverServlet;
 27  
 import org.mule.transport.servlet.MuleServletContextListener;
 28  
 import org.mule.util.ClassUtils;
 29  
 import org.mule.util.IOUtils;
 30  
 import org.mule.util.StringMessageUtils;
 31  
 import org.mule.util.StringUtils;
 32  
 
 33  
 import java.io.File;
 34  
 import java.io.InputStream;
 35  
 import java.net.URL;
 36  
 import java.util.ArrayList;
 37  
 import java.util.HashMap;
 38  
 import java.util.List;
 39  
 
 40  
 import javax.servlet.Servlet;
 41  
 import javax.servlet.http.HttpServlet;
 42  
 
 43  
 import org.mortbay.jetty.Connector;
 44  
 import org.mortbay.jetty.Handler;
 45  
 import org.mortbay.jetty.Server;
 46  
 import org.mortbay.jetty.annotations.Configuration;
 47  
 import org.mortbay.jetty.handler.ContextHandlerCollection;
 48  
 import org.mortbay.jetty.nio.SelectChannelConnector;
 49  
 import org.mortbay.jetty.servlet.Context;
 50  
 import org.mortbay.jetty.servlet.ServletHolder;
 51  
 import org.mortbay.jetty.webapp.WebAppContext;
 52  
 import org.mortbay.jetty.webapp.WebInfConfiguration;
 53  
 import org.mortbay.log.Log;
 54  
 import org.mortbay.xml.XmlConfiguration;
 55  
 
 56  
 /**
 57  
  * The <code>JettyConnector</code> can be using to embed a Jetty server to receive requests on an
 58  
  * http inound endpoint. One server is created for each connector declared, many Jetty endpoints
 59  
  * can share the same connector.
 60  
  */
 61  0
 public class JettyHttpConnector extends AbstractConnector
 62  
 {
 63  
     public static final String ROOT = "/";
 64  
 
 65  
     public static final String JETTY = "jetty";
 66  
 
 67  
     private Server httpServer;
 68  
 
 69  
     private String configFile;
 70  
 
 71  
     private JettyReceiverServlet receiverServlet;
 72  
 
 73  0
     private boolean useContinuations = false;
 74  
 
 75  
     private String resourceBase;
 76  
 
 77  
     private WebappsConfiguration webappsConfiguration;
 78  
 
 79  0
     protected HashMap<String, ConnectorHolder> holders = new HashMap<String, ConnectorHolder>();
 80  
 
 81  
     private WebAppDeployer deployer;
 82  
 
 83  
     public JettyHttpConnector(MuleContext context)
 84  
     {
 85  0
         super(context);
 86  0
         setupJettyLogging();
 87  0
         registerSupportedProtocol("http");
 88  0
         registerSupportedProtocol(JETTY);
 89  0
         setInitialStateStopped(true);
 90  0
     }
 91  
 
 92  
     protected void setupJettyLogging()
 93  
     {
 94  0
         if ((Log.getLog() instanceof JettyLogger) == false)
 95  
         {
 96  0
             Log.setLog(new JettyLogger());
 97  
         }
 98  0
     }
 99  
 
 100  
     public String getProtocol()
 101  
     {
 102  0
         return JETTY;
 103  
     }
 104  
 
 105  
     @Override
 106  
     protected void doInitialise() throws InitialisationException
 107  
     {
 108  0
         httpServer = new Server()
 109  0
         {
 110  
             @Override
 111  
             public void addHandler(Handler handler)
 112  
             {
 113  0
                 final Connector c = getServer().getConnectors()[0];
 114  0
                 if (handler instanceof WebAppContext)
 115  
                 {
 116  0
                     final WebAppContext webapp = (WebAppContext) handler;
 117  0
                     final String msg = String.format("Will deploy a web app at %s:/%s%s%s",
 118  
                                                      "http", c.getHost(),
 119  
                                                      c.getPort() == 80 ? StringUtils.EMPTY : ":" + c.getPort(),
 120  
                                                      webapp.getContextPath());
 121  
 
 122  0
                     final File workDir = new File(muleContext.getConfiguration().getWorkingDirectory(),
 123  
                                                   "_exploded_wars" + webapp.getContextPath());
 124  0
                     workDir.mkdirs();
 125  0
                     webapp.setTempDirectory(workDir);
 126  
                     // TODO extract to a better constant
 127  0
                     webapp.setAttribute("muleContext", muleContext);
 128  
 
 129  0
                     if (logger.isInfoEnabled())
 130  
                     {
 131  0
                         logger.info(StringMessageUtils.getBoilerPlate(msg, '*', 70));
 132  
                     }
 133  
                 }
 134  0
                 super.addHandler(handler);
 135  0
             }
 136  
         };
 137  
         
 138  0
         if (webappsConfiguration != null)
 139  
         {
 140  0
             deployer = new WebAppDeployer();
 141  0
             String webAppDir = webappsConfiguration.getDirectory();
 142  0
             if (StringUtils.isBlank(webAppDir))
 143  
             {
 144  
                 // if none specified, resolve defaults dynamically
 145  0
                 final String appDir = muleContext.getRegistry().get(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
 146  0
                 webAppDir = appDir + "/webapps";
 147  
             }
 148  
 
 149  0
             if (configFile == null)
 150  
             {
 151  
                 // override only if user hasn't specified one (turn off file-mapped buffer for
 152  
                 // static files to avoid resource locking, makes webapp resources editable on the fly)
 153  0
                 final URL muleDefaults = ClassUtils.getResource("org/mule/transport/jetty/webdefault.xml", getClass());
 154  0
                 deployer.setDefaultsDescriptor(muleDefaults.toExternalForm());
 155  
             }
 156  0
             deployer.setWebAppDir(webAppDir);
 157  0
             deployer.setExtract(true);
 158  0
             deployer.setParentLoaderPriority(false);
 159  0
             deployer.setServerClasses(webappsConfiguration.getServerClasses());
 160  0
             deployer.setSystemClasses(webappsConfiguration.getSystemClasses());
 161  
 
 162  0
             org.mortbay.jetty.AbstractConnector jettyConnector = createJettyConnector();
 163  0
             jettyConnector.setHost(webappsConfiguration.getHost());
 164  0
             jettyConnector.setPort(webappsConfiguration.getPort());
 165  0
             deployer.setContexts(httpServer);
 166  0
             String[] confClasses = new String[]
 167  
             {
 168  
                 // configures webapp's classloader as a child of a Mule app classloader
 169  
                 WebInfConfiguration.class.getName(),
 170  
                 // just to get jetty going, we don't need java ee bindings. inherits annotation processing
 171  
                 DummyJndiConfiguration.class.getName()
 172  
             };
 173  0
             deployer.setConfigurationClasses(confClasses);
 174  
 
 175  0
             httpServer.addConnector(jettyConnector);
 176  0
             httpServer.addLifeCycle(deployer);
 177  
         }
 178  
         
 179  0
         initialiseFromConfigFile();
 180  
 
 181  
         try
 182  
         {
 183  0
             muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>(){
 184  
                 public void onNotification(MuleContextNotification notification)
 185  
                 {
 186  0
                     if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED)
 187  
                     {
 188  
                         //We delay starting until the context has been started since we need the MuleAjaxServlet to initialise first
 189  0
                         setInitialStateStopped(false);
 190  
                         try
 191  
                         {
 192  0
                             start();
 193  
                             // update the agent displaying webapp urls to the user
 194  0
                             final JettyWebappServerAgent agent = (JettyWebappServerAgent) muleContext.getRegistry().lookupAgent(JettyWebappServerAgent.NAME);
 195  0
                             if (agent != null)
 196  
                             {
 197  0
                                 agent.onJettyConnectorStarted(JettyHttpConnector.this);
 198  
                             }
 199  
                         }
 200  0
                         catch (MuleException e)
 201  
                         {
 202  0
                             throw new MuleRuntimeException(CoreMessages.failedToStart(getName()), e);
 203  0
                         }
 204  
                     }
 205  0
                 }
 206  
             });
 207  
         }
 208  0
         catch (NotificationException e)
 209  
         {
 210  0
             throw new InitialisationException(e, this);
 211  0
         }
 212  0
     }
 213  
 
 214  
     @SuppressWarnings("unchecked")
 215  
     protected void initialiseFromConfigFile() throws InitialisationException
 216  
     {
 217  0
         if (configFile == null)
 218  
         {
 219  0
             return;
 220  
         }
 221  
         try
 222  
         {
 223  0
             InputStream is = IOUtils.getResourceAsStream(configFile, getClass());
 224  0
             XmlConfiguration config = new XmlConfiguration(is);
 225  
 
 226  0
             String appHome =
 227  
                 muleContext.getRegistry().lookupObject(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
 228  0
             if (appHome == null)
 229  
             {
 230  
                 // Mule IDE sets app.home as part of the launch config it creates
 231  0
                 appHome = System.getProperty(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
 232  
             }
 233  
 
 234  0
             if (appHome != null)
 235  
             {
 236  0
                 config.getProperties().put(MuleProperties.APP_HOME_DIRECTORY_PROPERTY, appHome);
 237  
             }
 238  
 
 239  0
             config.configure(httpServer);
 240  
         }
 241  0
         catch (Exception e)
 242  
         {
 243  0
             throw new InitialisationException(e, this);
 244  0
         }
 245  0
     }
 246  
 
 247  
     /**
 248  
      * Template method to dispose any resources associated with this receiver. There
 249  
      * is not need to dispose the connector as this is already done by the framework
 250  
      */
 251  
     @Override
 252  
     protected void doDispose()
 253  
     {
 254  0
         holders.clear();
 255  0
     }
 256  
 
 257  
     @Override
 258  
     protected void doStart() throws MuleException
 259  
     {
 260  
         try
 261  
         {
 262  0
             httpServer.start();
 263  
             
 264  0
             if (deployer != null)
 265  
             {
 266  0
                 deployer.start();
 267  
             }
 268  
             
 269  0
             for (ConnectorHolder<?, ?> contextHolder : holders.values())
 270  
             {
 271  0
                 contextHolder.start();
 272  
             }
 273  
         }
 274  0
         catch (Exception e)
 275  
         {
 276  0
             throw new LifecycleException(CoreMessages.failedToStart("Jetty Http Receiver"), e, this);
 277  0
         }
 278  0
     }
 279  
 
 280  
     @Override
 281  
     protected void doStop() throws MuleException
 282  
     {
 283  
         try
 284  
         {
 285  0
             httpServer.stop();
 286  
             
 287  0
             if (deployer != null)
 288  
             {
 289  0
                 deployer.stop();
 290  
             }
 291  
             
 292  0
             for (ConnectorHolder<?, ?> connectorRef : holders.values())
 293  
             {
 294  0
                 connectorRef.stop();
 295  
             }
 296  
         }
 297  0
         catch (Exception e)
 298  
         {
 299  0
             throw new LifecycleException(CoreMessages.failedToStop("Jetty Http Receiver"), e, this);
 300  0
         }
 301  0
     }
 302  
 
 303  
     /**
 304  
      * Template method where any connections should be made for the connector
 305  
      *
 306  
      * @throws Exception
 307  
      */
 308  
     @Override
 309  
     protected void doConnect() throws Exception
 310  
     {
 311  
         //do nothing
 312  0
     }
 313  
 
 314  
     /**
 315  
      * Template method where any connected resources used by the connector should be
 316  
      * disconnected
 317  
      *
 318  
      * @throws Exception
 319  
      */
 320  
     @Override
 321  
     protected void doDisconnect() throws Exception
 322  
     {
 323  
         //do nothing
 324  0
     }
 325  
 
 326  
      @Override
 327  
     protected MessageReceiver createReceiver(FlowConstruct flowConstruct, InboundEndpoint endpoint) throws Exception
 328  
     {
 329  0
         MessageReceiver receiver = super.createReceiver(flowConstruct, endpoint);
 330  0
         registerJettyEndpoint(receiver, endpoint);
 331  0
         return receiver;
 332  
     }
 333  
 
 334  
     protected org.mortbay.jetty.AbstractConnector createJettyConnector()
 335  
     {
 336  0
         return new SelectChannelConnector();
 337  
     }
 338  
 
 339  
     public void unregisterListener(MessageReceiver receiver) throws MuleException
 340  
     {
 341  0
         String connectorKey = getHolderKey(receiver.getEndpoint());
 342  
 
 343  0
         synchronized (this)
 344  
         {
 345  0
             ConnectorHolder connectorRef = holders.get(connectorKey);
 346  0
             if (connectorRef != null)
 347  
             {
 348  0
                 if (!connectorRef.isReferenced())
 349  
                 {
 350  0
                     getHttpServer().removeConnector(connectorRef.getConnector());
 351  0
                     holders.remove(connectorKey);
 352  0
                     connectorRef.stop();
 353  
                 }
 354  
             }
 355  0
         }
 356  0
     }
 357  
 
 358  
     public Server getHttpServer()
 359  
     {
 360  0
         return httpServer;
 361  
     }
 362  
 
 363  
     public String getConfigFile()
 364  
     {
 365  0
         return configFile;
 366  
     }
 367  
 
 368  
     public void setConfigFile(String configFile)
 369  
     {
 370  0
         this.configFile = configFile;
 371  0
     }
 372  
 
 373  
     public JettyReceiverServlet getReceiverServlet()
 374  
     {
 375  0
         return receiverServlet;
 376  
     }
 377  
 
 378  
     public void setReceiverServlet(JettyReceiverServlet receiverServlet)
 379  
     {
 380  0
         this.receiverServlet = receiverServlet;
 381  0
     }
 382  
 
 383  
     @Override
 384  
     public ReplyToHandler getReplyToHandler(ImmutableEndpoint endpoint)
 385  
     {
 386  0
         if (isUseContinuations())
 387  
         {
 388  0
             return new JettyContinuationsReplyToHandler(getDefaultResponseTransformers(endpoint), muleContext);
 389  
         }
 390  0
         return super.getReplyToHandler(endpoint);
 391  
     }
 392  
 
 393  
     public boolean isUseContinuations()
 394  
     {
 395  0
         return useContinuations;
 396  
     }
 397  
 
 398  
     public void setUseContinuations(boolean useContinuations)
 399  
     {
 400  0
         this.useContinuations = useContinuations;
 401  0
     }
 402  
 
 403  
     ConnectorHolder<? extends MuleReceiverServlet, ? extends JettyHttpMessageReceiver> registerJettyEndpoint(MessageReceiver receiver, InboundEndpoint endpoint) throws MuleException
 404  
     {
 405  
         // Make sure that there is a connector for the requested endpoint.
 406  0
         String connectorKey = getHolderKey(endpoint);
 407  
 
 408  
         ConnectorHolder holder;
 409  
 
 410  0
         synchronized (this)
 411  
         {
 412  0
             holder = holders.get(connectorKey);
 413  0
             if (holder == null)
 414  
             {
 415  0
                 Connector connector = createJettyConnector();
 416  
 
 417  0
                 connector.setPort(endpoint.getEndpointURI().getPort());
 418  0
                 connector.setHost(endpoint.getEndpointURI().getHost());
 419  0
                 if ("localhost".equalsIgnoreCase(endpoint.getEndpointURI().getHost()))
 420  
                 {
 421  0
                     logger.warn("You use localhost interface! It means that no external connections will be available."
 422  
                             + " Don't you want to use 0.0.0.0 instead (all network interfaces)?");
 423  
                 }
 424  0
                 getHttpServer().addConnector(connector);
 425  
 
 426  0
                 holder = createContextHolder(connector, receiver.getEndpoint(), receiver);
 427  0
                 holders.put(connectorKey, holder);
 428  0
                 if(isStarted())
 429  
                 {
 430  0
                     holder.start();
 431  
                 }
 432  0
             }
 433  
             else
 434  
             {
 435  0
                 holder.addReceiver(receiver);
 436  
             }
 437  0
         }
 438  0
         return holder;
 439  
     }
 440  
 
 441  
     protected ConnectorHolder createContextHolder(Connector connector, InboundEndpoint endpoint, MessageReceiver receiver)
 442  
     {
 443  0
         return new MuleReceiverConnectorHolder(connector, (JettyReceiverServlet) createServlet(connector, endpoint), (JettyHttpMessageReceiver)receiver);
 444  
     }
 445  
 
 446  
     protected Servlet createServlet(Connector connector, ImmutableEndpoint endpoint)
 447  
     {
 448  
         HttpServlet servlet;
 449  0
         if (getReceiverServlet() == null)
 450  
         {
 451  0
             if(isUseContinuations())
 452  
             {
 453  0
                 servlet = new JettyContinuationsReceiverServlet();
 454  
             }
 455  
             else
 456  
             {
 457  0
                 servlet = new JettyReceiverServlet();
 458  
             }
 459  
         }
 460  
         else
 461  
         {
 462  0
             servlet = getReceiverServlet();
 463  
         }
 464  
 
 465  0
         String path = endpoint.getEndpointURI().getPath();
 466  0
         if(StringUtils.isBlank(path))
 467  
         {
 468  0
             path = ROOT;
 469  
         }
 470  
 
 471  0
         ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
 472  0
         Context context = new Context(handlerCollection, ROOT, Context.NO_SECURITY);
 473  0
         context.setConnectorNames(new String[]{connector.getName()});
 474  0
         context.addEventListener(new MuleServletContextListener(muleContext, getName()));
 475  
 
 476  0
         if (resourceBase != null)
 477  
         {
 478  0
             Context resourceContext = new Context(handlerCollection, path, Context.NO_SECURITY);
 479  0
             resourceContext.setResourceBase(resourceBase);
 480  
         }
 481  
 
 482  0
         context.addServlet(JarResourceServlet.class, JarResourceServlet.DEFAULT_PATH_SPEC);
 483  
 
 484  0
         ServletHolder holder = new ServletHolder();
 485  0
         holder.setServlet(servlet);
 486  0
         context.addServlet(holder, "/*");
 487  0
         getHttpServer().addHandler(handlerCollection);
 488  0
         return servlet;
 489  
     }
 490  
 
 491  
     protected String getHolderKey(ImmutableEndpoint endpoint)
 492  
     {
 493  0
         return endpoint.getProtocol() + ":" + endpoint.getEndpointURI().getHost() + ":" + endpoint.getEndpointURI().getPort();
 494  
     }
 495  
 
 496  0
     public class MuleReceiverConnectorHolder extends AbstractConnectorHolder<JettyReceiverServlet, JettyHttpMessageReceiver>
 497  
     {
 498  0
         List<MessageReceiver> messageReceivers = new ArrayList<MessageReceiver>();
 499  
 
 500  
         public MuleReceiverConnectorHolder(Connector connector, JettyReceiverServlet servlet, JettyHttpMessageReceiver receiver)
 501  0
         {
 502  0
             super(connector, servlet, receiver);
 503  0
             addReceiver(receiver);
 504  0
         }
 505  
 
 506  
         public boolean isReferenced()
 507  
         {
 508  0
             return messageReceivers.size() > 0;
 509  
         }
 510  
 
 511  
         public void addReceiver(JettyHttpMessageReceiver receiver)
 512  
         {
 513  0
             messageReceivers.add(receiver);
 514  0
             if(started)
 515  
             {
 516  0
                 getServlet().addReceiver(receiver);
 517  
             }
 518  0
         }
 519  
 
 520  
         public void removeReceiver(JettyHttpMessageReceiver receiver)
 521  
         {
 522  0
             messageReceivers.remove(receiver);
 523  0
             getServlet().removeReceiver(receiver);
 524  0
         }
 525  
 
 526  
         @Override
 527  
         public void start() throws MuleException
 528  
         {
 529  0
             super.start();
 530  
             
 531  0
             for (MessageReceiver receiver : messageReceivers)
 532  
             {
 533  0
                 servlet.addReceiver(receiver);
 534  
             }
 535  0
         }
 536  
 
 537  
         @Override
 538  
         public void stop() throws MuleException
 539  
         {
 540  0
             super.stop();
 541  
 
 542  0
             for (MessageReceiver receiver : messageReceivers)
 543  
             {
 544  0
                 servlet.removeReceiver(receiver);
 545  
             }
 546  0
         }
 547  
     }
 548  
 
 549  
     public String getResourceBase()
 550  
     {
 551  0
         return resourceBase;
 552  
     }
 553  
 
 554  
     public void setResourceBase(String resourceBase)
 555  
     {
 556  0
         this.resourceBase = resourceBase;
 557  0
     }
 558  
 
 559  
     public WebappsConfiguration getWebappsConfiguration()
 560  
     {
 561  0
         return webappsConfiguration;
 562  
     }
 563  
 
 564  
     public void setWebappsConfiguration(WebappsConfiguration webappsConfiguration)
 565  
     {
 566  0
         this.webappsConfiguration = webappsConfiguration;
 567  0
     }
 568  
 
 569  
     /**
 570  
      * A helper method to differentiate between jetty-based connectors which can host full wars and ones which can't.
 571  
      */
 572  
     public boolean canHostFullWars()
 573  
     {
 574  0
         return true;
 575  
     }
 576  
 
 577  
     /**
 578  
      * A helper class to let jetty startup, we don't bind java ee objects like java:comp/UserTransaction.
 579  
      */
 580  
     public static class DummyJndiConfiguration extends Configuration
 581  
     {
 582  
 
 583  
         public DummyJndiConfiguration() throws ClassNotFoundException
 584  0
         {
 585  0
         }
 586  
 
 587  
         @Override
 588  
         public void bindUserTransaction() throws Exception
 589  
         {
 590  
             // no-op
 591  0
         }
 592  
 
 593  
         @Override
 594  
         protected void lockCompEnv() throws Exception
 595  
         {
 596  
             // no-op
 597  0
         }
 598  
     }
 599  
 }