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 org.apache.directory.api.asn1.DecoderException;
024import org.apache.directory.api.asn1.ber.grammar.AbstractGrammar;
025import org.apache.directory.api.asn1.ber.grammar.GrammarAction;
026import org.apache.directory.api.asn1.ber.grammar.GrammarTransition;
027import org.apache.directory.api.asn1.ber.tlv.BerValue;
028import org.apache.directory.api.asn1.ber.tlv.IntegerDecoder;
029import org.apache.directory.api.asn1.ber.tlv.IntegerDecoderException;
030import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
031import org.apache.directory.api.i18n.I18n;
032import org.apache.directory.api.ldap.model.exception.LdapURLEncodingException;
033import org.apache.directory.api.ldap.model.url.LdapUrl;
034import org.apache.directory.api.util.Strings;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038
039/**
040 * This class implements the Graceful Disconnect. All the actions are declared
041 * in this class. As it is a singleton, these declaration are only done once.
042 * The grammar is :
043 * 
044 * <pre>
045 *  GracefulDisconnect ::= SEQUENCE {
046 *      timeOffline INTEGER (0..720) DEFAULT 0,
047 *      delay [0] INTEGER (0..86400) DEFAULT 0,
048 *      replicatedContexts Referral OPTIONAL
049 * }
050 *  
051 *  Referral ::= SEQUENCE OF LDAPURL
052 *  
053 *  LDAPURL ::= LDAPString -- limited to characters permitted in URLs
054 *  
055 *  LDAPString ::= OCTET STRING
056 * </pre>
057 * 
058 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
059 */
060public final class GracefulDisconnectResponseGrammar extends AbstractGrammar<GracefulDisconnectResponseContainer>
061{
062    /** The logger */
063    static final Logger LOG = LoggerFactory.getLogger( GracefulDisconnectResponseGrammar.class );
064
065    /** The instance of grammar. GracefulDisconnectnGrammar is a singleton */
066    private static GracefulDisconnectResponseGrammar instance = new GracefulDisconnectResponseGrammar();
067
068    /**
069     * The action used to store a Time Offline.
070     */
071    private GrammarAction<GracefulDisconnectResponseContainer> storeDelay =
072        new GrammarAction<GracefulDisconnectResponseContainer>( "Set Graceful Disconnect Delay" )
073        {
074            public void action( GracefulDisconnectResponseContainer container ) throws DecoderException
075            {
076                BerValue value = container.getCurrentTLV().getValue();
077
078                try
079                {
080                    int delay = IntegerDecoder.parse( value, 0, 86400 );
081
082                    if ( LOG.isDebugEnabled() )
083                    {
084                        LOG.debug( I18n.msg( I18n.MSG_08204_DELAY, delay ) );
085                    }
086
087                    container.getGracefulDisconnectResponse().setDelay( delay );
088                    container.setGrammarEndAllowed( true );
089                }
090                catch ( IntegerDecoderException ide )
091                {
092                    String msg = I18n.err( I18n.ERR_08205_CANNOT_DECODE_DELAY, Strings.dumpBytes( value.getData() ) );
093                    LOG.error( msg );
094                    throw new DecoderException( msg, ide );
095                }
096            }
097        };
098
099    /**
100     * The action used to store a referral.
101     */
102    private GrammarAction<GracefulDisconnectResponseContainer> storeReferral =
103        new GrammarAction<GracefulDisconnectResponseContainer>( "Stores a referral" )
104        {
105            public void action( GracefulDisconnectResponseContainer container ) throws DecoderException
106            {
107                BerValue value = container.getCurrentTLV().getValue();
108
109                try
110                {
111                    if ( Strings.isEmpty( value.getData() ) )
112                    {
113                        String msg = I18n.err( I18n.ERR_08224_NULL_URL_DECODING_FAILURE );
114                        LOG.error( msg );
115                        throw new DecoderException( msg );
116                    }
117
118                    String url = Strings.utf8ToString( value.getData() );
119
120                    LdapUrl ldapUrl = new LdapUrl( url );
121                    container.getGracefulDisconnectResponse().addReplicatedContexts( url );
122                    container.setGrammarEndAllowed( true );
123
124                    if ( LOG.isDebugEnabled() )
125                    {
126                        LOG.debug( I18n.msg( I18n.MSG_08214_STORES_A_REFERRAL, ldapUrl ) );
127                    }
128                }
129                catch ( LdapURLEncodingException luee )
130                {
131                    String msg = I18n.err( I18n.ERR_08225_URL_DECODING_FAILURE, Strings.dumpBytes( value.getData() ) );
132                    LOG.error( msg );
133                    throw new DecoderException( msg, luee );
134                }
135            }
136        };
137
138    /**
139     * The action used to store a Time Offline.
140     */
141    private GrammarAction<GracefulDisconnectResponseContainer> storeTimeOffline =
142        new GrammarAction<GracefulDisconnectResponseContainer>( "Set Graceful Disconnect time offline" )
143        {
144            public void action( GracefulDisconnectResponseContainer container ) throws DecoderException
145            {
146                BerValue value = container.getCurrentTLV().getValue();
147
148                try
149                {
150                    int timeOffline = IntegerDecoder.parse( value, 0, 720 );
151
152                    if ( LOG.isDebugEnabled() )
153                    {
154                        LOG.debug( I18n.msg( I18n.MSG_08216_TIME_OFFLINE, timeOffline ) );
155                    }
156
157                    container.getGracefulDisconnectResponse().setTimeOffline( timeOffline );
158                    container.setGrammarEndAllowed( true );
159                }
160                catch ( IntegerDecoderException ide )
161                {
162                    String msg = I18n.err( I18n.ERR_08206_TIME_OFFLINE_DECODING_FAILED, Strings.dumpBytes( value.getData() ) );
163                    LOG.error( msg );
164                    throw new DecoderException( msg, ide );
165                }
166            }
167        };
168
169
170    /**
171     * Creates a new GracefulDisconnectGrammar object.
172     */
173    @SuppressWarnings("unchecked")
174    private GracefulDisconnectResponseGrammar()
175    {
176        setName( GracefulDisconnectResponseGrammar.class.getName() );
177
178        // Create the transitions table
179        super.transitions = new GrammarTransition[GracefulDisconnectStatesEnum.LAST_GRACEFUL_DISCONNECT_STATE.ordinal()][256];
180
181        /**
182         * Transition from init state to graceful disconnect
183         * GracefulDisconnect ::= SEQUENCE { 
184         *     ... 
185         * 
186         * Creates the GracefulDisconnect object
187         */
188        super.transitions[GracefulDisconnectStatesEnum.START_STATE.ordinal()][UniversalTag.SEQUENCE.getValue()] =
189            new GrammarTransition<GracefulDisconnectResponseContainer>( GracefulDisconnectStatesEnum.START_STATE,
190                GracefulDisconnectStatesEnum.GRACEFUL_DISCONNECT_SEQUENCE_STATE,
191                UniversalTag.SEQUENCE.getValue(),
192                new GrammarAction<GracefulDisconnectResponseContainer>( "Init Graceful Disconnect" )
193                {
194                    public void action( GracefulDisconnectResponseContainer container )
195                    {
196                        if ( container.getCurrentTLV().getLength() == 0 )
197                        {
198                            container.setGrammarEndAllowed( true );
199                        }
200                    }
201                } );
202
203        /**
204         * Transition from graceful disconnect to time offline
205         * 
206         * GracefulDisconnect ::= SEQUENCE { 
207         *     timeOffline INTEGER (0..720) DEFAULT 0, 
208         *     ... 
209         *     
210         * Set the time offline value into the GracefulDisconnect object.    
211         */
212        super.transitions[GracefulDisconnectStatesEnum.GRACEFUL_DISCONNECT_SEQUENCE_STATE.ordinal()][UniversalTag.INTEGER
213            .getValue()] =
214            new GrammarTransition<GracefulDisconnectResponseContainer>(
215                GracefulDisconnectStatesEnum.GRACEFUL_DISCONNECT_SEQUENCE_STATE,
216                GracefulDisconnectStatesEnum.TIME_OFFLINE_STATE,
217                UniversalTag.INTEGER.getValue(), storeTimeOffline );
218
219        /**
220         * Transition from graceful disconnect to delay
221         * 
222         * GracefulDisconnect ::= SEQUENCE { 
223         *     ... 
224         *     delay [0] INTEGER (0..86400) DEFAULT 0,
225         *     ... 
226         *     
227         * Set the delay value into the GracefulDisconnect object.    
228         */
229        super.transitions[GracefulDisconnectStatesEnum.GRACEFUL_DISCONNECT_SEQUENCE_STATE.ordinal()][GracefulActionConstants.GRACEFUL_ACTION_DELAY_TAG] =
230            new GrammarTransition<GracefulDisconnectResponseContainer>(
231                GracefulDisconnectStatesEnum.GRACEFUL_DISCONNECT_SEQUENCE_STATE,
232                GracefulDisconnectStatesEnum.DELAY_STATE,
233                GracefulActionConstants.GRACEFUL_ACTION_DELAY_TAG,
234                storeDelay );
235
236        /**
237         * Transition from graceful disconnect to replicated Contexts
238         * 
239         * GracefulDisconnect ::= SEQUENCE { 
240         *     ... 
241         *     replicatedContexts Referral OPTIONAL } 
242         *     
243         * Referral ::= SEQUENCE OF LDAPURL
244         *     
245         * Get some replicated contexts. Nothing to do    
246         */
247        super.transitions[GracefulDisconnectStatesEnum.GRACEFUL_DISCONNECT_SEQUENCE_STATE.ordinal()][UniversalTag.SEQUENCE
248            .getValue()] =
249            new GrammarTransition<GracefulDisconnectResponseContainer>(
250                GracefulDisconnectStatesEnum.GRACEFUL_DISCONNECT_SEQUENCE_STATE,
251                GracefulDisconnectStatesEnum.REPLICATED_CONTEXTS_STATE,
252                UniversalTag.SEQUENCE.getValue(), null );
253
254        /**
255         * Transition from time offline to delay
256         * 
257         * GracefulDisconnect ::= SEQUENCE { 
258         *     ... 
259         *     delay [0] INTEGER (0..86400) DEFAULT 0,
260         *     ... 
261         *     
262         * Set the delay value into the GracefulDisconnect object.    
263         */
264        super.transitions[GracefulDisconnectStatesEnum.TIME_OFFLINE_STATE.ordinal()][GracefulActionConstants.GRACEFUL_ACTION_DELAY_TAG] =
265            new GrammarTransition<GracefulDisconnectResponseContainer>( GracefulDisconnectStatesEnum.TIME_OFFLINE_STATE,
266                GracefulDisconnectStatesEnum.DELAY_STATE,
267                GracefulActionConstants.GRACEFUL_ACTION_DELAY_TAG,
268                storeDelay );
269
270        /**
271         * Transition from time offline to replicated Contexts
272         * 
273         * GracefulDisconnect ::= SEQUENCE { 
274         *     ... 
275         *     replicatedContexts Referral OPTIONAL } 
276         *     
277         * Referral ::= SEQUENCE OF LDAPURL
278         *     
279         * Get some replicated contexts. Nothing to do    
280         */
281        super.transitions[GracefulDisconnectStatesEnum.TIME_OFFLINE_STATE.ordinal()][UniversalTag.SEQUENCE.getValue()] =
282            new GrammarTransition<GracefulDisconnectResponseContainer>( GracefulDisconnectStatesEnum.TIME_OFFLINE_STATE,
283                GracefulDisconnectStatesEnum.REPLICATED_CONTEXTS_STATE,
284                UniversalTag.SEQUENCE.getValue(), null );
285
286        /**
287         * Transition from delay to replicated contexts
288         * 
289         * GracefulDisconnect ::= SEQUENCE { 
290         *     ... 
291         *     replicatedContexts Referral OPTIONAL } 
292         *     
293         * Referral ::= SEQUENCE OF LDAPURL
294         *     
295         * Get some replicated contexts. Nothing to do    
296         */
297        super.transitions[GracefulDisconnectStatesEnum.DELAY_STATE.ordinal()][UniversalTag.SEQUENCE.getValue()] =
298            new GrammarTransition<GracefulDisconnectResponseContainer>( GracefulDisconnectStatesEnum.DELAY_STATE,
299                GracefulDisconnectStatesEnum.REPLICATED_CONTEXTS_STATE,
300                UniversalTag.SEQUENCE.getValue(), null );
301
302        /**
303         * Transition from replicated contexts to referral
304         * 
305         * GracefulDisconnect ::= SEQUENCE { 
306         *     ... 
307         *     replicatedContexts Referral OPTIONAL } 
308         *     
309         * Referral ::= SEQUENCE OF LDAPURL
310         *     
311         * Stores the referral
312         */
313        super.transitions[GracefulDisconnectStatesEnum.REPLICATED_CONTEXTS_STATE.ordinal()][UniversalTag.OCTET_STRING
314            .getValue()] =
315            new GrammarTransition<GracefulDisconnectResponseContainer>( GracefulDisconnectStatesEnum.REPLICATED_CONTEXTS_STATE,
316                GracefulDisconnectStatesEnum.REFERRAL_STATE,
317                UniversalTag.OCTET_STRING.getValue(),
318                storeReferral );
319
320        /**
321         * Transition from referral to referral
322         * 
323         * GracefulDisconnect ::= SEQUENCE { 
324         *     ... 
325         *     replicatedContexts Referral OPTIONAL } 
326         *     
327         * Referral ::= SEQUENCE OF LDAPURL
328         *     
329         * Stores the referral
330         */
331        super.transitions[GracefulDisconnectStatesEnum.REFERRAL_STATE.ordinal()][UniversalTag.OCTET_STRING.getValue()] =
332            new GrammarTransition<GracefulDisconnectResponseContainer>( GracefulDisconnectStatesEnum.REFERRAL_STATE,
333                GracefulDisconnectStatesEnum.REFERRAL_STATE,
334                UniversalTag.OCTET_STRING.getValue(),
335                storeReferral );
336
337    }
338
339
340    /**
341     * This class is a singleton.
342     * 
343     * @return An instance on this grammar
344     */
345    public static GracefulDisconnectResponseGrammar getInstance()
346    {
347        return instance;
348    }
349}