001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2015 ForgeRock AS.
026 */
027
028package org.opends.guitools.controlpanel.util;
029
030import java.util.HashSet;
031import java.util.Set;
032
033import javax.naming.NamingEnumeration;
034import javax.naming.directory.SearchControls;
035import javax.naming.directory.SearchResult;
036import javax.naming.ldap.InitialLdapContext;
037
038import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
039import org.opends.guitools.controlpanel.event.EntryReadErrorEvent;
040import org.opends.guitools.controlpanel.event.EntryReadEvent;
041import org.opends.guitools.controlpanel.event.EntryReadListener;
042
043/**
044 * A class that reads an entry on the background.  This is used in the LDAP
045 * entries browser.  When the entry is read it notifies to the EntryReadListener
046 * objects that have been registered.
047 *
048 */
049public class LDAPEntryReader extends BackgroundTask<CustomSearchResult>
050{
051  private final String dn;
052  private final InitialLdapContext ctx;
053  private final Set<EntryReadListener> listeners = new HashSet<>();
054  private boolean isOver;
055  private boolean notifyListeners;
056
057  /**
058   * Constructor of the entry reader.
059   * @param dn the DN of the entry.
060   * @param ctx the connection to the server.
061   */
062  public LDAPEntryReader(String dn, InitialLdapContext ctx)
063  {
064    this.dn = dn;
065    this.ctx = ctx;
066    this.notifyListeners = true;
067  }
068
069  /** {@inheritDoc} */
070  @Override
071  public CustomSearchResult processBackgroundTask() throws Throwable
072  {
073    isOver = false;
074    NamingEnumeration<SearchResult> en = null;
075    try
076    {
077      SearchControls controls = new SearchControls();
078
079      String[] attrs = {"*", "+"};
080      controls.setReturningAttributes(attrs);
081      controls.setSearchScope(SearchControls.OBJECT_SCOPE);
082      final String filter = "(|(objectclass=*)(objectclass=ldapsubentry))";
083
084      en = ctx.search(Utilities.getJNDIName(dn), filter, controls);
085
086      SearchResult sr = null;
087      while (en.hasMore())
088      {
089        sr = en.next();
090      }
091
092      return new CustomSearchResult(sr, dn);
093    }
094    finally
095    {
096      if (isInterrupted())
097      {
098        isOver = true;
099      }
100      if (en != null)
101      {
102        en.close();
103      }
104    }
105  }
106
107  /** {@inheritDoc} */
108  @Override
109  public void backgroundTaskCompleted(CustomSearchResult sr,
110      Throwable throwable)
111  {
112    if (!isInterrupted() && isNotifyListeners())
113    {
114      if (throwable == null)
115      {
116        notifyListeners(sr);
117      }
118      else
119      {
120        notifyListeners(throwable);
121      }
122    }
123    isOver = true;
124  }
125
126  /**
127   * Returns whether this entry reader will notify the listeners once it is
128   * over.
129   * @return whether this entry reader will notify the listeners once it is
130   * over.
131   */
132  public boolean isNotifyListeners()
133  {
134    return notifyListeners;
135  }
136
137  /**
138   * Sets whether this entry reader will notify the listeners once it is
139   * over.
140   * @param notifyListeners whether this entry reader will notify the listeners
141   * once it is over.
142   */
143  public void setNotifyListeners(boolean notifyListeners)
144  {
145    this.notifyListeners = notifyListeners;
146  }
147
148  /**
149   * Returns <CODE>true</CODE> if the read process is over and
150   * <CODE>false</CODE> otherwise.
151   * @return <CODE>true</CODE> if the read process is over and
152   * <CODE>false</CODE> otherwise.
153   */
154  public boolean isOver()
155  {
156    return isOver;
157  }
158
159  /**
160   * Notifies listeners that a new entry was read.
161   * @param sr the new entry in form of CustomSearchResult.
162   */
163  private void notifyListeners(CustomSearchResult sr)
164  {
165    EntryReadEvent ev = new EntryReadEvent(this, sr);
166    for (EntryReadListener listener : listeners)
167    {
168      listener.entryRead(ev);
169    }
170  }
171
172  /**
173   * Notifies the listeners that an error occurred reading an entry.
174   * @param t the error that occurred reading an entry.
175   */
176  private void notifyListeners(Throwable t)
177  {
178    EntryReadErrorEvent ev = new EntryReadErrorEvent(this, dn, t);
179    for (EntryReadListener listener : listeners)
180    {
181      listener.entryReadError(ev);
182    }
183  }
184
185  /**
186   * Adds an EntryReadListener.
187   * @param listener the listener.
188   */
189  public void addEntryReadListener(EntryReadListener listener)
190  {
191    listeners.add(listener);
192  }
193
194  /**
195   * Removes an EntryReadListener.
196   * @param listener the listener.
197   */
198  public void removeEntryReadListener(EntryReadListener listener)
199  {
200    listeners.remove(listener);
201  }
202}