001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.codec.search.controls.persistentSearch;
021    
022    
023    import java.nio.ByteBuffer;
024    
025    import org.apache.directory.shared.asn1.ber.tlv.TLV;
026    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
027    import org.apache.directory.shared.asn1.ber.tlv.Value;
028    import org.apache.directory.shared.asn1.codec.EncoderException;
029    import org.apache.directory.shared.i18n.I18n;
030    import org.apache.directory.shared.ldap.codec.controls.AbstractControl;
031    import org.apache.directory.shared.ldap.codec.search.controls.ChangeType;
032    
033    
034    /**
035     * A persistence search object
036     * 
037     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038     * @version $Rev: 912399 $, $Date: 2010-02-21 21:52:31 +0100 (Sun, 21 Feb 2010) $, 
039     */
040    public class PersistentSearchControl extends AbstractControl
041    {
042        /** This control OID */
043        public static final String CONTROL_OID = "2.16.840.1.113730.3.4.3";
044    
045        /**
046         * If changesOnly is TRUE, the server MUST NOT return any existing entries
047         * that match the search criteria. Entries are only returned when they are
048         * changed (added, modified, deleted, or subject to a modifyDN operation).
049         */
050        private boolean changesOnly = true;
051    
052        /**
053         * If returnECs is TRUE, the server MUST return an Entry Change Notification
054         * control with each entry returned as the result of changes.
055         */
056        private boolean returnECs = false;
057    
058        /**
059         * As changes are made to the server, the effected entries MUST be returned
060         * to the client if they match the standard search criteria and if the
061         * operation that caused the change is included in the changeTypes field.
062         * The changeTypes field is the logical OR of one or more of these values:
063         * add    (1), 
064         * delete (2), 
065         * modify (4), 
066         * modDN  (8).
067         */
068        private int changeTypes = CHANGE_TYPES_MAX;
069        
070        /** Definition of the change types */
071        public static final int CHANGE_TYPE_ADD     = 1;
072        public static final int CHANGE_TYPE_DELETE  = 2;
073        public static final int CHANGE_TYPE_MODIFY  = 4;
074        public static final int CHANGE_TYPE_MODDN   = 8;
075        
076        /** Min and Max values for the possible combined change types */
077        public static final int CHANGE_TYPES_MIN = CHANGE_TYPE_ADD;
078        public static final int CHANGE_TYPES_MAX = CHANGE_TYPE_ADD | CHANGE_TYPE_DELETE | CHANGE_TYPE_MODIFY | CHANGE_TYPE_MODDN;
079    
080        /** A temporary storage for a psearch length */
081        private int psearchSeqLength;
082    
083        /**
084         * Default constructor
085         *
086         */
087        public PersistentSearchControl()
088        {
089            super( CONTROL_OID );
090            
091            decoder = new PersistentSearchControlDecoder();
092        }
093    
094        public void setChangesOnly( boolean changesOnly )
095        {
096            this.changesOnly = changesOnly;
097        }
098    
099    
100        public boolean isChangesOnly()
101        {
102            return changesOnly;
103        }
104    
105    
106        public void setReturnECs( boolean returnECs )
107        {
108            this.returnECs = returnECs;
109        }
110    
111    
112        public boolean isReturnECs()
113        {
114            return returnECs;
115        }
116    
117    
118        public void setChangeTypes( int changeTypes )
119        {
120            this.changeTypes = changeTypes;
121        }
122    
123    
124        public int getChangeTypes()
125        {
126            return changeTypes;
127        }
128    
129        /**
130         * Compute the PagedSearchControl length, which is the sum
131         * of the control length and the value length.
132         * 
133         * <pre>
134         * PersistentSearchControl value length :
135         * 
136         * 0x30 L1 
137         *   | 
138         *   +--> 0x02 0x0(1-4) [0..2^31-1] (changeTypes) 
139         *   +--> 0x01 0x01 [0x00 | 0xFF] (changeOnly) 
140         *   +--> 0x01 0x01 [0x00 | 0xFF] (returnRCs)
141         * </pre> 
142         */
143        public int computeLength()
144        {
145            int changeTypesLength = 1 + 1 + Value.getNbBytes( changeTypes );
146            int changesOnlyLength = 1 + 1 + 1;
147            int returnRCsLength = 1 + 1 + 1;
148    
149            psearchSeqLength = changeTypesLength + changesOnlyLength + returnRCsLength;
150            int valueLength = 1 + TLV.getNbBytes( psearchSeqLength ) + psearchSeqLength;
151    
152            // Call the super class to compute the global control length
153            return super.computeLength( valueLength );
154        }
155    
156    
157        /**
158         * Encodes the persistent search control.
159         * 
160         * @param buffer The encoded sink
161         * @return A ByteBuffer that contains the encoded PDU
162         * @throws EncoderException If anything goes wrong.
163         */
164        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
165        {
166            if ( buffer == null )
167            {
168                throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
169            }
170    
171            // Encode the Control envelop
172            super.encode( buffer );
173            
174            // Encode the OCTET_STRING tag
175            buffer.put( UniversalTag.OCTET_STRING_TAG );
176            buffer.put( TLV.getBytes( valueLength ) );
177    
178            // Now encode the PagedSearch specific part
179            buffer.put( UniversalTag.SEQUENCE_TAG );
180            buffer.put( TLV.getBytes( psearchSeqLength ) );
181    
182            Value.encode( buffer, changeTypes );
183            Value.encode( buffer, changesOnly );
184            Value.encode( buffer, returnECs );
185            
186            return buffer;
187        }
188        
189        
190        /**
191         * {@inheritDoc}
192         */
193        public byte[] getValue()
194        {
195            if ( value == null )
196            {
197                try
198                { 
199                    computeLength();
200                    ByteBuffer buffer = ByteBuffer.allocate( valueLength );
201                    
202                    // Now encode the PagedSearch specific part
203                    buffer.put( UniversalTag.SEQUENCE_TAG );
204                    buffer.put( TLV.getBytes( psearchSeqLength ) );
205    
206                    Value.encode( buffer, changeTypes );
207                    Value.encode( buffer, changesOnly );
208                    Value.encode( buffer, returnECs );
209                    
210                    value = buffer.array();
211                }
212                catch ( Exception e )
213                {
214                    return null;
215                }
216            }
217            
218            return value;
219        }
220    
221        
222        public boolean isNotificationEnabled( ChangeType changeType )
223        {
224            return ( changeType.getValue() & changeTypes ) > 0;
225        }
226    
227    
228        public void enableNotification( ChangeType changeType )
229        {
230            changeTypes |= changeType.getValue();
231        }
232    
233    
234        /**
235         * Return a String representing this PSearchControl.
236         */
237        public String toString()
238        {
239            StringBuffer sb = new StringBuffer();
240    
241            sb.append( "    Persistant Search Control\n" );
242            sb.append( "        oid : " ).append( getOid() ).append( '\n' );
243            sb.append( "        critical : " ).append( isCritical() ).append( '\n' );
244            sb.append( "        changeTypes : '" ).append( changeTypes ).append( "'\n" );
245            sb.append( "        changesOnly : '" ).append( changesOnly ).append( "'\n" );
246            sb.append( "        returnECs   : '" ).append( returnECs ).append( "'\n" );
247    
248            return sb.toString();
249        }
250    }