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