View Javadoc

1   /**
2    * This file is part of GoldenGate Project (named also GoldenGate or GG).
3    * 
4    * Copyright 2009, Frederic Bregier, 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 GoldenGate 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   * GoldenGate 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   * GoldenGate . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package openr66.context.task;
21  
22  import java.sql.Timestamp;
23  import java.util.Calendar;
24  import java.util.Date;
25  
26  import goldengate.common.logging.GgInternalLogger;
27  import goldengate.common.logging.GgInternalLoggerFactory;
28  import openr66.context.ErrorCode;
29  import openr66.context.R66Result;
30  import openr66.context.R66Session;
31  import openr66.context.task.exception.OpenR66RunnerErrorException;
32  import openr66.database.data.DbTaskRunner;
33  
34  /**
35   * Reschedule Transfer task to a time delayed by the specified number of milliseconds, 
36   * if the error code is one of the specified codes and the optional intervals of date are 
37   * compatible with the new time schedule<br><br>
38   * 
39   * Result of arguments will be as following options (the two first are
40   * mandatory):<br>
41   * <br>
42   * 
43   * "-delay ms" where ms is the added number of ms on current time before retry
44   * on schedule<br>
45   * "-case errorCode,errorCode,..." where errorCode is one of the following error
46   * of the current transfer (either literal or code in 1 character:<br>
47   * ConnectionImpossible(C), ServerOverloaded(l), BadAuthent(A), ExternalOp(E),
48   * TransferError(T), MD5Error(M), Disconnection(D), RemoteShutdown(r),
49   * FinalOp(F), Unimplemented(U), Shutdown(S), RemoteError(R), Internal(I),
50   * StoppedTransfer(H), CanceledTransfer(K), Warning(W), Unknown(-),
51   * QueryAlreadyFinished(Q), QueryStillRunning(s), NotKnownHost(N),
52   * QueryRemotelyUnknown(u), FileNotFound(f), CommandNotFound(c),
53   * PassThroughMode(p)<br>
54   * <br>
55   * "-between start;end" and/or "-notbetween start;end" (multiple times are
56   * allowed, start or end can be not set) and where start and stop are in the
57   * following format:<br>
58   * Yn:Mn:Dn:Hn:mn:Sn where n is a number for each time specification, each
59   * specification is optional, as Y=Year, M=Month, D=Day, H=Hour, m=minute,
60   * s=second.<br>Format can be X+n, X-n, X=n or Xn where X+-n means 
61   * adding/subtracting n to current date value, while X=n or Xn means setting exact value<br>
62   * If one time specification is not set, it is based on the current date.<br>
63   * <br>
64   * If "-notbetween" is specified, the planned date must not be in the area.<br>
65   * If "-between" is specified, the planned date must be found in any such specified areas (could be in any of the occurrence). 
66   * If not specified, it only depends on "-notbetween".<br>
67   * If none is specified, the planned date is always valid.<br>
68   * <br>
69   * 
70   * Note that if a previous called to a reschedule was done for this attempt and was successful,
71   * the following calls will be ignored.<br>
72   * <br>
73   * 
74   * In case start > end, end will be +1 day<br>
75   * In case start and end < current planned date, both will have +1 day.<br>
76   * <br>
77   * 
78   * Example: -delay 3600000 -case ConnectionImpossible,ServerOverloaded,Shutdown
79   * -notbetween H7:m0:S0;H19:m0:S0 -notbetween H1:m0:S0;H=3:m0:S0<br>
80   * means retry in case of error during initialization of connection in 1 hour if
81   * not between 7AM to 7PM and not between 1AM to 3AM.<br>
82   * 
83   * @author Frederic Bregier
84   * 
85   */
86  public class RescheduleTransferTask extends AbstractTask {
87      /**
88       * Internal Logger
89       */
90      private static final GgInternalLogger logger = GgInternalLoggerFactory
91              .getLogger(RescheduleTransferTask.class);
92  
93      protected long newdate = 0;
94  
95      protected Calendar newDate = null;
96  
97      /**
98       * @param argRule
99       * @param delay
100      * @param argTransfer
101      * @param session
102      */
103     public RescheduleTransferTask(String argRule, int delay,
104             String argTransfer, R66Session session) {
105         super(TaskType.RESCHEDULE, delay, argRule, argTransfer, session);
106     }
107 
108     /*
109      * (non-Javadoc)
110      * 
111      * @see openr66.context.task.AbstractTask#run()
112      */
113     @Override
114     public void run() {
115         logger.info("Reschedule with " + argRule + ":" + argTransfer +
116                 " and {}", session);
117         DbTaskRunner runner = session.getRunner();
118         if (runner == null) {
119             futureCompletion.setFailure(new OpenR66RunnerErrorException(
120                     "No valid runner in Reschedule"));
121             return;
122         }
123         if (runner.isRescheduledTransfer()) {
124             // Already rescheduled so ignore
125             R66Result result = new R66Result(session, false, ErrorCode.Warning,
126                     runner);
127             futureCompletion.setResult(result);
128             logger.warn("Transfer already Rescheduled: "+runner.toShortString());
129             futureCompletion.setSuccess();
130             return;
131         }
132         if (runner.isSelfRequested()) {
133             // Self Requested Request so reschedule is ignored
134             R66Result result = new R66Result(session, false, ErrorCode.LoopSelfRequestedHost,
135                     runner);
136             futureCompletion.setResult(result);
137             futureCompletion.setFailure(new OpenR66RunnerErrorException(
138                 "No valid runner in Reschedule since Self Requested"));
139             return;
140         }
141         String finalname = argRule;
142         finalname = getReplacedValue(finalname, argTransfer.split(" "));
143         String[] args = finalname.split(" ");
144         if (args.length < 4) {
145             R66Result result = new R66Result(session, false, ErrorCode.Warning,
146                     runner);
147             futureCompletion.setResult(result);
148             logger.warn("Not enough argument in Reschedule: "+runner.toShortString());
149             futureCompletion.setSuccess();
150             return;
151         }
152         if (!validateArgs(args)) {
153             R66Result result = new R66Result(session, false, ErrorCode.Warning,
154                     runner);
155             futureCompletion.setResult(result);
156             logger.warn("Reschedule unallowed due to argument: "+runner.toShortString());
157             futureCompletion.setSuccess();
158             return;
159         }
160         Timestamp start = new Timestamp(newdate);
161         try {
162             runner.setStart(start);
163         } catch (OpenR66RunnerErrorException e) {
164             logger.error(
165                     "Prepare transfer in\n    FAILURE\n     " +
166                             runner.toShortString() + "\n    <AT>" +
167                             (new Date(newdate)).toString() + "</AT>", e);
168             futureCompletion.setFailure(new OpenR66RunnerErrorException(
169                     "Reschedule failed: " + e.getMessage(), e));
170             return;
171         }
172         runner.setRescheduledTransfer();
173         R66Result result = new R66Result(session, false, ErrorCode.Warning,
174                 runner);
175         futureCompletion.setResult(result);
176         logger.warn("Reschedule transfer in\n    SUCCESS\n    " +
177                 runner.toShortString() + "\n    <AT>" +
178                 (new Date(newdate)).toString() + "</AT>");
179         futureCompletion.setSuccess();
180     }
181 
182     protected boolean validateArgs(String[] args) {
183         boolean validCode = false;
184         for (int i = 0; i < args.length; i ++) {
185             if (args[i].equalsIgnoreCase("-delay")) {
186                 i ++;
187                 try {
188                     newdate = Long.parseLong(args[i]);
189                 } catch (NumberFormatException e) {
190                     logger.warn("Bad Long format: args[i]");
191                     return false;
192                 }
193             } else if (args[i].equalsIgnoreCase("-case")) {
194                 i ++;
195                 if (!validCode) {
196                     String[] codes = args[i].split(",");
197                     for (int j = 0; j < codes.length; j ++) {
198                         ErrorCode code = ErrorCode.getFromCode(codes[j]);
199                         if (session.getLocalChannelReference().getCurrentCode() == code) {
200                             logger.debug("Code valid: "+code);
201                             validCode = true;
202                         }
203                     }
204                 }
205             }
206         }
207         // now we have new delay plus code
208         if (!validCode) {
209             logger.warn("No valid Code found");
210             return false;
211         }
212         if (newdate <= 0) {
213             logger.warn("Delay is negative: "+newdate);
214             return false;
215         }
216         newdate += System.currentTimeMillis();
217         newDate = Calendar.getInstance();
218         newDate.setTimeInMillis(newdate);
219         boolean betweenTest = false;
220         boolean betweenResult = false;
221         for (int i = 0; i < args.length; i ++) {
222             if (args[i].equalsIgnoreCase("-notbetween")) {
223                 i ++;
224                 String[] elmts = args[i].split(";");
225                 boolean startModified = false;
226                 String[] values = elmts[0].split(":");
227                 Calendar start = getCalendar(values);
228                 if (start != null) {
229                     startModified = true;
230                 } else {
231                     start = Calendar.getInstance();
232                 }
233                 boolean stopModified = false;
234                 values = elmts[1].split(":");
235                 Calendar stop = getCalendar(values);
236                 if (stop != null) {
237                     stopModified = true;
238                 } else {
239                     stop = Calendar.getInstance();
240                 }
241                 logger.debug("Dates before check: Not between "+start.getTime()+" and "+stop.getTime());
242                 // Check that start < stop
243                 if (start.compareTo(stop) > 0) {
244                     // no so add 24H to stop
245                     stop.add(Calendar.DAY_OF_MONTH, 1);
246                 }
247                 // Check that start and stop > newDate (only start is checked since start <= stop)
248                 if (start.compareTo(newDate) < 0) {
249                     start.add(Calendar.DAY_OF_MONTH, 1);
250                     stop.add(Calendar.DAY_OF_MONTH, 1);
251                 }
252                 logger.debug("Dates after check: Not between "+start.getTime()+" and "+stop.getTime());
253                 if (!startModified) {
254                     if (newDate.compareTo(stop) < 0) {
255                         logger.debug("newDate: "+newDate.getTime()+" Should not be between "+start.getTime()+" and "+stop.getTime());
256                         return false;
257                     }
258                 } else if (start.compareTo(newDate) < 0) {
259                     if ((!stopModified) || (newDate.compareTo(stop) < 0)) {
260                         logger.debug("newDate: "+newDate.getTime()+" Should not be between "+start.getTime()+" and "+stop.getTime());
261                         return false;
262                     }
263                 }
264             } else if (args[i].equalsIgnoreCase("-between")) {
265                 i ++;
266                 betweenTest = true;
267                 String[] elmts = args[i].split(";");
268                 boolean startModified = false;
269                 String[] values = elmts[0].split(":");
270                 Calendar start = getCalendar(values);
271                 if (start != null) {
272                     startModified = true;
273                 } else {
274                     start = Calendar.getInstance();
275                 }
276                 boolean stopModified = false;
277                 values = elmts[1].split(":");
278                 Calendar stop = getCalendar(values);
279                 if (stop != null) {
280                     stopModified = true;
281                 } else {
282                     stop = Calendar.getInstance();
283                 }
284                 logger.debug("Dates before check: Between "+start.getTime()+" and "+stop.getTime());
285                 // Check that start < stop
286                 if (start.compareTo(stop) > 0) {
287                     // no so add 24H to stop
288                     stop.add(Calendar.DAY_OF_MONTH, 1);
289                 }
290                 // Check that start and stop > newDate (only start is checked since start <= stop)
291                 if (start.compareTo(newDate) < 0) {
292                     start.add(Calendar.DAY_OF_MONTH, 1);
293                     stop.add(Calendar.DAY_OF_MONTH, 1);
294                 }
295                 logger.debug("Dates before check: Between "+start.getTime()+" and "+stop.getTime());
296                 if (!startModified) {
297                     if (newDate.compareTo(stop) < 0) {
298                         logger.debug("newDate: "+newDate.getTime()+" is between "+start.getTime()+" and "+stop.getTime());
299                         betweenResult = true;
300                     }
301                 } else if (start.compareTo(newDate) < 0) {
302                     if ((!stopModified) || (newDate.compareTo(stop) < 0)) {
303                         logger.debug("newDate: "+newDate.getTime()+" is between "+start.getTime()+" and "+stop.getTime());
304                         betweenResult = true;
305                     }
306                 }
307             }
308         }
309         if (betweenTest) {
310             logger.debug("Since between is specified, do we found newDate: "+newDate.getTime()+" Result: "+betweenResult);
311             return betweenResult;
312         }
313         logger.debug("newDate: "+newDate.getTime()+" rescheduled");
314         return true;
315     }
316 
317     /**
318      * 
319      * @param values
320      *            as X+n or X-n or X=n or Xn where X=Y/M/D/H/m/s, n=number and
321      *            +/- meaning adding/subtracting from current date and = meaning
322      *            specific set value
323      * @return the Calendar if any specification, or null if no calendar
324      *         specified
325      */
326     private Calendar getCalendar(String[] values) {
327         Calendar newcal = Calendar.getInstance();
328         boolean isModified = false;
329         for (int j = 0; j < values.length; j ++) {
330             if (values[j].length() > 1) {
331                 int addvalue = 0; // will be different of 0
332                 int value = -1; // will be >= 0
333                 switch (values[j].charAt(0)) {
334                     case '+':
335                         try {
336                             addvalue = Integer.parseInt(values[j].substring(2));
337                         } catch (NumberFormatException e) {
338                             continue;
339                         }
340                         break;
341                     case '-':
342                         try {
343                             addvalue = Integer.parseInt(values[j].substring(1));
344                         } catch (NumberFormatException e) {
345                             continue;
346                         }
347                         break;
348                     case '=':
349                         try {
350                             value = Integer.parseInt(values[j].substring(2));
351                         } catch (NumberFormatException e) {
352                             continue;
353                         }
354                         break;
355                     default: // no sign
356                         try {
357                             value = Integer.parseInt(values[j].substring(1));
358                         } catch (NumberFormatException e) {
359                             continue;
360                         }
361                 }
362                 switch (values[j].charAt(0)) {
363                     case 'Y':
364                         if (value >= 0) {
365                             newcal.set(Calendar.YEAR, value);
366                         } else {
367                             newcal.add(Calendar.YEAR, addvalue);
368                         }
369                         isModified = true;
370                         break;
371                     case 'M':
372                         if (value >= 0) {
373                             newcal.set(Calendar.MONTH, value);
374                         } else {
375                             newcal.add(Calendar.MONTH, addvalue);
376                         }
377                         isModified = true;
378                         break;
379                     case 'D':
380                         if (value >= 0) {
381                             newcal.set(Calendar.DAY_OF_MONTH, value);
382                         } else {
383                             newcal.add(Calendar.DAY_OF_MONTH, addvalue);
384                         }
385                         isModified = true;
386                         break;
387                     case 'H':
388                         if (value >= 0) {
389                             newcal.set(Calendar.HOUR_OF_DAY, value);
390                         } else {
391                             newcal.add(Calendar.HOUR_OF_DAY, addvalue);
392                         }
393                         isModified = true;
394                         break;
395                     case 'm':
396                         if (value >= 0) {
397                             newcal.set(Calendar.MINUTE, value);
398                         } else {
399                             newcal.add(Calendar.MINUTE, addvalue);
400                         }
401                         isModified = true;
402                         break;
403                     case 'S':
404                         if (value >= 0) {
405                             newcal.set(Calendar.SECOND, value);
406                         } else {
407                             newcal.add(Calendar.SECOND, addvalue);
408                         }
409                         isModified = true;
410                         break;
411                 }
412             }
413         }
414         if (isModified) return newcal;
415         return null;
416     }
417 }