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.net.URI;
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    import org.apache.camel.Endpoint;
024    import org.apache.camel.component.http.CamelServlet;
025    import org.apache.camel.component.http.HttpComponent;
026    import org.apache.camel.component.http.HttpConsumer;
027    import org.apache.camel.component.http.HttpEndpoint;
028    import org.apache.camel.component.http.HttpExchange;
029    import org.apache.camel.util.IntrospectionSupport;
030    import org.apache.camel.util.ObjectHelper;
031    import org.apache.camel.util.URISupport;
032    import org.apache.commons.httpclient.params.HttpClientParams;
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    import org.mortbay.jetty.Connector;
036    import org.mortbay.jetty.Handler;
037    import org.mortbay.jetty.Server;
038    import org.mortbay.jetty.handler.ContextHandlerCollection;
039    import org.mortbay.jetty.nio.SelectChannelConnector;
040    import org.mortbay.jetty.security.SslSocketConnector;
041    import org.mortbay.jetty.servlet.Context;
042    import org.mortbay.jetty.servlet.ServletHolder;
043    import org.mortbay.jetty.servlet.SessionHandler;
044    
045    /**
046     * An HttpComponent which starts an embedded Jetty for to handle consuming from
047     * the http endpoints.
048     *
049     * @version $Revision: 772883 $
050     */
051    public class JettyHttpComponent extends HttpComponent {
052    
053        class ConnectorRef {
054            Connector connector;
055            CamelServlet servlet;
056            int refCount;
057    
058            public ConnectorRef(Connector connector, CamelServlet servlet) {
059                this.connector = connector;
060                this.servlet = servlet;
061                increment();
062            }
063    
064            public int increment() {
065                return ++refCount;
066            }
067    
068            public int decrement() {
069                return --refCount;
070            }
071        }
072        
073        private static final transient Log LOG = LogFactory.getLog(JettyHttpComponent.class);
074        
075        protected Server server;
076        protected final HashMap<String, ConnectorRef> connectors = new HashMap<String, ConnectorRef>();
077        protected String sslKeyPassword;
078        protected String sslPassword;
079        protected String sslKeystore;
080        protected SslSocketConnector sslSocketConnector;
081    
082        @Override
083        protected Endpoint<HttpExchange> createEndpoint(String uri, String remaining, Map parameters) throws Exception {
084            uri = uri.startsWith("jetty:") ? remaining : uri;
085    
086            HttpClientParams params = new HttpClientParams();
087            IntrospectionSupport.setProperties(params, parameters, "httpClient.");   
088    
089            configureParameters(parameters);
090    
091            // restructure uri to be based on the parameters left as we dont want to include the Camel internal options
092            URI httpUri = URISupport.createRemainingURI(new URI(uri), parameters);
093            uri = httpUri.toString();
094    
095            JettyHttpEndpoint result = new JettyHttpEndpoint(this, uri, httpUri, params, getHttpConnectionManager(), httpClientConfigurer);
096            if (httpBinding != null) {
097                result.setBinding(httpBinding);
098            }
099            setProperties(result, parameters);
100            return result;
101        }
102    
103        /**
104         * Connects the URL specified on the endpoint to the specified processor.
105         *
106         * @throws Exception
107         */
108        @Override
109        public void connect(HttpConsumer consumer) throws Exception {
110            // Make sure that there is a connector for the requested endpoint.
111            JettyHttpEndpoint endpoint = (JettyHttpEndpoint)consumer.getEndpoint();
112            String connectorKey = getConnectorKey(endpoint);
113    
114            synchronized (connectors) {
115                ConnectorRef connectorRef = connectors.get(connectorKey);
116                if (connectorRef == null) {
117                    Connector connector;
118                    if ("https".equals(endpoint.getProtocol())) {
119                        connector = getSslSocketConnector();
120                    } else {
121                        connector = new SelectChannelConnector();
122                    }
123                    connector.setPort(endpoint.getPort());
124                    connector.setHost(endpoint.getHttpUri().getHost());
125                    if ("localhost".equalsIgnoreCase(endpoint.getHttpUri().getHost())) {
126                        LOG.warn("You use localhost interface! It means that no external connections will be available. Don't you want to use 0.0.0.0 instead (all network interfaces)?");
127                    }
128                    getServer().addConnector(connector);
129    
130                    connectorRef = new ConnectorRef(connector, createServletForConnector(connector, endpoint.getHandlers()));
131                    connector.start();
132                    
133                    connectors.put(connectorKey, connectorRef);
134                    
135                } else {
136                    // ref track the connector
137                    connectorRef.increment();
138                }
139                // check the session support
140                if (endpoint.isSessionSupport()) {
141                    enableSessionSupport();
142                }
143                connectorRef.servlet.connect(consumer);
144            }
145        }
146    
147        private void enableSessionSupport() throws Exception {
148            Context context = (Context)getServer().getChildHandlerByClass(Context.class);
149            if (context.getSessionHandler() == null) {
150                SessionHandler sessionHandler = new SessionHandler();
151                context.setSessionHandler(sessionHandler);
152                if (context.isStarted()) {
153                    // restart the context
154                    context.stop();
155                    context.start();
156                }
157            }
158    
159        }
160    
161        /**
162         * Disconnects the URL specified on the endpoint from the specified
163         * processor.
164         */
165        @Override
166        public void disconnect(HttpConsumer consumer) throws Exception {
167            // If the connector is not needed anymore then stop it
168            HttpEndpoint endpoint = consumer.getEndpoint();
169            String connectorKey = getConnectorKey(endpoint);
170            
171            synchronized (connectors) {
172                ConnectorRef connectorRef = connectors.get(connectorKey);
173                if (connectorRef != null) {
174                    connectorRef.servlet.disconnect(consumer);
175                    if (connectorRef.decrement() == 0) {
176                        getServer().removeConnector(connectorRef.connector);
177                        connectorRef.connector.stop();
178                        connectors.remove(connectorKey);
179                    }
180                }
181            }
182        }
183        
184        private String getConnectorKey(HttpEndpoint endpoint) {
185            return endpoint.getProtocol() + ":" + endpoint.getHttpUri().getHost() + ":" + endpoint.getPort();
186        }
187    
188        // Properties
189        // -------------------------------------------------------------------------
190    
191        public Server getServer() throws Exception {
192            if (server == null) {
193                server = createServer();
194            }
195            return server;
196        }
197    
198        public void setServer(Server server) {
199            this.server = server;
200        }
201    
202        public String getSslKeyPassword() {
203            return sslKeyPassword;
204        }
205    
206        public void setSslKeyPassword(String sslKeyPassword) {
207            this.sslKeyPassword = sslKeyPassword;
208        }
209    
210        public String getSslPassword() {
211            return sslPassword;
212        }
213    
214        public void setSslPassword(String sslPassword) {
215            this.sslPassword = sslPassword;
216        }
217    
218        public void setKeystore(String sslKeystore) {
219            this.sslKeystore = sslKeystore;
220        }
221    
222        public String getKeystore() {
223            return sslKeystore;
224        }
225    
226        public synchronized SslSocketConnector getSslSocketConnector() {
227            if (sslSocketConnector == null) {
228                sslSocketConnector = new SslSocketConnector();
229                // with default null values, jetty ssl system properties
230                // and console will be read by jetty implementation
231                sslSocketConnector.setPassword(sslPassword);
232                sslSocketConnector.setKeyPassword(sslKeyPassword);
233                if (sslKeystore != null) {
234                    sslSocketConnector.setKeystore(sslKeystore);
235                }
236            }
237            return sslSocketConnector;
238        }
239    
240        public void setSslSocketConnector(SslSocketConnector connector) {
241            sslSocketConnector = connector;
242        }
243    
244        protected CamelServlet createServletForConnector(Connector connector, String handlerNames) throws Exception {
245            CamelServlet camelServlet = new CamelContinuationServlet();
246            
247            Context context = new Context(server, "/", Context.NO_SECURITY | Context.NO_SESSIONS);
248            context.setConnectorNames(new String[] {connector.getName()});
249    
250            if (handlerNames != null) {
251                String[] handlerNameArray = handlerNames.split(",");
252                for (String handlerName : handlerNameArray) {
253                    Handler handler = getHandler(handlerName);
254                    context.addHandler(handler);
255                }
256            }
257            
258            ServletHolder holder = new ServletHolder();
259            holder.setServlet(camelServlet);
260            context.addServlet(holder, "/*");
261            connector.start();
262            context.start();
263            
264    
265            return camelServlet;
266        }
267        
268        // Implementation methods
269        // -------------------------------------------------------------------------
270    
271        protected Server createServer() throws Exception {
272            Server server = new Server();
273            ContextHandlerCollection collection = new ContextHandlerCollection();
274            collection.setServer(server);
275            server.addHandler(collection);
276            server.start();
277    
278            return server;
279        }
280    
281        @Override
282        protected void doStop() throws Exception {
283            for (ConnectorRef connectorRef : connectors.values()) {
284                connectorRef.connector.stop();            
285            }
286            connectors.clear();
287    
288            if (server != null) {
289                server.stop();
290            }
291            
292            super.doStop();
293        }
294       
295        private Handler getHandler(String handlerName) {
296            Handler handler = null;
297            if (handlerName != null) {
298                handler = getCamelContext().getRegistry().lookup(handlerName, Handler.class);
299                ObjectHelper.notNull(handler, handlerName);
300                if (LOG.isDebugEnabled()) {
301                    LOG.debug("Using context handler: " + handlerName);
302                }
303            }
304            return handler;
305        }
306    }