View Javadoc

1   package fr.in2p3.jsaga.adaptor.data;
2   
3   import fr.in2p3.jsaga.adaptor.base.defaults.Default;
4   import fr.in2p3.jsaga.adaptor.base.usage.Usage;
5   import fr.in2p3.jsaga.adaptor.data.optimise.DataCopy;
6   import fr.in2p3.jsaga.adaptor.data.optimise.DataCopyMonitor;
7   import fr.in2p3.jsaga.adaptor.data.optimise.DataRename;
8   import fr.in2p3.jsaga.adaptor.data.read.FileAttributes;
9   import fr.in2p3.jsaga.adaptor.data.read.FileReaderStreamFactory;
10  import fr.in2p3.jsaga.adaptor.data.write.FileWriterStreamFactory;
11  import fr.in2p3.jsaga.adaptor.security.SecurityCredential;
12  import fr.in2p3.jsaga.adaptor.security.impl.GSSCredentialSecurityCredential;
13  
14  import org.apache.log4j.Logger;
15  import org.globus.common.ChainedIOException;
16  import org.globus.ftp.DataChannelAuthentication;
17  import org.globus.ftp.FeatureList;
18  import org.globus.ftp.GridFTPSession;
19  import org.globus.ftp.exception.ClientException;
20  import org.globus.ftp.exception.ServerException;
21  import org.globus.ftp.exception.UnexpectedReplyCodeException;
22  import org.globus.gsi.gssapi.GlobusGSSException;
23  import org.globus.gsi.gssapi.auth.HostAuthorization;
24  import org.ietf.jgss.GSSCredential;
25  import org.ogf.saga.error.*;
26  
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  import java.util.Map;
31  
32  /* ***************************************************
33  * *** Centre de Calcul de l'IN2P3 - Lyon (France) ***
34  * ***             http://cc.in2p3.fr/             ***
35  * ***************************************************
36  * File:   GsiftpDataAdaptorAbstract
37  * Author: Sylvain Reynaud (sreynaud@in2p3.fr)
38  * Date:   20 juil. 2007
39  * ***************************************************
40  * Description:                                      */
41  /**
42   *
43   */
44  public abstract class GsiftpDataAdaptorAbstract implements DataCopy, DataRename, FileReaderStreamFactory, FileWriterStreamFactory
45  {
46      protected static final String TCP_BUFFER_SIZE = "TCPBufferSize";
47      protected int m_TCPBufferSize;
48      protected boolean m_DataChannelAuthentication;
49  
50      protected GSSCredential m_credential;
51      protected GsiftpClient m_client;
52  
53      public abstract String getType();
54      public abstract Usage getUsage();
55      public abstract Default[] getDefaults(Map attributes) throws IncorrectStateException;
56      public abstract FileAttributes getAttributes(String absolutePath, String additionalArgs) throws PermissionDeniedException, DoesNotExistException, TimeoutException, NoSuccessException;
57      public abstract FileAttributes[] listAttributes(String absolutePath, String additionalArgs) throws PermissionDeniedException, DoesNotExistException, TimeoutException, NoSuccessException;
58  
59      public Class[] getSupportedSecurityCredentialClasses() {
60          return new Class[]{GSSCredentialSecurityCredential.class};
61      }
62  
63      public void setSecurityCredential(SecurityCredential credential) {
64          m_credential = ((GSSCredentialSecurityCredential) credential).getGSSCredential();
65      }
66  
67      public int getDefaultPort() {
68          return 2811;
69      }
70  
71      public void connect(String userInfo, String host, int port, String basePath, Map attributes) throws AuthenticationFailedException, AuthorizationFailedException, BadParameterException, TimeoutException, NoSuccessException {
72          // configure
73          if (attributes!=null && attributes.containsKey(TCP_BUFFER_SIZE)) {
74              try {
75                  m_TCPBufferSize = Integer.parseInt((String) attributes.get(TCP_BUFFER_SIZE));
76              } catch (NumberFormatException e) {
77                  throw new BadParameterException("Bad value for configuration attribute: "+TCP_BUFFER_SIZE, e);
78              }
79          }
80          m_DataChannelAuthentication = false;
81  
82          // open connection
83          m_client = createConnection(m_credential, host, port, m_TCPBufferSize, m_DataChannelAuthentication);
84      }
85  
86      public void disconnect() throws NoSuccessException {
87          try {
88              m_client.disconnect();
89          } catch (Exception e) {
90              throw new NoSuccessException(e);
91          }
92      }
93  
94      public boolean exists(String absolutePath, String additionalArgs) throws PermissionDeniedException, TimeoutException, NoSuccessException {
95          try {
96              boolean exists = m_client.exists(absolutePath);
97  
98              //workaround: if permission is denied, throw an exception instead of returning false
99              if (exists) {
100                 return true;
101             } else {
102                 try {
103                     this.getAttributes(absolutePath, additionalArgs);   //may throw a PermissionDenied exception
104                     return true;
105                 } catch (DoesNotExistException e) {
106                     return false;
107                 }
108             }
109         } catch (Exception e) {
110             try {
111                 throw rethrowException(e);
112             } catch (DoesNotExistException doesNotExist) {
113                 throw new NoSuccessException(e);
114             } catch (BadParameterException badParameter) {
115                 throw new NoSuccessException("Unexpected exception", e);
116             }
117         }
118     }
119 
120     public InputStream getInputStream(String absolutePath, String additionalArgs) throws PermissionDeniedException, BadParameterException, DoesNotExistException, TimeoutException, NoSuccessException {
121         // create input stream
122         try {
123             return new GsiftpInputStream(m_client, absolutePath);
124         } catch (Exception e) {
125             throw rethrowException(e);
126         }
127     }
128 
129     protected void checkExists(String absolutePath) throws AlreadyExistsException, NoSuccessException, PermissionDeniedException, BadParameterException, TimeoutException, ParentDoesNotExist {
130         boolean exists;
131         try {
132             exists = m_client.exists(absolutePath);
133         } catch (Exception e) {
134             try {
135                 throw rethrowExceptionFull(e);
136             } catch (DoesNotExistException e1) {
137                 throw new ParentDoesNotExist(e);
138             }
139         }
140         if (exists) {
141             throw new AlreadyExistsException("File already exists: "+absolutePath);
142         }
143     }
144     
145     public OutputStream getOutputStream(String parentAbsolutePath, String fileName, boolean exclusive, boolean append, String additionalArgs) throws PermissionDeniedException, BadParameterException, AlreadyExistsException, ParentDoesNotExist, TimeoutException, NoSuccessException {
146         
147         // Check if Append is supported
148         if (append && !m_client.isAppendSupported())
149             throw new BadParameterException("'Append' flag will probably not work on the remote server");
150         
151         String absolutePath = parentAbsolutePath+"/"+fileName;
152 
153         // test existence
154         if (exclusive) {
155             checkExists(absolutePath);
156         }
157 
158         // create output stream
159         try {
160             return new GsiftpOutputStream(m_credential, m_client, absolutePath, append);
161         } catch (Exception e) {
162             try {
163                 throw rethrowExceptionFull(e);
164             } catch (DoesNotExistException e2) {
165                 throw new ParentDoesNotExist(e);
166             }
167         }
168     }
169 
170     public void copy(String sourceAbsolutePath, 
171                         String targetHost, 
172                         int targetPort, 
173                         String targetAbsolutePath, 
174                         boolean overwrite, 
175                         String additionalArgs,
176                         DataCopyMonitor progressMonitor) 
177             throws AuthenticationFailedException, AuthorizationFailedException, PermissionDeniedException, BadParameterException, AlreadyExistsException, DoesNotExistException, ParentDoesNotExist, TimeoutException, NoSuccessException {
178         // connect to peer server
179         GsiftpDataAdaptorAbstract targetAdaptor;
180         String type = this.getType();
181         if ("gsiftp-v1".equals(type))           targetAdaptor = new Gsiftp1DataAdaptor();
182         else if ("gsiftp-v2".equals(type))      targetAdaptor = new Gsiftp2DataAdaptor();
183         else if ("gsiftp-win".equals(type))     targetAdaptor = new GsiftpWinDataAdaptor();
184         else if ("gsiftp-dcache".equals(type))  targetAdaptor = new GsiftpDCacheDataAdaptor();
185         else if ("gsiftp-dpm".equals(type))     throw new NoSuccessException("Third-party transfer not implemented for protocol: "+type);
186         else                                    throw new NoSuccessException("[INTERNAL ERROR] Unexpected protocol: "+type);
187         targetAdaptor.m_credential = m_credential;
188         targetAdaptor.connect(null, targetHost, targetPort, null, null);
189 
190         try {
191              //todo: remove this block when overwriting target file will work (it only works with UrlCopy)
192              if (overwrite && targetAdaptor.exists(targetAbsolutePath, additionalArgs)) {
193                  try {
194                      targetAdaptor.m_client.deleteFile(targetAbsolutePath);
195                  } catch (Exception e) {
196                      throw new PermissionDeniedException("Failed to overwrite target file", e);
197                  }
198              }
199 
200              // need to check existence of target explicitely, else exception is never thrown
201              if (!overwrite && targetAdaptor.exists(targetAbsolutePath, additionalArgs)) {
202                  throw new AlreadyExistsException("File already exists");
203              }
204 
205             // for compatibility with VDT-1.6, .NET implementation
206             m_client.setDataChannelAuthentication(DataChannelAuthentication.NONE);
207             targetAdaptor.m_client.setDataChannelAuthentication(DataChannelAuthentication.NONE);
208 
209             // transfer file
210             m_client.setType(GridFTPSession.TYPE_IMAGE);
211             targetAdaptor.m_client.setType(GridFTPSession.TYPE_IMAGE);
212             m_client.setMode(GridFTPSession.MODE_EBLOCK);
213             targetAdaptor.m_client.setMode(GridFTPSession.MODE_EBLOCK);
214             m_client.setStripedActive(targetAdaptor.m_client.setStripedPassive());
215             if (progressMonitor == null) {
216                 m_client.extendedTransfer(sourceAbsolutePath, targetAdaptor.m_client, targetAbsolutePath, null);
217             } else {
218                 m_client.extendedTransfer(sourceAbsolutePath, targetAdaptor.m_client, targetAbsolutePath, new CopyListener(progressMonitor));
219             }
220         } catch (Exception e) {
221             throw rethrowExceptionFull(e);
222         } finally {
223             // disconnect from peer server
224             targetAdaptor.disconnect();
225         }
226     }
227 
228     public void copyFrom(String sourceHost, int sourcePort, String sourceAbsolutePath, String targetAbsolutePath, boolean overwrite, String additionalArgs) throws AuthenticationFailedException, AuthorizationFailedException, PermissionDeniedException, BadParameterException, AlreadyExistsException, DoesNotExistException, TimeoutException, NoSuccessException {
229         // connect to peer server
230         GsiftpDataAdaptorAbstract sourceAdaptor = new Gsiftp1DataAdaptor();
231         sourceAdaptor.m_credential = m_credential;
232         sourceAdaptor.connect(null, sourceHost, sourcePort, null, null);
233 
234         try {
235             //todo: remove this block when overwriting target file will work (it only works with UrlCopy)
236             if (overwrite && this.exists(targetAbsolutePath, additionalArgs)) {
237                 try {
238                     m_client.deleteFile(targetAbsolutePath);
239                 } catch (Exception e) {
240                     throw new PermissionDeniedException("Failed to overwrite target file", e);
241                 }
242             }
243 
244             // need to check existence of target explicitely, else exception is never thrown
245             if (!overwrite && this.exists(targetAbsolutePath, additionalArgs)) {
246                 throw new AlreadyExistsException("File already exists");
247             }
248 
249             // for compatibility with VDT-1.6, .NET implementation
250             sourceAdaptor.m_client.setDataChannelAuthentication(DataChannelAuthentication.NONE);
251             m_client.setDataChannelAuthentication(DataChannelAuthentication.NONE);
252 
253             // transfer file
254             sourceAdaptor.m_client.setType(GridFTPSession.TYPE_IMAGE);
255             m_client.setType(GridFTPSession.TYPE_IMAGE);
256             sourceAdaptor.m_client.setMode(GridFTPSession.MODE_EBLOCK);
257             m_client.setMode(GridFTPSession.MODE_EBLOCK);
258             sourceAdaptor.m_client.setStripedActive(m_client.setStripedPassive());
259             sourceAdaptor.m_client.extendedTransfer(sourceAbsolutePath, m_client, targetAbsolutePath, null);
260         } catch (Exception e) {
261             throw rethrowExceptionFull(e);
262         } finally {
263             // disconnect from peer server
264             sourceAdaptor.disconnect();
265         }
266     }
267 
268     public void rename(String sourceAbsolutePath, String targetAbsolutePath, boolean overwrite, String additionalArgs) throws PermissionDeniedException, BadParameterException, DoesNotExistException, AlreadyExistsException, TimeoutException, NoSuccessException {
269         try {
270             m_client.rename(sourceAbsolutePath, targetAbsolutePath);
271         } catch (Exception e) {
272             throw rethrowExceptionFull(e);
273         }
274     }
275 
276     public void removeFile(String parentAbsolutePath, String fileName, String additionalArgs) throws PermissionDeniedException, BadParameterException, DoesNotExistException, TimeoutException, NoSuccessException {
277         try {
278             m_client.deleteFile(parentAbsolutePath+"/"+fileName);
279         } catch (Exception e) {
280             throw rethrowException(e);
281         }
282     }
283 
284     public void makeDir(String parentAbsolutePath, String directoryName, String additionalArgs) throws PermissionDeniedException, BadParameterException, AlreadyExistsException, ParentDoesNotExist, TimeoutException, NoSuccessException {
285         try {
286             m_client.makeDir(parentAbsolutePath+"/"+directoryName);
287         } catch (Exception e) {
288             try {
289                 throw rethrowExceptionFull(e);
290             } catch (DoesNotExistException e2) {
291                 throw new ParentDoesNotExist(e);
292             }
293         }
294     }
295 
296     public void removeDir(String parentAbsolutePath, String directoryName, String additionalArgs) throws PermissionDeniedException, BadParameterException, DoesNotExistException, TimeoutException, NoSuccessException {
297         try {
298             m_client.deleteDir(parentAbsolutePath+"/"+directoryName);
299         } catch (Exception e) {
300             throw rethrowException(e);
301         }
302     }
303 
304     public static GsiftpClient createConnection(GSSCredential cred, String host, int port, int tcpBufferSize, boolean reqDCAU) throws AuthenticationFailedException, AuthorizationFailedException, TimeoutException, NoSuccessException {
305         Logger.getLogger(GsiftpDataAdaptorAbstract.class).info("Connecting to Gsiftp service at: " + host + ":" + port + "...");
306         GsiftpClient client;
307         try {
308             client = new GsiftpClient(host, port);
309         } catch (IOException e) {
310             if (e.getMessage()!=null && e.getMessage().indexOf("Authentication") > -1) {
311                 throw new AuthenticationFailedException(e);
312             } else {
313                 throw new TimeoutException(e);
314             }
315         }  catch (ServerException e) {
316             switch(e.getCode()) {
317                 case ServerException.SERVER_REFUSED:
318                     try {
319                         throw e.getRootCause();
320                     } catch (UnexpectedReplyCodeException unexpectedReplyCode) {
321                         switch(unexpectedReplyCode.getReply().getCode()) {
322                             case 530:
323                                 throw new AuthorizationFailedException(unexpectedReplyCode);
324                             default:
325                                 throw new NoSuccessException(unexpectedReplyCode);
326                         }
327                     } catch (Exception e1) {
328                         throw new NoSuccessException(e1);
329                     }
330                 default:
331                     throw new NoSuccessException(e);
332             }
333         }
334         
335         Logger.getLogger(GsiftpDataAdaptorAbstract.class).info(client.getWelcome());
336 
337         try {
338             client.setAuthorization(HostAuthorization.getInstance());
339             client.authenticate(cred);
340 
341             // may change TCP buffer size
342             if (tcpBufferSize > 0) {
343                 client.setTCPBufferSize(tcpBufferSize);
344             }
345 
346             // may disable data channel authentication
347             if (client.isFeatureSupported(FeatureList.DCAU)) {
348                 if (! reqDCAU) {
349                     client.setDataChannelAuthentication(DataChannelAuthentication.NONE);
350                 }
351             } else {
352                 client.setLocalNoDataChannelAuthentication();
353             }
354             // returns
355             return client;
356         } catch (ChainedIOException e) {
357             disconnectClient(client);
358             try {
359                 throw e.getCause();
360             } catch (GlobusGSSException gssException) {
361                 throw new AuthenticationFailedException(gssException);
362             } catch (Throwable throwable) {
363                 throw new TimeoutException(throwable);
364             }
365         } catch (IOException e) {
366             disconnectClient(client);
367             if (e.getMessage()!=null && e.getMessage().indexOf("Authentication") > -1) {
368                 throw new AuthenticationFailedException(e);
369             } else {
370                 throw new TimeoutException(e);
371             }
372         } catch (ServerException e) {
373             disconnectClient(client);
374             switch(e.getCode()) {
375                 case ServerException.SERVER_REFUSED:
376                     try {
377                         throw e.getRootCause();
378                     } catch (UnexpectedReplyCodeException unexpectedReplyCode) {
379                         switch(unexpectedReplyCode.getReply().getCode()) {
380                             case 530:
381                                 throw new AuthorizationFailedException(unexpectedReplyCode);
382                             default:
383                                 throw new NoSuccessException(unexpectedReplyCode);
384                         }
385                     } catch (Exception e1) {
386                         throw new NoSuccessException(e1);
387                     }
388                 default:
389                     throw new NoSuccessException(e);
390             }
391         } 
392     }
393     
394     private static void disconnectClient(GsiftpClient client) {
395         try {
396             client.disconnect();
397         } catch (ServerException ex) {
398             Logger.getLogger(GsiftpDataAdaptorAbstract.class).warn("Error disconnecting", ex);
399         } catch (IOException ex) {
400             Logger.getLogger(GsiftpDataAdaptorAbstract.class).warn("Error disconnecting", ex);
401         }
402     }
403 
404     protected NoSuccessException rethrowException(Exception exception) throws PermissionDeniedException, BadParameterException, DoesNotExistException, TimeoutException, NoSuccessException {
405         try {
406             throw rethrowExceptionFull(exception);
407         } catch (AlreadyExistsException e) {
408             throw new NoSuccessException(e);
409         }
410     }
411 
412     NoSuccessException rethrowExceptionFull(Exception exception) throws PermissionDeniedException, BadParameterException, DoesNotExistException, AlreadyExistsException, TimeoutException, NoSuccessException {
413         try {
414             throw exception;
415         }
416         catch (PermissionDeniedException e) {throw e;}
417         catch (BadParameterException e) {throw e;}
418         catch (DoesNotExistException e) {throw e;}
419         catch (AlreadyExistsException e) {throw e;}
420         catch (TimeoutException e) {throw e;}
421         catch (NoSuccessException e) {throw e;}
422         catch (IllegalArgumentException e) {
423             throw new BadParameterException(e);
424         } catch (IOException e) {
425             throw new TimeoutException(e);
426         } catch (ServerException e) {
427             switch(e.getCode()) {
428                 case ServerException.SERVER_REFUSED:
429                     try {
430                         throw e.getRootCause();
431                     } catch (UnexpectedReplyCodeException unexpectedReplyCode) {
432                         switch(unexpectedReplyCode.getReply().getCode()) {
433                             case 112:
434                                 throw new TimeoutException(e);
435                             case 500:
436                             case 521:
437                             case 550:
438                             case 451:
439                                 this.rethrowParsedException(unexpectedReplyCode);
440                             default:
441                                 throw new NoSuccessException(e);
442                         }
443                     } catch (Exception e1) {
444                         throw new PermissionDeniedException(e1);
445                     }
446                 case ServerException.REPLY_TIMEOUT:             throw new TimeoutException(e);
447                 default:                                        throw new NoSuccessException(e);
448             }
449         } catch (ClientException e) {
450             switch(e.getCode()) {
451                 case ClientException.NOT_AUTHORIZED:            throw new PermissionDeniedException(e);
452                 case ClientException.REPLY_TIMEOUT:             throw new TimeoutException(e);
453                 default:                                        throw new NoSuccessException(e);
454             }
455         } catch (Exception e) {
456             throw new NoSuccessException(e);
457         }
458     }
459     protected abstract void rethrowParsedException(UnexpectedReplyCodeException e) throws DoesNotExistException, AlreadyExistsException, PermissionDeniedException, NoSuccessException;
460 }