View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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 }