View Javadoc

1   // ========================================================================
2   // Copyright (c) 2008-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  
15  package org.eclipse.jetty.client.webdav;
16  
17  import java.io.IOException;
18  
19  import org.eclipse.jetty.client.HttpDestination;
20  import org.eclipse.jetty.client.HttpEventListenerWrapper;
21  import org.eclipse.jetty.client.HttpExchange;
22  import org.eclipse.jetty.client.security.SecurityListener;
23  import org.eclipse.jetty.http.HttpMethods;
24  import org.eclipse.jetty.http.HttpStatus;
25  import org.eclipse.jetty.io.Buffer;
26  import org.eclipse.jetty.util.URIUtil;
27  import org.eclipse.jetty.util.log.Log;
28  
29  /**
30   * WebdavListener
31   * 
32   * 
33   * 
34   * 
35   */
36  public class WebdavListener extends HttpEventListenerWrapper
37  {
38      private HttpDestination _destination;
39      private HttpExchange _exchange;
40      private boolean _requestComplete;
41      private boolean _responseComplete; 
42      private boolean _webdavEnabled;
43      private boolean _needIntercept;
44  
45      public WebdavListener(HttpDestination destination, HttpExchange ex)
46      {
47          // Start of sending events through to the wrapped listener
48          // Next decision point is the onResponseStatus
49          super(ex.getEventListener(),true);
50          _destination=destination;
51          _exchange=ex;
52  
53          // We'll only enable webdav if this is a PUT request
54          if ( HttpMethods.PUT.equalsIgnoreCase( _exchange.getMethod() ) )
55          {
56              _webdavEnabled = true;
57          }
58      }
59  
60      public void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
61      {
62          if ( !_webdavEnabled )
63          {
64              _needIntercept = false;
65              super.onResponseStatus(version, status, reason);
66              return;
67          }
68          
69          if (Log.isDebugEnabled())
70              Log.debug("WebdavListener:Response Status: " + status );
71  
72          // The dav spec says that CONFLICT should be returned when the parent collection doesn't exist but I am seeing
73          // FORBIDDEN returned instead so running with that.
74          if ( status == HttpStatus.FORBIDDEN_403 || status == HttpStatus.CONFLICT_409 )
75          {
76              if ( _webdavEnabled )
77              {
78                  if (Log.isDebugEnabled())
79                      Log.debug("WebdavListener:Response Status: dav enabled, taking a stab at resolving put issue" );
80                  setDelegatingResponses( false ); // stop delegating, we can try and fix this request
81                  _needIntercept = true;
82              }
83              else
84              {
85                  if (Log.isDebugEnabled())
86                      Log.debug("WebdavListener:Response Status: Webdav Disabled" );
87                  setDelegatingResponses( true ); // just make sure we delegate
88                  setDelegatingRequests( true );
89                  _needIntercept = false;
90              }
91          }
92          else
93          {
94              _needIntercept = false;
95              setDelegatingResponses( true );
96              setDelegatingRequests( true );
97          }
98  
99          super.onResponseStatus(version, status, reason);
100     }
101 
102     public void onResponseComplete() throws IOException
103     {
104         _responseComplete = true;
105         if (_needIntercept)
106         {
107             if ( _requestComplete && _responseComplete)
108             {
109                 try
110                 {
111                     // we have some work to do before retrying this
112                     if ( resolveCollectionIssues() )
113                     {
114                         setDelegatingRequests( true );
115                         setDelegatingResponses(true);
116                         _requestComplete = false;
117                         _responseComplete = false;
118                         _destination.resend(_exchange);
119                     }
120                     else
121                     {
122                         // admit defeat but retry because someone else might have 
123                         setDelegationResult(false);
124                         setDelegatingRequests( true );
125                         setDelegatingResponses(true);
126                         super.onResponseComplete();
127                     }
128                 }
129                 catch ( IOException ioe )
130                 {
131                     Log.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
132                     super.onResponseComplete();
133                 }
134             }
135             else
136             {
137                 if (Log.isDebugEnabled())
138                     Log.debug("WebdavListener:Not ready, calling super");
139                 super.onResponseComplete();
140             }
141         }
142         else
143         {
144             super.onResponseComplete();
145         }
146     }
147 
148     
149     
150     public void onRequestComplete () throws IOException
151     {
152         _requestComplete = true;
153         if (_needIntercept)
154         {
155             if ( _requestComplete && _responseComplete)
156             {
157                 try
158                 {
159                     // we have some work to do before retrying this
160                     if ( resolveCollectionIssues() )
161                     {
162                         setDelegatingRequests( true );
163                         setDelegatingResponses(true);
164                         _requestComplete = false;
165                         _responseComplete = false;
166                         _destination.resend(_exchange);
167                     }
168                     else
169                     {
170                         // admit defeat but retry because someone else might have 
171                         setDelegatingRequests( true );
172                         setDelegatingResponses(true);
173                         super.onRequestComplete();
174                     }
175                 }
176                 catch ( IOException ioe )
177                 {
178                     Log.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
179                     super.onRequestComplete();
180                 }
181             }
182             else
183             {
184                 if (Log.isDebugEnabled())
185                     Log.debug("WebdavListener:Not ready, calling super");
186                 super.onRequestComplete();
187             }
188         }
189         else
190         {
191             super.onRequestComplete();
192         } 
193     }
194 
195    
196     
197     
198     /**
199      * walk through the steps to try and resolve missing parent collection issues via webdav
200      *
201      * TODO this really ought to use URI itself for this resolution
202      *
203      * @return
204      * @throws IOException
205      */
206     private boolean resolveCollectionIssues() throws IOException
207     {
208 
209         String uri = _exchange.getURI();
210         String[] uriCollection = _exchange.getURI().split("/");
211         int checkNum = uriCollection.length;
212         int rewind = 0;
213 
214         String parentUri = URIUtil.parentPath( uri );
215         while ( parentUri != null && !checkExists(parentUri) )
216         {
217             ++rewind;
218             parentUri = URIUtil.parentPath( parentUri );
219         }
220 
221         // confirm webdav is supported for this collection
222         if ( checkWebdavSupported() )
223         {
224             for (int i = 0; i < rewind;)
225             {
226                 makeCollection(parentUri + "/" + uriCollection[checkNum - rewind - 1]);
227                 parentUri = parentUri + "/" + uriCollection[checkNum - rewind - 1];
228                 --rewind;
229             }
230         }
231         else
232         {
233             return false;
234         }
235 
236         return true;
237     }
238 
239     private boolean checkExists( String uri ) throws IOException
240     {
241         if (uri == null)
242         {
243             System.out.println("have failed miserably");
244             return false;
245         }
246         
247         PropfindExchange propfindExchange = new PropfindExchange();
248         propfindExchange.setAddress( _exchange.getAddress() );
249         propfindExchange.setMethod( HttpMethods.GET ); // PROPFIND acts wonky, just use get
250         propfindExchange.setScheme( _exchange.getScheme() );
251         propfindExchange.setEventListener( new SecurityListener( _destination, propfindExchange ) );
252         propfindExchange.setConfigureListeners( false );
253         propfindExchange.setURI( uri );
254 
255         _destination.send( propfindExchange );
256 
257         try
258         {
259             propfindExchange.waitForDone();
260 
261             return propfindExchange.exists();
262         }
263         catch ( InterruptedException ie )
264         {
265             Log.ignore( ie );                  
266             return false;
267         }
268     }
269 
270     private boolean makeCollection( String uri ) throws IOException
271     {
272         MkcolExchange mkcolExchange = new MkcolExchange();
273         mkcolExchange.setAddress( _exchange.getAddress() );
274         mkcolExchange.setMethod( "MKCOL " + uri + " HTTP/1.1" );
275         mkcolExchange.setScheme( _exchange.getScheme() );
276         mkcolExchange.setEventListener( new SecurityListener( _destination, mkcolExchange ) );
277         mkcolExchange.setConfigureListeners( false );
278         mkcolExchange.setURI( uri );
279 
280         _destination.send( mkcolExchange );
281 
282         try
283         {
284             mkcolExchange.waitForDone();
285 
286             return mkcolExchange.exists();
287         }
288         catch ( InterruptedException ie )
289         {
290             Log.ignore( ie );
291             return false;
292         }
293     }
294 
295     
296     private boolean checkWebdavSupported() throws IOException
297     {
298         WebdavSupportedExchange supportedExchange = new WebdavSupportedExchange();
299         supportedExchange.setAddress( _exchange.getAddress() );
300         supportedExchange.setMethod( HttpMethods.OPTIONS );
301         supportedExchange.setScheme( _exchange.getScheme() );
302         supportedExchange.setEventListener( new SecurityListener( _destination, supportedExchange ) );
303         supportedExchange.setConfigureListeners( false );
304         supportedExchange.setURI( _exchange.getURI() );
305 
306         _destination.send( supportedExchange );
307 
308         try
309         {
310             supportedExchange.waitTilCompletion();
311             return supportedExchange.isWebdavSupported();
312         }
313         catch (InterruptedException ie )
314         {            
315             Log.ignore( ie );
316             return false;
317         }
318 
319     }
320 
321 }