View Javadoc
1   /**
2    * This file Copyright (c) 2020 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.contentapp.async;
35  
36  import info.magnolia.context.AsynchronousContext;
37  import info.magnolia.context.Context;
38  import info.magnolia.i18nsystem.SimpleTranslator;
39  import info.magnolia.ui.api.ioc.AdmincentralScoped;
40  import info.magnolia.ui.api.message.Message;
41  import info.magnolia.ui.api.message.MessageType;
42  import info.magnolia.ui.framework.ioc.Destructible;
43  import info.magnolia.ui.framework.message.MessagesManager;
44  
45  import java.util.Optional;
46  import java.util.concurrent.ExecutionException;
47  import java.util.concurrent.Executors;
48  import java.util.concurrent.Future;
49  import java.util.concurrent.ScheduledExecutorService;
50  import java.util.concurrent.TimeUnit;
51  import java.util.concurrent.TimeoutException;
52  import java.util.concurrent.atomic.AtomicReference;
53  
54  import javax.inject.Inject;
55  
56  import org.apache.commons.lang3.exception.ExceptionUtils;
57  
58  import com.vaadin.ui.Notification;
59  import com.vaadin.ui.UI;
60  
61  import lombok.Builder;
62  import lombok.NonNull;
63  
64  /**
65   * Executes an {@link info.magnolia.ui.contentapp.async.AsyncActionExecutor.AsyncAction} by {@link java.util.concurrent.ExecutorService}.
66   * The service is shut down on admincentral destruction.
67   */
68  @AdmincentralScoped
69  public class AsyncActionExecutor implements Destructible {
70  
71      private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
72  
73      private final AsynchronousContext.OperationFactory operationFactory;
74      private final MessagesManager messagesManager;
75      private final SimpleTranslator simpleTranslator;
76      private final UI ui;
77      private final String userName;
78  
79      @Inject
80      AsyncActionExecutor(AsynchronousContext.OperationFactory operationFactory, Context context, MessagesManager messagesManager, SimpleTranslator simpleTranslator, UI ui) {
81          this.operationFactory = operationFactory;
82          this.messagesManager = messagesManager;
83          this.simpleTranslator = simpleTranslator;
84          this.ui = ui;
85          userName = context.getUser().getName();
86      }
87  
88      public void execute(AsyncAction action) {
89          final AtomicReference<Notification> asynchronousActionStartedNotification = new AtomicReference<>();
90          final Future<?> future = executorService.schedule(operationFactory.wrap(() -> {
91              try {
92                  action.action.run();
93                  if (asynchronousActionStartedNotification.get() != null && action.successMessage != null) {
94                      Messagee/Message.html#Message">Message message = new Message(MessageType.INFO, action.description, action.successMessage);
95                      messagesManager.sendMessage(userName, message);
96                      ui.push();
97                  }
98              } catch (Exception e) {
99                  Messagee/Message.html#Message">Message message = new Message(MessageType.ERROR, action.description, action.failureMessage);
100                 message.setView("ui-admincentral:longRunning");
101                 message.addProperty("exception", ExceptionUtils.getMessage(e));
102                 messagesManager.sendMessage(userName, message);
103                 ui.push();
104             }
105             Optional.ofNullable(asynchronousActionStartedNotification.get()).ifPresent(Notification::close);
106         }), action.delaySeconds, TimeUnit.SECONDS);
107 
108         try {
109             future.get(action.timeToWaitMilliseconds, TimeUnit.MILLISECONDS);
110         } catch (InterruptedException | ExecutionException e) {
111             throw new RuntimeException(e);
112         } catch (TimeoutException e) {
113             asynchronousActionStartedNotification.set(Notification.show(simpleTranslator.translate("ui-framework.abstractcommand.asyncaction.long")));
114         }
115     }
116 
117     @Override
118     public void destroy() {
119         executorService.shutdown();
120     }
121 
122     @Builder
123     public static class AsyncAction {
124         @NonNull
125         private Runnable action;
126         private long timeToWaitMilliseconds;
127         private long delaySeconds;
128         private String description;
129         private String successMessage;
130         private String failureMessage;
131     }
132 }