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.framework.action.async;
35
36 import info.magnolia.cms.security.User;
37 import info.magnolia.context.Context;
38 import info.magnolia.context.MgnlContext;
39 import info.magnolia.i18nsystem.SimpleTranslator;
40 import info.magnolia.module.scheduler.CommandJob;
41 import info.magnolia.module.scheduler.SchedulerConsts;
42 import info.magnolia.module.scheduler.SchedulerModule;
43 import info.magnolia.objectfactory.Components;
44 import info.magnolia.ui.api.action.CommandActionDefinition;
45 import info.magnolia.ui.api.app.SubAppContext;
46 import info.magnolia.ui.api.context.UiContext;
47 import info.magnolia.ui.api.message.Message;
48 import info.magnolia.ui.api.message.MessageType;
49 import info.magnolia.ui.framework.message.MessagesManager;
50 import info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter;
51
52 import java.util.Calendar;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.concurrent.atomic.AtomicInteger;
56
57 import javax.inject.Inject;
58 import javax.inject.Provider;
59 import javax.jcr.RepositoryException;
60
61 import org.apache.commons.lang3.StringUtils;
62 import org.apache.commons.lang3.exception.ExceptionUtils;
63 import org.quartz.JobDetail;
64 import org.quartz.JobExecutionContext;
65 import org.quartz.Scheduler;
66 import org.quartz.SchedulerException;
67 import org.quartz.SimpleTrigger;
68 import org.quartz.Trigger;
69 import org.quartz.TriggerListener;
70 import org.quartz.listeners.TriggerListenerSupport;
71
72
73
74
75
76
77 public class DefaultAsyncActionExecutor<D extends CommandActionDefinition> implements AsyncActionExecutor {
78
79 private static AtomicInteger idx = new AtomicInteger();
80
81 private final D definition;
82 private final Provider<SchedulerModule> schedulerModuleProvider;
83 private final User user;
84 private final UiContext uiContext;
85 private final String catalogName;
86 private final SimpleTranslator i18n;
87 private final String commandName;
88
89 @Inject
90 public DefaultAsyncActionExecutor(final D definition, final Provider<SchedulerModule> schedulerModuleProvider, final Context context,
91 final UiContext uiContext, final SimpleTranslator i18n) {
92 this.definition = definition;
93 this.schedulerModuleProvider = schedulerModuleProvider;
94 this.user = context.getUser();
95 this.uiContext = uiContext;
96 this.i18n = i18n;
97 this.commandName = definition.getCommand();
98 this.catalogName = definition.getCatalog();
99 }
100
101 @Override
102 public boolean execute(JcrItemAdapter item, Map<String, Object> params) throws Exception {
103 Calendar cal = Calendar.getInstance();
104
105 cal.add(Calendar.SECOND, definition.getDelay());
106
107
108 int timeToWait = definition.getTimeToWait();
109
110 String jobName = "UI Action triggered execution of [" + (StringUtils.isNotEmpty(catalogName) ? (catalogName + ":") : "") + commandName + "] by user [" + StringUtils.defaultIfEmpty(user.getName(), "") + "].";
111
112 if (definition.isParallel()) {
113 jobName += " (" + idx.getAndIncrement() + ")";
114 }
115 SimpleTrigger trigger = new SimpleTrigger(jobName, SchedulerConsts.SCHEDULER_GROUP_NAME, cal.getTime());
116 trigger.addTriggerListener(jobName + "_trigger");
117
118 final JobDetail jd = new JobDetail(jobName, SchedulerConsts.SCHEDULER_GROUP_NAME, info.magnolia.module.scheduler.CommandJob.class);
119 jd.getJobDataMap().put(SchedulerConsts.CONFIG_JOB_COMMAND, commandName);
120 jd.getJobDataMap().put(SchedulerConsts.CONFIG_JOB_COMMAND_CATALOG, catalogName);
121 jd.getJobDataMap().put(SchedulerConsts.CONFIG_JOB_PARAMS, params);
122
123 Scheduler scheduler = schedulerModuleProvider.get().getScheduler();
124 scheduler.addTriggerListener(getListener(jobName, item));
125 try {
126 scheduler.scheduleJob(jd, trigger);
127 }
128 catch (SchedulerException e) {
129 throw new ParallelExecutionException(e);
130 }
131
132
133 Thread.sleep(definition.getDelay() * 1000 + 100);
134 int timeToSleep = 500;
135
136 while (timeToWait > 0) {
137 List<JobExecutionContext> jobs = scheduler.getCurrentlyExecutingJobs();
138 if (isJobRunning(jobs, jobName)) {
139 Thread.sleep(timeToSleep);
140 } else {
141 break;
142 }
143 timeToWait -= timeToSleep;
144 }
145
146
147 return (timeToWait == 0);
148 }
149
150 protected TriggerListener getListener(String jobName, JcrItemAdapter item) throws RepositoryException {
151 return new CommandActionTriggerListener(definition, jobName + "_trigger", uiContext, i18n, item.getJcrItem().getPath());
152 }
153
154 private boolean isJobRunning(List<JobExecutionContext> jobs, String jobName) {
155 for (JobExecutionContext job : jobs) {
156 if (job.getJobDetail().getName().equals(jobName)) {
157 return true;
158 }
159 }
160 return false;
161 }
162
163
164
165
166 public class CommandActionTriggerListener extends TriggerListenerSupport {
167
168 private final D definition;
169 private final String name;
170 private final SimpleTranslator i18n;
171 private final String successMessageTitle;
172 private final String successMessage;
173 private final String errorMessageTitle;
174 private final String errorMessage;
175
176 @Inject
177 public CommandActionTriggerListener(D definition, String triggerName, UiContext uiContext, SimpleTranslator i18n, String path) {
178 this.definition = definition;
179 this.name = triggerName;
180 this.i18n = i18n;
181
182 String appName = uiContext instanceof SubAppContext ? ((SubAppContext) uiContext).getSubAppDescriptor().getLabel() : null;
183 this.successMessageTitle = i18n.translate("ui-framework.abstractcommand.asyncaction.successTitle", definition.getLabel());
184 this.successMessage = i18n.translate("ui-framework.abstractcommand.asyncaction.successMessage", definition.getLabel(), appName, path);
185 this.errorMessageTitle = i18n.translate("ui-framework.abstractcommand.asyncaction.errorTitle", definition.getLabel());
186 this.errorMessage = i18n.translate("ui-framework.abstractcommand.asyncaction.errorMessage", definition.getLabel(), appName, path);
187 }
188
189 @Override
190 public String getName() {
191 return name;
192 }
193
194 @Override
195 public void triggerComplete(final Trigger trigger, final JobExecutionContext jobExecutionContext, int i) {
196 if (!definition.isNotifyUser()) {
197 return;
198 }
199 MgnlContext.doInSystemContext(new MgnlContext.VoidOp() {
200 @Override
201 public void doExec() {
202
203 MessagesManager messagesManager = Components.getComponent(MessagesManager.class);
204
205 CommandJob.JobResult result = (CommandJob.JobResult) jobExecutionContext.getResult();
206 if (result.isSuccess()) {
207 messagesManager.sendMessage(user.getName(), new Message(MessageType.INFO, successMessageTitle, successMessage));
208 } else {
209 Message msg = new Message(MessageType.WARNING, errorMessageTitle, errorMessage);
210 msg.setView("ui-admincentral:longRunning");
211 msg.addProperty("exception", ExceptionUtils.getMessage(result.getException()));
212 msg.addProperty("comment", i18n.translate("ui-framework.abstractcommand.asyncaction.errorComment"));
213 messagesManager.sendMessage(user.getName(), msg);
214 }
215 }
216 });
217 }
218 }
219 }