View Javadoc

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