0001: /**
0002: * $Id: ConfigurationFactory.java,v 1.16 2005/09/28 20:30:25 rakeshn Exp $
0003: * Copyright 2002 Sun Microsystems, Inc. All
0004: * rights reserved. Use of this product is subject
0005: * to license terms. Federal Acquisitions:
0006: * Commercial Software -- Government Users
0007: * Subject to Standard License Terms and
0008: * Conditions.
0009: *
0010: * Sun, Sun Microsystems, the Sun logo, and iPlanet
0011: * are trademarks or registered trademarks of Sun Microsystems,
0012: * Inc. in the United States and other countries.
0013: */package com.sun.ssoadapter.config;
0014:
0015: import java.util.*;
0016: import java.net.*;
0017: import java.lang.reflect.*;
0018: import java.io.IOException;
0019: import javax.servlet.http.*;
0020: import java.security.AccessController;
0021:
0022: import com.iplanet.sso.*;
0023:
0024: import com.sun.identity.security.DecryptAction;
0025: import com.sun.identity.security.EncryptAction;
0026:
0027: import com.sun.ssoadapter.config.PSClientAwareContextFactory;
0028: import com.sun.ssoadapter.config.ClientAwareUserContext;
0029: import com.sun.ssoadapter.config.SAALException;
0030:
0031: import com.sun.ssoadapter.SSOAdapterLogger;
0032: import com.sun.ssoadapter.SSOAdapterSession;
0033: import java.util.logging.Logger;
0034: import java.util.logging.Level;
0035:
0036: /**
0037: * This class is used to create, destroy, and manipulate
0038: * configurations and instances of Configuration objects.
0039: * <p>A ConfigurationFactory is associated with the following:
0040: * <ul>
0041: * <li>A DSAME service.
0042: * <li>An attribute within that service that contains configuration information,
0043: * where this information is encoded as multiple URLs. Typically,
0044: * this attribute is defined as a DSAME "User" attribute.
0045: * <li>An attribute within that service that contains a "Configuration
0046: * Description". Typically, this attribute is defined as a DSAME
0047: * "Organization" attribute.
0048: * </ul>
0049: * <p>A cache of ConfigurationFactory objects is maintained, such that only
0050: * a single instance of a ConfigurationFactory is created for each combination
0051: * of the above listed values, i.e., service, configuration attribute name, and
0052: * configuration description attribute name.
0053: * <p>A ConfigurationFactory manages a cache of Configuration objects that
0054: * are derived from the aforementioned information. These Configuration
0055: * objects are managed on a per user basis.
0056: *
0057: * @version 1.0
0058: * @see com.sun.ssoadapter.config.ConfigurationException
0059: *
0060: */
0061:
0062: public class ConfigurationFactory implements ConfigurationConstants {
0063:
0064: //
0065: // Public constants...
0066: //
0067: public static final String DEFAULT_DESC_ATTR = "sunConfigurationTemplates";
0068:
0069: //
0070: // Factories are shared amongst all users...
0071: //
0072: private static Hashtable factoryHashtable = new Hashtable(5);
0073:
0074: //
0075: // Configurations are tracked/cached on a
0076: // per user basis. Keys typically include SSOTokenId string values.
0077: //
0078: private Hashtable mergedConfigCache = new Hashtable(100);
0079: private Hashtable rawConfigCache = new Hashtable(100);
0080:
0081: private boolean mergeDynamicConfigurations = false;
0082:
0083: //
0084: // SAAL...
0085: //
0086: private PSClientAwareContextFactory contextFactory = null;
0087: private String serviceName = null;
0088: private String rootAttributeName = null;
0089: private String descAttributeName = DEFAULT_DESC_ATTR;
0090: private Map serviceMap = null;
0091:
0092: private static Logger logger = SSOAdapterLogger
0093: .getLogger("com.sun.portal.ssoadapter.config");
0094:
0095: //
0096: // authless authentication session uid
0097: //
0098: private static final String authlessSUID = "ssoadapter.authless.suid";
0099:
0100: /**
0101: *
0102: * @param serviceName
0103: * @param rootAttributeName
0104: * @param descAttributeName
0105: * @throws java.lang.Exception
0106: */
0107: private ConfigurationFactory(String serviceName,
0108: String rootAttributeName, String descAttributeName)
0109: throws Exception {
0110: this .serviceName = serviceName;
0111: this .rootAttributeName = rootAttributeName;
0112: this .descAttributeName = descAttributeName;
0113: serviceMap = new HashMap();
0114: serviceMap.put("serviceName", serviceName);
0115: try {
0116: this .contextFactory = PSClientAwareContextFactory
0117: .getInstance();
0118: } catch (Exception e) {
0119: logger.log(Level.SEVERE, "PSSA_CSSC0020", e);
0120: throw e;
0121: }
0122: }
0123:
0124: /**
0125: * Returns (and instantiates if necessary), an instance of ConfigurationFactory.
0126: * <p>The ConfigurationFactory returned by this method will manipulate
0127: * Configuration objects that are derived from DSAME information referenced
0128: * by the method parameters, but the configuration description attribute
0129: * name will default to "sunConfigurationDescription".
0130: *
0131: * @param serviceName Identifies the DSAME service that contains configuration
0132: * information.
0133: * @param rootAttributeName Identifies the DSAME attribute that contains
0134: * the user specific configuration information.
0135: * @return ConfigurationFactory Returns factory if successful, null otherwise.
0136: */
0137: public static ConfigurationFactory getInstance(String serviceName,
0138: String rootAttributeName) {
0139: return getInstance(serviceName, rootAttributeName,
0140: DEFAULT_DESC_ATTR);
0141: }
0142:
0143: /**
0144: * Returns (and instantiates if necessary), an instance of ConfigurationFactory.
0145: * <p>The ConfigurationFactory returned by this method will manipulate
0146: * Configuration objects that are derived from DSAME information referenced
0147: * by the method parameters.
0148: *
0149: * @param serviceName Identifies the DSAME service that contains configuration
0150: * information.
0151: * @param rootAttributeName Identifies the DSAME attribute that contains
0152: * the user specific configuration information.
0153: * @param descAttributeName Identifies the DSAME attribute that contains
0154: * a configuration description that may be used
0155: * to control the construction of Configuration
0156: * objects.
0157: * @return ConfigurationFactory Returns factory if successful, null otherwise.
0158: */
0159: public static ConfigurationFactory getInstance(String serviceName,
0160: String rootAttributeName, String descAttributeName) {
0161: String hashKey = serviceName + rootAttributeName
0162: + descAttributeName;
0163: ConfigurationFactory configFactory = (ConfigurationFactory) factoryHashtable
0164: .get(hashKey);
0165:
0166: if (configFactory == null) {
0167: try {
0168: configFactory = new ConfigurationFactory(serviceName,
0169: rootAttributeName, descAttributeName);
0170: } catch (Exception e) {
0171: return null;
0172: }
0173: factoryHashtable.put(hashKey, configFactory);
0174:
0175: if (logger.isLoggable(Level.FINEST)) {
0176: String[] param = new String[3];
0177: param[0] = serviceName;
0178: param[1] = descAttributeName;
0179: param[2] = rootAttributeName;
0180: logger.log(Level.FINEST, "PSSA_CSSC0021", param);
0181: }
0182: }
0183:
0184: return configFactory;
0185: }
0186:
0187: /**
0188: * Returns an Enumeration of configuration names available to the user.
0189: * <p>Each value of the Enumeration is a String representing the name
0190: * of a configuration created by and/or available to the user.
0191: * <p>The values of the enumeration are sorted according to the
0192: * rules of the TreeMap class.
0193: *
0194: * @param session Contains information which is presumed to be sufficient
0195: * to create an SSOToken, that is used to identify
0196: * the user on who's behalf the session is being processed.
0197: * @return Enumeration An enumeration of names. Returns null if problem occurs.
0198: */
0199: public Enumeration getConfigurationNames(SSOAdapterSession session) {
0200: Enumeration configNames = null;
0201: SSOToken token;
0202: String tokenString;
0203:
0204: try {
0205: token = session.getSSOToken();
0206: SSOTokenID tokenID = token.getTokenID();
0207: tokenString = tokenID.toString();
0208:
0209: loadCache(session, tokenString);
0210:
0211: String hashKey = tokenString;
0212:
0213: TreeMap configHashMap = new TreeMap();
0214: HashMap tmpHashMap = (HashMap) rawConfigCache.get(hashKey);
0215: if (tmpHashMap != null) {
0216: configHashMap.putAll(tmpHashMap);
0217: }
0218: tmpHashMap = (HashMap) mergedConfigCache.get(hashKey);
0219: if (tmpHashMap != null) {
0220: configHashMap.putAll(tmpHashMap);
0221: }
0222: configNames = new HashMapKeyEnumeration(configHashMap);
0223: } catch (Exception e) {
0224: logger.log(Level.SEVERE, "PSSA_CSSC0022", e);
0225: }
0226:
0227: return configNames;
0228: }
0229:
0230: /**
0231: * Merge the given configuration with it's
0232: * Configuration Description.
0233: * <p>If a configuration description is specified
0234: * by "config", and if that config desc does
0235: * not permit a merge, then a null object will
0236: * be returned.
0237: * @param config
0238: * @param session
0239: * @throws com.sun.ssoadapter.config.ConfigurationException
0240: * @return
0241: */
0242: public Configuration computeMergedConfiguration(
0243: Configuration config, SSOAdapterSession session)
0244: throws ConfigurationException {
0245: String configDescName = config.getConfigurationDescription();
0246: String configName = config.getConfigurationName();
0247:
0248: //
0249: // If no Configuration Description specified, return original config...
0250: //
0251: if (configDescName == null) {
0252: return config;
0253: }
0254:
0255: ConfigurationFactory descFactory = ConfigurationFactory
0256: .getInstance(serviceName, descAttributeName);
0257:
0258: //
0259: // Retrieve Configuration Description...
0260: //
0261: Configuration configDescription;
0262: try {
0263: configDescription = descFactory.readConfiguration(
0264: configDescName, false, session);
0265: } catch (Exception ee) {
0266: String[] param = new String[3];
0267: param[0] = configDescName;
0268: param[1] = serviceName;
0269: param[2] = descAttributeName;
0270: logger.log(Level.SEVERE, "PSSA_CSSC0023", param);
0271: String msg = this .getClass().getName()
0272: + ".computeMergedConfiguration(): "
0273: + "problems reading configuration description, configDescName="
0274: + configDescName;
0275: throw new ConfigurationException(msg);
0276: }
0277: if (configDescription == null) {
0278: String[] param = new String[4];
0279: param[0] = configDescName;
0280: param[1] = serviceName;
0281: param[2] = descAttributeName;
0282: param[3] = configName;
0283: logger.log(Level.WARNING, "PSSA_CSSC0033", param);
0284: return config;
0285: // String msg = this.getClass().getName()+".computeMergedConfiguration(): "+
0286: // "attempt to reference non-existent configuration description, configDescName=" +
0287: // configDescName + " ignoring";
0288: // throw new ConfigurationException(msg);
0289: }
0290:
0291: String[] userDef = configDescription
0292: .getPropertyArray(PROP_USER_NAME);
0293: String[] defaultDef = configDescription
0294: .getPropertyArray(PROP_DEFAULT_NAME);
0295: String[] encodedDef = configDescription
0296: .getPropertyArray(PROP_ENCODED_NAME);
0297:
0298: //
0299: // If the config desc makes no merge possible, return null...
0300: //
0301: if (userDef == null && defaultDef == null) {
0302: return null;
0303: }
0304:
0305: Configuration mergedConfig = new Configuration();
0306:
0307: //
0308: // Set configName and configDesc in merged config...
0309: //
0310: mergedConfig.setConfigurationName(configName);
0311: mergedConfig.setConfigurationDescription(configDescName);
0312:
0313: HashMap mergedHashMap = mergedConfig.getHashMap();
0314:
0315: //
0316: // Copy default values to merged config.
0317: //
0318: int i;
0319: if (defaultDef != null) {
0320: for (i = 0; i < defaultDef.length; i++) {
0321: String[] val = configDescription
0322: .getPropertyArray(defaultDef[i]);
0323: if (val != null) {
0324: mergedHashMap.put(defaultDef[i], val);
0325: }
0326: }
0327: }
0328:
0329: //
0330: // Merge in user defineable properties...
0331: //
0332: if (userDef != null) {
0333: for (i = 0; i < userDef.length; i++) {
0334: String[] val = config.getPropertyArray(userDef[i]);
0335: if (val != null) {
0336: mergedHashMap.put(userDef[i], val);
0337: }
0338: mergedConfig.setAsUserProperty(userDef[i]);
0339: }
0340: }
0341:
0342: //
0343: // Decode "encoded" properties...
0344: //
0345: if (encodedDef != null) {
0346: for (i = 0; i < encodedDef.length; i++) {
0347: String val = mergedConfig.getProperty(encodedDef[i]);
0348: if (val != null) {
0349: mergedConfig
0350: .setProperty(
0351: encodedDef[i],
0352: (String) AccessController
0353: .doPrivileged(new DecryptAction(
0354: val)));
0355: }
0356: mergedConfig.setAsEncodedProperty(encodedDef[i]);
0357: }
0358: }
0359:
0360: return mergedConfig;
0361: }
0362:
0363: /**
0364: * Load cache, if needed...
0365: * @param session
0366: * @param tokenString
0367: * @throws java.lang.Exception
0368: */
0369: private void loadCache(SSOAdapterSession session, String tokenString)
0370: throws Exception {
0371: loadCache(session, tokenString, false, "");
0372: }
0373:
0374: /**
0375: * Load cache, if needed...
0376: * @param session
0377: * @param tokenString
0378: * @param reload
0379: * @param mergeConfigName
0380: * @throws java.lang.Exception
0381: */
0382: private void loadCache(SSOAdapterSession session,
0383: String tokenString, boolean reload, String mergeConfigName)
0384: throws Exception {
0385: String hashKey = tokenString;
0386:
0387: if (!reload) {
0388: if (rawConfigCache.get(hashKey) != null
0389: || mergedConfigCache.get(hashKey) != null) {
0390: return;
0391: }
0392: }
0393:
0394: HashMap rawConfigHashMap = new HashMap(10);
0395: HashMap mergedConfigHashMap = new HashMap(10);
0396:
0397: ClientAwareUserContext userContext = contextFactory
0398: .getClientAwareUserContext(session);
0399: Set configSet = new HashSet();
0400: //Do the following so that the new Configs added to the org will be added the existing users
0401: if (mergeDynamicConfigurations) {
0402: configSet = getMergedDynamicConfigurations(userContext,
0403: serviceMap, rootAttributeName);
0404: } else {
0405: configSet = userContext.getAttribute(serviceMap,
0406: rootAttributeName);
0407: }
0408: Iterator configIter = configSet.iterator();
0409:
0410: //
0411: // The following computes the merged configurations
0412: // for those cases where a user contributes some
0413: // portion of a configuration. The results are
0414: // cached.
0415: //
0416: while (configIter.hasNext()) {
0417: String configString = (String) configIter.next();
0418: Configuration rawConfig = new Configuration(configString);
0419:
0420: //
0421: // Get the configName for this entry...
0422: //
0423: String tmpConfigName = rawConfig.getConfigurationName();
0424:
0425: if (tmpConfigName == null) {
0426: String msg = this .getClass().getName()
0427: + ".loadCache(): "
0428: + "configName not specified in entry from "
0429: + rootAttributeName;
0430: logger.log(Level.SEVERE, "PSSA_CSSC0024",
0431: rootAttributeName);
0432: throw new ConfigurationException(msg);
0433: }
0434:
0435: rawConfigHashMap.put(tmpConfigName, rawConfig);
0436:
0437: //
0438: // Get the configDescName for this entry...
0439: //
0440: String configDescName = rawConfig
0441: .getConfigurationDescription();
0442:
0443: //
0444: // If not specified, don't try merging...
0445: //
0446: if (configDescName == null || configDescName.length() == 0) {
0447: continue;
0448: }
0449:
0450: //
0451: // If configDesc defined, build a merged Configuration...
0452: //
0453: Configuration mergedConfig = computeMergedConfiguration(
0454: rawConfig, session);
0455:
0456: if (mergedConfig != null) {
0457: mergedConfigHashMap.put(tmpConfigName, mergedConfig);
0458: } else {
0459: //
0460: // In this case..., the rule is that if a
0461: // a raw config specifies a config desc,
0462: // and if a merge with that config desc
0463: // produces no results, then the raw
0464: // config will be treated as invalid and
0465: // ignored.
0466: //
0467: rawConfigHashMap.remove(tmpConfigName);
0468: if (logger.isLoggable(Level.SEVERE)) {
0469: String[] params = { tmpConfigName, configDescName };
0470: logger.log(Level.SEVERE, "PSSA_CSSC0025", params);
0471: }
0472: }
0473: }
0474:
0475: //
0476: // The following computes the configurations for which
0477: // there is no contribution from the user, i.e., the
0478: // configuration is derived entirely from a Configuration
0479: // Description that does not specify any attributes
0480: // as being user provided. The results are cached.
0481: //
0482: configSet = userContext.getAttribute(serviceMap,
0483: descAttributeName);
0484: configIter = configSet.iterator();
0485: while (configIter.hasNext()) {
0486: String configString = (String) configIter.next();
0487: Configuration tmpConfig = new Configuration(configString);
0488:
0489: if (tmpConfig.getProperty(PROP_USER_NAME) != null
0490: || tmpConfig.getProperty(PROP_DEFAULT_NAME) != null) {
0491: continue;
0492: }
0493:
0494: String tmpConfigName = tmpConfig.getConfigurationName();
0495: tmpConfig.setConfigurationDescription(tmpConfigName);
0496: HashMap tmpConfigHashMap = tmpConfig.getHashMap();
0497: tmpConfigHashMap.remove(PROP_USER_NAME);
0498: tmpConfigHashMap.remove(PROP_DEFAULT_NAME);
0499: tmpConfigHashMap.remove(PROP_ENCODED_NAME);
0500:
0501: //
0502: // Note that we're NOT storing in rawConfigHashMap().
0503: // This is to avoid improperly writing purely
0504: // config desc derived info to the user attribute.
0505: //
0506: mergedConfigHashMap.put(tmpConfigName, tmpConfig);
0507: }
0508:
0509: //
0510: // Store the raw and merged configs in their respective caches...
0511: //
0512: mergedConfigCache.put(hashKey, mergedConfigHashMap);
0513: rawConfigCache.put(hashKey, rawConfigHashMap);
0514:
0515: //
0516: // Register a reaper for the cached configurations...
0517: //
0518: HashtableReaper reaper = new HashtableReaper(hashKey,
0519: mergedConfigCache);
0520:
0521: if (session != null) {
0522: session.addSSOTokenListener(reaper);
0523: }
0524:
0525: reaper = new HashtableReaper(hashKey, rawConfigCache);
0526:
0527: if (session != null) {
0528: session.addSSOTokenListener(reaper);
0529: }
0530: }
0531:
0532: /**
0533: * Returns a Configuration object that is constructed based upon information
0534: * contained in the DSAME service and attributes associated with this
0535: * ConfigurationFactory.
0536: * <p>If the user has previously requested this Configuration, it is returned
0537: * from a cache, otherwise, a Configuration object is constructed in the
0538: * following fashion:
0539: * <ol>
0540: * <li>The multi-valued "User" attribute is read based upon the serviceName
0541: * and rootAttributeName associated with this ConfigurationFactory. Each
0542: * value of this attribute represents a unique configuration. Processing
0543: * of this information will be described from the perspective of a
0544: * particular configuration, though actual implementation is somewhat
0545: * different.
0546: * <li>A "raw" Configuration object is constructed from the information
0547: * stored in the DSAME "User" attribute specified by this
0548: * ConfigurationFactory's rootAttributeName.
0549: * <li>If the "useDesc" parameter of this method is "false", the raw
0550: * Configuration object is returned and processing is completed, otherwise...
0551: * <li>If the "useDesc" parameter of this method is "true", the configuration
0552: * description information identified by the descAttributeName of this
0553: * factory and the value of the "configDesc" property of the raw
0554: * Configuration previously read will be retrieved.
0555: * <li>The configuration description is actually a special type of
0556: * configuration, that can control the configuration information
0557: * that is used on behalf of a user. It specifies what information
0558: * may be provided by the user and what information must be derived
0559: * from the configuration description. Ultimately, a "merged" Configuration
0560: * object is created and is returned as the intended configuration.
0561: * <li>Note: It is also possible that a Configuration object may be constructed
0562: * soley from data provided by the Configuration Description, with no
0563: * data derived from the user attribute.
0564: * </ol>
0565: *
0566: * @param configName Specifies the name of the configuration to retrieve.
0567: * @param useDesc If "true", a "merged" Configuration is returned, otherwise,
0568: * the "raw" Configuration is returned.
0569: * @param session Contains information which is presumed to be sufficient
0570: * to create an SSOToken, that is used to identify
0571: * the user on who's behalf the session is being processed.
0572: * @throws com.sun.ssoadapter.config.ConfigurationException
0573: * @return
0574: */
0575: public Configuration readConfiguration(String configName,
0576: boolean useDesc, SSOAdapterSession session)
0577: throws ConfigurationException {
0578: Configuration finalConfig = null;
0579: String tokenString = session.getSessionID();
0580:
0581: if (!session.isSessionValid()) {
0582: // the session is not found, try to retrieve the authless authentication
0583: // uid set by the ssoadapter factory
0584: //
0585: tokenString = session.getAuthlessUID();
0586:
0587: if (tokenString == null) {
0588: String msg = this .getClass().getName()
0589: + ".readConfiguration(): "
0590: + "failed: Session Invalid for configuration "
0591: + configName;
0592: logger.log(Level.SEVERE, "PSSA_CSSC0026", configName);
0593: throw new ConfigurationException(msg);
0594: }
0595: }
0596:
0597: try {
0598: String hashKey = tokenString;
0599: HashMap mergedConfigHashMap;
0600: HashMap rawConfigHashMap;
0601:
0602: //
0603: // See if this configuration is already loaded...
0604: //
0605: if (useDesc) {
0606: mergedConfigHashMap = (HashMap) mergedConfigCache
0607: .get(hashKey);
0608:
0609: if (mergedConfigHashMap != null) {
0610: Configuration tmpConfig = null;
0611: Set tmpSet = mergedConfigHashMap.keySet();
0612: boolean found = false;
0613:
0614: for (Iterator i = tmpSet.iterator(); i.hasNext();) {
0615: String key = (String) i.next();
0616: tmpConfig = (Configuration) mergedConfigHashMap
0617: .get(key);
0618:
0619: String tmpConfigName = tmpConfig
0620: .getConfigurationName();
0621:
0622: if (tmpConfigName.equals(configName)) {
0623: found = true;
0624: break;
0625: }
0626: }
0627:
0628: if (found) {
0629: return tmpConfig;
0630: } else {
0631: return null;
0632: }
0633: }
0634: } else {
0635: rawConfigHashMap = (HashMap) rawConfigCache
0636: .get(hashKey);
0637: if (rawConfigHashMap != null) {
0638: return (Configuration) rawConfigHashMap
0639: .get(configName);
0640: }
0641: }
0642:
0643: //
0644: // If not, load this session's cache...
0645: //
0646: loadCache(session, tokenString);
0647:
0648: //
0649: // Now, go back and return results from cache.
0650: //
0651: finalConfig = readConfiguration(configName, useDesc,
0652: session);
0653:
0654: } catch (Exception e) {
0655: String msg = this .getClass().getName()
0656: + ".readConfiguration(): " + "failed: " + e;
0657: logger.log(Level.SEVERE, "PSSA_CSSC0027", e);
0658: throw new ConfigurationException(msg);
0659: }
0660:
0661: return finalConfig;
0662: }
0663:
0664: /**
0665: * Determines if the Configuration named by "configName", that was or would be
0666: * created by this instance of ConfigurationFactory, contains any information
0667: * provided by the user.
0668: * <p>This is equivalent to asking: "Is this Configuration user editable"?
0669: *
0670: * @return true, if Configuration is user derived, false otherwise.
0671: * @param configName Name of the Configuration to be tested.
0672: * @param session Contains information which is presumed to be sufficient
0673: * to create an SSOToken, that is used to identify
0674: * the user on who's behalf the session is being processed.
0675: * @throws com.sun.ssoadapter.config.ConfigurationException
0676: */
0677: public boolean isConfigurationUserEditable(String configName,
0678: SSOAdapterSession session) throws ConfigurationException {
0679: Configuration rawConfig = readConfiguration(configName, false,
0680: session);
0681:
0682: if (rawConfig == null) {
0683: return false;
0684: }
0685:
0686: return true;
0687: }
0688:
0689: /**
0690: * Writes the Configuration to the directory in URL form.
0691: * @param configHashMap
0692: * @param session
0693: * @throws java.lang.Exception
0694: */
0695: private void writeHashMap(HashMap configHashMap,
0696: SSOAdapterSession session) throws Exception {
0697: HashSet configSet = new HashSet(configHashMap.size());
0698: Iterator configIter = configHashMap.values().iterator();
0699:
0700: while (configIter.hasNext()) {
0701: Configuration config = (Configuration) configIter.next();
0702: String configURL = config.getConfigurationURL();
0703: configSet.add(configURL);
0704: }
0705:
0706: try {
0707:
0708: ClientAwareUserContext userContext = contextFactory
0709: .getClientAwareUserContext(session);
0710: userContext.setAttribute(serviceMap, DEFAULT_CLIENT_TYPE,
0711: rootAttributeName, configSet);
0712:
0713: } catch (Exception e) {
0714: String msg = this .getClass().getName()
0715: + ".writeConfigPropertiesHashtable() failed. Reason: "
0716: + e.toString();
0717: logger.log(Level.SEVERE, "PSSA_CSSC0028", e);
0718: throw new ConfigurationException(msg);
0719: }
0720:
0721: return;
0722: }
0723:
0724: /**
0725: * Writes a configuration into the DSAME service and attribute associated
0726: * with this ConfigurationFactory.
0727: * <p>If the Configuration object specifies a Configuration Description,
0728: * then it is expected that the Configuration Description specifies
0729: * which values belong to the user, and only those values will be
0730: * written. If a Configuration Description is not specified, the
0731: * entire Configuration is written.
0732: *
0733: * @param config The Configuration to be written.
0734: * @param session Contains information which is presumed to be sufficient
0735: * to create an SSOToken, that is used to identify
0736: * the user on who's behalf the session is being processed.
0737: * @throws com.sun.ssoadapter.config.ConfigurationException
0738: */
0739: public void writeConfiguration(Configuration config,
0740: SSOAdapterSession session) throws ConfigurationException {
0741: SSOToken token;
0742: String tokenString;
0743: String hashKey;
0744: String configName = config.getConfigurationName();
0745:
0746: try {
0747: tokenString = session.getSessionID();
0748:
0749: loadCache(session, tokenString, true, configName);
0750:
0751: } catch (Exception e) {
0752: String msg = this .getClass().getName()
0753: + ".writeConfiguration(): failed. Reason: "
0754: + e.toString();
0755: logger.log(Level.SEVERE, "PSSA_CSSC0028", e);
0756: throw new ConfigurationException(msg);
0757: }
0758:
0759: hashKey = tokenString;
0760:
0761: //
0762: // Make sure configName is specified in Configuration...
0763: //
0764: if (configName == null) {
0765: String msg = this .getClass().getName()
0766: + ".writeConfiguration(): "
0767: + "Cannot write a configuration without a configuration name.";
0768: logger.log(Level.SEVERE, "PSSA_CSSC0029");
0769: throw new ConfigurationException(msg);
0770: }
0771:
0772: String configDescName = config.getConfigurationDescription();
0773: boolean badConfigDesc = false;
0774: HashMap rawConfigHashMap = (HashMap) rawConfigCache
0775: .get(hashKey);
0776:
0777: try {
0778:
0779: if (configDescName == null) {
0780: //
0781: // If untyped, write asis.
0782: //
0783: rawConfigHashMap.put(configName, config);
0784: writeHashMap(rawConfigHashMap, session);
0785:
0786: } else {
0787:
0788: //
0789: // If typed, write only user defineable values.
0790: //
0791: ConfigurationFactory descFactory = ConfigurationFactory
0792: .getInstance(serviceName, descAttributeName);
0793: Configuration configDescription = descFactory
0794: .readConfiguration(configDescName, false,
0795: session);
0796: if (configDescription == null) {
0797:
0798: // OK, this is hokey, but it beats rearranging the code
0799: // with a bunch of separate try/catch blocks.
0800: badConfigDesc = true;
0801: throw new Exception();
0802: }
0803: String[] userDef = configDescription
0804: .getPropertyArray(PROP_USER_NAME);
0805: String[] defaultDef = configDescription
0806: .getPropertyArray(PROP_DEFAULT_NAME);
0807: String[] encodedDef = configDescription
0808: .getPropertyArray(PROP_ENCODED_NAME);
0809:
0810: if (userDef != null || defaultDef != null) {
0811:
0812: //
0813: // Set values common to all configurations...
0814: //
0815: Configuration userConfig = new Configuration();
0816: userConfig.setConfigurationName(configName);
0817: userConfig
0818: .setConfigurationDescription(configDescName);
0819:
0820: //
0821: // Write only user editable values...
0822: //
0823: if (userDef != null) {
0824: for (int i = 0; i < userDef.length; i++) {
0825: String key = userDef[i];
0826: String[] val = config.getPropertyArray(key);
0827: if (val != null)
0828: userConfig.setPropertyArray(key, val);
0829: }
0830: }
0831:
0832: //
0833: // Encode "encoded" values...
0834: //
0835: if (encodedDef != null) {
0836: for (int i = 0; i < encodedDef.length; i++) {
0837: String key = encodedDef[i];
0838: String val = userConfig.getProperty(key);
0839: if (val != null)
0840: userConfig
0841: .setProperty(
0842: key,
0843: (String) AccessController
0844: .doPrivileged(new EncryptAction(
0845: val)));
0846: }
0847: }
0848:
0849: rawConfigHashMap.put(configName, userConfig);
0850: writeHashMap(rawConfigHashMap, session);
0851: }
0852:
0853: }
0854:
0855: //
0856: // Flush this session's config cache...
0857: //
0858: // TODO: Need to be smarter here. By whatever means,
0859: // the merged and raw config caches have to remain consistent.
0860: // This is the easy, but not efficient, way to maintain
0861: // consistency.
0862: //
0863: mergedConfigCache.remove(hashKey);
0864: rawConfigCache.remove(hashKey);
0865:
0866: } catch (Exception e) {
0867:
0868: //
0869: // Flush the cache in case anything went
0870: // wrong during the write...
0871: //
0872: mergedConfigCache.remove(hashKey);
0873: rawConfigCache.remove(hashKey);
0874:
0875: String msg;
0876: if (badConfigDesc) {
0877: msg = this .getClass().getName()
0878: + ".writeConfiguration(): "
0879: + "attempt to reference non-existent configuration description, configDescName="
0880: + configDescName;
0881: logger.log(Level.SEVERE, "PSSA_CSSC0030",
0882: configDescName);
0883: } else {
0884: msg = this .getClass().getName()
0885: + ".writeConfiguration(): failed. Reason: "
0886: + e.toString();
0887: logger.log(Level.SEVERE, "PSSA_CSSC0028", e);
0888: }
0889: throw new ConfigurationException(msg);
0890: }
0891:
0892: return;
0893: }
0894:
0895: /**
0896: * Removes a configuration from the DSAME service and attribute associated
0897: * with this ConfigurationFactory.
0898: * <p>Removal of a configuration will result in a notification of all
0899: * listeners registered with a Configuration object that was created
0900: * by this ConfigurationFactory for the specified configName.
0901: *
0902: * @param configName Name of the configuration to remove.
0903: * @param session Contains information which is presumed to be sufficient
0904: * to create an SSOToken, that is used to identify
0905: * the user on who's behalf the session is being processed.
0906: * @throws com.sun.ssoadapter.config.ConfigurationException
0907: */
0908: public void removeConfiguration(String configName,
0909: SSOAdapterSession session) throws ConfigurationException {
0910: SSOToken token;
0911: String tokenString;
0912: String hashKey = null;
0913:
0914: try {
0915: tokenString = session.getSessionID();
0916: hashKey = tokenString;
0917:
0918: loadCache(session, tokenString);
0919:
0920: Configuration tmpConfig;
0921:
0922: HashMap rawConfigHashMap = (HashMap) rawConfigCache
0923: .get(hashKey);
0924: if (rawConfigHashMap != null) {
0925: tmpConfig = (Configuration) rawConfigHashMap
0926: .get(configName);
0927: if (tmpConfig != null) {
0928: tmpConfig.setHashMap(tmpConfig.getHashMap()); // Force listener notification...
0929: rawConfigHashMap.remove(configName);
0930: }
0931: }
0932:
0933: HashMap mergedConfigHashMap = (HashMap) mergedConfigCache
0934: .get(hashKey);
0935: if (mergedConfigHashMap != null) {
0936: tmpConfig = (Configuration) mergedConfigHashMap
0937: .get(configName);
0938: if (tmpConfig != null) {
0939: tmpConfig.setHashMap(tmpConfig.getHashMap()); // Force listener notification...
0940: mergedConfigHashMap.remove(configName);
0941: }
0942: }
0943:
0944: writeHashMap(rawConfigHashMap, session);
0945:
0946: //
0947: // TODO: Again, must flush the cache, since we can't easily
0948: // determine if the config that was just removed was
0949: // truly "removeable". Configs which are created
0950: // w/o user values are not removeable, and this is
0951: // the easiest way to get them back in the cache.
0952: // Anybody who calls "loadCache()" after this will
0953: // cause the "unremoveable" config to be reloaded.
0954: //
0955: mergedConfigCache.remove(hashKey);
0956: rawConfigCache.remove(hashKey);
0957:
0958: } catch (Exception e) {
0959:
0960: if (hashKey != null) {
0961: mergedConfigCache.remove(hashKey);
0962: rawConfigCache.remove(hashKey);
0963: }
0964:
0965: String msg = this .getClass().getName()
0966: + ".removeConfiguration(): failed. Reason: "
0967: + e.toString();
0968: logger.log(Level.SEVERE, "PSSA_CSSC0031", e);
0969: throw new ConfigurationException(msg);
0970: }
0971: return;
0972: }
0973:
0974: private Set getMergedDynamicConfigurations(
0975: ClientAwareUserContext userContext, Map serviceMap,
0976: String rootAttributeName) throws IllegalStateException,
0977: IOException {
0978:
0979: if (userContext instanceof PSClientAwareUserContext) {
0980: return ((PSClientAwareUserContext) userContext)
0981: .getMergedDynamicConfigurations(serviceMap,
0982: rootAttributeName);
0983: }
0984: return userContext.getAttribute(serviceMap, rootAttributeName);
0985: }
0986:
0987: public void setMergeDynamicConfigurations(boolean flag) {
0988: this .mergeDynamicConfigurations = flag;
0989: }
0990:
0991: public boolean isMergeDynamicConfigurations() {
0992: return this .mergeDynamicConfigurations;
0993: }
0994:
0995: /**
0996: * Returns the keys of a HashMap as an enumeration.
0997: */
0998: private class HashMapKeyEnumeration implements Enumeration {
0999:
1000: Iterator hashIter = null;
1001:
1002: /**
1003: *
1004: * @param map
1005: */
1006: HashMapKeyEnumeration(TreeMap map) {
1007: hashIter = map.keySet().iterator();
1008: }
1009:
1010: /**
1011: *
1012: * @return
1013: */
1014: public boolean hasMoreElements() {
1015: return hashIter.hasNext();
1016: }
1017:
1018: /**
1019: *
1020: * @return
1021: */
1022: public Object nextElement() {
1023: return hashIter.next();
1024: }
1025: }
1026:
1027: /*
1028: * This class is used to purge the caches of per-session entries.
1029: */
1030: private class HashtableReaper implements SSOTokenListener {
1031:
1032: private String key;
1033: private Hashtable hashTable;
1034:
1035: /**
1036: *
1037: * @param key
1038: * @param hashTable
1039: */
1040: HashtableReaper(String key, Hashtable hashTable) {
1041: this .key = key;
1042: this .hashTable = hashTable;
1043: }
1044:
1045: /**
1046: * Implements SSOTokenListener "ssoTokenChanged" method.
1047: * @param evt
1048: */
1049: public void ssoTokenChanged(com.iplanet.sso.SSOTokenEvent evt) {
1050: try {
1051: int evtType = evt.getType();
1052:
1053: if (evtType != evt.SSO_TOKEN_DESTROY
1054: && evtType != evt.SSO_TOKEN_IDLE_TIMEOUT
1055: && evtType != evt.SSO_TOKEN_MAX_TIMEOUT) {
1056: return;
1057: }
1058:
1059: hashTable.remove(key);
1060:
1061: } catch (Exception e) {
1062: logger.log(Level.SEVERE, "PSSA_CSSC0032", e);
1063: }
1064: }
1065:
1066: }
1067:
1068: }
|