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.io.UnsupportedEncodingException;
022 import java.util.LinkedHashMap;
023 import java.util.Map;
024 import java.util.concurrent.CountDownLatch;
025
026 import org.apache.camel.AsyncCallback;
027 import org.apache.camel.CamelExchangeException;
028 import org.apache.camel.Exchange;
029 import org.apache.camel.ExchangeTimedOutException;
030 import org.apache.camel.util.ObjectHelper;
031 import org.apache.commons.logging.Log;
032 import org.apache.commons.logging.LogFactory;
033 import org.mortbay.io.Buffer;
034 import org.mortbay.jetty.HttpHeaders;
035 import org.mortbay.jetty.client.ContentExchange;
036 import org.mortbay.jetty.client.HttpClient;
037 import org.mortbay.jetty.client.HttpExchange;
038
039 /**
040 * Jetty specific exchange which keeps track of the the request and response.
041 *
042 * @version $Revision: 885380 $
043 */
044 public class JettyContentExchange extends ContentExchange {
045
046 private static final transient Log LOG = LogFactory.getLog(JettyContentExchange.class);
047
048 private final Map<String, String> headers = new LinkedHashMap<String, String>();
049 private volatile Exchange exchange;
050 private volatile AsyncCallback callback;
051 private volatile JettyHttpBinding jettyBinding;
052 private volatile HttpClient client;
053 private final CountDownLatch done = new CountDownLatch(1);
054
055 public JettyContentExchange(Exchange exchange, JettyHttpBinding jettyBinding, HttpClient client) {
056 super(true); // keep headers by default
057 this.exchange = exchange;
058 this.jettyBinding = jettyBinding;
059 this.client = client;
060 }
061
062 public void setCallback(AsyncCallback callback) {
063 this.callback = callback;
064 }
065
066 @Override
067 protected void onResponseHeader(Buffer name, Buffer value) throws IOException {
068 super.onResponseHeader(name, value);
069 headers.put(name.toString(), value.toString());
070 }
071
072 @Override
073 protected void onRequestComplete() throws IOException {
074 // close the input stream when its not needed anymore
075 InputStream is = getRequestContentSource();
076 if (is != null) {
077 ObjectHelper.close(is, "RequestContentSource", LOG);
078 }
079 }
080
081 @Override
082 protected void onResponseComplete() throws IOException {
083 try {
084 super.onResponseComplete();
085 } finally {
086 doTaskCompleted();
087 }
088 }
089
090 @Override
091 protected void onExpire() {
092 try {
093 super.onExpire();
094 } finally {
095 doTaskCompleted();
096 }
097 }
098
099 @Override
100 protected void onException(Throwable ex) {
101 try {
102 super.onException(ex);
103 } finally {
104 doTaskCompleted(ex);
105 }
106 }
107
108 @Override
109 protected void onConnectionFailed(Throwable ex) {
110 try {
111 super.onConnectionFailed(ex);
112 } finally {
113 doTaskCompleted(ex);
114 }
115 }
116
117 protected int waitForDoneOrFailure() throws InterruptedException {
118 done.await();
119 return getStatus();
120 }
121
122 public Map<String, String> getHeaders() {
123 return headers;
124 }
125
126 public String getBody() throws UnsupportedEncodingException {
127 return super.getResponseContent();
128 }
129
130 public String getUrl() {
131 String params = getRequestFields().getStringField(HttpHeaders.CONTENT_ENCODING);
132 return getScheme() + "//" + getAddress().toString() + getURI() + (params != null ? "?" + params : "");
133 }
134
135 protected void doTaskCompleted() {
136 // make sure to lower the latch
137 done.countDown();
138
139 if (callback == null) {
140 // this is only for the async callback
141 return;
142 }
143
144 int exchangeState = getStatus();
145
146 if (LOG.isDebugEnabled()) {
147 LOG.debug("TaskComplete with state " + exchangeState + " for url: " + getUrl());
148 }
149
150 try {
151 if (exchangeState == HttpExchange.STATUS_COMPLETED) {
152 // process the response as the state is ok
153 try {
154 jettyBinding.populateResponse(exchange, this);
155 } catch (Exception e) {
156 exchange.setException(e);
157 }
158 } else if (exchangeState == HttpExchange.STATUS_EXPIRED) {
159 // we did timeout
160 exchange.setException(new ExchangeTimedOutException(exchange, client.getTimeout()));
161 } else if (exchangeState == HttpExchange.STATUS_EXCEPTED) {
162 // some kind of other error
163 exchange.setException(new CamelExchangeException("JettyClient failed with state " + exchangeState, exchange));
164 }
165 } finally {
166 // now invoke callback
167 callback.onTaskCompleted(exchange);
168 }
169 }
170
171 protected void doTaskCompleted(Throwable ex) {
172 // make sure to lower the latch
173 done.countDown();
174
175 // some kind of other error
176 exchange.setException(new CamelExchangeException("JettyClient failed cause by: " + ex.getMessage(), exchange, ex));
177
178 if (callback != null) {
179 callback.onTaskCompleted(exchange);
180 }
181 }
182
183 }