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_MS = 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 if (ui.getPollInterval() < POLL_INTERVAL_MS) {
169 ui.setPollInterval(POLL_INTERVAL_MS);
170 }
171 ui.addPollListener(new ScriptDonePollListener(task, ui, out, callback, messagesManager, i18n));
172 }
173
174 private Script createScript(String code, Writer out) throws CompilationFailedException, IOException {
175 return createScript(new ByteArrayInputStream(code.getBytes()), out);
176 }
177
178 private Script createScript(InputStream in, Writer out) throws CompilationFailedException, IOException {
179 Script script = parse(new InputStreamReader(in, CharEncoding.UTF_8), generateScriptName());
180 script.setProperty("out", out);
181 script.setProperty("err", out);
182 return script;
183 }
184
185
186
187
188
189
190
191
192 @Override
193 public Script parse(final GroovyCodeSource codeSource) throws CompilationFailedException {
194 return InvokerHelper.createScript(parseClass(codeSource), getContext());
195 }
196
197 @Override
198 public String generateScriptName() {
199
200 return super.generateScriptName();
201 }
202
203
204
205
206 private Class parseClass(final GroovyCodeSource codeSource) throws CompilationFailedException {
207
208 return GROOVY_CLASS_LOADER.parseClass(codeSource, false);
209 }
210
211
212
213
214 public interface ScriptCallback {
215
216
217
218
219
220 void onSuccess(String result);
221
222
223
224
225
226
227 void onFailure(Throwable e);
228
229
230
231
232
233
234 void onProgress(Writer out);
235
236
237
238
239 boolean requiresNotificationMessageUponCompletion();
240 }
241
242 private static void handleScriptDone(ScriptCallback callback, final Future<Object> scriptTask, UI ui, Writer out, MessagesManager messagesManager, SimpleTranslator i18n, boolean sendNotificationMessage) {
243 try {
244 String resultAsString = scriptTask.get() == null ? "" : Objects.toString(scriptTask.get());
245 String completeOutput;
246 if (out.toString().isEmpty()) {
247 completeOutput = resultAsString;
248 } else {
249 completeOutput = out.toString().concat("\n").concat(resultAsString);
250 }
251
252 log.debug("Script run successfully with result {}", completeOutput);
253 callback.onSuccess(completeOutput);
254 if (sendNotificationMessage) {
255 messagesManager.sendLocalMessage(new Message(MessageType.INFO, i18n.translate("groovy.script.console.done.success"), completeOutput));
256 }
257
258 } catch (InterruptedException | ExecutionException e) {
259 log.error("An error occurred while running a Groovy script", e);
260 callback.onFailure(e);
261
262 if (sendNotificationMessage) {
263 messagesManager.sendLocalMessage(new Message(MessageType.WARNING, i18n.translate("groovy.script.console.done.error"), e.getMessage()));
264 }
265 } finally {
266 log.debug("Stop polling");
267 ui.setPollInterval(-1);
268 }
269 }
270
271 @SuppressWarnings("serial")
272 private static final class ScriptDonePollListener implements PollListener {
273 private Future<Object> scriptTask;
274 private UI ui;
275 private ScriptCallback callback;
276 private Writer out;
277 private MessagesManager messagesManager;
278 private SimpleTranslator i18n;
279
280 public ScriptDonePollListener(Future<Object> scriptTask, UI ui, Writer out, ScriptCallback callback, MessagesManager messagesManager, SimpleTranslator i18n) {
281 this.scriptTask = scriptTask;
282 this.ui = ui;
283 this.callback = callback;
284 this.out = out;
285 this.messagesManager = messagesManager;
286 this.i18n = i18n;
287 }
288
289 @Override
290 public void poll(PollEvent event) {
291 if (scriptTask.isDone()) {
292 handleScriptDone(callback, scriptTask, ui, out, messagesManager, i18n, callback.requiresNotificationMessageUponCompletion());
293
294 ui.removePollListener(this);
295 } else {
296
297
298
299 if (ui.getPollInterval() < POLL_INTERVAL_MS) {
300 ui.setPollInterval(POLL_INTERVAL_MS);
301 }
302 callback.onProgress(out);
303 }
304 }
305 }
306 }