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.server.core.partition.impl.btree.mavibot;
021
022
023import java.io.IOException;
024import java.nio.ByteBuffer;
025import java.util.Comparator;
026
027import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
028import org.apache.directory.api.ldap.model.name.Rdn;
029import org.apache.directory.api.ldap.model.schema.SchemaManager;
030import org.apache.directory.api.util.Serialize;
031import org.apache.directory.mavibot.btree.serializer.AbstractElementSerializer;
032import org.apache.directory.mavibot.btree.serializer.BufferHandler;
033import org.apache.directory.mavibot.btree.util.Strings;
034import org.apache.directory.server.i18n.I18n;
035import org.apache.directory.server.xdbm.ParentIdAndRdn;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039
040/**
041 * Serialize and deserialize a ParentidAndRdn.
042 * <br><br>
043 * <b>This class must *not* be used outside of the server.</b>
044 *  
045 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
046 */
047public class MavibotParentIdAndRdnSerializer extends AbstractElementSerializer<ParentIdAndRdn>
048{
049    /** The serialVersionUID */
050    private static final long serialVersionUID = 1L;
051
052    /** the logger for this class */
053    private static final Logger LOG = LoggerFactory.getLogger( MavibotParentIdAndRdnSerializer.class );
054
055    /**
056     * Speedup for logs
057     */
058    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
059
060    /** The schemaManager reference */
061    private static SchemaManager schemaManager;
062
063    private static Comparator<ParentIdAndRdn> comparator = new Comparator<ParentIdAndRdn>()
064    {
065
066        @Override
067        public int compare( ParentIdAndRdn rdn1, ParentIdAndRdn rdn2 )
068        {
069            return rdn1.compareTo( rdn2 );
070        }
071
072    };
073
074
075    /**
076     * Creates a new instance of ParentIdAndRdnSerializer.
077     * The schemaManager MUST be set explicitly using the static {@link #setSchemaManager(SchemaManager)}
078     */
079    public MavibotParentIdAndRdnSerializer()
080    {
081        super( comparator );
082    }
083
084
085    /**
086     * This is the place where we serialize ParentIdAndRdn. The format is the following :<br>
087     * <ul>
088     * <li>length</li>
089     * <li>the RDN</li>
090     * <li>the parent ID</li>
091     * <li>Number of children</li>
092     * <li>Number of descendant</li>
093     * <li></li>
094     * <li></li>
095     * <li></li>
096     * </ul>
097     */
098    public byte[] serialize( ParentIdAndRdn parentIdAndRdn )
099    {
100        try
101        {
102            int bufferSize = 1024;
103
104            while ( bufferSize < Integer.MAX_VALUE )
105            {
106                // allocate a big enough buffer for most of the cases
107                byte[] buffer = new byte[bufferSize];
108
109                try
110                {
111                    // The current position.
112                    int pos = 0;
113
114                    // First, the Dn
115                    Rdn[] rdns;
116
117                    rdns = parentIdAndRdn.getRdns();
118
119                    // Write the Rdn of the Dn
120                    // The number of RDN (we may have more than one)
121                    if ( ( rdns == null ) || ( rdns.length == 0 ) )
122                    {
123                        pos = Serialize.serialize( 0, buffer, pos );
124                    }
125                    else
126                    {
127                        pos = Serialize.serialize( rdns.length, buffer, pos );
128
129                        for ( Rdn rdn : rdns )
130                        {
131                            pos = rdn.serialize( buffer, pos );
132                        }
133                    }
134
135                    // Then the parentId.
136                    String parentId = parentIdAndRdn.getParentId();
137                    byte[] parentIdBytes = Strings.getBytesUtf8( parentId );
138                    pos = Serialize.serialize( parentIdBytes, buffer, pos );
139
140                    // The number of children
141                    pos = Serialize.serialize( parentIdAndRdn.getNbChildren(), buffer, pos );
142
143                    // The number of descendants
144                    pos = Serialize.serialize( parentIdAndRdn.getNbDescendants(), buffer, pos );
145
146                    if ( IS_DEBUG )
147                    {
148                        LOG.debug( ">------------------------------------------------" );
149                        LOG.debug( "Serialize {}", parentIdAndRdn );
150                    }
151
152
153                    // Copy the serialized data
154                    byte[] result = new byte[pos];
155                    System.arraycopy( buffer, 0, result, 0, pos );
156
157                    return result;
158                }
159                catch ( ArrayIndexOutOfBoundsException aioobe )
160                {
161                    // Bad luck, try with a bigger buffer
162                    bufferSize += bufferSize;
163                }
164            }
165
166            // No reason we should reach this point
167            throw new RuntimeException();
168        }
169        catch ( Exception e )
170        {
171            throw new RuntimeException( e );
172        }
173    }
174
175
176    /**
177     *  Deserialize a ParentIdAndRdn.
178     *  
179     *  @param bufferHandler The buffer containing the serialized ParentIdAndRdn
180     *  @return An instance of a ParentIdAndRdn object 
181     *  @throws IOException if we can't deserialize the ParentIdAndRdn
182     */
183    public ParentIdAndRdn deserialize( BufferHandler bufferHandler ) throws IOException
184    {
185        return deserialize( ByteBuffer.wrap( bufferHandler.getBuffer() ) );
186    }
187
188
189    @Override
190    public ParentIdAndRdn deserialize( ByteBuffer buffer ) throws IOException
191    {
192        return fromBytes( buffer.array(), buffer.position() );
193    }
194
195
196    @Override
197    public int compare( ParentIdAndRdn type1, ParentIdAndRdn type2 )
198    {
199        return type1.compareTo( type2 );
200    }
201
202
203    @Override
204    public Comparator<ParentIdAndRdn> getComparator()
205    {
206        return comparator;
207    }
208
209
210    public static void setSchemaManager( SchemaManager schemaManager )
211    {
212        MavibotParentIdAndRdnSerializer.schemaManager = schemaManager;
213    }
214
215
216    /**
217     * {@inheritDoc}
218     */
219    @Override
220    public ParentIdAndRdn fromBytes( byte[] buffer ) throws IOException
221    {
222        return fromBytes( buffer, 0 );
223    }
224
225
226    /**
227     * {@inheritDoc}
228     */
229    @Override
230    public ParentIdAndRdn fromBytes( byte[] buffer, int pos ) throws IOException
231    {
232        try
233        {
234            ParentIdAndRdn parentIdAndRdn = new ParentIdAndRdn();
235
236            // Read the number of rdns, if any
237            int nbRdns = Serialize.deserializeInt( buffer, pos );
238            pos += 4;
239
240            if ( nbRdns == 0 )
241            {
242                parentIdAndRdn.setRdns( new Rdn[0] );
243            }
244            else
245            {
246                Rdn[] rdns = new Rdn[nbRdns];
247
248                for ( int i = 0; i < nbRdns; i++ )
249                {
250                    Rdn rdn = new Rdn( schemaManager );
251                    pos = rdn.deserialize( buffer, pos );
252                    rdns[i] = rdn;
253                }
254
255                parentIdAndRdn.setRdns( rdns );
256            }
257
258            // Read the parent ID
259            byte[] uuidBytes = Serialize.deserializeBytes( buffer, pos );
260            pos += 4 + uuidBytes.length;
261            String uuid = Strings.utf8ToString( uuidBytes );
262
263            parentIdAndRdn.setParentId( uuid );
264
265            // Read the number of children and descendants
266            int nbChildren = Serialize.deserializeInt( buffer, pos );
267            pos += 4;
268
269            int nbDescendants = Serialize.deserializeInt( buffer, pos );
270            pos += 4;
271
272            parentIdAndRdn.setNbChildren( nbChildren );
273            parentIdAndRdn.setNbDescendants( nbDescendants );
274
275            return parentIdAndRdn;
276        }
277        catch ( LdapInvalidAttributeValueException cnfe )
278        {
279            LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
280            throw new IOException( cnfe.getLocalizedMessage() );
281        }
282    }
283
284
285    /**
286     * {@inheritDoc}
287     */
288    @Override
289    public Class<?> getType()
290    {
291        return ParentIdAndRdn.class;
292    }
293}