001    /**
002     * Copyright 2005-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.kim.impl.jaxb;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.joda.time.DateTime;
020    import org.kuali.rice.core.api.membership.MemberType;
021    import org.kuali.rice.core.api.util.jaxb.DateTimeAdapter;
022    import org.kuali.rice.core.util.jaxb.NameAndNamespacePair;
023    import org.kuali.rice.core.util.jaxb.NameAndNamespacePairValidatingAdapter;
024    import org.kuali.rice.kim.api.group.GroupContract;
025    import org.kuali.rice.kim.api.identity.principal.PrincipalContract;
026    import org.kuali.rice.kim.api.jaxb.QualificationListAdapter;
027    import org.kuali.rice.kim.api.role.RoleContract;
028    import org.kuali.rice.kim.api.role.RoleMember;
029    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
030    
031    import javax.xml.bind.Unmarshaller;
032    import javax.xml.bind.annotation.XmlAccessType;
033    import javax.xml.bind.annotation.XmlAccessorType;
034    import javax.xml.bind.annotation.XmlElement;
035    import javax.xml.bind.annotation.XmlTransient;
036    import javax.xml.bind.annotation.XmlType;
037    import javax.xml.bind.annotation.adapters.NormalizedStringAdapter;
038    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
039    import java.io.Serializable;
040    import java.util.HashMap;
041    import java.util.Map;
042    
043    /**
044     * Base class representing an unmarshalled <roleMember> element.
045     * Refer to the static inner classes for more information about the specific contexts.
046     * 
047     * @author Kuali Rice Team (rice.collab@kuali.org)
048     */
049    @XmlTransient
050    public abstract class RoleMemberXmlDTO implements Serializable {
051    
052        private static final long serialVersionUID = 1L;
053    
054        @XmlElement(name="principalId")
055        @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
056        private String principalId;
057        
058        @XmlElement(name="principalName")
059        @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
060        private String principalName;
061        
062        @XmlElement(name="groupId")
063        @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
064        private String groupId;
065        
066        @XmlElement(name="groupName")
067        @XmlJavaTypeAdapter(NameAndNamespacePairValidatingAdapter.class)
068        private NameAndNamespacePair groupName;
069        
070        @XmlElement(name="roleIdAsMember")
071        @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
072        private String roleIdAsMember;
073        
074        @XmlElement(name="roleNameAsMember")
075        @XmlJavaTypeAdapter(NameAndNamespacePairValidatingAdapter.class)
076        private NameAndNamespacePair roleNameAsMember;
077        
078        @XmlElement(name="activeFromDate")
079        @XmlJavaTypeAdapter(DateTimeAdapter.class)
080        private DateTime activeFromDate;
081        
082        @XmlElement(name="activeToDate")
083        @XmlJavaTypeAdapter(DateTimeAdapter.class)
084        private DateTime activeToDate;
085        
086        @XmlElement(name="qualifications")
087        @XmlJavaTypeAdapter(QualificationListAdapter.class)
088        private Map<String, String> qualifications;
089        
090        @XmlTransient
091        private MemberType memberType;
092        
093        /**
094         * Constructs an empty RoleMemberXmlDTO instance.
095         */
096        public RoleMemberXmlDTO() {}
097        
098        /**
099         * Constructs a RoleMemberXmlDTO instance that is populated with the info from the given role member.
100         * 
101         * @param roleMember The role member that this DTO should populate its data from.
102         * @param populateMemberId If true, the member principal/group/role ID will get populated; otherwise, only
103         * the member principal/group/role name and (if applicable) namespace will get populated.
104         * @throws IllegalArgumentException if roleMember is null, has an invalid member type, or refers to a nonexistent principal/group/role.
105         */
106        public RoleMemberXmlDTO(RoleMember roleMember, boolean populateMemberId) {
107            if (roleMember == null) {
108                throw new IllegalArgumentException("roleMember cannot be null");
109            }
110            this.memberType = roleMember.getType();
111            this.activeFromDate = roleMember.getActiveFromDate();
112            this.activeToDate = roleMember.getActiveToDate();
113            this.qualifications = (roleMember.getAttributes() != null) ? roleMember.getAttributes() : new HashMap<String, String>();
114            
115            if (MemberType.PRINCIPAL.equals(memberType)) {
116                if (populateMemberId) {
117                    this.principalId = roleMember.getMemberId();
118                }
119                PrincipalContract principal = KimApiServiceLocator.getIdentityService().getPrincipal(
120                        roleMember.getMemberId());
121                if (principal == null) {
122                    throw new IllegalArgumentException("Cannot find principal with ID \"" +  roleMember.getMemberId() + "\"");
123                }
124                this.principalName = principal.getPrincipalName();
125            } else if (MemberType.GROUP.equals(memberType)) {
126                if (populateMemberId) {
127                    this.groupId = roleMember.getMemberId();
128                }
129                GroupContract group = KimApiServiceLocator.getGroupService().getGroup(roleMember.getMemberId());
130                if (group == null) {
131                    throw new IllegalArgumentException("Cannot find group with ID \"" + roleMember.getMemberId() + "\"");
132                }
133                this.groupName = new NameAndNamespacePair(group.getNamespaceCode(), group.getName());
134            } else if (MemberType.ROLE.equals(memberType)) {
135                if (populateMemberId) {
136                    this.roleIdAsMember = roleMember.getMemberId();
137                }
138                RoleContract role = KimApiServiceLocator.getRoleService().getRole(roleMember.getMemberId());
139                if (role == null) {
140                    throw new IllegalArgumentException("Cannot find role with ID \"" + roleMember.getMemberId() + "\"");
141                }
142                this.roleNameAsMember = new NameAndNamespacePair(role.getNamespaceCode(), role.getName());
143            } else {
144                throw new IllegalArgumentException("Cannot construct a RoleMemberXmlDTO from a role member with an unrecognized member type code of \"" +
145                        memberType + "\"");
146            }    
147        }
148    
149        /**
150         * @return the principalId
151         */
152        public String getPrincipalId() {
153            return this.principalId;
154        }
155    
156        /**
157         * @param principalId the principalId to set
158         */
159        public void setPrincipalId(String principalId) {
160            this.principalId = principalId;
161        }
162    
163        /**
164         * @return the principalName
165         */
166        public String getPrincipalName() {
167            return this.principalName;
168        }
169    
170        /**
171         * @param principalName the principalName to set
172         */
173        public void setPrincipalName(String principalName) {
174            this.principalName = principalName;
175        }
176    
177        /**
178         * @return the groupId
179         */
180        public String getGroupId() {
181            return this.groupId;
182        }
183    
184        /**
185         * @param groupId the groupId to set
186         */
187        public void setGroupId(String groupId) {
188            this.groupId = groupId;
189        }
190    
191        /**
192         * @return the groupName
193         */
194        public NameAndNamespacePair getGroupName() {
195            return this.groupName;
196        }
197    
198        /**
199         * @param groupName the groupName to set
200         */
201        public void setGroupName(NameAndNamespacePair groupName) {
202            this.groupName = groupName;
203        }
204    
205        /**
206         * @return the roleIdAsMember
207         */
208        public String getRoleIdAsMember() {
209            return this.roleIdAsMember;
210        }
211    
212        /**
213         * @param roleIdAsMember the roleIdAsMember to set
214         */
215        public void setRoleIdAsMember(String roleIdAsMember) {
216            this.roleIdAsMember = roleIdAsMember;
217        }
218    
219        /**
220         * @return the roleNameAsMember
221         */
222        public NameAndNamespacePair getRoleNameAsMember() {
223            return this.roleNameAsMember;
224        }
225    
226        /**
227         * @param roleNameAsMember the roleNameAsMember to set
228         */
229        public void setRoleNameAsMember(NameAndNamespacePair roleNameAsMember) {
230            this.roleNameAsMember = roleNameAsMember;
231        }
232    
233        /**
234         * @return the activeFromDate
235         */
236        public DateTime getActiveFromDate() {
237            return this.activeFromDate;
238        }
239    
240        /**
241         * @param activeFromDate the activeFromDate to set
242         */
243        public void setActiveFromDate(DateTime activeFromDate) {
244            this.activeFromDate = activeFromDate;
245        }
246    
247        /**
248         * @return the activeToDate
249         */
250        public DateTime getActiveToDate() {
251            return this.activeToDate;
252        }
253    
254        /**
255         * @param activeToDate the activeToDate to set
256         */
257        public void setActiveToDate(DateTime activeToDate) {
258            this.activeToDate = activeToDate;
259        }
260    
261        /**
262         * @return the qualifications
263         */
264        public Map<String, String> getQualifications() {
265            return this.qualifications;
266        }
267    
268        /**
269         * @param qualifications the qualifications to set
270         */
271        public void setQualifications(Map<String, String> qualifications) {
272            this.qualifications = qualifications;
273        }
274    
275        /**
276         * Retrieves the member type.
277         * 
278         * <p>If the member type is null at the time that this method is invoked, an attempt will be made to set its
279         * value based on any populated member principal/group/role ID/name information.
280         * 
281         * @return the member type, or null if no membership identification information has been set on this member.
282         * @throws IllegalStateException if the role member is populated simultaneously with multiple member ID/name information
283         */
284        public MemberType getMemberType() {
285            if (memberType == null) {
286                boolean foundMemberInfo = false;
287                
288                if (StringUtils.isNotBlank(principalId) || StringUtils.isNotBlank(principalName)) {
289                    memberType = MemberType.PRINCIPAL;
290                    foundMemberInfo = true;
291                }
292                
293                if (StringUtils.isNotBlank(groupId) || groupName != null) {
294                    if (foundMemberInfo) {
295                        memberType = null;
296                        throw new IllegalStateException("Cannot have a role member that is simultaneously populated with member principal, member group, and/or member role information");
297                    }
298                    memberType = MemberType.GROUP;
299                    foundMemberInfo = true;
300                }
301                
302                if (StringUtils.isNotBlank(roleIdAsMember) || roleNameAsMember != null) {
303                    if (foundMemberInfo) {
304                        memberType = null;
305                        throw new IllegalStateException("Cannot have a role member that is simultaneously populated with member principal, member group, and/or member role information");
306                    }
307                    memberType = MemberType.ROLE;
308                    foundMemberInfo = true;
309                }
310            }
311            return this.memberType;
312        }
313    
314        /**
315         * Retrieves the role member's ID, based on the member type and any populated member principal/group/role IDs.
316         * 
317         * <p>If the member type is null at the time that this method is invoked, an attempt will be made to set its
318         * value based on any populated member principal/group/role ID/name information.
319         * 
320         * @return The member's ID, or null if the member type is null or the associated member ID information is null.
321         */
322        public String getMemberId() {
323            if (MemberType.PRINCIPAL.equals(getMemberType())) {
324                return principalId;
325            } else if (MemberType.GROUP.equals(getMemberType())) {
326                return groupId;
327            } else if (MemberType.ROLE.equals(getMemberType())) {
328                return roleIdAsMember;
329            }
330            return null;
331        }
332        
333        /**
334         * Retrieves the role member's name, based on the member type and any populated member principal/group/role names.
335         * 
336         * <p>If the member type is null at the time that this method is invoked, an attempt will be made to set its
337         * value based on any populated member principal/group/role ID/name information.
338         * 
339         * @return The member's name, or null if the member type is null or the associated member name information is null.
340         */
341        public String getMemberName() {
342            if (MemberType.PRINCIPAL.equals(getMemberType())) {
343                return principalName;
344            } else if (MemberType.GROUP.equals(getMemberType())) {
345                return (groupName != null) ? groupName.getName() : null;
346            } else if (MemberType.ROLE.equals(getMemberType())) {
347                return (roleNameAsMember != null) ? roleNameAsMember.getName() : null;
348            }
349            return null;
350        }
351        
352        /**
353         * Retrieves the role member's namespace code, based on the member type and any populated member principal/group/role names.
354         * 
355         * <p>If the member type is null at the time that this method is invoked, an attempt will be made to set its
356         * value based on any populated member principal/group/role ID/name information.
357         * 
358         * @return The member's namespace code, or null if the member type is null, the associated member name information is null,
359         * or the role member is a principal.
360         */
361        public String getMemberNamespaceCode() {
362            if (MemberType.PRINCIPAL.equals(getMemberType())) {
363                return null;
364            } else if (MemberType.GROUP.equals(getMemberType())) {
365                return (groupName != null) ? groupName.getName() : null;
366            } else if (MemberType.ROLE.equals(getMemberType())) {
367                return (roleNameAsMember != null) ? roleNameAsMember.getName() : null;
368            }
369            return null;
370        }
371        
372        /**
373         * Retrieves the ID of the role that this member belongs to.
374         * Subclasses are responsible for implementing this method so that it does so.
375         * 
376         * @return The role ID of the role that this member belongs to.
377         */
378        public abstract String getRoleId();
379        
380        // =======================================================================================================
381        
382        /**
383         * This class represents a &lt;roleMember&gt; element that is not a descendant of a &lt;role&gt; element.
384         * 
385         * @author Kuali Rice Team (rice.collab@kuali.org)
386         */
387        @XmlAccessorType(XmlAccessType.FIELD)
388        @XmlType(name="StandaloneRoleMemberType", propOrder={
389                "roleId", "roleNameAndNamespace", "principalId", "principalName", "groupId", "groupName", "roleIdAsMember",
390                        "roleNameAsMember", "activeFromDate", "activeToDate", "qualifications"
391        })
392        public static class OutsideOfRole extends RoleMemberXmlDTO {
393    
394            private static final long serialVersionUID = 1L;
395    
396            @XmlElement(name="roleId")
397            @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
398            private String roleId;
399    
400            @XmlElement(name="roleName")
401            @XmlJavaTypeAdapter(NameAndNamespacePairValidatingAdapter.class)
402            private NameAndNamespacePair roleNameAndNamespace;
403            
404            public OutsideOfRole() {
405                super();
406            }
407            
408            public OutsideOfRole(RoleMember roleMember, boolean populateMemberId) {
409                super(roleMember, populateMemberId);
410                this.roleId = roleMember.getRoleId();
411                RoleContract tempRole = KimApiServiceLocator.getRoleService().getRole(roleId);
412                if (tempRole == null) {
413                    throw new IllegalArgumentException("Cannot find role with ID \"" + roleId + "\"");
414                }
415                this.roleNameAndNamespace = new NameAndNamespacePair(tempRole.getNamespaceCode(), tempRole.getName());
416            }
417            
418            /**
419             * @see org.kuali.rice.kim.impl.jaxb.RoleMemberXmlDTO#getRoleId()
420             */
421            @Override
422            public String getRoleId() {
423                return roleId;
424            }
425    
426            /**
427             * @param roleId the roleId to set
428             */
429            public void setRoleId(String roleId) {
430                this.roleId = roleId;
431            }
432            
433            /**
434             * @return the roleNameAndNamespace
435             */
436            public NameAndNamespacePair getRoleNameAndNamespace() {
437                return this.roleNameAndNamespace;
438            }
439    
440            /**
441             * @param roleNameAndNamespace the roleNameAndNamespace to set
442             */
443            public void setRoleNameAndNamespace(NameAndNamespacePair roleNameAndNamespace) {
444                this.roleNameAndNamespace = roleNameAndNamespace;
445            }
446    
447            /**
448             * Retrieves the role name from the role-name-and-namespace combo.
449             * 
450             * @return The name of the role that this member belongs to, or null if the role-name-and-namespace combo is null.
451             */
452            public String getRoleName() {
453                return (roleNameAndNamespace != null) ? roleNameAndNamespace.getName() : null;
454            }
455    
456            /**
457             * Retrieves the role namespace code from the role-name-and-namespace combo.
458             * 
459             * @return The namespace code of the role that this member belongs to, or null if the role-name-and-namespace combo is null.
460             */
461            public String getRoleNamespaceCode() {
462                return (roleNameAndNamespace != null) ? roleNameAndNamespace.getNamespaceCode() : null;
463            }
464        }
465        
466        // =======================================================================================================
467        
468        /**
469         * This class represents a &lt;roleMember&gt; element that is a descendant of a &lt;role&gt; element.
470         * 
471         * @author Kuali Rice Team (rice.collab@kuali.org)
472         */
473        @XmlAccessorType(XmlAccessType.FIELD)
474        @XmlType(name="RoleMemberType", propOrder={
475                "principalId", "principalName", "groupId", "groupName", "roleIdAsMember",
476                        "roleNameAsMember", "activeFromDate", "activeToDate", "qualifications"
477        })
478        public static class WithinRole extends RoleMemberXmlDTO {
479    
480            private static final long serialVersionUID = 1L;
481            
482            @XmlTransient
483            private String roleId;
484    
485            public WithinRole() {
486                super();
487            }
488            
489            public WithinRole(RoleMember roleMember, boolean populateMemberId) {
490                super(roleMember, populateMemberId);
491                this.roleId = roleMember.getRoleId();
492            }
493            
494            void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {
495                if (parent instanceof RoleMembersXmlDTO.WithinRole) {
496                    this.roleId = ((RoleMembersXmlDTO.WithinRole)parent).getRoleId();
497                }
498            }
499            
500            /**
501             * @see org.kuali.rice.kim.impl.jaxb.RoleMemberXmlDTO#getRoleId()
502             */
503            @Override
504            public String getRoleId() {
505                return roleId;
506            }
507            
508        }
509    }