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: ServiceSchemaManager.java,v 1.12 2009/07/25 05:11:55 qcheng Exp $
026 *
027 */
028
029/*
030 * Portions Copyrighted 2012 ForgeRock Inc
031 */
032package com.sun.identity.sm;
033
034import com.iplanet.services.util.AMEncryption;
035import com.iplanet.sso.SSOException;
036import com.iplanet.sso.SSOToken;
037import com.iplanet.ums.IUMSConstants;
038import com.sun.identity.shared.debug.Debug;
039import com.sun.identity.shared.xml.XMLUtils;
040import java.io.IOException;
041import java.io.InputStream;
042import java.util.Collections;
043import java.util.HashSet;
044import java.util.Iterator;
045import java.util.Set;
046import org.w3c.dom.Document;
047import org.w3c.dom.Node;
048
049/**
050 * The class <code>ServiceSchemaManager</code> provides interfaces to manage
051 * the service's schema. It provides access to <code>ServiceSchema</code>,
052 * which represents a single "schema" in the service.
053 *
054 * @supported.api
055 */
056public class ServiceSchemaManager {
057    
058    private SSOToken token;
059    
060    private String serviceName;
061    
062    private String version;
063    
064    private ServiceSchemaManagerImpl ssm;
065    
066    private static Debug debug = Debug.getInstance("amSMS");
067    
068    /**
069     * Constructor for service's schema manager to manage the attributes and
070     * sub configurations. Assumes service version number to be <class>1.0
071     * </class>.
072     *
073     * @throws SMSException
074     *             if an error occurred while trying to perform the operation
075     * @throws SSOException
076     *             if the single sign on token is invalid or expired
077     */
078    public ServiceSchemaManager(String serviceName, SSOToken token)
079    throws SMSException, SSOException {
080        this(token, serviceName, ServiceManager.isCoexistenceMode() ?
081            ServiceManager.serviceDefaultVersion(token, serviceName) :
082            ServiceManager.getVersion(serviceName));
083    }
084    
085    /**
086     * Creates an instance of
087     * <code>ServiceSchemaManager</code> for the given service and version
088     * pair. It requires an user identity, that will used to perform operations
089     * with. It is assumed that the application calling this constructor should
090     * authenticate the user.
091     *
092     * @param token
093     *            single sign on token of the user identity on whose behalf the
094     *            operations are performed.
095     * @param serviceName
096     *            the name of the service.
097     * @param version
098     *            the version of the service.
099     * @throws SMSException
100     *             if an error occurred while trying to perform the operation
101     * @throws SSOException
102     *             if the single sign on token is invalid or expired
103     *
104     * @supported.api
105     */
106    public ServiceSchemaManager(SSOToken token, String serviceName,
107        String version) throws SMSException, SSOException {
108        if (token == null || serviceName == null || version == null) {
109            throw new IllegalArgumentException(SMSEntry.bundle
110                .getString(IUMSConstants.SMS_INVALID_PARAMETERS));
111        }
112        SMSEntry.validateToken(token);
113        this.token = token;
114        this.serviceName = serviceName;
115        this.version = version;
116        ssm = ServiceSchemaManagerImpl.getInstance(token, serviceName, version);
117    }
118    
119    /**
120     * Returns the name of the service.
121     *
122     * @return the name of the service
123     *
124     * @supported.api
125     */
126    public String getName() {
127        return (serviceName);
128    }
129    
130    /**
131     * Returns the version of the service.
132     *
133     * @return the version of the service
134     *
135     * @supported.api
136     */
137    public String getVersion() {
138        return (version);
139    }
140    
141    /**
142     * Returns the I18N properties file name for the
143     * service.
144     *
145     * @return the I18N properties file name for the service
146     *
147     * @supported.api
148     */
149    public String getI18NFileName() {
150        validate();
151        return (ssm.getI18NFileName());
152    }
153    
154    /**
155     * Sets the I18N properties file name for the service
156     *
157     * @param url
158     *            properties file name
159     * @throws SMSException
160     *             if an error occurred while trying to perform the operation
161     * @throws SSOException
162     *             if the single sign on token is invalid or expired
163     *
164     * @supported.api
165     */
166    public void setI18NFileName(String url) throws SMSException, SSOException {
167        SMSEntry.validateToken(token);
168        validateServiceSchemaManagerImpl();
169        String tmpS = ssm.getI18NFileName();
170        ssm.setI18NFileName(url);
171        try {
172            replaceSchema(ssm.getDocument());
173        } catch (SMSException se) {
174            ssm.setI18NFileName(tmpS);
175            throw se;
176        }
177    }
178    
179    /**
180     * Returns the URL of the JAR file that contains the
181     * I18N properties file. The method could return null, in which case the
182     * properties file should be in <code>CLASSPATH</code>.
183     *
184     * @return the URL of the JAR file containing the <code>I18N</code>
185     *         properties file.
186     *
187     * @supported.api
188     */
189    public String getI18NJarURL() {
190        validate();
191        return (ssm.getI18NJarURL());
192    }
193    
194    /**
195     * Sets the URL of the JAR file that contains the I18N
196     * properties
197     *
198     * @param url
199     *            URL
200     * @throws SMSException
201     *             if an error occurred while trying to perform the operation
202     * @throws SSOException
203     *             if the single sign on token is invalid or expired
204     *
205     * @supported.api
206     */
207    
208    public void setI18NJarURL(String url) throws SMSException, SSOException {
209        SMSEntry.validateToken(token);
210        validateServiceSchemaManagerImpl();
211        String tmpS = ssm.getI18NJarURL();
212        ssm.setI18NJarURL(url);
213        try {
214            replaceSchema(ssm.getDocument());
215        } catch (SMSException se) {
216            ssm.setI18NJarURL(tmpS);
217            throw se;
218        }
219    }
220    
221    /**
222     * Returns the service's hierarchy.
223     *
224     * @return service hierarchy in slash format.
225     *
226     * @supported.api
227     */
228    public String getServiceHierarchy() {
229        validate();
230        return (ssm.getServiceHierarchy());
231    }
232    
233    /**
234     * Sets the service's hierarchy
235     *
236     * @param newhierarchy
237     *            service hierarchy
238     * @throws SMSException
239     *             if an error occurred while trying to perform the operation
240     * @throws SSOException
241     *             if the single sign on token is invalid or expired
242     *
243     * @supported.api
244     */
245    public void setServiceHierarchy(String newhierarchy) throws SMSException,
246        SSOException {
247        SMSEntry.validateToken(token);
248        validateServiceSchemaManagerImpl();
249        String tmpS = getServiceHierarchy();
250        ssm.setServiceHierarchy(newhierarchy);
251        try {
252            replaceSchema(ssm.getDocument());
253        } catch (SMSException e) {
254            ssm.setServiceHierarchy(tmpS);
255            throw e;
256        }
257    }
258    
259    /**
260     * Returns i18nKey of the schema.
261     *
262     * @return i18nKey of the schema.
263     *
264     * @supported.api
265     */
266    public String getI18NKey() {
267        validate();
268        return (ssm.getI18NKey());
269    }
270    
271    /**
272     * Sets the i18nKey of the schema.
273     *
274     * @param i18nKey
275     *            <code>i18nKey</code> of the schema.
276     * @throws SMSException
277     *             if an error occurred while trying to perform the operation.
278     * @throws SSOException
279     *             if the single sign on token is invalid or expired.
280     *
281     * @supported.api
282     */
283    public void setI18NKey(String i18nKey) throws SMSException, SSOException {
284        SMSEntry.validateToken(token);
285        validateServiceSchemaManagerImpl();
286        String tmp = ssm.getI18NKey();
287        ssm.setI18NKey(i18nKey);
288        
289        try {
290            replaceSchema(ssm.getDocument());
291        } catch (SMSException e) {
292            ssm.setI18NKey(tmp);
293            throw e;
294        }
295    }
296    
297    /**
298     * Returns URL of the view bean for the service
299     *
300     * @return URL for view bean
301     *
302     * @supported.api
303     */
304    public String getPropertiesViewBeanURL() {
305        validate();
306        return (ssm.getPropertiesViewBeanURL());
307    }
308    
309    /**
310     * Sets the URL of the view bean for the service.
311     *
312     * @param url
313     *            of the view bean for the service.
314     * @throws SMSException
315     *             if an error occurred while trying to perform the operation.
316     * @throws SSOException
317     *             if the single sign on token is invalid or expired.
318     *
319     * @supported.api
320     */
321    public void setPropertiesViewBeanURL(String url) throws SMSException,
322        SSOException {
323        SMSEntry.validateToken(token);
324        validateServiceSchemaManagerImpl();
325        String tmpS = ssm.getPropertiesViewBeanURL();
326        ssm.setPropertiesViewBeanURL(url);
327        try {
328            replaceSchema(ssm.getDocument());
329        } catch (SMSException e) {
330            ssm.setPropertiesViewBeanURL(tmpS);
331            throw e;
332        }
333    }
334    
335    /**
336     * iPlanet_PUBLIC-METHOD Returns the revision number of the service schema.
337     *
338     * @return the revision number of the service schema
339     */
340    public int getRevisionNumber() {
341        validate();
342        return (ssm.getRevisionNumber());
343    }
344    
345    /**
346     * iPlanet_PUBLIC-METHOD Sets the revision number for the service schema.
347     *
348     * @param revisionNumber
349     *            revision number of the service schema.
350     * @throws SMSException
351     *             if there is a problem setting the value in the data store.
352     * @throws SSOException
353     *             If the user has an invalid SSO token.
354     */
355    public void setRevisionNumber(int revisionNumber) throws SMSException,
356        SSOException {
357        SMSEntry.validateToken(token);
358        validateServiceSchemaManagerImpl();
359        int tmpS = ssm.getRevisionNumber();
360        ssm.setRevisionNumber(revisionNumber);
361        try {
362            replaceSchema(ssm.getDocument());
363        } catch (SMSException e) {
364            ssm.setRevisionNumber(tmpS);
365            throw (e);
366        }
367    }
368    
369    /**
370     * Returns the schema types available with this
371     * service.
372     *
373     * @return set of <code>SchemaTypes</code> in this service.
374     * @throws SMSException
375     *             if an error occurred while trying to perform the operation
376     *
377     * @supported.api
378     */
379    public Set getSchemaTypes() throws SMSException {
380        SMSEntry.validateToken(token);
381        validate();
382        return (ssm.getSchemaTypes());
383    }
384    
385    /**
386     * Returns the configuration schema for the given
387     * schema type
388     *
389     * @param type
390     *            schema type.
391     * @return service schema.
392     * @throws SMSException
393     *             if an error occurred while trying to perform the operation
394     *
395     * @supported.api
396     */
397    public ServiceSchema getSchema(String type) throws SMSException {
398        validate();
399        SchemaType t = null;
400        if (type.equalsIgnoreCase("role")
401        || type.equalsIgnoreCase("filteredrole")
402        || type.equalsIgnoreCase("realm")) {
403            t = SchemaType.DYNAMIC;
404        } else if (type.equalsIgnoreCase("user")) {
405            t = SchemaType.USER;
406        } else {
407            t = new SchemaType(type);
408        }
409        return (getSchema(t));
410    }
411    
412    /**
413     * Returns the configuration schema for the given
414     * schema type
415     *
416     * @param type
417     *            schema type.
418     * @return service schema.
419     * @throws SMSException
420     *             if an error occurred while trying to perform the operation
421     *
422     * @supported.api
423     */
424    public ServiceSchema getSchema(SchemaType type) throws SMSException {
425        SMSEntry.validateToken(token);
426        validate();
427        ServiceSchemaImpl ss = ssm.getSchema(type);
428        if ((ss == null) && type.equals(SchemaType.USER)) {
429            type = SchemaType.DYNAMIC;
430            ss = ssm.getSchema(type);
431        }
432        if (ss != null) {
433            return (new ServiceSchema(ss, "", type, this));
434        }
435        return (null);
436    }
437    
438    /**
439     * Returns the organization creation configuration schema if present; else
440     * returns <code>null</code>
441     *
442     * @return service schema.
443     * @throws SMSException
444     *             if an error occurred while trying to perform the operation
445     */
446    public ServiceSchema getOrganizationCreationSchema() throws SMSException {
447        SMSEntry.validateToken(token);
448        validate();
449        ServiceSchemaImpl ss = ssm.getSchema(SchemaType.ORGANIZATION);
450        if (ss != null) {
451            ServiceSchemaImpl ssi = ss.getOrgAttrSchema();
452            if (ssi != null) {
453                return (new ServiceSchema(ssi, "", SchemaType.ORGANIZATION,
454                    this, true));
455            }
456        }
457        return (null);
458    }
459    
460    /**
461     * Returns the attribute schemas for the given schema
462     * type excluding status and service identifier attributes.
463     *
464     * @param type
465     *            schema type.
466     * @return service schema.
467     * @throws SMSException
468     *             if an error occurred while trying to perform the operation
469     *
470     * @supported.api
471     */
472    public Set getServiceAttributeNames(SchemaType type) throws SMSException {
473        SMSEntry.validateToken(token);
474        validate();
475        ServiceSchema ss = getSchema(type);
476        return (ss.getServiceAttributeNames());
477    }
478    
479    /**
480     * Returns the global service configuration schema.
481     *
482     * @return the global service configuration schema
483     * @throws SMSException
484     *             if an error occurred while trying to perform the operation
485     *
486     * @supported.api
487     */
488    public ServiceSchema getGlobalSchema() throws SMSException {
489        return (getSchema(SchemaType.GLOBAL));
490    }
491    
492    /**
493     * Returns the organization service configuration
494     * schema.
495     *
496     * @return the organization service configuration schema
497     * @throws SMSException
498     *             if an error occurred while trying to perform the operation
499     *
500     * @supported.api
501     */
502    public ServiceSchema getOrganizationSchema() throws SMSException {
503        return (getSchema(SchemaType.ORGANIZATION));
504    }
505    
506    /**
507     * Returns the dynamic service configuration schema.
508     *
509     * @return the dynamic service configuration schema
510     * @throws SMSException
511     *             if an error occurred while trying to perform the operation
512     *
513     * @supported.api
514     */
515    public ServiceSchema getDynamicSchema() throws SMSException {
516        return (getSchema(SchemaType.DYNAMIC));
517    }
518    
519    /**
520     * Returns the user service configuration schema.
521     *
522     * @return the user service configuration schema
523     * @throws SMSException
524     *             if an error occurred while trying to perform the operation
525     *
526     * @supported.api
527     */
528    public ServiceSchema getUserSchema() throws SMSException {
529        return (getSchema(SchemaType.USER));
530    }
531    
532    /**
533     * Returns the policy service configuration schema.
534     *
535     * @return the policy service configuration schema
536     * @throws SMSException
537     *             if an error occurred while trying to perform the operation
538     *
539     * @supported.api
540     */
541    public ServiceSchema getPolicySchema() throws SMSException {
542        return (getSchema(SchemaType.POLICY));
543    }
544    
545    /**
546     * Returns the service schema in XML for this service.
547     *
548     * @return the service schema in XML for this service
549     * @throws SMSException
550     *             if an error occurred while trying to perform the operation
551     *
552     * @supported.api
553     */
554    public InputStream getSchema() throws SMSException {
555        SMSEntry.validateToken(token);
556        validate();
557        return (ssm.getSchema());
558    }
559    
560    /**
561     * Replaces the existing service schema with the given
562     * schema defined by the XML input stream that follows the SMS DTD.
563     *
564     * @param xmlServiceSchema
565     *            the XML format of the service schema
566     * @throws SMSException
567     *             if an error occurred while trying to perform the operation
568     * @throws SSOException
569     *             if the single sign on token is invalid or expired
570     * @throws IOException
571     *             if an error occurred with the <code> InputStream </code>
572     *
573     * @supported.api
574     */
575    public void replaceSchema(InputStream xmlServiceSchema)
576    throws SSOException, SMSException, IOException {
577        SMSEntry.validateToken(token);
578        validateServiceSchemaManagerImpl();
579        CachedSMSEntry smsEntry = ssm.getCachedSMSEntry();
580        smsEntry.writeXMLSchema(token, xmlServiceSchema);
581    }
582
583    // @Override
584    public int hashCode() {
585        int hash = 7;
586        hash = 67 * hash + (serviceName != null ? serviceName.hashCode() : 0);
587        hash = 67 * hash + (version != null ? version.hashCode() : 0);
588        return hash;
589    }
590    
591    /**
592     * Returns true if the given object equals this
593     * object.
594     *
595     * @param o
596     *            object for comparison.
597     * @return true if the given object equals this object.
598     *
599     * @supported.api
600     */
601    public boolean equals(Object o) {
602        if (o instanceof ServiceSchemaManager) {
603            ServiceSchemaManager ossm = (ServiceSchemaManager) o;
604            if (serviceName.equals(ossm.serviceName)
605            && version.equals(ossm.version)) {
606                return (true);
607            }
608        }
609        return (false);
610    }
611    
612    /**
613     * Returns the string representation of the Service
614     * Schema.
615     *
616     * @return the string representation of the Service Schema.
617     *
618     * @supported.api
619     */
620    public String toString() {
621        validate();
622        return (ssm.toString());
623    }
624    
625    /**
626     * Registers for changes to service's schema. The
627     * object will be called when schema for this service and version is
628     * changed.
629     *
630     * @param listener
631     *            callback object that will be invoked when schema changes.
632     * @return an ID of the registered listener.
633     *
634     * @supported.api
635     */
636    public String addListener(ServiceListener listener) {
637        validate();
638        return (ssm.addListener(listener));
639    }
640    
641    /**
642     * Removes the listener from the service for the given
643     * listener ID. The ID was issued when the listener was registered.
644     *
645     * @param listenerID
646     *            the listener ID issued when the listener was registered
647     *
648     * @supported.api
649     */
650    public void removeListener(String listenerID) {
651        if (ssm !=null ) {
652            ssm.removeListener(listenerID);
653        }
654    }
655    
656    /**
657     * Returns the last modified time stamp of this service schema. This method
658     * is expensive because it does not cache the modified time stamp but goes
659     * directly to the data store to obtain the value of this entry
660     *
661     * @return The last modified time stamp as a string with the format of
662     *         <code>yyyyMMddhhmmss</code>
663     * @throws SMSException if there is an error trying to read from the
664     *         datastore.
665     * @throws SSOException if the single sign-on token of the user is invalid.
666     */
667    public String getLastModifiedTime() throws SMSException, SSOException {
668        validateServiceSchemaManagerImpl();
669        CachedSMSEntry ce = ssm.getCachedSMSEntry();
670        if (ce.isDirty()) {
671            ce.refresh();
672        }
673        SMSEntry e = ce.getSMSEntry();
674        String vals[] = e.getAttributeValues(SMSEntry.ATTR_MODIFY_TIMESTAMP,
675            true);
676        String mTS = null;
677        if (vals != null) {
678            mTS = vals[0];
679        }
680        return mTS;
681    }
682    
683    // ================= Plugin Interface Methods ========
684    
685    /**
686     * Returns the names of the plugin interfaces used by the service
687     *
688     * @return service's plugin interface names
689     */
690    public Set getPluginInterfaceNames() {
691        validate();
692        return (ssm.getPluginInterfaceNames());
693    }
694    
695    /**
696     * Returns the <code>PluginInterface</code> object of the service for the
697     * specified plugin interface name
698     *
699     * @param pluginInterfaceName
700     *            name of the plugin interface
701     * @return plugin interface configured for the service; else
702     *         <code>null</code>
703     */
704    public PluginInterface getPluginInterface(String pluginInterfaceName) {
705        validate();
706        return (ssm.getPluginInterface(pluginInterfaceName));
707    }
708    
709    /**
710     * Adds a new plugin interface objct to service's schema.
711     *
712     * @param interfaceName
713     *            name for the plugin interface
714     * @param interfaceClass
715     *            fully qualified interface class name
716     * @param i18nKey
717     *            I18N key that will by used by UI to get messages to display
718     *            the interface name
719     */
720    public void addPluginInterface(String interfaceName, String interfaceClass,
721        String i18nKey) throws SMSException, SSOException {
722        SMSEntry.validateToken(token);
723        validateServiceSchemaManagerImpl();
724        if ((interfaceName == null) || (interfaceClass == null)) {
725            throw (new IllegalArgumentException());
726        }
727        StringBuilder sb = new StringBuilder(100);
728        sb.append("<").append(SMSUtils.PLUGIN_INTERFACE).append(" ").append(
729            SMSUtils.NAME).append("=\"").append(interfaceName)
730            .append("\" ").append(SMSUtils.PLUGIN_INTERFACE_CLASS).append(
731            "=\"").append(interfaceClass).append("\"");
732        if (i18nKey != null) {
733            sb.append(" ").append(SMSUtils.I18N_KEY).append("=\"").append(
734                i18nKey).append("\"");
735        }
736        sb.append("></").append(SMSUtils.PLUGIN_INTERFACE).append(">");
737        // Construct XML document
738        Document pluginDoc = SMSSchema.getXMLDocument(sb.toString(), false);
739        Node node = XMLUtils.getRootNode(pluginDoc, SMSUtils.PLUGIN_INTERFACE);
740        
741        // Added to XML document and write it
742        Document schemaDoc = ssm.getDocumentCopy();
743        Node pluginNode = schemaDoc.importNode(node, true);
744        Node schemaNode = XMLUtils.getRootNode(schemaDoc, SMSUtils.SCHEMA);
745        schemaNode.appendChild(pluginNode);
746        replaceSchema(schemaDoc);
747    }
748    
749    /**
750     * Removes the plugin interface object from the service schema.
751     *
752     * @param interfacename Name of the plugin class.
753     */
754    public void removePluginInterface(String interfacename)
755    throws SMSException, SSOException {
756        SMSEntry.validateToken(token);
757        validateServiceSchemaManagerImpl();
758        Document schemaDoc = ssm.getDocumentCopy();
759        Node schemaNode = XMLUtils.getRootNode(schemaDoc, SMSUtils.SCHEMA);
760        // Get the plugin interface node
761        Node pluginNode = XMLUtils.getNamedChildNode(schemaNode,
762            SMSUtils.PLUGIN_INTERFACE, SMSUtils.NAME, interfacename);
763        if (pluginNode != null) {
764            schemaNode.removeChild(pluginNode);
765            replaceSchema(schemaDoc);
766        }
767    }
768    
769    // -----------------------------------------------------------
770    // Plugin Schema
771    // -----------------------------------------------------------
772    /**
773     * Returns the names of plugins configured for the plugin interface. If
774     * organization is <code>null</code>, returns the plugins configured for
775     * the "root" organization.
776     */
777    public Set getPluginSchemaNames(String interfaceName, String orgName)
778    throws SMSException {
779        SMSEntry.validateToken(token);
780        validate();
781        // Construct the DN to get CachedSubEntries
782        StringBuilder sb = new StringBuilder(100);
783        sb.append("ou=").append(interfaceName).append(",").append(
784            CreateServiceConfig.PLUGIN_CONFIG_NODE).append("ou=").append(
785            version).append(",").append("ou=").append(serviceName).append(
786            ",").append(SMSEntry.SERVICES_RDN).append(",").append(
787            DNMapper.orgNameToDN(orgName));
788        CachedSubEntries cse = CachedSubEntries.getInstance(token, sb
789            .toString());
790        try {
791            return (cse.getSubEntries(token));
792        } catch (SSOException s) {
793            debug.error("ServiceSchemaManager: Unable to get "
794                + "Plugin Schema Names", s);
795        }
796        return (Collections.EMPTY_SET);
797    }
798    
799    /**
800     * Returns the PluginSchema object given the schema name and the interface
801     * name for the specified organization. If organization is
802     * <code>null</code>, returns the PluginSchema for the "root" organization.
803     */
804    public PluginSchema getPluginSchema(String pluginSchemaName,
805        String interfaceName, String orgName) throws SMSException {
806        SMSEntry.validateToken(token);
807        validate();
808        return (new PluginSchema(token, serviceName, version, pluginSchemaName,
809            interfaceName, orgName));
810    }
811    
812    // -----------------------------------------------------------
813    // Internal protected method
814    // -----------------------------------------------------------
815    SSOToken getSSOToken() {
816        return (token);
817    }
818    
819    protected Document getDocumentCopy() throws SMSException {
820        validate();
821        return (ssm.getDocumentCopy());
822    }
823    
824    protected synchronized void replaceSchema(Document document)
825    throws SSOException, SMSException {
826        validate();
827        CachedSMSEntry smsEntry = ssm.getCachedSMSEntry();
828        SMSSchema smsSchema = new SMSSchema(document);
829        smsEntry.writeXMLSchema(token, smsSchema.getSchema());
830    }
831    
832    private void validate() {
833        try {
834            validateServiceSchemaManagerImpl();
835        } catch (SSOException e) {
836            // Since method signatures cannot be changed, a runtime
837            // exception is thrown. This conditions would happen only
838            // when SSOToken has become invalid or service has been
839            // removed.
840            debug.error("ServiceSchemaManager:validate failed for SN: " +
841                serviceName, e);
842            throw (new RuntimeException(e.getMessage()));
843        } catch (SMSException e) {
844            // Ignore the exception
845        }
846    }
847    
848    private void validateServiceSchemaManagerImpl()
849        throws SMSException, SSOException {
850        if (ssm == null || !ssm.isValid()) {
851            // Recreate the SSM
852            ssm = ServiceSchemaManagerImpl.getInstance(token,
853                serviceName, version);
854        }
855    }
856    
857    // -----------------------------------------------------------
858    // Static method to create a new service schema
859    // -----------------------------------------------------------
860    static void createService(SSOToken token, SMSSchema smsSchema)
861    throws SMSException, SSOException {
862        // Service node
863        SMSEntry smsEntry = new SMSEntry(token, ServiceManager
864            .getServiceNameDN(smsSchema.getServiceName()));
865        
866        if (smsEntry.isNewEntry()) {
867            // create this entry
868            smsEntry.addAttribute(SMSEntry.ATTR_OBJECTCLASS, SMSEntry.OC_TOP);
869            smsEntry.addAttribute(SMSEntry.ATTR_OBJECTCLASS,
870                SMSEntry.OC_SERVICE);
871            smsEntry.save();
872        }
873        // Version node
874        CachedSMSEntry cEntry = CachedSMSEntry.getInstance(token,
875            ServiceManager.getServiceNameDN(smsSchema.getServiceName(),
876            smsSchema.getServiceVersion()));
877        if (cEntry.isDirty()) {
878            cEntry.refresh();
879        }
880        smsEntry = cEntry.getSMSEntry();
881        String[] schema = new String[1];
882        if ((smsEntry.getAttributeValues(SMSEntry.ATTR_SCHEMA) == null)
883        || ((smsEntry.getAttributeValues(SMSEntry.ATTR_SCHEMA))[0]
884            .equalsIgnoreCase(SMSSchema.getDummyXML(smsSchema
885            .getServiceName(), smsSchema
886            .getServiceVersion())))) {
887            schema[0] = smsSchema.getSchema();
888            smsEntry.setAttribute(SMSEntry.ATTR_SCHEMA, schema);
889        } else {
890            // Throw service already exists exception
891            Object[] args = { smsSchema.getServiceName(),
892            smsSchema.getServiceVersion() };
893            throw (new SMSException(IUMSConstants.UMS_BUNDLE_NAME,
894                IUMSConstants.SMS_service_already_exists, args));
895        }
896        if (smsEntry.isNewEntry()) {
897            // add object classes
898            smsEntry.addAttribute(SMSEntry.ATTR_OBJECTCLASS, SMSEntry.OC_TOP);
899            smsEntry.addAttribute(SMSEntry.ATTR_OBJECTCLASS,
900                SMSEntry.OC_SERVICE);
901        }
902        smsEntry.save(token);
903        cEntry.refresh(smsEntry);
904    }
905    
906    public String toXML(AMEncryption encryptObj)
907        throws SMSException {
908        validate();
909        String xml = ssm.toXML(encryptObj);
910        int idx = xml.lastIndexOf("</" + SMSUtils.SERVICE + ">");
911        StringBuffer buff = new StringBuffer();
912        buff.append(xml.substring(0, idx));
913
914        Set realms = new HashSet();
915        realms.add("/");
916
917        for (Iterator i = getPluginInterfaceNames().iterator(); i.hasNext(); ) {
918            String iName = (String)i.next();
919            getPlugSchemaXML(buff, iName, realms);
920        }
921
922        buff.append("</" + SMSUtils.SERVICE + ">");
923        return buff.toString();
924    }
925
926    private void getPlugSchemaXML(
927        StringBuffer buff,
928        String interfaceName,
929        Set realms
930    ) throws SMSException {
931        for (Iterator i = realms.iterator(); i.hasNext(); ){
932            String realm = (String)i.next();
933            Set schemaNames = getPluginSchemaNames(interfaceName, realm);
934            for (Iterator j = schemaNames.iterator(); j.hasNext();) {
935                String pName = (String)j.next();
936                PluginSchema pSchema = getPluginSchema(
937                    pName, interfaceName, realm);
938                buff.append(pSchema.toXML());
939            }
940        }
941    }
942}