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