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.storedProcedure;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025import java.util.LinkedList;
026import java.util.List;
027
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.ExtendedRequestDecorator;
034import org.apache.directory.api.ldap.codec.api.LdapApiService;
035import org.apache.directory.api.ldap.extras.extended.storedProcedure.StoredProcedureParameter;
036import org.apache.directory.api.ldap.extras.extended.storedProcedure.StoredProcedureRequest;
037import org.apache.directory.api.ldap.extras.extended.storedProcedure.StoredProcedureRequestImpl;
038import org.apache.directory.api.util.Strings;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042
043/**
044 * A Decorator for stored procedure extended operation requests.
045 *
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 */
048public class StoredProcedureRequestDecorator extends ExtendedRequestDecorator<StoredProcedureRequest>
049    implements StoredProcedureRequest
050{
051    private static final Logger LOG = LoggerFactory.getLogger( StoredProcedureRequestDecorator.class );
052
053    private StoredProcedureParameter currentParameter;
054
055    /** The stored procedure length */
056    private int storedProcedureLength;
057
058    /** The parameters length */
059    private int parametersLength;
060
061    /** The list of all parameter lengths */
062    private List<Integer> parameterLength;
063
064
065    /**
066     * Create a new StoredProcedureRequestDecorator instance 
067     * @param codec The LDAP API service to use
068     */
069    public StoredProcedureRequestDecorator( LdapApiService codec )
070    {
071        super( codec, new StoredProcedureRequestImpl() );
072    }
073
074
075    /**
076     * Create a new StoredProcedureRequestDecorator instance 
077     * @param codec The LDAP API service to use
078     * @param decoratedRequest The decorated request
079     */
080    public StoredProcedureRequestDecorator( LdapApiService codec, StoredProcedureRequest decoratedRequest )
081    {
082        super( codec, decoratedRequest );
083        if ( decoratedRequest == null )
084        {
085            throw new NullPointerException( "decorated stored procedulre request is null" );
086        }
087    }
088
089
090    /**
091     * @return The current parameter
092     */
093    public StoredProcedureParameter getCurrentParameter()
094    {
095        return currentParameter;
096    }
097
098
099    /**
100     * Sets the current parameter
101     * 
102     * @param currentParameter The current parameter
103     */
104    public void setCurrentParameter( StoredProcedureParameter currentParameter )
105    {
106        this.currentParameter = currentParameter;
107    }
108
109
110    /**
111     * Compute the StoredProcedure length 
112     * <pre>
113     * 0x30 L1 
114     *   | 
115     *   +--&gt; 0x04 L2 language
116     *   +--&gt; 0x04 L3 procedure
117     *  [+--&gt; 0x30 L4 (parameters)
118     *          |
119     *          +--&gt; 0x30 L5-1 (parameter)
120     *          |      |
121     *          |      +--&gt; 0x04 L6-1 type
122     *          |      +--&gt; 0x04 L7-1 value
123     *          |      
124     *          +--&gt; 0x30 L5-2 (parameter)
125     *          |      |
126     *          |      +--&gt; 0x04 L6-2 type
127     *          |      +--&gt; 0x04 L7-2 value
128     *          |
129     *          +--&gt; ...
130     *          |      
131     *          +--&gt; 0x30 L5-m (parameter)
132     *                 |
133     *                 +--&gt; 0x04 L6-m type
134     *                 +--&gt; 0x04 L7-m value
135     * </pre>
136     */
137    /* no qualifier */ int computeLengthInternal()
138    {
139        // The language
140        byte[] languageBytes = Strings.getBytesUtf8( getDecorated().getLanguage() );
141
142        int languageLength = 1 + TLV.getNbBytes( languageBytes.length )
143            + languageBytes.length;
144
145        byte[] procedure = getDecorated().getProcedure();
146
147        // The procedure
148        int procedureLength = 1 + TLV.getNbBytes( procedure.length )
149            + procedure.length;
150
151        // Compute parameters length value
152        if ( getDecorated().getParameters() != null )
153        {
154            parameterLength = new LinkedList<>();
155
156            for ( StoredProcedureParameter spParam : getDecorated().getParameters() )
157            {
158                int localParameterLength;
159                int localParamTypeLength;
160                int localParamValueLength;
161
162                localParamTypeLength = 1 + TLV.getNbBytes( spParam.getType().length ) + spParam.getType().length;
163                localParamValueLength = 1 + TLV.getNbBytes( spParam.getValue().length ) + spParam.getValue().length;
164
165                localParameterLength = localParamTypeLength + localParamValueLength;
166
167                parametersLength += 1 + TLV.getNbBytes( localParameterLength ) + localParameterLength;
168
169                parameterLength.add( localParameterLength );
170            }
171        }
172
173        int localParametersLength = 1 + TLV.getNbBytes( parametersLength ) + parametersLength;
174        storedProcedureLength = languageLength + procedureLength + localParametersLength;
175
176        return 1 + TLV.getNbBytes( storedProcedureLength ) + storedProcedureLength;
177    }
178
179
180    /**
181     * Encodes the StoredProcedure extended operation.
182     * 
183     * @return A ByteBuffer that contains the encoded PDU
184     * @throws org.apache.directory.api.asn1.EncoderException If anything goes wrong.
185     */
186    /* no qualifier */ ByteBuffer encodeInternal() throws EncoderException
187    {
188        // Allocate the bytes buffer.
189        ByteBuffer bb = ByteBuffer.allocate( computeLengthInternal() );
190
191        try
192        {
193            // The StoredProcedure Tag
194            bb.put( UniversalTag.SEQUENCE.getValue() );
195            bb.put( TLV.getBytes( storedProcedureLength ) );
196
197            // The language
198            BerValue.encode( bb, getDecorated().getLanguage() );
199
200            // The procedure
201            BerValue.encode( bb, getDecorated().getProcedure() );
202
203            // The parameters sequence
204            bb.put( UniversalTag.SEQUENCE.getValue() );
205            bb.put( TLV.getBytes( parametersLength ) );
206
207            // The parameters list
208            if ( ( getDecorated().getParameters() != null ) && ( !getDecorated().getParameters().isEmpty() ) )
209            {
210                int parameterNumber = 0;
211
212                for ( StoredProcedureParameter spParam : getDecorated().getParameters() )
213                {
214                    // The parameter sequence
215                    bb.put( UniversalTag.SEQUENCE.getValue() );
216                    int localParameterLength = parameterLength.get( parameterNumber );
217                    bb.put( TLV.getBytes( localParameterLength ) );
218
219                    // The parameter type
220                    BerValue.encode( bb, spParam.getType() );
221
222                    // The parameter value
223                    BerValue.encode( bb, spParam.getValue() );
224
225                    // Go to the next parameter
226                    parameterNumber++;
227                }
228            }
229        }
230        catch ( BufferOverflowException boe )
231        {
232            throw new EncoderException( I18n.err( I18n.ERR_04005 ), boe );
233        }
234
235        return bb;
236    }
237
238
239    /**
240     * Returns the StoredProcedure string
241     * 
242     * @return The StoredProcedure string
243     */
244    @Override
245    public String toString()
246    {
247        StringBuilder sb = new StringBuilder();
248
249        sb.append( "    StoredProcedure\n" );
250        sb.append( "        Language : '" ).append( getDecorated().getLanguage() ).append( "'\n" );
251        sb.append( "        Procedure\n" ).append( getDecorated().getProcedureSpecification() ).append( "'\n" );
252
253        if ( ( getDecorated().getParameters() == null ) || ( !getDecorated().getParameters().isEmpty() ) )
254        {
255            sb.append( "        No parameters\n" );
256        }
257        else
258        {
259            sb.append( "        Parameters\n" );
260
261            int i = 1;
262
263            for ( StoredProcedureParameter spParam : getDecorated().getParameters() )
264            {
265                sb.append( "            type[" ).append( i ).append( "] : '" ).
266                    append( Strings.utf8ToString( spParam.getType() ) ).append( "'\n" );
267                sb.append( "            value[" ).append( i ).append( "] : '" ).
268                    append( Strings.dumpBytes( spParam.getValue() ) ).append( "'\n" );
269            }
270        }
271
272        return sb.toString();
273    }
274
275
276    @Override
277    public void setProcedure( byte[] procedure )
278    {
279        getDecorated().setProcedure( procedure );
280    }
281
282
283    /**
284     * {@inheritDoc}
285     */
286    @Override
287    public void setRequestValue( byte[] payload )
288    {
289        StoredProcedureDecoder decoder = new StoredProcedureDecoder();
290        StoredProcedureContainer container = new StoredProcedureContainer();
291
292        container.setStoredProcedure( this );
293
294        try
295        {
296            decoder.decode( ByteBuffer.wrap( payload ), container );
297        }
298        catch ( Exception e )
299        {
300            LOG.error( I18n.err( I18n.ERR_04165 ), e );
301            throw new RuntimeException( e );
302        }
303    }
304
305
306    /**
307     * {@inheritDoc}
308     */
309    @Override
310    public byte[] getRequestValue()
311    {
312        if ( requestValue == null )
313        {
314            try
315            {
316                requestValue = encodeInternal().array();
317            }
318            catch ( EncoderException e )
319            {
320                LOG.error( I18n.err( I18n.ERR_04174 ), e );
321                throw new RuntimeException( e );
322            }
323        }
324
325        return requestValue;
326    }
327
328
329    /**
330     * {@inheritDoc}
331     */
332    @Override
333    public String getLanguage()
334    {
335        return getDecorated().getLanguage();
336    }
337
338
339    /**
340     * {@inheritDoc}
341     */
342    @Override
343    public void setLanguage( String language )
344    {
345        getDecorated().setLanguage( language );
346    }
347
348
349    /**
350     * {@inheritDoc}
351     */
352    @Override
353    public String getProcedureSpecification()
354    {
355        return getDecorated().getProcedureSpecification();
356    }
357
358
359    /**
360     * {@inheritDoc}
361     */
362    @Override
363    public int size()
364    {
365        return getDecorated().size();
366    }
367
368
369    /**
370     * {@inheritDoc}
371     */
372    @Override
373    public Object getParameterType( int index )
374    {
375        return getDecorated().getParameterType( index );
376    }
377
378
379    /**
380     * {@inheritDoc}
381     */
382
383    @Override
384    public Class<?> getJavaParameterType( int index )
385    {
386        return getDecorated().getJavaParameterType( index );
387    }
388
389
390    /**
391     * {@inheritDoc}
392     */
393
394    @Override
395    public Object getParameterValue( int index )
396    {
397        return getDecorated().getParameterValue( index );
398    }
399
400
401    /**
402     * {@inheritDoc}
403     */
404    @Override
405    public Object getJavaParameterValue( int index )
406    {
407        return getDecorated().getJavaParameterValue( index );
408    }
409
410
411    /**
412     * {@inheritDoc}
413     */
414    @Override
415    public void addParameter( Object type, Object value )
416    {
417        getDecorated().addParameter( type, value );
418    }
419
420
421    /**
422     * {@inheritDoc}
423     */
424    @Override
425    public byte[] getProcedure()
426    {
427        return getDecorated().getProcedure();
428    }
429
430
431    /**
432     * {@inheritDoc}
433     */
434    @Override
435    public List<StoredProcedureParameter> getParameters()
436    {
437        return getDecorated().getParameters();
438    }
439
440
441    /**
442     * {@inheritDoc}
443     */
444    @Override
445    public void addParameter( StoredProcedureParameter parameter )
446    {
447        getDecorated().addParameter( parameter );
448    }
449}