001    /*
002     * Copyright 1999-2001,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.apache.commons.latka.jelly;
018    
019    import java.io.IOException;
020    import java.net.URL;
021    
022    import org.apache.commons.jelly.JellyTagException;
023    import org.apache.commons.jelly.TagSupport;
024    import org.apache.commons.jelly.XMLOutput;
025    
026    import org.apache.commons.latka.LatkaException;
027    import org.apache.commons.latka.event.LatkaEventInfo;
028    import org.apache.commons.latka.event.RequestErrorEvent;
029    import org.apache.commons.latka.event.RequestSkippedEvent;
030    import org.apache.commons.latka.event.RequestSucceededEvent;
031    import org.apache.commons.latka.http.Proxy;
032    import org.apache.commons.latka.http.Request;
033    import org.apache.commons.latka.http.Response;
034    import org.apache.commons.latka.http.Session;
035    import org.apache.commons.latka.http.SessionImpl;
036    
037    import org.apache.log4j.Category;
038    
039    /**
040     *
041     * @author  Morgan Delagrange
042     */
043    public class RequestTag extends TagSupport {
044    
045        protected String  _host = null;
046        protected int     _port = -1;
047        protected String  _proxyHost = null;
048        protected int     _proxyPort = -1;
049        protected String  _label = null;
050        protected int  _method = Request.HTTP_METHOD_GET;
051        protected String  _path = null;
052        protected boolean _secure = false;
053        protected boolean _followRedirects = true;
054        protected String  _httpVersion = "1.1";
055        
056        protected Request  _request  = null;
057        protected Response _response = null;
058        protected Session  _session  = null;
059        protected boolean  _requestExecuted = false;
060    
061        protected static final Category _log = Category.getInstance(RequestTag.class);
062    
063        /**
064         *  Wraps Latka tests, provides some defaults for host, port etc.
065         *
066         * @param xmlOutput a place to write output
067         * @throws JellyTagException if an HTTP request could not be created
068         */
069        public void doTag(XMLOutput xmlOutput) throws JellyTagException {
070            try {
071                _request = createRequest();
072            } catch (LatkaException e) {
073                throw new JellyTagException("could not create HTTP request",e);
074            }
075    
076            LatkaEventInfo listener =
077                JellyUtils.getInstance().getLatkaEventInfo(getContext());
078            if (listener.didSessionSucceed(findSession()) == false) {
079                listener.requestSkipped(new RequestSkippedEvent(_request,null));
080                return;
081            }
082    
083            // may set headers and such
084            invokeBody(xmlOutput);
085    
086            // even when there are no validations, we execute the request
087            // to make sure the URL is accessible
088            
089            // will throw an unrecoverable LatkaException if the request could not
090            // be created, typically because of a malformed URL
091            Response response = null;
092            try {
093                response = getResponse();
094            } catch (LatkaException e) {
095                throw new JellyTagException("could not obtain HTTP response",e);
096            }
097            
098            // if there's been a response, and the request has a label
099            // make the response available to the jelly context
100            if (response != null && _label != null) {
101                getContext().setVariable(_label, response);
102            }
103    
104            if (listener.didRequestSucceed(_request)) {
105                listener.requestSucceeded(new RequestSucceededEvent(
106                  response.getRequest(), response));            
107            }
108    
109        }
110    
111        /**
112         * 
113         * @return Request
114         * @exception IOException
115         *                   Error creating the request (unrecoverable, the script
116         *                   must fail)
117         */
118        private Request createRequest() throws LatkaException {
119            String host = _host;
120            int port    = _port;
121            String proxyHost = _proxyHost;
122            int proxyPort    = _proxyPort;
123    
124            if (host == null || port == -1 || proxyHost == null || proxyPort == -1) {
125                SuiteSettings settings = getSuiteSettings();
126                if (host == null) {
127                    host = settings.getDefaultHost();
128                }
129                if (port == -1) {
130                    port = settings.getDefaultPort();
131                }
132                if (proxyHost == null) {
133                    proxyHost = settings.getDefaultProxyHost();
134                }
135                if (proxyPort == -1) {
136                    proxyPort = settings.getDefaultProxyPort();
137                }
138            }
139    
140            Session session = findSession();
141    
142            Proxy proxy = null;
143            if (proxyHost != null) {
144                proxy = new Proxy(proxyHost,proxyPort);
145            }
146            
147            URL url = null;
148            try {
149                url = new URL(_secure ? "https" : "http", host, port, _path);
150            } catch (IOException e) {
151                throw new LatkaException(e);
152            }
153            return session.createRequest(_label,url,_method,_httpVersion,_followRedirects,proxy);
154        }
155    
156        public Request getRequest() {
157            return _request;
158        }
159    
160        protected Session findSession() {
161            if (_session == null) {
162                SessionTag tag = (SessionTag) findAncestorWithClass(SessionTag.class);
163                if (tag == null) {
164                    _session = new SessionImpl();
165                } else {
166                    _session = tag.getSession();
167                }
168            }
169    
170            return _session;
171        }
172    
173        public boolean getRequestExecuted() {
174            return _requestExecuted;
175        }
176    
177        /**
178         * The first time this method is called, a live HTTP
179         * call will be made to the server, returning null
180         * if the response cannot be obtained.  Subsequent
181         * calls return a cached response.
182         * 
183         * @return Response for the request specified by the Latka script
184         * @exception LatkaException
185         *                   error creating the Request (unrecoverable)
186         */
187        public Response getResponse() throws LatkaException {
188            if (_requestExecuted == false) {
189                _requestExecuted = true;
190    
191                LatkaEventInfo listener = 
192                    JellyUtils.getInstance().getLatkaEventInfo(getContext());
193                try {
194                    _response = _request.execute();
195                    
196                    _log.warn("Eventually this debug needs to go.");
197                    if (_log.isDebugEnabled()) {
198                      _log.debug(_response.getResource());
199                    }            
200                } catch (IOException e) {
201                    listener.requestError(new RequestErrorEvent(_request, null, e));
202                    return null;
203                }
204    
205                // hack because sometimes we need to generate a new request after
206                // a redirect
207                _request = _response.getRequest();
208            }
209    
210            return _response;
211        }
212    
213        /**
214         * get the suite settings from SuiteTag
215         * 
216         * @return SuiteSettings object
217         */
218        protected SuiteSettings getSuiteSettings() {
219            SuiteTag tag = 
220            (SuiteTag) findAncestorWithClass(org.apache.commons.latka.jelly.SuiteTag.class);
221            return tag.getSuiteSettings();
222        }
223    
224        /**
225         * Setter for host
226         * 
227         * @param host
228         *               host for the request
229         */
230        public void setHost(String host) {
231            _host = host;
232        }
233    
234        /**
235         * Setter for port
236         * 
237         * @param port
238         *               port for all requests 
239         */
240        public void setPort(int port) {
241            _port = port;
242        }
243    
244    
245        /**
246         * Setter for defaultProxyHost
247         * 
248         * @param defaultHost
249         *               defaultProxyHost for all requests
250         */
251        public void setProxyHost(String host) {
252            _proxyHost = host;
253        }
254    
255        /**
256         * Setter for defaultProxyPort
257         * 
258         * @param defaultPort
259         *               defaultProxyPort for all requests
260         * @return 
261         */
262        public void setProxyPort(int port) {
263            _proxyPort = port;
264        }
265    
266        /**
267         * Set the label for this suite
268         * 
269         * @param label  suite label
270         */
271        public void setLabel(String label) {
272            _label = label;
273        }
274    
275        /**
276         * Sets the HTTP method to use.  Supports post, get,
277         * and head.  Default is "get".
278         * 
279         * @param method set method to post, get or head
280         * @exception UnsupportedOperationException
281         *                   if an unsupported HTTP method is set
282         */
283        public void setMethod(String method) throws UnsupportedOperationException {
284            if (method.equals("get")) {
285                _method = Request.HTTP_METHOD_GET;
286            } else if (method.equals("post")) {
287                _method = Request.HTTP_METHOD_POST;
288            } else if (method.equals("head")) {
289                _method = Request.HTTP_METHOD_HEAD;
290            } else {
291                throw new UnsupportedOperationException("Unkonwn HTTP method: " + method);
292            }
293        }
294    
295        /**
296         * Sets the path of the document on the server, combined
297         * with the host and port
298         * 
299         * @param path   Path to the document on the server
300         */
301        public void setPath(String path) {
302            _path = path;
303        }
304    
305        /**
306         * Sets whether or not to transmit the request over
307         * SSL.
308         * 
309         * @param secure whether or not this request is SSL
310         */
311        public void setSecure(String secure) {
312            _secure = Boolean.valueOf(secure).booleanValue();
313        }
314    
315    
316        /**
317         * Sets whether or not to transmit the request over
318         * SSL.
319         * 
320         * @param secure whether or not this request is SSL
321         */
322        public void setFollowRedirects(String followRedirects) {
323            _followRedirects = Boolean.valueOf(followRedirects).booleanValue();
324        }
325    
326        /**
327         * HTTP version to use.  Legal values are 1.0 and 1.1.
328         * 1.1 is the default.
329         * 
330         * @param version HTTP specification version
331         */
332        public void setVersion(String version) {
333            _httpVersion = version;
334        }
335    
336    }