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