1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.services.forward;
18
19
20 import java.util.Map;
21 import java.util.Iterator;
22 import java.util.Collection;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.TreeMap;
26 import java.util.Map.Entry;
27 import java.io.File;
28 import java.io.FileReader;
29 import javax.servlet.ServletConfig;
30
31
32 import org.apache.turbine.util.RunData;
33 import org.apache.turbine.util.DynamicURI;
34 import org.apache.turbine.services.resources.ResourceService;
35 import org.apache.turbine.services.TurbineBaseService;
36 import org.apache.turbine.services.TurbineServices;
37 import org.apache.turbine.services.InitializationException;
38 import org.apache.turbine.services.servlet.TurbineServlet;
39
40
41 import javax.xml.parsers.DocumentBuilder;
42 import javax.xml.parsers.DocumentBuilderFactory;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Node;
45 import org.exolab.castor.mapping.Mapping;
46 import org.exolab.castor.xml.Unmarshaller;
47 import org.apache.xml.serialize.OutputFormat;
48 import org.xml.sax.InputSource;
49
50
51 import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
52 import org.apache.jetspeed.services.logging.JetspeedLogger;
53 import org.apache.jetspeed.util.template.JetspeedLink;
54 import org.apache.jetspeed.util.template.JetspeedLinkFactory;
55
56
57 import org.apache.jetspeed.services.forward.configuration.ForwardsConfiguration;
58 import org.apache.jetspeed.services.forward.configuration.Forward;
59 import org.apache.jetspeed.services.forward.configuration.Page;
60 import org.apache.jetspeed.services.forward.configuration.Pane;
61 import org.apache.jetspeed.services.forward.configuration.Portlet;
62 import org.apache.jetspeed.services.forward.configuration.PortletForward;
63 import org.apache.jetspeed.services.forward.configuration.QueryParam;
64
65
66 /***
67 * <P>This is the implementation of the Jetspeed Forward services.
68 * The interface defines methods for forwarding navigation to
69 * other pages or panes in the portal. The Forward service
70 * provides an abstraction, by removing the hard-coding of
71 * portal resources in your actions. Instead, all forward targets
72 * are defined in a centralized configuration file. By using the
73 * forward service, you use logical forward names in your java code.</P>
74 *
75 * @see org.apache.jetspeed.om.profile.Profile
76 * @author <a href="mailto:david@bluesunrise.com">David Sean Taylor</a>
77 * @version $Id: JetspeedForwardService.java,v 1.7 2004/02/23 03:51:09 jford Exp $
78 */
79
80 public class JetspeedForwardService extends TurbineBaseService
81 implements ForwardService
82 {
83 /***
84 * Static initialization of the logger for this class
85 */
86 private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedForwardService.class.getName());
87
88
89 protected final static String CONFIG_MAPPING = "mapping";
90 protected final static String CONFIG_DIRECTORY = "directory";
91
92
93 protected String mapping =
94 "/WEB-INF/conf/forwards-mapping.xml";
95
96 protected String directory =
97 "/WEB-INF/conf/forwards/";
98
99 /*** the Castor mapping file name */
100 protected Mapping mapper = null;
101
102 /*** the output format for pretty printing when saving registries */
103 protected OutputFormat format = null;
104
105
106
107 protected Map forwards = new HashMap();
108
109
110 protected Map portletForwards = new TreeMap();
111
112
113 protected final static String KEY_DELIMITER = ":";
114
115 /***
116 * This is the early initialization method called by the
117 * Turbine <code>Service</code> framework
118 * @param conf The <code>ServletConfig</code>
119 * @exception throws a <code>InitializationException</code> if the service
120 * fails to initialize
121 */
122 public synchronized void init(ServletConfig conf) throws InitializationException
123 {
124
125
126 if (getInit()) return;
127
128 try
129 {
130
131 ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
132 .getResources(ForwardService.SERVICE_NAME);
133
134 this.mapping = serviceConf.getString(CONFIG_MAPPING, this.mapping);
135
136 this.directory = serviceConf.getString(CONFIG_DIRECTORY, this.directory);
137
138 this.mapping = TurbineServlet.getRealPath(this.mapping);
139 this.directory = TurbineServlet.getRealPath(this.directory);
140
141 loadForwards();
142
143 }
144 catch (Exception e)
145 {
146 logger.error("ForwardService: Failed to load ", e);
147 }
148
149
150 setInit(true);
151
152 }
153
154
155
156 /***
157 * This is the shutdown method called by the
158 * Turbine <code>Service</code> framework
159 */
160 public void shutdown()
161 {
162 }
163
164 /***
165 * Forward to a specific forward by name.
166 * All parameters are resolved statically (via the forward definition)
167 *
168 * @param rundata The turbine rundata context for this request.
169 * @param forwardName Forward to this abstract forward name.
170 * @return DynamicURI the full link to the referenced page
171 */
172 public DynamicURI forward(RunData rundata, String forwardName)
173 {
174 return forwardInternal(rundata, forwardName, null, null);
175 }
176
177 /***
178 * Forward to a specific forward by name.
179 * Parameters are resolved both statically and dynamically, with the
180 * dynamic parameter overriding the static parameter definitions.
181 *
182 * @param rundata The turbine rundata context for this request.
183 * @param forwardName Forward to this abstract forward name.
184 * @param parameters The dynamic Validation Parameters used in creating validation forwards
185 * @return DynamicURI the full link to the referenced page
186 */
187 public DynamicURI forwardDynamic(RunData rundata, String forwardName, Map parameters)
188 {
189 return forwardInternal(rundata, forwardName, null, parameters);
190 }
191
192 /***
193 * Internal implementation of Forward used by both forwards and portlet forwards.
194 *
195 * @param rundata The turbine rundata context for this request.
196 * @param name Forward to this abstract forward name.
197 * @param staticParams Map of static query parameters from PortletForward
198 * overriding the static Forwards query parameters
199 * @param dynamicParams Map of dynamic query parameters overriding both
200 * static PortletForward parameters and static Forwards query parameters
201 * @return DynamicURI the full link to the referenced page
202 */
203 private DynamicURI forwardInternal(RunData rundata,
204 String forwardName,
205 Map staticParams,
206 Map dynamicParams)
207 {
208 DynamicURI duri = null;
209 Forward forward = null;
210
211 try
212 {
213 JetspeedLink link = JetspeedLinkFactory.getInstance(rundata);
214 int rootType = JetspeedLink.DEFAULT;
215 int elementType = JetspeedLink.DEFAULT;
216 String rootValue = null;
217 String pageName = null;
218 String elementValue = null;
219 String actionName = null;
220 String templateName = null;
221 String mediaType = null;
222 String language = null;
223 String country = null;
224
225
226 forward = (Forward)this.forwards.get(forwardName);
227 if (null != forward)
228 {
229 Pane pane = forward.getPane();
230 if (null != pane)
231 {
232 elementValue = pane.getId();
233 elementType = JetspeedLink.PANE_ID;
234 if (elementValue == null)
235 {
236 elementValue = pane.getName();
237 elementType = JetspeedLink.PANE_NAME;
238 }
239 }
240 else
241 {
242 Portlet portlet = forward.getPortlet();
243 if (null != portlet)
244 {
245 elementValue = portlet.getId();
246 elementType = JetspeedLink.PORTLET_ID;
247 if (elementValue == null)
248 {
249 elementValue = portlet.getName();
250 elementType = JetspeedLink.PORTLET_NAME;
251 }
252 actionName = portlet.getAction();
253 }
254 }
255
256 Page page = forward.getPage();
257 if (null != page)
258 {
259 pageName = page.getName();
260
261 String user = page.getUser();
262 if (user != null)
263 {
264 rootType = JetspeedLink.USER;
265 rootValue = user;
266 }
267 else
268 {
269 String role = page.getRole();
270 if (role != null)
271 {
272 rootType = JetspeedLink.ROLE;
273 rootValue = role;
274 }
275 else
276 {
277 String group = page.getGroup();
278 if (group != null)
279 {
280 rootType = JetspeedLink.GROUP;
281 rootValue = group;
282 }
283 else
284 {
285 rootType = JetspeedLink.CURRENT;
286 }
287 }
288 }
289 }
290
291 duri = link.getLink(rootType,
292 rootValue,
293 pageName,
294 elementType,
295 elementValue,
296 actionName,
297 templateName,
298 mediaType,
299 language,
300 country);
301
302 }
303 else
304 {
305
306
307 logger.error("Forward not found, going to Home Page:" + forwardName);
308 duri = link.getHomePage();
309
310 }
311
312 if (null == duri)
313 {
314 duri = link.getPage();
315 }
316
317 Map baseQueryParams = null;
318 if (null != forward)
319 {
320 baseQueryParams = forward.getQueryParams();
321 }
322 setQueryParams(duri, baseQueryParams, staticParams, dynamicParams);
323
324 rundata.setRedirectURI(duri.toString());
325 JetspeedLinkFactory.putInstance(link);
326 }
327 catch (Throwable t)
328 {
329 logger.error("Exception in Forward",t);
330 }
331 return duri;
332 }
333
334 /***
335 * Adds query parameters to the final URI.
336 * Parameters are merged from the base forwards definition, with the
337 * overlay parameters being overlaid over th base parameters
338 *
339 * @param duri The dynamic URI to have query parameters added to it
340 * @param baseQueryParams The base query parameters from the forward definition
341 * @param staticParams Map of static query parameters from PortletForward
342 * overriding the static Forwards query parameters
343 * @param dynamicParams Map of dynamic query parameters overriding both
344 * static PortletForward parameters and static Forwards query parameters
345 * @return DynamicURI The new URI including query parameters
346 */
347 private DynamicURI setQueryParams(DynamicURI duri,
348 Map baseQueryParams,
349 Map staticParams,
350 Map dynamicParams)
351 {
352 if (baseQueryParams == null && staticParams == null && dynamicParams == null)
353 {
354 return duri;
355 }
356
357 Iterator it = null;
358
359
360 if (baseQueryParams != null)
361 {
362 it = baseQueryParams.values().iterator();
363 while (it.hasNext())
364 {
365 QueryParam qparam = (QueryParam)it.next();
366 if ( (null == staticParams || !staticParams.containsKey(qparam.getName()))
367 && (null == dynamicParams || !dynamicParams.containsKey(qparam.getName())))
368 {
369 duri.addQueryData(qparam.getName(), qparam.getValue());
370 }
371 }
372 }
373
374
375 if (staticParams != null)
376 {
377 it = staticParams.values().iterator();
378 while (it.hasNext())
379 {
380 QueryParam qparam = (QueryParam)it.next();
381 if (null == dynamicParams || !dynamicParams.containsKey(qparam.getName()))
382 {
383 duri.addQueryData(qparam.getName(), qparam.getValue());
384 }
385 }
386 }
387
388
389 if (dynamicParams != null)
390 {
391 it = dynamicParams.entrySet().iterator();
392 while (it.hasNext())
393 {
394 Entry entry = (Entry)it.next();
395 duri.addQueryData((String)entry.getKey(), entry.getValue());
396 }
397 }
398
399 return duri;
400 }
401
402
403 private void dumpMap(String mapName, Map map)
404 {
405 System.out.println("----------- MAP: " + mapName);
406 Iterator it = map.values().iterator();
407 while (it.hasNext())
408 {
409 QueryParam qparam = (QueryParam)it.next();
410 System.out.println("name = " + qparam.getName() + ", value = " + qparam.getValue());
411 }
412 }
413
414 /***
415 * For the given portlet and given action, forward to the target
416 * defined in the forward configuration for the portlet + action.
417 * All parameters are resolved statically (via the forward definition)
418 *
419 * @param portlet The name of the portlet for which we are forwarding.
420 * @param target A logical target name. Portlets can have 1 or more targets.
421 * @return DynamicURI the full link to the referenced page
422 */
423 public DynamicURI forward(RunData rundata, String portlet, String target)
424 {
425 return forwardDynamic(rundata, portlet, target, null);
426 }
427
428 /***
429 * For the given portlet and given action, forward to the target
430 * defined in the forward configuration for the portlet + action.
431 * Parameters are resolved both statically and dynamically, with the
432 * dynamic parameter overriding the static parameter definitions.
433 *
434 * @param portlet The name of the portlet for which we are forwarding.
435 * @param target A logical target name. Portlets can have 1 or more targets.
436 * @param parameters The dynamic Validation Parameters used in creating validation forwards
437 * @return DynamicURI the full link to the referenced page
438 */
439 public DynamicURI forwardDynamic(RunData rundata,
440 String portlet,
441 String target,
442 Map parameters)
443 {
444 try
445 {
446 Map staticParams = null;
447 String forwardName = "";
448 String key = makePortletForwardKey(portlet, target);
449 PortletForward pf = (PortletForward)this.portletForwards.get(key);
450 if (null != pf)
451 {
452 staticParams = pf.getQueryParams();
453 Forward forward = (Forward)this.forwards.get(pf.getForward());
454 if (null != forward)
455 {
456 forwardName = forward.getName();
457 }
458 }
459 return forwardInternal(rundata, forwardName, staticParams, parameters);
460 }
461 catch (Throwable t)
462 {
463 t.printStackTrace();
464 }
465 return new DynamicURI();
466
467 }
468
469 /***
470 * Get a collection of all forwards in the system.
471 *
472 * @return Collection of all forward definitions
473 */
474 public Collection getForwards()
475 {
476 return this.forwards.values();
477 }
478
479 /***
480 * Get a collection of all portlet forwards in the system.
481 *
482 * @return Collection of all portlet forward definitions
483 */
484 public Collection getPortletForwards()
485 {
486 return this.portletForwards.values();
487 }
488
489 /***
490 * Lookup a single forward definition by forward name
491 *
492 * @param forwardName The name of the Forward to find
493 * @return Forward The found forward definition or null if not found
494 */
495 public Forward getForward(String forwardName)
496 {
497 return (Forward)this.forwards.get(forwardName);
498 }
499
500 /***
501 * Lookup a single portlet forward definition by portlet name + target name
502 *
503 * @param portlet The name of the portlet in the Portlet Forward to find
504 * @param target The name of the target in the Portlet Forward to find
505 * @return Forward The found portlet forward definition or null if not found
506 */
507 public PortletForward getPortletForward(String portlet, String target)
508 {
509 return (PortletForward)this.portletForwards.get(makePortletForwardKey(portlet, target));
510 }
511
512 /***
513 * Load all forward configuration files from forwards directory.
514 *
515 *
516 */
517 protected void loadForwards()
518 throws InitializationException
519 {
520
521 this.format = new OutputFormat();
522 this.format.setIndenting(true);
523 this.format.setIndent(4);
524
525 File map = new File(this.mapping);
526 if (map.exists() && map.isFile() && map.canRead())
527 {
528 try
529 {
530 this.mapper = new Mapping();
531 InputSource is = new InputSource(new FileReader(map));
532 is.setSystemId(this.mapping);
533 this.mapper.loadMapping(is);
534 }
535 catch (Exception e)
536 {
537 String msg = "ForwardService: Error in castor mapping creation";
538 logger.error(msg, e);
539 throw new InitializationException(msg, e);
540 }
541 }
542 else
543 {
544 String msg = "ForwardService: Mapping not found or not a file or unreadable: " + this.mapping;
545 logger.error(msg);
546 throw new InitializationException(msg);
547 }
548
549
550 try
551 {
552
553 File directory = new File(this.directory);
554 File[] files = directory.listFiles();
555 for (int ix=0; ix < files.length; ix++)
556 {
557 if (files[ix].isDirectory())
558 {
559 continue;
560 }
561
562 loadForwardConfiguration(files[ix]);
563 }
564
565 }
566 catch (Exception e)
567 {
568 String msg = "ForwardService: Fatal error loading Forward configurations";
569 logger.error(msg, e);
570 throw new InitializationException(msg, e);
571 }
572
573 }
574
575 protected String makePortletForwardKey(String portlet, String target)
576 {
577 StringBuffer key = new StringBuffer(portlet);
578 key.append(KEY_DELIMITER);
579 key.append(target);
580 return key.toString();
581 }
582
583 /***
584 * Load and unmarshal a Forward Configuration from a file.
585 *
586 * @param file the absolute file path storing this fragment
587 */
588 protected void loadForwardConfiguration(File file)
589 {
590 try
591 {
592 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
593 DocumentBuilder builder = dbfactory.newDocumentBuilder();
594
595 Document doc = builder.parse(file);
596
597 Unmarshaller unmarshaller = new Unmarshaller(this.mapper);
598 ForwardsConfiguration configuration =
599 (ForwardsConfiguration) unmarshaller.unmarshal((Node) doc);
600
601 Iterator it = configuration.getForwards().iterator();
602 while (it.hasNext())
603 {
604 Forward forward = (Forward)it.next();
605 if (this.forwards.containsKey(forward.getName()))
606 {
607 logger.error("ForwardService: already contains Forward key: " + forward.getName());
608 }
609 else
610 {
611 this.forwards.put(forward.getName(), forward);
612 }
613
614 resyncParamMap(forward.getQueryParams());
615
616 }
617
618 it = configuration.getPortletForwards().iterator();
619 while (it.hasNext())
620 {
621 PortletForward pf = (PortletForward)it.next();
622 String key = makePortletForwardKey(pf.getPortlet(), pf.getTarget());
623 if (this.portletForwards.containsKey(key))
624 {
625 logger.error("ForwardService: already contains portletForward key: " + key);
626 }
627 else
628 {
629 this.portletForwards.put(key, pf);
630 resyncParamMap(pf.getQueryParams());
631 }
632 }
633
634
635 }
636 catch (Throwable t)
637 {
638 logger.error("ForwardService: Could not unmarshal: " + file, t);
639 }
640
641 }
642
643 private void resyncParamMap(Map map)
644 {
645
646
647 ArrayList list = new ArrayList(map.size());
648 Iterator it = map.values().iterator();
649 while (it.hasNext())
650 {
651 QueryParam qp = (QueryParam)it.next();
652 list.add(qp);
653 }
654 map.clear();
655 it = list.iterator();
656 while (it.hasNext())
657 {
658 QueryParam qp = (QueryParam)it.next();
659 map.put(qp.getName(), qp);
660 }
661
662 }
663 }
664