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 */
020package org.apache.directory.api.ldap.codec.decorators;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025
026import org.apache.directory.api.asn1.EncoderException;
027import org.apache.directory.api.asn1.ber.tlv.BerValue;
028import org.apache.directory.api.asn1.ber.tlv.TLV;
029import org.apache.directory.api.i18n.I18n;
030import org.apache.directory.api.ldap.codec.api.Decorator;
031import org.apache.directory.api.ldap.codec.api.LdapApiService;
032import org.apache.directory.api.ldap.codec.api.LdapEncoder;
033import org.apache.directory.api.ldap.model.message.LdapResult;
034import org.apache.directory.api.ldap.model.message.Referral;
035import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
036import org.apache.directory.api.ldap.model.name.Dn;
037import org.apache.directory.api.util.Strings;
038
039
040/**
041 * A decorator for the LdapResultResponse message
042 *
043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044 */
045public class LdapResultDecorator implements LdapResult, Decorator<LdapResult>
046{
047    /** The decorated LdapResult */
048    private final LdapResult decoratedLdapResult;
049
050    /** Temporary storage for message bytes */
051    private byte[] errorMessageBytes;
052
053    /** Temporary storage of the byte[] representing the matchedDN */
054    private byte[] matchedDnBytes;
055
056    /** The codec responsible for encoding and decoding this object. */
057    private LdapApiService codec;
058
059    private static final byte[] DEFAULT_SUCCESS = new byte[]
060        { 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00 };
061
062
063    /**
064     * Makes a LdapResult encodable.
065     *
066     * @param codec The LDAP service instance
067     * @param decoratedLdapResult the decorated LdapResult
068     */
069    public LdapResultDecorator( LdapApiService codec, LdapResult decoratedLdapResult )
070    {
071        this.decoratedLdapResult = decoratedLdapResult;
072        this.codec = codec;
073    }
074
075
076    //-------------------------------------------------------------------------
077    // The LdapResult methods
078    //-------------------------------------------------------------------------
079
080    /**
081     * {@inheritDoc}
082     */
083    @Override
084    public ResultCodeEnum getResultCode()
085    {
086        return decoratedLdapResult.getResultCode();
087    }
088
089
090    /**
091     * {@inheritDoc}
092     */
093    @Override
094    public void setResultCode( ResultCodeEnum resultCode )
095    {
096        decoratedLdapResult.setResultCode( resultCode );
097    }
098
099
100    /**
101     * {@inheritDoc}
102     */
103    @Override
104    public Dn getMatchedDn()
105    {
106        return decoratedLdapResult.getMatchedDn();
107    }
108
109
110    /**
111     * {@inheritDoc}
112     */
113    @Override
114    public void setMatchedDn( Dn dn )
115    {
116        decoratedLdapResult.setMatchedDn( dn );
117    }
118
119
120    /**
121     * {@inheritDoc}
122     */
123    @Override
124    public String getDiagnosticMessage()
125    {
126        return decoratedLdapResult.getDiagnosticMessage();
127    }
128
129
130    /**
131     * {@inheritDoc}
132     */
133    @Override
134    public void setDiagnosticMessage( String diagnosticMessage )
135    {
136        decoratedLdapResult.setDiagnosticMessage( diagnosticMessage );
137    }
138
139
140    /**
141     * {@inheritDoc}
142     */
143    @Override
144    public boolean isReferral()
145    {
146        return decoratedLdapResult.isReferral();
147    }
148
149
150    /**
151     * {@inheritDoc}
152     */
153    @Override
154    public Referral getReferral()
155    {
156        return decoratedLdapResult.getReferral();
157    }
158
159
160    /**
161     * {@inheritDoc}
162     */
163    @Override
164    public void setReferral( Referral referral )
165    {
166        decoratedLdapResult.setReferral( referral );
167    }
168
169
170    /**
171     * {@inheritDoc}
172     */
173    @Override
174    public String toString()
175    {
176        return decoratedLdapResult.toString();
177    }
178
179
180    //-------------------------------------------------------------------------
181    // The Decorator methods
182    //-------------------------------------------------------------------------
183    /**
184     * Compute the LdapResult length 
185     * <br>
186     * LdapResult :
187     * <pre> 
188     *   0x0A 01 resultCode (0..80)
189     *   0x04 L1 matchedDN (L1 = Length(matchedDN)) 
190     *   0x04 L2 errorMessage (L2 = Length(errorMessage)) 
191     *   [0x83 L3] referrals 
192     *     | 
193     *     +--&gt; 0x04 L4 referral 
194     *     +--&gt; 0x04 L5 referral 
195     *     +--&gt; ... 
196     *     +--&gt; 0x04 Li referral 
197     *     +--&gt; ... 
198     *     +--&gt; 0x04 Ln referral 
199     *     
200     * L1 = Length(matchedDN) 
201     * L2 = Length(errorMessage) 
202     * L3 = n*Length(0x04) + sum(Length(L4) .. Length(Ln)) + sum(L4..Ln) 
203     * L4..n = Length(0x04) + Length(Li) + Li 
204     * Length(LdapResult) = Length(0x0x0A) +
205     *      Length(0x01) + 1 + Length(0x04) + Length(L1) + L1 + Length(0x04) +
206     *      Length(L2) + L2 + Length(0x83) + Length(L3) + L3
207     * </pre>
208     */
209    @Override
210    public int computeLength()
211    {
212        if ( decoratedLdapResult.isDefaultSuccess() )
213        {
214            // The length of a default success PDU : 0xA0 0x01 0x00 0x04 0x00 0x04 0x00
215            return DEFAULT_SUCCESS.length;
216        }
217
218        int ldapResultLength;
219
220        // The result code
221        ldapResultLength = 1 + 1 + BerValue.getNbBytes( getResultCode().getValue() );
222
223        // The matchedDN length
224        if ( getMatchedDn() == null )
225        {
226            ldapResultLength += 1 + 1;
227        }
228        else
229        {
230            matchedDnBytes = Strings.getBytesUtf8Ascii( Strings.trimLeft( getMatchedDn().getName() ) );
231            ldapResultLength += 1 + TLV.getNbBytes( matchedDnBytes.length ) + matchedDnBytes.length;
232        }
233
234        // The errorMessage length
235        errorMessageBytes = Strings.getBytesUtf8Ascii( getDiagnosticMessage() );
236        ldapResultLength += 1 + TLV.getNbBytes( errorMessageBytes.length ) + errorMessageBytes.length;
237
238        int referralLength = LdapEncoder.computeReferralLength( getReferral() );
239
240        if ( referralLength != 0 )
241        {
242            // The referrals
243            ldapResultLength += 1 + TLV.getNbBytes( referralLength ) + referralLength;
244        }
245
246        return ldapResultLength;
247    }
248
249
250    /**
251     * Encode the LdapResult message to a PDU.
252     * 
253     * @param buffer The buffer where to put the PDU
254     * @return The PDU.
255     */
256    @Override
257    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
258    {
259        if ( buffer == null )
260        {
261            throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
262        }
263
264        if ( decoratedLdapResult.isDefaultSuccess() )
265        {
266            // The length of a default success PDU : 0xA0 0x01 0x00 0x04 0x00 0x04 0x00
267            buffer.put( DEFAULT_SUCCESS );
268
269            return buffer;
270        }
271
272        try
273        {
274            // The result code
275            BerValue.encodeEnumerated( buffer, getResultCode().getValue() );
276        }
277        catch ( BufferOverflowException boe )
278        {
279            throw new EncoderException( I18n.err( I18n.ERR_04005 ), boe );
280        }
281
282        // The matchedDN
283        BerValue.encode( buffer, matchedDnBytes );
284
285        // The error message
286        BerValue.encode( buffer, errorMessageBytes );
287
288        // The referrals, if any
289        Referral referral = getReferral();
290
291        if ( referral != null )
292        {
293            LdapEncoder.encodeReferral( buffer, referral );
294        }
295
296        return buffer;
297    }
298
299
300    /**
301     * {@inheritDoc}
302     */
303    @Override
304    public LdapResult getDecorated()
305    {
306        return decoratedLdapResult;
307    }
308
309
310    /**
311     * {@inheritDoc}
312     */
313    @Override
314    public LdapApiService getCodecService()
315    {
316        return codec;
317    }
318
319
320    /**
321     * {@inheritDoc}
322     */
323    @Override
324    public boolean isDefaultSuccess()
325    {
326        return decoratedLdapResult.isDefaultSuccess();
327    }
328}