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: PersistentObject.java,v 1.8 2009/07/02 20:27:01 hengming Exp $
026 *
027 */
028
029/**
030 * Portions Copyrighted [2011] [ForgeRock AS]
031 */
032package com.iplanet.ums;
033
034import com.iplanet.am.sdk.AMException;
035import java.io.Serializable;
036import java.security.Principal;
037import java.util.ArrayList;
038import java.util.Arrays;
039import java.util.Collection;
040import java.util.Enumeration;
041import java.util.Iterator;
042import java.util.Locale;
043
044import com.sun.identity.shared.ldap.LDAPAttribute;
045import com.sun.identity.shared.ldap.LDAPModification;
046import com.sun.identity.shared.ldap.util.DN;
047import com.sun.identity.shared.ldap.util.RDN;
048
049import com.sun.identity.shared.debug.Debug;
050import com.iplanet.services.ldap.Attr;
051import com.iplanet.services.ldap.AttrSet;
052import com.iplanet.services.ldap.ModSet;
053import com.iplanet.services.ldap.aci.ACI;
054import com.iplanet.services.ldap.aci.ACIParseException;
055import com.iplanet.services.util.I18n;
056import com.iplanet.sso.SSOException;
057import com.iplanet.sso.SSOToken;
058import com.iplanet.sso.SSOTokenManager;
059import com.iplanet.ums.validation.Validation;
060
061/**
062 * Represents a persistent object in UMS. This is the base class for all objects
063 * that Object Management Module (OMM) manages in UMS.
064 *
065 * @supported.api
066 */
067public class PersistentObject implements ISearch, Serializable, IUMSConstants {
068
069    public static final String COMPUTED_MEMBER_ATTR_NAME = "nsRole";
070
071    private static Debug debug;
072    static {
073        debug = Debug.getInstance(IUMSConstants.UMS_DEBUG);
074    }
075
076    private static I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG);
077
078    /**
079     * Default Constructor
080     */
081    protected PersistentObject() {
082        super();
083    }
084
085    /**
086     * Constructor for PersistentObject given an authenticated session and guid,
087     * to instantiate from persistent storage.
088     * 
089     * @param session
090     *            Valid and authenticated session
091     * @param guid
092     *            Globally unique identifier for the entity
093     * @throws UMSException
094     *             for failure to find the object
095     * 
096     */
097    PersistentObject(Principal principal, Guid guid) throws UMSException {
098        String dn = guid.getDn();
099
100        if (principal == null || dn == null) {
101            String msg;
102            if (principal == null) {
103                msg = i18n.getString(IUMSConstants.BAD_PRINCIPAL_HDL);
104            } else {
105                msg = i18n.getString(IUMSConstants.BAD_GUID);
106            }
107
108            throw new IllegalArgumentException(msg);
109        }
110        setGuid(guid);
111        setPrincipal(principal);
112
113        // If reading in the object turns out to be too expensive, comment
114        // out the read. The read method will throw an exception for
115        // an unfound object due to access rights or bad guid.
116        // TODO: to be reviewed if we need to comment out this
117        //
118        read();
119    }
120
121    /**
122     * Constructor for in memory object to be added to the system. You can make
123     * the object persistent two ways:
124     * <P>
125     * 
126     * 1) call add on parent object (recommended)
127     * <P>
128     * 2) call save(...) method after all attributes for in memory object are
129     * set up properly.
130     * <P>
131     * 
132     * @param template
133     *            Object creation template. The template holds all the default
134     *            values such as objectclass and requirted attributes to be
135     *            supplied
136     * @param attrSet
137     *            Attribute set to construct the object in memory
138     * @throws UMSException
139     *             for failure to construct the object. The given attrSet needs
140     *             to provide the required attribute(s) defined in template
141     *
142     * @supported.api
143     */
144    public PersistentObject(CreationTemplate template, AttrSet attrSet)
145            throws UMSException {
146        m_attrSet = attrSet;
147        if (template == null) {
148            throw new UMSException(BAD_TEMPLATE);
149        }
150        m_namingAttribute = template.getNamingAttribute();
151        template.validateAttrSet(attrSet);
152        template.validateAttributes(attrSet);
153    }
154
155    /**
156     * Constructor for in memory object to be added to the system. You can make
157     * the object persistent two ways:
158     * <P>
159     * 
160     * 1) call add on parent object (recommended)
161     * <P>
162     * 2) call save(...) method after all attributes for in memory object are
163     * set up properly.
164     * <P>
165     * 
166     * @param template
167     *            Object creation template. The template holds all the default
168     *            values such as objectclass and requirted attributes to be
169     *            supplied
170     * @param attrSet
171     *            Attribute set to construct the object in memory
172     * @param namingAttribute
173     *            Internal naming attribute (ex: "ou").
174     * @throws UMSException
175     *             for failure to construct the object. The given attrSet needs
176     *             to provide the required attribute(s) defined in template
177     */
178    PersistentObject(CreationTemplate template, AttrSet attrSet,
179            String namingAttribute) throws UMSException {
180
181        m_attrSet = attrSet;
182        if (template == null) {
183            throw new UMSException(BAD_TEMPLATE);
184        }
185        template.validateAttrSet(attrSet);
186        template.validateAttributes(attrSet);
187        m_namingAttribute = namingAttribute;
188    }
189
190    /**
191     * Gets an attribute of the object. If the attribute is not in memory, the
192     * object is refreshed from persistent storage.
193     * 
194     * @param attrName
195     *            Name of the attribute to be queried
196     * @return Attribute value
197     *
198     * @supported.api
199     */
200    public Attr getAttribute(String attrName) {
201        Attr attr = getAttributeFromCache(attrName);
202        if ((attr == null) && isAttributeNotRead(attrName)
203                && (getGuid() != null) && (getPrincipal() != null)) {
204            try {
205                attr = readAttributeFromDataStore(attrName);
206            } catch (UMSException ex) {
207                if (debug.warningEnabled()) {
208                    debug.warning("PersistentObject.getAttribute: for DN: " +
209                        getGuid() + " attribute: " + attrName, ex);
210                }
211            }
212        }
213        return attr;
214    }
215
216    /**
217     * Gets an attribute value with a given locale
218     * 
219     * @param attrName
220     *            Name of the attribute
221     * @param locale
222     *            Locale of attribute to be retrieved
223     * @return Attribute value with the specified locale. May return null if the
224     *         attribute with locale not found. No fallback mechanism is
225     *         provided
226     * @see com.iplanet.ums.PersistentObject#getAttribute(String)
227     *
228     * @supported.api
229     */
230    public Attr getAttribute(String attrName, Locale locale)
231            throws UMSException {
232
233        if (locale == null) {
234            return getAttribute(attrName);
235        }
236
237        return getAttribute(Attr.getName(attrName, locale));
238    }
239
240    /**
241     * Gets attribute values
242     * 
243     * @param attrs
244     *            Array of strings representing attribute names
245     * @return attribute value set for the return values
246     * @see #getAttribute(String)
247     *
248     * @supported.api
249     */
250    public AttrSet getAttributes(String[] attrs) throws UMSException {
251        return getAttributes(attrs, false);
252    }
253
254    /**
255     * Gets attribute values
256     * 
257     * @param attrs
258     *            Array of strings representing attribute names
259     * @param cacheOnly
260     *            if true, read attributes from cache only without contacting
261     *            data stroe
262     * @return attribute value set for the return values
263     * @see #getAttribute(String)
264     *
265     * @supported.api
266     */
267    public AttrSet getAttributes(String[] attrs, boolean cacheOnly)
268            throws UMSException {
269        if (attrs == null) {
270            throw new IllegalArgumentException(i18n
271                    .getString(IUMSConstants.BAD_ATTRNAMES));
272        }
273        AttrSet attrSet = new AttrSet();
274        if (!cacheOnly) {
275            Collection attributesNotInCache = findAttributesNotRead(attrs);
276            if ((!attributesNotInCache.isEmpty()) && (getGuid() != null)
277                    && (getPrincipal() != null)) {
278                readAttributesFromDataStore(attributesNotInCache);
279            }
280        }
281        int length = attrs.length;
282        for (int i = 0; i < length; i++) {
283            Attr attr = getAttributeFromCache(attrs[i]);
284            if (attr != null) {
285                attrSet.add(attr);
286            }
287        }
288        return attrSet;
289    }
290
291    /**
292     * Returns attribute values with a specified locale.
293     * 
294     * @param attrNames Attribute names
295     * @param locale Locale of the attributes to be queried
296     * @return Attribute value set. May return null value for attribute(s) with
297     *         unfound locale. No fallback mechanism is provided.
298     * @see #getAttribute(String)
299     *
300     * @supported.api
301     */
302    public AttrSet getAttributes(String attrNames[], Locale locale)
303            throws UMSException {
304        if (locale == null)
305            return getAttributes(attrNames);
306
307        String[] namesWithLocale = new String[attrNames.length];
308
309        for (int i = 0; i < attrNames.length; i++) {
310            namesWithLocale[i] = Attr.getName(attrNames[i], locale);
311        }
312
313        return getAttributes(namesWithLocale);
314    }
315
316    /**
317     * Set an attribute value for the entity.
318     * <P>
319     * IMPORTANT: To make the changes persistent, you need to call the save
320     * method to save the changes.
321     * <P>
322     * 
323     * @param attr
324     *            Attribute and value
325     *
326     * @supported.api
327     */
328    public void setAttribute(Attr attr) {
329
330        if (attr == null || (attr.getName() == null)) {
331            throw new IllegalArgumentException(i18n
332                    .getString(IUMSConstants.ADD_NULL_OBJ));
333        }
334
335        checkCache();
336
337        if (m_attrSet == null)
338            m_attrSet = new AttrSet();
339
340        if (m_attrSet.contains(attr.getName())) {
341            modify(attr, ModSet.REPLACE);
342        } else {
343            modify(attr, ModSet.ADD);
344        }
345    }
346
347    /**
348     * Sets an attribute value with a given locale for the entity.
349     * <P>
350     * IMPORTANT: To make the changes persistent, you need to call the save
351     * method to save the changes.
352     * <P>
353     * 
354     * @param attr
355     *            Attribute and value
356     * @param locale
357     *            Intended locale of the attribute to be set
358     *
359     * @supported.api
360     */
361    public void setAttribute(Attr attr, Locale locale) {
362
363        if (locale == null) {
364            setAttribute(attr);
365            return;
366        }
367
368        // TODO: ??? should check if adding Attr.setName method makes more
369        // sense than recopying the data values of the passed in attribute
370        //
371        Attr attrWithLocale = new Attr(
372                Attr.getName(attr.getBaseName(), locale));
373        attrWithLocale.addValues(attr.getStringValues());
374        setAttribute(attrWithLocale);
375    }
376
377    /**
378     * Changes user password.
379     * 
380     * @param entryDN DN of the profile whose template is to be set
381     * @param attrName password attribute name
382     * @param oldPassword old password
383     * @param newPassword new password
384     * @throws AMException if an error occurs when changing user password
385     * @throws SSOException If user's single sign on token is invalid.
386     */
387    public void changePassword(String entryDN, String attrName,
388        String oldPassword, String newPassword) throws UMSException {
389
390        DataLayer.getInstance().changePassword(getGuid(), attrName,
391            oldPassword, newPassword);
392    }
393
394    /**
395     * Removes attribute value for the entity.
396     * <P>
397     * IMPORTANT: To make the changes persistent, you need to call the save
398     * method to save the changes.
399     * <P>
400     * 
401     * @param attr
402     *            Attribute to be removed
403     *
404     * @supported.api
405     */
406    public void removeAttribute(Attr attr) {
407        checkCache();
408        if (m_attrSet == null || m_attrSet.size() == 0) {
409            return;
410        }
411
412        modify(attr, ModSet.DELETE);
413    }
414
415    /**
416     * Gets names for all available attributes for this entity
417     * 
418     * @return Array of strings representing attribute names
419     * 
420     * @supported.api
421     */
422    public String[] getAttributeNames() {
423        if (m_principal != null && m_guid != null && m_attrSet == null) {
424            try {
425                read();
426            } catch (UMSException e) {
427                // TODO log exception here.
428                if (debug.messageEnabled()) {
429                    debug.message("PersistentObject.getAttributeNames: " +
430                            "UMSException: " + e.getMessage());
431                }
432            }
433        }
434
435        if (m_attrSet != null) {
436            return m_attrSet.getAttributeNames();
437        } else {
438            return null;
439        }
440    }
441
442    /**
443     * Modifies attribute values for the entity.
444     * <P>
445     * IMPORTANT: To make the changes persistent, you need to call the save
446     * method to save the changes.
447     * <P>
448     * 
449     * @param modSet
450     *            Set of modification of attributes
451     * @see ModSet
452     *
453     * @supported.api
454     */
455    public void modify(ModSet modSet) {
456        checkCache();
457        if (m_modSet == null) {
458            m_modSet = new ModSet();
459        }
460        if (m_attrSet == null) {
461            m_attrSet = new AttrSet();
462        }
463
464        int nMods = modSet.size();
465        LDAPModification mod = null;
466
467        for (int i = 0; i < nMods; i++) {
468            mod = modSet.elementAt(i);
469            switch (mod.getOp()) {
470            case ModSet.ADD:
471                m_attrSet.add(new Attr(mod.getAttribute()));
472                break;
473            case ModSet.DELETE:
474                if (mod.getAttribute().size() == 0) {
475                    m_attrSet.remove(mod.getAttribute().getName());
476                } else {
477                    LDAPAttribute attr = mod.getAttribute();
478                    Enumeration en = attr.getStringValues();
479                    while (en.hasMoreElements()) {
480                        m_attrSet.remove(attr.getName(), (String) en
481                                .nextElement());
482                    }
483                }
484                break;
485            case ModSet.REPLACE:
486                m_attrSet.replace(new Attr(mod.getAttribute()));
487                break;
488            default:
489                break;
490            }
491
492            m_modSet.add(mod.getOp(), mod.getAttribute());
493
494        }
495    }
496
497    /**
498     * Modifies the values of a single attribute for the entity.
499     * <P>
500     * IMPORTANT: To make the changes persistent, you need to call the save
501     * method to save the changes.
502     * <P>
503     * 
504     * @param attr
505     *            Attribute value to be modified
506     * @param op
507     *            Operation type in the modification. Input values include
508     * 
509     * <pre>
510     *               ModSet.ADD,
511     *               ModSet.DELETE,
512     *               ModSet.REPLACE
513     * </pre>
514     * 
515     * @see ModSet
516     *
517     * @supported.api
518     */
519    public void modify(Attr attr, int op) {
520        ModSet modSet = new ModSet();
521
522        modSet.add(op, attr.toLDAPAttribute());
523        modify(modSet);
524    }
525
526    /**
527     * Modify a single attribute for the entity.
528     * <P>
529     * IMPORTANT: To make the changes persistent, you need to call the save
530     * method to save the changes.
531     * <P>
532     * 
533     * @param attrName
534     *            Attribute name of the attribute to be modified
535     * @param value
536     *            String value of the attribute
537     * @param op
538     *            Operation type in the modification. Input values include
539     * 
540     * <pre>
541     *                   ModSet.ADD,
542     *                   ModSet.DELETE,
543     *                   ModSet.REPLACE
544     * </pre>
545     * 
546     * @see ModSet
547     *
548     * @supported.api
549     */
550    public void modify(String attrName, String value, int op) {
551        ModSet modSet = new ModSet();
552
553        modSet.add(op, new LDAPAttribute(attrName, value));
554        modify(modSet);
555    }
556
557    /**
558     * Get GUID of the given entity
559     * 
560     * @return the GUID.
561     *
562     * @supported.api
563     */
564    public Guid getGuid() {
565        return m_guid;
566    }
567
568    /**
569     * Renames the RDN to a new value. Note: The modified or added attribute
570     * values are not saved by this call.
571     * 
572     * @param newRDN
573     *            the new RDN value
574     * @param deleteOldName
575     *            if true old RDN value is deleted, otherwise the old value is
576     *            retained.
577     * 
578     * @throws AccessRightsException
579     *             if an access rights exception occurs.
580     * @throws EntryNotFoundException
581     *             if the entry is not found
582     * @throws UMSException
583     *             on failure to save to persistent storage
584     *
585     * @supported.api
586     */
587    public void rename(String newRDN, boolean deleteOldName)
588            throws AccessRightsException, EntryNotFoundException, UMSException {
589        String required = null;
590
591        if (m_principal == null) {
592            required = "principal";
593        } else if (m_guid == null) {
594            required = "guid";
595        }
596        if (required != null) {
597            // TODO: This is not an illegal argument case. Should be
598            // a more sophisticated exception.
599            String args[] = new String[1];
600
601            args[0] = required;
602            String msg = i18n.getString(IUMSConstants.NO_REQUIRED, args);
603            throw new UMSException(msg);
604        }
605
606        try {
607            DataLayer.getInstance().rename(getPrincipal(), getGuid(), newRDN,
608                    deleteOldName);
609        } finally {
610            // Must be set to new ID since the orignal DN would have changed now
611            RDN rdn = new RDN(newRDN);
612            DN parentDN = (new DN(m_guid.toString())).getParent();
613            parentDN.addRDN(rdn);
614            m_guid.setDn(parentDN.toRFCString());
615        }
616    }
617
618    /**
619     * Save the modification(s) to the object. Save the changes made so far for
620     * the persistent object. In other words, make the changes persistent for
621     * the object.
622     * <P>
623     * This save method takes no parameter. You use this save method when the
624     * object is already instantiated. For example,
625     * 
626     * <pre>
627     * User user = (User) UMSObject.getObject(principal, id);
628     * user.modify(&quot;telephonenumber&quot;, 
629     *      &quot;650.937.4444&quot;, ModSet.REPLACE);
630     * user.save();
631     * </pre>
632     * 
633     * <P>
634     * 
635     * @throws AccessRightsException
636     *             if an access rights exception occurs.
637     * @throws EntryNotFoundException
638     *             if the entry is not found
639     * @throws UMSException
640     *             on failure to save to persistent storage
641     *
642     * @supported.api
643     */
644    public void save() throws AccessRightsException, EntryNotFoundException,
645            UMSException {
646        String required = null;
647        if (m_modSet == null) {
648            return;
649        }
650        if (m_principal == null) {
651            required = "principal";
652        } else if (m_guid == null) {
653            required = "guid";
654        }
655        if (required != null) {
656            // TODO: This is not an illegal argument case. Should be
657            // a more sophisticated exception.
658            String args[] = new String[1];
659
660            args[0] = required;
661            String msg = i18n.getString(IUMSConstants.NO_REQUIRED, args)
662                    + " - "
663                    + i18n.getString(IUMSConstants.OBJECT_NOT_PERSISTENT);
664            throw new UMSException(msg);
665        }
666
667        try {
668            DataLayer.getInstance().modify(getPrincipal(), getGuid(), m_modSet);
669        } finally {
670            // Remember to set this to null as the changes
671            // are made persistent after this call
672            m_modSet = null;
673        }
674    }
675
676    /**
677     * Gets the attribute name that specifies the ID (or RDN in terms of DN in
678     * ldap) component in an object. Subclasses may choose to override this
679     * function. For instance, User takes either "uid" or "cn" for its
680     * identification
681     * <P>
682     * 
683     * @return Attribute name for identification
684     *
685     * @supported.api
686     */
687    public String getNamingAttribute() {
688
689        if (m_guid == null) {
690            return m_namingAttribute;
691        }
692
693        DN dn = new DN(getDN());
694
695        String[] components = dn.explodeDN(false);
696
697        if (components != null && components.length > 0) {
698            RDN rdn = new RDN(components[0]);
699            return rdn.getTypes()[0];
700        }
701        return null;
702    }
703
704    /**
705     * Gets the parent object
706     * 
707     * @return PersistentObject representing the parent object
708     * @throws UMSException
709     *             on failure instantiating the parent object
710     *
711     * @supported.api
712     */
713    public PersistentObject getParentObject() throws UMSException {
714
715        if (m_guid == null || m_principal == null) {
716            String msg;
717
718            if (m_principal == null) {
719                msg = i18n.getString(IUMSConstants.BAD_PRINCIPAL_HDL);
720            } else {
721                msg = i18n.getString(IUMSConstants.BAD_GUID);
722            }
723
724            throw new IllegalArgumentException(msg);
725        }
726
727        PersistentObject parent = UMSObject.getObject(getPrincipal(),
728                getParentGuid());
729        return parent;
730    }
731
732    /**
733     * Adds a child object to the persistent object container. All persistent
734     * objects can add objects as a container. To override this behavior or
735     * impose restrictions override the add method in a subclass so that e.g.
736     * User.add( object ) is restricted or disallowed in certain ways.
737     * 
738     * @param object Child object to be added to this persistent container.
739     * @throws AccessRightsException if an access rights exception occurs.
740     * @throws EntryAlreadyExistsException if the entry already exists.
741     * @throws UMSException if fail to add the given child object to the 
742     *         container. Possible causes include
743     *         <code>EntryAlreadyExists</code>, <code>AccessRights</code>
744     *         violation.
745     *
746     * @supported.api
747     */
748    public void addChild(PersistentObject object) throws AccessRightsException,
749            EntryAlreadyExistsException, UMSException {
750        if (object == null) {
751            String args[] = new String[1];
752
753            args[0] = this.toString();
754            String msg = i18n.getString(IUMSConstants.ADD_NULL_OBJ, args);
755
756            throw new IllegalArgumentException(msg);
757        }
758
759        String idAttr = object.getNamingAttribute();
760        String idValue = null;
761        Attr idAttrObj = object.getAttribute(idAttr);
762        if (idAttrObj != null) {
763            idValue = idAttrObj.getValue();
764        } else {
765            throw new UMSException(BAD_NAMING_ATTR + idAttr);
766        }
767
768        if (idAttr == null || idValue == null || idValue.length() == 0) {
769            String args[] = new String[1];
770
771            args[0] = object.toString();
772            String msg = i18n
773                    .getString(IUMSConstants.COMPOSE_GUID_FAILED, args);
774
775            throw new IllegalArgumentException(msg);
776        }
777
778        String childStr = null;
779
780        if (getGuid().getDn().length() > 0) {
781            childStr = idAttr + "=" + idValue + "," + getGuid().getDn();
782        } else {
783            childStr = idAttr + "=" + idValue;
784        }
785
786        Guid childGuid = new Guid(childStr);
787        object.setGuid(childGuid);
788
789        // Validation was done during the creation of the object
790        // Validation.validateAttributes( object.getAttrSet(),
791        // object.getClass(), this.getGUID() );
792
793        DataLayer.getInstance().addEntry(getPrincipal(), childGuid,
794                object.getAttrSet());
795
796        object.setModSet(null);
797        object.setPrincipal(getPrincipal());
798
799        EntityManager em = EntityManager.getEntityManager();
800        try {
801            em.execute(getPrincipal(), object, m_guid);
802        } catch (UMSException e) {
803            // TODO - we should log error...
804            if (debug.messageEnabled()) {
805                debug.message("PersistentObject.addChild : UMSException : "
806                        + e.getMessage());
807            }
808        }
809    }
810
811    /**
812     * Removes a child object from a persistent object container. It is
813     * important for constraints to be applied in overriding this method in
814     * subclasses of PersistentObject. For example, Organization may choose not
815     * to allow remove( object ) when object is an organization.
816     * 
817     * @param object Child object to be removed.
818     * @throws AccessRightsException if an access rights exception occurs.
819     * @throws EntryNotFoundException if the entry is not found.
820     * @throws UMSException if fail to remove the child object. Possible causes
821     *         includes EntryNotFount, AccessRights violation etc.
822     *
823     * @supported.api
824     */
825    public void removeChild(PersistentObject object)
826            throws AccessRightsException, EntryNotFoundException, UMSException {
827        String childStr;
828        if (object == null) {
829            String args[] = new String[1];
830
831            args[0] = this.toString();
832            String msg = i18n.getString(IUMSConstants.DEL_NULL_OBJ, args);
833            throw new IllegalArgumentException(msg);
834        }
835
836        childStr = object.getGuid().getDn();
837
838        // If this is an in-memory object, attempt to compose the guid
839        // for the child object
840        //
841        if (childStr == null) {
842            String idAttr = object.getNamingAttribute();
843            String idValue = object.getAttribute(idAttr).getValue();
844
845            if (idAttr == null || idValue == null || idValue.length() == 0) {
846                String args[] = new String[1];
847
848                args[0] = object.toString();
849                String msg = i18n.getString(IUMSConstants.COMPOSE_GUID_FAILED,
850                        args);
851
852                throw new IllegalArgumentException(msg);
853            }
854
855            if (getGuid().getDn().length() > 0) {
856                childStr = idAttr + "=" + idValue + "," + getGuid();
857            } else {
858                childStr = idAttr + "=" + idValue;
859            }
860        }
861
862        DN parentEntry = new DN(getDN());
863        DN childEntry = new DN(childStr);
864
865        if (!childEntry.isDescendantOf(parentEntry)) {
866            String msg = i18n.getString(IUMSConstants.BAD_CHILD_OBJ);
867            // TODO: need review. Should we throw something
868            // more meaningful
869            throw new IllegalArgumentException(msg);
870        }
871
872        DataLayer.getInstance().deleteEntry(getPrincipal(), new Guid(childStr));
873
874        // TODO: ??? do we need to mark the object that has been deleted
875        // with an invalid session
876        //
877        object.setGuid(new Guid("DELETED"));
878        object.setPrincipal(null);
879    }
880
881    /**
882     * Removes an object given its unique ID. This method expects the given
883     * child ID is a descendant (something being contained) in "this" object
884     * 
885     * @param childGuid Unique entry identification for the child to be removed.
886     * @throws AccessRights if an access rights exception occurs.
887     * @throws EntryNotFoundException if the entry is not found.
888     * @throws UMSException if failure to remove the entry from the persistent
889     *         store. Possible causes include AccessRights violation,
890     *         EntryNotFound etc.
891     *
892     * @supported.api
893     */
894    public void removeChild(Guid childGuid) throws AccessRightsException,
895            EntryNotFoundException, UMSException {
896        DN parentEntry = new DN(getDN());
897        DN childEntry = new DN(childGuid.getDn());
898
899        if (!childEntry.isDescendantOf(parentEntry)) {
900            String msg = i18n.getString(IUMSConstants.BAD_CHILD_OBJ);
901
902            throw new IllegalArgumentException(msg);
903        }
904
905        DataLayer.getInstance().deleteEntry(getPrincipal(), childGuid);
906    }
907
908    /**
909     * Remove itself from the persistent store. This method removes the object
910     * at hand from the persistent storage but keeps its internal data so that
911     * the ums client can save it to somewhere else or make reference to its
912     * internal data
913     * <P>
914     * 
915     * @throws AccessRights
916     *             Exception if an access rights exception occurs.
917     * @throws EntryNotFoundException
918     *             if the entry is not found
919     * @throws UMSException
920     *             from UMSSession.removeObject( principal, guid)
921     *
922     * @supported.api
923     */
924    public void remove() throws AccessRightsException, EntryNotFoundException,
925            UMSException {
926        // REVIEW: should we keep this method where an object can delete itself
927        // we don't allow an object to add itself ^%$#@
928
929        // If this is an in memory object with no reference to an entry,
930        // don't do anything
931        //
932        if (m_guid == null || m_principal == null)
933            return;
934
935        // Remove the object from persitent store
936        //
937        DataLayer.getInstance().deleteEntry(getPrincipal(), getGuid());
938
939        // Now reset it as a memory object with no reference to an entry on
940        // the persistent store. Possible use of this object in a move
941        // implementation. Call save(principal, namingAttr, parentGUID) to
942        // achieve
943        // the more functionality
944        //
945        setGuid(null);
946        setPrincipal(null);
947    }
948
949    /**
950     * Gets a string representation of the object
951     * 
952     * @return String representation of the object
953     *
954     * @supported.api
955     */
956    public String toString() {
957        StringBuilder sb = new StringBuilder();
958        sb.append("Object ID        :").append(m_guid).append("\n");
959        sb.append("Naming attribute :").append(getNamingAttribute()).append("\n");
960        sb.append("Class            :").append(getClass().getName()).append("\n");
961        sb.append("Principal        :").append(m_principal).append("\n");
962        sb.append("Attribute Set    :").append(m_attrSet).append("\n");
963        return sb.toString();
964    }
965
966    /**
967     * Gets the parent guid
968     * 
969     * @return string representation of the parent guid public String
970     *         getParentID() { return getParentID(null); }
971     */
972
973    /**
974     * Gets the parent guid
975     * 
976     * @return String representation of the parent guid
977     */
978    public Guid getParentGuid() {
979        if (m_guid == null || m_principal == null) {
980            return null;
981        }
982
983        DN dn = new DN(getDN());
984        return new Guid(dn.getParent().toString());
985    }
986
987    /**
988     * Gets the immediate children, subject to search filter constraints. Only
989     * the IDs and object classes of each child are returned.
990     * <P>
991     * 
992     * @param filter
993     *            Search filter
994     * @param searchControl
995     *            Search control
996     * @return Result child IDs in Vector
997     * @throws InvalidSearchFilterException
998     *             if the search filter is invalid
999     * @throws UMSException
1000     *             on searching for immediate children in the container
1001     *
1002     * @supported.api
1003     */
1004    public SearchResults getChildren(String filter, SearchControl searchControl)
1005            throws InvalidSearchFilterException, UMSException {
1006        // default is one level search scope in getChildren
1007        //
1008        int scope = SearchControl.SCOPE_ONE;
1009        if (searchControl != null) {
1010            scope = searchControl.getSearchScope(scope);
1011        }
1012
1013        SearchResults results = DataLayer.getInstance().searchIDs(
1014                getPrincipal(), getGuid(), scope, filter, searchControl);
1015        results.setPrincipal(getPrincipal());
1016        return results;
1017    }
1018
1019    /**
1020     * Gets the immediate children, returning only specified attributes
1021     * 
1022     * @param filter
1023     *            Search filter
1024     * @param resultAttributeNames
1025     *            Names of attributes to retrieve
1026     * @param searchControl
1027     *            Search control object
1028     * @return SearchResults
1029     * @throws InvalidSearchFilterException
1030     *             on invalid search filter
1031     * @throws UMSException
1032     *             on failure with searhing
1033     * 
1034     * @supported.api
1035     */
1036    public SearchResults getChildren(String filter,
1037            String[] resultAttributeNames, SearchControl searchControl)
1038            throws InvalidSearchFilterException, UMSException {
1039
1040        // default is one level search scope in getChildren
1041        //
1042        int scope = SearchControl.SCOPE_ONE;
1043        if (searchControl != null) {
1044            scope = searchControl.getSearchScope(scope);
1045        }
1046
1047        SearchResults searchResults = DataLayer.getInstance().search(
1048                getPrincipal(), getGuid(), scope, filter, resultAttributeNames,
1049                false, searchControl);
1050        searchResults.setPrincipal(getPrincipal());
1051
1052        return searchResults;
1053    }
1054
1055    /**
1056     * Gets all immediate children under current node based on search criteria
1057     * specified in template, and returns attributes specified there. Search
1058     * behavior is controlled by searchControl.
1059     * <P>
1060     * 
1061     * Returning attributes are determined by the search template
1062     * <P>
1063     * 
1064     * @param template
1065     *            Search template
1066     * @param searchControl
1067     *            Search control, use default setting if searchControl == null
1068     * @throws UMSException
1069     *             on failure with searching
1070     *
1071     * @supported.api
1072     */
1073    public SearchResults getChildren(SearchTemplate template,
1074            SearchControl searchControl) throws UMSException {
1075        return getChildren(template.getSearchFilter(), template
1076                .getAttributeSet().getAttributeNames(), searchControl);
1077    }
1078
1079    /**
1080     * Gets only the IDs and object classes of all objects at the current level
1081     * and below which match the search filter.
1082     * 
1083     * @param filter
1084     *            Search filter
1085     * @param searchControl
1086     *            Search control object
1087     * @throws InvalidSearchFilterException
1088     *             on invalid search filter
1089     * @throws UMSException
1090     *             on failure with searching
1091     *
1092     * @supported.api
1093     */
1094    public SearchResults search(String filter, SearchControl searchControl)
1095            throws InvalidSearchFilterException, UMSException {
1096
1097        // Default search scope is Subtree type of search
1098        //
1099        int scope = SearchControl.SCOPE_SUB;
1100        if (searchControl != null) {
1101            scope = searchControl.getSearchScope(scope);
1102        }
1103
1104        SearchResults results = DataLayer.getInstance().searchIDs(
1105                getPrincipal(), getGuid(), scope, filter, searchControl);
1106        results.setPrincipal(getPrincipal());
1107        return results;
1108    }
1109
1110    /**
1111     * Gets the specified attributes of all objects at the current level and
1112     * below which match the search filter.
1113     * 
1114     * @param filter
1115     *            Search filter
1116     * @param resultAttributeNames
1117     *            Names of attributes to retrieve
1118     * @param searchControl
1119     *            Search control object
1120     * @return SearchResults
1121     * @throws InvalidSearchFilterException
1122     *             on invalid search filter
1123     * @throws UMSException
1124     *             on failure with searching
1125     * 
1126     *
1127     * @supported.api
1128     */
1129    public SearchResults search(String filter, String[] resultAttributeNames,
1130            SearchControl searchControl) throws InvalidSearchFilterException,
1131            UMSException {
1132
1133        // Default search scope is Subtree type of search
1134        //
1135        int scope = SearchControl.SCOPE_SUB;
1136        if (searchControl != null) {
1137            scope = searchControl.getSearchScope(scope);
1138        }
1139
1140        SearchResults results = DataLayer.getInstance().search(getPrincipal(),
1141                getGuid(), scope, filter, resultAttributeNames, false,
1142                searchControl);
1143        results.setPrincipal(getPrincipal());
1144        return results;
1145    }
1146
1147    /**
1148     * Gets the attributes specified in the template for all objects at the
1149     * current level and below which match the search filter in the template.
1150     * Search behavior is controlled by searchControl.
1151     * <P>
1152     * 
1153     * @param template
1154     *            Search template
1155     * @param searchControl
1156     *            Search control, use default setting if searchControl == null
1157     * @throws UMSException
1158     *             on failure to search
1159     *
1160     * @supported.api
1161     */
1162    public SearchResults search(SearchTemplate template,
1163            SearchControl searchControl) throws UMSException {
1164        return search(template.getSearchFilter(), template.getAttributeSet()
1165                .getAttributeNames(), searchControl);
1166    }
1167
1168    /**
1169     * Gets the DN of the entity
1170     * 
1171     * @return String representing the distinguished name of the entry
1172     * 
1173     */
1174    public String getDN() {
1175        if (m_guid != null)
1176            return m_guid.getDn();
1177        else
1178            return null;
1179    }
1180
1181    /**
1182     * Sets the GUID of the entity; used within the package
1183     * 
1184     * @param guid
1185     *            String representation of guid
1186     */
1187    protected void setGuid(Guid guid) {
1188
1189        m_guid = guid;
1190    }
1191
1192    /**
1193     * Return the authenticated principal that is used to instantiate this
1194     * object
1195     * 
1196     * @return authenticated principal that is used to instantiate this object,
1197     *         return null if no authenticated principal is associated with this
1198     *         object
1199     */
1200    Principal getPrincipal() {
1201        return m_principal;
1202    }
1203
1204    /**
1205     * Set current authenticated session; used within the package
1206     * 
1207     * @param session
1208     *            A valid authenticated session
1209     */
1210    void setPrincipal(Principal principal) {
1211        m_principal = principal;
1212    }
1213
1214    /**
1215     * Sets the attribute set
1216     * 
1217     * @param attrSet
1218     *            The attribute set to be assigned as a reference (not a deep
1219     *            copy)
1220     */
1221    protected void setAttrSet(AttrSet attrSet) {
1222        m_attrSet = attrSet;
1223    }
1224
1225    /**
1226     * Gets the attribute set as a reference, not a deep copy
1227     * 
1228     * @return The in-memory attribute set
1229     */
1230    protected AttrSet getAttrSet() {
1231        return m_attrSet;
1232    }
1233
1234    /**
1235     * Checks if javaclass of the persistent object is the expected class
1236     * defined in its objectclass attribute.
1237     * 
1238     * @throws UMSException
1239     *             when the objectclass maps to a javaclass different from the
1240     *             one being constructed.
1241     * @see com.iplanet.ums.TempateManager#getJavaClassForEntry
1242     */
1243    void verifyClass() throws UMSException {
1244        Class expectedClass = 
1245            TemplateManager.getTemplateManager().getJavaClassForEntry(
1246                    this.getGuid().getDn(), this.getAttrSet());
1247
1248        // TODO: need to be reviewed and see if subclasses of entity are
1249        // allowed.
1250        // e.g. PersistentObject -> User -> MailUser kind of inheritence, do we
1251        // accept the formation of User class for MailUser.
1252        //
1253        if (this.getClass() != expectedClass) {
1254            String msg = i18n.getString(IUMSConstants.UNMATCHED_CLASS);
1255            // TODO: review for better exception
1256            throw new IllegalArgumentException(msg);
1257        }
1258    }
1259
1260    /**
1261     * Maps to a DN from naming attribute value of a persistent object.
1262     * 
1263     * @param namingAttribute Naming attribute of the object.
1264     * @param name Naming attribute value of the object.
1265     * @param parentID Array of its parent names, all assumed to take 
1266     *        <code>o</code> as the naming attributes.
1267     */
1268    static public String idToDN(
1269        String namingAttribute,
1270        String name,
1271        String[] parentID
1272    ) {
1273        StringBuilder sb = new StringBuilder();
1274
1275        sb.append(namingAttribute).append("=").append(name);
1276        for (int i = 0; i < parentID.length; i++) {
1277            if (parentID[i] != null) {
1278                // TODO: ??? This is hardcoded to take "o=something" as the
1279                // parent node(s). Needs a flexible scheme to handle
1280                // flexible DIT.
1281                sb.append(",o=").append(parentID[i]);
1282            }
1283        }
1284
1285        return sb.toString();
1286    }
1287
1288    /**
1289     * Maps a dn to guid
1290     * <P>
1291     * TODO: Not yet implemented
1292     * <P>
1293     */
1294    static String dnToGuid(String dn) {
1295        // TODO: Need to fill in base 64 encoding <P>
1296        //
1297        return dn;
1298    }
1299
1300    /**
1301     * Maps a guid to dn
1302     * <P>
1303     * TODO: Not yet implemented
1304     * <P>
1305     */
1306    static String guidToDN(String guid) {
1307        // TODO: Need to fill in base 64 encoding <P>
1308        //
1309        return guid;
1310    }
1311
1312    /**
1313     * Reads in the object from persistent store, assuming that the guid and
1314     * session are valid
1315     * 
1316     * @throws UMSException
1317     *             on failure in reading the object from persistent store
1318     */
1319    synchronized private void read() throws UMSException {
1320        if (m_principal == null || m_guid == null) {
1321            // TODO: there should be some warning to the client here
1322            return;
1323        }
1324
1325        m_attrSet = DataLayer.getInstance().read(getPrincipal(), getGuid());
1326    }
1327
1328    private void checkCache() {
1329        if (m_principal != null && m_guid != null && m_attrSet == null) {
1330            try {
1331                read();
1332            } catch (UMSException e) {
1333                // TODO: there should be some warning to the client here
1334                if (debug.messageEnabled()) {
1335                    debug.message("PersistentObject.checkCache() : " +
1336                            "UMSException : " + e.getMessage());
1337                }
1338            }
1339        }
1340    }
1341
1342    /**
1343     * Internal use only, set the internal modset. Can be used to nullify the
1344     * internal state of m_modSet
1345     * 
1346     * @param modSet
1347     *            Modification Set to be used for the internal
1348     *            <code>modSet</code>.
1349     */
1350    void setModSet(ModSet modSet) {
1351        m_modSet = modSet;
1352    }
1353
1354    /**
1355     * Checks if this object is a member of an IMembership. Roles and Groups
1356     * implement IMembership
1357     * 
1358     * @param im
1359     *            Role or Group against which the membership is to be checked
1360     * @return <code>true</code> if this object is a member of the
1361     *         IMembership, <code>false</code> otherwise
1362     * @throws UMSException
1363     *             propagates any exception from the datalayer
1364     *
1365     * @supported.api
1366     */
1367    public boolean isMemberOf(IMembership im) throws UMSException {
1368        return im.hasMember(getGuid());
1369    }
1370
1371    /**
1372     * Gets the list of GUIDS roles assosciated with this object
1373     * 
1374     * @return list that lets iterating over role GUIDs
1375     * @throws UMSException
1376     *             propagates any exception from the datalayer
1377     *
1378     * @supported.api
1379     */
1380    public Collection getRoles() throws UMSException {
1381        ArrayList roleList = new ArrayList();
1382        Attr roleAttr = getAttribute(COMPUTED_MEMBER_ATTR_NAME);
1383        if (roleAttr != null && roleAttr.getStringValues() != null) {
1384            roleList.addAll(Arrays.asList(roleAttr.getStringValues()));
1385        }
1386        return roleList;
1387    }
1388
1389    /**
1390     * Returns all the ACIs of this object.
1391     * 
1392     * @return collecion of ACIs of this object.
1393     * @throws ACIParseException if any error
1394     *
1395     * @supported.api
1396     */
1397    public Collection getACI() throws ACIParseException, UMSException {
1398        Collection acis = new ArrayList();
1399        Attr aciAttr = getAttribute(ACI.ACI);
1400        if (aciAttr != null) {
1401            String[] aciTexts = aciAttr.getStringValues();
1402            int size = aciTexts.length;
1403            for (int i = 0; i < size; i++) {
1404                acis.add(ACI.valueOf(aciTexts[i]));
1405            }
1406        }
1407        return acis;
1408    }
1409
1410    /**
1411     * Returns all the ACIs of this object with the given name.
1412     * 
1413     * @param name Name of the ACI to get.
1414     * @return collecion of ACIs of this object.
1415     * @throws ACIParseException in case of any error
1416     *
1417     * @supported.api
1418     */
1419    public Collection getACI(String name) throws ACIParseException,
1420            UMSException {
1421        Collection acis = new ArrayList();
1422        Attr aciAttr = getAttribute(ACI.ACI);
1423        if (aciAttr != null) {
1424            String[] aciTexts = aciAttr.getStringValues();
1425            int size = aciTexts.length;
1426            for (int i = 0; i < size; i++) {
1427                ACI aci = ACI.valueOf(aciTexts[i]);
1428                if (aci.getName().equals(name)) {
1429                    acis.add(aci);
1430                }
1431            }
1432        }
1433        return acis;
1434    }
1435
1436    /**
1437     * Adds an ACI to this object.
1438     * 
1439     * @param aci ACI added to be added to this object.
1440     * @throws AccessRightsException if an access rights exception occurs.
1441     * @throws UMSException if any error
1442     *
1443     * @supported.api
1444     */
1445    public void addACI(ACI aci) throws AccessRightsException, UMSException {
1446        Attr attr = new Attr(ACI.ACI, aci.toString());
1447        modify(attr, ModSet.ADD);
1448        save();
1449    }
1450
1451    /**
1452     * Deletes an ACI of this object
1453     * 
1454     * @param aci ACI to be deleted.
1455     * @throws AccessRightsException if an access rights exception occurs.
1456     * @throws UMSException if any error.
1457     *
1458     * @supported.api
1459     */
1460    public void deleteACI(ACI aci) throws AccessRightsException, UMSException {
1461        Attr attr = new Attr(ACI.ACI, aci.getACIText());
1462        modify(attr, ModSet.DELETE);
1463        save();
1464    }
1465
1466    /**
1467     * Replaces an ACI of this object
1468     * 
1469     * @param oldACI ACI to be replaced.
1470     * @param newACI the new ACI.
1471     * @throws AccessRightsException if an access rights exception occurs.
1472     * @throws UMSException if any error.
1473     *
1474     * @supported.api
1475     */
1476    public void replaceACI(ACI oldACI, ACI newACI)
1477            throws AccessRightsException, UMSException {
1478        Attr attr = new Attr(ACI.ACI, oldACI.getACIText());
1479        modify(attr, ModSet.DELETE);
1480        attr = new Attr(ACI.ACI, newACI.toString());
1481        modify(attr, ModSet.ADD);
1482        save();
1483    }
1484
1485    /**
1486     * Adds value for an attribute and saves the change in the database.
1487     * 
1488     * @param token Authenticated prinicpal's single sign on token.
1489     * @param guid Identifiation of the entry to which to add the attribute
1490     *        value.
1491     * @param name Name of the attribute to which value is being added.
1492     * @param value Value to be added to the attribute.
1493     * @throws UMSException if any exception from the data layer.
1494     *
1495     * @supported.api
1496     */
1497    public static void addAttributeValue(
1498        SSOToken token,
1499        Guid guid,
1500        String name,
1501        String value
1502    ) throws UMSException {
1503        if (guid == null) {
1504            throw new IllegalArgumentException(i18n
1505                    .getString(IUMSConstants.NULL_GUIDS));
1506        }
1507        if (token == null) {
1508            throw new IllegalArgumentException(i18n
1509                    .getString(IUMSConstants.NULL_TOKEN));
1510        }
1511        try {
1512            SSOTokenManager.getInstance().validateToken(token);
1513        } catch (SSOException se) {
1514            throw new UMSException(i18n.getString(IUMSConstants.INVALID_TOKEN),
1515                    se);
1516        }
1517
1518        Attr attr = new Attr(name, value);
1519        attr = null;
1520        Validation.validateAttribute(attr, UMSObject.getObject(token, guid)
1521                .getClass(), guid);
1522        try {
1523            DataLayer.getInstance().addAttributeValue(token.getPrincipal(),
1524                    guid, name, value);
1525        } catch (SSOException se) {
1526            throw new UMSException(i18n.getString(IUMSConstants.BAD_TOKEN_HDL),
1527                    se);
1528        }
1529    }
1530
1531    /**
1532     * Removes value for an attribute and saves the change in the database.
1533     * 
1534     * @param token Authenticated prinicpal's single sign on token.
1535     * @param guid Identification of the entry from which to remove the
1536     *        attribute value.
1537     * @param name Name of the attribute from which value is being removed.
1538     * @param value Value to be removed from the attribute.
1539     * @throws UMSException if any exception from the data layer.
1540     *
1541     * @supported.api
1542     */
1543    public static void removeAttributeValue(SSOToken token, Guid guid,
1544            String name, String value) throws UMSException {
1545        if (guid == null) {
1546            throw new IllegalArgumentException(i18n
1547                    .getString(IUMSConstants.NULL_GUIDS));
1548        }
1549        if (token == null) {
1550            throw new IllegalArgumentException(i18n
1551                    .getString(IUMSConstants.NULL_TOKEN));
1552        }
1553        try {
1554            SSOTokenManager.getInstance().validateToken(token);
1555        } catch (SSOException se) {
1556            throw new UMSException(i18n.getString(IUMSConstants.INVALID_TOKEN),
1557                    se);
1558        }
1559
1560        try {
1561            DataLayer.getInstance().removeAttributeValue(token.getPrincipal(),
1562                    guid, name, value);
1563        } catch (SSOException se) {
1564            throw new UMSException(i18n.getString(IUMSConstants.BAD_TOKEN_HDL),
1565                    se);
1566        }
1567    }
1568
1569    /**
1570     * Check if the object is persistent in the system
1571     * 
1572     * @return true if the object is persistent in the system and false
1573     *         otherwise
1574     */
1575    protected boolean isPersistent() {
1576        return m_principal != null && m_guid != null
1577                && m_guid.getDn().length() > 0;
1578    }
1579
1580    /**
1581     * Find the names of attributes not read from data store so far
1582     * 
1583     * @param attrNames
1584     *            names of attributes to get
1585     * @return collection of names of attributes not read from data store so far
1586     */
1587    private Collection findAttributesNotRead(String[] attrNames) {
1588        ArrayList attributesNotInCache = new ArrayList();
1589        if (m_attrSet == null) {
1590            m_attrSet = new AttrSet();
1591        }
1592        if (m_nullAttributes == null) {
1593            m_nullAttributes = new ArrayList();
1594        }
1595        int length = attrNames.length;
1596        for (int i = 0; i < length; i++) {
1597            if ((m_attrSet.getAttribute(attrNames[i]) == null)
1598                    && !m_nullAttributes.contains(attrNames[i])) {
1599                attributesNotInCache.add(attrNames[i]);
1600            }
1601        }
1602        return attributesNotInCache;
1603    }
1604
1605    /**
1606     * Find whether the attribute was not read from data store so far
1607     * 
1608     * @param attrName
1609     *            name of attribute to check
1610     * @return <code>true</code> if the attribute was not read, otherwise
1611     *         <code>false</code>
1612     */
1613    private boolean isAttributeNotRead(String attrName) {
1614        boolean attributeNotRead = false;
1615        if (m_attrSet == null) {
1616            m_attrSet = new AttrSet();
1617        }
1618        if (m_nullAttributes == null) {
1619            m_nullAttributes = new ArrayList();
1620        }
1621        if ((m_attrSet.getAttribute(attrName) == null)
1622                && !m_nullAttributes.contains(attrName)) {
1623            attributeNotRead = true;
1624        }
1625        return attributeNotRead;
1626    }
1627
1628    /**
1629     * Read the attributes from data store
1630     * 
1631     * @param attrNames
1632     *            names of attributes to get
1633     * @return collection of Attr read from data store
1634     */
1635    private Collection readAttributesFromDataStore(Collection attrNames)
1636            throws UMSException {
1637        Collection attributes = DataLayer.getInstance().getAttributes(
1638                getPrincipal(), getGuid(), attrNames);
1639        if (attributes == null) {
1640            String[] args = { getDN() };
1641            throw new UMSException(i18n.getString(
1642                    IUMSConstants.READ_ATTRIBUTES_ERROR, args));
1643        }
1644        Collection foundAttributes = new ArrayList();
1645        if (m_attrSet == null) {
1646            m_attrSet = new AttrSet();
1647        }
1648        if (m_nullAttributes == null) {
1649            m_nullAttributes = new ArrayList();
1650        }
1651        Iterator iter = attributes.iterator();
1652        while (iter.hasNext()) {
1653            Attr attr = (Attr) iter.next();
1654            foundAttributes.add(attr.getName());
1655            m_attrSet.replace(attr);
1656        }
1657        iter = attrNames.iterator();
1658        while (iter.hasNext()) {
1659            String attrName = (String) iter.next();
1660            if (!foundAttributes.contains(attrName)
1661                    && !m_nullAttributes.contains(attrName)) {
1662                m_nullAttributes.add(attrName);
1663            }
1664        }
1665        return attributes;
1666    }
1667
1668    /**
1669     * Read the attribute from data store
1670     * 
1671     * @param attrName
1672     *            names of attributes to get
1673     * @return Attr read from datastore
1674     */
1675    private Attr readAttributeFromDataStore(String attrName) throws UMSException {
1676        Attr attr = DataLayer.getInstance().getAttribute(getPrincipal(),
1677                getGuid(), attrName);
1678        if (m_attrSet == null) {
1679            m_attrSet = new AttrSet();
1680        }
1681        if (m_nullAttributes == null) {
1682            m_nullAttributes = new ArrayList();
1683        }
1684        if (attr != null) {
1685            m_attrSet.replace(attr);
1686        } else if (!m_nullAttributes.contains(attrName)) {
1687            m_nullAttributes.add(attrName);
1688        }
1689        return attr;
1690    }
1691
1692    /**
1693     * Get the attribute from cache, does not read data store
1694     * 
1695     * @param attrName
1696     *            name of attribute to get
1697     * @return Attr read from cache
1698     */
1699    private Attr getAttributeFromCache(String attrName) {
1700        Attr attr = null;
1701        if (m_attrSet != null) {
1702            attr = m_attrSet.getAttribute(attrName);
1703        }
1704        return attr;
1705    }
1706
1707    /**
1708     * Authenticated session in constructing the object
1709     * 
1710     * @serial
1711     */
1712    private Principal m_principal;
1713
1714    /**
1715     * Identification of the object
1716     * 
1717     * @serial
1718     */
1719    private Guid m_guid;
1720
1721    /**
1722     * Internal cache for attributes
1723     * 
1724     * @serial
1725     */
1726    private AttrSet m_attrSet;
1727
1728    /**
1729     * Internal cache for attributes read from directory and found to be null
1730     * 
1731     * @serial
1732     */
1733    private ArrayList m_nullAttributes;
1734
1735    /**
1736     * Internal cache for changes made to the object
1737     * 
1738     * @serial
1739     */
1740    private ModSet m_modSet;
1741
1742    /**
1743     * Internal naming attribute (ex: "ou")
1744     * 
1745     * @serial
1746     */
1747    private String m_namingAttribute = null;
1748}




























































Copyright © 2010-2017, ForgeRock All Rights Reserved.