1 /*
   2  * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.naming.ldap;
  27 
  28 import java.io.IOException;
  29 import com.sun.jndi.ldap.Ber;
  30 import com.sun.jndi.ldap.BerEncoder;
  31 
  32 /**
  33  * Requests that the results of a search operation be sorted by the LDAP server
  34  * before being returned.
  35  * The sort criteria are specified using an ordered list of one or more sort
  36  * keys, with associated sort parameters.
  37  * Search results are sorted at the LDAP server according to the parameters
  38  * supplied in the sort control and then returned to the requestor. If sorting
  39  * is not supported at the server (and the sort control is marked as critical)
  40  * then the search operation is not performed and an error is returned.
  41  * <p>
  42  * The following code sample shows how the class may be used:
  43  * <pre>{@code
  44  *
  45  *     // Open an LDAP association
  46  *     LdapContext ctx = new InitialLdapContext();
  47  *
  48  *     // Activate sorting
  49  *     String sortKey = "cn";
  50  *     ctx.setRequestControls(new Control[]{
  51  *         new SortControl(sortKey, Control.CRITICAL) });
  52  *
  53  *     // Perform a search
  54  *     NamingEnumeration results =
  55  *         ctx.search("", "(objectclass=*)", new SearchControls());
  56  *
  57  *     // Iterate over search results
  58  *     while (results != null && results.hasMore()) {
  59  *         // Display an entry
  60  *         SearchResult entry = (SearchResult)results.next();
  61  *         System.out.println(entry.getName());
  62  *         System.out.println(entry.getAttributes());
  63  *
  64  *         // Handle the entry's response controls (if any)
  65  *         if (entry instanceof HasControls) {
  66  *             // ((HasControls)entry).getControls();
  67  *         }
  68  *     }
  69  *     // Examine the sort control response
  70  *     Control[] controls = ctx.getResponseControls();
  71  *     if (controls != null) {
  72  *         for (int i = 0; i < controls.length; i++) {
  73  *             if (controls[i] instanceof SortResponseControl) {
  74  *                 SortResponseControl src = (SortResponseControl)controls[i];
  75  *                 if (! src.isSorted()) {
  76  *                     throw src.getException();
  77  *                 }
  78  *             } else {
  79  *                 // Handle other response controls (if any)
  80  *             }
  81  *         }
  82  *     }
  83  *
  84  *     // Close the LDAP association
  85  *     ctx.close();
  86  *     ...
  87  *
  88  * }</pre>
  89  * <p>
  90  * This class implements the LDAPv3 Request Control for server-side sorting
  91  * as defined in
  92  * <a href="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</a>.
  93  *
  94  * The control's value has the following ASN.1 definition:
  95  * <pre>
  96  *
  97  *     SortKeyList ::= SEQUENCE OF SEQUENCE {
  98  *         attributeType     AttributeDescription,
  99  *         orderingRule  [0] MatchingRuleId OPTIONAL,
 100  *         reverseOrder  [1] BOOLEAN DEFAULT FALSE }
 101  *
 102  * </pre>
 103  *
 104  * @since 1.5
 105  * @see SortKey
 106  * @see SortResponseControl
 107  * @author Vincent Ryan
 108  */
 109 final public class SortControl extends BasicControl {
 110 
 111     /**
 112      * The server-side sort control's assigned object identifier
 113      * is 1.2.840.113556.1.4.473.
 114      */
 115     public static final String OID = "1.2.840.113556.1.4.473";
 116 
 117     private static final long serialVersionUID = -1965961680233330744L;
 118 
 119     /**
 120      * Constructs a control to sort on a single attribute in ascending order.
 121      * Sorting will be performed using the ordering matching rule defined
 122      * for use with the specified attribute.
 123      *
 124      * @param   sortBy  An attribute ID to sort by.
 125      * @param   criticality     If true then the server must honor the control
 126      *                          and return the search results sorted as
 127      *                          requested or refuse to perform the search.
 128      *                          If false, then the server need not honor the
 129      *                          control.
 130      * @exception IOException If an error was encountered while encoding the
 131      *                        supplied arguments into a control.
 132      */
 133     public SortControl(String sortBy, boolean criticality) throws IOException {
 134 
 135         super(OID, criticality, null);
 136         super.value = setEncodedValue(new SortKey[]{ new SortKey(sortBy) });
 137     }
 138 
 139     /**
 140      * Constructs a control to sort on a list of attributes in ascending order.
 141      * Sorting will be performed using the ordering matching rule defined
 142      * for use with each of the specified attributes.
 143      *
 144      * @param   sortBy  A non-null list of attribute IDs to sort by.
 145      *                  The list is in order of highest to lowest sort key
 146      *                  precedence.
 147      * @param   criticality     If true then the server must honor the control
 148      *                          and return the search results sorted as
 149      *                          requested or refuse to perform the search.
 150      *                          If false, then the server need not honor the
 151      *                          control.
 152      * @exception IOException If an error was encountered while encoding the
 153      *                        supplied arguments into a control.
 154      */
 155     public SortControl(String[] sortBy, boolean criticality)
 156         throws IOException {
 157 
 158         super(OID, criticality, null);
 159         SortKey[] sortKeys = new SortKey[sortBy.length];
 160         for (int i = 0; i < sortBy.length; i++) {
 161             sortKeys[i] = new SortKey(sortBy[i]);
 162         }
 163         super.value = setEncodedValue(sortKeys);
 164     }
 165 
 166     /**
 167      * Constructs a control to sort on a list of sort keys.
 168      * Each sort key specifies the sort order and ordering matching rule to use.
 169      *
 170      * @param   sortBy      A non-null list of keys to sort by.
 171      *                      The list is in order of highest to lowest sort key
 172      *                      precedence.
 173      * @param   criticality     If true then the server must honor the control
 174      *                          and return the search results sorted as
 175      *                          requested or refuse to perform the search.
 176      *                          If false, then the server need not honor the
 177      *                          control.
 178      * @exception IOException If an error was encountered while encoding the
 179      *                        supplied arguments into a control.
 180      */
 181     public SortControl(SortKey[] sortBy, boolean criticality)
 182         throws IOException {
 183 
 184         super(OID, criticality, null);
 185         super.value = setEncodedValue(sortBy);
 186     }
 187 
 188     /*
 189      * Encodes the sort control's value using ASN.1 BER.
 190      * The result includes the BER tag and length for the control's value but
 191      * does not include the control's object identifer and criticality setting.
 192      *
 193      * @param   sortKeys    A non-null list of keys to sort by.
 194      * @return A possibly null byte array representing the ASN.1 BER encoded
 195      *         value of the sort control.
 196      * @exception IOException If a BER encoding error occurs.
 197      */
 198     private byte[] setEncodedValue(SortKey[] sortKeys) throws IOException {
 199 
 200         // build the ASN.1 BER encoding
 201         BerEncoder ber = new BerEncoder(30 * sortKeys.length + 10);
 202         String matchingRule;
 203 
 204         ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 205 
 206         for (int i = 0; i < sortKeys.length; i++) {
 207             ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 208             ber.encodeString(sortKeys[i].getAttributeID(), true); // v3
 209 
 210             if ((matchingRule = sortKeys[i].getMatchingRuleID()) != null) {
 211                 ber.encodeString(matchingRule, (Ber.ASN_CONTEXT | 0), true);
 212             }
 213             if (! sortKeys[i].isAscending()) {
 214                 ber.encodeBoolean(true, (Ber.ASN_CONTEXT | 1));
 215             }
 216             ber.endSeq();
 217         }
 218         ber.endSeq();
 219 
 220         return ber.getTrimmedBuf();
 221     }
 222 }