1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
131 bean = formToPrefs(request, view, mb);
132 }
133 else
134 {
135
136 bean = formToBean(request, view, mb);
137 }
138
139 String forward = null;
140
141
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
149 String action = request.getParameter(FrameworkConstants.BRIDGES_ACTION);
150 if (null == action)
151 {
152 if (mb.getBeanType() == ModelBean.PREFS_MAP)
153 {
154
155 storePreferences(request, (Map) bean);
156 }
157
158 forward = model.getForward(view, ForwardConstants.SUCCESS);
159 }
160 else
161 {
162
163 String actionForward = invokeVelocityPortletAction(action, request, response, bean);
164 forward = model.getForward(actionForward);
165 }
166 }
167 else
168 {
169
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
177 forwardToView(request, response, forward);
178
179 }
180
181 protected void forwardToView(ActionRequest request, ActionResponse response, String forward)
182 {
183 if (forward == null) { return; }
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
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
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
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
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 }