1   package com.atlassian.user.impl.ldap.search.page;
2   
3   import com.atlassian.user.EntityException;
4   import com.atlassian.user.impl.ldap.repository.LdapContextFactory;
5   import com.atlassian.user.impl.ldap.search.LDAPPagerInfo;
6   import com.atlassian.user.impl.ldap.properties.LdapSearchProperties;
7   import com.atlassian.user.search.page.AbstractPrefetchingPager;
8   import com.atlassian.user.util.EnumerationAdaptor;
9   import com.atlassian.user.util.LDAPUtils;
10  import com.atlassian.util.profiling.UtilTimerStack;
11  import net.sf.ldaptemplate.support.filter.Filter;
12  import org.apache.log4j.Logger;
13  
14  import javax.naming.NamingEnumeration;
15  import javax.naming.NamingException;
16  import javax.naming.directory.DirContext;
17  import javax.naming.directory.SearchControls;
18  import javax.naming.directory.SearchResult;
19  import java.util.Iterator;
20  import java.util.List;
21  
22  public abstract class AbstractLDAPPager<T> extends AbstractPrefetchingPager<T> implements Iterator<T>
23  {
24      protected final Logger log = Logger.getLogger(this.getClass());
25      protected NamingEnumeration<SearchResult> enume;
26      protected Filter originalQuery;
27      protected LdapSearchProperties searchProperties;
28      protected LdapContextFactory repository;
29      protected String originalBaseSearchContext;
30      protected boolean searchAllDepths;
31      protected String[] returningAttributes;
32      private int timeLimitMillis;
33      public boolean closed;
34  
35      protected AbstractLDAPPager(LdapSearchProperties searchProperties, LdapContextFactory repository, LDAPPagerInfo info)
36      {
37          this.searchProperties = searchProperties;
38          this.repository = repository;
39          this.enume = info.getNamingEnumeration();
40          this.originalQuery = info.getLDAPQuery();
41          this.originalBaseSearchContext = info.getBaseSearchContext();
42          this.searchAllDepths = info.isSearchAllDepths();
43          this.returningAttributes = info.getReturningAttributes();
44          this.timeLimitMillis = info.getTimeToLive();
45      }
46  
47      public AbstractLDAPPager()
48      {
49  
50      }
51  
52      /**
53       * Parses the {@link SearchResult} and stores the result in the list of prefetched items.
54       *
55       * @return the {@link List} of prefetched results.
56       */
57      protected abstract List<T> preloadSearchResult(SearchResult result, List<T> prefetched) throws EntityException;
58  
59      public void remove()
60      {
61          throw new UnsupportedOperationException("This iterator does not support removal.");
62      }
63  
64      /**
65       * Preloads the page into the prefetched list.
66       */
67      protected void preload()
68      {
69          indexOfFirstItemInCurrentPage = idx;
70  
71          if (UtilTimerStack.isActive())
72          {
73              UtilTimerStack.push(this.getClass().getName() + "_preload__(originalQuery= " + originalQuery + ")");
74          }
75  
76          DirContext ctx = null;
77  
78          try
79          {
80              if (closed)
81              {
82                  ctx = repository.getLDAPContext();
83                  SearchControls ctls = LDAPUtils.createSearchControls(returningAttributes, searchAllDepths,
84                      timeLimitMillis);
85                  if (log.isDebugEnabled())
86                  {
87                      log.debug("AbstractLDAPPager.preload:" + originalQuery.encode());
88                  }
89                  enume = ctx.search(originalBaseSearchContext, originalQuery.encode(), ctls);
90              }
91  
92              super.preload(new EnumerationAdaptor<SearchResult>(enume));
93          }
94          catch (Exception e)
95          {
96              log.error("Error while paging through results", e);
97              throw new RuntimeException("Unexpected error paging through LDAP results: " + e.getMessage(), e);
98          }
99          finally
100         {
101             if (enume != null)
102             {
103                 try { enume.close(); } catch (NamingException e) { log.error("Error closing enumeration.", e); }
104             }
105 
106             if (ctx != null)
107             {
108                 try { ctx.close(); } catch (NamingException e) { log.error("Error closing context.", e); }
109             }
110 
111             closed = true;
112         }
113 
114         if (UtilTimerStack.isActive())
115         {
116             UtilTimerStack.pop(this.getClass().getName() + "_preload__(originalQuery= " + originalQuery + ")");
117         }
118     }
119 
120     protected List<T> fetch(Object element, List<T> prefetched) throws EntityException
121     {
122         return preloadSearchResult((SearchResult) element, prefetched);
123     }
124 
125 }