Coverage Report - wjhk.jupload2.upload.UploadFileData
 
Classes in this File Line Coverage Branch Coverage Complexity
UploadFileData
20 %
20/97
10 %
3/28
1,967
 
 1  
 //
 2  
 // $Id: UploadFileData.java 1721 2015-03-29 17:48:44Z etienne_sf $
 3  
 //
 4  
 // jupload - A file upload applet.
 5  
 // Copyright 2007 The JUpload Team
 6  
 //
 7  
 // Created: 2006-11-20
 8  
 // Creator: etienne_sf
 9  
 // Last modified: $Date: 2015-03-29 19:48:44 +0200 (dim., 29 mars 2015) $
 10  
 //
 11  
 // This program is free software; you can redistribute it and/or modify it under
 12  
 // the terms of the GNU General Public License as published by the Free Software
 13  
 // Foundation; either version 2 of the License, or (at your option) any later
 14  
 // version. This program is distributed in the hope that it will be useful, but
 15  
 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 16  
 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 17  
 // details. You should have received a copy of the GNU General Public License
 18  
 // along with this program; if not, write to the Free Software Foundation, Inc.,
 19  
 // 675 Mass Ave, Cambridge, MA 02139, USA.
 20  
 
 21  
 package wjhk.jupload2.upload;
 22  
 
 23  
 import java.io.File;
 24  
 import java.io.IOException;
 25  
 import java.io.InputStream;
 26  
 import java.io.OutputStream;
 27  
 import java.util.Date;
 28  
 
 29  
 import wjhk.jupload2.exception.JUploadException;
 30  
 import wjhk.jupload2.exception.JUploadIOException;
 31  
 import wjhk.jupload2.exception.JUploadInterrupted;
 32  
 import wjhk.jupload2.filedata.FileData;
 33  
 import wjhk.jupload2.gui.filepanel.treeview.TreeFileDataNode;
 34  
 import wjhk.jupload2.policies.UploadPolicy;
 35  
 import wjhk.jupload2.upload.helper.ByteArrayEncoder;
 36  
 
 37  
 /**
 38  
  * This class implements the FileData interface, and is responsible to do the actual upload of the files.
 39  
  * 
 40  
  * @author etienne_sf
 41  
  */
 42  
 public class UploadFileData implements FileData {
 43  
 
 44  
     /**
 45  
      * The {@link FileData} instance that contains all information about the file to upload.
 46  
      */
 47  134
     private FileData fileData = null;
 48  
 
 49  
     /**
 50  
      * The value of the fileData InputStream. It's main use is to allow chunk upload, to reuse the previous InputStream,
 51  
      * that is: each chunk will start reading the stream where the previous one stopped.
 52  
      */
 53  134
     InputStream uploadInputStream = null;
 54  
 
 55  
     /**
 56  
      * Indicates the position of the file in the current upload (from 0 to max-1). It is mainly used by the
 57  
      * ProgressBarManager.updateUploadProgressBarText() method, to display the upload status to the user.
 58  
      */
 59  134
     int numOfFileInCurrentUpload = -1;
 60  
 
 61  
     // FIXME numOfFileInCurrentUpload should be from 1 to max
 62  
 
 63  
     /**
 64  
      * Instance of the fileUploadManagerThread. This allow this class to send feedback to the thread.
 65  
      * 
 66  
      * @see FileUploadManagerThread#nbBytesUploaded(long)
 67  
      */
 68  134
     private FileUploadManagerThread fileUploadManagerThread = null;
 69  
 
 70  
     /**
 71  
      * The number of bytes to upload, for this file (without the head and tail defined for the HTTP multipart body).
 72  
      */
 73  134
     private long uploadRemainingLength = -1;
 74  
 
 75  
     /**
 76  
      * The current {@link UploadPolicy}
 77  
      */
 78  134
     private UploadPolicy uploadPolicy = null;
 79  
 
 80  
     private final static int BUFLEN = 4096;
 81  
 
 82  
     /**
 83  
      * This field is no more static, as we could decide to upload two files simultaneously.
 84  
      */
 85  134
     private final byte readBuffer[] = new byte[BUFLEN];
 86  
 
 87  
     // /////////////////////////////////////////////////////////////////////////////////////////////////////
 88  
     // //////////////////////////////////// CONSTRUCTOR
 89  
     // ///////////////////////////////////////////
 90  
     // /////////////////////////////////////////////////////////////////////////////////////////////////////
 91  
 
 92  
     /**
 93  
      * Standard constructor for the UploadFileData class.
 94  
      * 
 95  
      * @param fileDataParam The file data the this instance must transmist.
 96  
      * @param numOfFileInCurrentUpload
 97  
      * @param fileUploadManagerThreadParam The current instance of {@link FileUploadThread}
 98  
      * @param uploadPolicyParam The current upload policy, instance of {@link UploadPolicy}
 99  
      */
 100  
     public UploadFileData(FileData fileDataParam, int numOfFileInCurrentUpload,
 101  134
             FileUploadManagerThread fileUploadManagerThreadParam, UploadPolicy uploadPolicyParam) {
 102  134
         if (fileDataParam == null && !(this instanceof UploadFileDataPoisonned)) {
 103  0
             throw new NullPointerException(
 104  
                     "fileData is null in UploadFileData(FileData, FileUploadManagerThread, UploadPolicy) constructor");
 105  
         }
 106  134
         this.fileData = fileDataParam;
 107  134
         this.numOfFileInCurrentUpload = numOfFileInCurrentUpload;
 108  134
         this.fileUploadManagerThread = fileUploadManagerThreadParam;
 109  134
         this.uploadPolicy = uploadPolicyParam;
 110  134
     }
 111  
 
 112  
     /**
 113  
      * This particular constructor is posted by the {@link FilePreparationThread} in the preparedFileQueue to indicate
 114  
      * that the last file has been prepared.
 115  
      * 
 116  
      * @param poisonned This parameter is here to avoid this constructor to be the default constructor. Its value must
 117  
      *            be 'true'.
 118  
      */
 119  0
     public UploadFileData(boolean poisonned) {
 120  0
         if (!poisonned) {
 121  0
             throw new IllegalArgumentException("poisonned must be true in UploadFileData(boolean) constructor");
 122  
         }
 123  0
     }
 124  
 
 125  
     /**
 126  
      * Get the number of files that are still to upload. It is initialized at the creation of the file, by a call to the
 127  
      * {@link FileData#getUploadLength()}. <BR>
 128  
      * <B>Note:</B> When the upload for this file is finish and you want to send it again (for instance the upload
 129  
      * failed, and you want to do a retry), you should not reuse this instance, but, instead, create a new
 130  
      * UploadFileData instance.
 131  
      * 
 132  
      * @return Number of bytes still to upload.
 133  
      * @see #getInputStream()
 134  
      */
 135  
     long getRemainingLength() {
 136  0
         return this.uploadRemainingLength;
 137  
     }
 138  
 
 139  
     /**
 140  
      * This methods writes the file data (see {@link FileData#getInputStream()} to the given outputStream (the output
 141  
      * toward the HTTP server).
 142  
      * 
 143  
      * @param outputStream The stream on which the data is to be written.
 144  
      * @param amount The number of bytes to write.
 145  
      * @throws JUploadException if an I/O error occurs.
 146  
      * @throws JUploadInterrupted Thrown when an interruption of the thread is detected.
 147  
      */
 148  
     void uploadFile(OutputStream outputStream, long amount) throws JUploadException, JUploadInterrupted {
 149  0
         if (this.uploadPolicy.getDebugLevel() >= 30) {
 150  0
             this.uploadPolicy.displayDebug("in UploadFileData.uploadFile (amount:" + amount + ", getUploadLength(): "
 151  0
                     + getUploadLength() + ")", 30);
 152  
         }
 153  
 
 154  
         // getInputStream will put a new fileInput in the inputStream attribute,
 155  
         // or leave it unchanged if it is not null.
 156  0
         InputStream inputStream = getInputStream();
 157  
 
 158  0
         while (amount > 0 && !this.fileUploadManagerThread.isUploadFinished()) {
 159  
             // Are we interrupted ?
 160  0
             if (Thread.interrupted()) {
 161  0
                 throw new JUploadInterrupted(getClass().getName() + ".uploadFile [" + this.getFileName() + "]",
 162  
                         this.uploadPolicy);
 163  
             }
 164  
 
 165  0
             int toread = (amount > BUFLEN) ? BUFLEN : (int) amount;
 166  0
             int towrite = 0;
 167  
 
 168  
             try {
 169  0
                 towrite = inputStream.read(this.readBuffer, 0, toread);
 170  0
             } catch (IOException e) {
 171  0
                 throw new JUploadIOException(e);
 172  0
             }
 173  0
             if (towrite > 0) {
 174  
                 try {
 175  0
                     outputStream.write(this.readBuffer, 0, towrite);
 176  0
                     this.fileUploadManagerThread.nbBytesUploaded(towrite, this);
 177  0
                     amount -= towrite;
 178  0
                     this.uploadRemainingLength -= towrite;
 179  
 
 180  
                     // For debug reason, I may need to simulate upload, that are
 181  
                     // on a real network. We then slow down the upload. This can
 182  
                     // occurs only when given a 'high' debugLevel (higher than
 183  
                     // what can be set with the applet GUI.
 184  0
                     if (this.uploadPolicy.getDebugLevel() > 100) {
 185  
                         try {
 186  0
                             Thread.sleep(20);
 187  0
                         } catch (InterruptedException e) {
 188  
                             // Nothing to do. We'll just take a look at the loop
 189  
                             // condition.
 190  0
                         }
 191  
                     }
 192  0
                 } catch (IOException ioe) {
 193  0
                     throw new JUploadIOException(this.getClass().getName() + ".uploadFile()", ioe);
 194  0
                 } catch (Exception e) {
 195  
                     // When the user may not override an existing file, I got a
 196  
                     // NullPointerException. Let's trap all errors here.
 197  0
                     throw new JUploadException(this.getClass().getName()
 198  
                             + ".uploadFile()  (check the user permission on the server)", e);
 199  0
                 }
 200  
             }
 201  0
         }// while
 202  0
     }
 203  
 
 204  
     /**
 205  
      * Clean any local resources, then transmit to the encapsulated {@link FileData#afterUpload()}.
 206  
      * 
 207  
      * @see FileData#afterUpload()
 208  
      */
 209  
     public void afterUpload() {
 210  0
         if (this.uploadInputStream != null) {
 211  
             try {
 212  0
                 this.uploadInputStream.close();
 213  0
             } catch (IOException ioe) {
 214  
                 // Let's ignore it.
 215  0
             }
 216  0
             this.uploadInputStream = null;
 217  
         }
 218  
         // Transmission to the 'real' FileData
 219  0
         this.fileData.afterUpload();
 220  0
     }
 221  
 
 222  
     /**
 223  
      * Clean any local resources, to allow retrying to upload this file. The file remains prepared..
 224  
      */
 225  
     public void beforeRetry() {
 226  
         // Reset of our upload counter.
 227  0
         this.uploadRemainingLength = this.fileData.getUploadLength();
 228  
 
 229  0
         if (this.uploadInputStream != null) {
 230  
             try {
 231  0
                 this.uploadInputStream.close();
 232  0
             } catch (IOException ioe) {
 233  
                 // Let's ignore it.
 234  0
                 this.uploadPolicy.displayDebug("[Warning] Ignoring " + ioe.getClass().getName()
 235  0
                         + " in UploadFileData.beforeRetry() [" + ioe.getMessage() + "]", 30);
 236  0
             }
 237  0
             this.uploadInputStream = null;
 238  
         }
 239  0
     }
 240  
 
 241  
     /** {@inheritDoc} */
 242  
     public void appendFileProperties(ByteArrayEncoder bae, int index) throws JUploadIOException {
 243  0
         this.fileData.appendFileProperties(bae, index);
 244  0
     }
 245  
 
 246  
     /** {@inheritDoc} */
 247  
     public void beforeUpload(String uploadFileRoot) throws JUploadException {
 248  7
         this.fileData.beforeUpload(uploadFileRoot);
 249  
 
 250  
         // Calculation of some internal variables.
 251  7
         this.uploadRemainingLength = this.fileData.getUploadLength();
 252  7
     }
 253  
 
 254  
     /** {@inheritDoc} */
 255  
     public boolean canRead() {
 256  0
         return this.fileData.canRead();
 257  
     }
 258  
 
 259  
     /** {@inheritDoc} */
 260  
     public String getDirectory() {
 261  0
         return this.fileData.getDirectory();
 262  
     }
 263  
 
 264  
     /** {@inheritDoc} */
 265  
     public File getFile() {
 266  0
         throw new IllegalAccessError("Internal error: getFile is deprecated and should not be called from "
 267  0
                 + this.getClass().getName());
 268  
     }
 269  
 
 270  
     /** {@inheritDoc} */
 271  
     public String getFileExtension() {
 272  0
         return this.fileData.getFileExtension();
 273  
     }
 274  
 
 275  
     /** {@inheritDoc} */
 276  
     public long getFileLength() {
 277  0
         return this.fileData.getFileLength();
 278  
     }
 279  
 
 280  
     /** {@inheritDoc} */
 281  
     public String getFileName() {
 282  12
         return this.fileData.getFileName();
 283  
     }
 284  
 
 285  
     /** {@inheritDoc} */
 286  
     public InputStream getInputStream() throws JUploadException {
 287  0
         if (this.uploadInputStream == null) {
 288  0
             this.uploadInputStream = this.fileData.getInputStream();
 289  
         }
 290  0
         return this.uploadInputStream;
 291  
     }
 292  
 
 293  
     /** {@inheritDoc} */
 294  
     public Date getLastModified() {
 295  0
         return this.fileData.getLastModified();
 296  
     }
 297  
 
 298  
     /** {@inheritDoc} */
 299  
     public String getMD5() throws JUploadException {
 300  0
         return this.fileData.getMD5();
 301  
     }
 302  
 
 303  
     /** {@inheritDoc} */
 304  
     public String getMimeType() {
 305  0
         return this.fileData.getMimeType();
 306  
     }
 307  
 
 308  
     /** {@inheritDoc} */
 309  
     public String getRelativeDir() {
 310  0
         return this.fileData.getRelativeDir();
 311  
     }
 312  
 
 313  
     /** {@inheritDoc} */
 314  
     public String getAbsolutePath() {
 315  0
         return this.fileData.getAbsolutePath();
 316  
     }
 317  
 
 318  
     /**
 319  
      * Retrieves the file name, that should be used in the server application. Default is to send the original filename.
 320  
      * 
 321  
      * @param index The index of this file in the current request to the server.
 322  
      * @return The real file name. Not used in FTP upload.
 323  
      * @throws JUploadException Thrown when an error occurs.
 324  
      * @see UploadPolicy#getUploadFilename(FileData, int)
 325  
      */
 326  
     public String getUploadFilename(int index) throws JUploadException {
 327  0
         return this.uploadPolicy.getUploadFilename(this.fileData, index);
 328  
     }
 329  
 
 330  
     /**
 331  
      * Retrieves the upload file name, that should be sent to the server. It's the technical name used to retrieve the
 332  
      * file content. Default is File0, File1... This method just calls the
 333  
      * {@link UploadPolicy#getUploadFilename(FileData, int)} method.
 334  
      * 
 335  
      * @param index The index of this file in the current request to the server.
 336  
      * @return The technical upload file name. Not used in FTP upload.
 337  
      * @throws JUploadException
 338  
      * @see UploadPolicy#getUploadName(FileData, int)
 339  
      */
 340  
     public String getUploadName(int index) throws JUploadException {
 341  0
         return this.uploadPolicy.getUploadName(this.fileData, index);
 342  
     }
 343  
 
 344  
     /**
 345  
      * This methods stores locally the upload length. So, on the contrary of the {@link FileData} interface, this method
 346  
      * may be called after {@link #afterUpload()}, at one condition: that it has been called once before
 347  
      * {@link #afterUpload()} is called.
 348  
      * 
 349  
      * @see FileData#getUploadLength()
 350  
      */
 351  
     public long getUploadLength() {
 352  224
         return this.fileData.getUploadLength();
 353  
     }
 354  
 
 355  
     /** {@inheritDoc} */
 356  
     public boolean isPreparedForUpload() {
 357  0
         return this.fileData.isPreparedForUpload();
 358  
     }
 359  
 
 360  
     /**
 361  
      * @return the poisonned status. Returns always false, as this instance is a true one. false indicates the 'End Of
 362  
      *         Queue' marker in the preparedFileQueue, which is not the case here
 363  
      * @see UploadFileDataPoisonned
 364  
      */
 365  
     public boolean isPoisonned() {
 366  8
         return false;
 367  
     }
 368  
 
 369  
     /**
 370  
      * @return the numOfFileInCurrentUpload
 371  
      */
 372  
     public int getNumOfFileInCurrentUpload() {
 373  0
         return numOfFileInCurrentUpload;
 374  
     }
 375  
 
 376  
     /** The upload floag is not managed here. This method alread returns true */
 377  
     public boolean getUploadFlag() {
 378  
         // TODO Auto-generated method stub
 379  0
         return false;
 380  
     }
 381  
 
 382  
     /**
 383  
      * The upload floag is not managed here. This method should not be called.
 384  
      * 
 385  
      * @throws IllegalArgumentException when uploadFlag is false.
 386  
      */
 387  
     public void setUploadFlag(boolean uploadFlag) {
 388  0
         if (!uploadFlag) {
 389  0
             throw new IllegalArgumentException(
 390  
                     "This method should not be called. At least, it may not be called with uploadeFlag=false");
 391  
         }
 392  0
     }
 393  
 
 394  
     public TreeFileDataNode getTreeFileDataNode() {
 395  0
         return fileData.getTreeFileDataNode();
 396  
     }
 397  
 
 398  
     public void setTreeFileDataNode(TreeFileDataNode node) {
 399  0
         throw new IllegalStateException("setTreeFileDataNode may not be called againts a " + this.getClass().getName());
 400  
     }
 401  
 }