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.io.IOException;
020 import java.io.InputStream;
021 import java.net.URI;
022 import java.net.URISyntaxException;
023 import java.util.Map;
024
025 import org.apache.camel.AsyncCallback;
026 import org.apache.camel.AsyncProcessor;
027 import org.apache.camel.CamelExchangeException;
028 import org.apache.camel.Endpoint;
029 import org.apache.camel.Exchange;
030 import org.apache.camel.ExchangeTimedOutException;
031 import org.apache.camel.Message;
032 import org.apache.camel.component.http.HttpMethods;
033 import org.apache.camel.component.http.helper.HttpProducerHelper;
034 import org.apache.camel.impl.DefaultProducer;
035 import org.apache.camel.spi.HeaderFilterStrategy;
036 import org.apache.camel.util.ExchangeHelper;
037 import org.apache.camel.util.ObjectHelper;
038 import org.apache.camel.util.URISupport;
039 import org.apache.commons.logging.Log;
040 import org.apache.commons.logging.LogFactory;
041 import org.mortbay.io.ByteArrayBuffer;
042 import org.mortbay.jetty.client.HttpClient;
043 import org.mortbay.jetty.client.HttpExchange;
044
045 /**
046 * @version $Revision: 886797 $
047 */
048 public class JettyHttpProducer extends DefaultProducer implements AsyncProcessor {
049 private static final transient Log LOG = LogFactory.getLog(JettyHttpProducer.class);
050 private final HttpClient client;
051 private JettyHttpBinding binding;
052
053 public JettyHttpProducer(Endpoint endpoint, HttpClient client) {
054 super(endpoint);
055 this.client = client;
056 }
057
058 @Override
059 public JettyHttpEndpoint getEndpoint() {
060 return (JettyHttpEndpoint) super.getEndpoint();
061 }
062
063 public void process(Exchange exchange) throws Exception {
064 HttpClient client = getEndpoint().getClient();
065
066 JettyContentExchange httpExchange = createHttpExchange(exchange);
067 sendSynchronous(exchange, client, httpExchange);
068 }
069
070 public void process(Exchange exchange, final AsyncCallback callback) throws Exception {
071 HttpClient client = getEndpoint().getClient();
072
073 JettyContentExchange httpExchange = createHttpExchange(exchange);
074 sendAsynchronous(exchange, client, httpExchange, callback);
075 }
076
077 protected void sendAsynchronous(final Exchange exchange, final HttpClient client, final JettyContentExchange httpExchange,
078 final AsyncCallback callback) throws IOException {
079
080 // set the callback for the async mode
081 httpExchange.setCallback(callback);
082
083 doSendExchange(client, httpExchange);
084
085 // the callback will handle all the response handling logic
086 }
087
088 protected void sendSynchronous(Exchange exchange, HttpClient client, JettyContentExchange httpExchange) throws Exception {
089 doSendExchange(client, httpExchange);
090
091 if (LOG.isTraceEnabled()) {
092 LOG.trace("Waiting for HTTP exchange to be done");
093 }
094 // we send synchronous so wait for it to be done
095 // must use our own lock detection as Jettys waitForDone will wait forever in case of connection issues
096 int exchangeState = httpExchange.waitForDoneOrFailure();
097 if (LOG.isTraceEnabled()) {
098 LOG.trace("HTTP exchange is done with state " + exchangeState);
099 }
100
101 if (exchangeState == HttpExchange.STATUS_COMPLETED) {
102 // process the response as the state is ok
103 getBinding().populateResponse(exchange, httpExchange);
104 } else if (exchangeState == HttpExchange.STATUS_EXPIRED) {
105 // we did timeout
106 throw new ExchangeTimedOutException(exchange, client.getTimeout());
107 } else {
108 // some kind of other error
109 if (exchange.getException() != null) {
110 throw exchange.getException();
111 } else {
112 exchange.setException(new CamelExchangeException("JettyClient failed with state " + exchangeState, exchange));
113 }
114 }
115 }
116
117 protected JettyContentExchange createHttpExchange(Exchange exchange) throws Exception {
118 String url = HttpProducerHelper.createURL(exchange, getEndpoint());
119 HttpMethods methodToUse = HttpProducerHelper.createMethod(exchange, getEndpoint(), exchange.getIn().getBody() != null);
120 String method = methodToUse.createMethod(url).getName();
121
122 JettyContentExchange httpExchange = new JettyContentExchange(exchange, getBinding(), client);
123 httpExchange.setMethod(method);
124 httpExchange.setURL(url);
125
126 // set query parameters
127 doSetQueryParameters(exchange, httpExchange);
128
129 // if we post then set data
130 if (HttpMethods.POST.equals(methodToUse)) {
131
132 String contentType = ExchangeHelper.getContentType(exchange);
133 if (contentType != null) {
134 httpExchange.setRequestContentType(contentType);
135 }
136
137 // try with String at first
138 String data = exchange.getIn().getBody(String.class);
139 if (data != null) {
140 String charset = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
141 if (charset != null) {
142 httpExchange.setRequestContent(new ByteArrayBuffer(data, charset));
143 } else {
144 httpExchange.setRequestContent(new ByteArrayBuffer(data));
145 }
146 } else {
147 // then fallback to input stream
148 InputStream is = exchange.getContext().getTypeConverter().mandatoryConvertTo(InputStream.class, exchange, exchange.getIn().getBody());
149 httpExchange.setRequestContentSource(is);
150 }
151 }
152
153 // and copy headers from IN message
154 Message in = exchange.getIn();
155 HeaderFilterStrategy strategy = getEndpoint().getHeaderFilterStrategy();
156
157 // propagate headers as HTTP headers
158 for (String headerName : in.getHeaders().keySet()) {
159 String headerValue = in.getHeader(headerName, String.class);
160 if (strategy != null && !strategy.applyFilterToCamelHeaders(headerName, headerValue, exchange)) {
161 httpExchange.addRequestHeader(headerName, headerValue);
162 }
163 }
164
165 return httpExchange;
166 }
167
168 @SuppressWarnings("unchecked")
169 private void doSetQueryParameters(Exchange exchange, JettyContentExchange httpExchange) throws URISyntaxException {
170 // is a query string provided in the endpoint URI or in a header (header
171 // overrules endpoint)
172 String queryString = exchange.getIn().getHeader(Exchange.HTTP_QUERY, String.class);
173 if (queryString == null) {
174 queryString = getEndpoint().getHttpUri().getQuery();
175 }
176
177 if (ObjectHelper.isEmpty(queryString)) {
178 return;
179 }
180
181 // okay we need to add the query string to the URI so we need to juggle a bit with the parameters
182 String uri = httpExchange.getURI();
183
184 Map parameters = URISupport.parseParameters(new URI(uri));
185 parameters.putAll(URISupport.parseQuery(queryString));
186
187 if (uri.contains("?")) {
188 uri = ObjectHelper.before(uri, "?");
189 }
190 if (!parameters.isEmpty()) {
191 uri = uri + "?" + URISupport.createQueryString(parameters);
192 httpExchange.setURI(uri);
193 }
194 }
195
196 protected static void doSendExchange(HttpClient client, JettyContentExchange httpExchange) throws IOException {
197 if (LOG.isDebugEnabled()) {
198 LOG.debug("Sending HTTP request to: " + httpExchange.getUrl());
199 }
200 client.send(httpExchange);
201 }
202
203 public JettyHttpBinding getBinding() {
204 return binding;
205 }
206
207 public void setBinding(JettyHttpBinding binding) {
208 this.binding = binding;
209 }
210
211 @Override
212 protected void doStart() throws Exception {
213 super.doStart();
214 client.start();
215 }
216
217 @Override
218 protected void doStop() throws Exception {
219 super.doStop();
220 client.stop();
221 }
222
223 }