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 }