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 }