1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.portals.bridges.groovy;
18
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.io.UnsupportedEncodingException;
23 import java.net.URLDecoder;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26
27 import javax.portlet.Portlet;
28 import javax.portlet.PortletConfig;
29 import javax.portlet.PortletException;
30 import javax.portlet.ActionRequest;
31 import javax.portlet.ActionResponse;
32 import javax.portlet.RenderRequest;
33 import javax.portlet.RenderResponse;
34 import javax.portlet.GenericPortlet;
35
36 import groovy.lang.GroovyClassLoader;
37 import groovy.lang.GroovyCodeSource;
38 import org.codehaus.groovy.control.CompilationFailedException;
39
40 /***
41 * <p>
42 * GroovyPortlet parses and invokes a groovy-scripted portlet. A groovy-scripted
43 * portlet just need to be implemented like any other Java-based portlet. So, a
44 * groovy-scripted portlet can support full features of JSR-168 portlet.
45 *
46 * @author <a href="mailto:woon_san@yahoo.com">Woonsan Ko</a>
47 * @Id@
48 */
49 public class GroovyPortlet extends GenericPortlet
50 {
51 public static final String SCRIPT_SOURCE_INIT_PARAM = "script-source";
52
53 public static final String SCRIPT_SOURCE_URL_ENCODING_INIT_PARAM = "script-source-uri-encoding";
54
55 public static final String AUTO_REFRESH_INIT_PARAM = "auto-refresh";
56
57 protected PortletConfig portletConfig;
58
59 protected String scriptSourceUri;
60
61 protected String scriptSourceUriEncoding = "UTF-8";
62
63 protected boolean autoRefresh;
64
65 protected long parsedFileLastModified;
66
67 protected GroovyCodeSource groovyCodeSource;
68
69 protected Portlet scriptPortletInstance;
70
71 protected GenericPortlet scriptGenericPortletInstance;
72
73 protected Method portletDoEditMethod;
74
75 protected GroovyClassLoader groovyClassLoader;
76
77 public GroovyPortlet()
78 {
79 super();
80 }
81
82 public void init(PortletConfig config) throws PortletException
83 {
84 this.portletConfig = config;
85 this.groovyClassLoader = new GroovyClassLoader();
86
87 this.autoRefresh = "true".equals(config.getInitParameter(AUTO_REFRESH_INIT_PARAM));
88
89 String param = config.getInitParameter(SCRIPT_SOURCE_URL_ENCODING_INIT_PARAM);
90
91 if (param != null)
92 {
93 this.scriptSourceUriEncoding = param;
94 }
95
96 this.scriptSourceUri = config.getInitParameter(SCRIPT_SOURCE_INIT_PARAM);
97
98 if (this.scriptSourceUri == null)
99 {
100 throw new PortletException("Configuration failed: " + SCRIPT_SOURCE_INIT_PARAM + " should be set properly!");
101 }
102 else
103 {
104 try
105 {
106 if (this.scriptSourceUri.startsWith("file:"))
107 {
108 String decodedScriptSourceUri = this.scriptSourceUri;
109
110 try
111 {
112 decodedScriptSourceUri = URLDecoder.decode(this.scriptSourceUri, this.scriptSourceUriEncoding);
113 }
114 catch (UnsupportedEncodingException encodingEx)
115 {
116 throw new PortletException("Unsupported encoding: " + this.scriptSourceUriEncoding);
117 }
118
119 this.groovyCodeSource = new GroovyCodeSource(new File(decodedScriptSourceUri.substring(5)));
120 }
121 else if (this.scriptSourceUri.startsWith("classpath:"))
122 {
123 String resourceURL = this.groovyClassLoader.getResource(this.scriptSourceUri.substring(10))
124 .toString();
125
126 if (resourceURL.startsWith("file:"))
127 {
128 String decodedScriptSourceUri = resourceURL;
129
130 try
131 {
132 decodedScriptSourceUri = URLDecoder.decode(resourceURL, this.scriptSourceUriEncoding);
133 }
134 catch (UnsupportedEncodingException encodingEx)
135 {
136 throw new PortletException("Unsupported encoding: " + this.scriptSourceUriEncoding);
137 }
138
139 this.groovyCodeSource = new GroovyCodeSource(new File(decodedScriptSourceUri.substring(5)));
140 }
141 else
142 {
143 throw new PortletException(SCRIPT_SOURCE_INIT_PARAM
144 + " with 'classpath:' prefix should indicate to a local resource");
145 }
146 }
147 else
148 {
149 this.groovyCodeSource = new GroovyCodeSource(new File(config.getPortletContext().getRealPath(
150 this.scriptSourceUri)));
151 }
152 }
153 catch (FileNotFoundException e)
154 {
155 throw new PortletException("File not found: " + this.scriptSourceUri, e);
156 }
157
158 this.groovyCodeSource.setCachable(!this.autoRefresh);
159 }
160
161 refreshPortletInstance();
162
163 if (this.scriptPortletInstance == null)
164 {
165 throw new PortletException("Groovy script portlet is not available!");
166 }
167 }
168
169 public void destroy()
170 {
171 if (this.scriptPortletInstance != null)
172 {
173 this.scriptPortletInstance.destroy();
174 }
175 }
176
177 public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException
178 {
179 refreshPortletInstance();
180
181 if (this.scriptPortletInstance == null)
182 {
183 throw new PortletException("Groovy script portlet is not available!");
184 }
185 else
186 {
187 this.scriptPortletInstance.processAction(request, response);
188 }
189 }
190
191 public void render(RenderRequest request, RenderResponse response) throws PortletException, IOException
192 {
193 refreshPortletInstance();
194
195 if (this.scriptPortletInstance == null)
196 {
197 throw new PortletException("Groovy script portlet is not available!");
198 }
199
200 this.scriptPortletInstance.render(request, response);
201 }
202
203 public PortletConfig getPortletConfig ()
204 {
205 return this.portletConfig;
206 }
207
208 public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException
209 {
210 if (this.scriptGenericPortletInstance != null && this.portletDoEditMethod != null)
211 {
212 try
213 {
214 this.portletDoEditMethod.invoke(this.scriptGenericPortletInstance, new Object [] { request, response });
215 }
216 catch (Exception e)
217 {
218 throw new PortletException("Failed to invoke doEdit method.", e);
219 }
220 }
221 else
222 {
223 throw new PortletException("doEdit method not implemented or not public.");
224 }
225 }
226
227 protected void refreshPortletInstance() throws PortletException
228 {
229 if (this.scriptPortletInstance == null)
230 {
231 try
232 {
233 createScriptPortletInstance();
234 }
235 catch (Exception ex)
236 {
237 throw new PortletException("Could not compile script: " + this.scriptSourceUri, ex);
238 }
239 }
240 else if (this.autoRefresh && isScriptFileModified())
241 {
242 synchronized (this.scriptPortletInstance)
243 {
244 try
245 {
246 createScriptPortletInstance();
247 }
248 catch (Exception ex)
249 {
250 throw new PortletException("Could not compile script: " + this.scriptSourceUri, ex);
251 }
252 }
253 }
254 }
255
256 protected boolean isScriptFileModified()
257 {
258 return (this.groovyCodeSource.getFile().lastModified() > this.parsedFileLastModified);
259 }
260
261 protected void createScriptPortletInstance() throws CompilationFailedException, InstantiationException,
262 IOException, IllegalAccessException, PortletException
263 {
264 Class scriptPortletClass = this.groovyClassLoader.parseClass(this.groovyCodeSource);
265 this.scriptPortletInstance = (Portlet) scriptPortletClass.newInstance();
266 this.scriptGenericPortletInstance = null;
267 this.portletDoEditMethod = null;
268
269 if (this.scriptPortletInstance instanceof GenericPortlet)
270 {
271 this.scriptGenericPortletInstance = (GenericPortlet) this.scriptPortletInstance;
272
273 try
274 {
275 Method doEditMethod = this.scriptGenericPortletInstance.getClass().getMethod("doEdit", new Class [] { RenderRequest.class, RenderResponse.class });
276
277 if (Modifier.isPublic(doEditMethod.getModifiers()))
278 {
279 this.portletDoEditMethod = doEditMethod;
280 }
281 }
282 catch (NoSuchMethodException e)
283 {
284 }
285 }
286
287 this.parsedFileLastModified = this.groovyCodeSource.getFile().lastModified();
288 this.scriptPortletInstance.init(this.portletConfig);
289 }
290 }