001: /**
002: * $Id: ResponseBufferService.java,v 1.4 2005/08/08 06:14:04 bs126381 Exp $
003: * Copyright 2003 Sun Microsystems, Inc.
004: * All rights reserved.
005: * Use of this product is subject to license terms.
006: * Federal Acquisitions: Commercial Software -- Government Users
007: * Subject to Standard License Terms and Conditions.
008: *
009: * Sun, Sun Microsystems, the Sun logo, and Sun ONE are trademarks or
010: * registered trademarks of Sun Microsystems, Inc. in the
011: * United States and other countries.
012: */package com.sun.mobile.responsebuffer;
013:
014: import java.net.URL;
015: import java.net.MalformedURLException;
016:
017: import java.util.Set;
018: import java.util.HashMap;
019: import java.util.Iterator;
020: import java.security.AccessController;
021:
022: import com.iplanet.services.cdm.Client;
023:
024: import com.iplanet.sso.SSOToken;
025: import com.iplanet.sso.SSOTokenManager;
026: import com.iplanet.sso.SSOTokenEvent;
027: import com.iplanet.sso.SSOTokenListener;
028: import com.iplanet.sso.SSOException;
029:
030: import com.iplanet.am.util.Debug;
031: import com.sun.identity.security.AdminTokenAction;
032:
033: import com.sun.identity.session.util.SessionUtils;
034: import com.sun.identity.sm.ServiceSchemaManager;
035: import com.sun.identity.sm.ServiceSchema;
036:
037: import javax.servlet.http.HttpServletRequest;
038:
039: import com.aligo.util.Cache;
040:
041: public class ResponseBufferService implements SSOTokenListener {
042: /**
043: * Constructor should not be called by anybody, it is a singleton and so
044: * use the getInstance method instead.
045: */
046: private ResponseBufferService() {
047: // Get the necessary information from DSAME
048: }
049:
050: /**
051: * This is the method everybody should be using to get access to
052: * ResponseBufferService.
053: */
054: public static ResponseBufferService getInstance() {
055: return (_instance);
056: }
057:
058: /**
059: * Gets the response buffer history depth of ResponseBufferGroup.
060: */
061: int getHistoryDepth() {
062: return (_response_buffer_history_depth);
063: }
064:
065: /**
066: * Returns the base URL used to access the contents of the
067: * ResponseBufferGroup. To access a particular entry the request
068: * url should have the following query information appended.
069: *
070: * <ENTRY_NUMBER>=<x>&...
071: */
072: public String getBaseURL() {
073: return (_base_url);
074: }
075:
076: /**
077: * Returns the base URL used to access the contents of the
078: * ResponseBufferGroup. To access a particular entry the request
079: * url should have the following query information appended.
080: *
081: * <ENTRY_NUMBER>=<x>&...
082: */
083: void setBaseURL(String base_url) {
084: _base_url = base_url;
085: }
086:
087: /**
088: * Constructs the uri for the ResponseBufferServlet from the request.
089: */
090: private String getBaseURLFromRequest(HttpServletRequest request) {
091: String url = null;
092:
093: if (request != null) {
094: StringBuffer url_buffer = new StringBuffer();
095:
096: // Append the context path
097: url_buffer.append(request.getContextPath());
098: url_buffer.append('/');
099:
100: // Append the uri for ResponseBufferServlet now
101: url_buffer.append(_servlet_relative_uri);
102:
103: url = url_buffer.toString();
104: }
105:
106: return (url);
107: }
108:
109: /**
110: * Returns the URL used to access the contents of a given response buffer
111: * entry.
112: */
113: private String getEntryURL(ResponseBufferEntry entry,
114: SSOToken token, HttpServletRequest request) {
115: if (entry == null) {
116: return (null);
117: }
118:
119: String base_url = getBaseURL();
120: if (base_url == null) {
121: base_url = getBaseURLFromRequest(request);
122: }
123:
124: String entry_number_string = Integer.toString(entry
125: .getEntryNumber().intValue(), ENTRY_NUMBER_RADIX);
126:
127: String base_url_with_entry_num = addNameValuePairAsQueryToURL(
128: base_url, ENTRY_NUMBER, entry_number_string);
129:
130: /*
131: * FIXME: We need to do something like this even when token is not available
132: * for as yet authenticating clients
133: */
134: if (token != null) {
135: try {
136: return (SessionUtils.encodeURL(token,
137: base_url_with_entry_num, false));
138: } catch (SSOException e) {
139: }
140: }
141:
142: return (base_url_with_entry_num);
143: }
144:
145: /**
146: * Returns the relative URL used to access the contents of desktop
147: */
148: public String getDesktopURL() {
149: return (_desktop_url);
150: }
151:
152: /**
153: * Returns the relative URL used to access the login screen
154: */
155: public String getLoginURL() {
156: return (_login_url);
157: }
158:
159: /**
160: * We listen to SSOTokenEvents and remove the corresponding SSOToken to
161: * ResponseBufferGroup mappings when token expires.
162: */
163: public void ssoTokenChanged(SSOTokenEvent event) {
164: try {
165: SSOToken token = event.getToken();
166:
167: switch (event.getType()) {
168: case SSOTokenEvent.SSO_TOKEN_IDLE_TIMEOUT:
169: case SSOTokenEvent.SSO_TOKEN_MAX_TIMEOUT:
170: case SSOTokenEvent.SSO_TOKEN_DESTROY:
171: invalidate(token);
172: break;
173:
174: default:
175: /* do nothing */
176: break;
177: }
178: } catch (Exception e) {
179: if (debug.warningEnabled()) {
180: debug.warning("ssoTokenChanged: Got exception "
181: + e.getMessage());
182: }
183: }
184: }
185:
186: /******************* SSO Token based methods *******************/
187:
188: /**
189: * Gets the ResponseBufferGroup for the given SSOToken
190: */
191: ResponseBufferGroup getGroup(SSOToken token) {
192: ResponseBufferGroup group = null;
193: synchronized (_response_buffer_group_table_for_tokens) {
194: group = (ResponseBufferGroup) _response_buffer_group_table_for_tokens
195: .get(getHashKeyFromSSOToken(token));
196: }
197:
198: if (group == null) {
199: if (debug.messageEnabled()) {
200: String sso_token_string = getStringRepresentationForSSOToken(token);
201: debug
202: .message(sso_token_string
203: + ": getGroup: Can't find group for the given token");
204: }
205: }
206:
207: return (group);
208: }
209:
210: /**
211: * Creates a ResponseBufferGroup for the given SSOToken
212: */
213: ResponseBufferGroup createGroupIfNecessary(SSOToken token)
214: throws SSOException {
215: boolean group_created = false;
216:
217: ResponseBufferGroup group = null;
218:
219: synchronized (_response_buffer_group_table_for_tokens) {
220: Object key = getHashKeyFromSSOToken(token);
221: group = (ResponseBufferGroup) _response_buffer_group_table_for_tokens
222: .get(key);
223:
224: if (group == null) {
225: group = new ResponseBufferGroup(getHistoryDepth());
226:
227: if (group != null) {
228: group_created = true;
229: _response_buffer_group_table_for_tokens.put(key,
230: group);
231: }
232: }
233: }
234:
235: if (group_created) {
236: token.addSSOTokenListener(this );
237: }
238:
239: // remove it from the other table
240: invalidate(getSessionIdFromSSOToken(token));
241:
242: if (group == null) {
243: String sso_token_string = getStringRepresentationForSSOToken(token);
244: debug
245: .error(sso_token_string
246: + ": createGroupIfNecessary: Can't create group for token");
247: } else if (group_created) {
248: if (debug.messageEnabled()) {
249: String sso_token_string = getStringRepresentationForSSOToken(token);
250: debug
251: .message(sso_token_string
252: + ": createGroupIfNecessary: Created a group for token");
253: }
254: }
255:
256: return (group);
257: }
258:
259: /**
260: * Invalidates the ResponseBufferGroup associated with a given SSOToken
261: */
262: public void invalidate(SSOToken token) {
263: if (token == null) {
264: return;
265: }
266:
267: ResponseBufferGroup group = null;
268: synchronized (_response_buffer_group_table_for_tokens) {
269: group = (ResponseBufferGroup) _response_buffer_group_table_for_tokens
270: .remove(getHashKeyFromSSOToken(token));
271: }
272:
273: if (group != null) {
274: group.invalidate();
275: if (debug.messageEnabled()) {
276: String sso_token_string = getStringRepresentationForSSOToken(token);
277: debug.message(sso_token_string
278: + ": invalidate: Just invalidated this token");
279: }
280: }
281: }
282:
283: /**
284: * Creates a ResponseBufferEntry object in a ResponseBufferGroup which is
285: * indexed by the given SSOToken.
286: */
287: public ResponseBufferEntry createEntry(SSOToken token,
288: String request_url, HttpServletRequest request)
289: throws SSOException {
290: if (token == null) {
291: return (null);
292: }
293:
294: ResponseBufferGroup group = createGroupIfNecessary(token);
295:
296: String sso_token_string = getStringRepresentationForSSOToken(token);
297: return (createEntryInGroup(group, token, sso_token_string,
298: request_url, request));
299: }
300:
301: /**
302: * Gets the ResponseBufferEntry object for the given response buffer entry
303: * number from the ResponseBufferGroup indexed by the given SSOToken.
304: */
305: public ResponseBufferEntry getEntry(SSOToken token,
306: Integer entry_number) {
307: ResponseBufferGroup group = getGroup(token);
308: if (group == null) {
309: if (debug.warningEnabled()) {
310: String sso_token_string = getStringRepresentationForSSOToken(token);
311: debug.warning(sso_token_string
312: + ": getEntry: Group containing the Entry "
313: + entry_number
314: + " cannot be found for the given token!");
315: }
316:
317: return (null);
318: }
319:
320: return (group.getEntry(entry_number));
321: }
322:
323: /**
324: * Returns the request url for the ResponseBufferEntry object indexed by
325: * response buffer entry number from the ResponseBufferGroup indexed by
326: * the given SSOToken.
327: */
328: public String getRequestURL(SSOToken token, Integer entry_number) {
329: ResponseBufferGroup group = getGroup(token);
330: if (group == null) {
331: return (null);
332: }
333:
334: return (group.getRequestURL(entry_number));
335: }
336:
337: /******************* Session ID based methods *******************/
338:
339: /**
340: * Gets the ResponseBufferGroup for the given SessionId
341: */
342: ResponseBufferGroup getGroup(String session_id) {
343: ResponseBufferGroup group = null;
344: synchronized (_response_buffer_group_table_for_sessions) {
345: group = (ResponseBufferGroup) _response_buffer_group_table_for_sessions
346: .get(session_id);
347: }
348:
349: if (group == null) {
350: if (debug.messageEnabled()) {
351: debug
352: .message(session_id
353: + ": getGroup: Can't find group for session id");
354: }
355: }
356:
357: return (group);
358: }
359:
360: /**
361: * Creates a ResponseBufferGroup for the given SessionId
362: */
363: ResponseBufferGroup createGroupIfNecessary(String session_id) {
364: boolean group_created = false;
365:
366: ResponseBufferGroup group = null;
367: synchronized (_response_buffer_group_table_for_sessions) {
368: group = (ResponseBufferGroup) _response_buffer_group_table_for_sessions
369: .get(session_id);
370:
371: if (group == null) {
372: group = new ResponseBufferGroup(getHistoryDepth());
373:
374: if (group != null) {
375: group_created = true;
376: _response_buffer_group_table_for_sessions.put(
377: session_id, group);
378: }
379: }
380: }
381:
382: if (group == null) {
383: debug
384: .error(session_id
385: + ": createGroupIfNecessary: Can't create group for session");
386: } else {
387: if (group_created) {
388: if (debug.messageEnabled()) {
389: debug
390: .message(session_id
391: + ": createGroupIfNecessary: Created a group for session");
392: }
393: }
394: }
395:
396: return (group);
397: }
398:
399: /**
400: * Invalidates the ResponseBufferGroup associated with a given SessionId
401: */
402: public void invalidate(String session_id) {
403: if (session_id == null) {
404: return;
405: }
406:
407: ResponseBufferGroup group = null;
408: synchronized (_response_buffer_group_table_for_sessions) {
409: group = (ResponseBufferGroup) _response_buffer_group_table_for_sessions
410: .remove(session_id);
411: }
412:
413: if (group != null) {
414: group.invalidate();
415: if (debug.messageEnabled()) {
416: debug
417: .message(session_id
418: + ": invalidate: Just invalidated this session");
419: }
420: }
421: }
422:
423: /**
424: * Creates a ResponseBufferEntry object in a ResponseBufferGroup which
425: * is indexed by the given session id.
426: *
427: * private since it is not usable by auth and in non-SSOToken based
428: * scheme (4895042). Will make it public when reaping is added.
429: */
430: private ResponseBufferEntry createEntry(String session_id,
431: String request_url, HttpServletRequest request) {
432: if (session_id == null) {
433: return (null);
434: }
435:
436: ResponseBufferGroup group = createGroupIfNecessary(session_id);
437: return (createEntryInGroup(group, null, session_id,
438: request_url, request));
439: }
440:
441: /**
442: * Gets the ResponseBufferEntry object for the given response buffer entry
443: * number from the ResponseBufferGroup indexed by the given session id.
444: */
445: public ResponseBufferEntry getEntry(String session_id,
446: Integer entry_number) {
447: ResponseBufferGroup group = getGroup(session_id);
448: if (group == null) {
449: if (debug.warningEnabled()) {
450: debug.warning(session_id
451: + ": getEntry: Group containing the Entry "
452: + entry_number
453: + " cannot be found for the given session!");
454: }
455:
456: return (null);
457: }
458:
459: return (group.getEntry(entry_number));
460: }
461:
462: /**
463: * Returns the request url for the ResponseBufferEntry object indexed by
464: * response buffer entry number from the ResponseBufferGroup indexed by
465: * the given SessionId
466: */
467: public String getRequestURL(String session_id, Integer entry_number) {
468: ResponseBufferGroup group = getGroup(session_id);
469: if (group == null) {
470: return (null);
471: }
472:
473: return (group.getRequestURL(entry_number));
474: }
475:
476: /******************* convenience methods *******************/
477:
478: /**
479: * Returns a url with name value pairs added to the query portion
480: * of the url
481: */
482: private static String addNameValuePairAsQueryToURL(String url,
483: String name, String value) {
484: if ((url == null) || (name == null) || (value == null)) {
485: return (null);
486: }
487:
488: StringBuffer url_with_name_value_pair = new StringBuffer(url);
489:
490: if (url.indexOf('?') == -1) {
491: url_with_name_value_pair.append('?');
492: } else {
493: url_with_name_value_pair.append('&');
494: }
495:
496: url_with_name_value_pair.append(name);
497: url_with_name_value_pair.append('=');
498: url_with_name_value_pair.append(value);
499:
500: return (url_with_name_value_pair.toString());
501: }
502:
503: /**
504: * Computes the login url
505: */
506: private static String computeLoginURL() {
507: String login_url = null;
508:
509: try {
510: String service = "iPlanetAMPlatformService";
511: String attribute = "iplanet-am-platform-login-url";
512: String version = "1.0";
513:
514: SSOToken admin_token = (SSOToken) AccessController
515: .doPrivileged(AdminTokenAction.getInstance());
516:
517: ServiceSchema service_schema = new ServiceSchemaManager(
518: admin_token, service, version).getGlobalSchema();
519:
520: Set attribute_values = (Set) service_schema
521: .getAttributeDefaults().get(attribute);
522: Iterator iterator = attribute_values.iterator();
523: while (iterator.hasNext()) {
524: login_url = (String) iterator.next();
525: if (login_url != null) {
526: break;
527: }
528: }
529: } catch (Exception e) {
530: // do nothing
531: }
532:
533: return (login_url);
534: }
535:
536: /*
537: * Computes the desktop url
538: */
539: private static String computeDesktopURL() {
540: /*
541: * FIXME: Wait till RFE #4832250 is fixed
542: *
543: * Till then we'll do it in a slightly roundabout way by just redirecting to
544: * login url and since the associated SSOToken is still valid the browser
545: * is redirected to the desktop url.
546: */
547: if (_login_url != null) {
548: return (_login_url);
549: }
550:
551: return (computeLoginURL());
552: }
553:
554: /*
555: * Gets the Hash Table's key from a given SSOToken
556: */
557: private static Object getHashKeyFromSSOToken(SSOToken token) {
558: /*
559: * FIXME: Ideally we should just use SSOToken as the Hash key,
560: * but the problem is it doesn't work.
561: */
562: return (getStringRepresentationForSSOToken(token));
563: }
564:
565: /*
566: * Gets the underlying HTTP Session ID from a given SSOToken
567: */
568: private static String getSessionIdFromSSOToken(SSOToken token) {
569: return (getStringRepresentationForSSOToken(token));
570: }
571:
572: /*
573: * Gets the String representation of a given SSOToken
574: */
575: private static String getStringRepresentationForSSOToken(
576: SSOToken token) {
577: try {
578: return (token.getTokenID().toString());
579: } catch (Exception e) {
580: }
581:
582: return (null);
583: }
584:
585: /**
586: * Convenience method called by createEntry methods.
587: */
588: private ResponseBufferEntry createEntryInGroup(
589: ResponseBufferGroup group, SSOToken token,
590: String token_or_session_id, String request_url,
591: HttpServletRequest request) {
592: ResponseBufferEntry entry = null;
593:
594: if (group != null) {
595: Cache buffer = new Cache(request_url);
596: if (buffer != null) {
597: entry = group.createEntry(buffer);
598: if (entry != null) {
599: buffer.addParamIdentifier(ENTRY_NUMBER);
600: buffer
601: .setBaseUrl(getEntryURL(entry, token,
602: request));
603: }
604: }
605: }
606:
607: if (entry == null) {
608: debug.error(token_or_session_id
609: + ": createEntry: Failed to create entry for "
610: + request_url);
611: } else {
612: if (debug.messageEnabled()) {
613: debug.message(token_or_session_id
614: + ": createEntry: Entry for " + request_url
615: + " has been created with number "
616: + entry.getEntryNumber());
617: }
618: }
619:
620: return (entry);
621: }
622:
623: /*
624: * Prepends <protocol>://<host>:<port>/ information to the relative url
625: * to make it absolute.
626: */
627: protected static String getAbsoluteURL(String url,
628: HttpServletRequest request, Client client)
629: throws MalformedURLException {
630:
631: if (url.regionMatches(true, 0, "http://", 0, 7)
632: || url.regionMatches(true, 0, "https://", 0, 7)) {
633: return (url);
634: }
635:
636: /*
637: * Get the request URL by using the HOST header if necessary
638: */
639:
640: StringBuffer ru = getRequestURLUsingHostHeader(request, client);
641:
642: URL requestURL = new URL(ru.toString());
643:
644: String scheme = requestURL.getProtocol();
645: int port = requestURL.getPort();
646:
647: // default port schemes need to fill in port
648: if (scheme.equals("http") && port <= 0) {
649: port = 80;
650: } else if (scheme.equals("https") && port <= 0) {
651: port = 443;
652: }
653:
654: StringBuffer absURL = new StringBuffer().append(scheme).append(
655: "://").append(requestURL.getHost()).append(":").append(
656: port).append(url);
657:
658: return absURL.toString();
659: }
660:
661: /*
662: * This method is gotten from getRequestServer method in class
663: * com.sun.portal.desktop.context.PSDesktopAppContext
664: */
665: private static StringBuffer getRequestURLUsingHostHeader(
666: HttpServletRequest request, Client client) {
667: StringBuffer url = new StringBuffer();
668:
669: boolean ignoreHostHeader = false;
670: if (client != null) {
671: String i = client.getProperty("ignoreHostHeader");
672: if (i != null) {
673: ignoreHostHeader = Boolean.getBoolean(i.toLowerCase());
674: }
675: }
676:
677: String host = null;
678: if (!ignoreHostHeader) {
679: host = request.getHeader("host");
680: }
681:
682: String scheme = request.getScheme();
683:
684: url.append(scheme) // http, https
685: .append("://");
686:
687: if (host == null) {
688: url.append(request.getServerName());
689:
690: int port = request.getServerPort();
691: if ((scheme.equals("http") && port != 80)
692: || (scheme.equals("https") && port != 443)) {
693: url.append(':').append(port);
694: }
695: } else {
696: url.append(host);
697: }
698:
699: return url;
700: }
701:
702: /**
703: * Ideally this information should be gotten from some
704: * configuration repository, say DSAME.
705: */
706: private int _response_buffer_history_depth = 20;
707:
708: /**
709: * Ideally this information should be gotten from some
710: * configuration repository, say DSAME.
711: *
712: * The necessary response buffer entry can be obtained by appending the
713: * following query string to the base url.
714: *
715: * <ENTRY_NUMBER>=<x>&...
716: */
717: private String _base_url = null;
718:
719: /**
720: * The following information needs to by dynamically computed.
721: */
722: private static String _login_url = computeLoginURL();
723:
724: /**
725: * The following information needs to by dynamically computed.
726: */
727: private String _desktop_url = computeDesktopURL();
728:
729: /**
730: * This contains the name used to add response buffer entry number information
731: * to the url
732: *
733: * i.e. http://<host>:<port>?<ENTRY_NUMBER>=<some value>&...
734: */
735: protected static final String ENTRY_NUMBER = "e";
736:
737: /**
738: * This contains the radix used to encode response buffer entry number information
739: * to the url
740: *
741: * i.e. http://<host>:<port>?<ENTRY_NUMBER>=<radix encoded value>&...
742: */
743: protected static final int ENTRY_NUMBER_RADIX = 36;
744:
745: /**
746: * Stores SSOToken -> ResponseBufferGroup mappings
747: */
748: private HashMap _response_buffer_group_table_for_tokens = new HashMap();
749:
750: /**
751: * Stores SessionId -> ResponseBufferGroup mappings
752: */
753: private HashMap _response_buffer_group_table_for_sessions = new HashMap();
754:
755: /**
756: * The relative uri of the servlet wrt to Context root
757: */
758: private static final String _servlet_relative_uri = "rb";
759:
760: /**
761: * To log debug messages
762: */
763: private static Debug debug = Debug
764: .getInstance("MAPResponseBufferService");
765:
766: /**
767: * Since this class is a singleton _instance variable holds the instance
768: */
769: private static ResponseBufferService _instance = new ResponseBufferService();
770: }
|