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