View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.vitam.ingest;
22  
23  import com.fasterxml.jackson.annotation.JsonGetter;
24  import com.fasterxml.jackson.annotation.JsonIgnore;
25  import com.fasterxml.jackson.annotation.JsonProperty;
26  import com.fasterxml.jackson.annotation.JsonSetter;
27  import fr.gouv.vitam.common.GlobalDataRest;
28  import fr.gouv.vitam.common.StringUtils;
29  import fr.gouv.vitam.common.exception.InvalidParseOperationException;
30  import fr.gouv.vitam.common.json.JsonHandler;
31  import fr.gouv.vitam.common.model.LocalFile;
32  import fr.gouv.vitam.common.model.RequestResponseOK;
33  import org.waarp.common.exception.IllegalFiniteStateException;
34  import org.waarp.common.exception.InvalidArgumentException;
35  import org.waarp.common.logging.WaarpLogger;
36  import org.waarp.common.logging.WaarpLoggerFactory;
37  import org.waarp.common.state.MachineState;
38  import org.waarp.common.state.Transition;
39  import org.waarp.common.utility.ParametersChecker;
40  import org.waarp.vitam.common.AbstractVitamRequest;
41  import org.waarp.vitam.common.WaarpCommon.TaskOption;
42  
43  import java.io.File;
44  import java.util.EnumSet;
45  import java.util.concurrent.ConcurrentHashMap;
46  
47  /**
48   * IngestRequest is the unitary entry for Ingest operations made by Waarp to
49   * Vitam
50   */
51  public class IngestRequest extends AbstractVitamRequest {
52    /**
53     * Only kind of action supported by Vitam and Waarp
54     */
55    static final String RESUME = "RESUME";
56    /**
57     * Internal Logger
58     */
59    private static final WaarpLogger logger =
60        WaarpLoggerFactory.getLogger(IngestRequest.class);
61    @JsonIgnore
62    MachineState<IngestStep> step = IngestStep.newSessionMachineState();
63    /*
64     contextId – a type of ingest among "DEFAULT_WORKFLOW" (Sip ingest),
65     "HOLDING_SCHEME" (tree) "FILING_SCHEME" (plan)
66     */
67    @JsonProperty("contextId")
68    private String contextId;
69    @JsonProperty("action")
70    private String action = RESUME;
71    @JsonProperty("checkAtr")
72    private boolean checkAtr;
73  
74    public IngestRequest() {
75      // Empty constructor for Json
76    }
77  
78    /**
79     * Standard constructor
80     *
81     * @param taskOption
82     * @param contextId
83     * @param action
84     * @param checkAtr
85     * @param factory
86     *
87     * @throws InvalidParseOperationException
88     */
89    public IngestRequest(final TaskOption taskOption, final String contextId,
90                         final String action, final boolean checkAtr,
91                         final IngestRequestFactory factory)
92        throws InvalidParseOperationException {
93      super(taskOption);
94      try {
95        ParametersChecker
96            .checkParameterDefault(getCheckMessage(), contextId, action);
97        ParametersChecker.checkSanityString(contextId, action);
98      } catch (IllegalArgumentException | InvalidArgumentException e) {
99        logger.error(e);
100       throw new InvalidParseOperationException(e);
101     }
102     this.contextId = contextId;
103     this.action = action;
104     this.checkAtr = checkAtr;
105     this.status = this.step.getCurrent().getStatusMonitor();
106     try {
107       factory.saveNewIngestRequest(this);
108     } catch (InvalidParseOperationException e) {
109       logger.error("Will not be able to save: {}", this, e);
110       throw e;
111     }
112   }
113 
114   @Override
115   public String toString() {
116     return "Ingest = Step: " + (step != null? step.getCurrent() : "noStep") +
117            " " + JsonHandler.unprettyPrint(this);
118   }
119 
120   @JsonGetter("contextId")
121   public String getContextId() {
122     return contextId;
123   }
124 
125   /**
126    * @param contextId a type of ingest among "DEFAULT_WORKFLOW" (Sip ingest),
127    *     "HOLDING_SCHEME" (tree) "FILING_SCHEME" (plan)
128    *
129    * @return this
130    */
131   @JsonSetter("contextId")
132   public IngestRequest setContextId(final String contextId) {
133     try {
134       ParametersChecker.checkParameterDefault(getCheckMessage(), contextId);
135       StringUtils.checkSanityString(contextId);
136     } catch (InvalidParseOperationException | IllegalArgumentException e) {
137       logger.error(e);
138       throw new IllegalArgumentException(e.getMessage(), e);
139     }
140     this.contextId = contextId;
141     return this;
142   }
143 
144   @JsonGetter("action")
145   public String getAction() {
146     return action;
147   }
148 
149   /**
150    * @param action shall be "RESUME" only
151    *
152    * @return this
153    */
154   @JsonSetter("action")
155   public IngestRequest setAction(final String action) {
156     try {
157       ParametersChecker.checkParameterDefault(getCheckMessage(), action);
158       StringUtils.checkSanityString(action);
159     } catch (InvalidParseOperationException | IllegalArgumentException e) {
160       logger.error(e);
161       throw new IllegalArgumentException(e.getMessage(), e);
162     }
163     this.action = action;
164     return this;
165   }
166 
167   /**
168    * Use to set the step and status accordingly.
169    *
170    * @param step
171    * @param status
172    * @param factory
173    *
174    * @return this
175    *
176    * @throws InvalidParseOperationException
177    */
178   @JsonIgnore
179   public IngestRequest setStep(final IngestStep step, final int status,
180                                IngestRequestFactory factory)
181       throws InvalidParseOperationException {
182     if (this.step == null) {
183       if (!step.equals(IngestStep.END)) {
184         logger.debug("Step {} could not be set since IngestRequest done", step);
185       }
186       // Nothing to do since already done
187       return this;
188     }
189     if (!step.equals(IngestStep.ERROR) && this.step.getCurrent().equals(step)) {
190       // nothing to do
191       return this;
192     }
193     try {
194       this.step.setCurrent(step);
195     } catch (IllegalFiniteStateException e) {
196       logger.error(e);
197       this.step.setDryCurrent(step);
198     }
199     setStatus(step != IngestStep.ERROR? step.getStatusMonitor() : status)
200         .setLastTryTime(System.currentTimeMillis());
201     return save(factory);
202   }
203 
204   /**
205    * Set the status AND the step according to the value of the status (if
206    * less than 0, it is a step value, not a final status), but in dry mode
207    * (no check, used by Json deserialization)
208    *
209    * @param status
210    *
211    * @return this
212    */
213   @JsonSetter("status")
214   public IngestRequest setStatus(final int status) {
215     this.status = status;
216     if (step != null) {
217       step.setDryCurrent(IngestStep.getFromInt(status));
218     }
219     return this;
220   }
221 
222   /**
223    * Save this IngestRequest
224    *
225    * @param factory
226    *
227    * @return this
228    *
229    * @throws InvalidParseOperationException
230    */
231   @JsonIgnore
232   public IngestRequest save(IngestRequestFactory factory)
233       throws InvalidParseOperationException {
234     factory.saveIngestRequest(this);
235     return this;
236   }
237 
238   @JsonIgnore
239   public IngestStep getStep() {
240     if (step == null) {
241       return null;
242     }
243     return step.getCurrent();
244   }
245 
246   @JsonGetter("checkAtr")
247   public boolean isCheckAtr() {
248     return checkAtr;
249   }
250 
251   @JsonSetter("checkAtr")
252   public IngestRequest setCheckAtr(final boolean checkAtr) {
253     this.checkAtr = checkAtr;
254     return this;
255   }
256 
257   /**
258    * Set extra information from first response from operation submission
259    *
260    * @param requestResponse
261    *
262    * @return this
263    */
264   @JsonIgnore
265   public IngestRequest setFromRequestResponse(
266       RequestResponseOK requestResponse) {
267     String requestIdNew =
268         requestResponse.getHeaderString(GlobalDataRest.X_REQUEST_ID);
269     String globalExecutionStateNew = requestResponse
270         .getHeaderString(GlobalDataRest.X_GLOBAL_EXECUTION_STATE);
271     String globalExecutionStatusNew = requestResponse
272         .getHeaderString(GlobalDataRest.X_GLOBAL_EXECUTION_STATUS);
273     try {
274       ParametersChecker.checkParameterDefault(getCheckMessage(), requestIdNew,
275                                               globalExecutionStateNew,
276                                               globalExecutionStatusNew);
277       ParametersChecker.checkSanityString(requestIdNew, globalExecutionStateNew,
278                                           globalExecutionStatusNew);
279     } catch (IllegalArgumentException | InvalidArgumentException e) {
280       logger.error(e);
281       throw new IllegalArgumentException(e.getMessage(), e);
282     }
283     setGlobalExecutionState(globalExecutionStateNew)
284         .setGlobalExecutionStatus(globalExecutionStatusNew)
285         .setRequestId(requestIdNew);
286     return this;
287   }
288 
289   /**
290    * @return the LocalFile according to this
291    */
292   @JsonIgnore
293   public LocalFile getLocalFile() {
294     return new LocalFile(getPath());
295   }
296 
297   /**
298    * @return the ATR File pointer according to this
299    */
300   @JsonIgnore
301   public File getAtrFile(IngestRequestFactory factory) {
302     return factory.getXmlAtrFile(this);
303   }
304 
305   /**
306    * Context accepted by Vitam
307    */
308   enum CONTEXT {
309     /**
310      * Sip ingest
311      */
312     DEFAULT_WORKFLOW,
313     /**
314      * Tree
315      */
316     HOLDING_SCHEME,
317     /**
318      * Plan
319      */
320     FILING_SCHEME;
321 
322     public static boolean checkCorrectness(String arg) {
323       try {
324         CONTEXT.valueOf(arg);
325         return true;
326       } catch (IllegalArgumentException ignore) {
327         return false;
328       }
329     }
330   }
331 
332   /**
333    * Different steps of Ingest from Waarp point of view
334    */
335   enum IngestStep {
336     /**
337      * IngestRequest not started yet
338      */
339     STARTUP(-1),
340     /**
341      * IngestRequest INGEST to retry
342      */
343     RETRY_INGEST(-2),
344     /**
345      * IngestRequest INGEST Id to retry
346      */
347     RETRY_INGEST_ID(-3),
348     /**
349      * IngestRequest ATR to get
350      */
351     RETRY_ATR(-4),
352     /**
353      * IngestRequest ATR to forward
354      */
355     RETRY_ATR_FORWARD(-5),
356     /**
357      * IngestRequest Error
358      */
359     ERROR(-7),
360     /**
361      * Final End step
362      */
363     END(-10);
364 
365     private static final ConcurrentHashMap<IngestStep, EnumSet<IngestStep>>
366         stateMap = new ConcurrentHashMap<>();
367 
368     static {
369       initR66FiniteStates();
370     }
371 
372     private final int statusMonitor;
373 
374     IngestStep(int status) {
375       this.statusMonitor = status;
376     }
377 
378     /**
379      * This method should be called once at startup to initialize the Finite
380      * States association.
381      */
382     private static void initR66FiniteStates() {
383       for (final IngestTransition trans : IngestTransition.values()) {
384         stateMap.put(trans.elt.getState(), trans.elt.getSet());
385       }
386     }
387 
388     /**
389      * @return a new Session MachineState for OpenR66
390      */
391     private static MachineState<IngestStep> newSessionMachineState() {
392       return new MachineState<>(STARTUP, stateMap);
393     }
394 
395     /**
396      * @param machine the Session MachineState to release
397      */
398     static void endSessionMachineSate(MachineState<IngestStep> machine) {
399       if (machine != null) {
400         machine.release();
401       }
402     }
403 
404     static IngestStep getFromInt(int status) {
405       switch (status) {
406         case -1:
407           return STARTUP;
408         case -2:
409           return RETRY_INGEST;
410         case -3:
411           return RETRY_INGEST_ID;
412         case -4:
413           return RETRY_ATR;
414         case -5:
415           return RETRY_ATR_FORWARD;
416         case -10:
417           return END;
418         case -7:
419         default:
420           return ERROR;
421       }
422     }
423 
424     int getStatusMonitor() {
425       return statusMonitor;
426     }
427 
428     private enum IngestTransition {
429       T_STARTUP(STARTUP, EnumSet.of(RETRY_INGEST, ERROR)),
430       T_RETRY_INGEST(RETRY_INGEST, EnumSet.of(RETRY_INGEST_ID, ERROR)),
431       T_RETRY_INGEST_ID(RETRY_INGEST_ID, EnumSet.of(RETRY_ATR, ERROR, END)),
432       T_RETRY_ATR(RETRY_ATR, EnumSet.of(RETRY_ATR_FORWARD, ERROR)),
433       T_RETRY_ATR_FORWARD(RETRY_ATR_FORWARD, EnumSet.of(ERROR, END)),
434       T_ERROR(ERROR, EnumSet.of(ERROR, END)), T_END(END, EnumSet.of(END));
435 
436       private final Transition<IngestStep> elt;
437 
438       IngestTransition(IngestStep state, EnumSet<IngestStep> set) {
439         elt = new Transition<>(state, set);
440       }
441 
442     }
443   }
444 
445 }