View Javadoc

1   /*
2    * Copyright (c) 2005 The University of Reading
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    * 1. Redistributions of source code must retain the above copyright
9    *    notice, this list of conditions and the following disclaimer.
10   * 2. Redistributions in binary form must reproduce the above copyright
11   *    notice, this list of conditions and the following disclaimer in the
12   *    documentation and/or other materials provided with the distribution.
13   * 3. Neither the name of the University of Reading, nor the names of the
14   *    authors or contributors may be used to endorse or promote products
15   *    derived from this software without specific prior written permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   */
28  
29  package uk.ac.rdg.resc.jstyx.client.callbacks;
30  
31  import java.io.File;
32  import java.io.InputStream;
33  import java.io.FileInputStream;
34  import java.io.FileNotFoundException;
35  import java.io.IOException;
36  
37  import uk.ac.rdg.resc.jstyx.client.StyxConnection;
38  import uk.ac.rdg.resc.jstyx.client.CStyxFile;
39  import uk.ac.rdg.resc.jstyx.client.MessageCallback;
40  
41  import uk.ac.rdg.resc.jstyx.messages.StyxMessage;
42  import uk.ac.rdg.resc.jstyx.messages.TwriteMessage;
43  import uk.ac.rdg.resc.jstyx.messages.RwriteMessage;
44  
45  import uk.ac.rdg.resc.jstyx.StyxUtils;
46  
47  /***
48   * Callback that is used when uploading a file to the server. Contains 
49   * state that needs to persist between message exchanges with the server.  See
50   * CStyxFile.uploadAsync()
51   *
52   * @author jdb
53   * $Revision: 475 $
54   * $Date: 2005-11-09 17:39:55 +0000 (Wed, 09 Nov 2005) $
55   * $Log$
56   * Revision 1.5  2005/11/09 17:39:55  jonblower
57   * Fixed bug with omission of response to Ropen/Rcreate message
58   *
59   * Revision 1.4  2005/11/07 22:01:35  jonblower
60   * Added code to close stream when UploadCallback has been created from a File object
61   *
62   * Revision 1.3  2005/11/04 19:25:14  jonblower
63   * Added code to write a zero-byte message to represent EOF
64   *
65   * Revision 1.1  2005/08/05 13:46:40  jonblower
66   * Factored out all callback objects from CStyxFile into separate classes
67   *
68   */
69  
70  public class UploadCallback extends MessageCallback
71  {
72      private InputStream in;
73      private byte[] bytes;
74      private long offset;
75      private MessageCallback callback;
76      private CStyxFile file;
77      private StyxConnection conn;
78      private boolean closeStreamWhenComplete;
79  
80      public UploadCallback(CStyxFile file, File localFile, MessageCallback callback)
81      {
82          this.init(file, null, callback);
83          this.closeStreamWhenComplete = true;
84          try
85          {
86              this.in = new FileInputStream(localFile);
87          }
88          catch(FileNotFoundException fnfe)
89          {
90              this.error("file does not exist", null);
91          }
92      }
93  
94      public UploadCallback(CStyxFile file, InputStream in, MessageCallback callback)
95      {
96          this.init(file, in, callback);
97      }
98  
99      private void init(CStyxFile file, InputStream in, MessageCallback callback)
100     {
101         this.file = file;
102         this.conn = this.file.getConnection();
103         this.in = in;
104         this.bytes = null;
105         this.offset = 0;
106         this.callback = callback;
107         this.closeStreamWhenComplete = false;
108     }
109 
110     public void nextStage(StyxMessage rMessage, StyxMessage tMessage)
111     {
112         if (this.file.isOpen())
113         {
114             // Now we can write to the file
115             try
116             {
117                 if (this.bytes == null)
118                 {
119                     this.bytes = new byte[this.file.getIoUnit()];
120                 }
121                 // Read from the source file
122                 int n = in.read(this.bytes);
123                 if (n > 0)
124                 {
125                     // Write to the server
126                     this.file.writeAsync(this.bytes, 0, n, this.offset, true, this);
127                 }
128                 else
129                 {
130                     // We've reached EOF. Write zero bytes
131                     // to the server to signify EOF
132                     this.file.writeAsync(new byte[0], this.offset, true, this);
133                 }
134             }
135             catch(IOException ioe)
136             {
137                 this.error("IOException occurred: " + ioe.getMessage(), null);
138             }
139         }
140         else
141         {
142             // If we haven't opened this file, open or create it
143             // The "false" means create a file, not a directory
144             this.file.openOrCreateAsync(false, StyxUtils.OWRITE | StyxUtils.OTRUNC, this);
145         }
146     }
147 
148     public void replyArrived(StyxMessage rMessage, StyxMessage tMessage)
149     {
150         if (rMessage instanceof RwriteMessage)
151         {
152             TwriteMessage tWriteMsg = (TwriteMessage)tMessage;
153             RwriteMessage rWriteMsg = (RwriteMessage)rMessage;
154             if (tWriteMsg.getCount() == rWriteMsg.getNumBytesWritten())
155             {
156                 if (rWriteMsg.getNumBytesWritten() > 0)
157                 {
158                     this.offset += rWriteMsg.getNumBytesWritten();
159                     this.nextStage(rMessage, tMessage);
160                 }
161                 else
162                 {
163                     // We've reached EOF. Close the file and notify that
164                     // upload is complete.
165                     this.file.close();
166                     if (this.closeStreamWhenComplete)
167                     {
168                         try
169                         {
170                             this.in.close();
171                         }
172                         catch(IOException ioe)
173                         {
174                             // Ignore this exception
175                         }
176                     }
177                     if (this.callback == null)
178                     {
179                         this.file.fireUploadComplete();
180                     }
181                     else
182                     {
183                         this.callback.replyArrived(rMessage, tMessage);
184                     }
185                 }
186             }
187             else
188             {
189                 this.error("Error writing data: tried to write " + tWriteMsg.getCount()
190                     + " bytes, actually wrote " + rWriteMsg.getNumBytesWritten()
191                     + " bytes.", tMessage);
192             }
193         }
194         else
195         {
196             // We've just got an Ropen or Rcreate message.
197             this.nextStage(rMessage, tMessage);
198         }
199     }
200 
201     public void error(String message, StyxMessage tMessage)
202     {
203         if (this.in != null)
204         {
205             try
206             {
207                 this.in.close();
208             }
209             catch(IOException ioe)
210             {
211                 CStyxFile.getLogger().debug("IOException when closing input stream: "
212                     + ioe.getMessage());
213             }
214         }
215         String errMsg = "Error uploading: " + message;
216         if (this.callback == null)
217         {
218             this.file.fireError(errMsg);
219         }
220         else
221         {
222             this.callback.error(errMsg, tMessage);
223         }
224     }
225 }