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