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.module.groovy.console;
35
36 import info.magnolia.context.Context;
37 import info.magnolia.context.MgnlContext;
38 import info.magnolia.i18nsystem.SimpleTranslator;
39 import info.magnolia.module.groovy.support.classes.MgnlGroovyClassLoader;
40 import info.magnolia.objectfactory.Components;
41 import info.magnolia.ui.api.message.Message;
42 import info.magnolia.ui.api.message.MessageType;
43 import info.magnolia.ui.framework.message.MessagesManager;
44
45 import java.io.ByteArrayInputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.InputStreamReader;
49 import java.io.StringWriter;
50 import java.io.Writer;
51 import java.util.Objects;
52 import java.util.concurrent.Callable;
53 import java.util.concurrent.ExecutionException;
54 import java.util.concurrent.ExecutorService;
55 import java.util.concurrent.Executors;
56 import java.util.concurrent.Future;
57
58 import org.apache.commons.lang3.CharEncoding;
59 import org.codehaus.groovy.control.CompilationFailedException;
60 import org.codehaus.groovy.runtime.InvokerHelper;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 import com.vaadin.event.UIEvents.PollEvent;
65 import com.vaadin.event.UIEvents.PollListener;
66 import com.vaadin.ui.UI;
67
68 import groovy.lang.Binding;
69 import groovy.lang.GroovyCodeSource;
70 import groovy.lang.GroovyShell;
71 import groovy.lang.Script;
72
73
74
75
76 public class MgnlGroovyConsole extends GroovyShell {
77
78 private static final Logger log = LoggerFactory.getLogger(MgnlGroovyConsole.class);
79
80 private static final MgnlGroovyClassLoader GROOVY_CLASS_LOADER = new MgnlGroovyClassLoader();
81
82 private static final int POLL_INTERVAL = 1000;
83
84 private MessagesManager messagesManager;
85
86 private SimpleTranslator i18n;
87
88
89
90
91 public MgnlGroovyConsole(Binding binding) {
92 this(binding, Components.getComponent(MessagesManager.class), Components.getComponent(SimpleTranslator.class));
93 }
94
95 public MgnlGroovyConsole(Binding binding, MessagesManager messagesManager, SimpleTranslator simpleTranslator) {
96 super(binding);
97 this.messagesManager = messagesManager;
98 this.i18n = simpleTranslator;
99 }
100
101
102
103
104
105
106
107
108 @Deprecated
109 public Object evaluate(InputStream in, String fileName, Writer out) throws CompilationFailedException {
110 Context originalCtx = MgnlContext.getInstance();
111 MgnlGroovyConsoleContext groovyCtx = new MgnlGroovyConsoleContext(originalCtx);
112 MgnlContext.setInstance(groovyCtx);
113
114 Script script = null;
115 try {
116 script = createScript(in, out);
117 script.setProperty("ctx", MgnlContext.getInstance());
118 return script.run();
119 } catch (IOException e) {
120 throw new RuntimeException(e);
121 } finally {
122 if (script != null) {
123 InvokerHelper.removeClass(script.getClass());
124 }
125 MgnlContext.setInstance(originalCtx);
126 }
127 }
128
129
130
131
132
133
134
135
136
137
138 public void runAsync(String source, UI ui, ScriptCallback callback) throws IOException, InterruptedException {
139 Writer out = new StringWriter();
140 final Script script = createScript(source, out);
141
142 final Context originalCtx = MgnlContext.getInstance();
143 final MgnlGroovyConsoleContext groovyCtx = new MgnlGroovyConsoleContext(originalCtx);
144
145 ExecutorService executorService = Executors.newSingleThreadExecutor();
146
147 final Future<Object> task = executorService.submit(new Callable<Object>() {
148 @Override
149 public Object call() throws Exception {
150
151
152 MgnlContext.setInstance(groovyCtx);
153 script.setProperty("ctx", groovyCtx);
154 return script.run();
155 }
156 });
157
158
159
160
161 Thread.sleep(200);
162 if (task.isDone()) {
163 handleScriptDone(callback, task, ui, out, messagesManager, i18n, false);
164 return;
165 }
166
167
168 for (Object listener : ui.getListeners(PollEvent.class)) {
169 if (listener instanceof ScriptDonePollListener) {
170 ui.removePollListener((ScriptDonePollListener) listener);
171 }
172 }
173
174
175 if (ui.getPollInterval() < POLL_INTERVAL) {
176 ui.setPollInterval(POLL_INTERVAL);
177 }
178 ui.addPollListener(new ScriptDonePollListener(task, ui, out, callback, messagesManager, i18n));
179 }
180
181 private Script createScript(String code, Writer out) throws CompilationFailedException, IOException {
182 return createScript(new ByteArrayInputStream(code.getBytes()), out);
183 }
184
185 private Script createScript(InputStream in, Writer out) throws CompilationFailedException, IOException {
186 Script script = parse(new InputStreamReader(in, CharEncoding.UTF_8), generateScriptName());
187 script.setProperty("out", out);
188 script.setProperty("err", out);
189 return script;
190 }
191
192
193
194
195
196
197
198
199 @Override
200 public Script parse(final GroovyCodeSource codeSource) throws CompilationFailedException {
201 return InvokerHelper.createScript(parseClass(codeSource), getContext());
202 }
203
204 @Override
205 public String generateScriptName() {
206
207 return super.generateScriptName();
208 }
209
210
211
212
213 private Class parseClass(final GroovyCodeSource codeSource) throws CompilationFailedException {
214
215 return GROOVY_CLASS_LOADER.parseClass(codeSource, false);
216 }
217
218
219
220
221 public interface ScriptCallback {
222
223
224
225
226
227 void onSuccess(String result);
228
229
230
231
232
233
234 void onFailure(Throwable e);
235
236
237
238
239
240
241 void onProgress(Writer out);
242
243
244
245
246 boolean requiresNotificationMessageUponCompletion();
247 }
248
249 private static void handleScriptDone(ScriptCallback callback, final Future<Object> scriptTask, UI ui, Writer out, MessagesManager messagesManager, SimpleTranslator i18n, boolean sendNotificationMessage) {
250 try {
251 String resultAsString = scriptTask.get() == null ? "" : Objects.toString(scriptTask.get());
252 String completeOutput;
253 if (out.toString().isEmpty()) {
254 completeOutput = resultAsString;
255 } else {
256 completeOutput = out.toString().concat("\n").concat(resultAsString);
257 }
258
259 log.debug("Script run successfully with result {}", completeOutput);
260 callback.onSuccess(completeOutput);
261 if (sendNotificationMessage) {
262 messagesManager.sendLocalMessage(new Message(MessageType.INFO, i18n.translate("groovy.script.console.done.success"), completeOutput));
263 }
264
265 } catch (InterruptedException | ExecutionException e) {
266 log.error("An error occurred while running a Groovy script", e);
267 callback.onFailure(e);
268
269 if (sendNotificationMessage) {
270 messagesManager.sendLocalMessage(new Message(MessageType.WARNING, i18n.translate("groovy.script.console.done.error"), e.getMessage()));
271 }
272 } finally {
273 log.debug("Stop polling");
274 ui.setPollInterval(-1);
275 }
276 }
277
278 @SuppressWarnings("serial")
279 private static final class ScriptDonePollListener implements PollListener {
280 private Future<Object> scriptTask;
281 private UI ui;
282 private ScriptCallback callback;
283 private Writer out;
284 private MessagesManager messagesManager;
285 private SimpleTranslator i18n;
286
287 public ScriptDonePollListener(Future<Object> scriptTask, UI ui, Writer out, ScriptCallback callback, MessagesManager messagesManager, SimpleTranslator i18n) {
288 this.scriptTask = scriptTask;
289 this.ui = ui;
290 this.callback = callback;
291 this.out = out;
292 this.messagesManager = messagesManager;
293 this.i18n = i18n;
294 }
295
296 @Override
297 public void poll(PollEvent event) {
298 if (scriptTask.isDone()) {
299 handleScriptDone(callback, scriptTask, ui, out, messagesManager, i18n, callback.requiresNotificationMessageUponCompletion());
300 } else {
301
302
303
304 if (ui.getPollInterval() < POLL_INTERVAL) {
305 ui.setPollInterval(POLL_INTERVAL);
306 }
307 callback.onProgress(out);
308 }
309 }
310 }
311 }