Sun Java Solaris Communities My SDN Account Join SDN
 
Article

SessionServlet Code

 
 

Articles Index

  1. /**
  2. * Copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.
  3. *
  4. * Permission to use, copy, modify, and distribute this software
  5. * and its documentation for NON-COMMERCIAL purposes and without
  6. * fee is hereby granted provided that this copyright notice
  7. * appears in all copies. Please refer to the file "copyright.html"
  8. * for further important copyright and licensing information.
  9. *
  10. * The Java source code is the confidential and proprietary information
  11. * of Sun Microsystems, Inc. ("Confidential Information"). You shall
  12. * not disclose such Confidential Information and shall use it only in
  13. * accordance with the terms of the license agreement you entered into
  14. * with Sun.
  15. *
  16. * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  17. * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  18. * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  19. * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  20. * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  21. * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  22. */
  23. import java.io.*;
  24. import javax.servlet.*;
  25. import javax.servlet.http.*;
  26. import sun.server.http.*;
  27. import sun.misc.*;
  28. import sun.servlet.http.Cookie;
  29. import java.util.*;
  30. /**
  31. *
  32. * On startup, the servlet looks for the following
  33.  parameters:
  34. * flush - how much time to wait before flushing  sessions.
  35. * cookieTimeout - how much time a session  "lives".
  36. *domain - domain used for the cookie.
  37. *home - name of the Home html document you  want users to be directed too after an  initial log in.
  38. *loginFirst - full path/name of the initial  Login page served to the client when first accessing the site.
  39. *loginInvalid - full path/name of the login  page served if the userid/password is invalid.
  40. *authenticator - a Authenicator implementation.
  41. *
  42. * The login must do a POST from a forum/applet  with the following values:
  43. *UserId=<users id>
  44. * Password=<users password>
  45. * action="login"
  46. *
  47. *If a session is valid, the either FileServlet or  SSIncludeServlet is responsible for serving the
  48. * file to the user.
  49. *
  50. * Users are authorized via a abstract method  "isAuthorized" in the  Authenciator interface.
  51. *
  52. * @author Tony Squier
  53. * @version 1.8 97/02/10 13:29:25
  54. */
  55. public class SessionServlet extends HttpServlet  implements Runnable
  56. {
  57. /**
  58. * Servlets context
  59. */
  60. protected ServletContext context;
  61. /**
  62. * Helper servlets: File and SSInlcude
  63. */
  64. protected SSIncludeServlet ssIncludeServlet;
  65. protected FileServlet fileServlet;
  66. /**
  67. * Authenticator
  68. */
  69. private Authenticator auth;
  70. /**
  71. * Restricted domain
  72. */
  73. protected String domain;
  74. /**
  75. * Timeout for sessions
  76. */
  77. protected long sessionTimeout;
  78. protected Thread tid;
  79. protected Hashtable sessionCache;
  80. protected long flush;
  81. /**
  82. * Login Template Name
  83. */
  84. protected String homeHtml;
  85. protected String loginFirstHtml;
  86. protected String loginInvalidHtml;
  87. /**
  88. * Initialize the Servlet and set the html pages.
  89. * @param stub Arguments passed by the Servlet loader
  90. * @return void
  91. */
  92. public void init (ServletConfig config) throws  ServletException
  93. {
  94. super.init(config);
  95. context = config.getServletContext();
  96. ssIncludeServlet =  (SSIncludeServlet)context.getServlet (  "ssinclude");
  97. fileServlet = (FileServlet)context.getServlet (  "file");
  98. try
  99. {
  100. auth =  (Authenticator)Class.forName(  config.getInitParameter(  "auth")).newInstance();
  101. }
  102. catch (Exception e)
  103. {
  104. e.printStackTrace();
  105. }
  106. homeHtml = config.getInitParameter ("home");
  107. loginFirstHtml = config.getInitParameter (  "loginFirst");
  108. loginInvalidHtml =  config.getInitParameter ("loginInvalid");
  109. domain = config.getInitParameter ("domain");
  110. flush = Long.parseLong (  config.getInitParameter("flush"));
  111. sessionTimeout =  Long.parseLong (  config.getInitParameter("cookieTimeout"));
  112. sessionCache = new Hashtable();
  113. tid = new Thread(this);
  114. tid.setPriority (Thread.MIN_PRIORITY);
  115. tid.start();
  116. }
  117. /**
  118. * Main service routine for the Servlet. This  method is responsible for checking
  119. * the request and determining the appropriate  action: login or serve pages.
  120. *
  121. * Subclasses shouldn't override this method,  but rather override startSession,
  122. * endSession, and serviceRequest. If this  method is overridden, you must
  123. * invoke it from the extended method or  Session tracking may/may not occur
  124. * correctly
  125. * @param request The Servlet request.
  126. * @param response The Servlet response.
  127. * @return void.
  128. */
  129. public void service (HttpServletRequest request,  HttpServletResponse response) throws  ServletException, IOException
  130. {
  131. // Get the session associated with the request. If
  132. // none exists, session is set to null.
  133. Session session =   _validateSession (request);
  134. // Since we may be chained, we will
  135. // just send the cookie along its way for
  136. // other servlets to consume
  137. if ( session != null )
  138. {
  139. response.setHeader ("Cookie",  "SessionServlet="+session.getId());
  140. }
  141. // Determine what we are being asked to do  by looking
  142. // at the action field first.
  143. // If action is set, then check it, otherwise, simply
  144. // service the request via the serviceRequest method.
  145. String action;
  146. if ( (action=request.getParameter (  "action")) != null )
  147. {
  148. _performAction (action, session, request, response);
  149. }
  150. // No action value, so service the request by
  151. // calling serviceRequest method.
  152. else
  153. {
  154. if ( session != null )
  155. {
  156. serviceRequest (session, request, response);
  157. }
  158. else
  159. {
  160. response.sendRedirect (  "/servlet/SessionServlet?action=showLogin");
  161. }
  162. }
  163. }
  164. /**
  165. * Method called by the service routine if  a valid Session
  166. * exists and a request can be served.
  167. * @param session The session.
  168. * @param request The Original HttpServletRequest.
  169. * @param response The Original HttpServletResponse.
  170. * @throws IOException
  171. * @throws ServletException
  172. * @return void
  173. */
  174. protected void serviceRequest (  Session session, HttpServletRequest  request, HttpServletResponse response)  throws IOException, ServletException
  175. {
  176. /**
  177. * Set the Session's URI path so we know where  this session is going.
  178. * We only track .html and .shtml files
  179. */
  180. if ( context.getMimeType (  request.getRequestURI()).equals  ("text/html") ||
  181. context.getMimeType (request.getRequestURI()).equals ("java-internal/parsed-html") ||
  182. request.getRequestURI().endsWith ("/") )
  183. {
  184. session.setURI (request.getRequestURI());
  185. }
  186. response.setHeader ("Expires",  String.valueOf (session.getExpires()));
  187. if ( request.getRequestURI().lastIndexOf (  ".") == -1 && request.getRequestURI().endsWith (  "/") == false )
  188. {
  189. ((sun.server.http.HttpServiceRequest)request).setPathInfo  (request.getRequestURI()+"/");
  190. }
  191. else
  192. {
  193. ((sun.server.http.HttpServiceRequest)request).setPathInfo  (request.getRequestURI());
  194. }
  195. if ( context.getMimeType (  request.getRequestURI()).equals ("java-internal/parsed-html") )
  196. {
  197. ssIncludeServlet.service (request, response);
  198. }
  199. else
  200. {
  201. fileServlet.service (request, response);
  202. }
  203. }
  204. /**
  205. * Starts a new Session. A new Session is  started by first
  206. * creating a new Session instance, putting  it into the SessionCache
  207. * and setting the Cookie with the SessionId
  208. * @param userId The user Id.
  209. * @param password The Password.
  210. * @param response The HttpServletResponse  used for setting the Cookie
  211. * @throws IOException
  212. * @throws ServletException
  213. * @return Session Null if authentication failed
  214. */
  215. protected Session startSession (String userId,  String password, HttpServletResponse response)
  216. {
  217. Session session = null;
  218. if ( auth.isAuthorized (userId, password) )
  219. {
  220. // Create a session
  221. session = new Session (userId);
  222. session.setExpires (  sessionTimeout+System.currentTimeMillis());
  223. sessionCache.put (session.key(), session);
  224. // Create a client cookie
  225. Cookie c = new Cookie("SessionServlet", String.valueOf (session.getId()));
  226. c.setPath ("/");
  227. c.setMaxAge (-1);
  228. c.setDomain (domain);
  229. response.setHeader ("Set-Cookie", "SessionServlet=" +
  230. c.getValue() +
  231. "; Domain=" +
  232. c.getDomain() +
  233. "; Path=" + c.getPath());
  234. }
  235. return session;
  236. }
  237. /**
  238. * Method invoked to terminate a Session
  239. * @param session The Session to terminate
  240. * @return void
  241. */
  242. protected void endSession (Session session)
  243. {
  244. synchronized (sessionCache)
  245. {
  246. sessionCache.remove (session.key());
  247. }
  248. }
  249. /**
  250. * Default page servicing routine. A InputStream  contains the page that is
  251. * written to the Servlet's output stream.  Neither the in or out Streams
  252. * are closed when finished!
  253. * @param in Contents are read from.
  254. * @param out Contents are copied into
  255. * @return void
  256. * @throws IOException
  257. */
  258. protected void copyInToOut (BufferedInputStream in, BufferedOutputStream out) throws IOException
  259. {
  260. int length;
  261. byte data[] = new byte[8192];
  262. while ( (length=in.read(data)) != -1 )
  263. {
  264. out.write (data, 0, length);
  265. }
  266. }
  267. /**
  268. * Retrives and Validates Session
  269. * @param request HttpServletRequest
  270. * @return Session Session or null
  271. */
  272. private  Session _validateSession  (HttpServletRequest request)
  273. {
  274. Cookie c[] = Cookie.getCookies (request);
  275. Session session;
  276. if ( c != null && c.length == 1 )
  277. {
  278. String key = String.valueOf (c[0].getValue());
  279. session = (Session)sessionCache.get (key);
  280. }
  281. else
  282. {
  283. session = null;
  284. }
  285. return session;
  286. }
  287. /**
  288. * Convience method to handle posts which set  the action hidden field
  289. * This is the main routine the does most  of the work. See comments
  290. * in service method.
  291. * @param action The value of action
  292. * @param session The session.
  293. * @param request The HttpServletRequest.
  294. * @param response The HttpServletResponse.
  295. * @return void
  296. * @throws IOException
  297. * @throws ServletException
  298. */
  299. private void _performAction (  String action, Session session,  HttpServletRequest request, HttpServletResponse  response) throws IOException, ServletException
  300. {
  301. response.setHeader ("Pragma",  "no-cache");
  302. // Invoke startSession method.
  303. if ( action.equals ("login") )
  304. {
  305. // Start a new Session and redirect to  client's original destination if
  306. // true is returned.
  307. Session s;
  308. if ( (s=startSession (request.getParameter ("UserId"), request.getParameter (  "Password"), response))!= null )
  309. {
  310. response.sendRedirect (homeHtml);
  311. }
  312. // Authenticator failed, so return the client  the showInvalid page.
  313. else
  314. {
  315. response.setContentType ("text/html");
  316. response.sendRedirect (  "/servlet/SessionServlet?action=  showInvalid");
  317. }
  318. }
  319. // Remove session, if one exists, from session cache.
  320. // Send the client back to the loginFirstHtml page.
  321. else if ( action.equals ("logout") )
  322. {
  323. if ( session != null )
  324. {
  325. endSession (session);
  326. }
  327. response.sendRedirect (  "/servlet/SessionServlet?action=showLogin");
  328. }
  329. // If show request, determine which page
  330. // and show it. We only allow this if the
  331. // user isn't logged into the SessionServlet.
  332. else if ( action.startsWith ("show") )
  333. {
  334. if ( session == null )
  335. {
  336. String page;
  337. if ( action.equals ("showInvalid") )
  338. {
  339. page = loginInvalidHtml;
  340. }
  341. else
  342. {
  343. page = loginFirstHtml;
  344. }
  345. // If we are not logged in, i.e. no Session, then
  346. // show the login page. Otherwise, if a valid  Session exists,
  347. // we don't want to un-necessarly show the  login page, so
  348. // re-send back to the homeHtml page. This way  we don't
  349. // get multiple Sessions for a single client because
  350. // they continually logged in multiple times.
  351. //
  352. // This is the only time we explictly serve  up a file, so
  353. // we write to the output stream for the  response and
  354. // close it when we are done.
  355. BufferedInputStream inputStream =  new BufferedInputStream (new FileInputStream (page));
  356. response.setContentType ("text/html");
  357. copyInToOut (inputStream,  new BufferedOutputStream  (response.getOutputStream()));
  358. response.getOutputStream().flush();
  359. response.getOutputStream().close();
  360. inputStream.close();
  361. }
  362. else
  363. {
  364. response.sendRedirect (homeHtml);
  365. }
  366. }
  367. // Show the currently cached sessions
  368. else if ( action.equals ("cache-peek") )
  369. {
  370. PrintStream print =  new PrintStream (response.getOutputStream());
  371. Thread.currentThread().setPriority (  Thread.MIN_PRIORITY);
  372. Enumeration sessions;
  373. synchronized (sessionCache)
  374. {
  375. sessions = sessionCache.elements();
  376. }
  377. while ( sessions.hasMoreElements() )
  378. {
  379. Session s = (Session)sessions.nextElement();
  380. print.println (s.getId()+":"+s.getURI());
  381. Thread.yield();
  382. }
  383. print.flush();
  384. sessions = null;
  385. }
  386. // Otherwise, no valid action value is set, so
  387. // send them to the login page.
  388. else
  389. {
  390. response.sendRedirect (  "/servlet/SessionServlet?action=showLogin");
  391. }
  392. }
  393. /**
  394. * Expires sessions
  395. * @return void
  396. */
  397. public void run()
  398. {
  399. while ( true )
  400. {
  401. try
  402. {
  403. Thread.sleep (flush);
  404. Enumeration sessions;
  405. Session s;
  406. long expire;
  407. expire = System.currentTimeMillis();
  408. synchronized (sessionCache)
  409. {
  410. sessions = sessionCache.elements();
  411. }
  412. while ( sessions.hasMoreElements() )
  413. {
  414. s = (Session)sessions.nextElement();
  415. if ( expire >= s.getExpires() )
  416. {
  417. synchronized (sessionCache)
  418. {
  419. sessionCache.remove (s.key());
  420. }
  421. }
  422. }
  423. }
  424. catch (Exception e)
  425. {
  426. return;
  427. }
  428. }
  429. }
  430. }