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    @Override
086    public byte[] getResponseValue()
087    {
088        if ( responseValue == null )
089        {
090            try
091            {
092                responseValue = encodeInternal().array();
093            }
094            catch ( EncoderException e )
095            {
096                LOG.error( I18n.err( I18n.ERR_04164 ), e );
097                throw new RuntimeException( e );
098            }
099        }
100
101        return responseValue;
102    }
103
104
105    /**
106     * Sets the response OID specific encoded response values.
107     * 
108     * @param responseValue the response specific encoded response values.
109     */
110    @Override
111    public void setResponseValue( byte[] responseValue )
112    {
113        GracefulDisconnectDecoder decoder = new GracefulDisconnectDecoder();
114
115        try
116        {
117            if ( responseValue != null )
118            {
119                decoder.decode( responseValue );
120                this.responseValue = new byte[responseValue.length];
121                System.arraycopy( responseValue, 0, this.responseValue, 0, responseValue.length );
122            }
123            else
124            {
125                this.responseValue = null;
126            }
127        }
128        catch ( DecoderException e )
129        {
130            LOG.error( I18n.err( I18n.ERR_04172 ), e );
131        }
132    }
133
134
135    /**
136     * {@inheritDoc}
137     */
138    @Override
139    public int getDelay()
140    {
141        return gracefulDisconnectResponse.getDelay();
142    }
143
144
145    /**
146     * {@inheritDoc}
147     */
148    @Override
149    public void setDelay( int delay )
150    {
151        gracefulDisconnectResponse.setDelay( delay );
152    }
153
154
155    /**
156     * {@inheritDoc}
157     */
158    @Override
159    public int getTimeOffline()
160    {
161        return gracefulDisconnectResponse.getTimeOffline();
162    }
163
164
165    /**
166     * {@inheritDoc}
167     */
168    @Override
169    public void setTimeOffline( int timeOffline )
170    {
171        gracefulDisconnectResponse.setTimeOffline( timeOffline );
172    }
173
174
175    /**
176     * {@inheritDoc}
177     */
178    @Override
179    public Referral getReplicatedContexts()
180    {
181        return gracefulDisconnectResponse.getReplicatedContexts();
182    }
183    
184    
185    /**
186     * {@inheritDoc}
187     */
188    @Override
189    public void addReplicatedContexts( String replicatedContext )
190    {
191        gracefulDisconnectResponse.getReplicatedContexts().addLdapUrl( replicatedContext );
192    }
193
194
195    /**
196     * Compute the GracefulDisconnect length 
197     * <pre>
198     * 0x30 L1 
199     *   | 
200     *   +--> [ 0x02 0x0(1-4) [0..720] ] 
201     *   +--> [ 0x80 0x0(1-3) [0..86400] ] 
202     *   +--> [ 0x30 L2 
203     *           | 
204     *           +--> (0x04 L3 value) + ]
205     * </pre>
206     */
207    /* no qualifier */ int computeLengthInternal()
208    {
209        gracefulDisconnectSequenceLength = 0;
210
211        if ( gracefulDisconnectResponse.getTimeOffline() != 0 )
212        {
213            gracefulDisconnectSequenceLength += 1 + 1 + BerValue.getNbBytes( gracefulDisconnectResponse.getTimeOffline() );
214        }
215
216        if ( gracefulDisconnectResponse.getDelay() != 0 )
217        {
218            gracefulDisconnectSequenceLength += 1 + 1 + BerValue.getNbBytes( gracefulDisconnectResponse.getDelay() );
219        }
220
221        if ( ( gracefulDisconnectResponse.getReplicatedContexts() != null )
222            && ( !gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls().isEmpty() ) )
223        {
224            replicatedContextsLength = 0;
225            
226            ldapUrlBytes = new ArrayList<>( gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls().size() );
227
228            // We may have more than one reference.
229            for ( String replicatedContext : gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls() )
230            {
231                byte[] bytes = Strings.getBytesUtf8( replicatedContext );
232                ldapUrlBytes.add( bytes );
233                int ldapUrlLength = bytes.length;
234                replicatedContextsLength += 1 + TLV.getNbBytes( ldapUrlLength ) + ldapUrlLength;
235            }
236
237            gracefulDisconnectSequenceLength += 1 + TLV.getNbBytes( replicatedContextsLength )
238                + replicatedContextsLength;
239        }
240
241        return 1 + TLV.getNbBytes( gracefulDisconnectSequenceLength ) + gracefulDisconnectSequenceLength;
242    }
243
244
245    /**
246     * Encodes the gracefulDisconnect extended operation.
247     * 
248     * @return A ByteBuffer that contains the encoded PDU
249     * @throws org.apache.directory.api.asn1.EncoderException If anything goes wrong.
250     */
251    /* no qualifier */ ByteBuffer encodeInternal() throws EncoderException
252    {
253        // Allocate the bytes buffer.
254        ByteBuffer bb = ByteBuffer.allocate( computeLengthInternal() );
255
256
257        bb.put( UniversalTag.SEQUENCE.getValue() );
258        bb.put( TLV.getBytes( gracefulDisconnectSequenceLength ) );
259
260        if ( gracefulDisconnectResponse.getTimeOffline() != 0 )
261        {
262            BerValue.encode( bb, gracefulDisconnectResponse.getTimeOffline() );
263        }
264
265        if ( gracefulDisconnectResponse.getDelay() != 0 )
266        {
267            bb.put( ( byte ) GracefulActionConstants.GRACEFUL_ACTION_DELAY_TAG );
268            bb.put( ( byte ) TLV.getNbBytes( gracefulDisconnectResponse.getDelay() ) );
269            bb.put( BerValue.getBytes( gracefulDisconnectResponse.getDelay() ) );
270        }
271
272        if ( ( gracefulDisconnectResponse.getReplicatedContexts() != null )
273            && ( !gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls().isEmpty() ) )
274        {
275            bb.put( UniversalTag.SEQUENCE.getValue() );
276            bb.put( TLV.getBytes( replicatedContextsLength ) );
277
278            // We may have more than one reference.
279            for ( byte[] replicatedContext : ldapUrlBytes )
280            {
281                BerValue.encode( bb, replicatedContext );
282            }
283        }
284
285        return bb;
286    }
287
288
289    /**
290     * Return a string representation of the graceful disconnect
291     */
292    @Override
293    public String toString()
294    {
295        StringBuilder sb = new StringBuilder();
296
297        sb.append( "Graceful Disconnect extended operation" );
298        sb.append( "    TimeOffline : " ).append( gracefulDisconnectResponse.getTimeOffline() ).append( '\n' );
299        sb.append( "    Delay : " ).append( gracefulDisconnectResponse.getDelay() ).append( '\n' );
300
301        if ( ( gracefulDisconnectResponse.getReplicatedContexts() != null ) 
302            && ( !gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls().isEmpty() ) )
303        {
304            sb.append( "    Replicated contexts :" );
305
306            // We may have more than one reference.
307            for ( String url : gracefulDisconnectResponse.getReplicatedContexts().getLdapUrls() )
308            {
309                sb.append( "\n        " ).append( url );
310            }
311        }
312
313        return sb.toString();
314    }
315}