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.extras.extended.ads_impl.gracefulDisconnect;
021
022
023import java.nio.ByteBuffer;
024import java.util.ArrayList;
025import java.util.List;
026
027import org.apache.directory.api.asn1.DecoderException;
028import org.apache.directory.api.asn1.EncoderException;
029import org.apache.directory.api.asn1.ber.tlv.BerValue;
030import org.apache.directory.api.asn1.ber.tlv.TLV;
031import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
032import org.apache.directory.api.i18n.I18n;
033import org.apache.directory.api.ldap.codec.api.ExtendedResponseDecorator;
034import org.apache.directory.api.ldap.codec.api.LdapApiService;
035import org.apache.directory.api.ldap.extras.extended.gracefulDisconnect.GracefulDisconnectResponse;
036import org.apache.directory.api.ldap.model.message.Referral;
037import org.apache.directory.api.util.Strings;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041
042/**
043 * A Decorator for CancelResponses.
044 *
045 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
046 */
047public class GracefulDisconnectResponseDecorator extends ExtendedResponseDecorator<GracefulDisconnectResponse>
048    implements GracefulDisconnectResponse
049{
050    /** The logger. */
051    private static final Logger LOG = LoggerFactory.getLogger( GracefulDisconnectResponseDecorator.class );
052
053    /** Length of the sequence */
054    private int gracefulDisconnectSequenceLength;
055
056    /** Length of the replicated contexts */
057    private int replicatedContextsLength;
058    
059    /** The encoded LDAP URL list */
060    private List<byte[]> ldapUrlBytes;
061
062    private GracefulDisconnectResponse gracefulDisconnectResponse;
063
064    /**
065     * Creates a new instance of CancelResponseDecorator.
066     *
067     * @param codec The LDAP service instance
068     * @param decoratedMessage The decorated message
069     */
070    public GracefulDisconnectResponseDecorator( LdapApiService codec, GracefulDisconnectResponse decoratedMessage )
071    {
072        super( codec, decoratedMessage );
073        gracefulDisconnectResponse = decoratedMessage;
074    }
075
076    
077    // ------------------------------------------------------------------------
078    // ExtendedResponse Interface Method Implementations
079    // ------------------------------------------------------------------------
080    /**
081     * Gets the response OID specific encoded response values.
082     * 
083     * @return the response specific encoded response values.
084     */
085    public byte[] getResponseValue()
086    {
087        if ( responseValue == null )
088        {
089            try
090            {
091                responseValue = encodeInternal().array();
092            }
093            catch ( EncoderException e )
094            {
095                LOG.error( I18n.err( I18n.ERR_04164 ), e );
096                throw new RuntimeException( e );
097            }
098        }
099
100        return responseValue;
101    }
102
103
104    /**
105     * Sets the response OID specific encoded response values.
106     * 
107     * @param responseValue the response specific encoded response values.
108     */
109    public void setResponseValue( byte[] responseValue )
110    {
111        GracefulDisconnectDecoder decoder = new GracefulDisconnectDecoder();
112
113        try
114        {
115            if ( responseValue != null )
116            {
117                decoder.decode( responseValue );
118                this.responseValue = new byte[responseValue.length];
119                System.arraycopy( responseValue, 0, this.responseValue, 0, responseValue.length );
120            }
121            else
122            {
123                this.responseValue = null;
124            }
125        }
126        catch ( DecoderException e )
127        {
128            LOG.error( I18n.err( I18n.ERR_04172 ), e );
129        }
130    }
131
132
133    /**
134     * {@inheritDoc}
135     */
136    public int getDelay()
137    {
138        return gracefulDisconnectResponse.getDelay();
139    }
140
141
142    /**
143     * {@inheritDoc}
144     */
145    public void setDelay( int delay )
146    {
147        gracefulDisconnectResponse.setDelay( delay );
148    }
149
150
151    /**
152     * {@inheritDoc}
153     */
154    public int getTimeOffline()
155    {
156        return gracefulDisconnectResponse.getTimeOffline();
157    }
158
159
160    /**
161     * {@inheritDoc}
162     */
163    public void setTimeOffline( int timeOffline )
164    {
165        gracefulDisconnectResponse.setTimeOffline( timeOffline );
166    }
167
168
169    /**
170     * {@inheritDoc}
171     */
172    public Referral getReplicatedContexts()
173    {
174        return gracefulDisconnectResponse.getReplicatedContexts();
175    }
176    
177    
178    /**
179     * {@inheritDoc}
180     */
181    public void addReplicatedContexts( String replicatedContext )
182    {
183        gracefulDisconnectResponse.getReplicatedContexts().addLdapUrl( replicatedContext );
184    }
185
186
187    /**
188     * Compute the GracefulDisconnect length 
189     * <pre>
190     * 0x30 L1 
191     *   | 
192     *   +--> [ 0x02 0x0(1-4) [0..720] ] 
193     *   +--> [ 0x80 0x0(1-3) [0..86400] ] 
194     *   +--> [ 0x30 L2 
195     *           | 
196     *           +--> (0x04 L3 value) + ]
197     * </pre>
198     */
199    /* no qualifier */ int computeLengthInternal()
200    {
201        gracefulDisconnectSequenceLength = 0;
202
203        if ( gracefulDisconnectResponse.getTimeOffline() != 0 )
204        {
205            gracefulDisconnectSequenceLength += 1 + 1 + BerValue.getNbBytes( gracefulDisconnectResponse.getTimeOffline() );
206        }
207
208        if ( gracefulDisconnectResponse.getDelay() != 0 )
209        {
210            gracefulDisconnectSequenceLength += 1 + 1 + BerValue.getNbBytes( gracefulDisconnectResponse.getDelay() );
211        }
212
213        if ( ( gracefulDisconnectResponse.getReplicatedContexts() != null )
214            && ( gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls().size() != 0 ) )
215        {
216            replicatedContextsLength = 0;
217            
218            ldapUrlBytes = new ArrayList<byte[]>( gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls().size() );
219
220            // We may have more than one reference.
221            for ( String replicatedContext : gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls() )
222            {
223                byte[] bytes = Strings.getBytesUtf8( replicatedContext );
224                ldapUrlBytes.add( bytes );
225                int ldapUrlLength = bytes.length;
226                replicatedContextsLength += 1 + TLV.getNbBytes( ldapUrlLength ) + ldapUrlLength;
227            }
228
229            gracefulDisconnectSequenceLength += 1 + TLV.getNbBytes( replicatedContextsLength )
230                + replicatedContextsLength;
231        }
232
233        return 1 + TLV.getNbBytes( gracefulDisconnectSequenceLength ) + gracefulDisconnectSequenceLength;
234    }
235
236
237    /**
238     * Encodes the gracefulDisconnect extended operation.
239     * 
240     * @return A ByteBuffer that contains the encoded PDU
241     * @throws org.apache.directory.api.asn1.EncoderException If anything goes wrong.
242     */
243    /* no qualifier */ ByteBuffer encodeInternal() throws EncoderException
244    {
245        // Allocate the bytes buffer.
246        ByteBuffer bb = ByteBuffer.allocate( computeLengthInternal() );
247
248
249        bb.put( UniversalTag.SEQUENCE.getValue() );
250        bb.put( TLV.getBytes( gracefulDisconnectSequenceLength ) );
251
252        if ( gracefulDisconnectResponse.getTimeOffline() != 0 )
253        {
254            BerValue.encode( bb, gracefulDisconnectResponse.getTimeOffline() );
255        }
256
257        if ( gracefulDisconnectResponse.getDelay() != 0 )
258        {
259            bb.put( ( byte ) GracefulActionConstants.GRACEFUL_ACTION_DELAY_TAG );
260            bb.put( ( byte ) TLV.getNbBytes( gracefulDisconnectResponse.getDelay() ) );
261            bb.put( BerValue.getBytes( gracefulDisconnectResponse.getDelay() ) );
262        }
263
264        if ( ( gracefulDisconnectResponse.getReplicatedContexts() != null )
265            && ( gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls().size() != 0 ) )
266        {
267            bb.put( UniversalTag.SEQUENCE.getValue() );
268            bb.put( TLV.getBytes( replicatedContextsLength ) );
269
270            // We may have more than one reference.
271            for ( byte[] replicatedContext : ldapUrlBytes )
272            {
273                BerValue.encode( bb, replicatedContext );
274            }
275        }
276
277        return bb;
278    }
279
280
281    /**
282     * Return a string representation of the graceful disconnect
283     */
284    public String toString()
285    {
286        StringBuffer sb = new StringBuffer();
287
288        sb.append( "Graceful Disconnect extended operation" );
289        sb.append( "    TimeOffline : " ).append( gracefulDisconnectResponse.getTimeOffline() ).append( '\n' );
290        sb.append( "    Delay : " ).append( gracefulDisconnectResponse.getDelay() ).append( '\n' );
291
292        if ( ( gracefulDisconnectResponse.getReplicatedContexts() != null ) && ( gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls().size() != 0 ) )
293        {
294            sb.append( "    Replicated contexts :" );
295
296            // We may have more than one reference.
297            for ( String url : gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls() )
298            {
299                sb.append( "\n        " ).append( url );
300            }
301        }
302
303        return sb.toString();
304    }
305}