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  
25  import javax.portlet.Portlet;
26  import javax.portlet.PortletConfig;
27  import javax.portlet.PortletException;
28  import javax.portlet.ActionRequest;
29  import javax.portlet.ActionResponse;
30  import javax.portlet.RenderRequest;
31  import javax.portlet.RenderResponse;
32  
33  import groovy.lang.GroovyClassLoader;
34  import groovy.lang.GroovyCodeSource;
35  import org.codehaus.groovy.control.CompilationFailedException;
36  
37  /***
38   * <p>
39   * GroovyPortlet parses and invokes a groovy-scripted portlet. A groovy-scripted
40   * portlet just need to be implemented like any other Java-based portlet. So, a
41   * groovy-scripted portlet can support full features of JSR-168 portlet.
42   * 
43   * @author <a href="mailto:woon_san@yahoo.com">Woonsan Ko</a>
44   * @Id@
45   */
46  public class GroovyPortlet implements Portlet
47  {
48      public static final String SCRIPT_SOURCE_INIT_PARAM = "script-source";
49  
50      public static final String SCRIPT_SOURCE_URL_ENCODING_INIT_PARAM = "script-source-uri-encoding";
51  
52      public static final String AUTO_REFRESH_INIT_PARAM = "auto-refresh";
53  
54      protected PortletConfig portletConfig;
55  
56      protected String scriptSourceUri;
57  
58      protected String scriptSourceUriEncoding = "UTF-8";
59  
60      protected boolean autoRefresh;
61  
62      protected long parsedFileLastModified;
63  
64      protected GroovyCodeSource groovyCodeSource;
65  
66      protected Portlet scriptPortletInstance;
67  
68      protected GroovyClassLoader groovyClassLoader;
69  
70      public GroovyPortlet()
71      {
72      }
73  
74      public void init(PortletConfig config) throws PortletException
75      {
76          this.portletConfig = config;
77          this.groovyClassLoader = new GroovyClassLoader();
78  
79          this.autoRefresh = "true".equals(config.getInitParameter(AUTO_REFRESH_INIT_PARAM));
80  
81          String param = config.getInitParameter(SCRIPT_SOURCE_URL_ENCODING_INIT_PARAM);
82  
83          if (param != null)
84          {
85              this.scriptSourceUriEncoding = param;
86          }
87  
88          this.scriptSourceUri = config.getInitParameter(SCRIPT_SOURCE_INIT_PARAM);
89  
90          if (this.scriptSourceUri == null)
91          {
92              throw new PortletException("Configuration failed: " + SCRIPT_SOURCE_INIT_PARAM + " should be set properly!");
93          }
94          else
95          {
96              try
97              {
98                  if (this.scriptSourceUri.startsWith("file:"))
99                  {
100                     String decodedScriptSourceUri = this.scriptSourceUri;
101 
102                     try
103                     {
104                         decodedScriptSourceUri = URLDecoder.decode(this.scriptSourceUri, this.scriptSourceUriEncoding);
105                     }
106                     catch (UnsupportedEncodingException encodingEx)
107                     {
108                         throw new PortletException("Unsupported encoding: " + this.scriptSourceUriEncoding);
109                     }
110 
111                     this.groovyCodeSource = new GroovyCodeSource(new File(decodedScriptSourceUri.substring(5)));
112                 }
113                 else if (this.scriptSourceUri.startsWith("classpath:"))
114                 {
115                     String resourceURL = this.groovyClassLoader.getResource(this.scriptSourceUri.substring(10))
116                             .toString();
117 
118                     if (resourceURL.startsWith("file:"))
119                     {
120                         String decodedScriptSourceUri = resourceURL;
121 
122                         try
123                         {
124                             decodedScriptSourceUri = URLDecoder.decode(resourceURL, this.scriptSourceUriEncoding);
125                         }
126                         catch (UnsupportedEncodingException encodingEx)
127                         {
128                             throw new PortletException("Unsupported encoding: " + this.scriptSourceUriEncoding);
129                         }
130 
131                         this.groovyCodeSource = new GroovyCodeSource(new File(decodedScriptSourceUri.substring(5)));
132                     }
133                     else
134                     {
135                         throw new PortletException(SCRIPT_SOURCE_INIT_PARAM
136                                 + " with 'classpath:' prefix should indicate to a local resource");
137                     }
138                 }
139                 else
140                 {
141                     this.groovyCodeSource = new GroovyCodeSource(new File(config.getPortletContext().getRealPath(
142                             this.scriptSourceUri)));
143                 }
144             }
145             catch (FileNotFoundException e)
146             {
147                 throw new PortletException("File not found: " + this.scriptSourceUri, e);
148             }
149 
150             this.groovyCodeSource.setCachable(!this.autoRefresh);
151         }
152 
153         refreshPortletInstance();
154 
155         if (this.scriptPortletInstance == null)
156         {
157             throw new PortletException("Groovy script portlet is not available!");
158         }
159     }
160 
161     public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException
162     {
163         refreshPortletInstance();
164 
165         if (this.scriptPortletInstance == null)
166         {
167             throw new PortletException("Groovy script portlet is not available!");
168         }
169         else
170         {
171             this.scriptPortletInstance.processAction(request, response);
172         }
173     }
174 
175     public void render(RenderRequest request, RenderResponse response) throws PortletException, IOException
176     {
177         refreshPortletInstance();
178 
179         if (this.scriptPortletInstance == null)
180         {
181             throw new PortletException("Groovy script portlet is not available!");
182         }
183         else
184         {
185             this.scriptPortletInstance.render(request, response);
186         }
187     }
188 
189     public void destroy()
190     {
191         if (this.scriptPortletInstance != null)
192         {
193             this.scriptPortletInstance.destroy();
194         }
195     }
196 
197     protected void refreshPortletInstance() throws PortletException
198     {
199         if (this.scriptPortletInstance == null)
200         {
201             try
202             {
203                 createScriptPortletInstance();
204             }
205             catch (Exception ex)
206             {
207                 throw new PortletException("Could not compile script: " + this.scriptSourceUri, ex);
208             }
209         }
210         else if (this.autoRefresh && isScriptFileModified())
211         {
212             synchronized (this.scriptPortletInstance)
213             {
214                 try
215                 {
216                     createScriptPortletInstance();
217                 }
218                 catch (Exception ex)
219                 {
220                     throw new PortletException("Could not compile script: " + this.scriptSourceUri, ex);
221                 }
222             }
223         }
224     }
225 
226     protected boolean isScriptFileModified()
227     {
228         return (this.groovyCodeSource.getFile().lastModified() > this.parsedFileLastModified);
229     }
230 
231     protected void createScriptPortletInstance() throws CompilationFailedException, InstantiationException,
232             IOException, IllegalAccessException, PortletException
233     {
234         Class scriptPortletClass = this.groovyClassLoader.parseClass(this.groovyCodeSource);
235         this.scriptPortletInstance = (Portlet) scriptPortletClass.newInstance();
236         this.parsedFileLastModified = this.groovyCodeSource.getFile().lastModified();
237         this.scriptPortletInstance.init(this.portletConfig);
238     }
239 }