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 }