View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.http.protocol;
22  
23  import org.waarp.common.database.data.AbstractDbData.UpdatedInfo;
24  import org.waarp.common.database.exception.WaarpDatabaseException;
25  import org.waarp.common.digest.FilesystemBasedDigest;
26  import org.waarp.common.digest.FilesystemBasedDigest.DigestAlgo;
27  import org.waarp.common.logging.WaarpLogger;
28  import org.waarp.common.logging.WaarpLoggerFactory;
29  import org.waarp.common.utility.WaarpSystemUtil;
30  import org.waarp.http.protocol.servlet.HttpAuthent;
31  import org.waarp.openr66.context.ErrorCode;
32  import org.waarp.openr66.context.R66BusinessInterface;
33  import org.waarp.openr66.context.R66FiniteDualStates;
34  import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
35  import org.waarp.openr66.database.data.DbTaskRunner;
36  import org.waarp.openr66.protocol.configuration.Configuration;
37  
38  import java.io.File;
39  import java.io.FileInputStream;
40  import java.io.IOException;
41  import java.io.OutputStream;
42  
43  import static org.waarp.openr66.context.R66FiniteDualStates.*;
44  
45  /**
46   * Http Resumable session
47   */
48  public class HttpDownloadSession extends HttpSessionAbstract {
49    private static final WaarpLogger logger =
50        WaarpLoggerFactory.getLogger(HttpDownloadSession.class);
51    protected static final String HASH = "HASH";
52    private final String filename;
53    private final String rulename;
54    private final String comment;
55    private final String identifier;
56    private final String finalFilename;
57    private final long filesize;
58    private final DbTaskRunner runner;
59  
60    /**
61     * Constructor for an Http Download Session
62     *
63     * @param filename
64     * @param rulename
65     * @param identifier
66     * @param comment
67     * @param authent already initialized
68     *
69     * @throws IllegalArgumentException if something wrong happens
70     */
71    public HttpDownloadSession(final String filename, final String rulename,
72                               final String identifier, final String comment,
73                               final HttpAuthent authent)
74        throws IllegalArgumentException {
75      super(authent);
76      this.filename = filename;
77      this.rulename = rulename;
78      this.comment = comment;
79      this.identifier = identifier;
80      if (!WaarpSystemUtil.isJunit()) {
81        final R66BusinessInterface business =
82            checkAuthentR66Business(this, session, authent);
83        runner =
84            getDbTaskRunner(authent.getUserId(), rulename, identifier, comment,
85                            business);
86        final File file = session.getFile().getTrueFile();
87        this.finalFilename = identifier + "_" + file.getName();
88        filesize = file.length();
89        logger.info("START: {}", this);
90        preTasks(business, runner);
91      } else {
92        runner = null;
93        this.finalFilename = "NOID" + "_" + filename;
94        this.filesize = 0;
95      }
96    }
97  
98    /**
99     * Constructor for reading from database only
100    *
101    * @param identifier
102    * @param authent
103    */
104   public HttpDownloadSession(final String identifier, final HttpAuthent authent)
105       throws WaarpDatabaseException {
106     super(authent);
107     this.filename = "nofile";
108     this.rulename = "norule";
109     this.comment = "nocomment";
110     this.identifier = identifier;
111     if (!WaarpSystemUtil.isJunit()) {
112       final R66BusinessInterface business =
113           checkAuthentR66Business(this, session, authent);
114 
115       runner = getDbTaskRunner(authent.getUserId(), identifier);
116       this.finalFilename = identifier + "_" + filename;
117     } else {
118       runner = null;
119       this.finalFilename = "NOID" + "_" + filename;
120     }
121     this.filesize = 0;
122   }
123 
124   public final boolean isFinished() {
125     return runner != null && runner.isFinished();
126   }
127 
128   public final boolean isTransmitting() {
129     return runner != null &&
130            runner.getUpdatedInfo().equals(UpdatedInfo.RUNNING) &&
131            this.filesize == 0;
132   }
133 
134   /**
135    * Initialize the DbTaskRunner for this user and rulename
136    *
137    * @param user
138    * @param rulename
139    * @param identifier
140    * @param comment
141    * @param business
142    *
143    * @return the DbTaskRunner, potentially new
144    *
145    * @throws IllegalArgumentException
146    */
147   private DbTaskRunner getDbTaskRunner(final String user, final String rulename,
148                                        final String identifier,
149                                        final String comment,
150                                        final R66BusinessInterface business)
151       throws IllegalArgumentException {
152     final long specialId = Long.parseLong(identifier);
153     return getDbTaskRunner(user, filename, rulename, specialId, comment,
154                            Configuration.configuration.getBlockSize(), business,
155                            false);
156   }
157 
158   /**
159    * Reload the DbTaskRunner for this user and rulename
160    *
161    * @param user
162    * @param identifier
163    *
164    * @return the DbTaskRunner, potentially new
165    *
166    * @throws IllegalArgumentException
167    */
168   private DbTaskRunner getDbTaskRunner(final String user,
169                                        final String identifier)
170       throws IllegalArgumentException, WaarpDatabaseException {
171     final long specialId = Long.parseLong(identifier);
172     final String requested = Configuration.configuration.getHostId();
173     DbTaskRunner runner = null;
174 
175     // Try to reload it
176     try {
177       logger.debug("{} {} <-> {}", specialId, user, requested);
178       runner = new DbTaskRunner(specialId, user, requested);
179     } catch (final WaarpDatabaseException e) {
180       logger.debug("{} {} {}", specialId, user, requested);
181       runner = new DbTaskRunner(specialId, requested, user);
182     }
183     runner.setSender(true);
184     try {
185       session.setRunner(runner);
186     } catch (final OpenR66RunnerErrorException e) {
187       logger.debug(e);
188     }
189     session.setReady(true);
190     return runner;
191   }
192 
193   /**
194    * @return the final name for download
195    */
196   public final String getFinalName() {
197     return finalFilename;
198   }
199 
200   /**
201    * @return the original file size
202    */
203   public final long getFileSize() {
204     return filesize;
205   }
206 
207   /**
208    * @return the identifier
209    */
210   public final String getIdentifier() {
211     return identifier;
212   }
213 
214   /**
215    * @return the corresponding Hash (SHA 256) or null if not possible
216    */
217   public final String getHash() {
218     final File file = session.getFile().getTrueFile();
219     final byte[] bin;
220     try {
221       bin = FilesystemBasedDigest.getHash(file, false, DigestAlgo.SHA256);
222     } catch (final IOException e) {
223       logger.warn(e);
224       return null;
225     }
226     final String hash = FilesystemBasedDigest.getHex(bin);
227     session.getRunner().addToTransferMap(HASH, hash);
228     try {
229       session.getRunner().saveStatus();
230     } catch (final OpenR66RunnerErrorException e) {
231       logger.debug(e);
232     }
233     return hash;
234   }
235 
236   /**
237    * @return the previously hash if computed, else null
238    */
239   public final String getComputedHadh() {
240     final String hash = (String) session.getRunner().getFromTransferMap(HASH);
241     logger.debug("Found {}", hash);
242     return hash;
243   }
244 
245   @Override
246   public final void error(final Exception e,
247                           final R66BusinessInterface business)
248       throws IllegalArgumentException {
249     logger.error(e.getMessage());
250     if (business != null) {
251       business.checkAtError(session);
252     }
253     session.newState(R66FiniteDualStates.ERROR);
254     throw new IllegalArgumentException(e);
255   }
256 
257   /**
258    * Try to read and return all bytes from file
259    *
260    * @param stream
261    *
262    * @return true if OK
263    *
264    * @throws IOException
265    */
266   public final boolean tryWrite(final OutputStream stream) throws IOException {
267     if (!session.isAuthenticated() || !session.isReady()) {
268       logger.error("Not authenticated or not Ready");
269       return false;
270     }
271     write(stream);
272     return true;
273   }
274 
275   /**
276    * Real write from the final file
277    *
278    * @param stream
279    *
280    * @throws IOException
281    */
282   private void write(final OutputStream stream) throws IOException {
283     startTransfer(runner);
284     final File file = session.getFile().getTrueFile();
285     final FileInputStream inputStream = new FileInputStream(file);
286     try {
287       final byte[] bytes = new byte[Configuration.configuration.getBlockSize()];
288       while (true) {
289         final int r = inputStream.read(bytes);
290         if (r < 0) {
291           break;
292         }
293         stream.write(bytes, 0, r);
294         session.getRunner().incrementRank();
295       }
296     } catch (final IOException e) {
297       session.getRunner().setErrorExecutionStatus(ErrorCode.TransferError);
298       session.getRunner().setErrorTask();
299       try {
300         session.getRunner().run();
301         session.getRunner().saveStatus();
302       } catch (final OpenR66RunnerErrorException ex) {
303         logger.info(e);
304       }
305       session.newState(R66FiniteDualStates.ERROR);
306       throw e;
307     } finally {
308       inputStream.close();
309       stream.flush();
310       stream.close();
311     }
312 
313   }
314 
315   /**
316    * Finalize the current download
317    */
318   public final void downloadFinished() {
319     if (session.getState() == CLOSEDCHANNEL) {
320       return;
321     }
322     logger.debug("Final block received! {}", session);
323     runPostTask();
324     authent.finalizeTransfer(this, session);
325   }
326 
327   @Override
328   public String toString() {
329     return "DS: {" + session.toString() + ", " + filename + ", " + rulename +
330            ", " + identifier + ", " + comment + ", " + finalFilename + "}";
331   }
332 }