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.frameworks;
18  
19  import java.io.IOException;
20  import java.io.Serializable;
21  import java.lang.reflect.Method;
22  import java.util.Iterator;
23  import java.util.Map;
24  import java.util.ResourceBundle;
25  import java.util.StringTokenizer;
26  
27  import javax.portlet.ActionRequest;
28  import javax.portlet.ActionResponse;
29  import javax.portlet.PortletConfig;
30  import javax.portlet.PortletContext;
31  import javax.portlet.PortletException;
32  import javax.portlet.PortletMode;
33  import javax.portlet.PortletModeException;
34  import javax.portlet.PortletPreferences;
35  import javax.portlet.PortletRequest;
36  import javax.portlet.PortletRequestDispatcher;
37  import javax.portlet.PortletResponse;
38  import javax.portlet.PortletSession;
39  import javax.portlet.ReadOnlyException;
40  import javax.portlet.RenderRequest;
41  import javax.portlet.RenderResponse;
42  import javax.portlet.WindowState;
43  import javax.portlet.WindowStateException;
44  
45  import org.apache.commons.beanutils.BeanUtils;
46  
47  import org.apache.portals.bridges.frameworks.model.ModelBean;
48  import org.apache.portals.bridges.frameworks.model.PortletApplicationModel;
49  import org.apache.portals.bridges.frameworks.spring.PortletApplicationModelImpl;
50  import org.apache.portals.bridges.velocity.GenericVelocityPortlet;
51  
52  /***
53   * SpringVelocityPortlet
54   * 
55   * @author <a href="mailto:taylor@apache.org">David Sean Taylor </a>
56   * @version $Id: GenericFrameworkPortlet.java,v 1.1 2004/11/04 18:09:33 taylor
57   *          Exp $
58   */
59  public class GenericFrameworkPortlet extends GenericVelocityPortlet
60  {
61  
62      /***
63       * Init Parameter: default spring configuration property
64       */
65      private static final String INITPARAM_SPRING_CONFIG = "spring-configuration";
66  
67      /***
68       * Init Parameter: default velocity configuration property
69       */
70      private static final String INITPARAM_VALIDATOR_CONFIG = "validator-configuration";
71  
72      private static final String PREFS_SUFFIX = ".prefs";
73  
74      private static final String SESSION_ERROR_MESSAGES = "portals.bridges.framework.errors";
75  
76      /***
77       * Action signature for calling velocity portlet actions
78       */
79      private static final Class[] VELOCITY_PORTLET_ACTION_SIGNATURE =
80      { ActionRequest.class, ActionResponse.class, Object.class};
81  
82      private static PortletApplicationModel model = null;
83  
84      private static Object semaphore = new Object();
85  
86      public GenericFrameworkPortlet()
87      {
88      }
89  
90      public void setExternalSupport(Map map)
91      {
92          model.setExternalSupport(map);
93      }
94  
95      public void init(PortletConfig config) throws PortletException
96      {
97          super.init(config);
98          String springConfig = this.getInitParameter(INITPARAM_SPRING_CONFIG);
99          if (springConfig == null) { throw new PortletException("Spring Configuration file not specified"); }
100 
101         String validatorConfig = this.getInitParameter(INITPARAM_VALIDATOR_CONFIG);
102 
103         synchronized (semaphore)
104         {
105             if (null == model)
106             {
107                 model = new PortletApplicationModelImpl(springConfig, validatorConfig);
108                 model.init(config);
109             }
110         }
111     }
112 
113     /***
114      * Invoke the velocity portlet pipeline: (1) determine the logical view (2)
115      * restore state from Form to Bean (3) validate the bean -- or -- (2)
116      * restore state from Form to Prefs
117      * 
118      * (4) execute the velocity action (5) forward to another view
119      *  
120      */
121     public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException
122     {
123         // (1) Determine the current view
124         String view = determineLogicalView(request);
125 
126         Object bean = null;
127         ModelBean mb = model.getModelBean(view);
128 
129         if (mb.getBeanType() == ModelBean.PREFS_MAP)
130         {
131             // (2) restore state from Form to Prefs
132             bean = formToPrefs(request, view, mb);
133         }
134         else
135         {
136             // (2) restore state from Form to Bean
137             bean = formToBean(request, view, mb);
138         }
139 
140         String forward = null;
141 
142         // (3) validate the bean
143         ResourceBundle bundle = this.getPortletConfig().getResourceBundle(request.getLocale());
144         Map errors = model.validate(bean, view, bundle);
145         if (errors.isEmpty())
146         {
147             request.getPortletSession().removeAttribute(SESSION_ERROR_MESSAGES, PortletSession.PORTLET_SCOPE);
148 
149             // (4) execute the velocity action
150             String action = request.getParameter(FrameworkConstants.BRIDGES_ACTION);
151             if (null == action)
152             {
153                 if (mb.getBeanType() == ModelBean.PREFS_MAP)
154                 {
155                     // store prefs
156                     storePreferences(request, (Map) bean);
157                 }
158 
159                 forward = model.getForward(view, ForwardConstants.SUCCESS);
160             }
161             else
162             {
163                 // call the specified action in the post params
164                 String actionForward = invokeVelocityPortletAction(action, request, response, bean);
165                 forward = model.getForward(actionForward);
166             }
167         }
168         else
169         {
170             // failed validation
171             request.getPortletSession().setAttribute(SESSION_ERROR_MESSAGES, errors, PortletSession.PORTLET_SCOPE);
172             forward = model.getForward(view, ForwardConstants.FAILURE);
173         }
174 
175         clearBeanFromSession(request, mb);
176         
177         // (5) forward to another view
178         forwardToView(request, response, forward);
179 
180     }
181 
182     protected void forwardToView(ActionRequest request, ActionResponse response, String forward)
183     {
184         if (forward == null) { return; } // stay on same page
185 
186         String logicalView = null;
187         PortletMode newMode = null;
188         StringTokenizer tokenizer = new StringTokenizer(forward, ForwardConstants.DELIMITER);
189         while (tokenizer.hasMoreTokens())
190         {
191             String token = tokenizer.nextToken();
192             if (token.startsWith(ForwardConstants.MODE_PREFIX))
193             {
194                 newMode = setPortletMode(response, token.substring(ForwardConstants.MODE_PREFIX.length()));
195             }
196             else if (token.startsWith(ForwardConstants.STATE_PREFIX))
197             {
198                 setWindowState(response, token.substring(ForwardConstants.STATE_PREFIX.length()));
199             }
200             else
201             {
202                 logicalView = token;
203             }
204         }
205         if (logicalView != null)
206         {
207             setLogicalView(request, response, logicalView, newMode);
208         }
209 
210     }
211 
212     private void setWindowState(ActionResponse response, String forward)
213     {
214         try
215         {
216             if (forward.equals(ForwardConstants.MAXIMIZED))
217             {
218                 response.setWindowState(WindowState.MAXIMIZED);
219             }
220             else if (forward.equals(ForwardConstants.MINIMIZED))
221             {
222                 response.setWindowState(WindowState.MINIMIZED);
223             }
224             else if (forward.equals(ForwardConstants.NORMAL))
225             {
226                 response.setWindowState(WindowState.NORMAL);
227             }
228         }
229         catch (WindowStateException e)
230         {
231         }
232     }
233 
234     private PortletMode setPortletMode(ActionResponse response, String forward)
235     {
236         PortletMode mode = null;
237         try
238         {
239             if (forward.equals(ForwardConstants.VIEW))
240             {
241                 response.setPortletMode(PortletMode.VIEW);
242                 mode = PortletMode.VIEW;
243             }
244             else if (forward.equals(ForwardConstants.EDIT))
245             {
246                 response.setPortletMode(PortletMode.EDIT);
247                 mode = PortletMode.EDIT;
248             }
249             else if (forward.equals(ForwardConstants.HELP))
250             {
251                 response.setPortletMode(PortletMode.HELP);
252                 mode = PortletMode.HELP;
253             }
254         }
255         catch (PortletModeException e)
256         {
257         }
258         return mode;
259     }
260 
261     protected void storePreferences(PortletRequest request, Map bean) throws IOException, PortletException
262     {
263         String key = "none";
264 
265         try
266         {
267             PortletPreferences prefs = request.getPreferences();
268             Iterator it = bean.entrySet().iterator();
269             while (it.hasNext())
270             {
271                 Map.Entry entry = (Map.Entry) it.next();
272                 key = (String) entry.getKey();
273                 if (!prefs.isReadOnly(key))
274                 {
275                     prefs.setValue(key, (String) entry.getValue());
276                 }
277             }
278             prefs.store();
279         }
280         catch (ReadOnlyException roe)
281         {
282             throw new PortletException("Failed to set preference " + key + ", value is readonly");
283         }
284 
285     }
286 
287     /***
288      * Get the current logical view based on velocity.view request parameter If
289      * the request parameter is not found, fall back to init param
290      * 
291      * @param request
292      * @return the current view
293      * @throws PortletException
294      */
295     protected String determineLogicalView(PortletRequest request) throws PortletException
296     {        
297         String view = null;
298         PortletSession session = request.getPortletSession();
299         if (request.getPortletMode().equals(PortletMode.VIEW))
300         {
301             view  = request.getParameter(FrameworkConstants.VIEW_VIEW_MODE);
302             if (view == null)
303             {
304                 view = (String)session.getAttribute(FrameworkConstants.VIEW_VIEW_MODE);
305                 //view = request.getParameter(FrameworkConstants.VIEW_VIEW_MODE);
306                 if (view == null)
307                 {
308                     view = this.getDefaultViewPage();
309                 }
310             }
311         }
312         else if (request.getPortletMode().equals(PortletMode.EDIT))
313         {
314             view  = request.getParameter(FrameworkConstants.VIEW_EDIT_MODE);
315             if (view == null)
316             {            
317                 view = (String)session.getAttribute(FrameworkConstants.VIEW_EDIT_MODE);
318                 //view = request.getParameter(FrameworkConstants.VIEW_EDIT_MODE);
319                 if (view == null)
320                 {
321                     view = this.getDefaultEditPage();
322                 }
323             }
324         }
325         else if (request.getPortletMode().equals(PortletMode.HELP))
326         {
327             view  = request.getParameter(FrameworkConstants.VIEW_HELP_MODE);
328             if (view == null)
329             {            
330                 view = (String)session.getAttribute(FrameworkConstants.VIEW_HELP_MODE);
331                 //view = request.getParameter(FrameworkConstants.VIEW_HELP_MODE);
332                 if (view == null)
333                 {
334                     view = this.getDefaultHelpPage();
335                 }
336             }
337         }
338         if (null == view) 
339         { 
340             throw new PortletException(
341                 "Portlet error: cant find view resource for portlet: "
342                         + this.getPortletName()); 
343         }
344         return view;
345     }
346     
347     protected void setLogicalView(PortletRequest request, PortletResponse response, String view, PortletMode newMode)
348     {
349         PortletSession session = request.getPortletSession();
350         if (request.getPortletMode().equals(PortletMode.VIEW))
351         {
352             session.setAttribute(FrameworkConstants.VIEW_VIEW_MODE, view);                
353         }
354         else if (request.getPortletMode().equals(PortletMode.EDIT))
355         {
356             session.setAttribute(FrameworkConstants.VIEW_EDIT_MODE, view);                
357         }
358         else if (request.getPortletMode().equals(PortletMode.HELP))
359         {
360             session.setAttribute(FrameworkConstants.VIEW_HELP_MODE, view);                
361         }
362     }
363 
364     protected Object formToBean(ActionRequest request, String view, ModelBean mb) throws PortletException
365     {
366 
367         // try to get the bean from the session first
368         Object bean = getBeanFromSession(request, mb);
369         if (bean == null)
370         {
371             bean = model.createBean(mb);
372             if (bean == null) { throw new PortletException("Portlet Action error in creating bean for view: " + view); }
373             putBeanInSession(request, mb, bean);
374         }
375 
376         Map params = request.getParameterMap();
377         try
378         {
379             BeanUtils.populate(bean, params);
380         }
381         catch (Exception e)
382         {
383             throw new PortletException("Portlet Action error in  populating bean: " + mb.getBeanName(), e);
384         }
385         return bean;
386     }
387 
388     protected Object formToPrefs(ActionRequest request, String view, ModelBean mb) throws PortletException
389     {
390         Map params = request.getParameterMap();
391         Map bean = (Map) request.getPortletSession().getAttribute(view + PREFS_SUFFIX);
392         if (bean == null)
393         {
394             PortletPreferences prefs = request.getPreferences();
395 
396             bean = model.createPrefsBean(mb, prefs.getMap());
397 
398             request.getPortletSession().setAttribute(view + PREFS_SUFFIX, bean);
399         }
400 
401         try
402         {
403             Iterator it = params.entrySet().iterator();
404             while (it.hasNext())
405             {
406                 Map.Entry entry = (Map.Entry) it.next();
407                 Object value = entry.getValue();
408                 String key = (String) entry.getKey();
409                 if (null == bean.get(key))
410                 {
411                     continue;
412                 }
413                 if (value instanceof String)
414                 {
415                     bean.put(key, value);
416                 }
417                 else if (value instanceof String[])
418                 {
419                     bean.put(key, ((String[]) value)[0]);
420                 }
421             }
422         }
423         catch (Exception e)
424         {
425             throw new PortletException("Portlet Action error in  populating bean: ", e);
426         }
427         return bean;
428     }
429 
430     /***
431      * Invokes a specific Velocity Portlet Action All portlet actions must have
432      * the signature:
433      * 
434      * String methodName(ActionRequest request, ActionResponse response)
435      * 
436      * @param methodName
437      */
438     protected String invokeVelocityPortletAction(String methodName, ActionRequest request, ActionResponse response,
439             Object bean) throws PortletException
440     {
441         try
442         {
443             Method method = this.getClass().getMethod(methodName, VELOCITY_PORTLET_ACTION_SIGNATURE);
444             Object[] parameters =
445             { request, response, bean};
446             String result = (String) method.invoke(this, parameters);
447             return result;
448         }
449         catch (Exception e)
450         {
451             throw new PortletException("Failed to invoke portlet action: " + methodName, e);
452         }
453     }
454 
455     public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException
456     {
457         doRender(request, response);
458     }
459 
460     public void doHelp(RenderRequest request, RenderResponse response) throws PortletException, IOException
461     {
462         doRender(request, response);
463     }
464 
465     public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException
466     {
467         doRender(request, response);
468     }
469 
470     protected void doRender(RenderRequest request, RenderResponse response) throws PortletException, IOException
471     {
472         String view = determineLogicalView(request);
473         if (view == null) { throw new PortletException("Logical View not found: " + view); }
474 
475         String template = model.getTemplate(view);
476         if (template == null) { throw new PortletException("Template not found for Logical View: " + view); }
477 
478         ModelBean mb = model.getModelBean(view);
479         switch (mb.getBeanType())
480         {
481         case ModelBean.PREFS_MAP:
482             preferencesToContext(request, view, mb);
483             break;
484         case ModelBean.POJO:
485             beanToContext(request, view, mb);
486             break;
487         }
488         putRequestVariable(request, FrameworkConstants.FORWARD_TOOL, new Forwarder(model, request, response));
489         Map errors = (Map) request.getPortletSession().getAttribute(SESSION_ERROR_MESSAGES,
490                 PortletSession.PORTLET_SCOPE);
491         if (errors != null)
492         {
493             putRequestVariable(request, "ERRORS", errors);
494         }
495         request.setAttribute(FrameworkConstants.MODEL_TOOL, model);
496 
497         PortletContext context = getPortletContext();
498         PortletRequestDispatcher rd = context.getRequestDispatcher(template);
499         rd.include(request, response);
500     }
501 
502     private void beanToContext(RenderRequest request, String view, ModelBean mb)
503     {
504         Object bean;
505 
506         String key = null;
507         if (mb.getLookupKey() != null)
508         {
509             key = (String) request.getAttribute(mb.getLookupKey());
510         }
511         
512         if (key != null)
513         {
514             bean = model.lookupBean(mb, key);
515             if (bean != null)
516                 putBeanInSession(request, mb, bean);
517         }
518         else
519         {
520             bean = getBeanFromSession(request, mb);
521         }
522         if (bean == null)
523         {
524             bean = model.createBean(mb);
525             if (bean == null) { return; }
526             putBeanInSession(request, mb, bean);
527         }
528         putRequestVariable(request, mb.getBeanName(), bean);
529     }
530 
531     private void preferencesToContext(RenderRequest request, String view, ModelBean mb)
532     {
533         Map bean = (Map) request.getPortletSession().getAttribute(view + PREFS_SUFFIX);
534         if (bean == null)
535         {
536             PortletPreferences prefs = request.getPreferences();
537             bean = model.createPrefsBean(mb, prefs.getMap());
538             putBeanInSession(request, mb, bean);
539         }
540         putRequestVariable(request, FrameworkConstants.PREFS_VARIABLE, bean);
541     }
542 
543     private Object getBeanFromSession(PortletRequest request, ModelBean mb)
544     {
545         return request.getPortletSession().getAttribute(makeModelBeanKey(mb));
546     }
547 
548     private void clearBeanFromSession(PortletRequest request, ModelBean mb)
549     {
550         request.getPortletSession().removeAttribute(makeModelBeanKey(mb));
551     }
552     
553     public void startNewRecord(PortletRequest request, String view)
554     {
555         ModelBean mb = model.getModelBean(view);
556         if (mb != null)
557             clearBeanFromSession(request, mb);
558     }
559     
560     private void putBeanInSession(PortletRequest request, ModelBean mb, Object bean)
561     {
562         if (bean instanceof Serializable)
563         {
564             request.getPortletSession().setAttribute(makeModelBeanKey(mb), bean);
565         }
566     }
567 
568     private String makeModelBeanKey(ModelBean mb)
569     {
570         return "ModelBean:" + mb.getBeanName();
571     }
572 
573     /***
574      * Specific for Velocity
575      * 
576      * @param name
577      * @param value
578      */
579     protected void putRequestVariable(RenderRequest request, String name, Object value)
580     {
581         request.setAttribute(name, value);
582     }
583 
584 }