View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.util.concurrent.processing;
20  
21  import java.time.Instant;
22  import java.util.concurrent.Callable;
23  import java.util.concurrent.CompletableFuture;
24  import java.util.concurrent.ExecutorService;
25  import java.util.concurrent.Future;
26  import java.util.function.Supplier;
27  
28  import org.mycore.common.MCRException;
29  import org.mycore.common.processing.MCRProcessable;
30  import org.mycore.common.processing.MCRProcessableStatus;
31  import org.mycore.common.processing.MCRProcessableTask;
32  import org.mycore.common.processing.MCRProgressable;
33  import org.mycore.util.concurrent.MCRDecorator;
34  import org.mycore.util.concurrent.MCRPrioritySupplier;
35  
36  /**
37   * A processable supplier combines a {@link Supplier} and a {@link MCRProcessable}.
38   * The supplier will be executed with the help of an {@link CompletableFuture}.
39   * To get the future call {@link #getFuture()}.
40   * 
41   * @author Matthias Eichner
42   *
43   * @param <R> the result of the task
44   */
45  public class MCRProcessableSupplier<R> extends MCRProcessableTask<Callable<R>> implements Supplier<R> {
46  
47      protected CompletableFuture<R> future;
48  
49      /**
50       * Creates a new {@link MCRProcessableSupplier} by the already committed task and its future.
51       * 
52       * @param task the task which should be executed
53       * @param future the future
54       * @return a new processable supplier
55       */
56      public static <T> MCRProcessableSupplier<T> of(Callable<T> task, CompletableFuture<T> future) {
57          MCRProcessableSupplier<T> ps = new MCRProcessableSupplier<>(task);
58          ps.future = future;
59          return ps;
60      }
61  
62      /**
63       * Creates a new {@link MCRProcessableSupplier} by submitting the task to the executorService with
64       * the given priority.
65       * 
66       * @param task the task to submit
67       * @param executorService the executor service
68       * @param priority the priority
69       * @return a new processable supplier
70       */
71      public static <T> MCRProcessableSupplier<T> of(Callable<T> task, ExecutorService executorService,
72          Integer priority) {
73          MCRProcessableSupplier<T> ps = new MCRProcessableSupplier<>(task);
74          ps.future = new MCRPrioritySupplier<>(ps, priority).runAsync(executorService);
75          return ps;
76      }
77  
78      /**
79       * Private constructor, 
80       * 
81       * @param task the task itself
82       */
83      private MCRProcessableSupplier(Callable<R> task) {
84          super(task);
85      }
86  
87      /**
88       * Gets the result of the task. Will usually be called by the executor service
89       * and should not be executed directly.
90       */
91      @Override
92      public R get() {
93          try {
94              this.setStatus(MCRProcessableStatus.processing);
95              this.startTime = Instant.now();
96              R result = getTask().call();
97              this.setStatus(MCRProcessableStatus.successful);
98              return result;
99          } catch (InterruptedException exc) {
100             this.error = exc;
101             this.setStatus(MCRProcessableStatus.canceled);
102             throw new MCRException(this.error);
103         } catch (Exception exc) {
104             this.error = exc;
105             this.setStatus(MCRProcessableStatus.failed);
106             throw new MCRException(this.error);
107         }
108     }
109 
110     /**
111      * The future the task is assigned to.
112      * 
113      * @return the future
114      */
115     public CompletableFuture<R> getFuture() {
116         return future;
117     }
118 
119     /**
120      * Returns true if this task completed. Completion may be due to normal termination,
121      * an exception, or cancellation -- in all of these cases, this method will return true.
122      *
123      * @return true if this task completed
124      */
125     public boolean isFutureDone() {
126         return future.isDone();
127     }
128 
129     /**
130      * Same as {@link Future#cancel(boolean)}.
131      * 
132      * @param mayInterruptIfRunning true if the thread executing this task should be interrupted;
133      *      otherwise, in-progress tasks are allowed to complete
134      * @return false if the task could not be cancelled, typically because it has already
135      *      completed normally; true otherwise
136      */
137     public boolean cancel(boolean mayInterruptIfRunning) {
138         return future.cancel(mayInterruptIfRunning);
139     }
140 
141     /**
142      * Returns an integer between 0-100 indicating the progress
143      * of the processable. Can return null if the task is not started
144      * yet or the task is not an instance of {@link MCRProgressable}.
145      * 
146      * @return the progress between 0-100 or null
147      */
148     @Override
149     public Integer getProgress() {
150         if (task instanceof MCRProgressable) {
151             return ((MCRProgressable) task).getProgress();
152         }
153         return super.getProgress();
154     }
155 
156     /**
157      * Returns a human readable text indicating the state of the progress.
158      * 
159      * @return progress text
160      */
161     @Override
162     public String getProgressText() {
163         if (task instanceof MCRProgressable) {
164             return ((MCRProgressable) task).getProgressText();
165         }
166         return super.getProgressText();
167     }
168 
169     /**
170      * Returns the name of this process. If no name is set
171      * this returns the simplified class name of the task.
172      * 
173      * @return a human readable name of this processable task
174      */
175     @Override
176     public String getName() {
177         String name = super.getName();
178         if (name == null) {
179             return MCRDecorator.resolve(this.task).map(object -> {
180                 if (object instanceof MCRProcessable) {
181                     return ((MCRProcessable) object).getName();
182                 }
183                 return object.toString();
184             }).orElse(this.task.toString());
185         }
186         return name;
187     }
188 
189 }