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.webpage;
18  
19  // java.io
20  import java.io.IOException;
21  
22  // java.util
23  import java.util.Collection;
24  import java.util.Iterator;
25  
26  // javax.servlet
27  import javax.servlet.ServletException;
28  import javax.servlet.ServletConfig;
29  import javax.servlet.http.HttpServlet;
30  import javax.servlet.http.HttpSession;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  // java.net
35  import java.net.URL;
36  import java.util.HashMap;
37  import java.net.MalformedURLException;
38  
39  import org.apache.log4j.Logger;
40  
41  /***
42   *
43   * <p>This is the default implementation of the <code>WebPageService</code> interface.</p>
44   *
45   * <p>
46   * It is a service that provides Web Page facade and delegation
47   * services for clients to transparently access resources existing on web
48   * pages from requests originating from the portal server.</p>
49   *
50   * <p>Since the WebPage service is giving the appearance of a single session to 
51   * the client, the service needs to manage the synchronization of sessions,
52   * including single-sign-on, and security authorization permissions between the
53   * the portal server and one or more sites.</p> 
54   *
55   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
56   * @version $Id: JetspeedWebPageService.java,v 1.4 2004/02/23 03:46:26 jford Exp $ 
57   */
58  
59  public class JetspeedWebPageService
60      implements WebPageService
61  {
62      // the session keys used to store network element proxy sessions
63      public final static String SESSION_MAP = "wps.SessionMap";
64      public final static String URL_SESSION_MAP = "wps.URLSessionMap";
65  
66      public final static String INIT_PROPERTIES_PARAM = "properties";
67  
68      /***
69       * service state
70       *
71       */
72  
73      // the name of the host that this server is running on
74      private String host = null;       
75  
76      // cache of sites cached and managed by the Proxy.
77      // objects are of type org.apache.jetspeed.services.httpProxy.Site
78      private HashMap sites = new HashMap();
79  
80      // active sessions that the Proxy is working with
81      // the objects are of type org.apache.jetspeed.services.httpProxy.SessionMap
82      // this cache is updated on servlet unbound events
83      private HashMap sessions = new HashMap();
84  
85      // has this service been initialized yet
86      private boolean init = false;
87  
88      // the log file singleton instance
89      static Logger log = Logger.getLogger(JetspeedWebPageService.class);
90  
91      // last error
92      private String lastError = "Jetspeed WebPage Service has not been initialized.";
93  
94      /***
95       * The primary method invoked when the a Jetspeed GET is executed.
96       *
97       * @param servlet the Servlet.     
98       * @param request Servlet request.
99       * @param response Servlet response.
100      * @exception IOException a servlet exception.
101      * @exception ServletException a servlet exception.
102      */
103     public void get(HttpServlet servlet,
104                     HttpServletRequest request, 
105                     HttpServletResponse response)                         
106     throws ServletException, IOException
107     {
108         ProxyRunData rundata = new ProxyRunData(servlet, request, response, false);
109         dispatch(rundata);
110     }
111 
112     /***
113      * The primary method invoked when the a Jetspeed POST is executed.
114      *
115      * @param servlet the Servlet.          
116      * @param request Servlet request.
117      * @param response Servlet response.
118      * @exception IOException a servlet exception.
119      * @exception ServletException a servlet exception.
120      */    
121     public void post(HttpServlet servlet,
122                      HttpServletRequest request, 
123                      HttpServletResponse response)
124     throws ServletException, IOException
125     {        
126         ProxyRunData rundata = new ProxyRunData(servlet, request, response, true);
127         dispatch(rundata);
128     }
129 
130 
131     /***
132      * The common dispatcher for both GETs and PUTs
133      *
134      * @param data the request specific state.
135      * @exception IOException a servlet exception.
136      * @exception ServletException a servlet exception.
137      */    
138     private void dispatch(ProxyRunData data)
139     throws ServletException, IOException
140     {        
141         // Turn this on for Debugging
142         //HttpProxyDebug.snoopParams(data.getRequest(), System.err);
143         //HttpProxyDebug.snoopHeaders(data.getRequest(), System.err);        
144 
145         // get the proxy host for this server
146         getHost(data.getRequest());
147 
148         //
149         // get the full Network Element IP and Resource parameters from request
150         //
151         Configuration config = Configuration.getInstance();
152         String sid = data.getRequest().getParameter(config.getSID());
153         String url = data.getRequest().getParameter(config.getURL());
154 
155         if (null == sid)
156         {
157             if (null == url)
158             {
159                 throw new ServletException("Bad Request. No proxy-parameters passed in request.");
160             }
161             proxyByURL(url, data);
162             return;
163         }
164 
165         // 
166         // found the proxy query parameter denoting proxy-by-network-element-id
167         //
168 
169         // maps a Site unique id to a Site object
170         Site site = getSite(sid);
171         if (null == site)
172         {
173             // logon failed, return error screen here:
174             throw new ServletException("The Requested Site ID is currently not configured on this system: " + sid);
175         }
176 
177         // 
178         // check the status of the site
179         // if it isn't online, exit out with exception
180         //        
181         if (site.getStatus() != Configuration.STATUS_ONLINE)
182         {
183             throw new ServletException("The Requested Site (" 
184                                        + site.getURL()
185                                        + ") is not available. Status = "
186                                        + WebPageHelper.getAvailabilityStatus(site.getStatus()) );            
187         }
188 
189         // 
190         // get the path to the requested resource
191         //
192         String resource = getResourcePath(site.getURL(), data.getRequest());
193 
194         boolean loggedOn = true;
195 
196 
197         // get the ession Map for this Portal Session
198         // we get the session with 'false' since we don't want to create a new
199         // session. The session should be already created 
200         HttpSession session = data.getRequest().getSession(false);
201         if (null == session)
202         {
203             session = data.getRequest().getSession(true);
204         }
205 
206         // 
207         // look up the session map from the current servlet session
208         //
209         String sessionID = session.getId();
210         SessionMap smap = (SessionMap)sessions.get(sessionID);
211         SiteSession jss = null;
212         if (null == smap)
213         {
214             // get the user from the session
215             /*
216             User user = (User)session.getAttribute(config.getUserSessionKey());
217             String username;
218             if (user != null)
219             {
220                 username = user.getUserName();
221             }
222             */
223             // it wasn't found, create a new map 
224             String username = "";
225             smap = new SessionMap(sessionID, username);
226 
227 
228             // add the map to the servlet session for callbacks on unbound
229             session.setAttribute(SESSION_MAP, smap);
230             // add the map to my collection of sessions
231             sessions.put(sessionID, smap);
232 
233             // and create the network element session
234             jss = new JetspeedSiteSession(site, this.host, username);
235 
236             // and then put the session into the network element map
237             smap.put(site.getURL(), jss);
238 
239             // always logon when creating a new session
240             loggedOn = jss.logon(data);
241 
242         } else
243         {
244             // found the session map, lets get the session
245             jss = (JetspeedSiteSession)smap.get(site.getURL());
246             if (null == jss)
247             {
248                 // get the user from the session
249                 /*
250                 User user = (User)session.getAttribute(config.getUserSessionKey());
251                 String username;
252                 if (user != null) 
253                 {
254                     username = user.getUserName();
255                 }
256                 */
257 
258                 // no session exists, so create one
259                 String username = "";
260                 jss = new JetspeedSiteSession(site, this.host, username);
261                 smap.put(site.getURL(), jss);        
262 
263                 // and then always logon when creating a new ne session 
264                 loggedOn = jss.logon(data);
265             }
266         }        
267         if (loggedOn)
268         {
269             // debug TODO: remove this eventually
270             if (data.getRequest().getParameter("logon-test") != null)
271                 return;
272 
273             if (WebPageCache.isCacheableResource(resource))
274             {                
275                 if (WebPageCache.getResourceFromCache(resource, site.getID(), site.getURL(), this.host, data))
276                 {
277                     smap.incCacheCount();
278                     jss.incCacheCount();
279                 }
280                 else
281                 {
282                     smap.incHitCount();
283                     jss.incHitCount();
284                 }
285                 return;
286             }
287 
288             smap.incHitCount();
289             jss.incHitCount();
290             jss.proxy(resource, data);        
291         }
292     }
293 
294     /***
295      * Builds the proxy url which is used when rewriting other URLs
296      * 
297      *
298      * @param req Servlet request.
299      */
300     private void getHost(HttpServletRequest request)
301     {
302         // TODO: try to get this to work!        
303         // URL.setURLStreamHandlerFactory(new sun.net.www.protocol.http.handler( );
304 
305         if (null != this.host)
306             return;
307 
308         StringBuffer root = new StringBuffer();
309         String scheme = request.getScheme();
310         root.append(scheme);
311         root.append( "://");
312         int port = request.getServerPort();
313 
314         String hostname = request.getServerName();
315         String ip = WebPageHelper.getIP(hostname);
316         if (null == ip)
317             root.append(hostname);
318         else
319             root.append(ip);
320 
321         if ( (port > 0) &&
322              ((scheme.equals("http") && port != 80) ||
323               (scheme.equals("https") && port != 443)
324              )
325            )
326         {
327             root.append(":");
328             root.append(port);
329         }
330         root.append( request.getServletPath() );
331         this.host = root.toString();
332     }
333 
334     /***
335      * Given a Site id, maps to base URL for that site and returns the Site object
336      * 
337      *
338      * @param sid the string Site ID
339      * @return the Site object.
340      */
341     public Site getSite(String sid) throws ServletException
342     {
343         return(Site)sites.get(sid);
344     }
345 
346     /***
347      * Creates the full path the requested resource on the site from a relative path in request.
348      *
349      * @param url the base url for the site.
350      * @param request the Servlet request.
351      * @return the full path to the resource.
352      */
353     public String getResourcePath(String url, HttpServletRequest request)
354     {
355         String path = request.getParameter(Configuration.getInstance().getPath());           
356         if (path == null)
357             return "";
358 
359         String fullPath = WebPageHelper.concatURLs(url, path);
360 
361         return fullPath.replace('@', '&');
362     }
363 
364 
365     /***
366      *  Given a URL, begin a Jetspeed session with that host
367      *  It is here for future use.
368      *
369      * @param url the URL of the host to proxy.
370      * @param data the runData
371      * @return a new session
372      */
373     private SiteSession proxyByURL(String url,
374                                       ProxyRunData data)
375     throws ServletException, IOException
376     {
377         String newURL = url.replace('@', '&');
378         String base = getTargetBase(newURL);
379 
380         // get the Session Map for this session
381         // we get the session with 'false' since we don't want to create a new
382         // session. The session should be already created 
383         HttpSession session = data.getRequest().getSession(false);
384         if (null == session)
385         {
386             session = data.getRequest().getSession(true);
387         }
388 
389         String sessionID = session.getId();
390         SessionMap smap = (SessionMap)sessions.get(sessionID);
391 
392         SiteSession pxSession = null;
393         if (null == smap)
394         {
395             // create the map 
396             smap = new SessionMap(sessionID, "NA"); // username not relevant....
397 
398             session.setAttribute(URL_SESSION_MAP, smap);
399             sessions.put(sessionID, smap);
400 
401             Site site = new SecuredSite(base, base);
402             pxSession = new JetspeedSiteSession(site, base, this.host);
403             smap.put(base, pxSession);  // map(targetBaseHostName, Session)
404         } 
405         else
406         {
407             pxSession = (JetspeedSiteSession)smap.get(base);
408             if (null == pxSession)
409             {
410                 Site site = new SecuredSite(base, base);
411                 pxSession = new JetspeedSiteSession(site, base, this.host);
412                 smap.put(base, pxSession);  // map(targetBaseHostName, Session)
413             }
414         }
415 
416         if (WebPageCache.isCacheableResource(newURL))
417         {
418 
419             if (WebPageCache.getResourceFromCache(newURL, -1, base, this.host, data))
420             {
421                 smap.incCacheCount();
422                 pxSession.incCacheCount();
423             }
424             else
425             {
426                 smap.incHitCount();
427                 pxSession.incHitCount();
428             }
429             return (JetspeedSiteSession)pxSession;
430         }
431 
432         smap.incHitCount();
433         pxSession.incHitCount();
434         pxSession.proxy(newURL, data);
435         return(JetspeedSiteSession)pxSession;       
436     }
437 
438     /***
439      * Maps a full URL path to a resource to a base path
440      *   given: http://localhost:8080/jetspeed/search/index.html
441      *   returns: http://localhost:8080/jetspeed/
442      *
443      * @param url the full URL of the resource.
444      * @return the base host application string
445      */
446     public String getTargetBase(String url) throws ServletException
447     {
448         try
449         {
450             URL u = new URL(url);
451             StringBuffer base = new StringBuffer();
452             String protocol = u.getProtocol();
453             base.append(protocol);
454             base.append( "://");
455             int port = u.getPort();
456             base.append(u.getHost());  
457             if ( (port > 0) &&
458                  ((protocol.equals("http") && port != 80) ||
459                   (protocol.equals("https") && port != 443)
460                  )
461                )
462             {
463                 base.append(":");
464                 base.append(port);
465             }
466 
467             // we need to separate the filename from the resource, since
468             // URL.getPath() and .getFile() return the same string
469             String path = u.getFile();
470 
471             if (null != path)
472             {
473 
474                 int dot = path.lastIndexOf('.');
475                 int slash = path.lastIndexOf('/');
476                 if (dot > slash && slash != -1)
477                 { // its a file
478                     path = path.substring(0, slash);
479                 }
480                 // 
481 
482                 base.append(path);
483 
484                 if ('/' != base.charAt(base.length()-1))
485                     base.append('/');
486             } else
487                 base.append("/");
488 
489             return base.toString();
490         } catch (MalformedURLException e)
491         {
492             throw new ServletException(e.toString());
493         }
494     }
495 
496 
497     /***
498      * One time initialization of the proxy service
499      *
500      * @param config the servlet configuration.     
501      * @exception IOException a servlet exception.
502      * @exception ServletException a servlet exception.
503      */
504     public boolean init(ServletConfig config)
505     throws ServletException, IOException
506     {
507 
508         String paramFile = config.getInitParameter(INIT_PROPERTIES_PARAM);
509         if (null == paramFile)
510         {
511             lastError = "Jetspeed HTTP Proxy Init Property Not Found:" + INIT_PROPERTIES_PARAM;
512             log.error(lastError);                                
513             return false;
514         }
515 
516         String fullPath = config.getServletContext().getRealPath(paramFile);
517 
518         Configuration pc = Configuration.getInitialInstance(fullPath);  
519         if (null == pc)
520         {
521             return false;
522         }
523 
524         lastError = "";
525         init = true;
526         return true;
527     }
528 
529     /***
530      * Returns true if the service was initialized successfully.
531      *
532      * @retun true if the service was initialized successfully.
533      */
534     public boolean isInit()
535     {
536         return init;
537     }
538 
539     /***
540      * One time de-initialization of the proxy service
541      *
542      */
543     public void destroy()
544     {
545         try
546         {
547             //
548             // first logout of all Network Element Sessions
549             //
550             Iterator it = sessions.values().iterator();
551             while (it.hasNext())
552             {
553                 SessionMap map = (SessionMap)it.next();
554                 Iterator itElements = map.values().iterator();
555                 while (itElements.hasNext())
556                 {
557                     SiteSession has = (SiteSession)itElements.next();
558                     try 
559                     {
560                         has.logout(null);
561                     }
562                     catch (Exception e)
563                     {
564                         // continue logging out even if one fails
565                         log.error("Shutdown-Logout of Session: " + e);                                
566                     }
567                 }
568             }
569 
570         } catch ( Exception ex )
571         {
572             log.error( ex );
573         }
574     }
575 
576     /***
577      * Returns a snapshot collection of all the active and inactive sessions.
578      *
579      * @return the collection of sessions.
580      */
581     public Collection getSessions()
582     {
583         return sessions.values();
584     }
585 
586     /***
587      * Returns a session, give a string id key identifying that session.
588      *
589      * @param id The ID of the session.
590      * @return The corresponding session.
591      */
592     public SessionMap getSession(String id)
593     {
594         return (SessionMap)sessions.get(id);
595     }
596 
597     /***
598      * Returns a snapshot collection of all the managed sites in the system.
599      *
600      * @return the collection of sites.
601      */
602     public Collection getSites()
603     {
604         return sites.values();
605     }
606 
607 
608     /***
609      * Returns the error string from failed initialized.
610      *
611      * @return the error string from last error.
612      */
613     public String getErrorString()
614     {
615         return lastError;
616     }
617 
618 
619 }
620 
621 
622 
623 
624