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.servicemix.http.endpoints;
018
019 import java.io.IOException;
020 import java.io.InputStream;
021 import java.net.InetSocketAddress;
022 import java.security.GeneralSecurityException;
023 import java.security.KeyStore;
024 import java.security.SecureRandom;
025
026 import javax.jbi.messaging.ExchangeStatus;
027 import javax.jbi.messaging.MessageExchange;
028 import javax.jbi.messaging.NormalizedMessage;
029 import javax.jbi.servicedesc.ServiceEndpoint;
030 import javax.jbi.management.DeploymentException;
031 import javax.xml.namespace.QName;
032 import javax.net.ssl.SSLContext;
033 import javax.net.ssl.KeyManager;
034 import javax.net.ssl.KeyManagerFactory;
035 import javax.net.ssl.TrustManager;
036 import javax.net.ssl.TrustManagerFactory;
037
038 import org.apache.servicemix.common.JbiConstants;
039 import org.apache.servicemix.common.DefaultComponent;
040 import org.apache.servicemix.common.ServiceUnit;
041 import org.apache.servicemix.common.security.KeystoreManager;
042 import org.apache.servicemix.common.endpoints.ProviderEndpoint;
043 import org.apache.servicemix.http.HttpComponent;
044 import org.apache.servicemix.http.HttpEndpointType;
045 import org.apache.servicemix.http.HttpConfiguration;
046 import org.apache.servicemix.http.SslParameters;
047 import org.apache.servicemix.http.jetty.SmxHttpExchange;
048 import org.mortbay.jetty.client.HttpClient;
049 import org.mortbay.jetty.client.security.ProxyAuthorization;
050 import org.mortbay.thread.QueuedThreadPool;
051 import org.mortbay.resource.Resource;
052
053 /**
054 * A plain HTTP provider. This type of endpoint can be used to send non-SOAP requests to HTTP endpoints.
055 *
056 * @author gnodet
057 * @since 3.2
058 * @org.apache.xbean.XBean element="provider"
059 */
060 public class HttpProviderEndpoint extends ProviderEndpoint implements HttpEndpointType {
061
062 // private BasicAuthCredentials basicAuthentication;
063 private HttpProviderMarshaler marshaler;
064 private String locationURI;
065 private int clientSoTimeout = 60000;
066 private HttpClient jettyClient;
067 private boolean ownClient = false;
068
069 private String proxyHost;
070 private int proxyPort = 80;
071 private String proxyUsername;
072 private String proxyPassword;
073
074 private SslParameters ssl;
075
076 public HttpProviderEndpoint() {
077 super();
078 }
079
080 public HttpProviderEndpoint(DefaultComponent component, ServiceEndpoint endpoint) {
081 super(component, endpoint);
082 }
083
084 public HttpProviderEndpoint(ServiceUnit serviceUnit, QName service, String endpoint) {
085 super(serviceUnit, service, endpoint);
086 }
087
088 /**
089 * Returns the URI to which the endpoint sends requests.
090 *
091 * @return a string representing the URI to which requests are sent
092 */
093 public String getLocationURI() {
094 return locationURI;
095 }
096
097 /**
098 * Sets the URI to which an endpoint sends requests.
099 *
100 * @param locationURI a string representing the URI
101 * @org.apache.xbean.Property description="the URI to which the endpoint sends requests"
102 */
103 public void setLocationURI(String locationURI) {
104 this.locationURI = locationURI;
105 }
106
107 /**
108 * @return the marshaler
109 */
110 public HttpProviderMarshaler getMarshaler() {
111 return marshaler;
112 }
113
114 /**
115 * Sets the class used to marshal messages.
116 *
117 * @param marshaler the marshaler to set
118 * @org.apache.xbean.Property description="the bean used to marshal HTTP messages. The default is a
119 * <code>DefaultHttpProviderMarshaler</code>."
120 */
121 public void setMarshaler(HttpProviderMarshaler marshaler) {
122 this.marshaler = marshaler;
123 }
124
125 public String getProxyHost() {
126 return proxyHost;
127 }
128
129 /**
130 * Sets the host name of the HTTP proxy used
131 *
132 * @param proxyHost the host name of the HTTP proxy
133 * @org.apache.xbean.Property description="the host name of the HTTP proxy"
134 */
135 public void setProxyHost(String proxyHost) {
136 this.proxyHost = proxyHost;
137 }
138
139 public int getProxyPort() {
140 return proxyPort;
141 }
142
143 /**
144 * Sets the host port of the HTTP proxy used (defaults to 80)
145 *
146 * @param proxyPort the host name of the HTTP proxy
147 * @org.apache.xbean.Property description="the host port of the HTTP proxy (defaults to 80)"
148 */
149 public void setProxyPort(int proxyPort) {
150 this.proxyPort = proxyPort;
151 }
152
153 public String getProxyUsername() {
154 return proxyUsername;
155 }
156
157 /**
158 * Sets the user name for the HTTP proxy authentication
159 *
160 * @param proxyUsername the user name for the HTTP proxy authentication
161 * @org.apache.xbean.Property description="the user name for the HTTP proxy authentication"
162 */
163 public void setProxyUsername(String proxyUsername) {
164 this.proxyUsername = proxyUsername;
165 }
166
167 public String getProxyPassword() {
168 return proxyPassword;
169 }
170
171 /**
172 * Sets the password for the HTTP proxy authentication
173 *
174 * @param proxyPassword the password for the HTTP proxy authentication
175 * @org.apache.xbean.Property description="the password for the HTTP proxy authentication"
176 */
177 public void setProxyPassword(String proxyPassword) {
178 this.proxyPassword = proxyPassword;
179 }
180
181 public SslParameters getSsl() {
182 return ssl;
183 }
184
185 /**
186 * Sets the SSL parameters
187 *
188 * @param ssl the SSL parameters
189 * @org.apache.xbean.Property description="the SSL parameters"
190 */
191 public void setSsl(SslParameters ssl) {
192 this.ssl = ssl;
193 }
194
195 public void process(MessageExchange exchange) throws Exception {
196 if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
197 NormalizedMessage nm = exchange.getMessage("in");
198 if (nm == null) {
199 throw new IllegalStateException("Exchange has no input message");
200 }
201 SmxHttpExchange httpEx = new Exchange(exchange);
202 marshaler.createRequest(exchange, nm, httpEx);
203 getConnectionPool().send(httpEx);
204 }
205 }
206
207
208 public void stop() throws Exception {
209 if (ownClient && jettyClient != null) {
210 jettyClient.stop();
211 jettyClient = null;
212 }
213 super.stop();
214 }
215
216 protected void handle(SmxHttpExchange httpExchange, MessageExchange exchange) throws IOException {
217 try {
218 marshaler.handleResponse(exchange, httpExchange);
219 } catch (Exception e) {
220 exchange.setError(e);
221 }
222 try {
223 boolean txSync = exchange.getStatus() == ExchangeStatus.ACTIVE && exchange.isTransacted()
224 && Boolean.TRUE.equals(exchange.getProperty(JbiConstants.SEND_SYNC));
225 if (txSync) {
226 sendSync(exchange);
227 } else {
228 send(exchange);
229 }
230 } catch (Exception e) {
231 throw (IOException) new IOException(e.getMessage()).initCause(e);
232 }
233 }
234
235 protected void handleException(SmxHttpExchange httpExchange, MessageExchange exchange, Throwable ex) {
236 try {
237 marshaler.handleException(exchange, httpExchange, ex);
238 boolean txSync = exchange.getStatus() == ExchangeStatus.ACTIVE
239 && exchange.isTransacted()
240 && Boolean.TRUE.equals(exchange.getProperty(JbiConstants.SEND_SYNC));
241 if (txSync) {
242 sendSync(exchange);
243 } else {
244 send(exchange);
245 }
246 } catch (Exception e) {
247 logger.warn("Unable to send back exchange in error", e);
248 }
249 }
250
251 protected org.mortbay.jetty.client.HttpClient getConnectionPool() throws Exception {
252 if (jettyClient == null) {
253 HttpComponent comp = (HttpComponent) getServiceUnit().getComponent();
254 if (comp.getConfiguration().isJettyClientPerProvider() || proxyHost != null || ssl != null) {
255 ownClient = true;
256 jettyClient = new SSLManagedHttpClient();
257 jettyClient.setThreadPool(new QueuedThreadPool(getConfiguration().getJettyClientThreadPoolSize()));
258 jettyClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
259 if (proxyHost != null) {
260 jettyClient.setProxy(new InetSocketAddress(proxyHost, proxyPort));
261 if (proxyUsername != null) {
262 jettyClient.setProxyAuthentication(new ProxyAuthorization(proxyUsername, proxyPassword));
263 }
264 }
265 jettyClient.setSoTimeout(getClientSoTimeout());
266 jettyClient.start();
267 } else {
268 ownClient = false;
269 // return shared client
270 jettyClient = comp.getConnectionPool();
271 }
272 }
273 if (!ownClient) {
274 // Always reset the SO timeout, in case the client is shared
275 jettyClient.setSoTimeout(getClientSoTimeout());
276 }
277 return jettyClient;
278 }
279
280 public int getClientSoTimeout() {
281 return clientSoTimeout;
282 }
283
284 /**
285 * Sets the number of milliseconds the endpoint will block while attempting to read a request. The default value is 60000.
286 * Setting this to 0 specifies that the endpoint will never timeout.
287 *
288 * @param clientTimeout an int specifying the number of milliseconds the socket will block while attempting to read a request
289 * @org.apache.xbean.Property description="the number of milliseconds the endpoint will block while attempting to read a request. The default value is 60000. Setting this to 0 specifies that the endpoint will never timeout."
290 */
291 public void setClientSoTimeout(int clientTimeout) {
292 this.clientSoTimeout = clientTimeout;
293 }
294
295 public void validate() throws DeploymentException {
296 super.validate();
297 if (marshaler == null) {
298 marshaler = new DefaultHttpProviderMarshaler();
299 }
300 if (marshaler instanceof DefaultHttpProviderMarshaler && locationURI != null) {
301 ((DefaultHttpProviderMarshaler)marshaler).setLocationURI(locationURI);
302 }
303 }
304
305 private HttpConfiguration getConfiguration() {
306 return ((HttpComponent) getServiceUnit().getComponent()).getConfiguration();
307 }
308
309 protected class Exchange extends SmxHttpExchange {
310 MessageExchange jbiExchange;
311
312 public Exchange(MessageExchange jbiExchange) {
313 this.jbiExchange = jbiExchange;
314 }
315
316 protected void onResponseComplete() throws IOException {
317 handle(this, jbiExchange);
318 }
319
320 protected void onConnectionFailed(Throwable throwable) {
321 handleException(this, jbiExchange, throwable);
322 }
323
324 protected void onException(Throwable throwable) {
325 handleException(this, jbiExchange, throwable);
326 }
327 }
328
329 protected class SSLManagedHttpClient extends HttpClient {
330
331 protected SSLContext getSSLContext() throws IOException {
332 if (ssl.getKeyStore() != null) {
333 return getStrictSSLContext();
334 } else {
335 return getLooseSSLContext();
336 }
337 }
338
339 protected SSLContext getStrictSSLContext() throws IOException {
340 try {
341 if (ssl.isManaged()) {
342 KeystoreManager keystoreMgr = KeystoreManager.Proxy.create(getConfiguration().getKeystoreManager());
343 return keystoreMgr.createSSLContext(ssl.getProvider(), ssl.getProtocol(),
344 ssl.getKeyManagerFactoryAlgorithm(), ssl.getKeyStore(),
345 ssl.getKeyAlias(), ssl.getTrustStore());
346 } else {
347 if (ssl.getTrustStore() == null) {
348 ssl.setTrustStore(ssl.getKeyStore());
349 ssl.setTrustStoreType(ssl.getKeyStoreType());
350 ssl.setTrustManagerFactoryAlgorithm(ssl.getKeyManagerFactoryAlgorithm());
351 }
352
353 KeyManager[] keyManagers;
354 TrustManager[] trustManagers;
355
356 InputStream keystoreInputStream = Resource.newResource(ssl.getKeyStore()).getInputStream();
357 KeyStore keyStore = KeyStore.getInstance(ssl.getKeyStoreType());
358 keyStore.load(keystoreInputStream, ssl.getKeyStorePassword() == null ? null : ssl.getKeyStorePassword().toString().toCharArray());
359
360 KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(ssl.getKeyManagerFactoryAlgorithm());
361 keyManagerFactory.init(keyStore, ssl.getKeyPassword() == null ? null : ssl.getKeyPassword().toString().toCharArray());
362 keyManagers = keyManagerFactory.getKeyManagers();
363
364 InputStream truststoreInputStream = Resource.newResource(ssl.getTrustStore()).getInputStream();
365 KeyStore trustStore = KeyStore.getInstance(ssl.getTrustStoreType());
366 trustStore.load(truststoreInputStream, ssl.getTrustStorePassword() == null ? null : ssl.getTrustStorePassword().toString().toCharArray());
367
368 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(ssl.getTrustManagerFactoryAlgorithm());
369 trustManagerFactory.init(trustStore);
370 trustManagers = trustManagerFactory.getTrustManagers();
371
372 SSLContext context = ssl.getProvider() == null
373 ? SSLContext.getInstance(ssl.getProtocol())
374 : SSLContext.getInstance(ssl.getProtocol(), ssl.getProvider());
375 context.init(keyManagers, trustManagers, new SecureRandom());
376 return context;
377 }
378 } catch (GeneralSecurityException e) {
379 throw (IOException) new IOException("Unable to create SSL context").initCause(e);
380 }
381 }
382
383 }
384
385 }