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 }