001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: ServiceConfig.java,v 1.18 2009/01/28 05:35:03 ww203982 Exp $
026 *
027 * Portions Copyrighted 2011-2016 ForgeRock AS.
028 * Portions Copyrighted 2012 Open Source Solution Technology Corporation
029 */
030package com.sun.identity.sm;
031
032import java.util.Collections;
033import java.util.HashSet;
034import java.util.Iterator;
035import java.util.Map;
036import java.util.Set;
037
038import org.forgerock.opendj.ldap.DN;
039
040import com.iplanet.services.util.AMEncryption;
041import com.iplanet.sso.SSOException;
042import com.iplanet.sso.SSOToken;
043import com.iplanet.ums.IUMSConstants;
044
045/**
046 * The class <code>ServiceConfig</code> provides interfaces to manage the
047 * configuration information of a service configuration. It provides methods to
048 * get and set configuration parameters for this service configuration.
049 *
050 * @supported.all.api
051 */
052public class ServiceConfig {
053    // Instance variables
054    private SSOToken token;
055
056    private ServiceConfigImpl sc;
057
058    private ServiceSchemaImpl ss;
059
060    private ServiceConfigManager scm;
061
062    /**
063     * Default constructor. Makes it private so that it can not be instantiated.
064     */
065    private ServiceConfig() {
066        // hence can not be instantiated
067    }
068
069    /**
070     * Protected constructor
071     */
072    protected ServiceConfig(ServiceConfigManager scm, ServiceConfigImpl sc)
073            throws SMSException, SSOException {
074        this.scm = scm;
075        token = scm.getSSOToken();
076        this.sc = sc;
077        this.ss = sc.getServiceSchemaImpl();
078    }
079
080    /**
081     * Returns the name of this service configuration.
082     * 
083     * @return the name of this service configuration
084     */
085    public String getServiceName() {
086        return (scm.getName());
087    }
088
089    /**
090     * Returns the service version
091     * 
092     * @return service version
093     */
094    public String getVersion() {
095        return (scm.getVersion());
096    }
097
098    /**
099     * Returns the service component name. It is "/" separated and the root
100     * component name is "/".
101     *
102     * @return service component name
103     */
104    public String getComponentName() {
105        validate();
106        return (sc.getComponentName());
107    }
108
109    /**
110     * Returns the service name.
111     *
112     * @return service name
113     */
114    public String getName() {
115        validate();
116        return sc.getName();
117    }
118
119    /**
120     * Returns the service component's schema ID. For global and organization's
121     * root configurations it returns an empty string.
122     * 
123     * @return service component's schema ID
124     */
125    public String getSchemaID() {
126        validate();
127        return (sc.getSchemaID());
128    }
129
130    /**
131     * Returns the priority assigned to the service configuration.
132     * 
133     * @return the priority assigned to the service configuration
134     */
135    public int getPriority() {
136        validate();
137        return (sc.getPriority());
138    }
139
140    /**
141     * Sets the priority to the service configuration.
142     * 
143     * @param priority
144     *            the priority to be assigned to the configuration
145     * @throws SMSException
146     *             if there is an error occurred while performing the operation
147     * @throws SSOException
148     *             if the user's single sign-on is invalid or expired
149     */
150    public void setPriority(int priority) throws SSOException, SMSException {
151        validateServiceConfigImpl();
152        StringBuilder sb = new StringBuilder(8);
153        String[] priorities = { sb.append(priority).toString() };
154        SMSEntry e = sc.getSMSEntry();
155        e.setAttribute(SMSEntry.ATTR_PRIORITY, priorities);
156        saveSMSEntry(e);
157    }
158
159    /**
160     * Returns the labeled uri assigned to the service configuration.
161     * 
162     * @return the labeled uri assigned to the service configuration
163     * @deprecated The labeledURI setting shall not be used for storing configuration data.
164     */
165    public String getLabeledUri() {
166        validate();
167        return (sc.getLabeledUri());
168    }
169
170    /**
171     * Sets the labeled uri to the service configuration.
172     * 
173     * @param luri the labeled uri to be assigned to the configuration
174     * @throws SMSException
175     *             if there is an error occurred while performing the operation
176     * @throws SSOException
177     *             if the user's single sign-on is invalid or expired
178     * @deprecated The labeledURI setting shall not be used for storing configuration data.
179     */
180    public void setLabeledUri(String luri) throws SSOException, SMSException {
181        validateServiceConfigImpl();
182        StringBuilder sb = new StringBuilder(8);
183        String[] lUris = { sb.append(luri).toString() };
184        SMSEntry e = sc.getSMSEntry();
185        e.setAttribute(SMSEntry.ATTR_LABELED_URI, lUris);
186        saveSMSEntry(e);
187    }
188
189    /**
190     * delete the labeled uri to the service configuration.
191     * 
192     * @param luri the labeled uri to be assigned to the configuration
193     * @throws SMSException
194     *             if there is an error occurred while performing the operation
195     * @throws SSOException
196     *             if the user's single sign-on is invalid or expired
197     * @deprecated The labeledURI setting shall not be used for storing configuration data.
198     */
199    public void deleteLabeledUri(String luri) throws SSOException, SMSException {
200        validateServiceConfigImpl();
201        SMSEntry e = sc.getSMSEntry();
202        sc.setLabeledUri(null);
203        e.removeAttribute(SMSEntry.ATTR_LABELED_URI, luri);
204        saveSMSEntry(e);
205    }
206
207    /**
208     * Returns the names of all service's sub-configurations.
209     * 
210     * @return set of names of all service's sub-configurations
211     * @throws SMSException
212     *             if there is an error accessing the data store
213     */
214    public Set<String> getSubConfigNames() throws SMSException {
215        validateServiceConfigImpl();
216        try {
217            return sc.getSubConfigNames(token);
218        } catch (SSOException s) {
219            SMSEntry.debug.error("ServiceConfig: Unable to "
220                    + "get subConfig Names", s);
221        }
222        return (Collections.EMPTY_SET);
223    }
224
225    /**
226     * Method to get names of service's sub-configurations that match the given
227     * pattern.
228     * 
229     * @param pattern
230     *            pattern to match for sub-configuration names
231     * @return names of the service sub-configuration
232     * @throws SMSException
233     *             if an error occurred while performing the operation.
234     */
235    public Set<String> getSubConfigNames(String pattern) throws SMSException {
236        validateServiceConfigImpl();
237        try {
238            return (sc.getSubConfigNames(token, pattern));
239        } catch (SSOException s) {
240            SMSEntry.debug.error("ServiceConfigManager: Unable to "
241                    + "get subConfig Names for filter: " + pattern, s);
242        }
243        return Collections.emptySet();
244
245    }
246
247    /**
248     * Method to get names of service's sub-configurations that match the given
249     * pattern and belongs to the specified service schema name.
250     * 
251     * @param pattern
252     *            pattern to match for other entities.
253     * @param schemaName
254     *            service schema name.
255     * @return names of the service sub-configuration
256     * @throws SMSException
257     *             if an error occurred while performing the operation.
258     */
259    public Set getSubConfigNames(String pattern, String schemaName)
260            throws SMSException {
261        validateServiceConfigImpl();
262        try {
263            return (sc.getSubConfigNames(token, pattern, schemaName));
264        } catch (SSOException s) {
265            SMSEntry.debug.error("ServiceConfigManager: Unable to "
266                    + "get subConfig Names for filters: " + pattern + "AND"
267                    + schemaName, s);
268        }
269        return (Collections.EMPTY_SET);
270
271    }
272
273    /**
274     * Returns a set of exported fully qualified sub-configuration names that
275     * can be imported used locally as service configuration
276     * 
277     * @param serviceId
278     *            service schema identifier
279     * @return names of fully qualified applicable service sub-configurations
280     * @throws SMSException
281     *             if an error occurred while performing the operation.
282     */
283    public Set getExportedSubConfigNames(String serviceId) throws SMSException {
284        return (null);
285    }
286
287    /**
288     * Returns the service's sub-configuration given the service's
289     * sub-configuration name.
290     * 
291     * @param subConfigName
292     *            The name of the service's sub-configuration to retrieve.
293     * @return The <code>ServiceConfig</code> object corresponding to the
294     *         specified name of the service's sub-configuration.
295     * @throws SMSException
296     *             if there is an error occurred while performing the operation
297     * @throws SSOException
298     *             if the user's single sign-on is invalid or expired
299     */
300    public ServiceConfig getSubConfig(String subConfigName)
301            throws SSOException, SMSException {
302        ServiceConfigImpl sci = sc.getSubConfig(token, subConfigName);
303        return ((sci == null) ? null : new ServiceConfig(scm, sci));
304    }
305
306    /**
307     * Adds a service sub-configuration with configuration parameters.
308     * 
309     * @param subConfigName
310     *            the name of service sub-configuration to add
311     * @param subConfigId
312     *            type of service sub-configuration
313     * @param priority
314     *            the priority of the configuration
315     * @param attrs
316     *            configuration parameters for the sub-configuration
317     * @throws SMSException
318     *             if there is an error occurred while performing the operation
319     * @throws SSOException
320     *             if the user's single sign-on is invalid or expired
321     */
322    public void addSubConfig(String subConfigName, String subConfigId,
323            int priority, Map attrs) throws SMSException, SSOException {
324        validateServiceConfigImpl();
325        // Check if this entry exists
326        if (sc.isNewEntry()) {
327            // Ideally these nodes should have been created, since they
328            // are not present we need to create them
329            scm.createOrganizationConfig(sc.getOrganizationName(), null);
330            // Check if rest of the component names are present
331            checkAndCreateComponents(sc.getDN());
332        }
333
334        // Get service schemas
335        String subSchemaIdentifier = subConfigId == null ? subConfigName : subConfigId;
336        ServiceSchemaImpl nss = ss.getSubSchema(subSchemaIdentifier);
337
338        if (nss == null) {
339            String[] args = { subSchemaIdentifier };
340            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
341                    "sms-invalid-add-sub-config-unknown-schema-name", args));
342        }
343
344        if (!nss.supportsMultipleConfigurations()
345                && !getSubConfigNames().isEmpty()) {
346            String[] args = { subConfigName };
347            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
348                    "sms-invalid-add-sub-config", args));
349        }
350
351        // Convert priority to string
352        StringBuilder sb = new StringBuilder(8);
353        sb.append(priority);
354
355        // Create the entry
356        CreateServiceConfig.createSubConfigEntry(token, ("ou=" + subConfigName
357                + "," + sc.getDN()), nss, subConfigId, sb.toString(),
358                SMSUtils.copyAttributes(attrs), sc.getOrganizationName());
359    }
360
361    /**
362     * Removes the service sub-configuration.
363     * 
364     * @param subConfigName
365     *            name of service sub-configuration to remove
366     * @throws SMSException
367     *             if there is an error occurred while performing the operation
368     * @throws SSOException
369     *             if the user's single sign-on is invalid or expired
370     */
371    public void removeSubConfig(String subConfigName) throws SMSException,
372            SSOException {
373        validateServiceConfigImpl();
374        // Obtain the SMSEntry for the subconfig and delete it
375        // unescape in case users provide such a subConfigName for deletion.
376        // "http:&amp;#47;&amp;#47;abc.east.sun.com:58080"
377
378        subConfigName = SMSSchema.unescapeName(subConfigName);
379
380        // First remove the entry from ServiceConfigImpl Cache.
381
382        // Construct subconfig DN
383        String sdn = "ou=" + subConfigName + "," + sc.getDN();
384
385        // Construct ServiceConfigManagerImpl
386        ServiceConfigManagerImpl scmImpl = ServiceConfigManagerImpl.
387            getInstance(token, getServiceName(), getVersion());
388
389        // Construct ServiceConfigImpl of the removed subconfig.
390        ServiceConfigImpl sConfigImpl =
391            sc.getSubConfig(token, subConfigName);
392        
393        // Call ServiceConfigImpl's deleteInstance() to remove from cache.
394        if (sConfigImpl != null) {
395            ServiceConfigImpl.deleteInstance(token, scmImpl, null, sdn, "/", 
396                sConfigImpl.getGroupName(), (getComponentName() + "/" 
397                + SMSSchema.escapeSpecialCharacters(subConfigName)), false, 
398                ss);
399        }
400        // Remove this entry from smsentry.
401        CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token, sdn);
402        if (cEntry.isDirty()) {
403            cEntry.refresh();
404        }
405        SMSEntry entry = cEntry.getClonedSMSEntry();
406        entry.delete(token);
407        cEntry.refresh(entry);
408
409        // Remove the entry from CachedSubEntries
410        CachedSubEntries cse = CachedSubEntries.getInstance(token, sc.getDN());
411        cse.remove(subConfigName);
412    }
413
414    /**
415     * Imports a service sub-configuration to the list of localy defined
416     * sub-configuration. The imported sub-configuration name must be fully
417     * qualified, as obtained from <code>getExportedSubConfigNames</code>.
418     * 
419     * @param subConfigName
420     *            the name of service sub-configuration to add locally
421     * @param exportedSubConfigName
422     *            the fully qualified name of the exported sub-configuration
423     *            name
424     * @throws SMSException
425     *             if there is an error occurred while performing the operation
426     * @throws SSOException
427     *             if the user's single sign-on is invalid or expired
428     */
429    public void importSubConfig(String subConfigName,
430            String exportedSubConfigName) throws SMSException, SSOException {
431    }
432
433    /**
434     * Returns the service configuration parameters. The keys in the
435     * <code>Map</code> contains the attribute names and their corresponding
436     * values in the <code>Map</code> is a <code>Set</code> that contains
437     * the values for the attribute. This method picks up the default values for
438     * any attributes not defined in the <code>ServiceConfig</code>. The
439     * default values for these attributes are picked up from the Service
440     * Schema. If there is no default value defined, then this method will still
441     * return the attribute-value pair, except that the Set will be a
442     * Collections.EMPTY_SET. This is distinct from an empty Set with no entries
443     * in it. AN empty set represents an attribute whose value has been set to
444     * an empty value by the application using the <code>setAttributes()</code>
445     * method.
446     * 
447     * @return the <code>Map</code> where key is the attribute name and value
448     *         is the <code>Set</code> of attribute values
449     */
450    public Map getAttributes() {
451        validate();
452        return (sc.getAttributes());
453    }
454
455        /**
456     * Returns the service configuration parameters for read only.
457     * The keys in the <code>Map</code> contains the attribute names and
458     * their corresponding values in the <code>Map</code> is a
459     * <code>Set</code> that contains the values for the attribute.
460     */
461
462    /**
463     * Returns the service configuration parameters without inheriting the
464     * default values from service's schema. The keys in the <code>Map</code>
465     * contains the attribute names and their corresponding values in the
466     * <code>Map</code> is a <code>Set</code> that contains the values for
467     * the attribute.
468     */
469    public Map getAttributesWithoutDefaults() {
470        validate();
471        return (sc.getAttributesWithoutDefaults());
472    }
473    
474    /**
475     * Returns the service configuration parameters for read only,
476     * modification cannot be performed on the return <code>Map</code>.
477     * The keys in the
478     * <code>Map</code> contains the attribute names and their
479     * corresponding values in the <code>Map</code> is a
480     * <code>Set</code> that contains the values for the attribute.
481     * This method picks up the default values for any attributes
482     * not defined in the <code>ServiceConfig</code>. The default values for
483     * these attributes are picked up from the Service Schema.
484     * If there is no default value defined, then this method
485     * will still return the attribute-value pair, except that
486     * the Set will be a Collections.EMPTY_SET.
487     * This is distinct from an empty Set with no entries in it.
488     * AN empty set represents an attribute whose value has
489     * been set to an empty value by the application using
490     * the <code>setAttributes()</code> method.
491     * 
492     * @return the <code>Map</code> where key is the attribute name
493     *   and value is the <code>Set</code> of attribute values
494     */
495    public Map getAttributesForRead() {
496        validate();
497        return (sc.getAttributesForRead());
498    }
499
500    /**
501     * Returns the service configuration parameters for read only without
502     * inheriting the default values from service's schema. The keys
503     * in the  <code>Map</code> contains the attribute names and their
504     * corresponding values in the <code>Map</code> is a
505     * <code>Set</code> that contains the values for the attribute.
506     */
507    public Map getAttributesWithoutDefaultsForRead() {
508        validate();
509        return (sc.getAttributesWithoutDefaultsForRead());
510    }
511
512    /**
513     * Sets the service configuration parameters. The keys in the
514     * <code>Map</code> contains the attribute names and their corresponding
515     * values in the <code>Map</code> is a <code>Set</code> that contains
516     * the values for the attribute. This method will replace the existing
517     * attribute values with the given one. For attributes that are not
518     * specified in <code>attrs</code>, it will not be modified.
519     * 
520     * @param attrs
521     *            the <code>Map</code> where key is the attribute name and
522     *            value is the <code>Set</code> of attribute values
523     * @throws SMSException
524     *             if there is an error occurred while performing the operation
525     * @throws SSOException
526     *             if the user's single sign-on is invalid or expired
527     */
528    public void setAttributes(Map attrs) throws SMSException, SSOException {
529        validateServiceConfigImpl();
530        Map oldAttrs = sc.getAttributesWithoutDefaults();
531        Iterator it = oldAttrs.keySet().iterator();
532        Map newAttrs = SMSUtils.copyAttributes(attrs);
533        while (it.hasNext()) {
534            String s = (String) it.next();
535            if (!newAttrs.containsKey(s)) {
536                newAttrs.put(s, oldAttrs.get(s));
537            }
538        }
539        /*
540         * For validation using ChoiceValues plugin we need to pass in
541         * OrganizationName, since the plugins use organization names to compute
542         * the choice values
543         */
544        ss.validateAttributes(token, newAttrs, true, sc.getOrganizationName());
545        SMSEntry e = sc.getSMSEntry();
546        SMSUtils.setAttributeValuePairs(e, newAttrs, ss
547                .getSearchableAttributeNames());
548        saveSMSEntry(e);
549    }
550
551    /**
552     * Adds a configuration parameter to the service configuration.
553     * 
554     * @param attrName
555     *            the name of the attribute to add
556     * @param values
557     *            the set of values to add
558     * @throws SMSException
559     *             if there is an error occurred while performing the operation
560     * @throws SSOException
561     *             if the user's single sign-on is invalid or expired
562     */
563    public void addAttribute(String attrName, Set values) throws SMSException,
564            SSOException {
565        validateServiceConfigImpl();
566        // Get current attributes
567        Map attributes = getAttributes();
568        // Validate attribute values
569        Set newVals = values;
570        Set oldVals = (Set) attributes.get(attrName);
571        if (oldVals != null) {
572            newVals = new HashSet();
573            newVals.addAll(values);
574            newVals.addAll(oldVals);
575        }
576        ss
577                .validateAttrValues(token, attrName, newVals, true, sc
578                        .getOrganizationName());
579        // Store the entry
580        SMSEntry e = sc.getSMSEntry();
581        SMSUtils.addAttribute(e, attrName, values, ss
582                .getSearchableAttributeNames());
583        saveSMSEntry(e);
584    }
585
586    /**
587     * Removes a configuration parameter from the service configuration.
588     * 
589     * @param attrName
590     *            the name of the attribute to remove
591     * @throws SMSException
592     *             if there is an error occurred while performing the operation
593     * @throws SSOException
594     *             if the user's single sign-on is invalid or expired
595     */
596    public void removeAttribute(String attrName) throws SMSException,
597            SSOException {
598        validateServiceConfigImpl();
599        SMSEntry e = sc.getSMSEntry();
600        SMSUtils.removeAttribute(e, attrName);
601        saveSMSEntry(e);
602    }
603
604    /**
605     * Removes a configuration parameters from the service configuration.
606     * 
607     * @param attrNames
608     *            <code>Set</code> of attribute names to remove
609     * @throws SMSException
610     *             if there is an error occurred while performing the operation
611     * @throws SSOException
612     *             if the user's single sign-on is invalid or expired
613     */
614    public void removeAttributes(Set attrNames) throws SMSException,
615            SSOException {
616        validateServiceConfigImpl();
617        SMSEntry e = sc.getSMSEntry();
618        if (attrNames != null && !attrNames.isEmpty()) {
619            for (Iterator items = attrNames.iterator(); items.hasNext();) {
620                SMSUtils.removeAttribute(e, (String) items.next());
621            }
622            saveSMSEntry(e);
623        }
624    }
625
626    /**
627     * Removes the specific values for the given configuration parameter.
628     * 
629     * @param attrName
630     *            the name of the attribute
631     * @param values
632     *            set of attribute values to remove from the given attribute
633     * @throws SMSException
634     *             if there is an error occurred while performing the operation
635     * @throws SSOException
636     *             if the user's single sign-on is invalid or expired
637     */
638    public void removeAttributeValues(String attrName, Set values)
639            throws SMSException, SSOException {
640        validateServiceConfigImpl();
641        SMSEntry e = sc.getSMSEntry();
642        SMSUtils.removeAttributeValues(e, attrName, values, ss
643                .getSearchableAttributeNames());
644        saveSMSEntry(e);
645    }
646
647    /**
648     * Replaces old value of the configuration parameter with new value.
649     * 
650     * @param attrName
651     *            the name of the attribute
652     * @param oldValue
653     *            the old value to remove from the attribute
654     * @param newValue
655     *            the new value to add to the attribute
656     * @throws SMSException
657     *             if there is an error occurred while performing the operation
658     * @throws SSOException
659     *             if the user's single sign-on is invalid or expired
660     */
661    public void replaceAttributeValue(String attrName, String oldValue,
662            String newValue) throws SMSException, SSOException {
663        validateServiceConfigImpl();
664        // Get current attributes
665        Map attributes = getAttributes();
666        // Validate values
667
668        Set currentValues = (Set) attributes.get(attrName);
669        if (currentValues != null && !currentValues.contains(oldValue)) {
670            throw (new SMSException("Current value doesn't match supplied value",
671                    "sms-INVALID_PARAMETERS"));
672        }
673
674        Set newVals = new HashSet();
675        Set oldVals = (Set) attributes.get(attrName);
676        if (oldVals != null) {
677            newVals.addAll(oldVals);
678            newVals.remove(oldValue);
679        }
680        newVals.add(newValue);
681        ss
682                .validateAttrValues(token, attrName, newVals, true, sc
683                        .getOrganizationName());
684        // Store the entry
685        SMSEntry e = sc.getSMSEntry();
686        SMSUtils.replaceAttributeValue(e, attrName, oldValue, newValue, ss
687                .getSearchableAttributeNames());
688        saveSMSEntry(e);
689    }
690
691    /**
692     * Replaces the old values of the configuration parameter with the new
693     * values.
694     * 
695     * @param attrName
696     *            the name of the attribute
697     * @param oldValues
698     *            the set of old values to remove from the attribute
699     * @param newValues
700     *            the set of new values to add to the attribute
701     * @throws SMSException
702     *             if there is an error occurred while performing the operation
703     * @throws SSOException
704     *             if the user's single sign-on is invalid or expired
705     */
706    public void replaceAttributeValues(String attrName, Set oldValues,
707            Set newValues) throws SMSException, SSOException {
708        validateServiceConfigImpl();
709        // Get current attributes
710        Map attributes = getAttributes();
711        // Validate values
712        Set newVals = new HashSet();
713        Set oldVals = (Set) attributes.get(attrName);
714        if (oldVals != null) {
715            newVals.addAll(oldVals);
716            newVals.removeAll(oldValues);
717        }
718        newVals.addAll(newValues);
719        ss.validateAttrValues(token, attrName, newVals, true,
720            sc.getOrganizationName());
721        // Store the entry
722        SMSEntry e = sc.getSMSEntry();
723        SMSUtils.replaceAttributeValues(e, attrName, oldValues, newValues, ss
724                .getSearchableAttributeNames());
725        saveSMSEntry(e);
726    }
727
728    /**
729     * Returns the LDAP DN represented by this <code>ServiceConfig</code>
730     * object.
731     * 
732     * @return the LDAP DN represented by this <code>ServiceConfig</code>
733     *         object.
734     */
735    public String getDN() {
736        validate();
737        return (sc.getDN());
738    }
739
740    /**
741     * Returns the last modified time stamp of this configuration This method is
742     * expensive because it does not cache the modified time stamp but goes
743     * directly to the data store to obtain the value of this entry
744     * 
745     * @return The last modified time stamp as a string with the format of
746     *         <code> yyyyMMddhhmmss </code>
747     * @throws SMSException
748     *             if there is an error trying to read from the data store
749     * @throws SSOException
750     *             if the single sign-on token of the user is invalid.
751     */
752
753    public String getLastModifiedTime() throws SMSException, SSOException {
754        validateServiceConfigImpl();
755        SMSEntry e = sc.getSMSEntry();
756        String vals[] = e.getAttributeValues(SMSEntry.ATTR_MODIFY_TIMESTAMP,
757                true);
758        String mTS = null;
759        if (vals != null) {
760            mTS = vals[0];
761        }
762        return mTS;
763    }
764
765    /**
766     * Returns the organization names to which the service configuration is
767     * being exported. The organization names would be fully qualified starting
768     * with a forward slash "/". To specify an entire sub-tree that can use the
769     * service configuration, a "*" would have to be appended after the final
770     * forward slash. For example "/a/b/c/*" would imply all sub-organization
771     * under "/a/b/c" can use this service configuration. Exporting implies
772     * privileges to read the service configuration data, but not to modify or
773     * delete.
774     * 
775     * @return names of organizations to which service configuration
776     *         configuration is exported
777     */
778    public Set getExportedOrganizationNames() {
779        return (null);
780    }
781
782    /**
783     * Sets the organization names that can import the service configuration.
784     * The organization names must be fully qualified, starting with a forward
785     * slash "/". To specify an entire sub-tree that can use the service
786     * configuration, a "*" would have to be appended after the final forward
787     * slash. For example "/a/b/c/*" would imply all sub-organization under
788     * "/a/b/c" can use this service configuration. Exporting implies privileges
789     * to read the service configuration data and not to modify or delete.
790     * 
791     * @param names
792     *            names of the organizations that can import the service
793     *            configuration
794     */
795    public void setExportedOrganizationNames(Set names) throws SMSException,
796            SSOException {
797    }
798
799    /**
800     * Adds the organization names to the list of organization names that can
801     * import this service configutation. If one does not exist it will be
802     * created. The organization names must be fully qualified, starting with a
803     * forward slash "/". To specify an entire sub-tree that can use the service
804     * configuration, a "*" would have to be appended after the final forward
805     * slash. For example "/a/b/c/*" would imply all sub-organization under
806     * "/a/b/c" can use this service configuration. Exporting implies privileges
807     * to read the service configuration data and not to modify or delete.
808     * 
809     * @param names
810     *            names of the organizations that can import the service
811     *            configuration
812     */
813    public void addExportedOrganizationNames(Set names) throws SMSException,
814            SSOException {
815    }
816
817    /**
818     * Removes the organization names from the list of organization names that
819     * can import the service configuration. If the organization has already
820     * imported the service configutation, it would have to be undone before the
821     * organization name can be removed from the list. The organization names
822     * must be fully qualified, starting with a forward slash "/". To specify an
823     * entire sub-tree that can use the service configuration, a "*" would have
824     * to be appended after the final forward slash. For example "/a/b/c/*"
825     * would imply all sub-organization under "/a/b/c" can use this service
826     * configuration.
827     * 
828     * @param names
829     *            names of the organizations that will be removed from the list
830     *            of organization names that can import the service
831     *            configutation
832     */
833    public void removeSharedOrganizationNames(Set names) throws SMSException,
834            SSOException {
835    }
836
837    /**
838     * Returns String representation of the <code>ServiceConfig</code> object.
839     * It returns attributes defined and sub configurations.
840     * 
841     * @return String representation of the <code>ServiceConfig</code> object.
842     */
843    public String toString() {
844        StringBuilder sb = new StringBuilder();
845        // Print the attributes
846        sb.append("Service Component name: " + getComponentName());
847        sb.append("\n\tAttributes: " + getAttributes()).append("\n");
848
849        // Try sub-configs
850        try {
851            Iterator subConfigNames = getSubConfigNames().iterator();
852            while (subConfigNames.hasNext()) {
853                ServiceConfig ssc = getSubConfig((String)subConfigNames.next());
854                sb.append(ssc);
855            }
856        } catch (Exception e) {
857            sb.append(e.getMessage());
858        }
859        return (sb.toString());
860    }
861
862    // Protected methods
863    void saveSMSEntry(SMSEntry e) throws SMSException, SSOException {
864        if (e.isNewEntry()) {
865            // Check if base nodes exists
866            CreateServiceConfig.checkBaseNodesForOrg(token, DNMapper
867                    .orgNameToDN(sc.getOrganizationName()), getServiceName(),
868                    getVersion());
869            // Check if parent DN is present
870            String parentDN = DN.valueOf(e.getDN()).parent().toString();
871            checkAndCreateComponents(parentDN);
872            // Add object classses to this entry
873            e.addAttribute(SMSEntry.ATTR_OBJECTCLASS, SMSEntry.OC_TOP);
874            e.addAttribute(SMSEntry.ATTR_OBJECTCLASS, SMSEntry.OC_SERVICE_COMP);
875        }
876        e.save(token);
877        sc.refresh(e);
878    }
879
880    public void checkAndCreateGroup(String dn, String groupName) 
881        throws SMSException, SSOException {
882
883        CachedSMSEntry entry = CachedSMSEntry.getInstance(token, dn);
884        if (entry.isDirty()) {
885            entry.refresh();
886        }
887        if (entry.isNewEntry()) {
888            // Check if parent exisits
889            String pDN = DN.valueOf(dn).parent().toString();
890            CachedSMSEntry pEntry = CachedSMSEntry.getInstance(token, pDN);
891            if (pEntry.isDirty()) {
892                pEntry.refresh();
893            }
894            if (pEntry.isNewEntry()) {
895                checkAndCreateComponents(pDN);
896            }
897            // Create this entry
898            SMSEntry e = entry.getClonedSMSEntry();
899            e.addAttribute(SMSEntry.ATTR_OBJECTCLASS, SMSEntry.OC_TOP);
900            e.addAttribute(SMSEntry.ATTR_OBJECTCLASS,SMSEntry.OC_SERVICE_COMP);
901            e.addAttribute(SMSEntry.ATTR_SERVICE_ID, groupName);
902            e.save(token);
903            entry.refresh(e);
904        }
905    }
906
907    void checkAndCreateComponents(String dn) throws SMSException, SSOException {
908        CachedSMSEntry entry = CachedSMSEntry.getInstance(token, dn);
909        if (entry.isDirty()) {
910            entry.refresh();
911        }
912        if (entry.isNewEntry()) {
913            // Check if parent exisits
914            String pDN = DN.valueOf(dn).parent().toString();
915            CachedSMSEntry pEntry = CachedSMSEntry.getInstance(token, pDN);
916            if (pEntry.isDirty()) {
917                pEntry.refresh();
918            }
919            if (pEntry.isNewEntry()) {
920                checkAndCreateComponents(pDN);
921            }
922            // Create this entry
923            SMSEntry e = entry.getClonedSMSEntry();
924            e.addAttribute(SMSEntry.ATTR_OBJECTCLASS, SMSEntry.OC_TOP);
925            e.addAttribute(SMSEntry.ATTR_OBJECTCLASS, SMSEntry.OC_SERVICE_COMP);
926            e.save(token);
927            entry.refresh(e);
928        }
929    }
930    
931    private void validate() {
932        try {
933            validateServiceConfigImpl();
934        } catch (SMSException e) {
935            // Ignore the exception
936        }
937    }
938    
939    private void validateServiceConfigImpl() throws SMSException {
940        if (!sc.isValid()) {
941            throw (new SMSException("service-config: " + sc.getDN() +
942                " No longer valid. Cache has been cleared. Recreate from" +
943                "ServiceConfigManager"));
944        }
945    }
946    
947    /**
948     * Returns the status of this Service Configuration Object.
949     * Must be used by classes that cache ServiceConfig.
950     * 
951     * @return <code>true</code> if this object is still valid.
952     */
953    public boolean isValid() {
954        return (sc.isValid());
955    }
956    
957    /**
958     * Returns <code>true</code> if the entry exist
959     */
960    public boolean exists() {
961        return (!sc.isNewEntry());
962    }
963    
964    public String toXML(String NodeTag, AMEncryption encryptObj)
965        throws SMSException, SSOException {
966        validateServiceConfigImpl();
967        return sc.toXML(token, NodeTag, encryptObj);
968    }
969
970    public String toXML(String NodeTag, AMEncryption encryptObj, String orgName)
971        throws SMSException, SSOException {
972        validateServiceConfigImpl();
973        return sc.toXML(token, NodeTag, encryptObj, orgName);
974    }
975}