1
2
3
4
5
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
58
59
60
61 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 private boolean useContinuations = false;
74
75 private String resourceBase;
76
77 private WebappsConfiguration webappsConfiguration;
78
79 protected HashMap<String, ConnectorHolder> holders = new HashMap<String, ConnectorHolder>();
80
81 private WebAppDeployer deployer;
82
83 public JettyHttpConnector(MuleContext context)
84 {
85 super(context);
86 setupJettyLogging();
87 registerSupportedProtocol("http");
88 registerSupportedProtocol(JETTY);
89 setInitialStateStopped(true);
90 }
91
92 protected void setupJettyLogging()
93 {
94 if ((Log.getLog() instanceof JettyLogger) == false)
95 {
96 Log.setLog(new JettyLogger());
97 }
98 }
99
100 public String getProtocol()
101 {
102 return JETTY;
103 }
104
105 @Override
106 protected void doInitialise() throws InitialisationException
107 {
108 httpServer = new Server()
109 {
110 @Override
111 public void addHandler(Handler handler)
112 {
113 final Connector c = getServer().getConnectors()[0];
114 if (handler instanceof WebAppContext)
115 {
116 final WebAppContext webapp = (WebAppContext) handler;
117 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 final File workDir = new File(muleContext.getConfiguration().getWorkingDirectory(),
123 "_exploded_wars" + webapp.getContextPath());
124 workDir.mkdirs();
125 webapp.setTempDirectory(workDir);
126
127 webapp.setAttribute("muleContext", muleContext);
128
129 if (logger.isInfoEnabled())
130 {
131 logger.info(StringMessageUtils.getBoilerPlate(msg, '*', 70));
132 }
133 }
134 super.addHandler(handler);
135 }
136 };
137
138 if (webappsConfiguration != null)
139 {
140 deployer = new WebAppDeployer();
141 String webAppDir = webappsConfiguration.getDirectory();
142 if (StringUtils.isBlank(webAppDir))
143 {
144
145 final String appDir = muleContext.getRegistry().get(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
146 webAppDir = appDir + "/webapps";
147 }
148
149 if (configFile == null)
150 {
151
152
153 final URL muleDefaults = ClassUtils.getResource("org/mule/transport/jetty/webdefault.xml", getClass());
154 deployer.setDefaultsDescriptor(muleDefaults.toExternalForm());
155 }
156 deployer.setWebAppDir(webAppDir);
157 deployer.setExtract(true);
158 deployer.setParentLoaderPriority(false);
159 deployer.setServerClasses(webappsConfiguration.getServerClasses());
160 deployer.setSystemClasses(webappsConfiguration.getSystemClasses());
161
162 org.mortbay.jetty.AbstractConnector jettyConnector = createJettyConnector();
163 jettyConnector.setHost(webappsConfiguration.getHost());
164 jettyConnector.setPort(webappsConfiguration.getPort());
165 deployer.setContexts(httpServer);
166 String[] confClasses = new String[]
167 {
168
169 WebInfConfiguration.class.getName(),
170
171 DummyJndiConfiguration.class.getName()
172 };
173 deployer.setConfigurationClasses(confClasses);
174
175 httpServer.addConnector(jettyConnector);
176 httpServer.addLifeCycle(deployer);
177 }
178
179 initialiseFromConfigFile();
180
181 try
182 {
183 muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>(){
184 public void onNotification(MuleContextNotification notification)
185 {
186 if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED)
187 {
188
189 setInitialStateStopped(false);
190 try
191 {
192 start();
193
194 final JettyWebappServerAgent agent = (JettyWebappServerAgent) muleContext.getRegistry().lookupAgent(JettyWebappServerAgent.NAME);
195 if (agent != null)
196 {
197 agent.onJettyConnectorStarted(JettyHttpConnector.this);
198 }
199 }
200 catch (MuleException e)
201 {
202 throw new MuleRuntimeException(CoreMessages.failedToStart(getName()), e);
203 }
204 }
205 }
206 });
207 }
208 catch (NotificationException e)
209 {
210 throw new InitialisationException(e, this);
211 }
212 }
213
214 @SuppressWarnings("unchecked")
215 protected void initialiseFromConfigFile() throws InitialisationException
216 {
217 if (configFile == null)
218 {
219 return;
220 }
221 try
222 {
223 InputStream is = IOUtils.getResourceAsStream(configFile, getClass());
224 XmlConfiguration config = new XmlConfiguration(is);
225
226 String appHome =
227 muleContext.getRegistry().lookupObject(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
228 if (appHome == null)
229 {
230
231 appHome = System.getProperty(MuleProperties.APP_HOME_DIRECTORY_PROPERTY);
232 }
233
234 if (appHome != null)
235 {
236 config.getProperties().put(MuleProperties.APP_HOME_DIRECTORY_PROPERTY, appHome);
237 }
238
239 config.configure(httpServer);
240 }
241 catch (Exception e)
242 {
243 throw new InitialisationException(e, this);
244 }
245 }
246
247
248
249
250
251 @Override
252 protected void doDispose()
253 {
254 holders.clear();
255 }
256
257 @Override
258 protected void doStart() throws MuleException
259 {
260 try
261 {
262 httpServer.start();
263
264 if (deployer != null)
265 {
266 deployer.start();
267 }
268
269 for (ConnectorHolder<?, ?> contextHolder : holders.values())
270 {
271 contextHolder.start();
272 }
273 }
274 catch (Exception e)
275 {
276 throw new LifecycleException(CoreMessages.failedToStart("Jetty Http Receiver"), e, this);
277 }
278 }
279
280 @Override
281 protected void doStop() throws MuleException
282 {
283 try
284 {
285 httpServer.stop();
286
287 if (deployer != null)
288 {
289 deployer.stop();
290 }
291
292 for (ConnectorHolder<?, ?> connectorRef : holders.values())
293 {
294 connectorRef.stop();
295 }
296 }
297 catch (Exception e)
298 {
299 throw new LifecycleException(CoreMessages.failedToStop("Jetty Http Receiver"), e, this);
300 }
301 }
302
303
304
305
306
307
308 @Override
309 protected void doConnect() throws Exception
310 {
311
312 }
313
314
315
316
317
318
319
320 @Override
321 protected void doDisconnect() throws Exception
322 {
323
324 }
325
326 @Override
327 protected MessageReceiver createReceiver(FlowConstruct flowConstruct, InboundEndpoint endpoint) throws Exception
328 {
329 MessageReceiver receiver = super.createReceiver(flowConstruct, endpoint);
330 registerJettyEndpoint(receiver, endpoint);
331 return receiver;
332 }
333
334 protected org.mortbay.jetty.AbstractConnector createJettyConnector()
335 {
336 return new SelectChannelConnector();
337 }
338
339 public void unregisterListener(MessageReceiver receiver) throws MuleException
340 {
341 String connectorKey = getHolderKey(receiver.getEndpoint());
342
343 synchronized (this)
344 {
345 ConnectorHolder connectorRef = holders.get(connectorKey);
346 if (connectorRef != null)
347 {
348 if (!connectorRef.isReferenced())
349 {
350 getHttpServer().removeConnector(connectorRef.getConnector());
351 holders.remove(connectorKey);
352 connectorRef.stop();
353 }
354 }
355 }
356 }
357
358 public Server getHttpServer()
359 {
360 return httpServer;
361 }
362
363 public String getConfigFile()
364 {
365 return configFile;
366 }
367
368 public void setConfigFile(String configFile)
369 {
370 this.configFile = configFile;
371 }
372
373 public JettyReceiverServlet getReceiverServlet()
374 {
375 return receiverServlet;
376 }
377
378 public void setReceiverServlet(JettyReceiverServlet receiverServlet)
379 {
380 this.receiverServlet = receiverServlet;
381 }
382
383 @Override
384 public ReplyToHandler getReplyToHandler(ImmutableEndpoint endpoint)
385 {
386 if (isUseContinuations())
387 {
388 return new JettyContinuationsReplyToHandler(getDefaultResponseTransformers(endpoint), muleContext);
389 }
390 return super.getReplyToHandler(endpoint);
391 }
392
393 public boolean isUseContinuations()
394 {
395 return useContinuations;
396 }
397
398 public void setUseContinuations(boolean useContinuations)
399 {
400 this.useContinuations = useContinuations;
401 }
402
403 ConnectorHolder<? extends MuleReceiverServlet, ? extends JettyHttpMessageReceiver> registerJettyEndpoint(MessageReceiver receiver, InboundEndpoint endpoint) throws MuleException
404 {
405
406 String connectorKey = getHolderKey(endpoint);
407
408 ConnectorHolder holder;
409
410 synchronized (this)
411 {
412 holder = holders.get(connectorKey);
413 if (holder == null)
414 {
415 Connector connector = createJettyConnector();
416
417 connector.setPort(endpoint.getEndpointURI().getPort());
418 connector.setHost(endpoint.getEndpointURI().getHost());
419 if ("localhost".equalsIgnoreCase(endpoint.getEndpointURI().getHost()))
420 {
421 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 getHttpServer().addConnector(connector);
425
426 holder = createContextHolder(connector, receiver.getEndpoint(), receiver);
427 holders.put(connectorKey, holder);
428 if(isStarted())
429 {
430 holder.start();
431 }
432 }
433 else
434 {
435 holder.addReceiver(receiver);
436 }
437 }
438 return holder;
439 }
440
441 protected ConnectorHolder createContextHolder(Connector connector, InboundEndpoint endpoint, MessageReceiver receiver)
442 {
443 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 if (getReceiverServlet() == null)
450 {
451 if(isUseContinuations())
452 {
453 servlet = new JettyContinuationsReceiverServlet();
454 }
455 else
456 {
457 servlet = new JettyReceiverServlet();
458 }
459 }
460 else
461 {
462 servlet = getReceiverServlet();
463 }
464
465 String path = endpoint.getEndpointURI().getPath();
466 if(StringUtils.isBlank(path))
467 {
468 path = ROOT;
469 }
470
471 ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
472 Context context = new Context(handlerCollection, ROOT, Context.NO_SECURITY);
473 context.setConnectorNames(new String[]{connector.getName()});
474 context.addEventListener(new MuleServletContextListener(muleContext, getName()));
475
476 if (resourceBase != null)
477 {
478 Context resourceContext = new Context(handlerCollection, path, Context.NO_SECURITY);
479 resourceContext.setResourceBase(resourceBase);
480 }
481
482 context.addServlet(JarResourceServlet.class, JarResourceServlet.DEFAULT_PATH_SPEC);
483
484 ServletHolder holder = new ServletHolder();
485 holder.setServlet(servlet);
486 context.addServlet(holder, "/*");
487 getHttpServer().addHandler(handlerCollection);
488 return servlet;
489 }
490
491 protected String getHolderKey(ImmutableEndpoint endpoint)
492 {
493 return endpoint.getProtocol() + ":" + endpoint.getEndpointURI().getHost() + ":" + endpoint.getEndpointURI().getPort();
494 }
495
496 public class MuleReceiverConnectorHolder extends AbstractConnectorHolder<JettyReceiverServlet, JettyHttpMessageReceiver>
497 {
498 List<MessageReceiver> messageReceivers = new ArrayList<MessageReceiver>();
499
500 public MuleReceiverConnectorHolder(Connector connector, JettyReceiverServlet servlet, JettyHttpMessageReceiver receiver)
501 {
502 super(connector, servlet, receiver);
503 addReceiver(receiver);
504 }
505
506 public boolean isReferenced()
507 {
508 return messageReceivers.size() > 0;
509 }
510
511 public void addReceiver(JettyHttpMessageReceiver receiver)
512 {
513 messageReceivers.add(receiver);
514 if(started)
515 {
516 getServlet().addReceiver(receiver);
517 }
518 }
519
520 public void removeReceiver(JettyHttpMessageReceiver receiver)
521 {
522 messageReceivers.remove(receiver);
523 getServlet().removeReceiver(receiver);
524 }
525
526 @Override
527 public void start() throws MuleException
528 {
529 super.start();
530
531 for (MessageReceiver receiver : messageReceivers)
532 {
533 servlet.addReceiver(receiver);
534 }
535 }
536
537 @Override
538 public void stop() throws MuleException
539 {
540 super.stop();
541
542 for (MessageReceiver receiver : messageReceivers)
543 {
544 servlet.removeReceiver(receiver);
545 }
546 }
547 }
548
549 public String getResourceBase()
550 {
551 return resourceBase;
552 }
553
554 public void setResourceBase(String resourceBase)
555 {
556 this.resourceBase = resourceBase;
557 }
558
559 public WebappsConfiguration getWebappsConfiguration()
560 {
561 return webappsConfiguration;
562 }
563
564 public void setWebappsConfiguration(WebappsConfiguration webappsConfiguration)
565 {
566 this.webappsConfiguration = webappsConfiguration;
567 }
568
569
570
571
572 public boolean canHostFullWars()
573 {
574 return true;
575 }
576
577
578
579
580 public static class DummyJndiConfiguration extends Configuration
581 {
582
583 public DummyJndiConfiguration() throws ClassNotFoundException
584 {
585 }
586
587 @Override
588 public void bindUserTransaction() throws Exception
589 {
590
591 }
592
593 @Override
594 protected void lockCompEnv() throws Exception
595 {
596
597 }
598 }
599 }