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      @Override
61      public void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
62      {
63          if ( !_webdavEnabled )
64          {
65              _needIntercept = false;
66              super.onResponseStatus(version, status, reason);
67              return;
68          }
69          
70          if (Log.isDebugEnabled())
71              Log.debug("WebdavListener:Response Status: " + status );
72  
73          // The dav spec says that CONFLICT should be returned when the parent collection doesn't exist but I am seeing
74          // FORBIDDEN returned instead so running with that.
75          if ( status == HttpStatus.FORBIDDEN_403 || status == HttpStatus.CONFLICT_409 )
76          {
77              if ( _webdavEnabled )
78              {
79                  if (Log.isDebugEnabled())
80                      Log.debug("WebdavListener:Response Status: dav enabled, taking a stab at resolving put issue" );
81                  setDelegatingResponses( false ); // stop delegating, we can try and fix this request
82                  _needIntercept = true;
83              }
84              else
85              {
86                  if (Log.isDebugEnabled())
87                      Log.debug("WebdavListener:Response Status: Webdav Disabled" );
88                  setDelegatingResponses( true ); // just make sure we delegate
89                  setDelegatingRequests( true );
90                  _needIntercept = false;
91              }
92          }
93          else
94          {
95              _needIntercept = false;
96              setDelegatingResponses( true );
97              setDelegatingRequests( true );
98          }
99  
100         super.onResponseStatus(version, status, reason);
101     }
102 
103     @Override
104     public void onResponseComplete() throws IOException
105     {
106         _responseComplete = true;
107         if (_needIntercept)
108         {
109             if ( _requestComplete && _responseComplete)
110             {
111                 try
112                 {
113                     // we have some work to do before retrying this
114                     if ( resolveCollectionIssues() )
115                     {
116                         setDelegatingRequests( true );
117                         setDelegatingResponses(true);
118                         _requestComplete = false;
119                         _responseComplete = false;
120                         _destination.resend(_exchange);
121                     }
122                     else
123                     {
124                         // admit defeat but retry because someone else might have 
125                         setDelegationResult(false);
126                         setDelegatingRequests( true );
127                         setDelegatingResponses(true);
128                         super.onResponseComplete();
129                     }
130                 }
131                 catch ( IOException ioe )
132                 {
133                     Log.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
134                     super.onResponseComplete();
135                 }
136             }
137             else
138             {
139                 if (Log.isDebugEnabled())
140                     Log.debug("WebdavListener:Not ready, calling super");
141                 super.onResponseComplete();
142             }
143         }
144         else
145         {
146             super.onResponseComplete();
147         }
148     }
149 
150     
151     
152     @Override
153     public void onRequestComplete () throws IOException
154     {
155         _requestComplete = true;
156         if (_needIntercept)
157         {
158             if ( _requestComplete && _responseComplete)
159             {
160                 try
161                 {
162                     // we have some work to do before retrying this
163                     if ( resolveCollectionIssues() )
164                     {
165                         setDelegatingRequests( true );
166                         setDelegatingResponses(true);
167                         _requestComplete = false;
168                         _responseComplete = false;
169                         _destination.resend(_exchange);
170                     }
171                     else
172                     {
173                         // admit defeat but retry because someone else might have 
174                         setDelegatingRequests( true );
175                         setDelegatingResponses(true);
176                         super.onRequestComplete();
177                     }
178                 }
179                 catch ( IOException ioe )
180                 {
181                     Log.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
182                     super.onRequestComplete();
183                 }
184             }
185             else
186             {
187                 if (Log.isDebugEnabled())
188                     Log.debug("WebdavListener:Not ready, calling super");
189                 super.onRequestComplete();
190             }
191         }
192         else
193         {
194             super.onRequestComplete();
195         } 
196     }
197 
198    
199     
200     
201     /**
202      * walk through the steps to try and resolve missing parent collection issues via webdav
203      *
204      * TODO this really ought to use URI itself for this resolution
205      *
206      * @return
207      * @throws IOException
208      */
209     private boolean resolveCollectionIssues() throws IOException
210     {
211 
212         String uri = _exchange.getURI();
213         String[] uriCollection = _exchange.getURI().split("/");
214         int checkNum = uriCollection.length;
215         int rewind = 0;
216 
217         String parentUri = URIUtil.parentPath( uri );
218         while ( parentUri != null && !checkExists(parentUri) )
219         {
220             ++rewind;
221             parentUri = URIUtil.parentPath( parentUri );
222         }
223 
224         // confirm webdav is supported for this collection
225         if ( checkWebdavSupported() )
226         {
227             for (int i = 0; i < rewind;)
228             {
229                 makeCollection(parentUri + "/" + uriCollection[checkNum - rewind - 1]);
230                 parentUri = parentUri + "/" + uriCollection[checkNum - rewind - 1];
231                 --rewind;
232             }
233         }
234         else
235         {
236             return false;
237         }
238 
239         return true;
240     }
241 
242     private boolean checkExists( String uri ) throws IOException
243     {
244         if (uri == null)
245         {
246             System.out.println("have failed miserably");
247             return false;
248         }
249         
250         PropfindExchange propfindExchange = new PropfindExchange();
251         propfindExchange.setAddress( _exchange.getAddress() );
252         propfindExchange.setMethod( HttpMethods.GET ); // PROPFIND acts wonky, just use get
253         propfindExchange.setScheme( _exchange.getScheme() );
254         propfindExchange.setEventListener( new SecurityListener( _destination, propfindExchange ) );
255         propfindExchange.setConfigureListeners( false );
256         propfindExchange.setURI( uri );
257 
258         _destination.send( propfindExchange );
259 
260         try
261         {
262             propfindExchange.waitForDone();
263 
264             return propfindExchange.exists();
265         }
266         catch ( InterruptedException ie )
267         {
268             Log.ignore( ie );                  
269             return false;
270         }
271     }
272 
273     private boolean makeCollection( String uri ) throws IOException
274     {
275         MkcolExchange mkcolExchange = new MkcolExchange();
276         mkcolExchange.setAddress( _exchange.getAddress() );
277         mkcolExchange.setMethod( "MKCOL " + uri + " HTTP/1.1" );
278         mkcolExchange.setScheme( _exchange.getScheme() );
279         mkcolExchange.setEventListener( new SecurityListener( _destination, mkcolExchange ) );
280         mkcolExchange.setConfigureListeners( false );
281         mkcolExchange.setURI( uri );
282 
283         _destination.send( mkcolExchange );
284 
285         try
286         {
287             mkcolExchange.waitForDone();
288 
289             return mkcolExchange.exists();
290         }
291         catch ( InterruptedException ie )
292         {
293             Log.ignore( ie );
294             return false;
295         }
296     }
297 
298     
299     private boolean checkWebdavSupported() throws IOException
300     {
301         WebdavSupportedExchange supportedExchange = new WebdavSupportedExchange();
302         supportedExchange.setAddress( _exchange.getAddress() );
303         supportedExchange.setMethod( HttpMethods.OPTIONS );
304         supportedExchange.setScheme( _exchange.getScheme() );
305         supportedExchange.setEventListener( new SecurityListener( _destination, supportedExchange ) );
306         supportedExchange.setConfigureListeners( false );
307         supportedExchange.setURI( _exchange.getURI() );
308 
309         _destination.send( supportedExchange );
310 
311         try
312         {
313             supportedExchange.waitTilCompletion();
314             return supportedExchange.isWebdavSupported();
315         }
316         catch (InterruptedException ie )
317         {            
318             Log.ignore( ie );
319             return false;
320         }
321 
322     }
323 
324 }