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  
17  package org.apache.jetspeed.services.forward;
18  
19  // java
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  // turbine, services
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  // marshaling
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  // jetspeed
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  // forwarding configuration
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      // configuration keys
89      protected final static String CONFIG_MAPPING = "mapping";
90      protected final static String CONFIG_DIRECTORY = "directory";
91  
92      // configuration parameters
93      protected String mapping =                 // the forwards XML-Java mapping 
94                 "/WEB-INF/conf/forwards-mapping.xml";
95  
96      protected String directory =                // the location of forwards definitions
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     // Forward definitions
107     protected Map forwards = new HashMap();
108     
109     // Portlet Forward definitions
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         // already initialized
126         if (getInit()) return;
127 
128         try
129         {
130             // get configuration parameters from Jetspeed Resources
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         // initialization done
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 // can't have both portlet and pane
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, // not yet implemented
298                              mediaType,    // not yet implemented 
299                              language,     // not yet implemented
300                              country);     // not yet implemented 
301 
302             }
303             else
304             {
305                 // forward not found, log it and return to home page
306                 // TODO: perhaps this could be configurable to go to a default error page
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         // First add the base params
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         // Then add the static params
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         // Then add the dynamic params
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         // create the serializer output format
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         // Castor doesn't set the keys properly for maps
646         // get the base query params        
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