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