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