View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.core.partition.impl.btree.mavibot;
21  
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.ByteArrayOutputStream;
25  import java.io.IOException;
26  import java.io.ObjectInputStream;
27  import java.io.ObjectOutput;
28  import java.io.ObjectOutputStream;
29  import java.nio.ByteBuffer;
30  import java.util.Comparator;
31  
32  import org.apache.directory.api.ldap.model.entry.Attribute;
33  import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
34  import org.apache.directory.api.ldap.model.entry.DefaultEntry;
35  import org.apache.directory.api.ldap.model.entry.Entry;
36  import org.apache.directory.api.ldap.model.exception.LdapException;
37  import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
38  import org.apache.directory.api.ldap.model.name.Dn;
39  import org.apache.directory.api.ldap.model.name.Rdn;
40  import org.apache.directory.api.ldap.model.schema.AttributeType;
41  import org.apache.directory.api.ldap.model.schema.SchemaManager;
42  import org.apache.directory.mavibot.btree.serializer.AbstractElementSerializer;
43  import org.apache.directory.mavibot.btree.serializer.BufferHandler;
44  import org.apache.directory.server.i18n.I18n;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  
49  /**
50   * Serialize and deserialize a ServerEntry. There is a big difference with the standard
51   * Entry serialization : we don't serialize the entry's Dn, we just serialize it's Rdn.
52   * <br><br>
53   * <b>This class must *not* be used outside of the server.</b>
54   *  
55   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
56   */
57  public class MavibotEntrySerializer extends AbstractElementSerializer<Entry>
58  {
59      /** The serialVersionUID */
60      private static final long serialVersionUID = 1L;
61  
62      /** the logger for this class */
63      private static final Logger LOG = LoggerFactory.getLogger( MavibotEntrySerializer.class );
64  
65      /**
66       * Speedup for logs
67       */
68      private static final boolean IS_DEBUG = LOG.isDebugEnabled();
69  
70      /** The schemaManager reference */
71      private static SchemaManager schemaManager;
72  
73      private static class EntryComparator implements Comparator<Entry>
74      {
75  
76          @Override
77          public int compare( Entry entry1, Entry entry2 )
78          {
79              return entry1.getDn().getName().compareTo( entry1.getDn().getName() );
80          }
81  
82      }
83  
84      private static Comparator<Entry> comparator = new EntryComparator();
85  
86  
87      /**
88       * Creates a new instance of ServerEntrySerializer.
89       * The schemaManager MUST be set explicitly using the static {@link #setSchemaManager(SchemaManager)}
90       */
91      public MavibotEntrySerializer()
92      {
93          super( comparator );
94      }
95  
96  
97      @Override
98      public Comparator<Entry> getComparator()
99      {
100         return comparator;
101     }
102 
103 
104     /**
105      * <p>
106      * 
107      * This is the place where we serialize entries, and all theirs
108      * elements. the reason why we don't call the underlying methods
109      * (<code>ServerAttribute.write(), Value.write()</code>) is that we need
110      * access to the registries to read back the values.
111      * <p>
112      * The structure used to store the entry is the following :
113      * <ul>
114      *   <li><b>[a byte]</b> : if the Dn is empty 0 will be written else 1</li>
115      *   <li><b>[Rdn]</b> : The entry's Rdn.</li>
116      *   <li><b>[numberAttr]</b> : the bumber of attributes. Can be 0</li>
117      *   <li>For each Attribute :
118      *     <ul>
119      *       <li><b>[attribute's oid]</b> : The attribute's OID to get back
120      *       the attributeType on deserialization</li>
121      *       <li><b>[Attribute]</b> The attribute</li>
122      *     </ul>
123      *   </li>
124      * </ul>
125      */
126     public byte[] serialize( Entry entry )
127     {
128         try
129         {
130             ByteArrayOutputStream baos = new ByteArrayOutputStream();
131 
132             ObjectOutput out = new ObjectOutputStream( baos );
133 
134             // First, the Dn
135             Dn dn = entry.getDn();
136 
137             // Write the Rdn of the Dn
138             if ( dn.isEmpty() )
139             {
140                 out.writeByte( 0 );
141             }
142             else
143             {
144                 out.writeByte( 1 );
145                 Rdn rdn = dn.getRdn();
146                 rdn.writeExternal( out );
147             }
148 
149             // Then the attributes.
150             out.writeInt( entry.getAttributes().size() );
151 
152             // Iterate through the keys. We store the Attribute
153             // here, to be able to restore it in the readExternal :
154             // we need access to the registries, which are not available
155             // in the ServerAttribute class.
156             for ( Attribute attribute : entry.getAttributes() )
157             {
158                 AttributeType attributeType = attribute.getAttributeType();
159 
160                 // Write the oid to be able to restore the AttributeType when deserializing
161                 // the attribute
162                 String oid = attributeType.getOid();
163 
164                 out.writeUTF( oid );
165 
166                 // Write the attribute
167                 attribute.writeExternal( out );
168             }
169 
170             out.flush();
171 
172             // Note : we don't store the ObjectClassAttribute. It has already
173             // been stored as an attribute.
174 
175             if ( IS_DEBUG )
176             {
177                 LOG.debug( ">------------------------------------------------" );
178                 LOG.debug( "Serialize {}", entry );
179             }
180 
181             return baos.toByteArray();
182         }
183         catch ( Exception e )
184         {
185             throw new RuntimeException( e );
186         }
187     }
188 
189 
190     /**
191      *  Deserialize a Entry.
192      *  
193      *  @param buffer The buffer containing the serialized entry
194      *  @return An instance of a Entry object 
195      *  @throws IOException if we can't deserialize the Entry
196      */
197     public Entry deserialize( ByteBuffer buffer ) throws IOException
198     {
199         // read the length
200         int len = buffer.limit();
201 
202         ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( buffer.array(), buffer.position(), len ) );
203 
204         try
205         {
206             Entry entry = new DefaultEntry( schemaManager );
207 
208             // Read the Dn, if any
209             byte hasDn = in.readByte();
210 
211             if ( hasDn == 1 )
212             {
213                 Rdn rdn = new Rdn( schemaManager );
214                 rdn.readExternal( in );
215 
216                 try
217                 {
218                     entry.setDn( new Dn( schemaManager, rdn ) );
219                 }
220                 catch ( LdapInvalidDnException lide )
221                 {
222                     IOException ioe = new IOException( lide.getMessage() );
223                     ioe.initCause( lide );
224                     throw ioe;
225                 }
226             }
227             else
228             {
229                 entry.setDn( Dn.EMPTY_DN );
230             }
231 
232             // Read the number of attributes
233             int nbAttributes = in.readInt();
234 
235             // Read the attributes
236             for ( int i = 0; i < nbAttributes; i++ )
237             {
238                 // Read the attribute's OID
239                 String oid = in.readUTF();
240 
241                 try
242                 {
243                     AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( oid );
244 
245                     // Create the attribute we will read
246                     Attribute attribute = new DefaultAttribute( attributeType );
247 
248                     // Read the attribute
249                     attribute.readExternal( in );
250 
251                     entry.add( attribute );
252                 }
253                 catch ( LdapException ne )
254                 {
255                     // We weren't able to find the OID. The attribute will not be added
256                     throw new ClassNotFoundException( ne.getMessage(), ne );
257                 }
258             }
259 
260             buffer.position( buffer.position() + len ); // previous position + length
261 
262             return entry;
263         }
264         catch ( ClassNotFoundException cnfe )
265         {
266             LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
267             throw new IOException( cnfe.getLocalizedMessage() );
268         }
269     }
270 
271 
272     @Override
273     public Entry deserialize( BufferHandler bufferHandler ) throws IOException
274     {
275         return deserialize( ByteBuffer.wrap( bufferHandler.getBuffer() ) );
276     }
277 
278 
279     public static void setSchemaManager( SchemaManager schemaManager )
280     {
281         MavibotEntrySerializer.schemaManager = schemaManager;
282     }
283 
284 
285     /**
286      * {@inheritDoc}
287      */
288     @Override
289     public Entry fromBytes( byte[] buffer ) throws IOException
290     {
291         return fromBytes( buffer, 0 );
292     }
293 
294 
295     /**
296      * {@inheritDoc}
297      */
298     @Override
299     public Entry fromBytes( byte[] buffer, int pos ) throws IOException
300     {
301         // read the length
302         int len = buffer.length - pos;
303 
304         ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( buffer, pos, len ) );
305 
306         try
307         {
308             Entry entry = new DefaultEntry( schemaManager );
309 
310             // Read the Dn, if any
311             byte hasDn = in.readByte();
312 
313             if ( hasDn == 1 )
314             {
315                 Rdn rdn = new Rdn( schemaManager );
316                 rdn.readExternal( in );
317 
318                 try
319                 {
320                     entry.setDn( new Dn( schemaManager, rdn ) );
321                 }
322                 catch ( LdapInvalidDnException lide )
323                 {
324                     IOException ioe = new IOException( lide.getMessage() );
325                     ioe.initCause( lide );
326                     throw ioe;
327                 }
328             }
329             else
330             {
331                 entry.setDn( Dn.EMPTY_DN );
332             }
333 
334             // Read the number of attributes
335             int nbAttributes = in.readInt();
336 
337             // Read the attributes
338             for ( int i = 0; i < nbAttributes; i++ )
339             {
340                 // Read the attribute's OID
341                 String oid = in.readUTF();
342 
343                 try
344                 {
345                     AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( oid );
346 
347                     // Create the attribute we will read
348                     Attribute attribute = new DefaultAttribute( attributeType );
349 
350                     // Read the attribute
351                     attribute.readExternal( in );
352 
353                     entry.add( attribute );
354                 }
355                 catch ( LdapException ne )
356                 {
357                     // We weren't able to find the OID. The attribute will not be added
358                     throw new ClassNotFoundException( ne.getMessage(), ne );
359                 }
360             }
361 
362             return entry;
363         }
364         catch ( ClassNotFoundException cnfe )
365         {
366             LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
367             throw new IOException( cnfe.getLocalizedMessage() );
368         }
369     }
370 
371 
372     /**
373      * {@inheritDoc}
374      */
375     @Override
376     public Class<?> getType()
377     {
378         return Entry.class;
379     }
380 }