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  package org.waarp.openr66.database.data;
21  
22  import com.fasterxml.jackson.databind.JsonNode;
23  import com.fasterxml.jackson.databind.node.ArrayNode;
24  import com.fasterxml.jackson.databind.node.ObjectNode;
25  import org.dom4j.Document;
26  import org.dom4j.DocumentException;
27  import org.dom4j.DocumentHelper;
28  import org.dom4j.Element;
29  import org.dom4j.Node;
30  import org.dom4j.io.OutputFormat;
31  import org.dom4j.io.SAXReader;
32  import org.dom4j.io.XMLWriter;
33  import org.dom4j.tree.DefaultElement;
34  import org.waarp.common.command.exception.CommandAbstractException;
35  import org.waarp.common.database.DbConstant;
36  import org.waarp.common.database.DbPreparedStatement;
37  import org.waarp.common.database.DbSession;
38  import org.waarp.common.database.data.AbstractDbData;
39  import org.waarp.common.database.exception.WaarpDatabaseException;
40  import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
41  import org.waarp.common.database.exception.WaarpDatabaseNoDataException;
42  import org.waarp.common.database.exception.WaarpDatabaseSqlException;
43  import org.waarp.common.digest.FilesystemBasedDigest;
44  import org.waarp.common.file.DirInterface;
45  import org.waarp.common.file.FileUtils;
46  import org.waarp.common.json.JsonHandler;
47  import org.waarp.common.logging.WaarpLogger;
48  import org.waarp.common.logging.WaarpLoggerFactory;
49  import org.waarp.common.lru.SynchronizedLruCache;
50  import org.waarp.common.utility.ParametersChecker;
51  import org.waarp.common.utility.WaarpStringUtils;
52  import org.waarp.common.xml.XmlUtil;
53  import org.waarp.openr66.context.ErrorCode;
54  import org.waarp.openr66.context.R66FiniteDualStates;
55  import org.waarp.openr66.context.R66Result;
56  import org.waarp.openr66.context.R66Session;
57  import org.waarp.openr66.context.filesystem.R66Dir;
58  import org.waarp.openr66.context.filesystem.R66File;
59  import org.waarp.openr66.context.task.AbstractTask;
60  import org.waarp.openr66.context.task.TaskType;
61  import org.waarp.openr66.context.task.exception.OpenR66RunnerEndTasksException;
62  import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
63  import org.waarp.openr66.context.task.exception.OpenR66RunnerException;
64  import org.waarp.openr66.dao.AbstractDAO;
65  import org.waarp.openr66.dao.DAOFactory;
66  import org.waarp.openr66.dao.Filter;
67  import org.waarp.openr66.dao.TransferDAO;
68  import org.waarp.openr66.dao.database.DBTransferDAO;
69  import org.waarp.openr66.dao.database.StatementExecutor;
70  import org.waarp.openr66.dao.exception.DAOConnectionException;
71  import org.waarp.openr66.dao.exception.DAONoDataException;
72  import org.waarp.openr66.dao.xml.XMLTransferDAO;
73  import org.waarp.openr66.pojo.Transfer;
74  import org.waarp.openr66.protocol.configuration.Configuration;
75  import org.waarp.openr66.protocol.configuration.PartnerConfiguration;
76  import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessException;
77  import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoSslException;
78  import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
79  import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
80  import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
81  import org.waarp.openr66.protocol.localhandler.LocalTransaction;
82  import org.waarp.openr66.protocol.localhandler.packet.ErrorPacket;
83  import org.waarp.openr66.protocol.localhandler.packet.RequestPacket;
84  import org.waarp.openr66.protocol.localhandler.packet.RequestPacket.TRANSFERMODE;
85  import org.waarp.openr66.protocol.networkhandler.NetworkServerHandler;
86  import org.waarp.openr66.protocol.utils.ChannelUtils;
87  import org.waarp.openr66.protocol.utils.NbAndSpecialId;
88  import org.waarp.openr66.protocol.utils.R66Future;
89  import org.xml.sax.SAXException;
90  
91  import java.io.File;
92  import java.io.FileNotFoundException;
93  import java.io.FileOutputStream;
94  import java.io.IOException;
95  import java.io.OutputStream;
96  import java.io.UnsupportedEncodingException;
97  import java.sql.SQLException;
98  import java.sql.Timestamp;
99  import java.sql.Types;
100 import java.util.ArrayList;
101 import java.util.List;
102 import java.util.Map;
103 import java.util.regex.Matcher;
104 import java.util.regex.Pattern;
105 
106 import static org.waarp.common.database.DbConstant.*;
107 import static org.waarp.openr66.client.TransferArgs.*;
108 
109 /**
110  * Task Runner from pre operation to transfer to post operation, except in case
111  * of error
112  */
113 public class DbTaskRunner extends AbstractDbDataDao<Transfer> {
114   private static final String TRANSFER_NOT_FOUND = "Transfer not found";
115 
116   public static final String JSON_ORIGINALSIZE = "ORIGINALSIZE";
117 
118   public static final String JSON_THROUGHMODE = "THROUGHMODE";
119 
120   public static final String JSON_RESCHEDULE = "RESCHEDULE";
121 
122   public static final String JSON_COMPRESSION = "COMPRESSION";
123 
124   /**
125    * Internal Logger
126    */
127   private static final WaarpLogger logger =
128       WaarpLoggerFactory.getLogger(DbTaskRunner.class);
129   private static final String GETTING_VALUES_IN_ERROR =
130       "Getting values in error";
131   private static final String CANNOT_WRITE_XML_FILE = "Cannot write XML file";
132   private static final String UNSUPPORTED_ENCODING = "Unsupported Encoding";
133   private static final String CANNOT_DELETE_WRONG_XML_FILE =
134       "Cannot delete wrong XML file";
135   private static final String FOLLOW_ID_LIKE = "\"" + FOLLOW_JSON_KEY + "\":";
136   public static final String AND = " AND ";
137   public static final String SELECT_COUNT = "SELECT COUNT(";
138   public static final String TRACE_FOR_ERROR = "Trace for error";
139   /**
140    * HashTable in case of lack of database using LRU mode with 20 000 items
141    * maximum (< 100 KB?) for 180s
142    */
143   private static SynchronizedLruCache<Long, Boolean> dbR66TaskHashMap;
144 
145   /**
146    * Create the LRU cache
147    *
148    * @param limit limit of number of entries in the cache
149    * @param ttl time to leave used
150    */
151   public static void createLruCache(final int limit, final long ttl) {
152     XMLTransferDAO.createLruCache(limit, ttl);
153     dbR66TaskHashMap = new SynchronizedLruCache<Long, Boolean>(limit, ttl);
154   }
155 
156   public static String hashStatus() {
157     return XMLTransferDAO.hashStatus();
158   }
159 
160   /**
161    * To enable clear of oldest entries in the cache
162    *
163    * @return the number of elements removed
164    */
165   public static int clearCache() {
166     dbR66TaskHashMap.forceClearOldest();
167     return XMLTransferDAO.clearCache();
168   }
169 
170   /**
171    * To update the TTL for the cache (to 10xTIMEOUT)
172    *
173    * @param ttl
174    */
175   public static void updateLruCacheTimeout(final long ttl) {
176     XMLTransferDAO.updateLruCacheTimeout(ttl);
177   }
178 
179   public enum Columns {
180     SPECIALID, GLOBALSTEP, GLOBALLASTSTEP, STEP, RANK, BLOCKSZ, MODETRANS,
181     UPDATEDINFO, STEPSTATUS, INFOSTATUS, RETRIEVEMODE, ISMOVED, STARTTRANS,
182     STOPTRANS, OWNERREQ, REQUESTER, REQUESTED, IDRULE, FILENAME, ORIGINALNAME,
183     FILEINFO, TRANSFERINFO
184   }
185 
186   public static final int[] dbTypes = {
187       Types.BIGINT, Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER,
188       Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.CHAR, Types.CHAR,
189       Types.BIT, Types.BIT, Types.TIMESTAMP, Types.TIMESTAMP, Types.NVARCHAR,
190       Types.NVARCHAR, Types.NVARCHAR, Types.NVARCHAR, Types.VARCHAR,
191       Types.VARCHAR, Types.VARCHAR, Types.VARCHAR
192   };
193 
194   public static final String table = " RUNNER ";
195 
196   public static final String fieldseq = "RUNSEQ";
197 
198   public static final String[] indexesNames = {
199       "IDX_PUSH_FILTER", "IDX_RUN_FILTER", "IDX_MON_FILTER"
200   };
201   public static final Columns[][] indexes = {
202       // Push
203       {
204           Columns.STOPTRANS, Columns.OWNERREQ
205       },
206       // Run
207       {
208           Columns.UPDATEDINFO, Columns.OWNERREQ, Columns.STARTTRANS
209       },
210       // Mon
211       {
212           Columns.GLOBALSTEP, Columns.OWNERREQ, Columns.STARTTRANS
213       }
214   };
215 
216   /**
217    * Special For DbTaskRunner
218    */
219   public static final String[] PRIMARY_KEY = {
220       Columns.REQUESTED.name(), Columns.REQUESTER.name(),
221       Columns.OWNERREQ.name(), Columns.SPECIALID.name()
222   };
223 
224   public static final String XMLRUNNERS = "taskrunners";
225   public static final String XMLRUNNER = "runner";
226   public static final String XMLEXTENSION = "_singlerunner.xml";
227 
228   /**
229    * GlobalStep Bounds
230    */
231   public enum TASKSTEP {
232     NOTASK, PRETASK, TRANSFERTASK, POSTTASK, ALLDONETASK, ERRORTASK
233   }
234 
235   // Values
236   private DbRule rule;
237 
238   private R66Session session;
239 
240   private boolean continueTransfer = true;
241 
242   private boolean rescheduledTransfer;
243 
244   private LocalChannelReference localChannelReference;
245 
246   private boolean isRecvThrough;
247   private boolean isSendThrough;
248   private long originalSize = -1;
249   private boolean isOtherThanStatus = true;
250   private Map<String, Object> transferMap = null;
251 
252   // ALL TABLE SHOULD IMPLEMENT THIS
253 
254   protected static final String selectAllFields = " * ";
255 
256   @Override
257   protected final void initObject() {
258     // Nothing
259   }
260 
261   @Override
262   protected final String getTable() {
263     return table;
264   }
265 
266   @Override
267   protected final AbstractDAO<Transfer> getDao(final boolean isCacheable)
268       throws DAOConnectionException {
269     return DAOFactory.getInstance().getTransferDAO();
270   }
271 
272   /**
273    * {@link UnsupportedOperationException}
274    *
275    * @return never
276    */
277   @Override
278   protected final String getPrimaryKey() {
279     throw new UnsupportedOperationException("Not correct for Transfer");
280   }
281 
282   /**
283    * {@link UnsupportedOperationException}
284    *
285    * @return never
286    */
287   @Override
288   protected final String getPrimaryField() {
289     throw new UnsupportedOperationException("Not correct for Transfer");
290   }
291 
292   /**
293    * @param session
294    * @param requestPacket
295    *
296    * @return The associated requested Host Id
297    */
298   public static String getRequested(final R66Session session,
299                                     final RequestPacket requestPacket) {
300     if (requestPacket.isToValidate()) {
301       // the request is initiated and sent by the requester
302       try {
303         return Configuration.configuration.getHostId(session.getAuth().isSsl());
304       } catch (final OpenR66ProtocolNoSslException e) {
305         return Configuration.configuration.getHostId();
306       }
307     } else {
308       // the request is sent after acknowledge by the requested
309       return session.getAuth().getUser();
310     }
311   }
312 
313   /**
314    * @param session
315    * @param requestPacket
316    *
317    * @return The associated requester Host Id
318    */
319   public static String getRequester(final R66Session session,
320                                     final RequestPacket requestPacket) {
321     if (requestPacket.isToValidate()) {
322       return session.getAuth().getUser();
323     } else {
324       try {
325         return Configuration.configuration.getHostId(session.getAuth().isSsl());
326       } catch (final OpenR66ProtocolNoSslException e) {
327         return Configuration.configuration.getHostId();
328       }
329     }
330   }
331 
332   public final void checkThroughMode() {
333     isRecvThrough = RequestPacket.isRecvThroughMode(pojo.getTransferMode(),
334                                                     isRequestOnRequested());
335     isSendThrough = RequestPacket.isSendThroughMode(pojo.getTransferMode(),
336                                                     isRequestOnRequested());
337 
338     if (localChannelReference != null) {
339       if (localChannelReference.isRecvThroughMode()) {
340         isRecvThrough = true;
341       }
342       if (localChannelReference.isSendThroughMode()) {
343         isSendThrough = true;
344       }
345       if (isRecvThrough && !localChannelReference.isRecvThroughMode()) {
346         // Cannot be a RecvThrough
347         isRecvThrough = false;
348       }
349       if (isSendThrough && !localChannelReference.isSendThroughMode()) {
350         isSendThrough = false;
351       }
352     }
353     logger.debug("DbTask {} isRecvThrough: {} isSendThrough: {}",
354                  pojo.getTransferMode(), isRecvThrough, isSendThrough);
355   }
356 
357   private void setStopNow() {
358     isSaved = false;
359     pojo.setStop(new Timestamp(System.currentTimeMillis()));
360   }
361 
362   public DbTaskRunner(final Transfer transfer) {
363     if (transfer == null) {
364       throw new IllegalArgumentException(
365           "Argument in constructor cannot be null");
366     }
367     this.pojo = transfer;
368     isSaved = false;
369     isOtherThanStatus = true;
370     initializeTransferInfo();
371     checkThroughMode();
372     checkMapInfo();
373   }
374 
375   /**
376    * Constructor for submission (no transfer session), from database. It is
377    * created, so with a new specialId if
378    * necessary
379    *
380    * @param rule
381    * @param isSender
382    * @param requestPacket
383    * @param requested
384    * @param startTime
385    *
386    * @throws WaarpDatabaseException
387    */
388   public DbTaskRunner(final DbRule rule, final boolean isSender,
389                       final RequestPacket requestPacket, final String requested,
390                       final Timestamp startTime) throws WaarpDatabaseException {
391     session = null;
392     this.rule = rule;
393 
394     if (startTime != null) {
395       pojo = new Transfer(requested, rule.getIdRule(), requestPacket.getMode(),
396                           isSender, requestPacket.getFilename(),
397                           requestPacket.getTransferInformation(),
398                           requestPacket.getBlocksize(), startTime);
399     } else {
400       pojo = new Transfer(requested, rule.getIdRule(), requestPacket.getMode(),
401                           isSender, requestPacket.getFilename(),
402                           requestPacket.getTransferInformation(),
403                           requestPacket.getBlocksize());
404     }
405 
406     // Usefull ?
407     pojo.setRank(requestPacket.getRank());
408     pojo.setId(requestPacket.getSpecialId());
409 
410     originalSize = requestPacket.getOriginalSize();
411     setOriginalSizeTransferMap(originalSize);
412     // itself but according to SSL
413     pojo.setRequester(Configuration.configuration.getHostId(requested));
414 
415     // Retrieve rule
416     this.rule = new DbRule(getRuleId());
417     if (requestPacket.getMode() != rule.getMode()) {
418       if (RequestPacket.isMD5Mode(requestPacket.getMode())) {
419         pojo.setTransferMode(RequestPacket.getModeMD5(rule.getMode()));
420       } else {
421         pojo.setTransferMode(rule.getMode());
422       }
423     }
424     checkMapInfo();
425     checkThroughMode();
426     insert();
427     requestPacket.setSpecialId(pojo.getId());
428   }
429 
430   /**
431    * Constructor from a request without a valid Special Id to be inserted into
432    * databases
433    *
434    * @param session
435    * @param rule
436    * @param isSender
437    * @param requestPacket
438    *
439    * @throws WaarpDatabaseException
440    */
441   public DbTaskRunner(final R66Session session, final DbRule rule,
442                       final boolean isSender, final RequestPacket requestPacket)
443       throws WaarpDatabaseException {
444     this.session = session;
445     localChannelReference = session.getLocalChannelReference();
446     this.rule = rule;
447 
448     pojo = new Transfer(getRequested(session, requestPacket), rule.getIdRule(),
449                         requestPacket.getMode(), isSender,
450                         requestPacket.getFilename(),
451                         requestPacket.getTransferInformation(),
452                         requestPacket.getBlocksize());
453     pojo.setRequester(getRequester(session, requestPacket));
454     pojo.setRank(requestPacket.getRank());
455     if (requestPacket.getSpecialId() != ILLEGALVALUE) {
456       pojo.setId(requestPacket.getSpecialId());
457     }
458     originalSize = requestPacket.getOriginalSize();
459     setOriginalSizeTransferMap(originalSize);
460     checkMapInfo();
461     checkThroughMode();
462     insert();
463     requestPacket.setSpecialId(pojo.getId());
464   }
465 
466   /**
467    * Constructor from a request with a valid Special Id so loaded from
468    * database
469    *
470    * @param session
471    * @param rule
472    * @param id
473    * @param requester
474    * @param requested
475    *
476    * @throws WaarpDatabaseException
477    */
478   public DbTaskRunner(final R66Session session, final DbRule rule,
479                       final long id, final String requester,
480                       final String requested) throws WaarpDatabaseException {
481     this.session = session;
482     TransferDAO transferAccess = null;
483     try {
484       transferAccess = DAOFactory.getInstance().getTransferDAO();
485       pojo = transferAccess.select(id, requester, requested,
486                                    Configuration.configuration.getHostId());
487       addMap();
488     } catch (final DAOConnectionException e) {
489       if (Configuration.configuration.isShutdown()) {
490         // ignore since shutdown
491         throw new WaarpDatabaseException(
492             "Shutdown on going so database not accessible");
493       }
494       throw new WaarpDatabaseException(e);
495     } catch (final DAONoDataException e) {
496       if (Configuration.configuration.isShutdown()) {
497         // ignore since shutdown
498         throw new WaarpDatabaseException(
499             "Shutdown on going so database not accessible");
500       }
501       throw new WaarpDatabaseNoDataException(TRANSFER_NOT_FOUND, e);
502     } finally {
503       DAOFactory.closeDAO(transferAccess);
504     }
505     this.rule = new DbRule(getRuleId());
506     if (rule != null && !pojo.getRule().equals(rule.getIdRule())) {
507       throw new WaarpDatabaseNoDataException("Rule does not correspond");
508     }
509     initializeTransferInfo();
510     checkThroughMode();
511     isSaved = true;
512     isOtherThanStatus = false;
513   }
514 
515   /**
516    * Minimal constructor from database
517    *
518    * @param id
519    * @param requester
520    * @param requested
521    *
522    * @throws WaarpDatabaseException
523    */
524   public DbTaskRunner(final long id, final String requester,
525                       final String requested) throws WaarpDatabaseException {
526     TransferDAO transferAccess = null;
527     try {
528       transferAccess = DAOFactory.getInstance().getTransferDAO();
529       pojo = transferAccess.select(id, requester, requested,
530                                    Configuration.configuration.getHostId());
531       addMap();
532     } catch (final DAOConnectionException e) {
533       throw new WaarpDatabaseException(e);
534     } catch (final DAONoDataException e) {
535       throw new WaarpDatabaseNoDataException(TRANSFER_NOT_FOUND, e);
536     } finally {
537       DAOFactory.closeDAO(transferAccess);
538     }
539     rule = new DbRule(getRuleId());
540     initializeTransferInfo();
541     checkThroughMode();
542     isSaved = true;
543     isOtherThanStatus = false;
544   }
545 
546   /**
547    * Minimal constructor from database
548    *
549    * @param id
550    * @param requester
551    * @param requested
552    *
553    * @throws WaarpDatabaseException
554    */
555   public DbTaskRunner(final long id, final String requester,
556                       final String requested, final String owner)
557       throws WaarpDatabaseException {
558     this(id, requester, requested);
559     if (ParametersChecker.isEmpty(owner)) {
560       pojo.setOwnerRequest(Configuration.configuration.getHostId());
561     } else {
562       pojo.setOwnerRequest(owner);
563     }
564     initializeTransferInfo();
565   }
566 
567   /**
568    * To create a new DbTaskRunner (specialId could be invalid) without making
569    * any entry in the database
570    *
571    * @param source
572    *
573    * @throws WaarpDatabaseException
574    */
575   public DbTaskRunner(final ObjectNode source) throws WaarpDatabaseException {
576     pojo = new Transfer();
577     setFromJson(source, false);
578   }
579 
580   private final void checkMapInfo() {
581     if (getFileInformation() != null) {
582       setMapFromFileInfo();
583     } else {
584       initializeTransferInfo();
585     }
586   }
587 
588   @Override
589   public final void setFromJson(final ObjectNode source,
590                                 final boolean ignorePrimaryKey)
591       throws WaarpDatabaseSqlException {
592     if (pojo == null) {
593       pojo = new Transfer();
594     }
595     for (final Columns column : Columns.values()) {
596       if (column == Columns.UPDATEDINFO) {
597         continue;
598       }
599       final JsonNode item = source.get(column.name());
600       if (item != null && !item.isMissingNode() && !item.isNull()) {
601         switch (column) {
602           case BLOCKSZ:
603             pojo.setBlockSize(item.asInt());
604             break;
605           case FILEINFO:
606             pojo.setFileInfo(item.asText());
607             break;
608           case FILENAME:
609             pojo.setFilename(item.asText());
610             break;
611           case GLOBALLASTSTEP:
612             pojo.setLastGlobalStep(Transfer.TASKSTEP.valueOf(item.asInt()));
613             break;
614           case GLOBALSTEP:
615             pojo.setGlobalStep(Transfer.TASKSTEP.valueOf(item.asInt()));
616             break;
617           case IDRULE:
618             pojo.setRule(item.asText());
619             break;
620           case INFOSTATUS:
621             pojo.setInfoStatus(ErrorCode.getFromCode(item.asText()));
622             break;
623           case ISMOVED:
624             pojo.setIsMoved(item.asBoolean());
625             break;
626           case MODETRANS:
627             pojo.setTransferMode(item.asInt());
628             break;
629           case ORIGINALNAME:
630             pojo.setOriginalName(item.asText());
631             break;
632           case OWNERREQ:
633             String owner = item.asText();
634             if (ParametersChecker.isEmpty(owner)) {
635               owner = Configuration.configuration.getHostId();
636             }
637             pojo.setOwnerRequest(owner);
638             break;
639           case RANK:
640             pojo.setRank(item.asInt());
641             break;
642           case REQUESTED:
643             pojo.setRequested(item.asText());
644             break;
645           case REQUESTER:
646             pojo.setRequester(item.asText());
647             break;
648           case RETRIEVEMODE:
649             pojo.setRetrieveMode(item.asBoolean());
650             break;
651           case SPECIALID:
652             pojo.setId(item.asLong());
653             break;
654           case STARTTRANS:
655             long start = item.asLong();
656             if (start == 0) {
657               start = System.currentTimeMillis();
658             }
659             pojo.setStart(new Timestamp(start));
660             break;
661           case STEP:
662             pojo.setStep(item.asInt());
663             break;
664           case STEPSTATUS:
665             pojo.setStepStatus(ErrorCode.getFromCode(item.asText()));
666             break;
667           case STOPTRANS:
668             long stop = item.asLong();
669             if (stop == 0) {
670               stop = System.currentTimeMillis();
671             }
672             pojo.setStop(new Timestamp(stop));
673             break;
674           case TRANSFERINFO: {
675             String text = item.asText().trim();
676             if (text.isEmpty()) {
677               text = "{}";
678               if (transferMap != null) {
679                 transferMap.clear();
680               }
681               transferMap = null;
682             }
683             pojo.setTransferInfo(text);
684             break;
685           }
686           default:
687             break;
688         }
689       }
690     }
691     setMapFromFileInfo();
692     JsonNode node = source.path(JSON_RESCHEDULE);
693     if (!node.isMissingNode() || !node.isNull()) {
694       rescheduledTransfer = node.asBoolean(false);
695     }
696     node = source.path(JSON_THROUGHMODE);
697     if (!node.isMissingNode() || !node.isNull()) {
698       if (RequestPacket.isRecvMode(pojo.getTransferMode())) {
699         isRecvThrough = node.asBoolean();
700       } else {
701         isSendThrough = node.asBoolean();
702       }
703     }
704     node = source.path(JSON_ORIGINALSIZE);
705     if (!node.isMissingNode() || !node.isNull()) {
706       originalSize = node.asLong(getOriginalSizeTransferMap());
707     }
708     isSaved = false;
709     isOtherThanStatus = true;
710     try {
711       rule = new DbRule(getRuleId());
712     } catch (final WaarpDatabaseException e) {
713       // ignore
714       rule = null;
715     }
716     if (ParametersChecker.isEmpty(pojo.getFilename())) {
717       throw new WaarpDatabaseSqlException(
718           "Cannot create a transfer without filename");
719     } else if (ParametersChecker.isEmpty(pojo.getRule())) {
720       throw new WaarpDatabaseSqlException(
721           "Cannot create a transfer without rule");
722     } else if (ParametersChecker.isEmpty(pojo.getOwnerRequest())) {
723       throw new WaarpDatabaseSqlException(
724           "Cannot create a transfer without owner");
725     } else if (ParametersChecker.isEmpty(pojo.getRequester())) {
726       throw new WaarpDatabaseSqlException(
727           "Cannot create a transfer without requester");
728     } else if (ParametersChecker.isEmpty(pojo.getRequested())) {
729       throw new WaarpDatabaseSqlException(
730           "Cannot create a transfer without requested");
731     }
732     checkThroughMode();
733     checkValues();
734   }
735 
736   /**
737    * Constructor to initiate a request with a valid previous Special Id so
738    * loaded from database.
739    *
740    * @param id
741    * @param requested
742    *
743    * @throws WaarpDatabaseException
744    */
745   public DbTaskRunner(final long id, final String requested)
746       throws WaarpDatabaseException {
747     TransferDAO transferAccess = null;
748     try {
749       transferAccess = DAOFactory.getInstance().getTransferDAO();
750       final String requester = Configuration.configuration.getHostId(requested);
751       pojo = transferAccess.select(id, requester, requested,
752                                    Configuration.configuration.getHostId());
753       addMap();
754     } catch (final DAOConnectionException e) {
755       throw new WaarpDatabaseException(e);
756     } catch (final DAONoDataException e) {
757       throw new WaarpDatabaseNoDataException(TRANSFER_NOT_FOUND, e);
758     } finally {
759       DAOFactory.closeDAO(transferAccess);
760     }
761     initializeTransferInfo();
762     checkThroughMode();
763     isSaved = true;
764     isOtherThanStatus = false;
765   }
766 
767   @Override
768   protected final void checkValues() throws WaarpDatabaseSqlException {
769     pojo.checkValues();
770   }
771 
772   /**
773    * @return the condition to limit access to the row concerned by the Host
774    */
775   private static String getLimitWhereCondition() {
776     return " " + Columns.OWNERREQ + " = '" +
777            Configuration.configuration.getHostId() + "' ";
778   }
779 
780   /**
781    * To allow to remove specifically one SpecialId from MemoryHashmap
782    *
783    * @param specialId
784    */
785   public static void removeNoDbSpecialId(final long specialId) {
786     XMLTransferDAO.removeNoDbSpecialId(specialId);
787   }
788 
789   private void addMap() {
790     dbR66TaskHashMap.put(pojo.getId(), Boolean.TRUE);
791   }
792 
793   private boolean existMap() {
794     return dbR66TaskHashMap.contains(pojo.getId());
795   }
796 
797   @Override
798   public final boolean exist() throws WaarpDatabaseException {
799     if (existMap()) {
800       return true;
801     }
802     TransferDAO transferAccess = null;
803     try {
804       transferAccess = DAOFactory.getInstance().getTransferDAO();
805       return transferAccess.exist(pojo.getId(), pojo.getRequester(),
806                                   pojo.getRequested(),
807                                   Configuration.configuration.getHostId());
808     } catch (final DAOConnectionException e) {
809       throw new WaarpDatabaseException(e);
810     } finally {
811       DAOFactory.closeDAO(transferAccess);
812     }
813   }
814 
815   /**
816    * Shall be called to ensure that item is really available in database
817    *
818    * @return True iff the element exists in a database (and reloaded then from
819    *     Database)
820    *
821    * @throws WaarpDatabaseException
822    */
823   public final boolean checkFromDbForSubmit() throws WaarpDatabaseException {
824     if (exist()) {
825       select();
826       rule = new DbRule(getRuleId());
827       return true;
828     }
829     return false;
830   }
831 
832   @Override
833   public final void select() throws WaarpDatabaseException {
834     TransferDAO transferAccess = null;
835     try {
836       transferAccess = DAOFactory.getInstance().getTransferDAO();
837       final boolean isSender = this.isSender();
838       pojo = transferAccess.select(pojo.getId(), pojo.getRequester(),
839                                    pojo.getRequested(),
840                                    Configuration.configuration.getHostId());
841       this.setSender(isSender);
842       transferMap.clear();
843       transferMap = null;
844       addMap();
845       isSaved = true;
846       isOtherThanStatus = false;
847     } catch (final DAOConnectionException e) {
848       throw new WaarpDatabaseNoConnectionException(e);
849     } catch (final DAONoDataException e) {
850       throw new WaarpDatabaseNoDataException(TRANSFER_NOT_FOUND, e);
851     } finally {
852       DAOFactory.closeDAO(transferAccess);
853     }
854     rule = new DbRule(getRuleId());
855     initializeTransferInfo();
856     checkThroughMode();
857   }
858 
859   private void checkSnmp() {
860     if (pojo.getUpdatedInfo().equals(UpdatedInfo.INERROR) ||
861         pojo.getUpdatedInfo().equals(UpdatedInfo.INTERRUPTED)) {
862       if (Configuration.configuration.getR66Mib() != null) {
863         Configuration.configuration.getR66Mib().notifyInfoTask(
864             "Task is " + pojo.getUpdatedInfo().name(), this);
865       } else {
866         logger.debug("Could send a SNMP trap here since {}",
867                      pojo.getUpdatedInfo());
868       }
869     } else {
870       if (pojo.getGlobalStep() != Transfer.TASKSTEP.TRANSFERTASK ||
871           pojo.getGlobalStep() == Transfer.TASKSTEP.TRANSFERTASK &&
872           pojo.getRank() % 100 == 0) {
873         if (Configuration.configuration.getR66Mib() != null) {
874           Configuration.configuration.getR66Mib().notifyTask(
875               "Task is currently " + pojo.getUpdatedInfo().name(), this);
876         }
877       }
878     }
879   }
880 
881   @Override
882   public final void insert() throws WaarpDatabaseException {
883     final String nomap = pojo.getTransferInfo();
884     pojo.setTransferInfo(getTransferInfo());
885     super.insert();
886     addMap();
887     pojo.setTransferInfo(nomap);
888     isSaved = true;
889     isOtherThanStatus = false;
890   }
891 
892   /**
893    * Update Rank and Stop only
894    *
895    * @throws WaarpDatabaseException
896    */
897   public final void updateRank() throws WaarpDatabaseException {
898     // SNMP notification
899     checkSnmp();
900     // FIX SelfRequest
901     if (isSelfRequest()) {
902       if (RequestPacket.isCompatibleMode(pojo.getTransferMode(),
903                                          pojo.getRetrieveMode()?
904                                              RequestPacket.TRANSFERMODE.RECVMODE.ordinal() :
905                                              RequestPacket.TRANSFERMODE.SENDMODE.ordinal())) {
906         if (isOtherThanStatus) {
907           optimizedUpdate();
908         } else {
909           optimizedRankUpdate();
910         }
911       }
912     } else {
913       if (isOtherThanStatus) {
914         optimizedUpdate();
915       } else {
916         optimizedRankUpdate();
917       }
918     }
919   }
920 
921   /**
922    * Update Runner using Rank (and stop) only update
923    *
924    * @throws WaarpDatabaseException
925    */
926   private void optimizedRankUpdate() throws WaarpDatabaseException {
927     setStopNow();
928     TransferDAO transferAccess = null;
929     try {
930       transferAccess = DAOFactory.getInstance().getTransferDAO();
931       if (transferAccess instanceof DBTransferDAO) {
932         ((DBTransferDAO) transferAccess).updateRank(pojo);
933       } else {
934         final String nomap = pojo.getTransferInfo();
935         pojo.setTransferInfo(getTransferInfo());
936         transferAccess.update(pojo);
937         pojo.setTransferInfo(nomap);
938       }
939       addMap();
940       isSaved = true;
941     } catch (final DAOConnectionException e) {
942       throw new WaarpDatabaseException(e);
943     } catch (final DAONoDataException e) {
944       throw new WaarpDatabaseNoDataException(TRANSFER_NOT_FOUND, e);
945     } finally {
946       DAOFactory.closeDAO(transferAccess);
947     }
948   }
949 
950   /**
951    * Update Runner using Rank, stop, UpdatedInfo and StepStatus only update
952    *
953    * @throws WaarpDatabaseException
954    */
955   private void optimizedRankUpdatedInfoStepStatusUpdate()
956       throws WaarpDatabaseException {
957     TransferDAO transferAccess = null;
958     try {
959       transferAccess = DAOFactory.getInstance().getTransferDAO();
960       if (transferAccess instanceof DBTransferDAO) {
961         ((DBTransferDAO) transferAccess).updateRankUpdatedInfoStepStatusStop(
962             pojo);
963       } else {
964         final String nomap = pojo.getTransferInfo();
965         pojo.setTransferInfo(getTransferInfo());
966         transferAccess.update(pojo);
967         pojo.setTransferInfo(nomap);
968       }
969       addMap();
970       isSaved = true;
971       isOtherThanStatus = false;
972     } catch (final DAOConnectionException e) {
973       throw new WaarpDatabaseException(e);
974     } catch (final DAONoDataException e) {
975       throw new WaarpDatabaseNoDataException(TRANSFER_NOT_FOUND, e);
976     } finally {
977       DAOFactory.closeDAO(transferAccess);
978     }
979   }
980 
981   @Override
982   public final void update() throws WaarpDatabaseException {
983     if (isSaved) {
984       return;
985     }
986     // SNMP notification
987     checkSnmp();
988     // FIX SelfRequest
989     if (isSelfRequest()) {
990       if (RequestPacket.isCompatibleMode(pojo.getTransferMode(),
991                                          pojo.getRetrieveMode()?
992                                              RequestPacket.TRANSFERMODE.RECVMODE.ordinal() :
993                                              RequestPacket.TRANSFERMODE.SENDMODE.ordinal())) {
994         if (isOtherThanStatus) {
995           optimizedUpdate();
996         } else {
997           optimizedRankUpdatedInfoStepStatusUpdate();
998         }
999       }
1000     } else {
1001       if (isOtherThanStatus) {
1002         optimizedUpdate();
1003       } else {
1004         optimizedRankUpdatedInfoStepStatusUpdate();
1005       }
1006     }
1007   }
1008 
1009   /**
1010    * Update Runner
1011    *
1012    * @throws WaarpDatabaseException
1013    */
1014   protected final void optimizedUpdate() throws WaarpDatabaseException {
1015     setStopNow();
1016     checkValues();
1017     TransferDAO transferAccess = null;
1018     try {
1019       transferAccess = DAOFactory.getInstance().getTransferDAO();
1020       final String nomap = pojo.getTransferInfo();
1021       pojo.setTransferInfo(getTransferInfo());
1022       transferAccess.update(pojo);
1023       pojo.setTransferInfo(nomap);
1024       addMap();
1025       isSaved = true;
1026       isOtherThanStatus = false;
1027     } catch (final DAOConnectionException e) {
1028       throw new WaarpDatabaseException(e);
1029     } catch (final DAONoDataException e) {
1030       throw new WaarpDatabaseNoDataException(TRANSFER_NOT_FOUND, e);
1031     } finally {
1032       DAOFactory.closeDAO(transferAccess);
1033     }
1034   }
1035 
1036   @Override
1037   public final void delete() throws WaarpDatabaseException {
1038     dbR66TaskHashMap.remove(pojo.getId());
1039     super.delete();
1040   }
1041 
1042   public final void clean() {
1043     // ignore
1044   }
1045 
1046   /**
1047    * Special method used to force insert in case of SelfSubmit
1048    *
1049    * @throws WaarpDatabaseException
1050    */
1051   public final boolean specialSubmit() throws WaarpDatabaseException {
1052     setStopNow();
1053     insert();
1054     return false;
1055   }
1056 
1057   /**
1058    * Partial set from another runner (infostatus, rank, status, step, stop,
1059    * filename, globallastep, globalstep,
1060    * isFileMoved)
1061    *
1062    * @param runner
1063    */
1064   public final void setFrom(final DbTaskRunner runner) {
1065     if (runner != null) {
1066       pojo.setInfoStatus(runner.getErrorInfo());
1067       pojo.setRank(runner.getRank());
1068       pojo.setStepStatus(runner.getStatus());
1069       pojo.setStep(runner.getStep());
1070       pojo.setStop(runner.getStop());
1071       pojo.setFilename(runner.getFilename());
1072       pojo.setGlobalStep(runner.pojo.getGlobalStep());
1073       pojo.setLastGlobalStep(runner.pojo.getLastGlobalStep());
1074       pojo.setIsMoved(runner.isFileMoved());
1075       isOtherThanStatus = true;
1076       isSaved = false;
1077     }
1078   }
1079 
1080   public final boolean isRecvThrough() {
1081     return isRecvThrough;
1082   }
1083 
1084   public final boolean isSendThrough() {
1085     return isSendThrough;
1086   }
1087 
1088   /**
1089    * Private constructor for Commander only
1090    */
1091   private DbTaskRunner() {
1092     pojo = new Transfer();
1093     session = null;
1094     rule = null;
1095   }
1096 
1097   /**
1098    * Set a localChannelReference
1099    *
1100    * @param localChannelReference
1101    */
1102   public final void setLocalChannelReference(
1103       final LocalChannelReference localChannelReference) {
1104     this.localChannelReference = localChannelReference;
1105   }
1106 
1107   /**
1108    * @return the localChannelReference
1109    */
1110   public final LocalChannelReference getLocalChannelReference() {
1111     return localChannelReference;
1112   }
1113 
1114   /**
1115    * For instance from Commander when getting updated information
1116    *
1117    * @param preparedStatement
1118    *
1119    * @return the next updated DbTaskRunner
1120    *
1121    * @throws WaarpDatabaseNoConnectionException
1122    * @throws WaarpDatabaseSqlException
1123    */
1124   public static DbTaskRunner getFromStatement(
1125       final DbPreparedStatement preparedStatement)
1126       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1127     final DbTaskRunner dbTaskRunner = new DbTaskRunner();
1128     AbstractDAO<Transfer> transferDAO = null;
1129     try {
1130       transferDAO = dbTaskRunner.getDao(false);
1131       dbTaskRunner.pojo =
1132           ((StatementExecutor<Transfer>) transferDAO).getFromResultSet(
1133               preparedStatement.getResultSet());
1134       if (dbTaskRunner.rule == null && dbTaskRunner.pojo.getRule() != null) {
1135         try {
1136           dbTaskRunner.rule = new DbRule(dbTaskRunner.getRuleId());
1137         } catch (final WaarpDatabaseException e) {
1138           throw new WaarpDatabaseSqlException(
1139               "Rule cannot be found for DbTaskRunner: " + dbTaskRunner.asJson(),
1140               e);
1141         }
1142       }
1143       dbTaskRunner.initializeTransferInfo();
1144       dbTaskRunner.checkThroughMode();
1145       return dbTaskRunner;
1146     } catch (final SQLException e) {
1147       DbConstant.error(e);
1148       throw new WaarpDatabaseSqlException(GETTING_VALUES_IN_ERROR, e);
1149     } catch (final DAOConnectionException e) {
1150       throw new WaarpDatabaseSqlException(GETTING_VALUES_IN_ERROR, e);
1151     } finally {
1152       DAOFactory.closeDAO(transferDAO);
1153     }
1154   }
1155 
1156   /**
1157    * For instance from Commander when getting updated information
1158    * <p></p>
1159    * <p>This version tries to load DbRule but will not make any error if not found!</p>
1160    *
1161    * @param preparedStatement
1162    *
1163    * @return the next updated DbTaskRunner
1164    *
1165    * @throws WaarpDatabaseNoConnectionException
1166    * @throws WaarpDatabaseSqlException
1167    */
1168   public static DbTaskRunner getFromStatementNoRule(
1169       final DbPreparedStatement preparedStatement)
1170       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1171     final DbTaskRunner dbTaskRunner = new DbTaskRunner();
1172     AbstractDAO<Transfer> transferDAO = null;
1173     try {
1174       transferDAO = dbTaskRunner.getDao(false);
1175       dbTaskRunner.pojo =
1176           ((StatementExecutor<Transfer>) transferDAO).getFromResultSet(
1177               preparedStatement.getResultSet());
1178       if (dbTaskRunner.rule == null && dbTaskRunner.pojo.getRule() != null) {
1179         try {
1180           dbTaskRunner.rule = new DbRule(dbTaskRunner.getRuleId());
1181         } catch (final WaarpDatabaseException e) {
1182           logger.warn("Rule cannot be found for DbTaskRunner: " +
1183                       dbTaskRunner.asJson() + " : {}", e.getMessage());
1184         }
1185       }
1186       dbTaskRunner.initializeTransferInfo();
1187       dbTaskRunner.checkThroughMode();
1188       return dbTaskRunner;
1189     } catch (final SQLException e) {
1190       DbConstant.error(e);
1191       throw new WaarpDatabaseSqlException(GETTING_VALUES_IN_ERROR, e);
1192     } catch (final DAOConnectionException e) {
1193       throw new WaarpDatabaseSqlException(GETTING_VALUES_IN_ERROR, e);
1194     } finally {
1195       DAOFactory.closeDAO(transferDAO);
1196     }
1197   }
1198 
1199   /**
1200    * For REST interface, to prevent DbRule issue
1201    *
1202    * @param preparedStatement
1203    *
1204    * @return the next updated DbTaskRunner
1205    *
1206    * @throws WaarpDatabaseNoConnectionException
1207    * @throws WaarpDatabaseSqlException
1208    */
1209   public static DbTaskRunner getFromStatementNoDbRule(
1210       final DbPreparedStatement preparedStatement)
1211       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1212     final DbTaskRunner dbTaskRunner = new DbTaskRunner();
1213     AbstractDAO<Transfer> transferDAO = null;
1214     try {
1215       transferDAO = dbTaskRunner.getDao(false);
1216       dbTaskRunner.pojo =
1217           ((StatementExecutor<Transfer>) transferDAO).getFromResultSet(
1218               preparedStatement.getResultSet());
1219       if (dbTaskRunner.rule == null) {
1220         try {
1221           dbTaskRunner.rule = new DbRule(dbTaskRunner.getRuleId());
1222         } catch (final WaarpDatabaseNoDataException e) {
1223           // ignore
1224         } catch (final WaarpDatabaseException e) {
1225           throw new WaarpDatabaseSqlException(e);
1226         }
1227       }
1228       dbTaskRunner.initializeTransferInfo();
1229       dbTaskRunner.checkThroughMode();
1230       return dbTaskRunner;
1231     } catch (final SQLException e) {
1232       DbConstant.error(e);
1233       throw new WaarpDatabaseSqlException(GETTING_VALUES_IN_ERROR, e);
1234     } catch (final DAOConnectionException e) {
1235       throw new WaarpDatabaseSqlException(GETTING_VALUES_IN_ERROR, e);
1236     } finally {
1237       DAOFactory.closeDAO(transferDAO);
1238     }
1239   }
1240 
1241   /**
1242    * @param preparedStatement
1243    * @param srcrequest
1244    * @param limit
1245    * @param orderby
1246    * @param startid
1247    * @param stopid
1248    * @param start
1249    * @param stop
1250    * @param rule
1251    * @param req
1252    * @param pending
1253    * @param transfer
1254    * @param error
1255    * @param done
1256    * @param all
1257    *
1258    * @return The DbPreparedStatement already prepared according to select or
1259    *     delete command
1260    *
1261    * @throws WaarpDatabaseNoConnectionException
1262    * @throws WaarpDatabaseSqlException
1263    */
1264   private static void getFilterCondition(
1265       final DbPreparedStatement preparedStatement, final String srcrequest,
1266       final int limit, final String whereCond, final String orderby,
1267       final String startid, final String stopid, final Timestamp start,
1268       final Timestamp stop, final String rule, final String req,
1269       final boolean pending, final boolean transfer, final boolean error,
1270       final boolean done, final boolean all)
1271       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1272     String request = srcrequest;
1273     if (ParametersChecker.isEmpty(startid) &&
1274         ParametersChecker.isEmpty(stopid) && start == null && stop == null &&
1275         ParametersChecker.isEmpty(rule) && ParametersChecker.isEmpty(req) &&
1276         all) {
1277       // finish
1278       if (ParametersChecker.isNotEmpty(whereCond)) {
1279         request += " WHERE " + whereCond + orderby;
1280       }
1281       if (limit > 0) {
1282         request = preparedStatement.getDbSession().getAdmin().getDbModel()
1283                                    .limitRequest(selectAllFields, request,
1284                                                  limit);
1285       }
1286       preparedStatement.createPrepareStatement(request);
1287       return;
1288     }
1289     request += " WHERE ";
1290     final StringBuilder scondition = new StringBuilder(whereCond);
1291     boolean hasCondition = ParametersChecker.isNotEmpty(whereCond);
1292     if (start != null && stop != null) {
1293       if (hasCondition) {
1294         scondition.append(AND);
1295       }
1296       scondition.append(Columns.STARTTRANS.name()).append(" BETWEEN ? AND ? ");
1297       hasCondition = true;
1298     } else if (start != null) {
1299       if (hasCondition) {
1300         scondition.append(AND);
1301       }
1302       scondition.append(Columns.STARTTRANS.name()).append(" >= ? ");
1303       hasCondition = true;
1304     } else if (stop != null) {
1305       if (hasCondition) {
1306         scondition.append(AND);
1307       }
1308       scondition.append(Columns.STARTTRANS.name()).append(" <= ? ");
1309       hasCondition = true;
1310     }
1311     if (ParametersChecker.isNotEmpty(startid, stopid)) {
1312       if (hasCondition) {
1313         scondition.append(AND);
1314       }
1315       hasCondition = true;
1316       scondition.append(Columns.SPECIALID.name()).append(" BETWEEN ? AND ? ");
1317     } else if (ParametersChecker.isNotEmpty(startid)) {
1318       if (hasCondition) {
1319         scondition.append(AND);
1320       }
1321       hasCondition = true;
1322       scondition.append(Columns.SPECIALID.name()).append(" >= ? ");
1323     } else if (ParametersChecker.isNotEmpty(stopid)) {
1324       if (hasCondition) {
1325         scondition.append(AND);
1326       }
1327       hasCondition = true;
1328       scondition.append(Columns.SPECIALID.name()).append(" <= ? ");
1329     }
1330     if (ParametersChecker.isNotEmpty(rule)) {
1331       if (hasCondition) {
1332         scondition.append(AND);
1333       }
1334       hasCondition = true;
1335       scondition.append(Columns.IDRULE.name()).append(" = '").append(rule)
1336                 .append("' ");
1337     }
1338     if (!all) {
1339       if (hasCondition) {
1340         scondition.append(AND);
1341       }
1342       hasCondition = true;
1343       scondition.append("( ");
1344       List<Integer> upds = new ArrayList<Integer>();
1345       List<Integer> gss = new ArrayList<Integer>();
1346       boolean hasone = false;
1347       if (pending) {
1348         upds.add(UpdatedInfo.TOSUBMIT.ordinal());
1349         hasone = true;
1350       }
1351       if (transfer) {
1352         upds.add(UpdatedInfo.RUNNING.ordinal());
1353         hasone = true;
1354       }
1355       if (error) {
1356         upds.add(UpdatedInfo.INERROR.ordinal());
1357         upds.add(UpdatedInfo.INTERRUPTED.ordinal());
1358         gss.add(TASKSTEP.ERRORTASK.ordinal());
1359         hasone = true;
1360       }
1361       if (done) {
1362         upds.add(UpdatedInfo.DONE.ordinal());
1363         gss.add(TASKSTEP.ALLDONETASK.ordinal());
1364         hasone = true;
1365       }
1366       if (!hasone) {
1367         scondition.append(Columns.UPDATEDINFO.name()).append(" IS NOT NULL ");
1368       } else {
1369         if (!upds.isEmpty()) {
1370           if (upds.size() == 1) {
1371             scondition.append(Columns.UPDATEDINFO.name()).append(" = ")
1372                       .append(upds.get(0).toString());
1373           } else {
1374             scondition.append(Columns.UPDATEDINFO.name()).append(" IN(");
1375             for (int rank = 0; rank < upds.size() - 1; rank++) {
1376               scondition.append(upds.get(rank).toString()).append(", ");
1377             }
1378             scondition.append(upds.get(upds.size() - 1).toString()).append(")");
1379           }
1380           upds.clear();
1381           if (!gss.isEmpty()) {
1382             scondition.append(" OR ");
1383           }
1384         }
1385         if (!gss.isEmpty()) {
1386           if (gss.size() == 1) {
1387             scondition.append(Columns.GLOBALSTEP.name()).append(" = ")
1388                       .append(gss.get(0).toString());
1389           } else {
1390             scondition.append(Columns.GLOBALSTEP.name()).append(" IN(");
1391             for (int rank = 0; rank < gss.size() - 1; rank++) {
1392               scondition.append(gss.get(rank).toString()).append(", ");
1393             }
1394             scondition.append(gss.get(gss.size() - 1).toString()).append(")");
1395           }
1396           gss.clear();
1397         }
1398       }
1399       scondition.append(") ");
1400     }
1401     if (ParametersChecker.isNotEmpty(req)) {
1402       if (hasCondition) {
1403         scondition.append(AND);
1404       }
1405       hasCondition = true;
1406       scondition.append("( ").append(Columns.REQUESTED.name()).append(" = '")
1407                 .append(req).append("' OR ").append(Columns.REQUESTER.name())
1408                 .append(" = '").append(req).append("' )");
1409     }
1410     if (limit > 0) {
1411       scondition.insert(0, request).append(orderby);
1412       request = scondition.toString();
1413       request = preparedStatement.getDbSession().getAdmin().getDbModel()
1414                                  .limitRequest(selectAllFields, request, limit);
1415     } else {
1416       scondition.insert(0, request).append(orderby);
1417       request = scondition.toString();
1418     }
1419     preparedStatement.createPrepareStatement(request);
1420     int rank = 1;
1421     try {
1422       if (start != null && stop != null) {
1423         preparedStatement.getPreparedStatement().setTimestamp(rank, start);
1424         rank++;
1425         preparedStatement.getPreparedStatement().setTimestamp(rank, stop);
1426         rank++;
1427       } else if (start != null) {
1428         preparedStatement.getPreparedStatement().setTimestamp(rank, start);
1429         rank++;
1430       } else if (stop != null) {
1431         preparedStatement.getPreparedStatement().setTimestamp(rank, stop);
1432         rank++;
1433       }
1434       if (ParametersChecker.isNotEmpty(startid)) {
1435         long value = ILLEGALVALUE;
1436         try {
1437           value = Long.parseLong(startid);
1438         } catch (final NumberFormatException e) {
1439           // ignore then
1440         }
1441         preparedStatement.getPreparedStatement().setLong(rank, value);
1442         rank++;
1443       }
1444       if (ParametersChecker.isNotEmpty(stopid)) {
1445         long value = Long.MAX_VALUE;
1446         try {
1447           value = Long.parseLong(stopid);
1448         } catch (final NumberFormatException e) {
1449           // ignore then
1450         }
1451         preparedStatement.getPreparedStatement().setLong(rank, value);
1452       }
1453     } catch (final SQLException e) {
1454       preparedStatement.realClose();
1455       throw new WaarpDatabaseSqlException(e);
1456     }
1457   }
1458 
1459   /**
1460    * @param session
1461    * @param limit
1462    * @param orderBySpecialId
1463    * @param startid
1464    * @param stopid
1465    * @param start
1466    * @param stop
1467    * @param rule
1468    * @param req
1469    * @param pending
1470    * @param transfer
1471    * @param error
1472    * @param done
1473    * @param all
1474    *
1475    * @return the DbPreparedStatement according to the filter
1476    *
1477    * @throws WaarpDatabaseNoConnectionException
1478    * @throws WaarpDatabaseSqlException
1479    */
1480   public static DbPreparedStatement getFilterPrepareStatement(
1481       final DbSession session, final int limit, final boolean orderBySpecialId,
1482       final String startid, final String stopid, final Timestamp start,
1483       final Timestamp stop, final String rule, final String req,
1484       final boolean pending, final boolean transfer, final boolean error,
1485       final boolean done, final boolean all)
1486       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1487     return getFilterPrepareStatement(session, limit, orderBySpecialId, startid,
1488                                      stopid, start, stop, rule, req, pending,
1489                                      transfer, error, done, all, null);
1490   }
1491 
1492   /**
1493    * @param session
1494    * @param limit
1495    * @param orderBySpecialId
1496    * @param startid
1497    * @param stopid
1498    * @param start
1499    * @param stop
1500    * @param rule
1501    * @param req
1502    * @param pending
1503    * @param transfer
1504    * @param error
1505    * @param done
1506    * @param all
1507    * @param owner
1508    *
1509    * @return the DbPreparedStatement according to the filter
1510    *
1511    * @throws WaarpDatabaseNoConnectionException
1512    * @throws WaarpDatabaseSqlException
1513    */
1514   public static DbPreparedStatement getFilterPrepareStatement(
1515       final DbSession session, final int limit, final boolean orderBySpecialId,
1516       final String startid, final String stopid, final Timestamp start,
1517       final Timestamp stop, final String rule, final String req,
1518       final boolean pending, final boolean transfer, final boolean error,
1519       final boolean done, final boolean all, final String owner)
1520       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1521     final DbPreparedStatement preparedStatement =
1522         new DbPreparedStatement(session);
1523     final String request = "SELECT " + selectAllFields + " FROM " + table;
1524     String whereCond = "";
1525     String orderby = "";
1526     if (ParametersChecker.isEmpty(owner)) {
1527       whereCond = getLimitWhereCondition();
1528     } else if (!"*".equals(owner)) {
1529       whereCond = Columns.OWNERREQ + " = '" + owner + "' ";
1530     }
1531     if (orderBySpecialId) {
1532       orderby = " ORDER BY " + Columns.SPECIALID.name() + " DESC ";
1533     } else {
1534       orderby = " ORDER BY " + Columns.STARTTRANS.name() + " DESC ";
1535     }
1536     getFilterCondition(preparedStatement, request, limit, whereCond, orderby,
1537                        startid, stopid, start, stop, rule, req, pending,
1538                        transfer, error, done, all);
1539     return preparedStatement;
1540   }
1541 
1542   /**
1543    * @param followId the followId to find
1544    *
1545    * @return the associated Filter
1546    */
1547   public static Filter getFollowIdFilter(final String followId) {
1548     return new Filter(DBTransferDAO.TRANSFER_INFO_FIELD, Filter.LIKE,
1549                       "%" + FOLLOW_ID_LIKE + followId + "%");
1550   }
1551 
1552   /**
1553    * @return the Filter on current Owner
1554    */
1555   public static Filter getOwnerFilter() {
1556     return new Filter(DBTransferDAO.OWNER_REQUEST_FIELD, "=",
1557                       Configuration.configuration.getHostId());
1558   }
1559 
1560   /**
1561    * @param followId the followId to find
1562    * @param orderByStart If true, sort on Start ; If false, does not
1563    *     set the order on start
1564    * @param limit the limit of items
1565    * @param allOwner if admin role, can resolve for all Owners
1566    *
1567    * @return the DbPreparedStatement for getting Updated Object
1568    *
1569    * @throws WaarpDatabaseNoConnectionException
1570    * @throws WaarpDatabaseSqlException
1571    */
1572   public static DbTaskRunner[] getSelectSameFollowId(final String followId,
1573                                                      final boolean orderByStart,
1574                                                      final int limit,
1575                                                      final boolean allOwner)
1576       throws WaarpDatabaseNoConnectionException {
1577     final List<Filter> filters = new ArrayList<Filter>(2);
1578     filters.add(getFollowIdFilter(followId));
1579     if (!allOwner) {
1580       filters.add(new Filter(Columns.OWNERREQ.toString(), "=",
1581                              Configuration.configuration.getHostId()));
1582     }
1583     TransferDAO transferAccess = null;
1584     List<Transfer> transfers;
1585     try {
1586       transferAccess = DAOFactory.getInstance().getTransferDAO();
1587       if (orderByStart) {
1588         transfers =
1589             transferAccess.find(filters, DBTransferDAO.TRANSFER_START_FIELD,
1590                                 true, limit);
1591       } else {
1592         transfers = transferAccess.find(filters, limit);
1593       }
1594     } catch (final DAOConnectionException e) {
1595       throw new WaarpDatabaseNoConnectionException(e);
1596     } finally {
1597       DAOFactory.closeDAO(transferAccess);
1598     }
1599     final DbTaskRunner[] res = new DbTaskRunner[transfers.size()];
1600     int i = 0;
1601     for (final Transfer transfer : transfers) {
1602       res[i] = new DbTaskRunner(transfer);
1603       i++;
1604     }
1605     return res;
1606   }
1607 
1608   /**
1609    * @param info
1610    * @param orderByStart If true, sort on Start ; If false, does not
1611    *     set the limit on start
1612    * @param limit
1613    *
1614    * @return the DbPreparedStatement for getting Updated Object
1615    *
1616    * @throws WaarpDatabaseNoConnectionException
1617    * @throws WaarpDatabaseSqlException
1618    */
1619   public static DbTaskRunner[] getSelectFromInfoPrepareStatement(
1620       final UpdatedInfo info, final boolean orderByStart, final int limit)
1621       throws WaarpDatabaseNoConnectionException {
1622     final List<Filter> filters = new ArrayList<Filter>(3);
1623     filters.add(getOwnerFilter());
1624     filters.add(new Filter(DBTransferDAO.TRANSFER_START_FIELD, "<=",
1625                            new Timestamp(System.currentTimeMillis())));
1626     filters.add(new Filter(DBTransferDAO.UPDATED_INFO_FIELD, "=",
1627                            org.waarp.openr66.pojo.UpdatedInfo.fromLegacy(info)
1628                                                              .ordinal()));
1629     TransferDAO transferAccess = null;
1630     List<Transfer> transfers;
1631     try {
1632       transferAccess = DAOFactory.getInstance().getTransferDAO();
1633       if (orderByStart) {
1634         transfers =
1635             transferAccess.find(filters, DBTransferDAO.TRANSFER_START_FIELD,
1636                                 true, limit);
1637       } else {
1638         transfers = transferAccess.find(filters, limit);
1639       }
1640     } catch (final DAOConnectionException e) {
1641       throw new WaarpDatabaseNoConnectionException(e);
1642     } finally {
1643       DAOFactory.closeDAO(transferAccess);
1644     }
1645     final DbTaskRunner[] res = new DbTaskRunner[transfers.size()];
1646     int i = 0;
1647     for (final Transfer transfer : transfers) {
1648       res[i] = new DbTaskRunner(transfer);
1649       i++;
1650     }
1651     return res;
1652   }
1653 
1654   /**
1655    * @param session
1656    *
1657    * @return the DbPreparedStatement for getting Updated Object
1658    *
1659    * @throws WaarpDatabaseNoConnectionException
1660    * @throws WaarpDatabaseSqlException
1661    */
1662   public static DbPreparedStatement getCountInfoPrepareStatement(
1663       final DbSession session)
1664       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1665     final String request =
1666         SELECT_COUNT + Columns.SPECIALID.name() + ") FROM " + table +
1667         " WHERE " + getLimitWhereCondition() + AND + Columns.STARTTRANS.name() +
1668         " >= ? AND " + Columns.UPDATEDINFO.name() + " = ? ";
1669     final DbPreparedStatement pstt = new DbPreparedStatement(session, request);
1670     session.addLongTermPreparedStatement(pstt);
1671     return pstt;
1672   }
1673 
1674   /**
1675    * @param pstt
1676    * @param info
1677    * @param time
1678    *
1679    * @return the number of elements (COUNT) from the statement
1680    */
1681   public static long getResultCountPrepareStatement(
1682       final DbPreparedStatement pstt, final UpdatedInfo info, final long time) {
1683     long result = 0;
1684     try {
1685       finishSelectOrCountPrepareStatement(pstt, time);
1686       pstt.getPreparedStatement().setInt(2, info.ordinal());
1687       pstt.executeQuery();
1688       if (pstt.getNext()) {
1689         result = pstt.getResultSet().getLong(1);
1690       }
1691     } catch (final WaarpDatabaseNoConnectionException ignored) {
1692       // nothing
1693     } catch (final WaarpDatabaseSqlException ignored) {
1694       // nothing
1695     } catch (final SQLException ignored) {
1696       // nothing
1697     } finally {
1698       pstt.close();
1699     }
1700     return result;
1701   }
1702 
1703   /**
1704    * @param session
1705    * @param globalstep
1706    *
1707    * @return the DbPreparedStatement for getting Runner according to
1708    *     globalstep
1709    *     ordered by start
1710    *
1711    * @throws WaarpDatabaseNoConnectionException
1712    * @throws WaarpDatabaseSqlException
1713    */
1714   public static DbPreparedStatement getCountStepPrepareStatement(
1715       final DbSession session, final TASKSTEP globalstep)
1716       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1717     String request =
1718         SELECT_COUNT + Columns.SPECIALID.name() + ") FROM " + table;
1719     if (globalstep != null) {
1720       request += " WHERE " + getLimitWhereCondition() + AND +
1721                  Columns.STARTTRANS.name() + " >= ? AND " +
1722                  Columns.GLOBALSTEP.name() + " " + "= " + globalstep.ordinal();
1723     } else {
1724       request += " WHERE " + getLimitWhereCondition() + AND +
1725                  Columns.STARTTRANS.name() + " >= ? ";
1726     }
1727     final DbPreparedStatement prep = new DbPreparedStatement(session, request);
1728     session.addLongTermPreparedStatement(prep);
1729     return prep;
1730   }
1731 
1732   /**
1733    * @param session
1734    *
1735    * @return the DbPreparedStatement for getting Runner according to status
1736    *     ordered by start
1737    *
1738    * @throws WaarpDatabaseNoConnectionException
1739    * @throws WaarpDatabaseSqlException
1740    */
1741   public static DbPreparedStatement getCountStatusPrepareStatement(
1742       final DbSession session)
1743       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1744     String request =
1745         SELECT_COUNT + Columns.SPECIALID.name() + ") FROM " + table;
1746     request +=
1747         " WHERE " + getLimitWhereCondition() + AND + Columns.STARTTRANS.name() +
1748         " >= ? ";
1749     request += AND + Columns.INFOSTATUS.name() + " = ? ";
1750     final DbPreparedStatement prep = new DbPreparedStatement(session, request);
1751     session.addLongTermPreparedStatement(prep);
1752     return prep;
1753   }
1754 
1755   /**
1756    * @param pstt
1757    * @param error
1758    * @param time
1759    *
1760    * @return the number of elements (COUNT) from the statement
1761    */
1762   public static long getResultCountPrepareStatement(
1763       final DbPreparedStatement pstt, final ErrorCode error, final long time) {
1764     long result = 0;
1765     try {
1766       finishSelectOrCountPrepareStatement(pstt, time);
1767       pstt.getPreparedStatement().setString(2, error.getCode());
1768       pstt.executeQuery();
1769       if (pstt.getNext()) {
1770         result = pstt.getResultSet().getLong(1);
1771       }
1772     } catch (final WaarpDatabaseNoConnectionException ignored) {
1773       // ignore
1774     } catch (final WaarpDatabaseSqlException ignored) {
1775       // ignore
1776     } catch (final SQLException ignored) {
1777       // ignore
1778     } finally {
1779       pstt.close();
1780     }
1781     return result;
1782   }
1783 
1784   /**
1785    * Only running transfers
1786    *
1787    * @param session
1788    * @param status
1789    *
1790    * @return the DbPreparedStatement for getting Runner according to status
1791    *     ordered by start
1792    *
1793    * @throws WaarpDatabaseNoConnectionException
1794    * @throws WaarpDatabaseSqlException
1795    */
1796   public static DbPreparedStatement getCountStatusRunningPrepareStatement(
1797       final DbSession session, final ErrorCode status)
1798       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1799     String request =
1800         SELECT_COUNT + Columns.SPECIALID.name() + ") FROM " + table;
1801     request += " WHERE " + getLimitWhereCondition();
1802     request += AND + Columns.STARTTRANS.name() + " >= ? ";
1803     request += AND + Columns.UPDATEDINFO.name() + " = " +
1804                UpdatedInfo.RUNNING.ordinal();
1805     if (status != null) {
1806       request +=
1807           AND + Columns.STEPSTATUS.name() + " = '" + status.getCode() + '\'';
1808     }
1809     final DbPreparedStatement prep = new DbPreparedStatement(session, request);
1810     session.addLongTermPreparedStatement(prep);
1811     return prep;
1812   }
1813 
1814   /**
1815    * Running or not transfers are concerned
1816    *
1817    * @param session
1818    * @param in True for Incoming, False for Outgoing
1819    *
1820    * @return the DbPreparedStatement for getting Runner according to in or out
1821    *     going way and Error
1822    *
1823    * @throws WaarpDatabaseNoConnectionException
1824    * @throws WaarpDatabaseSqlException
1825    */
1826   public static DbPreparedStatement getCountInOutErrorPrepareStatement(
1827       final DbSession session, final boolean in)
1828       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1829     String request =
1830         SELECT_COUNT + Columns.SPECIALID.name() + ") FROM " + table;
1831     request += " WHERE " + getLimitWhereCondition() + " ";
1832     final String requesterd;
1833     final String from = Configuration.configuration.getHostId();
1834     final String sfrom = Configuration.configuration.getHostSslId();
1835     if (in) {
1836       requesterd = Columns.REQUESTED.name();
1837     } else {
1838       requesterd = Columns.REQUESTER.name();
1839     }
1840     if (from != null && sfrom != null) {
1841       request += AND + requesterd + " IN('" + from + "', '" + sfrom + "') ";
1842     } else if (from != null) {
1843       request += AND + requesterd + " = '" + from + "' ";
1844     } else {
1845       request += AND + requesterd + " = '" + sfrom + "' ";
1846     }
1847     request += AND + Columns.STARTTRANS.name() + " >= ? ";
1848     request += AND + Columns.UPDATEDINFO.name() + " = " +
1849                UpdatedInfo.INERROR.ordinal();
1850     final DbPreparedStatement prep = new DbPreparedStatement(session, request);
1851     session.addLongTermPreparedStatement(prep);
1852     return prep;
1853   }
1854 
1855   /**
1856    * Running or not transfers are concerned
1857    *
1858    * @param session
1859    * @param in True for Incoming, False for Outgoing
1860    * @param running True for Running only, False for all
1861    *
1862    * @return the DbPreparedStatement for getting Runner according to in or out
1863    *     going way
1864    *
1865    * @throws WaarpDatabaseNoConnectionException
1866    * @throws WaarpDatabaseSqlException
1867    */
1868   public static DbPreparedStatement getCountInOutRunningPrepareStatement(
1869       final DbSession session, final boolean in, final boolean running)
1870       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1871     String request =
1872         SELECT_COUNT + Columns.SPECIALID.name() + ") FROM " + table;
1873     request += " WHERE " + getLimitWhereCondition() + " ";
1874     final String requesterd;
1875     final String from = Configuration.configuration.getHostId();
1876     final String sfrom = Configuration.configuration.getHostSslId();
1877     if (in) {
1878       requesterd = Columns.REQUESTED.name();
1879     } else {
1880       requesterd = Columns.REQUESTER.name();
1881     }
1882     if (from != null && sfrom != null) {
1883       request += AND + requesterd + " IN('" + from + "', '" + sfrom + "') ";
1884     } else if (from != null) {
1885       request += AND + requesterd + " = '" + from + "' ";
1886     } else {
1887       request += AND + requesterd + " = '" + sfrom + "' ";
1888     }
1889     request += AND + Columns.STARTTRANS.name() + " >= ? ";
1890     if (running) {
1891       request += AND + Columns.UPDATEDINFO.name() + " = " +
1892                  UpdatedInfo.RUNNING.ordinal();
1893     }
1894     final DbPreparedStatement prep = new DbPreparedStatement(session, request);
1895     session.addLongTermPreparedStatement(prep);
1896     return prep;
1897   }
1898 
1899   /**
1900    * @param pstt
1901    *
1902    * @return the number of elements (COUNT) from the statement
1903    */
1904   public static long getResultCountPrepareStatement(
1905       final DbPreparedStatement pstt) {
1906     long result = 0;
1907     try {
1908       pstt.executeQuery();
1909       if (pstt.getNext()) {
1910         result = pstt.getResultSet().getLong(1);
1911       }
1912     } catch (final WaarpDatabaseNoConnectionException ignored) {
1913       // ignore
1914     } catch (final WaarpDatabaseSqlException ignored) {
1915       // ignore
1916     } catch (final SQLException ignored) {
1917       // ignore
1918     } finally {
1919       pstt.close();
1920     }
1921     return result;
1922   }
1923 
1924   /**
1925    * Set the current time in the given updatedPreparedStatement
1926    *
1927    * @param pstt
1928    *
1929    * @throws WaarpDatabaseNoConnectionException
1930    * @throws WaarpDatabaseSqlException
1931    */
1932   public static void finishSelectOrCountPrepareStatement(
1933       final DbPreparedStatement pstt, final long time)
1934       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1935     final Timestamp startlimit = new Timestamp(time);
1936     try {
1937       pstt.getPreparedStatement().setTimestamp(1, startlimit);
1938     } catch (final SQLException e) {
1939       logger.error("Database SQL Error: Cannot set timestamp: {}",
1940                    e.getMessage());
1941       throw new WaarpDatabaseSqlException("Cannot set timestamp", e);
1942     }
1943   }
1944 
1945   /**
1946    * @param session
1947    * @param start
1948    * @param stop
1949    *
1950    * @return the DbPreparedStatement for getting Selected Object, whatever
1951    *     their
1952    *     status
1953    *
1954    * @throws WaarpDatabaseNoConnectionException
1955    * @throws WaarpDatabaseSqlException
1956    */
1957   public static DbPreparedStatement getLogPrepareStatement(
1958       final DbSession session, final Timestamp start, final Timestamp stop)
1959       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
1960     final DbPreparedStatement preparedStatement =
1961         new DbPreparedStatement(session);
1962     String request = "SELECT " + selectAllFields + " FROM " + table;
1963     if (start != null && stop != null) {
1964       request += " WHERE " + getLimitWhereCondition() + AND +
1965                  Columns.STARTTRANS.name() + " BETWEEN ? AND ? ORDER BY " +
1966                  Columns.SPECIALID.name() + " DESC ";
1967       preparedStatement.createPrepareStatement(request);
1968       try {
1969         preparedStatement.getPreparedStatement().setTimestamp(1, start);
1970         preparedStatement.getPreparedStatement().setTimestamp(2, stop);
1971       } catch (final SQLException e) {
1972         preparedStatement.realClose();
1973         throw new WaarpDatabaseSqlException(e);
1974       }
1975     } else if (start != null) {
1976       request += " WHERE " + getLimitWhereCondition() + AND +
1977                  Columns.STARTTRANS.name() + " >= ? ORDER BY " +
1978                  Columns.SPECIALID.name() + " DESC ";
1979       preparedStatement.createPrepareStatement(request);
1980       try {
1981         preparedStatement.getPreparedStatement().setTimestamp(1, start);
1982       } catch (final SQLException e) {
1983         preparedStatement.realClose();
1984         throw new WaarpDatabaseSqlException(e);
1985       }
1986     } else if (stop != null) {
1987       request += " WHERE " + getLimitWhereCondition() + AND +
1988                  Columns.STARTTRANS.name() + " <= ? ORDER BY " +
1989                  Columns.SPECIALID.name() + " DESC ";
1990       preparedStatement.createPrepareStatement(request);
1991       try {
1992         preparedStatement.getPreparedStatement().setTimestamp(1, stop);
1993       } catch (final SQLException e) {
1994         preparedStatement.realClose();
1995         throw new WaarpDatabaseSqlException(e);
1996       }
1997     } else {
1998       request += " WHERE " + getLimitWhereCondition() + " ORDER BY " +
1999                  Columns.SPECIALID.name() + " DESC ";
2000       preparedStatement.createPrepareStatement(request);
2001     }
2002     return preparedStatement;
2003   }
2004 
2005   /**
2006    * purge in same interval all runners with globallaststep as ALLDONETASK or
2007    * UpdatedInfo as Done
2008    *
2009    * @param session
2010    * @param start
2011    * @param stop
2012    *
2013    * @return the number of log purged
2014    *
2015    * @throws WaarpDatabaseNoConnectionException
2016    * @throws WaarpDatabaseSqlException
2017    */
2018   public static int purgeLogPrepareStatement(final DbSession session,
2019                                              final Timestamp start,
2020                                              final Timestamp stop)
2021       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
2022     final DbPreparedStatement preparedStatement =
2023         new DbPreparedStatement(session);
2024     String request =
2025         "DELETE FROM " + table + " WHERE " + getLimitWhereCondition() + AND +
2026         "(" + Columns.UPDATEDINFO + " = " + UpdatedInfo.DONE.ordinal() +
2027         " OR " + Columns.GLOBALLASTSTEP + " = " +
2028         TASKSTEP.ALLDONETASK.ordinal() + ") ";
2029     try {
2030       if (start != null && stop != null) {
2031         request += AND + Columns.STARTTRANS.name() + " >= ? AND " +
2032                    Columns.STOPTRANS.name() + " <= ? ";
2033         preparedStatement.createPrepareStatement(request);
2034         try {
2035           preparedStatement.getPreparedStatement().setTimestamp(1, start);
2036           preparedStatement.getPreparedStatement().setTimestamp(2, stop);
2037         } catch (final SQLException e) {
2038           preparedStatement.realClose();
2039           throw new WaarpDatabaseSqlException(e);
2040         }
2041       } else if (start != null) {
2042         request += AND + Columns.STARTTRANS.name() + " >= ? ";
2043         preparedStatement.createPrepareStatement(request);
2044         try {
2045           preparedStatement.getPreparedStatement().setTimestamp(1, start);
2046         } catch (final SQLException e) {
2047           preparedStatement.realClose();
2048           throw new WaarpDatabaseSqlException(e);
2049         }
2050       } else if (stop != null) {
2051         request += AND + Columns.STOPTRANS.name() + " <= ? ";
2052         preparedStatement.createPrepareStatement(request);
2053         try {
2054           preparedStatement.getPreparedStatement().setTimestamp(1, stop);
2055         } catch (final SQLException e) {
2056           preparedStatement.realClose();
2057           throw new WaarpDatabaseSqlException(e);
2058         }
2059       } else {
2060         preparedStatement.createPrepareStatement(request);
2061       }
2062       final int nb = preparedStatement.executeUpdate();
2063       logger.info("Purge {} from {}", nb, request);
2064       return nb;
2065     } finally {
2066       preparedStatement.realClose();
2067     }
2068   }
2069 
2070   /**
2071    * @param session
2072    * @param startid
2073    * @param stopid
2074    * @param start
2075    * @param stop
2076    * @param rule
2077    * @param req
2078    * @param pending
2079    * @param transfer
2080    * @param error
2081    * @param done
2082    * @param all
2083    *
2084    * @return the DbPreparedStatement according to the filter and ALLDONE,
2085    *     ERROR globallaststep
2086    *
2087    * @throws WaarpDatabaseNoConnectionException
2088    * @throws WaarpDatabaseSqlException
2089    */
2090   public static int purgeLogPrepareStatement(final DbSession session,
2091                                              final String startid,
2092                                              final String stopid,
2093                                              final Timestamp start,
2094                                              final Timestamp stop,
2095                                              final String rule,
2096                                              final String req,
2097                                              final boolean pending,
2098                                              final boolean transfer,
2099                                              final boolean error,
2100                                              final boolean done,
2101                                              final boolean all)
2102       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
2103     final DbPreparedStatement preparedStatement =
2104         new DbPreparedStatement(session);
2105     final String request = "DELETE FROM " + table;
2106     final String orderby = "";
2107     final String whereCond;
2108     if (ParametersChecker.isEmpty(startid) &&
2109         ParametersChecker.isEmpty(stopid) && start == null && stop == null &&
2110         ParametersChecker.isEmpty(rule) && ParametersChecker.isEmpty(req) &&
2111         all) {
2112       whereCond =
2113           getLimitWhereCondition() + " AND (" + Columns.UPDATEDINFO + " = " +
2114           UpdatedInfo.DONE.ordinal() + " OR " + Columns.GLOBALLASTSTEP + " = " +
2115           TASKSTEP.ALLDONETASK.ordinal() + ") ";
2116     } else {
2117       if (all) {
2118         whereCond =
2119             getLimitWhereCondition() + " AND (" + Columns.UPDATEDINFO + " IN(" +
2120             UpdatedInfo.DONE.ordinal() + ", " + UpdatedInfo.INERROR.ordinal() +
2121             ") OR " + Columns.GLOBALLASTSTEP + " = " +
2122             TASKSTEP.ALLDONETASK.ordinal() + ") ";
2123       } else {
2124         whereCond =
2125             getLimitWhereCondition() + AND + Columns.UPDATEDINFO + " <> " +
2126             UpdatedInfo.RUNNING.ordinal();// limit by field
2127       }
2128     }
2129     int nb;
2130     try {
2131       getFilterCondition(preparedStatement, request, 0, whereCond, orderby,
2132                          startid, stopid, start, stop, rule, req, pending,
2133                          transfer, error, done, all);
2134       nb = preparedStatement.executeUpdate();
2135       logger.info("Purge {} from {}", nb, request);
2136     } finally {
2137       preparedStatement.realClose();
2138     }
2139     return nb;
2140   }
2141 
2142   /**
2143    * Change RUNNING, INTERRUPTED to TOSUBMIT TaskRunner from database. This
2144    * method is to be used when the
2145    * commander is starting the very first time, in order to be ready to rerun
2146    * tasks that are pending.
2147    *
2148    * @param session
2149    *
2150    * @throws WaarpDatabaseNoConnectionException
2151    */
2152   public static void resetToSubmit(final DbSession session)
2153       throws WaarpDatabaseNoConnectionException {
2154     // Change RUNNING and INTERRUPTED to TOSUBMIT since they should be ready
2155     final String request =
2156         "UPDATE " + table + " SET " + Columns.UPDATEDINFO.name() + '=' +
2157         AbstractDbData.UpdatedInfo.TOSUBMIT.ordinal() + " WHERE " +
2158         getLimitWhereCondition() + " AND " + Columns.UPDATEDINFO.name() +
2159         " IN(" + AbstractDbData.UpdatedInfo.RUNNING.ordinal() + ", " +
2160         AbstractDbData.UpdatedInfo.INTERRUPTED.ordinal() + ")";
2161     final DbPreparedStatement initial = new DbPreparedStatement(session);
2162     try {
2163       initial.createPrepareStatement(request);
2164       initial.executeUpdate();
2165     } catch (final WaarpDatabaseNoConnectionException e) {
2166       logger.error("Database No Connection Error: Cannot execute Commander: {}",
2167                    e.getMessage());
2168     } catch (final WaarpDatabaseSqlException e) {
2169       logger.error("Database SQL Error: Cannot execute Commander", e);
2170     } finally {
2171       initial.close();
2172     }
2173   }
2174 
2175   /**
2176    * Change CompleteOk+ALLDONETASK to Updated = DONE TaskRunner from database.
2177    * This method is a clean function
2178    * to be used for instance before log export or at the very beginning of the
2179    * commander.
2180    *
2181    * @throws WaarpDatabaseNoConnectionException
2182    */
2183   public static void changeFinishedToDone()
2184       throws WaarpDatabaseNoConnectionException {
2185     // Update all UpdatedInfo to DONE where GlobalLastStep = ALLDONETASK and
2186     // status = CompleteOk
2187     final List<Filter> filters = new ArrayList<Filter>();
2188     filters.add(getOwnerFilter());
2189     filters.add(new Filter(DBTransferDAO.UPDATED_INFO_FIELD, "<>",
2190                            UpdatedInfo.DONE.ordinal()));
2191     filters.add(new Filter(DBTransferDAO.UPDATED_INFO_FIELD, ">",
2192                            UpdatedInfo.UNKNOWN.ordinal()));
2193     filters.add(new Filter(DBTransferDAO.GLOBAL_LAST_STEP_FIELD, "=",
2194                            Transfer.TASKSTEP.ALLDONETASK.ordinal()));
2195     filters.add(new Filter(DBTransferDAO.STEP_STATUS_FIELD, ">",
2196                            ErrorCode.CompleteOk.getCode()));
2197 
2198     TransferDAO transferAccess = null;
2199     try {
2200       transferAccess = DAOFactory.getInstance().getTransferDAO();
2201       final List<Transfer> transfers = transferAccess.find(filters);
2202       for (final Transfer transfer : transfers) {
2203         transfer.setUpdatedInfo(org.waarp.openr66.pojo.UpdatedInfo.DONE);
2204         if (transferAccess instanceof DBTransferDAO) {
2205           ((DBTransferDAO) transferAccess).updateRankUpdatedInfoStepStatusStop(
2206               transfer);
2207         } else {
2208           transferAccess.update(transfer);
2209         }
2210       }
2211     } catch (final DAOConnectionException e) {
2212       throw new WaarpDatabaseNoConnectionException(e);
2213     } catch (final DAONoDataException e) {
2214       throw new WaarpDatabaseNoConnectionException(TRANSFER_NOT_FOUND);
2215     } finally {
2216       DAOFactory.closeDAO(transferAccess);
2217     }
2218   }
2219 
2220   /**
2221    * Reset the runner (ready to be run again)
2222    *
2223    * @return True if OK, False if already finished
2224    */
2225   public final boolean reset() {
2226     // Reset the status if already stopped and not finished
2227     if (getStatus() != ErrorCode.CompleteOk) {
2228       // restart
2229       switch (TASKSTEP.values()[getGloballaststep()]) {
2230         case PRETASK:
2231           // restart
2232           setPreTask();
2233           setExecutionStatus(ErrorCode.InitOk);
2234           break;
2235         case TRANSFERTASK:
2236           // continue
2237           final int newrank = getRank();
2238           setTransferTask(newrank);
2239           setExecutionStatus(ErrorCode.PreProcessingOk);
2240           break;
2241         case POSTTASK:
2242           // restart
2243           setPostTask();
2244           setExecutionStatus(ErrorCode.TransferOk);
2245           break;
2246         case NOTASK:
2247           setInitialTask();
2248           setExecutionStatus(ErrorCode.Unknown);
2249           break;
2250         default:
2251           break;
2252       }
2253       changeUpdatedInfo(UpdatedInfo.UNKNOWN);
2254       setErrorExecutionStatus(pojo.getStepStatus());
2255       return true;
2256     } else {
2257       // Already finished
2258       return false;
2259     }
2260   }
2261 
2262   /**
2263    * Decrease if necessary the rank
2264    */
2265   public final void restartRank() {
2266     if (!pojo.getRetrieveMode()) {
2267       int newrank = getRank();
2268       if (newrank > 0) {
2269         logger.debug("Decrease Rank Restart of -{} from {}",
2270                      Configuration.getRankRestart(), newrank);
2271         newrank -= Configuration.getRankRestart();
2272         if (newrank <= 0) {
2273           newrank = 1;
2274         }
2275         if (getRank() != newrank) {
2276           logger.warn("Decreased Rank Restart at rank: {} for {}", newrank,
2277                       this);
2278         }
2279       }
2280       setTransferTask(newrank);
2281     }
2282   }
2283 
2284   /**
2285    * Make this Runner ready for restart
2286    *
2287    * @param submit True to resubmit this task, else False to keep it
2288    *     as
2289    *     running (only reset)
2290    *
2291    * @return True if OK or False if Already finished or if submitted and the
2292    *     request is a selfRequested and is
2293    *     not ready to restart locally
2294    */
2295   public final boolean restart(final boolean submit) {
2296     // Restart if not Requested
2297     if (submit && isRequestOnRequested()) {
2298       if (pojo.getLastGlobalStep() != Transfer.TASKSTEP.ALLDONETASK ||
2299           pojo.getLastGlobalStep() != Transfer.TASKSTEP.ERRORTASK) {
2300         // nothing
2301       }
2302       return false;
2303     }
2304     // Restart if already stopped and not finished
2305     if (reset()) {
2306       // if not submit and transfertask and receiver AND not requester
2307       // If requester and receiver => rank is already decreased when request is sent
2308       if (!submit && pojo.getGlobalStep() == Transfer.TASKSTEP.TRANSFERTASK &&
2309           !pojo.getRetrieveMode() && isRequestOnRequested()) {
2310         logger.debug("Will try to restart transfer {}", this);
2311         restartRank();
2312         logger.debug("New restart for transfer is {}", this);
2313       }
2314       if (submit) {
2315         changeUpdatedInfo(UpdatedInfo.TOSUBMIT);
2316       } else {
2317         changeUpdatedInfo(UpdatedInfo.RUNNING);
2318       }
2319       return true;
2320     } else {
2321       // Already finished so DONE
2322       setAllDone();
2323       setErrorExecutionStatus(ErrorCode.QueryAlreadyFinished);
2324       forceSaveStatus();
2325       return false;
2326     }
2327   }
2328 
2329   /**
2330    * Stop or Cancel a Runner from database point of view
2331    *
2332    * @param code
2333    *
2334    * @return True if correctly stopped or canceled
2335    */
2336   public final boolean stopOrCancelRunner(final ErrorCode code) {
2337     if (!isFinished()) {
2338       reset();
2339       switch (code) {
2340         case CanceledTransfer:
2341         case StoppedTransfer:
2342         case RemoteShutdown:
2343           changeUpdatedInfo(UpdatedInfo.INERROR);
2344           break;
2345         default:
2346           changeUpdatedInfo(UpdatedInfo.INTERRUPTED);
2347       }
2348       try {
2349         update();
2350       } catch (final WaarpDatabaseException e) {
2351         logger.error("Cannot save transfer status: {}", e.getMessage());
2352       }
2353       logger.warn("StopOrCancel: {}     {}", code.getMesg(), toShortString());
2354       return true;
2355     } else {
2356       if (logger.isInfoEnabled()) {
2357         logger.info("Transfer already finished {}", toShortString());
2358       }
2359     }
2360     return false;
2361   }
2362 
2363   @Override
2364   public final void changeUpdatedInfo(final UpdatedInfo info) {
2365     if (pojo.getUpdatedInfo() !=
2366         org.waarp.openr66.pojo.UpdatedInfo.valueOf(info.ordinal())) {
2367       setStopNow();
2368       isSaved = false;
2369       pojo.setUpdatedInfo(
2370           org.waarp.openr66.pojo.UpdatedInfo.valueOf(info.ordinal()));
2371     }
2372   }
2373 
2374   /**
2375    * Set the ErrorCode for the InfoStatus
2376    *
2377    * @param code
2378    */
2379   public final void setErrorExecutionStatus(final ErrorCode code) {
2380     if (pojo.getInfoStatus() != code) {
2381       setStopNow();
2382       isSaved = false;
2383       pojo.setInfoStatus(code);
2384     }
2385   }
2386 
2387   /**
2388    * @return The current UpdatedInfo value
2389    */
2390   public final UpdatedInfo getUpdatedInfo() {
2391     return pojo.getUpdatedInfo().getLegacy();
2392   }
2393 
2394   /**
2395    * @return the error code associated with the Updated Info
2396    */
2397   public final ErrorCode getErrorInfo() {
2398     return pojo.getInfoStatus();
2399   }
2400 
2401   /**
2402    * @return the step
2403    */
2404   public final int getStep() {
2405     return pojo.getStep();
2406   }
2407 
2408   /**
2409    * @return the rescheduledTransfer
2410    */
2411   public final boolean isRescheduledTransfer() {
2412     return rescheduledTransfer;
2413   }
2414 
2415   /**
2416    * Set this DbTaskRunner as rescheduled (valid only while still in memory)
2417    */
2418   public final void setRescheduledTransfer() {
2419     rescheduledTransfer = true;
2420   }
2421 
2422   /**
2423    * To set the rank at startup of the request if the request specify a
2424    * specific
2425    * rank
2426    *
2427    * @param rank the rank to set
2428    */
2429   public final void setRankAtStartup(final int rank) {
2430     if (pojo.getRank() > rank) {
2431       isSaved = false;
2432       pojo.setRank(rank);
2433     }
2434   }
2435 
2436   /**
2437    * @param blocksize the block size to set
2438    */
2439   public final void setBlocksize(final int blocksize) {
2440     if (pojo.getBlockSize() != blocksize) {
2441       isSaved = false;
2442       isOtherThanStatus = true;
2443       pojo.setBlockSize(blocksize);
2444     }
2445   }
2446 
2447   /**
2448    * @param filename the filename to set
2449    */
2450   public final void setFilename(final String filename) {
2451     if (pojo.getFilename() == null || !pojo.getFilename().equals(filename)) {
2452       isSaved = false;
2453       isOtherThanStatus = true;
2454       pojo.setFilename(filename);
2455     }
2456   }
2457 
2458   /**
2459    * @param newFilename the new Filename to set
2460    * @param isFileMoved the isFileMoved to set
2461    */
2462   public final void setFileMoved(final String newFilename,
2463                                  final boolean isFileMoved) {
2464     isSaved = false;
2465     isOtherThanStatus = true;
2466     pojo.setIsMoved(isFileMoved);
2467     pojo.setFilename(newFilename);
2468   }
2469 
2470   /**
2471    * @param originalFilename the originalFilename to set
2472    */
2473   public final void setOriginalFilename(final String originalFilename) {
2474     if (pojo.getOriginalName() == null ||
2475         !pojo.getOriginalName().equals(originalFilename)) {
2476       isSaved = false;
2477       isOtherThanStatus = true;
2478       pojo.setOriginalName(originalFilename);
2479     }
2480   }
2481 
2482   /**
2483    * @return the rank
2484    */
2485   public final int getRank() {
2486     return pojo.getRank();
2487   }
2488 
2489   /**
2490    * Change the status from Task Execution
2491    *
2492    * @param status
2493    */
2494   public final void setExecutionStatus(final ErrorCode status) {
2495     if (pojo.getStepStatus() != status) {
2496       isSaved = false;
2497       pojo.setStepStatus(status);
2498     }
2499   }
2500 
2501   /**
2502    * @return the status
2503    */
2504   public final ErrorCode getStatus() {
2505     return pojo.getStepStatus();
2506   }
2507 
2508   /**
2509    * @return the isSender
2510    */
2511   public final boolean isSender() {
2512     return pojo.getRetrieveMode();
2513   }
2514 
2515   /**
2516    * @return if is Sender from Session
2517    */
2518   public final boolean isSessionSender() {
2519     if (session == null) {
2520       return isSender();
2521     }
2522     return session.isSender();
2523   }
2524 
2525   /**
2526    * @return the isFileMoved
2527    */
2528   public final boolean isFileMoved() {
2529     return pojo.getIsMoved();
2530   }
2531 
2532   /**
2533    * @return the blocksize
2534    */
2535   public final int getBlocksize() {
2536     return pojo.getBlockSize();
2537   }
2538 
2539   /**
2540    * @return the filename
2541    */
2542   public final String getFilename() {
2543     return pojo.getFilename();
2544   }
2545 
2546   /**
2547    * @return the originalFilename
2548    */
2549   public final String getOriginalFilename() {
2550     return pojo.getOriginalName();
2551   }
2552 
2553   /**
2554    * Utility to get possible Json from File information to Transfer information
2555    */
2556   private void setMapFromFileInfo() {
2557     final Map<String, Object> mapFileInfo =
2558         getMapFromString(getFileInformation());
2559     final boolean isBlocCompressed =
2560         AbstractTask.isCompressionRequested(getFileInformation(), session);
2561     if (isBlocCompressed && (session == null || (session != null &&
2562                                                  session.isCompressionEnabled()))) {
2563       mapFileInfo.put(JSON_COMPRESSION, true);
2564     } else {
2565       mapFileInfo.remove(JSON_COMPRESSION);
2566     }
2567     final Map<String, Object> mapTransferInfo =
2568         getMapFromString(getTransferInfo());
2569     mapTransferInfo.putAll(mapFileInfo);
2570     setTransferMap(mapTransferInfo);
2571   }
2572 
2573   /**
2574    * Initialize the internal Object
2575    */
2576   private void initializeTransferInfo() {
2577     if (transferMap == null) {
2578       transferMap = getTransferMap();
2579     }
2580   }
2581 
2582   /**
2583    * @param smap the source of the map
2584    *
2585    * @return the Map<String, Object> from the content of the
2586    *     argument
2587    */
2588   public static Map<String, Object> getMapFromString(final String smap) {
2589     final Pattern pattern = Pattern.compile("\\{[^\\}]*\\}");
2590     final Matcher matcher = pattern.matcher(JsonHandler.unEscape(smap));
2591     final StringBuilder map = new StringBuilder("{");
2592     while (matcher.find()) {
2593       final String temp = matcher.group(0);
2594       if (temp.length() > 5) { // {a:a} = 5
2595         if (map.length() != 1) {
2596           map.append(", ");
2597         }
2598         map.append(temp, 1, temp.length() - 1);
2599       }
2600     }
2601     map.append("}");
2602     return JsonHandler.getMapFromString(map.toString());
2603   }
2604 
2605   /**
2606    * @param smap the source of the map
2607    *
2608    * @return the String without the Map<String, Object> from the content of the
2609    *     argument
2610    */
2611   public static String getOutOfMapFromString(final String smap) {
2612     return smap.replaceAll("\\{[^\\}]*\\}", "");
2613   }
2614 
2615   /**
2616    * @return the Map<String, Object> from the content of the
2617    *     transferInformation
2618    */
2619   public final Map<String, Object> getTransferMap() {
2620     if (transferMap != null) {
2621       return transferMap;
2622     }
2623     final String noMap = getOtherInfoOutOfMap();
2624     final Map<String, Object> map = getMapFromString(pojo.getTransferInfo());
2625     internalSetNoMapMap(map, noMap);
2626     return map;
2627   }
2628 
2629   private String getOtherInfoOutOfMap() {
2630     return getOutOfMapFromString(pojo.getTransferInfo());
2631   }
2632 
2633   /**
2634    * @param map the Map to add as Json string to transferInformation
2635    */
2636   public final void setTransferMap(final Map<String, ?> map) {
2637     isSaved = false;
2638     isOtherThanStatus = true;
2639     transferMap = (Map<String, Object>) map;
2640   }
2641 
2642   private void internalSetNoMapMap(final Map<String, ?> map,
2643                                    final String noMap) {
2644     transferMap = (Map<String, Object>) map;
2645     if (noMap == null || noMap.isEmpty()) {
2646       pojo.setTransferInfo("");
2647     } else {
2648       pojo.setTransferInfo(noMap);
2649     }
2650   }
2651 
2652 
2653   /**
2654    * @param transferInfo the transfer Information to set
2655    */
2656   public final void setTransferInfo(final String transferInfo) {
2657     final String noMap = getOutOfMapFromString(transferInfo).trim();
2658     if (transferMap != null) {
2659       transferMap.clear();
2660       transferMap = null;
2661     }
2662     final Map<String, Object> map = getMapFromString(transferInfo);
2663     if (transferMap == null || !transferMap.equals(map) ||
2664         !pojo.getTransferInfo().equals(noMap)) {
2665       isSaved = false;
2666       isOtherThanStatus = true;
2667       internalSetNoMapMap(map, noMap);
2668     }
2669   }
2670 
2671   /**
2672    * Helper to set a new (key, value) in the map Transfer
2673    *
2674    * @param key
2675    * @param value
2676    */
2677   public final void addToTransferMap(final String key, final Object value) {
2678     final Map<String, Object> map =
2679         transferMap != null? transferMap : getTransferMap();
2680     map.put(key, value);
2681     setTransferMap(map);
2682   }
2683 
2684   /**
2685    * Helper to remove a (key, value) from the map Transfer
2686    *
2687    * @param key
2688    */
2689   public final void removeFromTransferMap(final String key) {
2690     final Map<String, Object> map =
2691         transferMap != null? transferMap : getTransferMap();
2692     map.remove(key);
2693     setTransferMap(map);
2694   }
2695 
2696   /**
2697    * @param key
2698    *
2699    * @return the associated value or null if it does not exist
2700    */
2701   public final Object getFromTransferMap(final String key) {
2702     return getTransferMap().get(key);
2703   }
2704 
2705   /**
2706    * @param size the new size value to set in TransferMap
2707    */
2708   private void setOriginalSizeTransferMap(final long size) {
2709     addToTransferMap(JSON_ORIGINALSIZE, size);
2710   }
2711 
2712   /**
2713    * @return the size set in TransferMap
2714    */
2715   private long getOriginalSizeTransferMap() {
2716     final Object size = getFromTransferMap(JSON_ORIGINALSIZE);
2717     if (size == null) {
2718       return -1;
2719     }
2720     if (size instanceof Long) {
2721       return (Long) size;
2722     } else {
2723       return (Integer) size;
2724     }
2725   }
2726 
2727   /**
2728    * @return the Follow Id or null if not exists
2729    */
2730   public final String getFollowId() {
2731     final Object followId = getFromTransferMap(FOLLOW_JSON_KEY);
2732     if (followId != null) {
2733       return followId.toString();
2734     }
2735     return null;
2736   }
2737 
2738   /**
2739    * @param followId the followId to set
2740    */
2741   public final void setFollowId(final long followId) {
2742     addToTransferMap(FOLLOW_JSON_KEY, followId);
2743   }
2744 
2745   /**
2746    * @return True if Block Compression is active (meaning both partners
2747    *     allow it and the transfer asked for it through '#COMPRESS#')
2748    */
2749   public final boolean isBlockCompression() {
2750     final Object compression = getFromTransferMap(JSON_COMPRESSION);
2751     if (logger.isDebugEnabled()) {
2752       logger.debug("current value {} {} => {}", pojo.getTransferInfo(),
2753                    transferMap, compression != null? compression : false,
2754                    new Exception("Trace for " + "debugging"));
2755     }
2756     if (compression != null) {
2757       if (compression instanceof Boolean) {
2758         return (Boolean) compression;
2759       } else {
2760         return Boolean.parseBoolean(compression.toString());
2761       }
2762     }
2763     return false;
2764   }
2765 
2766   /**
2767    * @param compression True if the Bock Compression is active
2768    */
2769   public final void setBlockCompression(final boolean compression) {
2770     final Object compressionObject = getFromTransferMap(JSON_COMPRESSION);
2771     if (compressionObject != null || compression) {
2772       addToTransferMap(JSON_COMPRESSION, compression);
2773     }
2774     if (!compression) {
2775       removeFromTransferMap(JSON_COMPRESSION);
2776     }
2777   }
2778 
2779   /**
2780    * @return the Transfer Information (internal informations)
2781    */
2782   public final String getTransferInfo() {
2783     if (transferMap == null) {
2784       return pojo.getTransferInfo();
2785     }
2786     final String nomap = pojo.getTransferInfo();
2787     final String result =
2788         (ParametersChecker.isEmpty(nomap)? "" : nomap.trim() + " ") +
2789         JsonHandler.writeAsString(transferMap).trim();
2790     return result;
2791   }
2792 
2793   /**
2794    * @return the fileInformation
2795    */
2796   public final String getFileInformation() {
2797     return pojo.getFileInfo();
2798   }
2799 
2800   /**
2801    * Set a new File information for this transfer
2802    *
2803    * @param newFileInformation
2804    */
2805   public final void setFileInformation(final String newFileInformation) {
2806     if (pojo.getFileInfo() == null ||
2807         !pojo.getFileInfo().equals(newFileInformation)) {
2808       pojo.setFileInfo(newFileInformation);
2809       isOtherThanStatus = true;
2810       isSaved = false;
2811       setMapFromFileInfo();
2812     }
2813   }
2814 
2815   /**
2816    * @return the specialId
2817    */
2818   public final long getSpecialId() {
2819     return pojo.getId();
2820   }
2821 
2822   /**
2823    * @return the rule
2824    */
2825   public final DbRule getRule() {
2826     if (rule == null && getRuleId() != null) {
2827       try {
2828         rule = new DbRule(getRuleId());
2829       } catch (final WaarpDatabaseException ignored) {
2830         // nothing
2831       }
2832     }
2833     return rule;
2834   }
2835 
2836   /**
2837    * @return the ruleId
2838    */
2839   public final String getRuleId() {
2840     return pojo.getRule();
2841   }
2842 
2843   /**
2844    * @param ruleId the RuleId to set
2845    *
2846    * @throws WaarpDatabaseException if the RuleId is wrong
2847    */
2848   public final void setRuleId(final String ruleId)
2849       throws WaarpDatabaseException {
2850     if (rule == null || !rule.getIdRule().equals(ruleId)) {
2851       rule = new DbRule(ruleId);
2852       pojo.setRule(ruleId);
2853       isSaved = false;
2854       isOtherThanStatus = true;
2855     }
2856   }
2857 
2858   /**
2859    * @return the mode
2860    */
2861   public final int getMode() {
2862     return pojo.getTransferMode();
2863   }
2864 
2865   /**
2866    * @return the globalstep
2867    */
2868   public final TASKSTEP getGlobalStep() {
2869     return pojo.getGlobalStep().toLegacy();
2870   }
2871 
2872   /**
2873    * @return the globalstep
2874    */
2875   public final TASKSTEP getLastGlobalStep() {
2876     return pojo.getLastGlobalStep().toLegacy();
2877   }
2878 
2879   /**
2880    * @return the globallaststep
2881    */
2882   public final int getGloballaststep() {
2883     return pojo.getLastGlobalStep().ordinal();
2884   }
2885 
2886   /**
2887    * @return True if this runner is ready for transfer or post operation
2888    */
2889   public final boolean ready() {
2890     return pojo.getGlobalStep() != Transfer.TASKSTEP.NOTASK &&
2891            pojo.getGlobalStep() != Transfer.TASKSTEP.PRETASK;
2892   }
2893 
2894   /**
2895    * @return True if the runner is currently in transfer
2896    */
2897   public final boolean isInTransfer() {
2898     return pojo.getGlobalStep() == Transfer.TASKSTEP.TRANSFERTASK;
2899   }
2900 
2901   /**
2902    * @return True if this runner is finished, either in success or in error
2903    */
2904   public final boolean isFinished() {
2905     return isAllDone() || isInError();
2906   }
2907 
2908   /**
2909    * @return True if this runner is in error and no more running
2910    */
2911   public final boolean isInError() {
2912     return pojo.getGlobalStep() == Transfer.TASKSTEP.ERRORTASK &&
2913            pojo.getStepStatus() != ErrorCode.Running;
2914   }
2915 
2916   /**
2917    * @return True if the runner is finished in success
2918    */
2919   public final boolean isAllDone() {
2920     return pojo.getGlobalStep() == Transfer.TASKSTEP.ALLDONETASK;
2921   }
2922 
2923   /**
2924    * To be called before executing Pre execution
2925    *
2926    * @return True if the task is going to run PRE task from the first action
2927    */
2928   public final boolean isPreTaskStarting() {
2929     if (pojo.getLastGlobalStep() == Transfer.TASKSTEP.PRETASK ||
2930         pojo.getLastGlobalStep() == Transfer.TASKSTEP.NOTASK) {
2931       return pojo.getStep() - 1 <= 0;
2932     }
2933     return false;
2934   }
2935 
2936   /**
2937    * Set the Initial Task step (before Pre task)
2938    */
2939   public final void setInitialTask() {
2940     setStopNow();
2941     isSaved = false;
2942     pojo.setGlobalStep(Transfer.TASKSTEP.NOTASK);
2943     pojo.setLastGlobalStep(Transfer.TASKSTEP.NOTASK);
2944     pojo.setStep(-1);
2945     pojo.setStepStatus(ErrorCode.Running);
2946     pojo.setInfoStatus(ErrorCode.Unknown);
2947     pojo.setUpdatedInfo(org.waarp.openr66.pojo.UpdatedInfo.valueOf(
2948         UpdatedInfo.RUNNING.ordinal()));
2949   }
2950 
2951   /**
2952    * Set Pre Task step
2953    */
2954   public final void setPreTask() {
2955     setStopNow();
2956     isSaved = false;
2957     pojo.setGlobalStep(Transfer.TASKSTEP.PRETASK);
2958     pojo.setLastGlobalStep(Transfer.TASKSTEP.PRETASK);
2959     final int step = pojo.getStep();
2960     if (step <= 0) {
2961       pojo.setStep(0);
2962     } else {
2963       pojo.setStep(step - 1);
2964     }
2965     pojo.setStepStatus(ErrorCode.Running);
2966     pojo.setInfoStatus(ErrorCode.InitOk);
2967     pojo.setUpdatedInfo(org.waarp.openr66.pojo.UpdatedInfo.valueOf(
2968         UpdatedInfo.RUNNING.ordinal()));
2969   }
2970 
2971   /**
2972    * Set Transfer rank
2973    *
2974    * @param rank
2975    */
2976   public final void setTransferTask(final int rank) {
2977     setStopNow();
2978     isSaved = false;
2979     pojo.setGlobalStep(Transfer.TASKSTEP.TRANSFERTASK);
2980     pojo.setLastGlobalStep(Transfer.TASKSTEP.TRANSFERTASK);
2981     final int lastRank = pojo.getRank();
2982     if (lastRank > rank) {
2983       pojo.setRank(rank);
2984     }
2985     pojo.setStepStatus(ErrorCode.Running);
2986     pojo.setInfoStatus(ErrorCode.PreProcessingOk);
2987   }
2988 
2989   /**
2990    * Set the Post Task step
2991    */
2992   public final void setPostTask() {
2993     setStopNow();
2994     isSaved = false;
2995     pojo.setGlobalStep(Transfer.TASKSTEP.POSTTASK);
2996     pojo.setLastGlobalStep(Transfer.TASKSTEP.POSTTASK);
2997     final int step = pojo.getStep();
2998     if (step <= 0) {
2999       pojo.setStep(0);
3000     } else {
3001       pojo.setStep(step - 1);
3002     }
3003     pojo.setStepStatus(ErrorCode.Running);
3004     pojo.setInfoStatus(ErrorCode.TransferOk);
3005   }
3006 
3007   /**
3008    * Set the Error Task step
3009    */
3010   public final void setErrorTask() {
3011     setStopNow();
3012     isSaved = false;
3013     pojo.setGlobalStep(Transfer.TASKSTEP.ERRORTASK);
3014     pojo.setStep(0);
3015     pojo.setStepStatus(ErrorCode.Running);
3016   }
3017 
3018   /**
3019    * Set the global step as finished (after post task in success)
3020    */
3021   public final void setAllDone() {
3022     setStopNow();
3023     isSaved = false;
3024     pojo.setGlobalStep(Transfer.TASKSTEP.ALLDONETASK);
3025     pojo.setLastGlobalStep(Transfer.TASKSTEP.ALLDONETASK);
3026     pojo.setStep(0);
3027     pojo.setStepStatus(ErrorCode.CompleteOk);
3028     pojo.setInfoStatus(ErrorCode.CompleteOk);
3029     pojo.setUpdatedInfo(
3030         org.waarp.openr66.pojo.UpdatedInfo.valueOf(UpdatedInfo.DONE.ordinal()));
3031   }
3032 
3033   /**
3034    * Set the status of the transfer
3035    *
3036    * @param code TransferOk if success
3037    *
3038    * @return the current rank of transfer
3039    */
3040   public final int finishTransferTask(final ErrorCode code) {
3041     setStopNow();
3042     if (code == ErrorCode.TransferOk) {
3043       pojo.setStepStatus(code);
3044       pojo.setInfoStatus(code);
3045       isSaved = false;
3046     } else {
3047       continueTransfer = false;
3048       final ErrorCode infostatus = pojo.getInfoStatus();
3049       if (infostatus == ErrorCode.InitOk ||
3050           infostatus == ErrorCode.PostProcessingOk ||
3051           infostatus == ErrorCode.PreProcessingOk ||
3052           infostatus == ErrorCode.Running ||
3053           infostatus == ErrorCode.TransferOk) {
3054         pojo.setInfoStatus(code);
3055         isSaved = false;
3056       }
3057       if (!pojo.getUpdatedInfo().equals(UpdatedInfo.INTERRUPTED)) {
3058         pojo.setUpdatedInfo(org.waarp.openr66.pojo.UpdatedInfo.valueOf(
3059             UpdatedInfo.INERROR.ordinal()));
3060         isSaved = false;
3061       }
3062     }
3063     return pojo.getRank();
3064   }
3065 
3066   /**
3067    * @return True if the transfer is valid to continue
3068    */
3069   public final boolean continueTransfer() {
3070     return continueTransfer;
3071   }
3072 
3073   /**
3074    * Run the task from the given task information (from rule)
3075    *
3076    * @param tasks
3077    *
3078    * @return The future of the operation (in success or not)
3079    *
3080    * @throws OpenR66RunnerEndTasksException
3081    * @throws OpenR66RunnerErrorException
3082    */
3083   private R66Future runNextTask(final String[][] tasks)
3084       throws OpenR66RunnerEndTasksException, OpenR66RunnerErrorException {
3085     if (logger.isDebugEnabled()) {
3086       logger.debug("{}:{}:{}:{}:{} Sender: {} {}", (session == null),
3087                    session == null? "norunner" : session.getRunner() == null,
3088                    toLogRunStep(), getStep(),
3089                    tasks == null? "null" : tasks.length, isSessionSender(),
3090                    rule.printTasks(isSessionSender(), getGlobalStep()));
3091     }
3092     if (tasks == null) {
3093       throw new OpenR66RunnerEndTasksException("No tasks!");
3094     }
3095     R66Session tempSession = session;
3096     if (tempSession == null) {
3097       tempSession = new R66Session();
3098       if (tempSession.getRunner() == null) {
3099         tempSession.setNoSessionRunner(this, localChannelReference);
3100       }
3101     } else {
3102       if (tempSession.getRunner() == null) {
3103         tempSession.setNoSessionRunner(this,
3104                                        tempSession.getLocalChannelReference());
3105       }
3106     }
3107     session = tempSession;
3108     final LocalChannelReference lcr = session.getLocalChannelReference();
3109     if (lcr != null && lcr.getCurrentCode() == ErrorCode.Unknown) {
3110       session.getLocalChannelReference()
3111              .setErrorMessage(getErrorInfo().getMesg(), getErrorInfo());
3112     }
3113     if (tasks.length <= getStep()) {
3114       throw new OpenR66RunnerEndTasksException();
3115     }
3116     // Possible long task
3117     NetworkServerHandler nsh = null;
3118     if (lcr != null) {
3119       nsh = lcr.getNetworkServerHandler();
3120       if (nsh != null) {
3121         nsh.resetKeepAlive();
3122       }
3123     }
3124     final AbstractTask task = getTask(tasks[getStep()], tempSession);
3125     if (logger.isDebugEnabled()) {
3126       logger.debug("{} Task: {}", toLogRunStep(), task.getClass().getName());
3127     }
3128     task.run();
3129     task.getFutureCompletion().awaitOrInterruptible();
3130     // Possible long task
3131     if (nsh != null) {
3132       nsh.resetKeepAlive();
3133     }
3134     if (task.getType() == TaskType.RESCHEDULE) {
3135       // Special case : must test if exec is OK since it must be the last
3136       if (isRescheduledTransfer()) {
3137         throw new OpenR66RunnerEndTasksException();
3138       }
3139     }
3140     setStopNow();
3141     return task.getFutureCompletion();
3142   }
3143 
3144   /**
3145    * @param task
3146    * @param tempSession
3147    *
3148    * @return the corresponding AbstractTask
3149    *
3150    * @throws OpenR66RunnerErrorException
3151    */
3152   public final AbstractTask getTask(final String[] task,
3153                                     final R66Session tempSession)
3154       throws OpenR66RunnerErrorException {
3155     final String name = task[0];
3156     final String arg = task[1];
3157     final int delay;
3158     try {
3159       delay = Integer.parseInt(task[2]);
3160     } catch (final NumberFormatException e) {
3161       logger.warn("Malformed task so stop the execution: " + toShortString());
3162       throw new OpenR66RunnerErrorException(
3163           "Malformed task so stop the execution");
3164     }
3165     return TaskType.getTaskFromId(name, arg, delay, tempSession);
3166   }
3167 
3168   /**
3169    * @return the future of the task run
3170    *
3171    * @throws OpenR66RunnerEndTasksException
3172    * @throws OpenR66RunnerErrorException
3173    * @throws OpenR66RunnerEndTasksException
3174    */
3175   private R66Future runNext()
3176       throws OpenR66RunnerErrorException, OpenR66RunnerEndTasksException {
3177     if (rule == null) {
3178       if (getRuleId() != null) {
3179         try {
3180           rule = new DbRule(getRuleId());
3181         } catch (final WaarpDatabaseException e) {
3182           rule = null;
3183         }
3184       }
3185       if (rule == null) {
3186         throw new OpenR66RunnerErrorException("Rule Object not initialized");
3187       }
3188     }
3189     if (logger.isDebugEnabled()) {
3190       logger.debug("{} Sender: {} {}", toLogRunStep(), isSessionSender(),
3191                    rule.printTasks(isSessionSender(), getGlobalStep()));
3192     }
3193     switch (getGlobalStep()) {
3194       case PRETASK:
3195         try {
3196           if (isSessionSender()) {
3197             return runNextTask(rule.getSpreTasksArray());
3198           } else {
3199             return runNextTask(rule.getRpreTasksArray());
3200           }
3201         } catch (final OpenR66RunnerEndTasksException e) {
3202           if (getStatus() == ErrorCode.Running) {
3203             setExecutionStatus(ErrorCode.PreProcessingOk);
3204             setErrorExecutionStatus(ErrorCode.PreProcessingOk);
3205           }
3206           throw e;
3207         }
3208       case POSTTASK:
3209         try {
3210           if (isSessionSender()) {
3211             return runNextTask(rule.getSpostTasksArray());
3212           } else {
3213             return runNextTask(rule.getRpostTasksArray());
3214           }
3215         } catch (final OpenR66RunnerEndTasksException e) {
3216           if (getStatus() == ErrorCode.Running) {
3217             setExecutionStatus(ErrorCode.PostProcessingOk);
3218             setErrorExecutionStatus(ErrorCode.PostProcessingOk);
3219           }
3220           throw e;
3221         }
3222       case ERRORTASK:
3223         try {
3224           if (isSessionSender()) {
3225             return runNextTask(rule.getSerrorTasksArray());
3226           } else {
3227             return runNextTask(rule.getRerrorTasksArray());
3228           }
3229         } catch (final OpenR66RunnerEndTasksException e) {
3230           if (getStatus() == ErrorCode.Running) {
3231             setExecutionStatus(getErrorInfo());
3232           }
3233           throw e;
3234         }
3235       default:
3236         throw new OpenR66RunnerErrorException("Global Step unknown");
3237     }
3238   }
3239 
3240   /**
3241    * Run all task from current status (globalstep and step)
3242    *
3243    * @throws OpenR66RunnerErrorException
3244    */
3245   public final void run() throws OpenR66RunnerErrorException {
3246     R66Future future;
3247     if (logger.isDebugEnabled()) {
3248       try {
3249         logger.debug("{} Status: {} Sender: {} {}", toLogRunStep(), getStatus(),
3250                      isSessionSender(),
3251                      rule.printTasks(isSessionSender(), getGlobalStep()));
3252       } catch (final NullPointerException ignored) {
3253         // Ignored
3254       }
3255     }
3256     if (getStatus() != ErrorCode.Running) {
3257       throw new OpenR66RunnerErrorException(
3258           "Current global STEP not ready to run: " + this);
3259     }
3260     while (true) {
3261       if (logger.isDebugEnabled()) {
3262         logger.debug(toLogRunStep());
3263       }
3264       try {
3265         future = runNext();
3266       } catch (final OpenR66RunnerEndTasksException e) {
3267         if (pojo.getStep() != 0) {
3268           pojo.setStep(0);
3269           isSaved = false;
3270         }
3271         return;
3272       } catch (final OpenR66RunnerErrorException e) {
3273         setErrorExecutionStatus(ErrorCode.ExternalOp);
3274         saveStatus();
3275         throw new OpenR66RunnerErrorException(
3276             "Runner is in error: " + e.getMessage(), e);
3277       }
3278       if (!future.isDone() || future.isFailed()) {
3279         final R66Result result = future.getResult();
3280         if (result != null) {
3281           setErrorExecutionStatus(future.getResult().getCode());
3282         } else {
3283           setErrorExecutionStatus(ErrorCode.ExternalOp);
3284         }
3285         saveStatus();
3286         logger.info("Future is failed: {}", getErrorInfo().getMesg());
3287         if (future.getCause() != null) {
3288           throw new OpenR66RunnerErrorException(
3289               "Runner is failed: " + future.getCause().getMessage(),
3290               future.getCause());
3291         } else {
3292           throw new OpenR66RunnerErrorException(
3293               "Runner is failed: " + getErrorInfo().getMesg());
3294         }
3295       }
3296       pojo.setStep(getStep() + 1);
3297       isSaved = false;
3298     }
3299   }
3300 
3301   /**
3302    * Once the transfer is over, finalize the Runner by running the error or
3303    * post
3304    * operation according to the
3305    * status.
3306    *
3307    * @param localChannelReference
3308    * @param file
3309    * @param finalValue
3310    * @param status
3311    *
3312    * @throws OpenR66RunnerErrorException
3313    * @throws OpenR66ProtocolSystemException
3314    */
3315   public final void finalizeTransfer(
3316       LocalChannelReference localChannelReference, final R66File file,
3317       final R66Result finalValue, final boolean status)
3318       throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException {
3319     logger.debug("status: {}:{}", status, finalValue);
3320 
3321     if (session == null) {
3322       if (localChannelReference == null) {
3323         return;
3324       }
3325       session = localChannelReference.getSession();
3326     }
3327     if (localChannelReference == null) {
3328       localChannelReference = session.getLocalChannelReference();
3329     }
3330     if (status) {
3331       internalFinalizeValid(localChannelReference, file, finalValue);
3332     } else {
3333       logger.debug("ContinueTransfer: {} status:{}:{}", continueTransfer,
3334                    status, finalValue);
3335       if (finalValue.getException() == null) {
3336         finalValue.setException(new OpenR66RunnerException(TRACE_FOR_ERROR));
3337       }
3338       errorTransfer(finalValue, file, localChannelReference);
3339     }
3340   }
3341 
3342   private void internalFinalizeValid(
3343       final LocalChannelReference localChannelReference, final R66File file,
3344       final R66Result finalValue)
3345       throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException {
3346     // First move the file
3347     if (session.isSender()) {
3348       // Nothing to do since it is the original file
3349       setPostTask();
3350     } else {
3351       finalizeReceiver(localChannelReference, file, finalValue);
3352     }
3353     if (isRecvThrough() || isSendThrough()) {
3354       // File could not exist
3355     } else if (getStep() == 0) {
3356       // File must exist
3357       try {
3358         if (!file.exists()) {
3359           // error
3360           final R66Result error = new R66Result(
3361               new OpenR66RunnerException(ErrorCode.FileNotFound.getMesg()),
3362               session, finalValue.isAnswered(), ErrorCode.FileNotFound, this);
3363           setErrorExecutionStatus(ErrorCode.FileNotFound);
3364           errorTransfer(error, file, localChannelReference);
3365           return;
3366         }
3367       } catch (final CommandAbstractException e) {
3368         // error
3369         final R66Result error = new R66Result(
3370             new OpenR66RunnerException(ErrorCode.FileNotFound.getMesg()),
3371             session, finalValue.isAnswered(), ErrorCode.FileNotFound, this);
3372         setErrorExecutionStatus(ErrorCode.FileNotFound);
3373         errorTransfer(error, file, localChannelReference);
3374         return;
3375       }
3376     }
3377     try {
3378       run();
3379     } catch (final OpenR66RunnerErrorException e1) {
3380       final R66Result result =
3381           new R66Result(e1, session, false, ErrorCode.ExternalOp, this);
3382       result.setFile(file);
3383       result.setRunner(this);
3384       changeUpdatedInfo(UpdatedInfo.INERROR);
3385       saveStatus();
3386       result.setException(e1);
3387       errorTransfer(result, file, localChannelReference);
3388       if (localChannelReference != null) {
3389         localChannelReference.invalidateRequest(result);
3390       }
3391       throw e1;
3392     }
3393     saveStatus();
3394     /*
3395      * Done later on after EndRequest this.setAllDone() this.saveStatus()
3396      */
3397     logger.info("Transfer done on {} at RANK {}",
3398                 file != null? file : "no file", getRank());
3399     if (localChannelReference != null) {
3400       localChannelReference.validateEndTransfer(finalValue);
3401     }
3402   }
3403 
3404   private void finalizeReceiver(
3405       final LocalChannelReference localChannelReference, final R66File file,
3406       final R66Result finalValue)
3407       throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException {
3408     final int poststep = getStep();
3409     setPostTask();
3410     // in case of error
3411     final R66Result error =
3412         new R66Result(session, finalValue.isAnswered(), ErrorCode.FinalOp,
3413                       this);
3414     if (!isRecvThrough() && (getGlobalStep() == TASKSTEP.TRANSFERTASK ||
3415                              getGlobalStep() == TASKSTEP.POSTTASK &&
3416                              poststep == 0)) {
3417       resultFileMove(localChannelReference, file, error);
3418       // check if necessary once more the hash
3419       if (Configuration.configuration.isLocalDigest()) {
3420         String hash = null;
3421         if (localChannelReference != null &&
3422             !localChannelReference.isPartialHash() &&
3423             !localChannelReference.getPartner().useFinalHash()) {
3424           // If partner is using final hash, not necessary since both
3425           // sides already checked already during end of transfer
3426           hash = localChannelReference.getHashComputeDuringTransfer();
3427         }
3428         if (hash != null) {
3429           // we can compute it once more
3430           try {
3431             if (!FilesystemBasedDigest.getHex(
3432                                           FilesystemBasedDigest.getHash(file.getTrueFile(), true,
3433                                                                         Configuration.configuration.getDigest()))
3434                                       .equals(hash)) {
3435               // KO
3436               final R66Result result = new R66Result(
3437                   new OpenR66RunnerErrorException(
3438                       "Bad final digest on receive operation"), session, false,
3439                   ErrorCode.FinalOp, this);
3440               result.setFile(file);
3441               result.setRunner(this);
3442               localChannelReference.invalidateRequest(result);
3443               error.setException(result.getException());
3444               errorTransfer(error, file, localChannelReference);
3445               throw (OpenR66RunnerErrorException) result.getException();
3446             }
3447           } catch (final IOException e) {
3448             final R66Result result = new R66Result(
3449                 new OpenR66RunnerErrorException(
3450                     "Bad final digest on receive operation", e), session, false,
3451                 ErrorCode.FinalOp, this);
3452             result.setFile(file);
3453             result.setRunner(this);
3454             localChannelReference.invalidateRequest(result);
3455             error.setException(result.getException());
3456             errorTransfer(error, file, localChannelReference);
3457             throw (OpenR66RunnerErrorException) result.getException();
3458           }
3459         }
3460       }
3461     }
3462   }
3463 
3464   private void resultFileMove(final LocalChannelReference localChannelReference,
3465                               final R66File file, final R66Result error)
3466       throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException {
3467     // Result file moves
3468     final String finalpath = R66Dir.getFinalUniqueFilename(file);
3469     logger.debug("Will move file {}", finalpath);
3470     try {
3471       if (!file.renameTo(getRule().setRecvPath(finalpath))) {
3472         final OpenR66ProtocolSystemException e =
3473             new OpenR66ProtocolSystemException(
3474                 "Cannot move file to final position");
3475         final R66Result result =
3476             new R66Result(e, session, false, ErrorCode.FinalOp, this);
3477         result.setFile(file);
3478         result.setRunner(this);
3479         if (localChannelReference != null) {
3480           localChannelReference.invalidateRequest(result);
3481         }
3482         error.setException(result.getException());
3483         errorTransfer(error, file, localChannelReference);
3484         throw e;
3485       }
3486     } catch (final OpenR66ProtocolSystemException e) {
3487       final R66Result result =
3488           new R66Result(e, session, false, ErrorCode.FinalOp, this);
3489       result.setFile(file);
3490       result.setRunner(this);
3491       if (localChannelReference != null) {
3492         localChannelReference.invalidateRequest(result);
3493       }
3494       error.setException(result.getException());
3495       errorTransfer(error, file, localChannelReference);
3496       throw e;
3497     } catch (final CommandAbstractException e) {
3498       final R66Result result =
3499           new R66Result(new OpenR66RunnerErrorException(e), session, false,
3500                         ErrorCode.FinalOp, this);
3501       result.setFile(file);
3502       result.setRunner(this);
3503       if (localChannelReference != null) {
3504         localChannelReference.invalidateRequest(result);
3505       }
3506       error.setException(result.getException());
3507       errorTransfer(error, file, localChannelReference);
3508       throw (OpenR66RunnerErrorException) result.getException();
3509     }
3510     logger.debug("File finally moved: {}", file);
3511     try {
3512       setFilename(file.getFile());
3513     } catch (final CommandAbstractException ignored) {
3514       // nothing
3515     }
3516   }
3517 
3518   /**
3519    * Finalize a transfer in error
3520    *
3521    * @param finalValue
3522    * @param file
3523    * @param localChannelReference
3524    *
3525    * @throws OpenR66RunnerErrorException
3526    */
3527   private void errorTransfer(final R66Result finalValue, final R66File file,
3528                              final LocalChannelReference localChannelReference)
3529       throws OpenR66RunnerErrorException {
3530     // error or not ?
3531     final ErrorCode runnerStatus = getErrorInfo();
3532     if (finalValue.getException() != null) {
3533       logger.error("Transfer KO on " + file + " due to " +
3534                    finalValue.getException().getMessage());
3535       if (TRACE_FOR_ERROR.equalsIgnoreCase(
3536           finalValue.getException().getMessage())) {
3537         logger.error(finalValue.getException());
3538       }
3539     } else {
3540       logger.error("Transfer KO on " + file + " due to " + finalValue);
3541     }
3542     if (runnerStatus == ErrorCode.CanceledTransfer) {
3543       // delete file, reset runner
3544       setRankAtStartup(0);
3545       deleteTempFile();
3546       changeUpdatedInfo(UpdatedInfo.INERROR);
3547       saveStatus();
3548       finalValue.setAnswered(true);
3549     } else if (runnerStatus == ErrorCode.StoppedTransfer ||
3550                runnerStatus == ErrorCode.Shutdown) {
3551       // just save runner and stop
3552       changeUpdatedInfo(UpdatedInfo.INERROR);
3553       saveStatus();
3554       finalValue.setAnswered(true);
3555     }
3556     logger.debug("status: {} wasNotError:{}:{}", getStatus(),
3557                  getGlobalStep() != TASKSTEP.ERRORTASK, finalValue);
3558     if (getGlobalStep() != TASKSTEP.ERRORTASK) {
3559       // errorstep was not already executed
3560       // real error
3561       if (localChannelReference != null) {
3562         localChannelReference.setErrorMessage(finalValue.getMessage(),
3563                                               finalValue.getCode());
3564       }
3565       // First send error mesg
3566       if (!finalValue.isAnswered() && localChannelReference != null) {
3567         localChannelReference.sessionNewState(R66FiniteDualStates.ERROR);
3568         final ErrorPacket errorPacket = new ErrorPacket(finalValue.getMessage(),
3569                                                         finalValue.getCode()
3570                                                                   .getCode(),
3571                                                         ErrorPacket.FORWARDCLOSECODE);
3572         try {
3573           ChannelUtils.writeAbstractLocalPacket(localChannelReference,
3574                                                 errorPacket, true);
3575           finalValue.setAnswered(true);
3576         } catch (final OpenR66ProtocolPacketException e1) {
3577           // should not be
3578         }
3579       }
3580       // now run error task
3581       setErrorTask();
3582       saveStatus();
3583       try {
3584         run();
3585       } catch (final OpenR66RunnerErrorException e1) {
3586         changeUpdatedInfo(UpdatedInfo.INERROR);
3587         setErrorExecutionStatus(runnerStatus);
3588         saveStatus();
3589         if (localChannelReference != null) {
3590           localChannelReference.invalidateRequest(finalValue);
3591         }
3592         throw e1;
3593       }
3594     }
3595     if (!isRescheduledTransfer()) {
3596       changeUpdatedInfo(UpdatedInfo.INERROR);
3597     }
3598     RequestPacket.isThroughMode(getMode());
3599     // re set the original status
3600     setErrorExecutionStatus(runnerStatus);
3601     saveStatus();
3602     if (localChannelReference != null) {
3603       localChannelReference.invalidateRequest(finalValue);
3604     }
3605   }
3606 
3607   /**
3608    * Increment the rank of the transfer
3609    */
3610   public final void incrementRank() {
3611     pojo.setRank(getRank() + 1);
3612     isSaved = false;
3613     int modulo = 10;
3614     if (!admin.isCompatibleWithThreadSharedConnexion()) {
3615       modulo =
3616           100; // Bug in JDBC MariaDB/MySQL which tends to consume more memory
3617     }
3618     if (getRank() % modulo == 0) {
3619       // Save each 10 blocks
3620       try {
3621         updateRank();
3622       } catch (final WaarpDatabaseException e) {
3623         logger.warn("Cannot update Runner: {}", e.getMessage());
3624       }
3625     }
3626   }
3627 
3628   /**
3629    * This method is to be called each time an operation is happening on Runner
3630    *
3631    * @throws OpenR66RunnerErrorException
3632    */
3633   public final void saveStatus() throws OpenR66RunnerErrorException {
3634     try {
3635       update();
3636     } catch (final WaarpDatabaseException e) {
3637       throw new OpenR66RunnerErrorException(e);
3638     }
3639   }
3640 
3641   /**
3642    * This method is to be called each time an operation is happening on Runner
3643    * and it is forced (for SelfRequest
3644    * handling)
3645    *
3646    * @return True if saved
3647    *
3648    * @throws OpenR66RunnerErrorException
3649    */
3650   public final boolean forceSaveStatus() {
3651     final boolean isSender = isSender();
3652     setSenderForUpdate();
3653     boolean status = true;
3654     try {
3655       saveStatus();
3656     } catch (final OpenR66RunnerErrorException e) {
3657       status = false;
3658     }
3659     setSender(isSender);
3660     return status;
3661   }
3662 
3663   /**
3664    * Clear the runner
3665    */
3666   public final void clear() {
3667     // ignore
3668   }
3669 
3670   /**
3671    * Delete the temporary empty file (retrieved file at rank 0)
3672    */
3673   public final void deleteTempFile() {
3674     if (!isSessionSender() && getRank() == 0) {
3675       try {
3676         if (session != null) {
3677           final R66File file = session.getFile();
3678           if (file != null) {
3679             file.delete();
3680           }
3681         }
3682       } catch (final CommandAbstractException e1) {
3683         logger.warn("Cannot delete temporary empty file" + " : {}",
3684                     e1.getMessage());
3685       }
3686     }
3687   }
3688 
3689   @Override
3690   public final String toString() {
3691     return "Run: '" + (rule != null? rule.toString() : getRuleId()) +
3692            "' Filename: '" + getFilename() + "', STEP: '" + getGlobalStep() +
3693            '(' + getLastGlobalStep() + "):" + getStep() + ':' +
3694            getStatus().getMesg() + "', TransferRank: " + getRank() +
3695            ", Blocksize: " + getBlocksize() + ", SpecialId: " + getSpecialId() +
3696            ", isSender: " + isSender() + ", isMoved: " + isFileMoved() +
3697            ", Mode: '" + getMode() + "', Requester: '" + getRequester() +
3698            "', Requested: '" + getRequested() + "', Start: '" + getStart() +
3699            "', Stop: '" + getStop() + "', Internal: '" +
3700            getUpdatedInfo().name() + ':' + getErrorInfo().getMesg() +
3701            "', OriginalSize: " + originalSize + ", Fileinfo: '" +
3702            getFileInformation() + "', Transferinfo: '" + getTransferInfo() +
3703            '\'';
3704   }
3705 
3706   public final String toLogRunStep() {
3707     return "Run: " + getRuleId() + " on " + getFilename() + " STEP: " +
3708            getGlobalStep() + '(' + getLastGlobalStep() + "):" + getStep() +
3709            ':' + getStatus().getMesg();
3710   }
3711 
3712   public final String toShortNoHtmlString(final String newline) {
3713     return "{Run: '" + getRuleId() + "', Filename: '" + getFilename() + "'," +
3714            newline + " STEP: '" + getGlobalStep() + '(' + getLastGlobalStep() +
3715            "):" + getStep() + ':' + getStatus().getMesg() + "'," + newline +
3716            " TransferRank: " + getRank() + ", Blocksize: " + getBlocksize() +
3717            ", SpecialId: " + getSpecialId() + ", isSender: '" + isSender() +
3718            "', isMoved: '" + isFileMoved() + "', Mode: '" +
3719            TRANSFERMODE.values()[getMode()] + newline + "', Requester: '" +
3720            getRequester() + "', Requested: '" + getRequested() + "', Start: '" +
3721            getStart() + "', Stop: '" + getStop() + "'," + newline +
3722            " Internal: '" + getUpdatedInfo().name() + ':' +
3723            getErrorInfo().getMesg() + "', OriginalSize: " + originalSize + ',' +
3724            newline + " Fileinfo: '" + getFileInformation() +
3725            "', Transferinfo: '" + getTransferInfo() + "'}";
3726   }
3727 
3728   public final String toShortString() {
3729     return "<RULE>" + getRuleId() + "</RULE><ID>" + getSpecialId() +
3730            "</ID><FILE>" + getFilename() + "</FILE><ORIGINALFILE>" +
3731            getOriginalFilename() + "</ORIGINALFILE>     <STEP>" +
3732            getGlobalStep() + '(' + getLastGlobalStep() + "):" + getStep() +
3733            ':' + getStatus().getMesg() + "</STEP><RANK>" + getRank() +
3734            "</RANK><BLOCKSIZE>" + getBlocksize() + "</BLOCKSIZE>     <SENDER>" +
3735            isSender() + "</SENDER><MOVED>" + isFileMoved() + "</MOVED><MODE>" +
3736            TRANSFERMODE.values()[getMode()] + "</MODE>     <REQR>" +
3737            getRequester() + "</REQR><REQD>" + getRequested() +
3738            "</REQD>     <START>" + getStart() + "</START><STOP>" + getStop() +
3739            "</STOP>     <INTERNAL>" + getUpdatedInfo().name() + " : " +
3740            getErrorInfo().getMesg() + "</INTERNAL><ORIGINALSIZE>" +
3741            originalSize + "</ORIGINALSIZE>     <FILEINFO>" +
3742            getFileInformation() + "</FILEINFO> <TRANSFERINFO>" +
3743            getTransferInfo() + "</TRANSFERINFO>";
3744   }
3745 
3746   /**
3747    * @return the header for a table of runners in Html format
3748    */
3749   public static String headerHtml() {
3750     return "<td>SpecialId</td><td>Rule</td><td>Filename</td><td>Info" +
3751            "</td><td>Step (LastStep)</td><td>Action</td><td>Status" +
3752            "</td><td>Internal</t><td>Transfer Rank</td><td>BlockSize</td><td>isMoved" +
3753            "</td><td>Requester</td><td>Requested" +
3754            "</td><td>Start</td><td>Stop</td><td>Bandwidth (Mbits)</td><td>Free Space(MB)</td>";
3755   }
3756 
3757   /**
3758    * @param session
3759    *
3760    * @return The associated freespace of the current directory (in MB)
3761    */
3762   public final long freespaceMB(final R66Session session) {
3763     if (getLastGlobalStep() == TASKSTEP.ALLDONETASK ||
3764         getLastGlobalStep() == TASKSTEP.POSTTASK) {
3765       // All finished or Post task
3766       return freespace(session, false) / 0x100000L;
3767     } else {
3768       // are we in sending or receive
3769       return freespace(session, true) / 0x100000L;
3770     }
3771   }
3772 
3773   /**
3774    * @param session
3775    * @param isWorkingPath
3776    *
3777    * @return The associated freespace of the directory (Working if True, Recv
3778    *     if
3779    *     False) (in B, not MB)
3780    */
3781   public final long freespace(final R66Session session,
3782                               final boolean isWorkingPath) {
3783     long freespace = -1;
3784     DbRule dbRule = null;
3785     try {
3786       dbRule = this.rule != null? this.rule : new DbRule(getRuleId());
3787     } catch (final WaarpDatabaseException ignored) {
3788       // ignore
3789     }
3790     if (this.rule == null) {
3791       this.rule = dbRule;
3792     }
3793     if (dbRule != null && !isSender()) {
3794       try {
3795         final String sdir;
3796         if (isWorkingPath) {
3797           sdir = dbRule.getWorkPath();
3798         } else {
3799           sdir = dbRule.getRecvPath();
3800         }
3801         final R66Dir dir;
3802         if (session.getDirsFromSession().containsKey(sdir)) {
3803           dir = session.getDirsFromSession().get(sdir);
3804         } else {
3805           dir = new R66Dir(session);
3806           dir.changeDirectory(sdir);
3807           session.getDirsFromSession().put(sdir, dir);
3808         }
3809         freespace = dir.getFreeSpace();
3810       } catch (final CommandAbstractException e) {
3811         logger.warn("Error while freespace compute {}", e.getMessage(), e);
3812       }
3813     }
3814     return freespace;
3815   }
3816 
3817   private String bandwidthMB() {
3818     final double drank = getRank() <= 0? 1 : getRank();
3819     final double dblocksize = getBlocksize();
3820     final double size = drank * dblocksize;
3821     final double time = getStop().getTime() + 1 - getStart().getTime();
3822     final double result = size / time / 0x100000L * 1000;
3823     return String.format("%,.2f", result);
3824   }
3825 
3826   private String getHtmlColor() {
3827     final String color;
3828     switch (getGlobalStep()) {
3829       case NOTASK:
3830         color = "Orange";
3831         break;
3832       case PRETASK:
3833         color = "Yellow";
3834         break;
3835       case TRANSFERTASK:
3836         color = "LightGreen";
3837         break;
3838       case POSTTASK:
3839         color = "Turquoise";
3840         break;
3841       case ERRORTASK:
3842         color = "Red";
3843         break;
3844       case ALLDONETASK:
3845         color = "Cyan";
3846         break;
3847       default:
3848         color = "";
3849     }
3850     return color;
3851   }
3852 
3853   private String getInfoHtmlColor() {
3854     final String color;
3855     switch (getUpdatedInfo()) {
3856       case DONE:
3857         color = "Cyan";
3858         break;
3859       case INERROR:
3860         color = "Red";
3861         break;
3862       case INTERRUPTED:
3863         color = "Orange";
3864         break;
3865       case NOTUPDATED:
3866         color = "Yellow";
3867         break;
3868       case RUNNING:
3869         color = "LightGreen";
3870         break;
3871       case TOSUBMIT:
3872       case UNKNOWN:
3873         color = "Turquoise";
3874         break;
3875       default:
3876         color = "";
3877     }
3878     return color;
3879   }
3880 
3881   /**
3882    * @param session
3883    * @param running special info
3884    *
3885    * @return the runner in Html format compatible with the header from
3886    *     headerHtml method
3887    */
3888   public final String toHtml(final R66Session session, final String running) {
3889     final long freespace = freespaceMB(session);
3890     final String color = getHtmlColor();
3891     final String updcolor = getInfoHtmlColor();
3892     return "<td>" + getSpecialId() + "</td><td>" +
3893            (rule != null? rule.toShortString() : getRuleId()) + "</td><td>" +
3894            getFilename() + "</td><td>" + getFileInformation() + '[' +
3895            getTransferInfo() + ']' + "</td><td bgcolor=\"" + color + "\">" +
3896            getGlobalStep() + " (" + getGloballaststep() + ")</td><td>" +
3897            getStep() + "</td><td>" + getStatus().getMesg() + " <b>" + running +
3898            "</b></td><td bgcolor=\"" + updcolor + "\">" +
3899            getUpdatedInfo().name() + " : " + getErrorInfo().getMesg() +
3900            "</td><td>" + getRank() + "</td><td>" + getBlocksize() +
3901            "</td><td>" + isFileMoved() + "</td><td>" + getRequester() +
3902            "</td><td>" + getRequested() + "</td><td>" + getStart() +
3903            "</td><td>" + getStop() + "</td><td>" + bandwidthMB() + "</td>" +
3904            "<td>" + freespace + "</td>";
3905   }
3906 
3907   /**
3908    * @param session
3909    * @param body
3910    * @param running special info
3911    *
3912    * @return the runner in Html format specified by body by replacing all
3913    *     instance of fields
3914    */
3915   public final String toSpecializedHtml(final R66Session session,
3916                                         final String body,
3917                                         final String running) {
3918     final long freespace = freespaceMB(session);
3919     final StringBuilder builder = new StringBuilder(body);
3920     WaarpStringUtils.replaceAll(builder, "XXXSpecIdXXX",
3921                                 Long.toString(getSpecialId()));
3922     WaarpStringUtils.replace(builder, "XXXRulXXX",
3923                              rule != null? rule.toShortString() :
3924                                  "<p style='color:red'>Rule Name:" +
3925                                  getRuleId() +
3926                                  " <em>(rule not found)</em></p>");
3927     WaarpStringUtils.replace(builder, "XXXFileXXX", getFilename());
3928     WaarpStringUtils.replace(builder, "XXXInfoXXX", getFileInformation());
3929     WaarpStringUtils.replace(builder, "XXXTransXXX", getTransferInfo());
3930     WaarpStringUtils.replace(builder, "XXXStepXXX",
3931                              getGlobalStep() + " (" + getGloballaststep() +
3932                              ')');
3933     WaarpStringUtils.replace(builder, "XXXCOLXXX", getHtmlColor());
3934     WaarpStringUtils.replace(builder, "XXXActXXX", Integer.toString(getStep()));
3935     WaarpStringUtils.replace(builder, "XXXStatXXX",
3936                              pojo.getStepStatus().getMesg());
3937     WaarpStringUtils.replace(builder, "XXXRunningXXX", running);
3938     WaarpStringUtils.replace(builder, "XXXInternXXX",
3939                              getUpdatedInfo().name() + " : " +
3940                              getErrorInfo().getMesg());
3941     WaarpStringUtils.replace(builder, "XXXUPDCOLXXX", getInfoHtmlColor());
3942     WaarpStringUtils.replace(builder, "XXXBloXXX", Integer.toString(getRank()));
3943     WaarpStringUtils.replace(builder, "XXXisSendXXX",
3944                              Boolean.toString(isSender()));
3945     WaarpStringUtils.replace(builder, "XXXisMovXXX",
3946                              Boolean.toString(isFileMoved()));
3947     WaarpStringUtils.replace(builder, "XXXModXXX",
3948                              TRANSFERMODE.values()[getMode()].toString());
3949     WaarpStringUtils.replaceAll(builder, "XXXReqrXXX", getRequester());
3950     WaarpStringUtils.replaceAll(builder, "XXXReqdXXX", getRequested());
3951     WaarpStringUtils.replace(builder, "XXXStarXXX", getStart().toString());
3952     WaarpStringUtils.replace(builder, "XXXStopXXX", getStop().toString());
3953     WaarpStringUtils.replace(builder, "XXXBandXXX", bandwidthMB());
3954     WaarpStringUtils.replace(builder, "XXXFreeXXX", Long.toString(freespace));
3955     return builder.toString();
3956   }
3957 
3958   /**
3959    * @return True if the current host is the requested host but not requester
3960    *     (to prevent request to itself)
3961    */
3962   public final boolean isRequestOnRequested() {
3963     if (pojo.getRequested().equals(Configuration.configuration.getHostId()) ||
3964         pojo.getRequested()
3965             .equals(Configuration.configuration.getHostSslId())) {
3966       // check if not calling itself
3967       return !pojo.getRequester()
3968                   .equals(Configuration.configuration.getHostId()) &&
3969              !pojo.getRequester()
3970                   .equals(Configuration.configuration.getHostSslId());
3971     }
3972     return false;
3973   }
3974 
3975   /**
3976    * @return True if this is a self request and current action is on Requested
3977    */
3978   public final boolean shallIgnoreSave() {
3979     return isSelfRequest() && (isSessionSender() && getRule().isSendMode() ||
3980                                !isSessionSender() && getRule().isRecvMode());
3981   }
3982 
3983   /**
3984    * @return True if the request is a self request (same host on both side)
3985    */
3986   public final boolean isSelfRequest() {
3987     return
3988         (pojo.getRequested().equals(Configuration.configuration.getHostId()) ||
3989          pojo.getRequested()
3990              .equals(Configuration.configuration.getHostSslId())) &&
3991         (pojo.getRequester().equals(Configuration.configuration.getHostId()) ||
3992          pojo.getRequester()
3993              .equals(Configuration.configuration.getHostSslId()));
3994   }
3995 
3996   /**
3997    * @return the requested HostId
3998    */
3999   public final String getRequested() {
4000     return pojo.getRequested();
4001   }
4002 
4003   /**
4004    * @return the requester HostId
4005    */
4006   public final String getRequester() {
4007     return pojo.getRequester();
4008   }
4009 
4010   /**
4011    * @return the start
4012    */
4013   public final Timestamp getStart() {
4014     return pojo.getStart();
4015   }
4016 
4017   /**
4018    * @param start new Start time to apply when reschedule
4019    */
4020   public final void setStart(final Timestamp start) {
4021     if (pojo.getStart() == null || !pojo.getStart().equals(start)) {
4022       pojo.setStart(start);
4023       isOtherThanStatus = true;
4024       isSaved = false;
4025     }
4026   }
4027 
4028   /**
4029    * @return the stop
4030    */
4031   public final Timestamp getStop() {
4032     return pojo.getStop();
4033   }
4034 
4035   public final void setStop(final Timestamp stop) {
4036     if (pojo.getStop() == null || !pojo.getStop().equals(stop)) {
4037       pojo.setStop(stop);
4038       isSaved = false;
4039     }
4040   }
4041 
4042   /**
4043    * @return the associated request
4044    */
4045   public final RequestPacket getRequest() {
4046     final String sep;
4047     if (pojo.getRequested().equals(Configuration.configuration.getHostId()) ||
4048         pojo.getRequested()
4049             .equals(Configuration.configuration.getHostSslId())) {
4050       sep = PartnerConfiguration.getSeparator(pojo.getRequester());
4051     } else {
4052       sep = PartnerConfiguration.getSeparator(pojo.getRequested());
4053     }
4054     return new RequestPacket(pojo.getRule(), pojo.getTransferMode(),
4055                              pojo.getOriginalName(), pojo.getBlockSize(),
4056                              pojo.getRank(), pojo.getId(), pojo.getFileInfo(),
4057                              originalSize, sep);
4058   }
4059 
4060   /**
4061    * Used internally
4062    *
4063    * @return a Key representing the primary key as a unique string
4064    */
4065   public final String getKey() {
4066     return pojo.getRequested() + ' ' + pojo.getRequester() + ' ' + pojo.getId();
4067   }
4068 
4069   /**
4070    * Construct a new Element with value
4071    *
4072    * @param name
4073    * @param value
4074    *
4075    * @return the new Element
4076    */
4077   private static Element newElement(final String name, final String value) {
4078     final Element node = new DefaultElement(name);
4079     if (value != null) {
4080       node.addText(value);
4081     }
4082     return node;
4083   }
4084 
4085   private static Object getValue(final DbTaskRunner runner,
4086                                  final String field) {
4087     final Columns column = Columns.valueOf(field.toUpperCase());
4088     switch (column) {
4089       case GLOBALSTEP:
4090         return runner.pojo.getGlobalStep().ordinal();
4091       case GLOBALLASTSTEP:
4092         return runner.pojo.getLastGlobalStep().ordinal();
4093       case STEP:
4094         return runner.pojo.getStep();
4095       case RANK:
4096         return runner.pojo.getRank();
4097       case STEPSTATUS:
4098         return runner.pojo.getStepStatus().getCode();
4099       case RETRIEVEMODE:
4100         return runner.pojo.getRetrieveMode();
4101       case FILENAME:
4102         return runner.pojo.getFilename();
4103       case ISMOVED:
4104         return runner.pojo.getIsMoved();
4105       case IDRULE:
4106         return runner.pojo.getRule();
4107       case BLOCKSZ:
4108         return runner.pojo.getBlockSize();
4109       case ORIGINALNAME:
4110         return runner.pojo.getOriginalName();
4111       case FILEINFO:
4112         return runner.pojo.getFileInfo();
4113       case TRANSFERINFO:
4114         return runner.getTransferInfo();
4115       case MODETRANS:
4116         return runner.pojo.getTransferMode();
4117       case STARTTRANS:
4118         return runner.pojo.getStart();
4119       case STOPTRANS:
4120         return runner.pojo.getStop();
4121       case INFOSTATUS:
4122         return runner.pojo.getInfoStatus().getCode();
4123       case UPDATEDINFO:
4124         return runner.pojo.getUpdatedInfo().ordinal();
4125       case OWNERREQ:
4126         return runner.pojo.getOwnerRequest();
4127       case REQUESTER:
4128         return runner.pojo.getRequester();
4129       case REQUESTED:
4130         return runner.pojo.getRequested();
4131       case SPECIALID:
4132         return runner.pojo.getId();
4133       default:
4134         throw new IllegalArgumentException("Field unknown: " + column.name());
4135     }
4136   }
4137 
4138   /**
4139    * Need to call 'setToArray' before
4140    *
4141    * @param runner
4142    *
4143    * @return The Element representing the given Runner
4144    */
4145   private static Element getElementFromRunner(final DbTaskRunner runner) {
4146     final Element root = new DefaultElement(XMLRUNNER);
4147     for (final Columns column : Columns.values()) {
4148       if (column.name().equals(Columns.UPDATEDINFO.name()) ||
4149           column.name().equals(Columns.TRANSFERINFO.name())) {
4150         continue;
4151       }
4152       root.add(newElement(column.name().toLowerCase(),
4153                           getValue(runner, column.name()).toString()));
4154     }
4155     return root;
4156   }
4157 
4158   /**
4159    * Write the selected TaskRunners from PrepareStatement to a XMLWriter
4160    *
4161    * @param preparedStatement ready to be executed
4162    * @param xmlWriter
4163    *
4164    * @return the NbAndSpecialId for the number of transfer and higher rank
4165    *     found
4166    *
4167    * @throws WaarpDatabaseNoConnectionException
4168    * @throws WaarpDatabaseSqlException
4169    * @throws OpenR66ProtocolBusinessException
4170    */
4171   public static NbAndSpecialId writeXML(
4172       final DbPreparedStatement preparedStatement, final XMLWriter xmlWriter)
4173       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException,
4174              OpenR66ProtocolBusinessException {
4175     final Element root = new DefaultElement(XMLRUNNERS);
4176     final NbAndSpecialId nbAndSpecialId = new NbAndSpecialId();
4177     try {
4178       xmlWriter.writeOpen(root);
4179       Element node;
4180       while (preparedStatement.getNext()) {
4181         final DbTaskRunner runner = getFromStatementNoRule(preparedStatement);
4182         if (nbAndSpecialId.higherSpecialId < runner.getSpecialId()) {
4183           nbAndSpecialId.higherSpecialId = runner.getSpecialId();
4184         }
4185         node = getElementFromRunner(runner);
4186         xmlWriter.write(node);
4187         xmlWriter.flush();
4188         nbAndSpecialId.nb++;
4189       }
4190       xmlWriter.writeClose(root);
4191     } catch (final IOException e) {
4192       logger.error(CANNOT_WRITE_XML_FILE + ": {}", e.getMessage());
4193       throw new OpenR66ProtocolBusinessException(
4194           "Cannot write file: " + e.getMessage());
4195     }
4196     return nbAndSpecialId;
4197   }
4198 
4199   /**
4200    * Write selected TaskRunners to a Json String
4201    *
4202    * @param preparedStatement
4203    *
4204    * @return the associated Json String
4205    *
4206    * @throws WaarpDatabaseNoConnectionException
4207    * @throws WaarpDatabaseSqlException
4208    */
4209   public static String getJson(final DbPreparedStatement preparedStatement,
4210                                final int limit)
4211       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
4212     final ArrayNode arrayNode = JsonHandler.createArrayNode();
4213     try {
4214       preparedStatement.executeQuery();
4215       final LocalTransaction localTransaction =
4216           Configuration.configuration.getLocalTransaction();
4217       int nb = 0;
4218       while (preparedStatement.getNext()) {
4219         final DbTaskRunner runner = getFromStatementNoRule(preparedStatement);
4220         final ObjectNode node = runner.getJson();
4221         node.put(Columns.SPECIALID.name(),
4222                  Long.toString(runner.getSpecialId()));
4223         if (localTransaction == null) {
4224           node.put("Running", false);
4225         } else {
4226           node.put("Running", localTransaction.contained(runner.getKey()));
4227         }
4228         arrayNode.add(node);
4229         nb++;
4230         if (nb >= limit) {
4231           break;
4232         }
4233       }
4234     } finally {
4235       preparedStatement.realClose();
4236     }
4237     return WaarpStringUtils.cleanJsonForHtml(
4238         JsonHandler.writeAsString(arrayNode)
4239                    .replaceAll("(\\\"\\{)([^}]+)(\\}\\\")", "\"{$2}\"")
4240                    .replaceAll("([^\\\\])(\\\\\")([a-zA-Z_0-9]+)(\\\\\")",
4241                                "$1\\\\\"$3\\\\\""));
4242   }
4243 
4244   /**
4245    * Write selected TaskRunners to an XML file using an XMLWriter
4246    *
4247    * @param preparedStatement
4248    * @param filename
4249    *
4250    * @return the NbAndSpecialId for the number of transfer and higher rank
4251    *     found
4252    *
4253    * @throws WaarpDatabaseNoConnectionException
4254    * @throws WaarpDatabaseSqlException
4255    * @throws OpenR66ProtocolBusinessException
4256    */
4257   public static NbAndSpecialId writeXMLWriter(
4258       final DbPreparedStatement preparedStatement, final String filename)
4259       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException,
4260              OpenR66ProtocolBusinessException {
4261     NbAndSpecialId nbAndSpecialId;
4262     OutputStream outputStream = null;
4263     XMLWriter xmlWriter = null;
4264     boolean isOk = false;
4265     try {
4266       outputStream = new FileOutputStream(filename);
4267       final OutputFormat format = OutputFormat.createPrettyPrint();
4268       format.setEncoding(WaarpStringUtils.UTF_8);
4269       xmlWriter = new XMLWriter(outputStream, format);
4270       preparedStatement.executeQuery();
4271       nbAndSpecialId = writeXML(preparedStatement, xmlWriter);
4272       isOk = true;
4273     } catch (final FileNotFoundException e) {
4274       logger.error(CANNOT_WRITE_XML_FILE + ": {}", e.getMessage());
4275       throw new OpenR66ProtocolBusinessException("File not found");
4276     } catch (final UnsupportedEncodingException e) {
4277       logger.error(CANNOT_WRITE_XML_FILE + ": {}", e.getMessage());
4278       throw new OpenR66ProtocolBusinessException(UNSUPPORTED_ENCODING);
4279     } finally {
4280       if (xmlWriter != null) {
4281         try {
4282           xmlWriter.endDocument();
4283           xmlWriter.flush();
4284           xmlWriter.close();
4285         } catch (final SAXException e) {
4286           FileUtils.close(outputStream);
4287           final File file = new File(filename);
4288           if (!file.delete()) {
4289             logger.info(CANNOT_DELETE_WRONG_XML_FILE);
4290           }
4291           logger.error(CANNOT_WRITE_XML_FILE + ": {}", e.getMessage());
4292           throw new OpenR66ProtocolBusinessException(//NOSONAR
4293                                                      UNSUPPORTED_ENCODING);//NOSONAR
4294         } catch (final IOException e) {
4295           FileUtils.close(outputStream);
4296           final File file = new File(filename);
4297           if (!file.delete()) {
4298             logger.info(CANNOT_DELETE_WRONG_XML_FILE);
4299           }
4300           logger.error(CANNOT_WRITE_XML_FILE + ": {}", e.getMessage());
4301           throw new OpenR66ProtocolBusinessException(//NOSONAR
4302                                                      UNSUPPORTED_ENCODING);//NOSONAR
4303         }
4304         if (!isOk) {
4305           FileUtils.close(outputStream);
4306           final File file = new File(filename);
4307           if (!file.delete()) {
4308             logger.info("Cannot delete wrong  XML file");
4309           }
4310         }
4311       } else if (outputStream != null) {
4312         FileUtils.close(outputStream);
4313         final File file = new File(filename);
4314         if (!file.delete()) {
4315           logger.debug("Cannot delete not written XML file");
4316         }
4317       }
4318     }
4319     return nbAndSpecialId;
4320   }
4321 
4322   /**
4323    * Write all TaskRunners to an XML file using an XMLWriter
4324    *
4325    * @param filename
4326    *
4327    * @throws WaarpDatabaseNoConnectionException
4328    * @throws WaarpDatabaseSqlException
4329    * @throws OpenR66ProtocolBusinessException
4330    */
4331   public static void writeXMLWriter(final String filename)
4332       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException,
4333              OpenR66ProtocolBusinessException {
4334     final String request =
4335         "SELECT " + selectAllFields + " FROM " + table + " WHERE " +
4336         getLimitWhereCondition();
4337     DbPreparedStatement preparedStatement = null;
4338     try {
4339       preparedStatement = new DbPreparedStatement(admin.getSession());
4340       preparedStatement.createPrepareStatement(request);
4341       writeXMLWriter(preparedStatement, filename);
4342     } finally {
4343       if (preparedStatement != null) {
4344         preparedStatement.realClose();
4345       }
4346     }
4347   }
4348 
4349   /**
4350    * @return the backend XML filename for the current TaskRunner in NoDb
4351    *     Client mode
4352    */
4353   public final String backendXmlFilename() {
4354     return Configuration.configuration.getBaseDirectory() +
4355            Configuration.configuration.getArchivePath() +
4356            DirInterface.SEPARATOR + pojo.getRequester() + '_' +
4357            pojo.getRequested() + '_' + pojo.getId() + XMLEXTENSION;
4358   }
4359 
4360   /**
4361    * @return the runner as XML
4362    */
4363   public final String asXML() {
4364     final Element node = getElementFromRunner(this);
4365     return node.asXML();
4366   }
4367 
4368   @Override
4369   public final ObjectNode getJson() {
4370     final String nomap = pojo.getTransferInfo();
4371     pojo.setTransferInfo(getTransferInfo());
4372     final ObjectNode node = super.getJson();
4373     pojo.setTransferInfo(nomap);
4374     JsonNode value = node.get(Columns.FILEINFO.name());
4375     node.put(Columns.FILEINFO.name(), value.asText().replaceAll("[\\\\]+", ""));
4376     value = node.get(Columns.TRANSFERINFO.name());
4377     node.put(Columns.TRANSFERINFO.name(),
4378              value.asText().replaceAll("[\\\\]+", ""));
4379     if (rescheduledTransfer) {
4380       node.put(JSON_RESCHEDULE, true);
4381     }
4382     if (isRecvThrough || isSendThrough) {
4383       node.put(JSON_THROUGHMODE, true);
4384     }
4385     node.put(JSON_ORIGINALSIZE, originalSize);
4386     final String followId = getFollowId();
4387     if (followId != null) {
4388       node.put(FOLLOW_JSON_KEY, followId);
4389     } else {
4390       node.put(FOLLOW_JSON_KEY, "");
4391     }
4392     return node;
4393   }
4394 
4395   @Override
4396   protected final void setFromJson(final String field, final JsonNode value) {
4397     if (value == null) {
4398       return;
4399     }
4400     isSaved = false;
4401     isOtherThanStatus = true;
4402     for (final Columns column : Columns.values()) {
4403       if (column.name().equalsIgnoreCase(field)) {
4404         switch (column) {
4405           case BLOCKSZ:
4406             pojo.setBlockSize(value.asInt());
4407             break;
4408           case FILEINFO:
4409             pojo.setFileInfo(value.asText());
4410             break;
4411           case FILENAME:
4412             pojo.setFilename(value.asText());
4413             break;
4414           case GLOBALLASTSTEP:
4415             pojo.setLastGlobalStep(Transfer.TASKSTEP.valueOf(value.asInt()));
4416             break;
4417           case GLOBALSTEP:
4418             pojo.setGlobalStep(Transfer.TASKSTEP.valueOf(value.asInt()));
4419             break;
4420           case IDRULE:
4421             pojo.setRule(value.asText());
4422             break;
4423           case INFOSTATUS:
4424             pojo.setInfoStatus(ErrorCode.getFromCode(value.asText()));
4425             break;
4426           case ISMOVED:
4427             pojo.setIsMoved(value.asBoolean());
4428             break;
4429           case MODETRANS:
4430             pojo.setTransferMode(value.asInt());
4431             break;
4432           case ORIGINALNAME:
4433             pojo.setOriginalName(value.asText());
4434             break;
4435           case OWNERREQ:
4436             String owner = value.asText();
4437             if (ParametersChecker.isEmpty(owner)) {
4438               owner = Configuration.configuration.getHostId();
4439             }
4440             pojo.setOwnerRequest(owner);
4441             break;
4442           case RANK:
4443             pojo.setRank(value.asInt());
4444             break;
4445           case REQUESTED:
4446             pojo.setRequested(value.asText());
4447             break;
4448           case REQUESTER:
4449             pojo.setRequester(value.asText());
4450             break;
4451           case RETRIEVEMODE:
4452             pojo.setRetrieveMode(value.asBoolean());
4453             break;
4454           case SPECIALID:
4455             pojo.setId(value.asLong());
4456             break;
4457           case STARTTRANS:
4458             long start = value.asLong();
4459             if (start == 0) {
4460               start = System.currentTimeMillis();
4461             }
4462             pojo.setStart(new Timestamp(start));
4463             break;
4464           case STEP:
4465             pojo.setStep(value.asInt());
4466             break;
4467           case STEPSTATUS:
4468             pojo.setStepStatus(ErrorCode.getFromCode(value.asText()));
4469             break;
4470           case STOPTRANS:
4471             long stop = value.asLong();
4472             if (stop == 0) {
4473               stop = System.currentTimeMillis();
4474             }
4475             pojo.setStop(new Timestamp(stop));
4476             break;
4477           case TRANSFERINFO: {
4478             String text = value.asText().trim();
4479             if (text.isEmpty()) {
4480               text = "{}";
4481               if (transferMap != null) {
4482                 transferMap.clear();
4483               }
4484               transferMap = null;
4485             }
4486             pojo.setTransferInfo(text);
4487             break;
4488           }
4489           case UPDATEDINFO:
4490             pojo.setUpdatedInfo(
4491                 org.waarp.openr66.pojo.UpdatedInfo.valueOf(value.asInt()));
4492             break;
4493         }
4494       }
4495     }
4496     setMapFromFileInfo();
4497   }
4498 
4499   /**
4500    * @return the Json string for this
4501    */
4502   public final String getJsonAsString() {
4503     final ObjectNode node = getJson();
4504     node.put(Columns.SPECIALID.name(), Long.toString(pojo.getId()));
4505     final LocalTransaction localTransaction =
4506         Configuration.configuration.getLocalTransaction();
4507     if (localTransaction == null) {
4508       node.put("Running", false);
4509     } else {
4510       node.put("Running", localTransaction.contained(getKey()));
4511     }
4512     return WaarpStringUtils.cleanJsonForHtml(JsonHandler.writeAsString(node));
4513   }
4514 
4515   /**
4516    * Set the given runner from the root element of the runner itself
4517    * (XMLRUNNER but not XMLRUNNERS).
4518    *
4519    * @param runner
4520    * @param root
4521    */
4522   private static void setRunnerFromElementNoException(final DbTaskRunner runner,
4523                                                       final Element root) {
4524     final ObjectNode node = JsonHandler.createObjectNode();
4525     for (final Columns column : Columns.values()) {
4526       if (column.name().equals(Columns.UPDATEDINFO.name()) ||
4527           column.name().equals(Columns.TRANSFERINFO.name())) {
4528         continue;
4529       }
4530       final Element elt =
4531           (Element) root.selectSingleNode(column.name().toLowerCase());
4532       if (elt != null) {
4533         final String newValue = elt.getText();
4534         node.put(column.name(), newValue);
4535         runner.setFromJson(column.name(), node.findValue(column.name()));
4536         node.removeAll();
4537       }
4538     }
4539     runner.pojo.setTransferInfo("{}");
4540     if (runner.transferMap != null) {
4541       runner.transferMap.clear();
4542     }
4543     runner.transferMap = null;
4544     runner.isOtherThanStatus = true;
4545     runner.isSaved = false;
4546   }
4547 
4548   /**
4549    * Reload a to submitted runner from a remote partner's log (so reversing
4550    * isSender should be true)
4551    *
4552    * @param xml
4553    * @param reverse
4554    *
4555    * @return the TaskRunner from the XML source element
4556    *
4557    * @throws OpenR66ProtocolBusinessException
4558    */
4559   public static DbTaskRunner fromStringXml(final String xml,
4560                                            final boolean reverse)
4561       throws OpenR66ProtocolBusinessException {
4562     final Document document;
4563     try {
4564       document = DocumentHelper.parseText(xml);
4565     } catch (final DocumentException e1) {
4566       logger.warn("Cant parse XML" + " : {}", e1.getMessage());
4567       throw new OpenR66ProtocolBusinessException("Cannot parse the XML input");
4568     }
4569     final DbTaskRunner runner = new DbTaskRunner();
4570     setRunnerFromElementNoException(runner, document.getRootElement());
4571     runner.pojo.setOwnerRequest(Configuration.configuration.getHostId());
4572     if (reverse) {
4573       runner.setSender(!runner.isSender());
4574       if (runner.isSender()) {
4575         runner.setFilename(runner.getOriginalFilename());
4576       }
4577     }
4578     return runner;
4579   }
4580 
4581   /**
4582    * Special function for save or update for Log Import
4583    *
4584    * @throws WaarpDatabaseException
4585    */
4586   private void insertOrUpdateForLogsImport() throws WaarpDatabaseException {
4587     if (dbSession == null) {
4588       return;
4589     }
4590     if (exist()) {
4591       final String nomap = pojo.getTransferInfo();
4592       pojo.setTransferInfo(getTransferInfo());
4593       super.update();
4594       pojo.setTransferInfo(nomap);
4595     } else {
4596       insert();
4597     }
4598   }
4599 
4600   /**
4601    * Method to load several DbTaskRunner from File logs.
4602    *
4603    * @param logsFile File containing logs from export function
4604    *
4605    * @throws OpenR66ProtocolBusinessException
4606    */
4607   public static void loadXml(final File logsFile)
4608       throws OpenR66ProtocolBusinessException {
4609     if (!logsFile.canRead()) {
4610       throw new OpenR66ProtocolBusinessException("XML file cannot be read");
4611     }
4612     final SAXReader reader = XmlUtil.getNewSaxReader();
4613     final Document document;
4614     try {
4615       document = reader.read(logsFile);
4616     } catch (final DocumentException e) {
4617       throw new OpenR66ProtocolBusinessException(
4618           "XML file cannot be read as an XML file", e);
4619     }
4620     final List<Node> elts =
4621         document.selectNodes('/' + XMLRUNNERS + '/' + XMLRUNNER);
4622     boolean error = false;
4623     Exception one = null;
4624     for (final Node element : elts) {
4625       final DbTaskRunner runnerlog = new DbTaskRunner();
4626       try {
4627         setRunnerFromElementNoException(runnerlog, (Element) element);
4628         runnerlog.insertOrUpdateForLogsImport();
4629       } catch (final WaarpDatabaseSqlException e) {
4630         error = true;
4631         one = e;
4632       } catch (final WaarpDatabaseException e) {
4633         error = true;
4634         one = e;
4635       }
4636     }
4637     if (error) {
4638       throw new OpenR66ProtocolBusinessException(
4639           "Backend XML file is not conform to the model", one);
4640     }
4641   }
4642 
4643   /**
4644    * Utility for "self request" mode only
4645    *
4646    * @param sender
4647    */
4648   public final void setSender(final boolean sender) {
4649     if (pojo.getRetrieveMode() != sender) {
4650       pojo.setRetrieveMode(sender);
4651       isOtherThanStatus = true;
4652       isSaved = false;
4653     }
4654   }
4655 
4656   /**
4657    * Helper
4658    *
4659    * @param request
4660    *
4661    * @return isSender according to request
4662    */
4663   public static boolean getSenderByRequestPacket(final RequestPacket request) {
4664     if (request.isToValidate()) {
4665       return RequestPacket.isRecvMode(request.getMode());
4666     }
4667     return !RequestPacket.isRecvMode(request.getMode());
4668   }
4669 
4670   /**
4671    * Utility for "self request"
4672    *
4673    * @param requestToValidate
4674    */
4675   public final void setSenderByRequestToValidate(
4676       final boolean requestToValidate) {
4677     final boolean oldValue = pojo.getRetrieveMode();
4678     pojo.setRetrieveMode(RequestPacket.isRecvMode(pojo.getTransferMode()));
4679     if (!requestToValidate) {
4680       pojo.setRetrieveMode(!pojo.getRetrieveMode());
4681     }
4682     if (oldValue != pojo.getRetrieveMode()) {
4683       isOtherThanStatus = true;
4684       isSaved = false;
4685     }
4686   }
4687 
4688   /**
4689    * Utility to force "update"
4690    */
4691   private void setSenderForUpdate() {
4692     if (isSelfRequest()) {
4693       final boolean oldValue = pojo.getRetrieveMode();
4694       pojo.setRetrieveMode(RequestPacket.isRecvMode(pojo.getTransferMode()));
4695       if (oldValue != pojo.getRetrieveMode()) {
4696         isSaved = false;
4697         isOtherThanStatus = true;
4698       }
4699     }
4700   }
4701 
4702   /**
4703    * @return the originalSize
4704    */
4705   public final long getOriginalSize() {
4706     return originalSize;
4707   }
4708 
4709   /**
4710    * @param originalSize the originalSize to set
4711    */
4712   public final void setOriginalSize(final long originalSize) {
4713     this.originalSize = originalSize;
4714     setOriginalSizeTransferMap(originalSize);
4715   }
4716 
4717   /**
4718    * @return the full path for the current file
4719    *
4720    * @throws CommandAbstractException
4721    */
4722   public final String getFullFilePath() throws CommandAbstractException {
4723     if (isFileMoved()) {
4724       return getFilename();
4725     } else {
4726       final R66File file =
4727           new R66File(session, session.getDir(), getFilename(), false);
4728       return file.getTrueFile().getAbsolutePath();
4729     }
4730   }
4731 
4732 }