/* * Copyright 2000-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jetspeed.services.webpage; // java.io import java.io.IOException; // java.util import java.util.Collection; import java.util.Iterator; // javax.servlet import javax.servlet.ServletException; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // java.net import java.net.URL; import java.util.HashMap; import java.net.MalformedURLException; import org.apache.log4j.Logger; /** * * <p>This is the default implementation of the <code>WebPageService</code> interface.</p> * * <p> * It is a service that provides Web Page facade and delegation * services for clients to transparently access resources existing on web * pages from requests originating from the portal server.</p> * * <p>Since the WebPage service is giving the appearance of a single session to * the client, the service needs to manage the synchronization of sessions, * including single-sign-on, and security authorization permissions between the * the portal server and one or more sites.</p> * * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a> * @version $Id: JetspeedWebPageService.java,v 1.4 2004/02/23 03:46:26 jford Exp $ */ public class JetspeedWebPageService implements WebPageService { // the session keys used to store network element proxy sessions public final static String SESSION_MAP = "wps.SessionMap"; public final static String URL_SESSION_MAP = "wps.URLSessionMap"; public final static String INIT_PROPERTIES_PARAM = "properties"; /** * service state * */ // the name of the host that this server is running on private String host = null; // cache of sites cached and managed by the Proxy. // objects are of type org.apache.jetspeed.services.httpProxy.Site private HashMap sites = new HashMap(); // active sessions that the Proxy is working with // the objects are of type org.apache.jetspeed.services.httpProxy.SessionMap // this cache is updated on servlet unbound events private HashMap sessions = new HashMap(); // has this service been initialized yet private boolean init = false; // the log file singleton instance static Logger log = Logger.getLogger(JetspeedWebPageService.class); // last error private String lastError = "Jetspeed WebPage Service has not been initialized."; /** * The primary method invoked when the a Jetspeed GET is executed. * * @param servlet the Servlet. * @param request Servlet request. * @param response Servlet response. * @exception IOException a servlet exception. * @exception ServletException a servlet exception. */ public void get(HttpServlet servlet, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProxyRunData rundata = new ProxyRunData(servlet, request, response, false); dispatch(rundata); } /** * The primary method invoked when the a Jetspeed POST is executed. * * @param servlet the Servlet. * @param request Servlet request. * @param response Servlet response. * @exception IOException a servlet exception. * @exception ServletException a servlet exception. */ public void post(HttpServlet servlet, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProxyRunData rundata = new ProxyRunData(servlet, request, response, true); dispatch(rundata); } /** * The common dispatcher for both GETs and PUTs * * @param data the request specific state. * @exception IOException a servlet exception. * @exception ServletException a servlet exception. */ private void dispatch(ProxyRunData data) throws ServletException, IOException { // Turn this on for Debugging //HttpProxyDebug.snoopParams(data.getRequest(), System.err); //HttpProxyDebug.snoopHeaders(data.getRequest(), System.err); // get the proxy host for this server getHost(data.getRequest()); // // get the full Network Element IP and Resource parameters from request // Configuration config = Configuration.getInstance(); String sid = data.getRequest().getParameter(config.getSID()); String url = data.getRequest().getParameter(config.getURL()); if (null == sid) { if (null == url) { throw new ServletException("Bad Request. No proxy-parameters passed in request."); } proxyByURL(url, data); return; } // // found the proxy query parameter denoting proxy-by-network-element-id // // maps a Site unique id to a Site object Site site = getSite(sid); if (null == site) { // logon failed, return error screen here: throw new ServletException("The Requested Site ID is currently not configured on this system: " + sid); } // // check the status of the site // if it isn't online, exit out with exception // if (site.getStatus() != Configuration.STATUS_ONLINE) { throw new ServletException("The Requested Site (" + site.getURL() + ") is not available. Status = " + WebPageHelper.getAvailabilityStatus(site.getStatus()) ); } // // get the path to the requested resource // String resource = getResourcePath(site.getURL(), data.getRequest()); boolean loggedOn = true; // get the ession Map for this Portal Session // we get the session with 'false' since we don't want to create a new // session. The session should be already created HttpSession session = data.getRequest().getSession(false); if (null == session) { session = data.getRequest().getSession(true); } // // look up the session map from the current servlet session // String sessionID = session.getId(); SessionMap smap = (SessionMap)sessions.get(sessionID); SiteSession jss = null; if (null == smap) { // get the user from the session /* User user = (User)session.getAttribute(config.getUserSessionKey()); String username; if (user != null) { username = user.getUserName(); } */ // it wasn't found, create a new map String username = ""; smap = new SessionMap(sessionID, username); // add the map to the servlet session for callbacks on unbound session.setAttribute(SESSION_MAP, smap); // add the map to my collection of sessions sessions.put(sessionID, smap); // and create the network element session jss = new JetspeedSiteSession(site, this.host, username); // and then put the session into the network element map smap.put(site.getURL(), jss); // always logon when creating a new session loggedOn = jss.logon(data); } else { // found the session map, lets get the session jss = (JetspeedSiteSession)smap.get(site.getURL()); if (null == jss) { // get the user from the session /* User user = (User)session.getAttribute(config.getUserSessionKey()); String username; if (user != null) { username = user.getUserName(); } */ // no session exists, so create one String username = ""; jss = new JetspeedSiteSession(site, this.host, username); smap.put(site.getURL(), jss); // and then always logon when creating a new ne session loggedOn = jss.logon(data); } } if (loggedOn) { // debug TODO: remove this eventually if (data.getRequest().getParameter("logon-test") != null) return; if (WebPageCache.isCacheableResource(resource)) { if (WebPageCache.getResourceFromCache(resource, site.getID(), site.getURL(), this.host, data)) { smap.incCacheCount(); jss.incCacheCount(); } else { smap.incHitCount(); jss.incHitCount(); } return; } smap.incHitCount(); jss.incHitCount(); jss.proxy(resource, data); } } /** * Builds the proxy url which is used when rewriting other URLs * * * @param req Servlet request. */ private void getHost(HttpServletRequest request) { // TODO: try to get this to work! // URL.setURLStreamHandlerFactory(new sun.net.www.protocol.http.handler( ); if (null != this.host) return; StringBuffer root = new StringBuffer(); String scheme = request.getScheme(); root.append(scheme); root.append( "://"); int port = request.getServerPort(); String hostname = request.getServerName(); String ip = WebPageHelper.getIP(hostname); if (null == ip) root.append(hostname); else root.append(ip); if ( (port > 0) && ((scheme.equals("http") && port != 80) || (scheme.equals("https") && port != 443) ) ) { root.append(":"); root.append(port); } root.append( request.getServletPath() ); this.host = root.toString(); } /** * Given a Site id, maps to base URL for that site and returns the Site object * * * @param sid the string Site ID * @return the Site object. */ public Site getSite(String sid) throws ServletException { return(Site)sites.get(sid); } /** * Creates the full path the requested resource on the site from a relative path in request. * * @param url the base url for the site. * @param request the Servlet request. * @return the full path to the resource. */ public String getResourcePath(String url, HttpServletRequest request) { String path = request.getParameter(Configuration.getInstance().getPath()); if (path == null) return ""; String fullPath = WebPageHelper.concatURLs(url, path); return fullPath.replace('@', '&'); } /** * Given a URL, begin a Jetspeed session with that host * It is here for future use. * * @param url the URL of the host to proxy. * @param data the runData * @return a new session */ private SiteSession proxyByURL(String url, ProxyRunData data) throws ServletException, IOException { String newURL = url.replace('@', '&'); String base = getTargetBase(newURL); // get the Session Map for this session // we get the session with 'false' since we don't want to create a new // session. The session should be already created HttpSession session = data.getRequest().getSession(false); if (null == session) { session = data.getRequest().getSession(true); } String sessionID = session.getId(); SessionMap smap = (SessionMap)sessions.get(sessionID); SiteSession pxSession = null; if (null == smap) { // create the map smap = new SessionMap(sessionID, "NA"); // username not relevant.... session.setAttribute(URL_SESSION_MAP, smap); sessions.put(sessionID, smap); Site site = new SecuredSite(base, base); pxSession = new JetspeedSiteSession(site, base, this.host); smap.put(base, pxSession); // map(targetBaseHostName, Session) } else { pxSession = (JetspeedSiteSession)smap.get(base); if (null == pxSession) { Site site = new SecuredSite(base, base); pxSession = new JetspeedSiteSession(site, base, this.host); smap.put(base, pxSession); // map(targetBaseHostName, Session) } } if (WebPageCache.isCacheableResource(newURL)) { if (WebPageCache.getResourceFromCache(newURL, -1, base, this.host, data)) { smap.incCacheCount(); pxSession.incCacheCount(); } else { smap.incHitCount(); pxSession.incHitCount(); } return (JetspeedSiteSession)pxSession; } smap.incHitCount(); pxSession.incHitCount(); pxSession.proxy(newURL, data); return(JetspeedSiteSession)pxSession; } /** * Maps a full URL path to a resource to a base path * given: http://localhost:8080/jetspeed/search/index.html * returns: http://localhost:8080/jetspeed/ * * @param url the full URL of the resource. * @return the base host application string */ public String getTargetBase(String url) throws ServletException { try { URL u = new URL(url); StringBuffer base = new StringBuffer(); String protocol = u.getProtocol(); base.append(protocol); base.append( "://"); int port = u.getPort(); base.append(u.getHost()); if ( (port > 0) && ((protocol.equals("http") && port != 80) || (protocol.equals("https") && port != 443) ) ) { base.append(":"); base.append(port); } // we need to separate the filename from the resource, since // URL.getPath() and .getFile() return the same string String path = u.getFile(); if (null != path) { int dot = path.lastIndexOf('.'); int slash = path.lastIndexOf('/'); if (dot > slash && slash != -1) { // its a file path = path.substring(0, slash); } // base.append(path); if ('/' != base.charAt(base.length()-1)) base.append('/'); } else base.append("/"); return base.toString(); } catch (MalformedURLException e) { throw new ServletException(e.toString()); } } /** * One time initialization of the proxy service * * @param config the servlet configuration. * @exception IOException a servlet exception. * @exception ServletException a servlet exception. */ public boolean init(ServletConfig config) throws ServletException, IOException { String paramFile = config.getInitParameter(INIT_PROPERTIES_PARAM); if (null == paramFile) { lastError = "Jetspeed HTTP Proxy Init Property Not Found:" + INIT_PROPERTIES_PARAM; log.error(lastError); return false; } String fullPath = config.getServletContext().getRealPath(paramFile); Configuration pc = Configuration.getInitialInstance(fullPath); if (null == pc) { return false; } lastError = ""; init = true; return true; } /** * Returns true if the service was initialized successfully. * * @retun true if the service was initialized successfully. */ public boolean isInit() { return init; } /** * One time de-initialization of the proxy service * */ public void destroy() { try { // // first logout of all Network Element Sessions // Iterator it = sessions.values().iterator(); while (it.hasNext()) { SessionMap map = (SessionMap)it.next(); Iterator itElements = map.values().iterator(); while (itElements.hasNext()) { SiteSession has = (SiteSession)itElements.next(); try { has.logout(null); } catch (Exception e) { // continue logging out even if one fails log.error("Shutdown-Logout of Session: " + e); } } } } catch ( Exception ex ) { log.error( ex ); } } /** * Returns a snapshot collection of all the active and inactive sessions. * * @return the collection of sessions. */ public Collection getSessions() { return sessions.values(); } /** * Returns a session, give a string id key identifying that session. * * @param id The ID of the session. * @return The corresponding session. */ public SessionMap getSession(String id) { return (SessionMap)sessions.get(id); } /** * Returns a snapshot collection of all the managed sites in the system. * * @return the collection of sites. */ public Collection getSites() { return sites.values(); } /** * Returns the error string from failed initialized. * * @return the error string from last error. */ public String getErrorString() { return lastError; } }