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: 885380 $
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 if (exchangeState == HttpExchange.STATUS_EXCEPTED) {
108 // some kind of other error
109 throw new CamelExchangeException("JettyClient failed with state " + exchangeState, exchange);
110 }
111 }
112
113 protected JettyContentExchange createHttpExchange(Exchange exchange) throws Exception {
114 String url = HttpProducerHelper.createURL(exchange, getEndpoint());
115 HttpMethods methodToUse = HttpProducerHelper.createMethod(exchange, getEndpoint(), exchange.getIn().getBody() != null);
116 String method = methodToUse.createMethod(url).getName();
117
118 JettyContentExchange httpExchange = new JettyContentExchange(exchange, getBinding(), client);
119 httpExchange.setMethod(method);
120 httpExchange.setURL(url);
121
122 // set query parameters
123 doSetQueryParameters(exchange, httpExchange);
124
125 // if we post then set data
126 if (HttpMethods.POST.equals(methodToUse)) {
127
128 String contentType = ExchangeHelper.getContentType(exchange);
129 if (contentType != null) {
130 httpExchange.setRequestContentType(contentType);
131 }
132
133 // try with String at first
134 String data = exchange.getIn().getBody(String.class);
135 if (data != null) {
136 String charset = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
137 if (charset != null) {
138 httpExchange.setRequestContent(new ByteArrayBuffer(data, charset));
139 } else {
140 httpExchange.setRequestContent(new ByteArrayBuffer(data));
141 }
142 } else {
143 // then fallback to input stream
144 InputStream is = exchange.getContext().getTypeConverter().mandatoryConvertTo(InputStream.class, exchange, exchange.getIn().getBody());
145 httpExchange.setRequestContentSource(is);
146 }
147 }
148
149 // and copy headers from IN message
150 Message in = exchange.getIn();
151 HeaderFilterStrategy strategy = getEndpoint().getHeaderFilterStrategy();
152
153 // propagate headers as HTTP headers
154 for (String headerName : in.getHeaders().keySet()) {
155 String headerValue = in.getHeader(headerName, String.class);
156 if (strategy != null && !strategy.applyFilterToCamelHeaders(headerName, headerValue, exchange)) {
157 httpExchange.addRequestHeader(headerName, headerValue);
158 }
159 }
160
161 return httpExchange;
162 }
163
164 @SuppressWarnings("unchecked")
165 private void doSetQueryParameters(Exchange exchange, JettyContentExchange httpExchange) throws URISyntaxException {
166 // is a query string provided in the endpoint URI or in a header (header
167 // overrules endpoint)
168 String queryString = exchange.getIn().getHeader(Exchange.HTTP_QUERY, String.class);
169 if (queryString == null) {
170 queryString = getEndpoint().getHttpUri().getQuery();
171 }
172
173 if (ObjectHelper.isEmpty(queryString)) {
174 return;
175 }
176
177 // okay we need to add the query string to the URI so we need to juggle a bit with the parameters
178 String uri = httpExchange.getURI();
179
180 Map parameters = URISupport.parseParameters(new URI(uri));
181 parameters.putAll(URISupport.parseQuery(queryString));
182
183 if (uri.contains("?")) {
184 uri = ObjectHelper.before(uri, "?");
185 }
186 if (!parameters.isEmpty()) {
187 uri = uri + "?" + URISupport.createQueryString(parameters);
188 httpExchange.setURI(uri);
189 }
190 }
191
192 protected static void doSendExchange(HttpClient client, JettyContentExchange httpExchange) throws IOException {
193 if (LOG.isDebugEnabled()) {
194 LOG.debug("Sending HTTP request to: " + httpExchange.getUrl());
195 }
196 client.send(httpExchange);
197 }
198
199 public JettyHttpBinding getBinding() {
200 return binding;
201 }
202
203 public void setBinding(JettyHttpBinding binding) {
204 this.binding = binding;
205 }
206
207 @Override
208 protected void doStart() throws Exception {
209 super.doStart();
210 client.start();
211 }
212
213 @Override
214 protected void doStop() throws Exception {
215 super.doStop();
216 client.stop();
217 }
218
219 }