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: TemplateManager.java,v 1.4 2008/06/25 05:41:46 qcheng Exp $
026 *
027 */
028
029package com.iplanet.ums;
030
031import java.util.ArrayList;
032import java.util.Collections;
033import java.util.Enumeration;
034import java.util.Set;
035import java.util.Vector;
036
037import com.iplanet.services.ldap.Attr;
038import com.iplanet.services.ldap.AttrSet;
039import com.iplanet.services.util.I18n;
040
041/**
042 * The class manages a set of templates. The set of templates can be used to
043 * define what attributes to use when creating an object, or what attributes to
044 * retrieve for a particular object.
045 * 
046 * <p>
047 * Example:
048 * <p>
049 * 
050 * <pre>
051 * TemplateManager mgr = TemplateManager.getTemplateManager();
052 * 
053 * Guid guid = new Guid(&quot;o=vortex.com&quot;);
054 * 
055 * CreationTemplate t1 = mgr.getCreationTemplate(&quot;BasicUser&quot;, guid,
056 *         TemplateManager.SCOPE_ANCESTORS);
057 * 
058 * CreationTemplate t2 = mgr.getCreationTemplate(User.class, guid,
059 *         TemplateManager.SCOPE_ANCESTORS);
060 * 
061 * SearchTemplate t3 = mgr.getSearchTemplate(&quot;BasicUserSearch&quot;, guid,
062 *         TemplateManager.SCOPE_ANCESTORS);
063 * </pre>
064 * 
065 * @see com.iplanet.ums.Template
066 * @see com.iplanet.ums.CreationTemplate
067 * @see com.iplanet.ums.SearchTemplate
068 * @supported.api
069 */
070public class TemplateManager implements java.io.Serializable {
071
072    /**
073     * Search scope for determining how to get the configuration data. This will
074     * get the configuration data at the organization level specified.
075     * 
076     * @supported.api
077     */
078    public static final int SCOPE_ORG = 0;
079
080    /**
081     * Search scope for determining how to get the configuration data. This will
082     * get the configuration data from the nearest ancestor containing the
083     * configuration data.
084     * 
085     * @supported.api
086     */
087    public static final int SCOPE_ANCESTORS = 1;
088
089    /**
090     * Search scope for determining how to get the configuration data. This will
091     * get the configuration data at the organization level, and if not found,
092     * then at the root level.
093     * 
094     * @supported.api
095     */
096    public static final int SCOPE_TOP = 2;
097
098    private static final String TEMPLATE_NAME = "name";
099
100    private static final String TEMPLATE_JAVACLASS = "javaclass";
101
102    private static final String TEMPLATE_OPTIONAL = "optional";
103
104    private static final String TEMPLATE_REQUIRED = "required";
105
106    private static final String TEMPLATE_VALIDATED = "validated";
107
108    private static final String TEMPLATE_NAMINGATTRIBUTE = "namingattribute";
109
110    private static final String TEMPLATE_SEARCH_FILTER = "searchfilter";
111
112    private static final String SCHEMA2_SEARCH_FILTER = 
113        "inetDomainSearchFilter";
114
115    private static final String TEMPLATE_ATTRS = "attrs";
116
117    private static I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG);
118
119    /**
120     * Default constructor that registers a default class resolver and accesses
121     * template configuration information.
122     * 
123     * @throws UMSException
124     *             if an exception occurs registering the resolver or accessing
125     *             configuration data.
126     */
127    protected TemplateManager() throws UMSException {
128        // Register com.iplanet.ums.DefaultClassResolver to resolve
129        // a set of attributes to a Java class.
130        addClassResolver(new DefaultClassResolver());
131        // Register com.iplanet.ums.GroupResolver that can distinguish
132        // between regular dynamic groups and assignable ones
133        addClassResolver(new GroupResolver());
134        try {
135            m_configManager = ConfigManagerUMS.getConfigManager();
136        } catch (ConfigManagerException e) {
137            throw new UMSException(e.getMessage());
138        }
139    }
140
141    /**
142     * Clients can only obtain a reference through this method.
143     * 
144     * @return the one and only instance
145     * @throws UMSException
146     *             if an exception occurs while getting an instance of a
147     *             template manager.
148     * 
149     * @supported.api
150     */
151    public static synchronized TemplateManager getTemplateManager()
152            throws UMSException {
153        if (m_mgr == null) {
154            m_mgr = new TemplateManager();
155        }
156        return m_mgr;
157    }
158
159    /**
160     * Registers a class that can resolve a set of attributes to a Java class.
161     * The last class registered is the first to be called in the resolution
162     * chain.
163     * 
164     * @param resolver
165     *            a class that can produce a Java class instance from an ID and
166     *            a set of attributes
167     */
168    public void addClassResolver(IClassResolver resolver) {
169        m_resolvers.addElement(resolver);
170    }
171
172    /**
173     * Unregisters a class that can resolve a set of attributes to a Java class.
174     * 
175     * @param resolver
176     *            A class that can produce a Java class instance from an ID and
177     *            a set of attributes
178     */
179    public void removeClassResolver(IClassResolver resolver) {
180        m_resolvers.remove(resolver);
181    }
182
183    /**
184     * Given a class, gets the default creation template for the object. This
185     * will traverse the tree all the way to the root until the CreationTemplate
186     * is found.
187     * 
188     * @param cls Class (instance of) to be queried for the template.
189     * @param orgGuid GUID of the Organization where the config data is stored.
190     * @return Creation template for the class or <code>null</code> if the
191     *         class is not known or no template is registered for the class.
192     * @throws UMSException if an exception occurs while getting the creation
193     *         template.
194     * @supported.api
195     */
196    public CreationTemplate getCreationTemplate(Class cls, Guid orgGuid)
197            throws UMSException {
198
199        return getCreationTemplate(cls, orgGuid, SCOPE_ANCESTORS);
200    }
201
202    /**
203     * Returns default creation template of a given class.
204     * 
205     * @param cls Class (instance of) to be queried for the template.
206     * @param orgGuid GUID of the Organization where the config data is stored.
207     * @param scope Search scope for determining how to get the configuration
208     *        data
209     * @return Creation template for the class or <code>null</code> if the
210     *         class is not known or no template is registered for the class
211     * @throws UMSException if error occurs while getting the creation template.
212     * @supported.api
213     */
214    public CreationTemplate getCreationTemplate(Class cls, Guid orgGuid,
215            int scope) throws UMSException {
216        if (cls == null) {
217            String msg = i18n.getString(IUMSConstants.BAD_CLASS);
218            throw new IllegalArgumentException(msg);
219        }
220
221        AttrSet attrSet = null;
222        try {
223            attrSet = m_configManager.getCreationTemplateForClass(orgGuid, cls
224                    .getName(), scope);
225        } catch (ConfigManagerException e) {
226            throw new UMSException(e.getMessage());
227        }
228        if (attrSet == null) {
229            return null;
230        }
231
232        return toCreationTemplate(attrSet);
233    }
234
235    /**
236     * Returns a template from a supplied template name. This will traverse the
237     * tree all the way to the root until the CreationTemplate is found.
238     * 
239     * @param name Name of template.
240     * @param orgGuid GUID of the Organization where the config data is stored.
241     * @return CreationTemplate matching the supplied name, or <code>null</code>
242     *         if there is no matching template
243     * @throws UMSException if error occurs while getting the creation template.
244     * @supported.api
245     */
246    public CreationTemplate getCreationTemplate(String name, Guid orgGuid)
247            throws UMSException {
248        return getCreationTemplate(name, orgGuid, SCOPE_ANCESTORS);
249    }
250
251    /**
252     * Returns a template from a supplied template name.
253     * 
254     * @param name Name of template.
255     * @param orgGuid GUID of the Organization where the config data is stored.
256     * @param scope Search scope for determining how to get the configuration
257     *        data.
258     * @return CreationTemplate matching the supplied name, or <code>null</code>
259     *         if there is no matching template
260     * @throws UMSException if an exception occurs while getting the creation
261     *         template.
262     * @supported.api
263     */
264    public CreationTemplate getCreationTemplate(String name, Guid orgGuid,
265            int scope) throws UMSException {
266        if (name == null) {
267            String msg = i18n.getString(IUMSConstants.MISSING_TEMPL_NAME);
268            throw new IllegalArgumentException(msg);
269        }
270
271        AttrSet attrSet = null;
272
273        try {
274            attrSet = m_configManager.getCreationTemplate(orgGuid, name, scope);
275        } catch (ConfigManagerException e) {
276            throw new UMSException(e.getMessage());
277        }
278
279        if (attrSet == null) {
280            return null;
281        }
282
283        return toCreationTemplate(attrSet);
284    }
285
286    /**
287     * Returns a template from a supplied template name. This will traverse the
288     * tree all the way to the root till the SearchTemplate is found.
289     * 
290     * @param name Name of template.
291     * @param orgGuid GUID of the Organization where the config data is stored
292     * @return SearchTemplate matching the supplied name, or <code>null</code>
293     *         if there is no matching template
294     * @throws UMSException if error occurs while getting the search template.
295     * @supported.api
296     */
297    public SearchTemplate getSearchTemplate(String name, Guid orgGuid)
298            throws UMSException {
299        return getSearchTemplate(name, orgGuid, SCOPE_ANCESTORS);
300    }
301
302    /**
303     * Returns a template from a supplied template name.
304     * 
305     * @param name Name of Template.
306     * @param orgGuid GUID of the Organization where the config data is stored.
307     * @param scope Search scope for determining how to get the configuration
308     *        data.
309     * @return SearchTemplate matching the supplied name, or <code>null</code>
310     *         if there is no matching template.
311     * @throws UMSException if an exception occurs while getting the search
312     *         template.
313     * @supported.api
314     */
315    public SearchTemplate getSearchTemplate(
316        String name,
317        Guid orgGuid, 
318        int scope
319    ) throws UMSException {
320        if (name == null) {
321            String msg = i18n.getString(IUMSConstants.MISSING_TEMPL_NAME);
322
323            throw new IllegalArgumentException(msg);
324        }
325        AttrSet attrSet = null;
326        try {
327            attrSet = m_configManager.getSearchTemplate(orgGuid, name, scope);
328        } catch (ConfigManagerException e) {
329            throw new UMSException(e.getMessage());
330        }
331        if (attrSet == null) {
332            return null;
333        }
334        return toSearchTemplate(attrSet);
335    }
336
337    /**
338     * Returns a set of known creation templates.
339     * 
340     * @param orgGuid GUID of the Organization where the config data is stored.
341     * @return Names of known creation templates
342     * @throws UMSException if an exception occurs.
343     * @supported.api
344     */
345    public Set getCreationTemplateNames(Guid orgGuid) throws UMSException {
346        Set names = null;
347        try {
348            names = m_configManager.getCreationTemplateNames(orgGuid);
349        } catch (ConfigManagerException e) {
350            throw new UMSException(e.getMessage());
351        }
352        return (names != null) ? names : Collections.EMPTY_SET;
353    }
354
355    /**
356     * Returns a set of known search templates.
357     * 
358     * @param orgGuid GUID of the Organization where the config data is stored.
359     * @return Names of known search templates.
360     * @throws UMSException if an exception occurs.
361     * @supported.api
362     */
363    public Set getSearchTemplateNames(Guid orgGuid) throws UMSException {
364        Set names = null;
365        try {
366            names = m_configManager.getSearchTemplateNames(orgGuid);
367        } catch (ConfigManagerException e) {
368            throw new UMSException(e.getMessage());
369        }
370        return (names != null) ? names : Collections.EMPTY_SET;
371    }
372
373    /**
374     * Replaces an existing CreationTemplate with the one specified.
375     * 
376     * @param template
377     *            CreationTemplate to be modified
378     * @param orgGuid
379     *            the guid of the Organization where the config data is stored
380     * @throws UMSException
381     *             if an exception occurs
382     */
383    public void replaceCreationTemplate(CreationTemplate template, Guid orgGuid)
384            throws UMSException {
385        if (template == null) {
386            return;
387        }
388
389        String templateName = template.getName();
390        if (templateName == null) {
391            String msg = i18n.getString(IUMSConstants.MISSING_TEMPL_NAME);
392
393            throw new IllegalArgumentException(msg);
394        }
395
396        AttrSet attrSet = toAttrSet(template);
397        try {
398            m_configManager.replaceCreationTemplate(orgGuid, templateName,
399                    attrSet);
400        } catch (ConfigManagerException e) {
401            throw new UMSException(e.getMessage());
402        }
403    }
404
405    private AttrSet toAttrSet(CreationTemplate t) {
406        AttrSet attrSet = new AttrSet();
407
408        attrSet.add(new Attr(TemplateManager.TEMPLATE_NAME, t.getName()));
409
410        attrSet.add(new Attr(TemplateManager.TEMPLATE_NAMINGATTRIBUTE, t
411                .getNamingAttribute()));
412
413        ArrayList classes = t.getCreationClasses();
414        String[] classNames = new String[classes.size()];
415        for (int i = 0; i < classes.size(); i++) {
416            Class cls = (Class) classes.get(i);
417            classNames[i] = cls.getName();
418        }
419        attrSet.add(new Attr(TemplateManager.TEMPLATE_JAVACLASS, classNames));
420
421        Attr required = encodeAttrSet(TemplateManager.TEMPLATE_REQUIRED, t
422                .getRequiredAttributeSet(), "=");
423        if (required != null) {
424            attrSet.add(required);
425        }
426
427        Attr optional = encodeAttrSet(TemplateManager.TEMPLATE_OPTIONAL, t
428                .getOptionalAttributeSet(), "=");
429        if (optional != null) {
430            attrSet.add(optional);
431        }
432
433        Attr validated = encodeAttrSet(TemplateManager.TEMPLATE_VALIDATED, t
434                .getValidation(), "=");
435        if (validated != null) {
436            attrSet.add(validated);
437        }
438
439        return attrSet;
440    }
441
442    /**
443     * Gets the Java class given the id and attribute set of an entry. The Java
444     * class is inferred from the default CreationTemplate(s) registered with
445     * this template manager. A different inference rule could be implemented by
446     * subclassing the TemplateManager and overriding this method. This
447     * implementation figures out the Java class for the entry.
448     * 
449     * @param guid
450     *            Guid of the entry
451     * @param attrSet
452     *            Attribute set of the entry
453     * @return Java Class that maps to the list of LDAP object classes
454     * @exception UMSException
455     *                if the template manager has not been properly initialized
456     */
457    Class getJavaClassForEntry(String id, AttrSet attrSet) throws UMSException {
458        Class javaClass = null;
459        int i = m_resolvers.size() - 1;
460        while ((javaClass == null) && (i >= 0)) {
461            IClassResolver resolver = (IClassResolver) m_resolvers.elementAt(i);
462            javaClass = resolver.resolve(id, attrSet);
463            i--;
464        }
465
466        if (javaClass == null) {
467            javaClass = PersistentObject.class;
468        }
469
470        return javaClass;
471    }
472
473    /**
474     * Reads in a attribute set and converts name-value pairs to a
475     * CreationTemplate object.
476     * 
477     * @param t
478     *            attribute set contains template values
479     * @return CreationTemplate object based on the name-value pairs
480     */
481    private CreationTemplate toCreationTemplate(AttrSet t) {
482        Attr nameAttr = t.getAttribute(TEMPLATE_NAME);
483        String name = null;
484        if (nameAttr != null) {
485            name = nameAttr.getValue();
486        }
487
488        Attr namingAttr = t.getAttribute(TEMPLATE_NAMINGATTRIBUTE);
489        String namingAttribute = null;
490        if (namingAttr != null) {
491            namingAttribute = namingAttr.getValue();
492        }
493
494        Attr classAttr = t.getAttribute(TEMPLATE_JAVACLASS);
495        String[] classNames = null;
496        if (classAttr != null) {
497            classNames = classAttr.getStringValues();
498        }
499
500        AttrSet required = decodeAttr(t.getAttribute(TEMPLATE_REQUIRED), "=");
501        AttrSet optional = decodeAttr(t.getAttribute(TEMPLATE_OPTIONAL), "=");
502        AttrSet validated = decodeAttr(t.getAttribute(TEMPLATE_VALIDATED), "=");
503
504        CreationTemplate template = new CreationTemplate();
505        ArrayList classes = new ArrayList();
506
507        try {
508            if (classNames != null) {
509                for (int i = 0; i < classNames.length; i++) {
510                    Class cls = Class.forName(classNames[i]);
511                    classes.add(cls);
512                }
513            }
514            template = new CreationTemplate(name, required, optional, classes);
515
516        } catch (ClassNotFoundException e) {
517            template = new CreationTemplate(name, required, optional);
518        }
519
520        if (validated != null) {
521            template.setValidation(validated);
522        }
523
524        if (namingAttribute != null) {
525            template.setNamingAttribute(namingAttribute);
526        }
527
528        return template;
529    }
530
531    /**
532     * Reads in a attribute set and converts name-value pairs to a
533     * SearchTemplate object.
534     * 
535     * @param t
536     *            attribute set contains template values
537     * @return SearchTemplate object based on the name-value pairs
538     */
539    private SearchTemplate toSearchTemplate(AttrSet t) {
540        Attr nameAttr = t.getAttribute(TEMPLATE_NAME);
541        String name = null;
542        if (nameAttr != null) {
543            name = nameAttr.getValue();
544        }
545
546        Attr filterAttr = t.getAttribute(SCHEMA2_SEARCH_FILTER);
547        if (filterAttr == null) {
548            filterAttr = t.getAttribute(TEMPLATE_SEARCH_FILTER);
549        }
550        String filter = null;
551        if (filterAttr != null) {
552            filter = filterAttr.getValue();
553        }
554
555        AttrSet attrSet = decodeAttr(t.getAttribute(TEMPLATE_ATTRS), "=");
556
557        SearchTemplate template = new SearchTemplate();
558
559        template = new SearchTemplate(name, attrSet, filter);
560
561        return template;
562    }
563
564    /**
565     * Decode single attribute with multiple values into an AttrSet. For
566     * example:
567     * 
568     * <pre>
569     *    Attribute:
570     *       required: objectclass=top
571     *       required: objectclass=groupofuniquenames
572     *       required: cn
573     *       required: sn
574     *  
575     *    Attribute Set:
576     *       objectclass: top
577     *       objectclass: groupofuniquenames
578     *       cn:
579     *       sn:
580     * </pre>
581     * 
582     * @param attr
583     *            Attribute to be decoded from
584     * @param delimiter
585     *            Delimiter used in the encoding
586     * @return Decoded attribute set
587     */
588    private AttrSet decodeAttr(Attr attr, String delimiter) {
589
590        if (attr == null)
591            return null;
592
593        String[] values = attr.getStringValues();
594        AttrSet attrSet = new AttrSet();
595
596        for (int i = 0, size = attr.size(); i < size; i++) {
597            String value = values[i];
598            String attrName = null;
599            String attrValue = null;
600
601            int index = value.indexOf('=');
602            if (index < 0) {
603                attrName = value;
604            } else {
605                attrName = value.substring(0, index);
606                attrValue = value.substring(index + 1, value.length());
607            }
608
609            if (attrValue != null && attrValue.length() != 0) {
610                attrSet.add(new Attr(attrName, attrValue));
611            } else {
612                attrSet.add(new Attr(attrName));
613            }
614
615        }
616        return attrSet;
617    }
618
619    /**
620     * Encode an attrSet in a single attribute with multiple values using the
621     * given attribute name and the values (tag,value) found in the given
622     * attribute set. For example:
623     * 
624     * <pre>
625     *    Attribute:
626     *       required: objectclass=top
627     *       required: objectclass=groupofuniquenames
628     *       required: cn
629     *       required: sn
630     *  
631     *    Attribute Set:
632     *       objectclass: top
633     *       objectclass: groupofuniquenames
634     *       cn:
635     *       sn:
636     * </pre>
637     * 
638     * @param attrName
639     *            Name of the encoded attribute.
640     * @param attrSet
641     *            Attribute set to be encoded in a single attribut.e
642     * @param delimiter
643     *            String token used as delimiter for the encoding.
644     * @return Encoded attribute or null object if attrSet is empty.
645     */
646    private Attr encodeAttrSet(String attrName, AttrSet attrSet,
647            String delimiter) {
648        if (attrSet == null || attrSet.size() == 0) {
649            return null;
650        }
651
652        Enumeration attrEnum = attrSet.getAttributes();
653        Attr encodedAttr = new Attr(attrName);
654
655        while (attrEnum.hasMoreElements()) {
656            Attr a = (Attr) attrEnum.nextElement();
657            String[] values = a.getStringValues();
658            String[] encodedValues = new String[values.length];
659
660            if (values.length == 0) {
661                encodedAttr.addValue(a.getName());
662            } else {
663                for (int i = 0; i < values.length; i++) {
664                    encodedValues[i] = a.getName() + delimiter + values[i];
665                }
666                encodedAttr.addValues(encodedValues);
667            }
668        }
669
670        return encodedAttr;
671    }
672
673    private Vector m_resolvers = new Vector();
674
675    private ConfigManagerUMS m_configManager = null;
676
677    // Single instance of TemplateManager
678    private static TemplateManager m_mgr;
679
680}