1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
66
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 } else if (action.alwaysShowSuccessMessage) {
97 ui.access(() -> { Notification.show(action.successMessage); });
98 }
99 ui.push();
100 } catch (Exception e) {
101 Messagee/Message.html#Message">Message message = new Message(MessageType.ERROR, action.description, action.failureMessage);
102 message.setView("ui-admincentral:longRunning");
103 message.addProperty("exception", ExceptionUtils.getMessage(e));
104 messagesManager.sendMessage(userName, message);
105 ui.push();
106 }
107 Optional.ofNullable(asynchronousActionStartedNotification.get()).ifPresent(Notification::close);
108 }), action.delaySeconds, TimeUnit.SECONDS);
109
110 try {
111 future.get(action.timeToWaitMilliseconds, TimeUnit.MILLISECONDS);
112 } catch (InterruptedException | ExecutionException e) {
113 throw new RuntimeException(e);
114 } catch (TimeoutException e) {
115 asynchronousActionStartedNotification.set(Notification.show(simpleTranslator.translate("ui-framework.abstractcommand.asyncaction.long")));
116 }
117 }
118
119 @Override
120 public void destroy() {
121 executorService.shutdown();
122 }
123
124 @Builder
125 public static class AsyncAction {
126 @NonNull
127 private Runnable action;
128 private long timeToWaitMilliseconds;
129 private long delaySeconds;
130 private String description;
131 private String successMessage;
132 private String failureMessage;
133 private boolean alwaysShowSuccessMessage;
134 }
135 }