001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.component.jetty; 018 019 import java.io.File; 020 import java.net.URI; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 025 import javax.management.MBeanServer; 026 import javax.servlet.Filter; 027 028 import org.apache.camel.CamelContext; 029 import org.apache.camel.Endpoint; 030 import org.apache.camel.RuntimeCamelException; 031 import org.apache.camel.component.http.CamelServlet; 032 import org.apache.camel.component.http.HttpBinding; 033 import org.apache.camel.component.http.HttpComponent; 034 import org.apache.camel.component.http.HttpConsumer; 035 import org.apache.camel.component.http.HttpEndpoint; 036 import org.apache.camel.spi.ManagementAgent; 037 import org.apache.camel.spi.ManagementStrategy; 038 import org.apache.camel.util.CastUtils; 039 import org.apache.camel.util.IntrospectionSupport; 040 import org.apache.camel.util.ObjectHelper; 041 import org.apache.camel.util.URISupport; 042 import org.eclipse.jetty.client.Address; 043 import org.eclipse.jetty.client.HttpClient; 044 import org.eclipse.jetty.jmx.MBeanContainer; 045 import org.eclipse.jetty.server.Connector; 046 import org.eclipse.jetty.server.Handler; 047 import org.eclipse.jetty.server.Server; 048 import org.eclipse.jetty.server.handler.ContextHandlerCollection; 049 import org.eclipse.jetty.server.handler.HandlerCollection; 050 import org.eclipse.jetty.server.handler.HandlerWrapper; 051 import org.eclipse.jetty.server.nio.SelectChannelConnector; 052 import org.eclipse.jetty.server.session.SessionHandler; 053 import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; 054 import org.eclipse.jetty.servlet.FilterHolder; 055 import org.eclipse.jetty.servlet.ServletContextHandler; 056 import org.eclipse.jetty.servlet.ServletHolder; 057 import org.eclipse.jetty.servlets.MultiPartFilter; 058 import org.eclipse.jetty.util.component.LifeCycle; 059 import org.eclipse.jetty.util.thread.QueuedThreadPool; 060 import org.eclipse.jetty.util.thread.ThreadPool; 061 import org.slf4j.Logger; 062 import org.slf4j.LoggerFactory; 063 064 /** 065 * An HttpComponent which starts an embedded Jetty for to handle consuming from 066 * the http endpoints. 067 * 068 * @version 069 */ 070 public class JettyHttpComponent extends HttpComponent { 071 public static final String TMP_DIR = "CamelJettyTempDir"; 072 073 protected static final HashMap<String, ConnectorRef> CONNECTORS = new HashMap<String, ConnectorRef>(); 074 075 private static final transient Logger LOG = LoggerFactory.getLogger(JettyHttpComponent.class); 076 private static final String JETTY_SSL_KEYSTORE = "org.eclipse.jetty.ssl.keystore"; 077 private static final String JETTY_SSL_KEYPASSWORD = "org.eclipse.jetty.ssl.keypassword"; 078 private static final String JETTY_SSL_PASSWORD = "org.eclipse.jetty.ssl.password"; 079 080 protected String sslKeyPassword; 081 protected String sslPassword; 082 protected String sslKeystore; 083 protected Map<Integer, SslSelectChannelConnector> sslSocketConnectors; 084 protected Map<Integer, SelectChannelConnector> socketConnectors; 085 protected Map<String, Object> sslSocketConnectorProperties; 086 protected Map<String, Object> socketConnectorProperties; 087 protected HttpClient httpClient; 088 protected ThreadPool httpClientThreadPool; 089 protected Integer httpClientMinThreads; 090 protected Integer httpClientMaxThreads; 091 protected Integer minThreads; 092 protected Integer maxThreads; 093 protected ThreadPool threadPool; 094 protected MBeanContainer mbContainer; 095 protected boolean enableJmx; 096 protected JettyHttpBinding jettyHttpBinding; 097 protected Long continuationTimeout; 098 protected boolean useContinuation = true; 099 100 class ConnectorRef { 101 Server server; 102 Connector connector; 103 CamelServlet servlet; 104 int refCount; 105 106 public ConnectorRef(Server server, Connector connector, CamelServlet servlet) { 107 this.server = server; 108 this.connector = connector; 109 this.servlet = servlet; 110 increment(); 111 } 112 113 public int increment() { 114 return ++refCount; 115 } 116 117 public int decrement() { 118 return --refCount; 119 } 120 121 public int getRefCount() { 122 return refCount; 123 } 124 } 125 126 @Override 127 protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { 128 String addressUri = uri.startsWith("jetty:") ? remaining : uri; 129 Map<String, Object> httpClientParameters = new HashMap<String, Object>(parameters); 130 131 // must extract well known parameters before we create the endpoint 132 List<Handler> handlerList = resolveAndRemoveReferenceListParameter(parameters, "handlers", Handler.class); 133 HttpBinding binding = resolveAndRemoveReferenceParameter(parameters, "httpBindingRef", HttpBinding.class); 134 JettyHttpBinding jettyBinding = resolveAndRemoveReferenceParameter(parameters, "jettyHttpBindingRef", JettyHttpBinding.class); 135 Boolean throwExceptionOnFailure = getAndRemoveParameter(parameters, "throwExceptionOnFailure", Boolean.class); 136 Boolean transferException = getAndRemoveParameter(parameters, "transferException", Boolean.class); 137 Boolean bridgeEndpoint = getAndRemoveParameter(parameters, "bridgeEndpoint", Boolean.class); 138 Boolean matchOnUriPrefix = getAndRemoveParameter(parameters, "matchOnUriPrefix", Boolean.class); 139 Boolean enableJmx = getAndRemoveParameter(parameters, "enableJmx", Boolean.class); 140 Boolean enableMultipartFilter = getAndRemoveParameter(parameters, "enableMultipartFilter", 141 Boolean.class, true); 142 Filter multipartFilter = resolveAndRemoveReferenceParameter(parameters, "multipartFilterRef", Filter.class); 143 Long continuationTimeout = getAndRemoveParameter(parameters, "continuationTimeout", Long.class); 144 Boolean useContinuation = getAndRemoveParameter(parameters, "useContinuation", Boolean.class); 145 146 // configure http client if we have url configuration for it 147 // http client is only used for jetty http producer (hence not very commonly used) 148 HttpClient client = null; 149 if (IntrospectionSupport.hasProperties(parameters, "httpClient.")) { 150 // set additional parameters on http client 151 // only create client when needed 152 client = getHttpClient(); 153 IntrospectionSupport.setProperties(client, parameters, "httpClient."); 154 // validate that we could resolve all httpClient. parameters as this component is lenient 155 validateParameters(uri, parameters, "httpClient."); 156 } 157 // keep the configure parameters for the http client 158 for (String key : parameters.keySet()) { 159 httpClientParameters.remove(key); 160 } 161 URI endpointUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(httpClientParameters)); 162 163 // restructure uri to be based on the parameters left as we dont want to include the Camel internal options 164 URI httpUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(parameters)); 165 166 // create endpoint after all known parameters have been extracted from parameters 167 JettyHttpEndpoint endpoint = new JettyHttpEndpoint(this, endpointUri.toString(), httpUri); 168 setEndpointHeaderFilterStrategy(endpoint); 169 170 if (client != null) { 171 endpoint.setClient(client); 172 } 173 if (handlerList.size() > 0) { 174 endpoint.setHandlers(handlerList); 175 } 176 // prefer to use endpoint configured over component configured 177 if (binding == null) { 178 // fallback to component configured 179 binding = getHttpBinding(); 180 } 181 if (binding != null) { 182 endpoint.setBinding(binding); 183 } 184 // prefer to use endpoint configured over component configured 185 if (jettyBinding == null) { 186 // fallback to component configured 187 jettyBinding = getJettyHttpBinding(); 188 } 189 if (jettyBinding != null) { 190 endpoint.setJettyBinding(jettyBinding); 191 } 192 // should we use an exception for failed error codes? 193 if (throwExceptionOnFailure != null) { 194 endpoint.setThrowExceptionOnFailure(throwExceptionOnFailure); 195 } 196 // should we transfer exception as serialized object 197 if (transferException != null) { 198 endpoint.setTransferException(transferException); 199 } 200 if (bridgeEndpoint != null) { 201 endpoint.setBridgeEndpoint(bridgeEndpoint); 202 } 203 if (matchOnUriPrefix != null) { 204 endpoint.setMatchOnUriPrefix(matchOnUriPrefix); 205 } 206 207 if (enableJmx != null) { 208 endpoint.setEnableJmx(enableJmx); 209 } else { 210 // set this option based on setting of JettyHttpComponent 211 endpoint.setEnableJmx(isEnableJmx()); 212 } 213 214 endpoint.setEnableMultipartFilter(enableMultipartFilter); 215 216 if (multipartFilter != null) { 217 endpoint.setMultipartFilter(multipartFilter); 218 endpoint.setEnableMultipartFilter(true); 219 } 220 221 if (continuationTimeout != null) { 222 endpoint.setContinuationTimeout(continuationTimeout); 223 } 224 if (useContinuation != null) { 225 endpoint.setUseContinuation(useContinuation); 226 } 227 228 setProperties(endpoint, parameters); 229 return endpoint; 230 } 231 232 /** 233 * Connects the URL specified on the endpoint to the specified processor. 234 */ 235 @Override 236 public void connect(HttpConsumer consumer) throws Exception { 237 // Make sure that there is a connector for the requested endpoint. 238 JettyHttpEndpoint endpoint = (JettyHttpEndpoint)consumer.getEndpoint(); 239 String connectorKey = getConnectorKey(endpoint); 240 241 synchronized (CONNECTORS) { 242 ConnectorRef connectorRef = CONNECTORS.get(connectorKey); 243 if (connectorRef == null) { 244 Connector connector; 245 if ("https".equals(endpoint.getProtocol())) { 246 connector = getSslSocketConnector(endpoint.getPort()); 247 } else { 248 connector = getSocketConnector(endpoint.getPort()); 249 } 250 connector.setPort(endpoint.getPort()); 251 connector.setHost(endpoint.getHttpUri().getHost()); 252 if ("localhost".equalsIgnoreCase(endpoint.getHttpUri().getHost())) { 253 LOG.warn("You use localhost interface! It means that no external connections will be available." 254 + " Don't you want to use 0.0.0.0 instead (all network interfaces)? " + endpoint); 255 } 256 Server server = createServer(); 257 if (endpoint.isEnableJmx()) { 258 enableJmx(server); 259 } 260 server.addConnector(connector); 261 262 connectorRef = new ConnectorRef(server, connector, createServletForConnector(server, connector, endpoint.getHandlers(), endpoint)); 263 // must enable session before we start 264 if (endpoint.isSessionSupport()) { 265 enableSessionSupport(connectorRef.server, connectorKey); 266 } 267 connectorRef.server.start(); 268 269 CONNECTORS.put(connectorKey, connectorRef); 270 271 } else { 272 // ref track the connector 273 connectorRef.increment(); 274 } 275 // check the session support 276 if (endpoint.isSessionSupport()) { 277 enableSessionSupport(connectorRef.server, connectorKey); 278 } 279 280 if (endpoint.isEnableMultipartFilter()) { 281 enableMultipartFilter(endpoint, connectorRef.server, connectorKey); 282 } 283 connectorRef.servlet.connect(consumer); 284 } 285 } 286 287 private void enableJmx(Server server) { 288 MBeanContainer containerToRegister = getMbContainer(); 289 if (containerToRegister != null) { 290 LOG.info("Jetty JMX Extensions is enabled"); 291 server.getContainer().addEventListener(containerToRegister); 292 // Since we may have many Servers running, don't tie the MBeanContainer 293 // to a Server lifecycle or we end up closing it while it is still in use. 294 //server.addBean(mbContainer); 295 } 296 } 297 298 private void enableSessionSupport(Server server, String connectorKey) throws Exception { 299 ServletContextHandler context = (ServletContextHandler)server.getChildHandlerByClass(ServletContextHandler.class); 300 if (context.getSessionHandler() == null) { 301 SessionHandler sessionHandler = new SessionHandler(); 302 if (context.isStarted()) { 303 throw new IllegalStateException("Server has already been started. Cannot enabled sessionSupport on " + connectorKey); 304 } else { 305 context.setSessionHandler(sessionHandler); 306 } 307 } 308 } 309 310 private void enableMultipartFilter(HttpEndpoint endpoint, Server server, String connectorKey) throws Exception { 311 ServletContextHandler context = (ServletContextHandler) server 312 .getChildHandlerByClass(ServletContextHandler.class); 313 CamelContext camelContext = this.getCamelContext(); 314 FilterHolder filterHolder = new FilterHolder(); 315 filterHolder.setInitParameter("deleteFiles", "true"); 316 if (ObjectHelper.isNotEmpty(camelContext.getProperties().get(TMP_DIR))) { 317 File file = new File(camelContext.getProperties().get(TMP_DIR)); 318 if (!file.isDirectory()) { 319 throw new RuntimeCamelException( 320 "The temp file directory of camel-jetty is not exists, please recheck it with directory name :" 321 + camelContext.getProperties().get(TMP_DIR)); 322 } 323 context.setAttribute("javax.servlet.context.tempdir", file); 324 } 325 // if a filter ref was provided, use it. 326 Filter filter = ((JettyHttpEndpoint) endpoint).getMultipartFilter(); 327 if (filter == null) { 328 // if no filter ref was provided, use the default filter 329 filter = new MultiPartFilter(); 330 } 331 filterHolder.setFilter(new CamelMultipartFilter(filter)); 332 String pathSpec = endpoint.getPath(); 333 if (pathSpec == null || "".equals(pathSpec)) { 334 pathSpec = "/"; 335 } 336 if (endpoint.isMatchOnUriPrefix()) { 337 pathSpec = pathSpec.endsWith("/") ? pathSpec + "*" : pathSpec + "/*"; 338 } 339 context.addFilter(filterHolder, pathSpec, 0); 340 LOG.debug("using multipart filter implementation " + filter.getClass().getName() + " for path " + pathSpec); 341 } 342 343 /** 344 * Disconnects the URL specified on the endpoint from the specified processor. 345 */ 346 @Override 347 public void disconnect(HttpConsumer consumer) throws Exception { 348 // If the connector is not needed anymore then stop it 349 HttpEndpoint endpoint = consumer.getEndpoint(); 350 String connectorKey = getConnectorKey(endpoint); 351 352 synchronized (CONNECTORS) { 353 ConnectorRef connectorRef = CONNECTORS.get(connectorKey); 354 if (connectorRef != null) { 355 connectorRef.servlet.disconnect(consumer); 356 if (connectorRef.decrement() == 0) { 357 connectorRef.server.removeConnector(connectorRef.connector); 358 connectorRef.connector.stop(); 359 connectorRef.server.stop(); 360 CONNECTORS.remove(connectorKey); 361 // Camel controls the lifecycle of these entities so remove the 362 // registered MBeans when Camel is done with the managed objects. 363 if (mbContainer != null) { 364 mbContainer.removeBean(connectorRef.server); 365 mbContainer.removeBean(connectorRef.connector); 366 } 367 } 368 } 369 } 370 } 371 372 private String getConnectorKey(HttpEndpoint endpoint) { 373 return endpoint.getProtocol() + ":" + endpoint.getHttpUri().getHost() + ":" + endpoint.getPort(); 374 } 375 376 // Properties 377 // ------------------------------------------------------------------------- 378 379 public String getSslKeyPassword() { 380 return sslKeyPassword; 381 } 382 383 public void setSslKeyPassword(String sslKeyPassword) { 384 this.sslKeyPassword = sslKeyPassword; 385 } 386 387 public String getSslPassword() { 388 return sslPassword; 389 } 390 391 public void setSslPassword(String sslPassword) { 392 this.sslPassword = sslPassword; 393 } 394 395 public void setKeystore(String sslKeystore) { 396 this.sslKeystore = sslKeystore; 397 } 398 399 public String getKeystore() { 400 return sslKeystore; 401 } 402 403 protected SslSelectChannelConnector getSslSocketConnector(int port) throws Exception { 404 SslSelectChannelConnector answer = null; 405 if (sslSocketConnectors != null) { 406 answer = sslSocketConnectors.get(port); 407 } 408 if (answer == null) { 409 answer = createSslSocketConnector(); 410 } 411 return answer; 412 } 413 414 protected SslSelectChannelConnector createSslSocketConnector() throws Exception { 415 SslSelectChannelConnector answer = new SslSelectChannelConnector(); 416 // with default null values, jetty ssl system properties 417 // and console will be read by jetty implementation 418 419 String keystoreProperty = System.getProperty(JETTY_SSL_KEYSTORE); 420 if (keystoreProperty != null) { 421 answer.setKeystore(keystoreProperty); 422 } else if (sslKeystore != null) { 423 answer.setKeystore(sslKeystore); 424 } 425 426 String keystorePassword = System.getProperty(JETTY_SSL_KEYPASSWORD); 427 if (keystorePassword != null) { 428 answer.setKeyPassword(keystorePassword); 429 } else if (sslKeyPassword != null) { 430 answer.setKeyPassword(sslKeyPassword); 431 } 432 433 String password = System.getProperty(JETTY_SSL_PASSWORD); 434 if (password != null) { 435 answer.setPassword(password); 436 } else if (sslPassword != null) { 437 answer.setPassword(sslPassword); 438 } 439 440 if (getSslSocketConnectorProperties() != null) { 441 // must copy the map otherwise it will be deleted 442 Map<String, Object> properties = new HashMap<String, Object>(getSslSocketConnectorProperties()); 443 IntrospectionSupport.setProperties(answer, properties); 444 if (properties.size() > 0) { 445 throw new IllegalArgumentException("There are " + properties.size() 446 + " parameters that couldn't be set on the SslSocketConnector." 447 + " Check the uri if the parameters are spelt correctly and that they are properties of the SslSocketConnector." 448 + " Unknown parameters=[" + properties + "]"); 449 } 450 } 451 return answer; 452 } 453 454 public Map<Integer, SslSelectChannelConnector> getSslSocketConnectors() { 455 return sslSocketConnectors; 456 } 457 458 public void setSslSocketConnectors(Map <Integer, SslSelectChannelConnector> connectors) { 459 sslSocketConnectors = connectors; 460 } 461 462 public SelectChannelConnector getSocketConnector(int port) throws Exception { 463 SelectChannelConnector answer = null; 464 if (socketConnectors != null) { 465 answer = socketConnectors.get(port); 466 } 467 if (answer == null) { 468 answer = createSocketConnector(); 469 } 470 return answer; 471 } 472 473 protected SelectChannelConnector createSocketConnector() throws Exception { 474 SelectChannelConnector answer = new SelectChannelConnector(); 475 if (getSocketConnectorProperties() != null) { 476 // must copy the map otherwise it will be deleted 477 Map<String, Object> properties = new HashMap<String, Object>(getSocketConnectorProperties()); 478 IntrospectionSupport.setProperties(answer, properties); 479 if (properties.size() > 0) { 480 throw new IllegalArgumentException("There are " + properties.size() 481 + " parameters that couldn't be set on the SocketConnector." 482 + " Check the uri if the parameters are spelt correctly and that they are properties of the SelectChannelConnector." 483 + " Unknown parameters=[" + properties + "]"); 484 } 485 } 486 return answer; 487 } 488 489 public void setSocketConnectors(Map<Integer, SelectChannelConnector> socketConnectors) { 490 this.socketConnectors = socketConnectors; 491 } 492 493 public synchronized HttpClient getHttpClient() { 494 if (httpClient == null) { 495 httpClient = new HttpClient(); 496 httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); 497 498 if (System.getProperty("http.proxyHost") != null && System.getProperty("http.proxyPort") != null) { 499 String host = System.getProperty("http.proxyHost"); 500 int port = Integer.parseInt(System.getProperty("http.proxyPort")); 501 if (LOG.isDebugEnabled()) { 502 LOG.debug("Java System Property http.proxyHost and http.proxyPort detected. Using http proxy host: " 503 + host + " port: " + port); 504 } 505 httpClient.setProxy(new Address(host, port)); 506 } 507 508 // use QueueThreadPool as the default bounded is deprecated (see SMXCOMP-157) 509 if (getHttpClientThreadPool() == null) { 510 QueuedThreadPool qtp = new QueuedThreadPool(); 511 if (httpClientMinThreads != null) { 512 qtp.setMinThreads(httpClientMinThreads.intValue()); 513 } 514 if (httpClientMaxThreads != null) { 515 qtp.setMaxThreads(httpClientMaxThreads.intValue()); 516 } 517 try { 518 qtp.start(); 519 } catch (Exception e) { 520 throw new RuntimeCamelException("Error starting JettyHttpClient thread pool: " + qtp, e); 521 } 522 setHttpClientThreadPool(qtp); 523 } 524 httpClient.setThreadPool(getHttpClientThreadPool()); 525 } 526 return httpClient; 527 } 528 529 public void setHttpClient(HttpClient httpClient) { 530 this.httpClient = httpClient; 531 } 532 533 public ThreadPool getHttpClientThreadPool() { 534 return httpClientThreadPool; 535 } 536 537 public void setHttpClientThreadPool(ThreadPool httpClientThreadPool) { 538 this.httpClientThreadPool = httpClientThreadPool; 539 } 540 541 public Integer getHttpClientMinThreads() { 542 return httpClientMinThreads; 543 } 544 545 public void setHttpClientMinThreads(Integer httpClientMinThreads) { 546 this.httpClientMinThreads = httpClientMinThreads; 547 } 548 549 public Integer getHttpClientMaxThreads() { 550 return httpClientMaxThreads; 551 } 552 553 public void setHttpClientMaxThreads(Integer httpClientMaxThreads) { 554 this.httpClientMaxThreads = httpClientMaxThreads; 555 } 556 557 public Integer getMinThreads() { 558 return minThreads; 559 } 560 561 public void setMinThreads(Integer minThreads) { 562 this.minThreads = minThreads; 563 } 564 565 public Integer getMaxThreads() { 566 return maxThreads; 567 } 568 569 public void setMaxThreads(Integer maxThreads) { 570 this.maxThreads = maxThreads; 571 } 572 573 public ThreadPool getThreadPool() { 574 return threadPool; 575 } 576 577 public void setThreadPool(ThreadPool threadPool) { 578 this.threadPool = threadPool; 579 } 580 581 public void setEnableJmx(boolean enableJmx) { 582 this.enableJmx = enableJmx; 583 } 584 585 public boolean isEnableJmx() { 586 return enableJmx; 587 } 588 589 public JettyHttpBinding getJettyHttpBinding() { 590 return jettyHttpBinding; 591 } 592 593 public void setJettyHttpBinding(JettyHttpBinding jettyHttpBinding) { 594 this.jettyHttpBinding = jettyHttpBinding; 595 } 596 597 public synchronized MBeanContainer getMbContainer() { 598 // If null, provide the default implementation. 599 if (mbContainer == null) { 600 MBeanServer mbs = null; 601 602 final ManagementStrategy mStrategy = this.getCamelContext().getManagementStrategy(); 603 final ManagementAgent mAgent = mStrategy.getManagementAgent(); 604 if (mAgent != null) { 605 mbs = mAgent.getMBeanServer(); 606 } 607 608 if (mbs != null) { 609 mbContainer = new MBeanContainer(mbs); 610 startMbContainer(); 611 } else { 612 LOG.warn("JMX disabled in CamelContext. Jetty JMX extensions will remain disabled."); 613 } 614 } 615 616 return this.mbContainer; 617 } 618 619 public void setMbContainer(MBeanContainer mbContainer) { 620 this.mbContainer = mbContainer; 621 } 622 623 public Map<String, Object> getSslSocketConnectorProperties() { 624 return sslSocketConnectorProperties; 625 } 626 627 public void setSslSocketConnectorProperties(Map<String, Object> sslSocketConnectorProperties) { 628 this.sslSocketConnectorProperties = sslSocketConnectorProperties; 629 } 630 631 public Map<String, Object> getSocketConnectorProperties() { 632 return socketConnectorProperties; 633 } 634 635 public void setSocketConnectorProperties(Map<String, Object> socketConnectorProperties) { 636 this.socketConnectorProperties = socketConnectorProperties; 637 } 638 639 public void addSocketConnectorProperty(String key, Object value) { 640 if (socketConnectorProperties == null) { 641 socketConnectorProperties = new HashMap<String, Object>(); 642 } 643 socketConnectorProperties.put(key, value); 644 } 645 646 public void addSslSocketConnectorProperty(String key, Object value) { 647 if (sslSocketConnectorProperties == null) { 648 sslSocketConnectorProperties = new HashMap<String, Object>(); 649 } 650 sslSocketConnectorProperties.put(key, value); 651 } 652 653 public Long getContinuationTimeout() { 654 return continuationTimeout; 655 } 656 657 public void setContinuationTimeout(Long continuationTimeout) { 658 this.continuationTimeout = continuationTimeout; 659 } 660 661 public boolean isUseContinuation() { 662 return useContinuation; 663 } 664 665 public void setUseContinuation(boolean useContinuation) { 666 this.useContinuation = useContinuation; 667 } 668 669 // Implementation methods 670 // ------------------------------------------------------------------------- 671 protected CamelServlet createServletForConnector(Server server, Connector connector, 672 List<Handler> handlers, JettyHttpEndpoint endpoint) throws Exception { 673 ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.NO_SECURITY | ServletContextHandler.NO_SESSIONS); 674 context.setConnectorNames(new String[] {connector.getName()}); 675 676 if (handlers != null && !handlers.isEmpty()) { 677 for (Handler handler : handlers) { 678 if (handler instanceof HandlerWrapper) { 679 ((HandlerWrapper) handler).setHandler(server.getHandler()); 680 server.setHandler(handler); 681 } else { 682 HandlerCollection handlerCollection = new HandlerCollection(); 683 handlerCollection.addHandler(server.getHandler()); 684 handlerCollection.addHandler(handler); 685 server.setHandler(handlerCollection); 686 } 687 } 688 } 689 690 CamelServlet camelServlet; 691 boolean jetty = endpoint.getUseContinuation() != null ? endpoint.getUseContinuation() : isUseContinuation(); 692 if (jetty) { 693 // use Jetty continuations 694 CamelContinuationServlet jettyServlet = new CamelContinuationServlet(); 695 // configure timeout and log it so end user know what we are using 696 Long timeout = endpoint.getContinuationTimeout() != null ? endpoint.getContinuationTimeout() : getContinuationTimeout(); 697 if (timeout != null) { 698 LOG.info("Using Jetty continuation timeout: " + timeout + " millis for: " + endpoint); 699 jettyServlet.setContinuationTimeout(timeout); 700 } else { 701 LOG.info("Using default Jetty continuation timeout for: " + endpoint); 702 } 703 704 // use the jetty servlet 705 camelServlet = jettyServlet; 706 } else { 707 // do not use jetty so use a plain servlet 708 camelServlet = new CamelServlet(); 709 LOG.info("Jetty continuation is disabled for: " + endpoint); 710 } 711 712 ServletHolder holder = new ServletHolder(); 713 holder.setServlet(camelServlet); 714 context.addServlet(holder, "/*"); 715 716 return camelServlet; 717 } 718 719 protected Server createServer() throws Exception { 720 Server server = new Server(); 721 ContextHandlerCollection collection = new ContextHandlerCollection(); 722 server.setHandler(collection); 723 724 // configure thread pool if min/max given 725 if (minThreads != null || maxThreads != null) { 726 if (getThreadPool() != null) { 727 throw new IllegalArgumentException("You cannot configure both minThreads/maxThreads and a custom threadPool on JettyHttpComponent: " + this); 728 } 729 QueuedThreadPool qtp = new QueuedThreadPool(); 730 if (minThreads != null) { 731 qtp.setMinThreads(minThreads.intValue()); 732 } 733 if (maxThreads != null) { 734 qtp.setMaxThreads(maxThreads.intValue()); 735 } 736 try { 737 qtp.start(); 738 } catch (Exception e) { 739 throw new RuntimeCamelException("Error starting JettyServer thread pool: " + qtp, e); 740 } 741 server.setThreadPool(qtp); 742 } 743 744 if (getThreadPool() != null) { 745 server.setThreadPool(getThreadPool()); 746 } 747 748 return server; 749 } 750 751 /** 752 * Starts {@link #mbContainer} and registers the container with itself as a managed bean 753 * logging an error if there is a problem starting the container. 754 * Does nothing if {@link #mbContainer} is {@code null}. 755 */ 756 protected void startMbContainer() { 757 if (mbContainer != null && !mbContainer.isStarted()) { 758 try { 759 mbContainer.start(); 760 // Publish the container itself for consistency with 761 // traditional embedded Jetty configurations. 762 mbContainer.addBean(mbContainer); 763 } catch (Throwable e) { 764 LOG.warn("Could not start Jetty MBeanContainer. Jetty JMX extensions will remain disabled.", e); 765 } 766 } 767 } 768 769 @Override 770 protected void doStart() throws Exception { 771 super.doStart(); 772 if (httpClientThreadPool != null && httpClientThreadPool instanceof LifeCycle) { 773 LifeCycle lc = (LifeCycle) httpClientThreadPool; 774 lc.start(); 775 } 776 if (httpClient != null && !httpClient.isStarted()) { 777 httpClient.start(); 778 } 779 780 startMbContainer(); 781 } 782 783 @Override 784 protected void doStop() throws Exception { 785 super.doStop(); 786 if (CONNECTORS.size() > 0) { 787 for (String connectorKey : CONNECTORS.keySet()) { 788 ConnectorRef connectorRef = CONNECTORS.get(connectorKey); 789 if (connectorRef != null && connectorRef.getRefCount() == 0) { 790 connectorRef.server.removeConnector(connectorRef.connector); 791 connectorRef.connector.stop(); 792 connectorRef.server.stop(); 793 // Camel controls the lifecycle of these entities so remove the 794 // registered MBeans when Camel is done with the managed objects. 795 if (mbContainer != null) { 796 mbContainer.removeBean(connectorRef.server); 797 mbContainer.removeBean(connectorRef.connector); 798 } 799 CONNECTORS.remove(connectorKey); 800 } 801 } 802 } 803 if (httpClient != null) { 804 httpClient.stop(); 805 } 806 if (httpClientThreadPool != null && httpClientThreadPool instanceof LifeCycle) { 807 LifeCycle lc = (LifeCycle) httpClientThreadPool; 808 lc.stop(); 809 } 810 if (mbContainer != null) { 811 mbContainer.stop(); 812 } 813 } 814 }