View Javadoc

1   /*
2    * $Id: JettyHttpConnector.java 19554 2010-09-10 13:39:43Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.servlet.jetty;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.MuleException;
15  import org.mule.api.MuleRuntimeException;
16  import org.mule.api.config.MuleProperties;
17  import org.mule.api.construct.FlowConstruct;
18  import org.mule.api.context.notification.MuleContextNotificationListener;
19  import org.mule.api.endpoint.ImmutableEndpoint;
20  import org.mule.api.endpoint.InboundEndpoint;
21  import org.mule.api.lifecycle.InitialisationException;
22  import org.mule.api.lifecycle.LifecycleException;
23  import org.mule.api.transport.MessageReceiver;
24  import org.mule.api.transport.ReplyToHandler;
25  import org.mule.config.i18n.CoreMessages;
26  import org.mule.context.notification.MuleContextNotification;
27  import org.mule.context.notification.NotificationException;
28  import org.mule.transport.AbstractConnector;
29  import org.mule.transport.servlet.JarResourceServlet;
30  import org.mule.transport.servlet.MuleReceiverServlet;
31  import org.mule.transport.servlet.MuleServletContextListener;
32  import org.mule.util.IOUtils;
33  import org.mule.util.StringUtils;
34  
35  import java.io.InputStream;
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.Server;
45  import org.mortbay.jetty.handler.ContextHandlerCollection;
46  import org.mortbay.jetty.nio.SelectChannelConnector;
47  import org.mortbay.jetty.servlet.Context;
48  import org.mortbay.jetty.servlet.ServletHolder;
49  import org.mortbay.xml.XmlConfiguration;
50  
51  /**
52   * The <code>JettyConnector</code> can be using to embed a Jetty server to receive requests on an
53   * http inound endpoint. One server is created for each connector declared, many Jetty endpoints
54   * can share the same connector.
55   */
56  public class JettyHttpConnector extends AbstractConnector
57  {
58      public static final String RESOURCE_BASE_PROPERTY = "resourceBase";
59  
60      public static final String ROOT = "/";
61  
62      public static final String JETTY = "jetty";
63  
64      private Server httpServer;
65  
66      private String configFile;
67  
68      private JettyReceiverServlet receiverServlet;
69  
70      private boolean useContinuations = false;
71  
72      protected HashMap<String, ConnectorHolder> holders = new HashMap<String, ConnectorHolder>();
73  
74      public JettyHttpConnector(MuleContext context)
75      {
76          super(context);
77          registerSupportedProtocol("http");
78          registerSupportedProtocol(JETTY);
79          setInitialStateStopped(true);
80      }
81  
82      public String getProtocol()
83      {
84          return JETTY;
85      }
86  
87      @Override
88      protected void doInitialise() throws InitialisationException
89      {
90          httpServer = new Server();
91          initialiseFromConfigFile();
92  
93          try
94          {
95              muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>(){
96                  public void onNotification(MuleContextNotification notification)
97                  {
98                      if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED)
99                      {
100                         //We delay starting until the context has been started since we need the MuleAjaxServlet to initialise first
101                         setInitialStateStopped(false);
102                         try
103                         {
104                             start();
105                         }
106                         catch (MuleException e)
107                         {
108                             throw new MuleRuntimeException(CoreMessages.failedToStart(getName()), e);
109                         }
110                     }
111                 }
112             });
113         }
114         catch (NotificationException e)
115         {
116             throw new InitialisationException(e, this);
117         }
118     }
119 
120     @SuppressWarnings("unchecked")
121     protected void initialiseFromConfigFile() throws InitialisationException
122     {
123         if (configFile != null)
124         {
125             try
126             {
127                 InputStream is = IOUtils.getResourceAsStream(configFile, getClass());
128                 XmlConfiguration config = new XmlConfiguration(is);
129 
130                 String appHome =
131                     muleContext.getRegistry().lookupObject(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
132                 if (appHome == null)
133                 {
134                     // Mule IDE sets app.home as part of the launch config it creates
135                     appHome = System.getProperty(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
136                 }
137 
138                 if (appHome != null)
139                 {
140                     config.getProperties().put(MuleProperties.APP_HOME_DIRECTORY_PROPERTY, appHome);
141                 }
142                 
143                 config.configure(httpServer);
144             }
145             catch (Exception e)
146             {
147                 throw new InitialisationException(e, this);
148             }
149         }
150     }
151 
152     /**
153      * Template method to dispose any resources associated with this receiver. There
154      * is not need to dispose the connector as this is already done by the framework
155      */
156     @Override
157     protected void doDispose()
158     {
159         holders.clear();
160     }
161 
162     @Override
163     protected void doStart() throws MuleException
164     {
165         try
166         {
167             httpServer.start();
168             for (ConnectorHolder<?, ?> contextHolder : holders.values())
169             {
170                 contextHolder.start();
171             }
172         }
173         catch (Exception e)
174         {
175             throw new LifecycleException(CoreMessages.failedToStart("Jetty Http Receiver"), e, this);
176         }
177     }
178 
179     @Override
180     protected void doStop() throws MuleException
181     {
182         try
183         {
184             httpServer.stop();
185             for (ConnectorHolder<?, ?> connectorRef : holders.values())
186             {
187                 connectorRef.stop();
188             }
189         }
190         catch (Exception e)
191         {
192             throw new LifecycleException(CoreMessages.failedToStop("Jetty Http Receiver"), e, this);
193         }
194     }
195 
196     /**
197      * Template method where any connections should be made for the connector
198      *
199      * @throws Exception
200      */
201     @Override
202     protected void doConnect() throws Exception
203     {
204         //do nothing
205     }
206 
207     /**
208      * Template method where any connected resources used by the connector should be
209      * disconnected
210      *
211      * @throws Exception
212      */
213     @Override
214     protected void doDisconnect() throws Exception
215     {
216         //do nothing
217     }
218 
219      @Override
220     protected MessageReceiver createReceiver(FlowConstruct flowConstruct, InboundEndpoint endpoint) throws Exception
221     {
222         MessageReceiver receiver = super.createReceiver(flowConstruct, endpoint);
223         registerJettyEndpoint(receiver, endpoint);
224         return receiver;
225     }
226 
227     protected org.mortbay.jetty.AbstractConnector createJettyConnector()
228     {
229         return new SelectChannelConnector();
230     }
231 
232     public void unregisterListener(MessageReceiver receiver) throws MuleException
233     {
234         String connectorKey = getHolderKey(receiver.getEndpoint());
235 
236         synchronized (this)
237         {
238             ConnectorHolder connectorRef = holders.get(connectorKey);
239             if (connectorRef != null)
240             {
241                 if (!connectorRef.isReferenced())
242                 {
243                     getHttpServer().removeConnector(connectorRef.getConnector());
244                     holders.remove(connectorKey);
245                     connectorRef.stop();
246                 }
247             }
248         }
249     }
250 
251     public Server getHttpServer()
252     {
253         return httpServer;
254     }
255 
256     public String getConfigFile()
257     {
258         return configFile;
259     }
260 
261     public void setConfigFile(String configFile)
262     {
263         this.configFile = configFile;
264     }
265 
266     public JettyReceiverServlet getReceiverServlet()
267     {
268         return receiverServlet;
269     }
270 
271     public void setReceiverServlet(JettyReceiverServlet receiverServlet)
272     {
273         this.receiverServlet = receiverServlet;
274     }
275 
276     @Override
277     public ReplyToHandler getReplyToHandler(ImmutableEndpoint endpoint)
278     {
279         if (isUseContinuations())
280         {
281             return new JettyContinuationsReplyToHandler(getDefaultResponseTransformers(endpoint), muleContext);
282         }
283         return super.getReplyToHandler(endpoint);
284     }
285 
286     public boolean isUseContinuations()
287     {
288         return useContinuations;
289     }
290 
291     public void setUseContinuations(boolean useContinuations)
292     {
293         this.useContinuations = useContinuations;
294     }
295 
296     ConnectorHolder<? extends MuleReceiverServlet, ? extends JettyHttpMessageReceiver> registerJettyEndpoint(MessageReceiver receiver, InboundEndpoint endpoint) throws MuleException
297     {
298         // Make sure that there is a connector for the requested endpoint.
299         String connectorKey = getHolderKey(endpoint);
300 
301         ConnectorHolder holder;
302 
303         synchronized (this)
304         {
305             holder = holders.get(connectorKey);
306             if (holder == null)
307             {
308                 Connector connector = createJettyConnector();
309 
310                 connector.setPort(endpoint.getEndpointURI().getPort());
311                 connector.setHost(endpoint.getEndpointURI().getHost());
312                 if ("localhost".equalsIgnoreCase(endpoint.getEndpointURI().getHost()))
313                 {
314                     logger.warn("You use localhost interface! It means that no external connections will be available."
315                             + " Don't you want to use 0.0.0.0 instead (all network interfaces)?");
316                 }
317                 getHttpServer().addConnector(connector);
318 
319                 holder = createContextHolder(connector, receiver.getEndpoint(), receiver);
320                 holders.put(connectorKey, holder);
321                 if(isStarted())
322                 {
323                     holder.start();
324                 }
325             }
326             else
327             {
328                 holder.addReceiver(receiver);
329             }
330         }
331         return holder;
332     }
333 
334     protected ConnectorHolder createContextHolder(Connector connector, InboundEndpoint endpoint, MessageReceiver receiver)
335     {
336         return new MuleReceiverConnectorHolder(connector, (JettyReceiverServlet) createServlet(connector, endpoint), (JettyHttpMessageReceiver)receiver);
337     }
338 
339     protected Servlet createServlet(Connector connector, ImmutableEndpoint endpoint)
340     {
341         HttpServlet servlet;
342         if (getReceiverServlet() == null)
343         {
344             if(isUseContinuations())
345             {
346                 servlet = new JettyContinuationsReceiverServlet();
347             }
348             else
349             {
350                 servlet = new JettyReceiverServlet();
351             }
352         }
353         else
354         {
355             servlet = getReceiverServlet();
356         }
357 
358         String path = endpoint.getEndpointURI().getPath();
359         if(StringUtils.isBlank(path))
360         {
361             path = ROOT;
362         }
363 
364         ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
365         Context context = new Context(handlerCollection, ROOT, Context.NO_SECURITY);
366         context.setConnectorNames(new String[]{connector.getName()});
367         context.addEventListener(new MuleServletContextListener(muleContext, getName()));
368 
369         String resourceBase = (String)endpoint.getProperty(RESOURCE_BASE_PROPERTY);
370         if(resourceBase!=null)
371         {
372 
373             Context resourceContext = new Context(handlerCollection, path, Context.NO_SECURITY);
374             resourceContext.setResourceBase(resourceBase);
375         }
376 
377         context.addServlet(JarResourceServlet.class, JarResourceServlet.DEFAULT_PATH_SPEC);
378 
379         ServletHolder holder = new ServletHolder();
380         holder.setServlet(servlet);
381         context.addServlet(holder, "/*");
382         getHttpServer().addHandler(handlerCollection);
383         return servlet;
384     }
385 
386     protected String getHolderKey(ImmutableEndpoint endpoint)
387     {
388         return endpoint.getProtocol() + ":" + endpoint.getEndpointURI().getHost() + ":" + endpoint.getEndpointURI().getPort();
389     }
390 
391     public class MuleReceiverConnectorHolder extends AbstractConnectorHolder<JettyReceiverServlet, JettyHttpMessageReceiver>
392     {
393         List<MessageReceiver> messageReceivers = new ArrayList<MessageReceiver>();
394 
395         public MuleReceiverConnectorHolder(Connector connector, JettyReceiverServlet servlet, JettyHttpMessageReceiver receiver)
396         {
397             super(connector, servlet, receiver);
398             addReceiver(receiver);
399         }
400 
401         public boolean isReferenced()
402         {
403             return messageReceivers.size() > 0;
404         }
405 
406         public void addReceiver(JettyHttpMessageReceiver receiver)
407         {
408             messageReceivers.add(receiver);
409             if(started)
410             {
411                 getServlet().addReceiver(receiver);
412             }
413         }
414 
415         public void removeReceiver(JettyHttpMessageReceiver receiver)
416         {
417             messageReceivers.remove(receiver);
418             getServlet().removeReceiver(receiver);
419         }
420 
421         @Override
422         public void start() throws MuleException
423         {
424             super.start();
425             
426             for (MessageReceiver receiver : messageReceivers)
427             {
428                 servlet.addReceiver(receiver);
429             }
430         }
431 
432         @Override
433         public void stop() throws MuleException
434         {
435             super.stop();
436 
437             for (MessageReceiver receiver : messageReceivers)
438             {
439                 servlet.removeReceiver(receiver);
440             }
441         }
442     }
443 }