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