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