1
2
3
4
5
6
7
8
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 edu.emory.mathcs.backport.java.util.Arrays;
44
45 import org.mortbay.jetty.Connector;
46 import org.mortbay.jetty.Server;
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.xml.XmlConfiguration;
52
53
54
55
56
57
58 public class JettyHttpConnector extends AbstractConnector
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 private String resourceBase;
73
74 private WebappsConfiguration webappsConfiguration;
75
76 protected HashMap<String, ConnectorHolder> holders = new HashMap<String, ConnectorHolder>();
77
78 private WebAppDeployer deployer;
79
80 public JettyHttpConnector(MuleContext context)
81 {
82 super(context);
83 registerSupportedProtocol("http");
84 registerSupportedProtocol(JETTY);
85 setInitialStateStopped(true);
86 }
87
88 public String getProtocol()
89 {
90 return JETTY;
91 }
92
93 @Override
94 protected void doInitialise() throws InitialisationException
95 {
96 httpServer = new Server();
97
98 if (webappsConfiguration != null)
99 {
100 deployer = new WebAppDeployer();
101 deployer.setWebAppDir(webappsConfiguration.getDirectory());
102 deployer.setExtract(true);
103 deployer.setParentLoaderPriority(false);
104 deployer.setServerClasses(webappsConfiguration.getServerClasses());
105 deployer.setSystemClasses(webappsConfiguration.getSystemClasses());
106
107 org.mortbay.jetty.AbstractConnector connector = createJettyConnector();
108 connector.setHost(webappsConfiguration.getHost());
109 connector.setPort(webappsConfiguration.getPort());
110 deployer.setContexts(httpServer);
111
112 httpServer.addConnector(connector);
113 httpServer.addLifeCycle(deployer);
114 }
115
116 initialiseFromConfigFile();
117
118 try
119 {
120 muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>(){
121 public void onNotification(MuleContextNotification notification)
122 {
123 if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED)
124 {
125
126 setInitialStateStopped(false);
127 try
128 {
129 start();
130 }
131 catch (MuleException e)
132 {
133 throw new MuleRuntimeException(CoreMessages.failedToStart(getName()), e);
134 }
135 }
136 }
137 });
138 }
139 catch (NotificationException e)
140 {
141 throw new InitialisationException(e, this);
142 }
143 }
144
145 @SuppressWarnings("unchecked")
146 protected void initialiseFromConfigFile() throws InitialisationException
147 {
148 if (configFile != null)
149 {
150 try
151 {
152 InputStream is = IOUtils.getResourceAsStream(configFile, getClass());
153 XmlConfiguration config = new XmlConfiguration(is);
154
155 String appHome =
156 muleContext.getRegistry().lookupObject(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
157 if (appHome == null)
158 {
159
160 appHome = System.getProperty(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
161 }
162
163 if (appHome != null)
164 {
165 config.getProperties().put(MuleProperties.APP_HOME_DIRECTORY_PROPERTY, appHome);
166 }
167
168 config.configure(httpServer);
169 }
170 catch (Exception e)
171 {
172 throw new InitialisationException(e, this);
173 }
174 }
175 }
176
177
178
179
180
181 @Override
182 protected void doDispose()
183 {
184 holders.clear();
185 }
186
187 @Override
188 protected void doStart() throws MuleException
189 {
190 try
191 {
192 httpServer.start();
193
194 if (deployer != null)
195 {
196 deployer.start();
197 }
198
199 for (ConnectorHolder<?, ?> contextHolder : holders.values())
200 {
201 contextHolder.start();
202 }
203 }
204 catch (Exception e)
205 {
206 throw new LifecycleException(CoreMessages.failedToStart("Jetty Http Receiver"), e, this);
207 }
208 }
209
210 @Override
211 protected void doStop() throws MuleException
212 {
213 try
214 {
215 httpServer.stop();
216
217 if (deployer != null)
218 {
219 deployer.stop();
220 }
221
222 for (ConnectorHolder<?, ?> connectorRef : holders.values())
223 {
224 connectorRef.stop();
225 }
226 }
227 catch (Exception e)
228 {
229 throw new LifecycleException(CoreMessages.failedToStop("Jetty Http Receiver"), e, this);
230 }
231 }
232
233
234
235
236
237
238 @Override
239 protected void doConnect() throws Exception
240 {
241
242 }
243
244
245
246
247
248
249
250 @Override
251 protected void doDisconnect() throws Exception
252 {
253
254 }
255
256 @Override
257 protected MessageReceiver createReceiver(FlowConstruct flowConstruct, InboundEndpoint endpoint) throws Exception
258 {
259 MessageReceiver receiver = super.createReceiver(flowConstruct, endpoint);
260 registerJettyEndpoint(receiver, endpoint);
261 return receiver;
262 }
263
264 protected org.mortbay.jetty.AbstractConnector createJettyConnector()
265 {
266 return new SelectChannelConnector();
267 }
268
269 public void unregisterListener(MessageReceiver receiver) throws MuleException
270 {
271 String connectorKey = getHolderKey(receiver.getEndpoint());
272
273 synchronized (this)
274 {
275 ConnectorHolder connectorRef = holders.get(connectorKey);
276 if (connectorRef != null)
277 {
278 if (!connectorRef.isReferenced())
279 {
280 getHttpServer().removeConnector(connectorRef.getConnector());
281 holders.remove(connectorKey);
282 connectorRef.stop();
283 }
284 }
285 }
286 }
287
288 public Server getHttpServer()
289 {
290 return httpServer;
291 }
292
293 public String getConfigFile()
294 {
295 return configFile;
296 }
297
298 public void setConfigFile(String configFile)
299 {
300 this.configFile = configFile;
301 }
302
303 public JettyReceiverServlet getReceiverServlet()
304 {
305 return receiverServlet;
306 }
307
308 public void setReceiverServlet(JettyReceiverServlet receiverServlet)
309 {
310 this.receiverServlet = receiverServlet;
311 }
312
313 @Override
314 public ReplyToHandler getReplyToHandler(ImmutableEndpoint endpoint)
315 {
316 if (isUseContinuations())
317 {
318 return new JettyContinuationsReplyToHandler(getDefaultResponseTransformers(endpoint), muleContext);
319 }
320 return super.getReplyToHandler(endpoint);
321 }
322
323 public boolean isUseContinuations()
324 {
325 return useContinuations;
326 }
327
328 public void setUseContinuations(boolean useContinuations)
329 {
330 this.useContinuations = useContinuations;
331 }
332
333 ConnectorHolder<? extends MuleReceiverServlet, ? extends JettyHttpMessageReceiver> registerJettyEndpoint(MessageReceiver receiver, InboundEndpoint endpoint) throws MuleException
334 {
335
336 String connectorKey = getHolderKey(endpoint);
337
338 ConnectorHolder holder;
339
340 synchronized (this)
341 {
342 holder = holders.get(connectorKey);
343 if (holder == null)
344 {
345 Connector connector = createJettyConnector();
346
347 connector.setPort(endpoint.getEndpointURI().getPort());
348 connector.setHost(endpoint.getEndpointURI().getHost());
349 if ("localhost".equalsIgnoreCase(endpoint.getEndpointURI().getHost()))
350 {
351 logger.warn("You use localhost interface! It means that no external connections will be available."
352 + " Don't you want to use 0.0.0.0 instead (all network interfaces)?");
353 }
354 getHttpServer().addConnector(connector);
355
356 holder = createContextHolder(connector, receiver.getEndpoint(), receiver);
357 holders.put(connectorKey, holder);
358 if(isStarted())
359 {
360 holder.start();
361 }
362 }
363 else
364 {
365 holder.addReceiver(receiver);
366 }
367 }
368 return holder;
369 }
370
371 protected ConnectorHolder createContextHolder(Connector connector, InboundEndpoint endpoint, MessageReceiver receiver)
372 {
373 return new MuleReceiverConnectorHolder(connector, (JettyReceiverServlet) createServlet(connector, endpoint), (JettyHttpMessageReceiver)receiver);
374 }
375
376 protected Servlet createServlet(Connector connector, ImmutableEndpoint endpoint)
377 {
378 HttpServlet servlet;
379 if (getReceiverServlet() == null)
380 {
381 if(isUseContinuations())
382 {
383 servlet = new JettyContinuationsReceiverServlet();
384 }
385 else
386 {
387 servlet = new JettyReceiverServlet();
388 }
389 }
390 else
391 {
392 servlet = getReceiverServlet();
393 }
394
395 String path = endpoint.getEndpointURI().getPath();
396 if(StringUtils.isBlank(path))
397 {
398 path = ROOT;
399 }
400
401 ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
402 Context context = new Context(handlerCollection, ROOT, Context.NO_SECURITY);
403 context.setConnectorNames(new String[]{connector.getName()});
404 context.addEventListener(new MuleServletContextListener(muleContext, getName()));
405
406 if (resourceBase != null)
407 {
408 Context resourceContext = new Context(handlerCollection, path, Context.NO_SECURITY);
409 resourceContext.setResourceBase(resourceBase);
410 }
411
412 context.addServlet(JarResourceServlet.class, JarResourceServlet.DEFAULT_PATH_SPEC);
413
414 ServletHolder holder = new ServletHolder();
415 holder.setServlet(servlet);
416 context.addServlet(holder, "/*");
417 getHttpServer().addHandler(handlerCollection);
418 return servlet;
419 }
420
421 protected String getHolderKey(ImmutableEndpoint endpoint)
422 {
423 return endpoint.getProtocol() + ":" + endpoint.getEndpointURI().getHost() + ":" + endpoint.getEndpointURI().getPort();
424 }
425
426 public class MuleReceiverConnectorHolder extends AbstractConnectorHolder<JettyReceiverServlet, JettyHttpMessageReceiver>
427 {
428 List<MessageReceiver> messageReceivers = new ArrayList<MessageReceiver>();
429
430 public MuleReceiverConnectorHolder(Connector connector, JettyReceiverServlet servlet, JettyHttpMessageReceiver receiver)
431 {
432 super(connector, servlet, receiver);
433 addReceiver(receiver);
434 }
435
436 public boolean isReferenced()
437 {
438 return messageReceivers.size() > 0;
439 }
440
441 public void addReceiver(JettyHttpMessageReceiver receiver)
442 {
443 messageReceivers.add(receiver);
444 if(started)
445 {
446 getServlet().addReceiver(receiver);
447 }
448 }
449
450 public void removeReceiver(JettyHttpMessageReceiver receiver)
451 {
452 messageReceivers.remove(receiver);
453 getServlet().removeReceiver(receiver);
454 }
455
456 @Override
457 public void start() throws MuleException
458 {
459 super.start();
460
461 for (MessageReceiver receiver : messageReceivers)
462 {
463 servlet.addReceiver(receiver);
464 }
465 }
466
467 @Override
468 public void stop() throws MuleException
469 {
470 super.stop();
471
472 for (MessageReceiver receiver : messageReceivers)
473 {
474 servlet.removeReceiver(receiver);
475 }
476 }
477 }
478
479 public String getResourceBase()
480 {
481 return resourceBase;
482 }
483
484 public void setResourceBase(String resourceBase)
485 {
486 this.resourceBase = resourceBase;
487 }
488
489 public WebappsConfiguration getWebappsConfiguration()
490 {
491 return webappsConfiguration;
492 }
493
494 public void setWebappsConfiguration(WebappsConfiguration webappsConfiguration)
495 {
496 this.webappsConfiguration = webappsConfiguration;
497 }
498 }