KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > services > security > management > DefaultGroupImpl


1 /*
2  * The contents of this file are subject to the Sapient Public License
3  * Version 1.0 (the "License"); you may not use this file except in compliance
4  * with the License. You may obtain a copy of the License at
5  * http://carbon.sf.net/License.html.
6  *
7  * Software distributed under the License is distributed on an "AS IS" basis,
8  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9  * the specific language governing rights and limitations under the License.
10  *
11  * The Original Code is The Carbon Component Framework.
12  *
13  * The Initial Developer of the Original Code is Sapient Corporation
14  *
15  * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
16  */

17
18 package org.sape.carbon.services.security.management;
19
20 import java.io.Serializable JavaDoc;
21 import java.security.AccessControlException JavaDoc;
22 import java.security.Principal JavaDoc;
23 import java.security.acl.Group JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.Enumeration JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Set JavaDoc;
28
29 import org.sape.carbon.core.component.Lookup;
30 import org.sape.carbon.core.exception.ExceptionUtility;
31
32 /**
33  * Default implementation of the group object.
34  *
35  * <p>
36  * This basic implementation contains an internal set of members which are
37  * passed in upon creation in a flat set of principals. It does not story
38  * any heirarchy information and will not traverse the list of members
39  * below the first layer. It will not remain synchronized with a
40  * bank-end store if other objects are making changes to it.
41  * </p>
42  *
43  * <p>
44  * Calls made to update the internal members list are passed on to the
45  * UserManager service it takes during contruction. The user manager
46  * service is lost upon serialization (such as when passing this object
47  * from the EJB layer to the web container).
48  * </p>
49  *
50  * @author $Author: dvoet $ $Date: 2003/10/28 19:02:00 $
51  * @version $Revision: 1.7 $
52  *
53  * @since carbon 1.2
54  */

