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