View Javadoc

1   package com.atlassian.user.impl.hibernate;
2   
3   import com.atlassian.user.*;
4   import com.atlassian.user.impl.DuplicateEntityException;
5   import com.atlassian.user.impl.RepositoryException;
6   import com.atlassian.user.impl.hibernate.repository.HibernateRepository;
7   import com.atlassian.user.repository.RepositoryIdentifier;
8   import com.atlassian.user.search.page.DefaultPager;
9   import com.atlassian.user.search.page.Pager;
10  import com.atlassian.user.search.page.PagerFactory;
11  import com.atlassian.user.util.Assert;
12  import net.sf.hibernate.HibernateException;
13  import net.sf.hibernate.ObjectDeletedException;
14  import net.sf.hibernate.Query;
15  import net.sf.hibernate.Session;
16  import org.springframework.dao.DataAccessException;
17  import org.springframework.orm.hibernate.HibernateCallback;
18  import org.springframework.orm.hibernate.HibernateTemplate;
19  import org.springframework.orm.hibernate.SessionFactoryUtils;
20  import org.springframework.orm.hibernate.support.HibernateDaoSupport;
21  
22  import java.util.*;
23  
24  /**
25   * A HibernateGroupManager which handles membership for local and external entities.
26   */
27  public class HibernateGroupManager extends HibernateDaoSupport implements GroupManager
28  {
29      public static final String GROUPNAME_FIELD = "groupname";
30      public static final String GROUPID_FIELD = "groupid";
31      public static final String ENTITYID_FIELD = "entityid";
32      public static final String EXTERNAL_ENTITY_NAME_FIELD = "externalEntityName";
33  
34      private final RepositoryIdentifier identifier;
35      protected final HibernateRepository repository;
36      protected final UserManager userManager;
37      protected final ExternalEntityDAO externalEntityDao;
38  
39      private static final boolean USE_EXPERIMENTAL_MAPPINGS = Boolean.getBoolean("com.atlassian.user.experimentalMapping");
40  
41      public HibernateGroupManager(RepositoryIdentifier identifier, HibernateRepository repository, UserManager userManager, ExternalEntityDAO externalEntityDao)
42      {
43          this.identifier = identifier;
44          this.repository = repository;
45          this.userManager = userManager;
46          setSessionFactory(repository.getSessionFactory());
47          this.externalEntityDao = externalEntityDao;
48      }
49  
50      public Pager<Group> getGroups() throws EntityException
51      {
52          List<Group> result;
53  
54          try
55          {
56              result = getGroupsFromHibernate();
57          }
58          catch (DataAccessException e)
59          {
60              throw new RepositoryException(e);
61          }
62  
63          if (result == null)
64              return DefaultPager.emptyPager();
65  
66          return new DefaultPager<Group>(result);
67      }
68  
69      public List<Group> getWritableGroups()
70      {
71          return getGroupsFromHibernate();
72      }
73  
74      public Pager<Group> getGroups(User user) throws EntityException
75      {
76          Collection<Group> groups = getAllGroupsForUser(user);
77          return new DefaultPager<Group>(groups);
78      }
79  
80      protected Collection<Group> getAllGroupsForUser(User user) throws RepositoryException
81      {
82          Assert.notNull(user, "User must not be null");
83  
84          if (isUserExternal(user))
85              return getGroupsForExternalEntity(getCorrespondingExternalEntity(user));
86  
87          return getGroupsForLocalUser(user);
88      }
89  
90      /**
91       * for the time being, lets define an internal user as a hibernate user. All other impl's of User are external users.
92       */
93      protected boolean isUserExternal(User user)
94      {
95          return !(user instanceof DefaultHibernateUser);
96      }
97  
98      private List<Group> getGroupsForLocalUser(User user) throws RepositoryException
99      {
100         Assert.notNull(user, "User must not be null");
101         Assert.isInstanceOf(DefaultHibernateUser.class, user);
102 
103         if (isUserExternal(user))
104             return Collections.emptyList();
105 
106         return getLocalUserGroupsFromHibernate((DefaultHibernateUser) user);
107     }
108 
109     private List<Group> getGroupsForExternalEntity(final ExternalEntity externalEntity) throws RepositoryException
110     {
111         if (externalEntity == null)
112             throw new IllegalArgumentException("Input (externalEntity) is null.");
113 
114         return getExternalUserGroupsFromHibernate(externalEntity);
115     }
116 
117     /**
118      * @return a {@link Pager} instance which can hold {@link User} <b>and</b> {@link ExternalEntity} objects.
119      */
120     public Pager<String> getMemberNames(Group group) throws EntityException
121     {
122         if (group == null)
123             throw new IllegalArgumentException("Group cannot be null.");
124         if (!isHandledGroup(group))
125             throw new IllegalArgumentException("Group passed to HibernateGroupManager must be of type 'DefaultHibernateGroup'");
126 
127         // We don't try to merge the result returned by getExternalMemberNames(group) and getLocalMemberNames(group)
128         // because it is not possible with hql(not implemented) or native sql through hibernate (bug in hibernate)
129         // When iterating through returned result client will get ordered internal members and then ordered external members.
130         // To order everything together we can use SQL union but HQL does not support it yet
131         // http://opensource.atlassian.com/projects/hibernate/browse/HHH-1050
132         return PagerFactory.getPager(getExternalMemberNames(group), getLocalMemberNames(group));
133     }
134 
135     protected void validateGroup(Group group)
136     {
137         if (group == null)
138             throw new IllegalArgumentException("Input (group) is null.");
139     }
140 
141     public Pager<String> getLocalMemberNames(Group group) throws EntityException
142     {
143         validateGroup(group);
144         return new DefaultPager<String>(getLocalMemberNamesFromHibernate((DefaultHibernateGroup) group));
145     }
146 
147     public Pager<User> getLocalMembers(Group group) throws RepositoryException
148     {
149         if (group == null)
150             throw new IllegalArgumentException("Input (group) is null.");
151         else if (!isHandledGroup(group)) //nothing here for a non-Hibernate group
152             return DefaultPager.emptyPager();
153 
154         DefaultHibernateGroup defGroup = (DefaultHibernateGroup) group;
155         return new DefaultPager<User>(new ArrayList<User>(defGroup.getLocalMembers()));
156     }
157 
158     private boolean isHandledGroup(Group group)
159     {
160         return (group instanceof DefaultHibernateGroup);
161     }
162 
163     public Pager<String> getExternalMemberNames(Group group) throws EntityException
164     {
165         if (group == null)
166             throw new IllegalArgumentException("Input (group) is null.");
167         else if (!isHandledGroup(group))
168             return DefaultPager.emptyPager();
169 
170         final DefaultHibernateGroup defGroup = (DefaultHibernateGroup) group;
171 
172         return new DefaultPager<String>(getExternalMemberNamesFromHibernate(defGroup));
173     }
174 
175     public DefaultHibernateGroup getGroup(Group group) throws EntityException
176     {
177         //for permormance try to fetch a group by id if it is hibernate group
178         if (group instanceof DefaultHibernateGroup)
179         {
180            // let's make sure we load this group in Session
181             try
182             {
183                 return (DefaultHibernateGroup) getSession().get(DefaultHibernateGroup.class, ((DefaultHibernateGroup) group).getId());
184             }
185             catch (ObjectDeletedException e)
186             {
187                 return null;
188             }
189             catch (HibernateException e)
190             {
191                 throw new EntityException(e);
192             }
193         }
194         else
195         {
196             return getGroup(group.getName());
197         }
198 
199     }
200 
201 
202     public DefaultHibernateGroup getGroup(final String groupname) throws EntityException
203     {
204         if (groupname == null)
205             throw new IllegalArgumentException("Input (groupname) is null.");
206 
207         List result;
208         DefaultHibernateGroup foundGroup = null;
209 
210         try
211         {
212             result = getHibernateTemplate().executeFind(new HibernateCallback()
213             {
214                 public Object doInHibernate(Session session) throws HibernateException
215                 {
216                     Query queryObject = session.getNamedQuery("atluser.group_find");
217                     SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
218 
219                     if (groupname != null)
220                         queryObject.setParameter(GROUPNAME_FIELD, groupname);
221 
222                     return queryObject.list();
223                 }
224             });
225         }
226         catch (DataAccessException e)
227         {
228             throw new RepositoryException(e);
229         }
230 
231         try
232         {
233             foundGroup = (DefaultHibernateGroup) result.get(0);
234         }
235         catch (Exception e)
236         {
237             return foundGroup;
238         }
239 
240         return foundGroup;
241     }
242 
243     public Group createGroup(String groupname) throws EntityException
244     {
245         if (groupname == null)
246             throw new IllegalArgumentException("Input (groupname) is null.");
247 
248         Group group = getGroup(groupname);
249 
250         if (group == null)
251         {
252             group = new DefaultHibernateGroup(groupname);
253             getHibernateTemplate().save(group);
254         }
255         else
256             throw new DuplicateEntityException("Group [" + groupname + "] already exists in this repository (" +
257                     identifier.getName() + ")");
258 
259         return group;
260     }
261 
262     /**
263      * Removes the specified group, if it is present.
264      *
265      * @throws com.atlassian.user.EntityException
266      *          - representing the exception which prohibited removal
267      */
268     public void removeGroup(Group group) throws EntityException
269     {
270         Group groupInSession = getGroupInSession(group);
271 
272         DefaultHibernateGroup dGroup = (DefaultHibernateGroup) groupInSession;
273         dGroup.setExternalMembers(null);
274         dGroup.setLocalMembers(null);
275 
276         getHibernateTemplate().delete(groupInSession);
277     }
278 
279     public void addMembership(Group group, User user) throws EntityException
280     {
281         validateGroupAndUser(group, user);
282 
283         DefaultHibernateGroup dGroup = getGroupInSession(group);
284 
285         if (isUserExternal(user))
286         {
287             addExternalUserMembership(user, dGroup);
288         }
289         else
290         {
291             addLocalUserMembership(user, dGroup);
292         }
293 
294         getHibernateTemplate().saveOrUpdate(dGroup);
295     }
296 
297     private void addLocalUserMembership(User user, DefaultHibernateGroup dGroup)
298     {
299         if (USE_EXPERIMENTAL_MAPPINGS)
300         {
301             DefaultHibernateUser huser = (DefaultHibernateUser) user;
302             huser.getGroups().add(dGroup);
303             getHibernateTemplate().saveOrUpdate(user);
304         }
305         else
306         {
307             if (dGroup.getLocalMembers() == null)
308                 dGroup.setLocalMembers(new HashSet<User>());
309 
310             dGroup.getLocalMembers().add(user);
311         }
312     }
313 
314     private void addExternalUserMembership(User user, DefaultHibernateGroup dGroup)
315             throws RepositoryException
316     {
317         if (dGroup.getExternalMembers() == null)
318             dGroup.setExternalMembers(new HashSet<ExternalEntity>());
319 
320         dGroup.getExternalMembers().add(getCorrespondingExternalEntity(user));
321     }
322 
323     protected ExternalEntity getCorrespondingExternalEntity(final User user) throws RepositoryException
324     {
325         if (user == null)
326             throw new IllegalArgumentException("Input (user) is null.");
327 
328         ExternalEntity result = externalEntityDao.getExternalEntity(user.getName());
329 
330         if (result == null)
331             return externalEntityDao.createExternalEntity(user.getName());
332         else
333             return result;
334     }
335 
336     public boolean hasMembership(Group group, User user) throws EntityException
337     {
338         if (group==null || getGroup(group) == null)
339             return false;
340 
341         validateGroupAndUser(group, user);
342 
343         final DefaultHibernateGroup defGroup = getGroupInSession(group);
344 
345         if (isUserExternal(user))
346         {
347             return hasExternalMembership(defGroup, user);
348         }
349         else
350         {
351             return hasLocalMembership(defGroup, (DefaultHibernateUser) user);
352         }
353     }
354 
355     protected void validateGroupAndUser(Group group, User user) throws EntityException
356     {
357         if (group == null)
358             throw new IllegalArgumentException("Can't add membership for null group");
359 
360         if (getGroup(group) == null)
361             throw new IllegalArgumentException("Group unknown: [" + group + "] in [" + identifier.getKey() + "]");
362 
363         if (user == null)
364             throw new IllegalArgumentException("User unknown: [" + user + "] in [" + identifier.getKey() + "]");
365 
366         if (!isHandledGroup(group))
367             throw new IllegalArgumentException("Group is not a Hibernate entity [" + group.getClass().getName());
368     }
369 
370     protected boolean hasExternalMembership(DefaultHibernateGroup defGroup, User user) throws EntityException
371     {
372         ExternalEntity entity = getCorrespondingExternalEntity(user);
373 
374         return defGroup.getExternalMembers().contains(entity);
375     }
376 
377     protected boolean hasLocalMembership(DefaultHibernateGroup defGroup, DefaultHibernateUser defUser) throws EntityException
378     {
379         Collection usersGroups = getAllGroupsForUser(defUser);
380 
381         return (usersGroups != null && usersGroups.contains(defGroup));
382     }
383 
384     public void removeMembership(Group group, User user) throws EntityException
385     {
386         validateGroupAndUser(group, user);
387 
388         Group groupInSession = getGroupInSession(group);
389 
390         DefaultHibernateGroup hibernateGroup = (DefaultHibernateGroup) groupInSession;
391 
392         HibernateTemplate hibernateTemplate = getHibernateTemplate();
393         if (isUserExternal(user))
394         {
395             ExternalEntity extUser = getCorrespondingExternalEntity(user);
396 
397             // remove user from group
398             hibernateGroup.getExternalMembers().remove(extUser);
399 
400         }
401         else
402         {
403             if(USE_EXPERIMENTAL_MAPPINGS) {
404                 DefaultHibernateUser huser = (DefaultHibernateUser)user;
405                 huser.getGroups().remove(groupInSession);
406                 hibernateTemplate.saveOrUpdate(huser);
407             }
408 
409             else {
410             // remove user from group
411             hibernateGroup.getLocalMembers().remove(user);
412             hibernateTemplate.saveOrUpdate(groupInSession);
413             }
414         }
415 
416         hibernateTemplate.flush();
417     }
418 
419     public boolean isReadOnly(Group group) throws EntityException
420     {
421         return (getGroup(group) == null);
422     }
423 
424     public boolean supportsExternalMembership() throws EntityException
425     {
426         return true;
427     }
428 
429     /**
430      * @return the {@link com.atlassian.user.repository.RepositoryIdentifier} which is managed by this instance.
431      */
432     public RepositoryIdentifier getIdentifier()
433     {
434         return identifier;
435     }
436 
437     public RepositoryIdentifier getRepository(Entity entity) throws EntityException
438     {
439         if (getGroup(entity.getName()) != null)
440             return identifier;
441 
442         return null;
443     }
444 
445     /**
446      * Used to detemine whether an entity can be added (eg, can call {@link com.atlassian.user.UserManager#createUser(String)} or
447      * {@link com.atlassian.user.GroupManager#createGroup(String)}
448      */
449     public boolean isCreative()
450     {
451         return true;
452     }
453 
454     /**
455      * Used to get a group from Hibernate (in session) from the "cached" version.
456      * Ensures that we do not have un-attached group objects.
457      */
458     private DefaultHibernateGroup getGroupInSession(Group group)
459             throws EntityException
460     {
461         if (group == null)
462             throw new IllegalArgumentException("Input (group) is null.");
463         else if (!isHandledGroup(group))
464             throw new IllegalArgumentException("Group is not a Hibernate entity [" + group.getClass().getName());
465 
466         return getGroup(group);
467     }
468 
469     @SuppressWarnings("unchecked")
470     private List<Group> getGroupsFromHibernate()
471     {
472         return getHibernateTemplate().executeFind(new HibernateCallback()
473         {
474             public Object doInHibernate(Session session) throws HibernateException
475             {
476                 Query queryObject = session.getNamedQuery("atluser.group_findAll");
477                 SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
478                 return queryObject.list();
479             }
480         });
481     }
482 
483 
484     @SuppressWarnings("unchecked")
485     private List<Group> getLocalUserGroupsFromHibernate(final DefaultHibernateUser defUser) throws RepositoryException
486     {
487         try
488         {
489             return getHibernateTemplate().executeFind(new HibernateCallback()
490             {
491                 public Object doInHibernate(Session session) throws HibernateException
492                 {
493                     Query queryObject = session.getNamedQuery("atluser.group_getGroupsForUser");
494                     SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
495                     queryObject.setLong(ENTITYID_FIELD, defUser.getId());
496 
497                     return queryObject.list();
498                 }
499             });
500         }
501         catch (DataAccessException e)
502         {
503             throw new RepositoryException(e);
504         }
505     }
506 
507     @SuppressWarnings("unchecked")
508     private List<Group> getExternalUserGroupsFromHibernate(final ExternalEntity externalEntity) throws RepositoryException
509     {
510         try
511         {
512             return getHibernateTemplate().executeFind(new HibernateCallback()
513             {
514                 public Object doInHibernate(Session session) throws HibernateException
515                 {
516                     Query queryObject = session.getNamedQuery("atluser.group_getGroupsForExternalEntity");
517                     SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
518 
519                     if (externalEntity != null)
520                         queryObject.setLong(ENTITYID_FIELD, externalEntity.getId());
521 
522                     return queryObject.list();
523                 }
524             });
525         }
526         catch (DataAccessException e)
527         {
528             throw new RepositoryException(e);
529         }
530     }
531 
532     @SuppressWarnings("unchecked")
533     private List<String> getLocalMemberNamesFromHibernate(final DefaultHibernateGroup defGroup) throws RepositoryException
534     {
535         try
536         {
537             return getHibernateTemplate().executeFind(new HibernateCallback()
538             {
539                 public Object doInHibernate(Session session) throws HibernateException
540                 {
541                     Query queryObject = session.getNamedQuery("atluser.group_getLocalMemberNames");
542                     SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
543 
544                     if (defGroup != null)
545                         queryObject.setLong(GROUPID_FIELD, defGroup.getId());
546 
547                     return queryObject.list();
548                 }
549             });
550         }
551         catch (DataAccessException e)
552         {
553             throw new RepositoryException(e);
554         }
555     }
556 
557     @SuppressWarnings("unchecked")
558     private List<String> getExternalMemberNamesFromHibernate(final DefaultHibernateGroup defGroup)
559             throws RepositoryException
560     {
561         List result;
562         try
563         {
564             result = getHibernateTemplate().executeFind(new HibernateCallback()
565             {
566                 public Object doInHibernate(Session session) throws HibernateException
567                 {
568                     Query queryObject = session.getNamedQuery("atluser.group_getExternalMemberNames");
569                     SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
570 
571                     if (defGroup != null)
572                         queryObject.setLong(GROUPID_FIELD, defGroup.getId());
573 
574                     return queryObject.list();
575                 }
576             });
577         }
578         catch (DataAccessException e)
579         {
580             throw new RepositoryException(e);
581         }
582         return result;
583     }
584 }