55 public class DefaultGroupImpl implements LazyLoadGroup, Serializable JavaDoc {
56     /** Holds the location of the UserManager service. */
57     protected transient String JavaDoc userManagerServiceLocation;
58
59     /** Synchronization lock for adjusting the members object. */
60     protected Object JavaDoc membersLock = new Object JavaDoc();
61
62     /** Holds a flat set of member principals of this group. */
63     protected Set JavaDoc members;
64
65     /** Holds the name of this group. */
66     protected String JavaDoc name;
67
68     /**
69      * Holds checked group for the recursive for the isMember call. This
70      * is used to avoid an infinite recursion in the case of a cycle in
71      * the graph of groups.
72      *
73      * @see #isMember
74      */

75     private ThreadLocal JavaDoc checkedGroupsLocal = new ThreadLocal JavaDoc();
76
77     /**
78      * Constructs a new instance of a group.
79      *
80      * @param name the name of the group
81      * @param members a set contain the members of the group.
82      * <code>null</code> implies there are no member of this
83      * group.
84      * @param userManagerServiceLocation the location of the UserManager
85      * service. <code>null</code> implies this group has no
86      * backend store and cannot be updated.
87      */

88     public DefaultGroupImpl(
89         String JavaDoc name, Set JavaDoc members, String JavaDoc userManagerServiceLocation) {
90         this.name = name;
91         this.members = members;
92
93         this.userManagerServiceLocation = userManagerServiceLocation;
94     }
95
96     /**
97      * Sets the members object.
98      *
99      * @param members a set of members for this group.
100      *
101      * @throws IllegalStateException indicates that members have already
102      * been set on this object and that state is initialized
103      */

104     public synchronized void setMembers(Set JavaDoc members) {
105         if (this.members != null) {
106             throw new IllegalStateException JavaDoc(
107                 "Object was initialized without "
108                 + "any members and no members have been added.");
109         }
110
111         this.members = members;
112     }
113
114     /**
115      * Returns if this object is fully loaded. It is possible to
116      * construct this object without passing in a set of members and then
117      * set the members once later. This is meant to allow for the case
118      * where the member list contains a cycle, and therefore it is
119      * impossible to load all child members before creating this one.
120      *
121      * @return if this object is fully loaded
122      */

123     public boolean isLoaded() {
124         return !(this.members == null);
125     }
126
127     /**
128      * Adds a member to this group. This method is only supported if this
129      * is not a value object style and has been attached to a
130      * UserManager.
131      *
132      * @param principal the principal to add to this group
133      *
134      * @return <code>true</code> if the user store changed as a result of
135      * the call
136      *
137      * @throws IllegalStateException indicates that members have never
138      * been set on this group and it is not ready for
139      * manipulation
140      * @throws UnsupportedOperationException indicates there is no back
141      * UserManager to complete this operation and therefore it is
142      * not support on this instance of the object
143      * @throws AccessControlException indicates a problem communicating
144      * with the user-store
145      */

146     public synchronized boolean addMember(Principal JavaDoc principal) {
147         boolean result = false;
148
149         if (this.members == null) {
150             throw new IllegalStateException JavaDoc(
151                 "Object was initialized without "
152                 + "any members and no members have been added.");
153         }
154
155         if (this.userManagerServiceLocation == null) {
156             throw new UnsupportedOperationException JavaDoc(
157                 "Cannot add users to group. "
158                 + "Try using the UserManager.addUserToGroup() method instead.");
159         } else {
160             UserManager userManager =
161                 (UserManager) Lookup.getInstance().fetchComponent(
162                     this.userManagerServiceLocation);
163
164             try {
165                 result = userManager.addPrincipalToGroup(principal, this);
166                 members.add(principal);
167             } catch (SecurityManagementException sme) {
168                 throw new AccessControlException JavaDoc(
169                     "Caught SecurityManagementException: "
170                     + ExceptionUtility.printStackTracesToString(sme));
171             }
172         }
173
174         return result;
175     }
176
177     /**
178      * Removes a member of this group. This method is only supported if
179      * this is not a value object style and has been attached to a
180      * UserManager.
181      *
182      * @param principal the principal to remove to this group
183      *
184      * @return <code>true</code> if the user store changed as a result of
185      * the call
186      *
187      * @throws IllegalStateException indicates that members have never
188      * been set on this group and it is not ready for
189      * manipulation
190      * @throws UnsupportedOperationException indicates there is no back
191      * UserManager to complete this operation and therefore it is
192      * not support on this instance of the object
193      * @throws AccessControlException indicates a problem communicating
194      * with the user-store
195      */

196     public synchronized boolean removeMember(Principal JavaDoc principal) {
197         boolean result = false;
198
199         if (this.members == null) {
200             throw new IllegalStateException JavaDoc(
201                 "Object was initialized without "
202                 + "any members and no members have been added.");
203         }
204
205         if (this.userManagerServiceLocation == null) {
206             throw new UnsupportedOperationException JavaDoc(
207                 "Cannot remove users from group. "
208                 + "Try using the UserManager.addUserToGroup() method instead.");
209         } else {
210             UserManager userManager =
211                 (UserManager) Lookup.getInstance().fetchComponent(
212                     this.userManagerServiceLocation);
213
214             try {
215                 result =
216                     userManager.removePrincipalFromGroup(principal, this);
217                 members.remove(principal);
218             } catch (SecurityManagementException sme) {
219                 throw new AccessControlException JavaDoc(
220                     "Caught SecurityManagementException: "
221                     + ExceptionUtility.printStackTracesToString(sme));
222             }
223         }
224
225         return result;
226     }
227
228     /**
229      * Internal method to check if direct members (non-recursive) of
230      * this group contain a Principal.
231      * <p>
232      * If the members group does not contain an equal principal, it
233      * will iterate through the group checking the name field.
234      * </p>
235      *
236      * @param principal the principal to check for membership of
237      * @return if the group directly contains this principal
238      */

239     protected boolean isDirectMember(Principal JavaDoc principal) {
240         boolean isDirectMember = this.members.contains(principal);
241
242         if (!isDirectMember) {
243             Enumeration JavaDoc membersEnumeration = this.members();
244             while (
245                 membersEnumeration.hasMoreElements() && (!isDirectMember)) {
246                 Principal JavaDoc currentMember =
247                     (Principal JavaDoc) membersEnumeration.nextElement();
248
249                 if (principal != null && currentMember != null
250                     && principal.getName().equals(currentMember.getName())) {
251                     isDirectMember = true;
252                 }
253             }
254         }
255
256         return isDirectMember;
257     }
258
259     /**
260      * Checks if the principal is in the group.
261      *
262      * <p>
263      * Synchornized against the internal members list. So while this is
264      * executing (and possibly traversing a node list), no changes can be
265      * made to the structure of the members list through this object.
266      * </p>
267      *
268      * <p>
269      * To handle the potential of cyclic groups, this method stores the
270      * groups already checked within a ThreadLocal variable. Storing this
271      * information in the ThreadLocal allows the algorithm to use the
272      * standard <code>isMember(Principal principal)</code> method from
273      * the <code>Group</code> interface without requiring any specialized
274      * interface. This is important in case this group contains groups
275      * which may not be a sub-class of this implementation.
276      * </p>
277      *
278      * <p>
279      * It is also not done as a member variable since as the algorithm
280      * recurses Group A to Group B to Group C the list of already queried
281      * groups must be passed down and remain static across the recursive
282      * cycle.
283      * </p>
284      *
285      * @param principal the principal to check membership of.
286      *
287      * @return if the principal is a member of this group
288      *
289      * @throws IllegalStateException indicates that members have never
290      * been set on this group and it is not ready for
291      * manipulation
292      */

293     public synchronized boolean isMember(Principal JavaDoc principal) {
294         // for an in-depth explanation of the algorithm used
295
// including the logic of ThreadLocal, read the
296
// method's javadoc
297
boolean isMember = false;
298
299         if (this.members == null) {
300             throw new IllegalStateException JavaDoc(
301                 "Object was initialized without "
302                 + "any members and no members have been added.");
303         }
304
305         if (isDirectMember(principal)) {
306             isMember = true;
307         } else {
308             boolean isFirstCycle = false;
309             Set JavaDoc checkedGroups = (Set JavaDoc) checkedGroupsLocal.get();
310
311             if (checkedGroups == null) {
312                 // Mark that this is the first cycle so that it can
313
// clear the threadlocal. Generate a a new set
314
// of all the groups already checked.
315
isFirstCycle = true;
316                 checkedGroups = new HashSet JavaDoc();
317                 checkedGroupsLocal.set(checkedGroups);
318             }
319
320             checkedGroups.add(this);
321
322             Enumeration JavaDoc membersEnumeration = this.members();
323
324             while (
325                 membersEnumeration.hasMoreElements() && (!isMember)) {
326                 Object JavaDoc currentMember = membersEnumeration.nextElement();
327
328                 if (
329                     currentMember instanceof Group JavaDoc
330                         && !checkedGroups.contains(currentMember)) {
331                     // Add the sub group into the list to make sure that
332
// any cycles will not check this group a second
333
// time.
334
checkedGroups.add(currentMember);
335
336                     if (((Group JavaDoc) currentMember).isMember(principal)) {
337                         isMember = true;
338                     }
339                 }
340             }
341
342             // On first cycle, set the ThreadLocal variable back to
343
// null.
344
if (isFirstCycle) {
345                 checkedGroupsLocal.set(null);
346             }
347         }
348
349         return isMember;
350     }
351
352     /**
353      * Returns an enumeration of all members of this group.
354      *
355      * <p>
356      * The enumeration returned is static as of the time the method is
357      * called. If changes are made to the members of the group while the
358      * enumeration it being iterated over, changes will not appear. It
359      * will require a new call to this method after the enumeration is
360      * complete.
361      * </p>
362      *
363      * @return all members of this group
364      *
365      * @throws IllegalStateException indicates that members have never
366      * been set on this group and it is not ready for
367      * manipulation
368      */

369     public synchronized Enumeration JavaDoc members() {
370         if (this.members == null) {
371             throw new IllegalStateException JavaDoc(
372                 "Object was initialized without "
373                 + "any members and no members have been added.");
374         }
375
376         // Create a copy to keep the enumeration from
377
// break if the members list is concurrently
378
// updated
379
HashSet JavaDoc membersCopy = new HashSet JavaDoc(members);
380
381         return Collections.enumeration(membersCopy);
382     }
383
384     /**
385      * Returns the name of the group.
386      *
387      * @return the name of the group
388      */

389     public String JavaDoc getName() {
390         return this.name;
391     }
392
393     /**
394      * Compares this group against another using only the group name.
395      *
396      * @param obj the object to compare against this group
397      *
398      * @return if the names of the two groups are equals
399      */

400     public boolean equals(Object JavaDoc obj) {
401         boolean result = false;
402
403         if (obj instanceof Group JavaDoc) {
404             if (this.getName().equals(((Group JavaDoc) obj).getName())) {
405                 result = true;
406             }
407         }
408
409         return result;
410     }
411
412     /**
413      * Returns the hashcode of the name of the group.
414      *
415      * @return a code based on the group name.
416      */

417     public int hashCode() {
418         return getName().hashCode();
419     }
420
421     /**
422      * A String representation of the group.
423      *
424      * <p>
425      * This does not return the members of the group to avoid a infinite
426      * recursion if if there is a cycle in the groups
427      * </p>
428      *
429      * @return string representation of the group
430      */

431     public String JavaDoc toString() {
432         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(32);
433         sb.append(this.getClass().getName());
434         sb.append(" [");
435         sb.append("name=");
436         sb.append(getName());
437         sb.append(", members.size()=");
438         sb.append(members.size());
439         sb.append("]");
440
441         return sb.toString();
442     }
443 }
444
Popular Tags