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                         setDelegatingRequests( true );
124                         setDelegatingResponses(true);
125                         super.onResponseComplete();
126                     }
127                 }
128                 catch ( IOException ioe )
129                 {
130                     Log.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
131                     super.onResponseComplete();
132                 }
133             }
134             else
135             {
136                 if (Log.isDebugEnabled())
137                     Log.debug("WebdavListener:Not ready, calling super");
138                 super.onResponseComplete();
139             }
140         }
141         else
142         {
143             super.onResponseComplete();
144         }
145     }
146 
147     
148     
149     public void onRequestComplete () throws IOException
150     {
151         _requestComplete = true;
152         if (_needIntercept)
153         {
154             if ( _requestComplete && _responseComplete)
155             {
156                 try
157                 {
158                     // we have some work to do before retrying this
159                     if ( resolveCollectionIssues() )
160                     {
161                         setDelegatingRequests( true );
162                         setDelegatingResponses(true);
163                         _requestComplete = false;
164                         _responseComplete = false;
165                         _destination.resend(_exchange);
166                     }
167                     else
168                     {
169                         // admit defeat but retry because someone else might have 
170                         setDelegatingRequests( true );
171                         setDelegatingResponses(true);
172                         super.onRequestComplete();
173                     }
174                 }
175                 catch ( IOException ioe )
176                 {
177                     Log.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
178                     super.onRequestComplete();
179                 }
180             }
181             else
182             {
183                 if (Log.isDebugEnabled())
184                     Log.debug("WebdavListener:Not ready, calling super");
185                 super.onRequestComplete();
186             }
187         }
188         else
189         {
190             super.onRequestComplete();
191         } 
192     }
193 
194    
195     
196     
197     /**
198      * walk through the steps to try and resolve missing parent collection issues via webdav
199      *
200      * @return
201      * @throws IOException
202      */
203     private boolean resolveCollectionIssues() throws IOException
204     {
205 
206         String uri = _exchange.getURI();
207         String[] uriCollection = _exchange.getURI().split("/");
208         int checkNum = uriCollection.length;
209         int rewind = 0;
210 
211         String parentUri = URIUtil.parentPath( uri );
212         while ( !checkExists( parentUri ) )
213         {
214             ++rewind;
215             parentUri = URIUtil.parentPath( parentUri );
216         }
217 
218         // confirm webdav is supported for this collection
219         if ( checkWebdavSupported() )
220         {
221             for (int i = 0; i < rewind;)
222             {
223                 makeCollection(parentUri + "/" + uriCollection[checkNum - rewind - 1]);
224                 parentUri = parentUri + "/" + uriCollection[checkNum - rewind - 1];
225                 --rewind;
226             }
227         }
228         else
229         {
230             return false;
231         }
232 
233         return true;
234     }
235 
236     private boolean checkExists( String uri ) throws IOException
237     {
238         PropfindExchange propfindExchange = new PropfindExchange();
239         propfindExchange.setAddress( _exchange.getAddress() );
240         propfindExchange.setMethod( HttpMethods.GET ); // PROPFIND acts wonky, just use get
241         propfindExchange.setScheme( _exchange.getScheme() );
242         propfindExchange.setEventListener( new SecurityListener( _destination, propfindExchange ) );
243         propfindExchange.setConfigureListeners( false );
244         propfindExchange.setURI( uri );
245 
246         _destination.send( propfindExchange );
247 
248         try
249         {
250             propfindExchange.waitForDone();
251 
252             return propfindExchange.exists();
253         }
254         catch ( InterruptedException ie )
255         {
256             Log.ignore( ie );                  
257             return false;
258         }
259     }
260 
261     private boolean makeCollection( String uri ) throws IOException
262     {
263         MkcolExchange mkcolExchange = new MkcolExchange();
264         mkcolExchange.setAddress( _exchange.getAddress() );
265         mkcolExchange.setMethod( "MKCOL " + uri + " HTTP/1.1" );
266         mkcolExchange.setScheme( _exchange.getScheme() );
267         mkcolExchange.setEventListener( new SecurityListener( _destination, mkcolExchange ) );
268         mkcolExchange.setConfigureListeners( false );
269         mkcolExchange.setURI( uri );
270 
271         _destination.send( mkcolExchange );
272 
273         try
274         {
275             mkcolExchange.waitForDone();
276 
277             return mkcolExchange.exists();
278         }
279         catch ( InterruptedException ie )
280         {
281             Log.ignore( ie );
282             return false;
283         }
284     }
285 
286     
287     private boolean checkWebdavSupported() throws IOException
288     {
289         WebdavSupportedExchange supportedExchange = new WebdavSupportedExchange();
290         supportedExchange.setAddress( _exchange.getAddress() );
291         supportedExchange.setMethod( HttpMethods.OPTIONS );
292         supportedExchange.setScheme( _exchange.getScheme() );
293         supportedExchange.setEventListener( new SecurityListener( _destination, supportedExchange ) );
294         supportedExchange.setConfigureListeners( false );
295         supportedExchange.setURI( _exchange.getURI() );
296 
297         _destination.send( supportedExchange );
298 
299         try
300         {
301             supportedExchange.waitTilCompletion();
302             return supportedExchange.isWebdavSupported();
303         }
304         catch (InterruptedException ie )
305         {            
306             Log.ignore( ie );
307             return false;
308         }
309 
310     }
311 
312 }