001: /**
002: * $Id: CCPPClientDetector.java,v 1.3 2004/03/30 02:59:27 sathyakn Exp $
003: * Allrights reserved. Use of this product is subject to license terms.
004: * Federal Acquisitions: Commercial Software -- Government Users Subject
005: * to Standard License Terms and Conditions.
006: *
007: * Sun, Sun Microsystems, the Sun logo, and Sun ONE are trademarks or
008: * registered trademarks of Sun Microsystems,Inc. in the United States
009: * and other countries.
010: */
011:
012: /**
013: * <p>The <CODE>CCPPClientDetector</CODE> class implements the client detection
014: * algorithm for supporting CC/PP in the wireless portal server.
015: */package com.sun.mobile.cdm;
016:
017: import java.util.Map;
018: import java.util.Set;
019: import java.util.HashMap;
020: import java.util.Hashtable;
021: import java.util.Enumeration;
022: import java.util.StringTokenizer;
023:
024: import javax.servlet.http.HttpServletRequest;
025:
026: import com.iplanet.services.cdm.Client;
027: import com.iplanet.services.cdm.ClientsManager;
028: import com.iplanet.services.cdm.ClientDetectionException;
029: import com.iplanet.am.util.Debug;
030:
031: import com.sun.mobile.util.MAPConfigProperties;
032:
033: public class CCPPClientDetector extends MAPClientDetector {
034: private String this ClassName = null; // for debug
035:
036: //
037: // The HTTP headers containing the profile URI
038: //
039: public static final String[] CPI_PROFILE_HEADERS = {
040: "x-wap-profile", // specified in UAProf
041: "Profile" // Nokia devices use this
042: };
043:
044: public static final String PROPS_HEADERS = "ps.uaprof.http.headers";
045:
046: public static final String REQ_PROF_ATTR = "com.sun.profile.uri";
047:
048: protected static Debug debug = Debug
049: .getInstance("CCPPClientDetector");
050:
051: //
052: // To store the cached profiles: key = profile URI
053: //
054: private static Hashtable profileURITable = new Hashtable();
055:
056: //
057: // Map to store the information regarding the profile being merged
058: //
059: private static Map statusMap = new HashMap();
060:
061: //
062: // Get the HTTP header names to look for WAP2.0 profiles
063: //
064: private static String[] headerNames = null;
065: static {
066: String value = MAPConfigProperties.get(PROPS_HEADERS, null);
067: if (value == null) {
068: headerNames = CPI_PROFILE_HEADERS; // default
069: } else {
070: //
071: // <space> separated names
072: //
073: StringTokenizer st = new StringTokenizer(value);
074: headerNames = new String[st.countTokens()];
075: for (int i = 0; st.hasMoreTokens(); i++) {
076: headerNames[i] = st.nextToken();
077: }
078: }
079: }
080:
081: //
082: // set when the getMergedProfile() throws a ClientDetectionException.
083: // We wont try merging profiles anymore.
084: //
085: private boolean implInitFailed = false;
086:
087: //
088: // Constructor
089: //
090: public CCPPClientDetector() {
091: this ClassName = getClass().getName();
092: }
093:
094: /**
095: * Gets the CCPPClient correesponding to the profile URI
096: *
097: * profileURITable is defined Hashtable, so we dont have to synchronize
098: * get & put.
099: *
100: * ALGORITHM:
101: * 1: sync on statusMap & try to get the CCPPClient
102: * 1.1: Return if found
103: * 1.2: Look if some one is merging our profile
104: * 1.2.1: if yes; get the Monitor to sync on.
105: * 1.2.2: if no; put in the Monitor in the statusMap to indicate
106: * that merging has started for the URI
107: *
108: * 2: sync on the Monitor obtained in 1.2.1 ; and
109: * check if the profile exists & return if it does.
110: *
111: * 3: Call getMergedProfile() and add the
112: * returned CCPPClient to the cache & the TypesManager
113: * 3.1: sync on statusMap; & remove the mergingIndicator Monitor;
114: *
115: * 4: return the CCPPClient
116: */
117: public Client getCCPPClient(String profURI, Client client,
118: HttpServletRequest request) {
119: if (debug.messageEnabled()) {
120: debug.message(this ClassName + " :getCCPPClient(uri) = "
121: + profURI);
122: }
123:
124: Client ccppClient = null;
125: Object mergeIndicator = null;
126:
127: //
128: // (1): serialized access by all threads here
129: //
130: synchronized (statusMap) {
131: if ((ccppClient = (Client) profileURITable.get(profURI)) != null) {
132: return ccppClient; // (1.1)
133: } else {
134: //
135: // (1.2) Look if some one is merging our URI
136: //
137: mergeIndicator = (Object) statusMap.get(profURI); // (1.2.1)
138: if (mergeIndicator == null) {
139: //
140: // (1.2.2) - create Monitor
141: //
142: mergeIndicator = new Object();
143: statusMap.put(profURI, mergeIndicator);
144:
145: if (debug.messageEnabled()) {
146: debug.message("*** Adding to MonitorList: "
147: + profURI);
148: }
149: }
150: }
151: }
152:
153: synchronized (mergeIndicator) {
154: //
155: // 2 Check if any thread ahead of us did the merge.
156: //
157: if ((ccppClient = (Client) profileURITable.get(profURI)) != null) {
158: return (ccppClient);
159: }
160:
161: if (debug.messageEnabled()) {
162: debug.message("*** Merging profiles for: " + profURI);
163: }
164: //
165: // 3. Merging thread goes here.
166: //
167: try {
168: Map map = getMergedUAProfile(client, request);
169:
170: if (map != null) {
171: ccppClient = makeAndAddClient(profURI, client, map);
172: }
173: } catch (ClientDetectionException cde) {
174: //
175: // set implInitFailed, so we dont try to merge again !
176: //
177: implInitFailed = true;
178: debug
179: .warning(
180: this ClassName
181: + " :: turning off cc/pp "
182: + " detection, switching to MAPClientDetector: ",
183: cde);
184: } catch (Throwable e) {
185: //
186: // ignore - & return null
187: //
188: debug.warning(this ClassName
189: + " :: Merge profile failed.", e);
190: }
191:
192: //
193: // (3.1) remove Monitor.
194: //
195: synchronized (statusMap) {
196: statusMap.remove(profURI);
197: }
198: }
199:
200: return ccppClient; // (4)
201: }
202:
203: /**
204: * 1. Make a clientType string
205: * 2. Add the parent
206: * 3. Add the profile object with the key = profileURI & with
207: * Client for the value.
208: * 4. add it to the ClientTypesManager's client data cache.
209: *
210: * @param localClient The Client matched in our local store. Should not be
211: * null.
212: */
213: private Client makeAndAddClient(String profURI, Client localClient,
214: Map cMap) {
215: if (profURI == null || cMap == null) {
216: return null;
217: }
218:
219: Set ctSet = makeClientType(profURI);
220: cMap.put(CLIENT_TYPE, ctSet);
221:
222: //
223: // Set the parent's clientType as the parentID for the new Client
224: //
225: String parentCT = localClient.getClientType();
226: Set parentSet = createSet(parentCT);
227: cMap.put(PARENT_ID, parentSet);
228:
229: //
230: // Dont add the userAgent Property - it will cause this CCPPClient to
231: // to overwrite the localClient in the userAgentMap of the Manager
232: //
233:
234: String clientType = getFirstAndOnlyString(ctSet);
235: if (debug.messageEnabled()) {
236: debug.message(this ClassName + " : makeClient() = "
237: + profURI + " :: ClientType = " + clientType
238: + " : Parent : " + parentSet);
239: }
240:
241: //
242: // add to the ClientTypesManager (memory only not persistent store).
243: //
244: Client c = null;
245: try {
246: c = addClient(clientType, cMap, false);
247: profileURITable.put(profURI, c);
248: } catch (Exception e) {
249: debug.warning(this ClassName
250: + " :: Failed to add clientType to Manager : "
251: + clientType, e);
252: }
253:
254: return c;
255: }
256:
257: /**
258: * Creates a key with all the x-wap-profile headers collected from the
259: * request.
260: */
261: protected String getProfileURI(HttpServletRequest request) {
262: String profileURI = null;
263: if ((profileURI = (String) request.getAttribute(REQ_PROF_ATTR)) != null) {
264: return profileURI;
265: }
266:
267: StringBuffer sb = null;
268: for (int i = 0; i < headerNames.length; i++) {
269: Enumeration e = request.getHeaders(headerNames[i]);
270: while (e.hasMoreElements()) {
271: String header = (String) e.nextElement();
272: header = header.trim();
273:
274: if (sb == null) {
275: sb = new StringBuffer();
276: } else {
277: sb.append('_'); // some separator
278: }
279:
280: sb.append(header);
281: }
282: }
283:
284: if (sb != null) {
285: /**
286: * Getting the URI is intensive op. set it in the request attribute
287: * so subsequent calls using the same request, dont have to go thro'
288: * the Enumeration loop.
289: */
290: profileURI = sb.toString();
291: request.setAttribute(REQ_PROF_ATTR, profileURI);
292: }
293:
294: return profileURI;
295: }
296:
297: public String getClientType(HttpServletRequest request)
298: throws ClientDetectionException {
299: String profileURI = getProfileURI(request);
300:
301: //
302: // try a non-syncd lookup and if we find it - great !!
303: //
304: Client client = null;
305: if ((profileURI != null)
306: && (client = (Client) profileURITable.get(profileURI)) != null) {
307: String ct = client.getClientType();
308: if (debug.messageEnabled()) {
309: debug.message(this ClassName
310: + ": ====== getClientType(perf) Returning: "
311: + ct);
312: }
313: return ct;
314: }
315:
316: String uaMatchedClientType = super .getClientType(request);
317:
318: if (debug.messageEnabled()) {
319: debug.message(this ClassName + ":: UA Match = "
320: + uaMatchedClientType + " :: Profile URI = "
321: + profileURI);
322: }
323:
324: if ((profileURI == null) || implInitFailed) {
325: if (debug.messageEnabled()) {
326: debug.message(this ClassName
327: + " :: returning Legacy device = "
328: + uaMatchedClientType);
329: }
330: return uaMatchedClientType; // old behaviour
331: }
332:
333: //
334: // We have a WAP2.0 client. Try to see if we have the profile in
335: // the ClientDataManager cache.
336: //
337:
338: try {
339: client = ClientsManager.getInstance(uaMatchedClientType);
340: } catch (Exception e) {
341: debug.warning("GOT Excptn: ", e);
342: }
343:
344: Client ccppClient = getCCPPClient(profileURI, client, request);
345: String clientType = null;
346: if (ccppClient != null) {
347: clientType = ccppClient.getClientType();
348: }
349:
350: if (clientType == null) {
351: clientType = uaMatchedClientType; // return the old clientType
352: }
353:
354: if (debug.messageEnabled()) {
355: debug.message(this ClassName
356: + ": ====== getClientType() Returning: "
357: + clientType);
358: }
359: return clientType;
360: }
361:
362: /**
363: * This method will be implemented by the DELI/jsr188 Reference
364: * implementation to do the profile merge ...
365: *
366: * @param client The client object matched with user-agent by the native
367: * ClientDetector
368: * @param request HttpServletRequest
369: *
370: * @return Map of client properties [key=property, value={set of Strings}]
371: */
372: protected Map getMergedUAProfile(Client client,
373: HttpServletRequest request) throws ClientDetectionException {
374: debug.error(this ClassName
375: + " : Cannot support cc/pp without an RI");
376: return null;
377: }
378: }
|