001: /* Copyright 2001, 2002 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal;
007:
008: import java.io.IOException;
009: import java.io.UnsupportedEncodingException;
010: import java.net.URL;
011: import java.net.URLConnection;
012: import java.util.Collections;
013: import java.util.Date;
014: import java.util.HashSet;
015: import java.util.Random;
016: import java.util.Set;
017:
018: import javax.servlet.ServletConfig;
019: import javax.servlet.ServletContext;
020: import javax.servlet.ServletException;
021: import javax.servlet.http.HttpServlet;
022: import javax.servlet.http.HttpServletRequest;
023: import javax.servlet.http.HttpServletResponse;
024: import javax.servlet.http.HttpSession;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028: import org.jasig.portal.channels.portlet.CPortletAdapter;
029: import org.jasig.portal.jndi.JNDIManager;
030: import org.jasig.portal.properties.PropertiesManager;
031: import org.jasig.portal.security.IPerson;
032: import org.jasig.portal.security.IPersonManager;
033: import org.jasig.portal.security.PersonManagerFactory;
034: import org.jasig.portal.utils.ResourceLoader;
035:
036: /**
037: * This is an entry point into the uPortal.
038: * @author Peter Kharchenko <pkharchenko@interactivebusiness.com>
039: * @version $Revision: 36816 $
040: */
041: public class PortalSessionManager extends HttpServlet {
042:
043: private static final Log log = LogFactory
044: .getLog(PortalSessionManager.class);
045:
046: /**
047: * Default value for ALLOW_REPEATED_REQUESTS.
048: * This value will be used when the corresponding property cannot be loaded.
049: */
050: private static final boolean DEFAULT_ALLOW_REPEATED_REQUESTS = false;
051:
052: /**
053: * Default value for whether to cache URLs.
054: * This value will be used when the corresponding property cannot be loaded.
055: */
056: private static final boolean DEFAULT_URL_CACHING = true;
057:
058: /**
059: * Default SAX driver name.
060: * This will be used when the System property is not set and
061: * the corresponding portal.properties property is not set.
062: */
063: private static final String DEFAULT_SAX_DRIVER = "org.apache.xerces.parsers.SAXParser";
064:
065: public static final String INTERNAL_TAG_VALUE = Long
066: .toHexString((new Random()).nextLong());
067: public static final String IDEMPOTENT_URL_TAG = "idempotent";
068:
069: private static boolean initialized = false;
070: private static ServletContext servletContext = null;
071: private static PortalSessionManager instance = null;
072: private static boolean fatalError = false;
073: private static int unauthenticatedUserSessionTimeout = 0;
074: private static final IPersonManager personManager = PersonManagerFactory
075: .getPersonManagerInstance();
076:
077: public static final ErrorID initPortalContext = new ErrorID(
078: "config", "JNDI", "Cannot initialize JNDI context");
079:
080: private static final ThreadGroup threadGroup = new ThreadGroup(
081: "uPortal");
082:
083: public static ThreadGroup getThreadGroup() {
084: return threadGroup;
085: }
086:
087: /**
088: * Provides access to the servlet instance ultimately to provide access
089: * to the servlet context of the portal.
090: * @return instance, the PortalSessionManager servlet instance
091: */
092: public static final PortalSessionManager getInstance() {
093: return instance;
094: }
095:
096: // Following flag allows to disable features that prevent
097: // repeated requests from going through. This is useful
098: // when debugging and typing things in on a command line.
099: // Otherwise, the flag should be set to false.
100: private static final boolean ALLOW_REPEATED_REQUESTS = PropertiesManager
101: .getPropertyAsBoolean(
102: "org.jasig.portal.PortalSessionManager.allow_repeated_requests",
103: DEFAULT_ALLOW_REPEATED_REQUESTS);
104:
105: // random number generator
106: private static final Random randomGenerator = new Random();
107:
108: public static Date STARTED_AT = new Date(0); // Loaded, but not initialized
109:
110: /**
111: * Initialize the PortalSessionManager servlet
112: * @throws ServletException
113: */
114: public void init() throws ServletException {
115: if (!initialized) {
116: STARTED_AT = new Date();
117:
118: instance = this ;
119: // Retrieve the servlet configuration object from the servlet container
120: // and make sure it's available
121: ServletConfig sc = getServletConfig();
122: if (sc == null) {
123: throw new ServletException(
124: "PortalSessionManager.init(): ServletConfig object was returned as null");
125: }
126:
127: // Supply PortletContainer with ServletConfig
128: CPortletAdapter.setServletConfig(sc);
129:
130: servletContext = sc.getServletContext();
131:
132: try {
133: JNDIManager.initializePortalContext();
134: } catch (Exception pe) {
135: ExceptionHelper
136: .genericTopHandler(initPortalContext, pe);
137: fatalError = true;
138: }
139:
140: // Turn off URL caching if it has been requested
141: if (!PropertiesManager
142: .getPropertyAsBoolean(
143: "org.jasig.portal.PortalSessionManager.url_caching",
144: DEFAULT_URL_CACHING)) {
145: // strangely, we have to instantiate a URLConnection to turn off caching, so we'll get something we know is there
146: try {
147: URL url = ResourceLoader.getResourceAsURL(
148: PortalSessionManager.class,
149: "/properties/portal.properties");
150: URLConnection conn = url.openConnection();
151: conn.setDefaultUseCaches(false);
152: } catch (Exception e) {
153: if (log.isWarnEnabled())
154: log
155: .warn(
156: "PortalSessionManager.init(): "
157: + "Caught Exception trying to disable URL Caching",
158: e);
159: }
160: }
161:
162: // Get the SAX implementation
163: if (System.getProperty("org.xml.sax.driver") == null) {
164: System.setProperty("org.xml.sax.driver",
165: PropertiesManager.getProperty(
166: "org.xml.sax.driver",
167: DEFAULT_SAX_DRIVER));
168: }
169:
170: // try to set the unauthenticated user's timeout, defaulting to the current server setting
171: unauthenticatedUserSessionTimeout = PropertiesManager
172: .getPropertyAsInt(PortalSessionManager.class
173: .getName()
174: + ".unauthenticatedUserSessionTimeout", 0);
175:
176: // anything less than -1 is undefined, so
177: // set it to no timeout
178: if (unauthenticatedUserSessionTimeout < -1) {
179: unauthenticatedUserSessionTimeout = -1;
180: }
181:
182: // Flag that the portal has been initialized
183: initialized = true;
184: log.info("uPortal started");
185: }
186: }
187:
188: public void destroy() {
189: // Log orderly shutdown time
190: log.info("uPortal stopped");
191: }
192:
193: /**
194: * Process HTTP POST request
195: *
196: * @param req an incoming <code>HttpServletRequest</code> value
197: * @param res an outgoing <code>HttpServletResponse</code> value
198: */
199: public void doPost(HttpServletRequest req, HttpServletResponse res) {
200: doGet(req, res);
201: }
202:
203: /**
204: * Process HTTP GET request.
205: *
206: * @param req an incoming <code>HttpServletRequest</code>
207: * @param res an outgoing <code>HttpServletResponse</code>
208: */
209: public void doGet(HttpServletRequest req, HttpServletResponse res) {
210: // Send the uPortal version in a header
211: res.setHeader("uPortal-version", Version.getProduct() + "_"
212: + Version.getReleaseTag());
213:
214: if (fatalError) {
215: try {
216: res.sendRedirect("error/fatal.htm");
217: } catch (IOException e) {
218: ExceptionHelper.genericTopHandler(Errors.bug, e);
219: }
220: return;
221: }
222:
223: // Call to setCharacterEncoding method should be done before any call to req.getParameter() method.
224: try {
225: req.setCharacterEncoding("UTF-8");
226: } catch (UnsupportedEncodingException uee) {
227: log.error("Unable to set UTF-8 character encoding!", uee);
228: }
229:
230: HttpSession session = req.getSession(false);
231:
232: if (session != null) {
233: // Update the session timeout for an unauthenticated user.
234: IPerson person = personManager.getPerson(req);
235: if (person != null
236: && !person.getSecurityContext().isAuthenticated()) {
237:
238: if (unauthenticatedUserSessionTimeout != 0) {
239: session
240: .setMaxInactiveInterval(unauthenticatedUserSessionTimeout);
241: if (log.isDebugEnabled()) {
242: log
243: .debug("UniconPortalSessionManager::doGet : Unauthenticated user session timeout set to: "
244: + unauthenticatedUserSessionTimeout);
245: }
246: }
247: }
248:
249: Set requestTags = null;
250: boolean request_verified = false;
251:
252: if (!ALLOW_REPEATED_REQUESTS) {
253: // obtain a tag table
254: synchronized (session) {
255: requestTags = (Set) session
256: .getAttribute("uP_requestTags");
257: if (requestTags == null) {
258: requestTags = Collections
259: .synchronizedSet(new HashSet());
260: session.setAttribute("uP_requestTags",
261: requestTags);
262: }
263: }
264: // determine current tag
265: UPFileSpec upfs = new UPFileSpec(req);
266:
267: String tag = upfs.getTagId();
268:
269: // see if the tag was registered
270: if (tag != null) {
271: request_verified = true;
272: requestTags.remove(tag);
273: }
274: if (log.isDebugEnabled())
275: log
276: .debug("PortalSessionManager::doGet() : request verified: "
277: + request_verified);
278: } else {
279: request_verified = true;
280: }
281:
282: try {
283: UserInstance userInstance = null;
284: try {
285: // Retrieve the user's UserInstance object
286: userInstance = UserInstanceManager
287: .getUserInstance(req);
288: } catch (Exception e) {
289: ExceptionHelper.genericTopHandler(Errors.bug, e);
290: ExceptionHelper.generateErrorPage(res, e);
291: return;
292: }
293:
294: final RequestParamWrapper wrappedRequest = new RequestParamWrapper(
295: req, request_verified);
296:
297: // fire away
298: if (ALLOW_REPEATED_REQUESTS) {
299: userInstance.writeContent(wrappedRequest, res);
300: } else {
301: // generate and register a new tag
302: String newTag = Long.toHexString(randomGenerator
303: .nextLong());
304: if (log.isDebugEnabled())
305: log
306: .debug("PortalSessionManager::doGet() : generated new tag \""
307: + newTag
308: + "\" for the session "
309: + session.getId());
310: // no need to check for duplicates :) we'd have to wait a lifetime of a universe for this time happen
311: if (!requestTags.add(newTag)) {
312: log
313: .error("PortalSessionManager::doGet() : a duplicate tag has been generated ! Time's up !");
314: }
315:
316: long startTime = System.currentTimeMillis();
317: userInstance.writeContent(wrappedRequest,
318: new ResponseSubstitutionWrapper(res,
319: INTERNAL_TAG_VALUE, newTag));
320:
321: }
322: } catch (Exception e) {
323: ExceptionHelper.genericTopHandler(Errors.bug, e);
324: ExceptionHelper.generateErrorPage(res, e);
325: return;
326: }
327:
328: } else {
329: try {
330: //throw new ServletException("Session object is null !");
331: res.sendRedirect(req.getContextPath() + "/Login");
332: } catch (Exception e) {
333: ExceptionHelper.genericTopHandler(Errors.bug, e);
334: ExceptionHelper.generateErrorPage(res, e);
335: return;
336: }
337: }
338:
339: }
340:
341: /**
342: * Gets a URL associated with the named resource.
343: * Call this to access files with paths relative to the
344: * document root. Paths should begin with a "/".
345: * @param resource relative to the document root
346: * @return a URL associated with the named resource or null if the URL isn't accessible
347: */
348: public static URL getResourceAsURL(String resource) {
349: //Make sure resource string starts with a "/"
350: if (!resource.startsWith("/"))
351: resource = "/" + resource;
352:
353: URL url = null;
354:
355: try {
356: url = servletContext.getResource(resource);
357: } catch (java.net.MalformedURLException murle) {
358: // if the URL is bad, just return null
359: }
360: return url;
361: }
362:
363: /**
364: * Gets an input stream associated with the named resource.
365: * Call this to access files with paths relative to the
366: * document root. Paths should begin with a "/".
367: * @param resource relative to the document root
368: * @return an input stream assosiated with the named resource
369: */
370: public static java.io.InputStream getResourceAsStream(
371: String resource) {
372:
373: //Make sure resource string starts with a "/"
374: if (!resource.startsWith("/")) {
375: resource = "/" + resource;
376: }
377:
378: if (servletContext != null) {
379: return servletContext.getResourceAsStream(resource);
380: } else {
381: throw new IllegalStateException(
382: "Unable to load resource '"
383: + resource
384: + "' because the servlet context has not been initialized yet");
385: }
386: }
387:
388: /**
389: * See if our servlet context is available
390: * @return boolean if so
391: */
392: public static boolean isServletContext() {
393: return servletContext != null;
394: }
395:
396: /**
397: *Accessor for the fatalError member.
398: *@return a boolean value indicating if a fatal error occured duing init
399: */
400: public boolean getFatalError() {
401: return fatalError;
402: }
403:
404: /**
405: *Accessor for the ALLOW_REPEATED_REQUESTS member.
406: *@return boolean value that indicates whether handling repeated requests is
407: * enabled
408: */
409: public boolean getAllowRepeatedRequests() {
410: return ALLOW_REPEATED_REQUESTS;
411: }
412:
413: /**
414: *Accessor for the Random number generator instance
415: *@return an instance of a random number generator instantiated
416: * by the portal session manager.
417: */
418: public Random getRandom() {
419: return randomGenerator;
420: }
421:
422: }
|