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    }