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 groovy.lang.Binding;
37 import groovy.lang.GroovySystem;
38 import groovy.lang.MissingPropertyException;
39 import info.magnolia.cms.i18n.MessagesUtil;
40 import info.magnolia.cms.security.User;
41 import info.magnolia.cms.util.NodeDataUtil;
42 import info.magnolia.context.Context;
43 import info.magnolia.context.MgnlContext;
44 import info.magnolia.module.admininterface.TemplatedMVCHandler;
45
46 import java.io.ByteArrayInputStream;
47 import java.io.FileInputStream;
48 import java.io.FileNotFoundException;
49 import java.io.InputStream;
50 import java.io.PrintWriter;
51 import java.io.Serializable;
52
53 import javax.jcr.RepositoryException;
54 import javax.servlet.http.HttpServletRequest;
55 import javax.servlet.http.HttpServletResponse;
56
57 import org.apache.commons.io.IOUtils;
58 import org.apache.commons.lang.StringUtils;
59 import org.codehaus.groovy.control.CompilationFailedException;
60 import org.codehaus.groovy.runtime.InvokerInvocationException;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64
65
66
67
68
69
70 public class MgnlGroovyInteractiveConsole extends TemplatedMVCHandler {
71
72 public MgnlGroovyInteractiveConsole(String name, HttpServletRequest request, HttpServletResponse response) {
73 super(name, request, response);
74 }
75
76 private static final Logger log = LoggerFactory.getLogger(MgnlGroovyInteractiveConsole.class);
77
78 private static final String MSG_BASENAME = "info.magnolia.module.groovy.messages";
79
80 private static final String BINDING_SESSION_ATTRIBUTE = "info.magnolia.module.groovy.console.binding.session.attribute";
81
82 private static final String GROOVY_VERSION = GroovySystem.getVersion();
83
84 public String clean() throws Exception {
85 log.debug("calling command {} and cleaning groovy context", getCommand());
86 request.getSession().setAttribute(BINDING_SESSION_ATTRIBUTE, new SerializableBinding());
87 return VIEW_SHOW;
88 }
89
90 public String evaluateGroovy() throws Exception {
91 User currentUser = MgnlContext.getUser();
92 if(!isAuthorized(currentUser)){
93 String msg = "User " + currentUser.getName() + " is trying to use the Magnolia Groovy Interactive Console but is not authorized.";
94 log.warn(msg);
95 response.getWriter().println(msg);
96 return StringUtils.EMPTY;
97 }
98 String code = request.getParameter("code");
99 log.debug("calling command {} with code {}", getCommand(), code);
100 response.setContentType("text/xml");
101
102 PrintWriter out = response.getWriter();
103
104 if (handleCommand(code, out)) {
105 return StringUtils.EMPTY;
106 }
107
108 evaluate(new ByteArrayInputStream(code.getBytes()), out);
109
110 return StringUtils.EMPTY;
111 }
112
113 public String getGroovyVersion() {
114 return GROOVY_VERSION;
115 }
116
117
118
119
120
121
122 private void evaluate(final InputStream is, final PrintWriter out) {
123 Binding binding = (Binding) request.getSession().getAttribute(BINDING_SESSION_ATTRIBUTE);
124 MgnlGroovyConsole console = new MgnlGroovyConsole(binding != null ? binding : new SerializableBinding());
125
126 Object lastResult = null;
127 Context originalCtx = MgnlContext.getInstance();
128 MgnlGroovyConsoleContext groovyCtx = new MgnlGroovyConsoleContext(originalCtx);
129 MgnlContext.setInstance(groovyCtx);
130 try {
131 lastResult = console.evaluate(is, console.generateScriptName(), out);
132
133 out.print("===> \n");
134 out.println(lastResult);
135 }
136 catch (CompilationFailedException e) {
137 out.println(e);
138 }
139 catch (Throwable e) {
140
141 if (e instanceof InvokerInvocationException) {
142 e = e.getCause();
143 }
144 if(e instanceof MissingPropertyException){
145 out.println(e.getMessage());
146 } else {
147 log.error("Error while evaluating script: ", e);
148 out.println(e.getClass().getSimpleName() + ": " +e.getMessage());
149 }
150 } finally {
151 MgnlContext.setInstance(originalCtx);
152 }
153 }
154
155
156
157
158 private static final class SerializableBinding extends Binding implements Serializable {
159
160 public static final long serialVersionUID = 42L;
161 }
162
163
164
165
166
167
168
169 private boolean handleCommand(final String command, final PrintWriter out) throws RepositoryException {
170 if (StringUtils.isEmpty(command)) {
171
172 return true;
173 }
174 String[] tokens = command.trim().split("\\s+");
175 if ("help".equals(tokens[0]) || "?".equals(tokens[0])) {
176 out.println(MessagesUtil.get("console.help", MSG_BASENAME));
177 return true;
178 }
179 else if ("clear".equals(tokens[0])) {
180 return true;
181 }
182 else if ("clean".equals(tokens[0])) {
183 request.getSession().setAttribute(BINDING_SESSION_ATTRIBUTE, new SerializableBinding());
184 return true;
185 }
186
187
188
189
190 else if ("run".equals(tokens[0])) {
191 if (tokens.length != 2) {
192 out.println(MessagesUtil.get("console.commands.run.usage", MSG_BASENAME));
193 return true;
194 }
195 InputStream is = null;
196 String path = tokens[1];
197
198 String source = NodeDataUtil.getString("scripts", path.endsWith("/") ? path + "text" : path + "/text");
199 try {
200 if (StringUtils.isNotEmpty(source)) {
201 is = new ByteArrayInputStream(source.getBytes());
202 }
203 else {
204
205 is = new FileInputStream(path);
206 }
207 evaluate(is, out);
208 return true;
209 }
210 catch (FileNotFoundException e) {
211 out.println(e.getMessage());
212 return true;
213 }
214 finally {
215 IOUtils.closeQuietly(is);
216 }
217 }
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 return false;
260 }
261
262 private boolean isAuthorized(User currentUser) {
263 return currentUser.hasRole("superuser") || currentUser.hasRole("scripter");
264 }
265 }