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.shared;
021
022
023import java.io.ByteArrayInputStream;
024import java.io.ByteArrayOutputStream;
025import java.io.IOException;
026import java.io.ObjectInputStream;
027import java.io.ObjectOutput;
028import java.io.ObjectOutputStream;
029
030import jdbm.helper.Serializer;
031
032import org.apache.directory.api.ldap.model.entry.Attribute;
033import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
034import org.apache.directory.api.ldap.model.entry.DefaultEntry;
035import org.apache.directory.api.ldap.model.entry.Entry;
036import org.apache.directory.api.ldap.model.exception.LdapException;
037import org.apache.directory.api.ldap.model.name.Dn;
038import org.apache.directory.api.ldap.model.schema.AttributeType;
039import org.apache.directory.api.ldap.model.schema.SchemaManager;
040import org.apache.directory.server.i18n.I18n;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044
045/**
046 * Serialize and deserialize a ServerEntry. 
047 * 
048 * WARNING: This serializer stores the complete DN as well (unlike other entry 
049 *          serializers which store only RDN).
050 * 
051 * <b>This class must *not* be used anywhere else other than for storing sorted entries in server.</b>
052 *  
053 *  Note: this was initially used by Mavibot tree, but changed to use in JDBM later.
054 *        This will again be ported to Mavibot as soon as it gets ready.
055 *        
056 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
057 */
058public class SortedEntrySerializer implements Serializer
059{
060    /** The serialVersionUID */
061    private static final long serialVersionUID = 1L;
062
063    /** the logger for this class */
064    private static final Logger LOG = LoggerFactory.getLogger( SortedEntrySerializer.class );
065
066    /**
067     * Speedup for logs
068     */
069    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
070
071    /** The schemaManager reference */
072    private static SchemaManager schemaManager;
073
074
075    /**
076     * Creates a new instance of ServerEntrySerializer.
077     * The schemaManager MUST be set explicitly set using the static {@link #setSchemaManager(SchemaManager)}
078     */
079    public SortedEntrySerializer()
080    {
081    }
082
083
084    @Override
085    public byte[] serialize( Object obj ) throws IOException
086    {
087        return serialize( ( Entry ) obj );
088    }
089
090
091
092    @Override
093    public Object deserialize( byte[] serialized ) throws IOException
094    {
095        ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( serialized ) );
096
097        try
098        {
099            Entry entry = new DefaultEntry( schemaManager );
100
101            Dn dn = new Dn( schemaManager );
102            dn.readExternal( in );
103            entry.setDn( dn );
104
105            // Read the number of attributes
106            int nbAttributes = in.readInt();
107
108            // Read the attributes
109            for ( int i = 0; i < nbAttributes; i++ )
110            {
111                // Read the attribute's OID
112                String oid = in.readUTF();
113
114                try
115                {
116                    AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( oid );
117
118                    // Create the attribute we will read
119                    Attribute attribute = new DefaultAttribute( attributeType );
120
121                    // Read the attribute
122                    attribute.readExternal( in );
123
124                    entry.add( attribute );
125                }
126                catch ( LdapException ne )
127                {
128                    // We weren't able to find the OID. The attribute will not be added
129                    throw new ClassNotFoundException( ne.getMessage(), ne );
130                }
131            }
132
133            return entry;
134        }
135        catch ( ClassNotFoundException cnfe )
136        {
137            LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) );
138            throw new IOException( cnfe.getLocalizedMessage() );
139        }
140    }
141
142
143
144    /**
145     * <p>
146     * 
147     * This is the place where we serialize entries, and all theirs
148     * elements. the reason why we don't call the underlying methods
149     * (<code>ServerAttribute.write(), Value.write()</code>) is that we need
150     * access to the registries to read back the values.
151     * <p>
152     * The structure used to store the entry is the following :
153     * <ul>
154     *   <li><b>[Dn]</b> : The entry's Rdn.</li>
155     *   <li><b>[numberAttr]</b> : the bumber of attributes. Can be 0</li>
156     *   <li>For each Attribute :
157     *     <ul>
158     *       <li><b>[attribute's oid]</b> : The attribute's OID to get back
159     *       the attributeType on deserialization</li>
160     *       <li><b>[Attribute]</b> The attribute</li>
161     *     </ul>
162     *   </li>
163     * </ul>
164     * 
165     * @param entry The entry to serialize
166     * @return The byte[] containing the serialized entry
167     */
168    public byte[] serialize( Entry entry )
169    {
170        try
171        {
172            ByteArrayOutputStream baos = new ByteArrayOutputStream();
173
174            ObjectOutput out = new ObjectOutputStream( baos );
175
176            // First, the Dn
177            Dn dn = entry.getDn();
178
179            // Write the Dn
180            dn.writeExternal( out );
181
182            // Then the attributes.
183            out.writeInt( entry.getAttributes().size() );
184
185            // Iterate through the keys. We store the Attribute
186            // here, to be able to restore it in the readExternal :
187            // we need access to the registries, which are not available
188            // in the ServerAttribute class.
189            for ( Attribute attribute : entry.getAttributes() )
190            {
191                AttributeType attributeType = attribute.getAttributeType();
192
193                // Write the oid to be able to restore the AttributeType when deserializing
194                // the attribute
195                String oid = attributeType.getOid();
196
197                out.writeUTF( oid );
198
199                // Write the attribute
200                attribute.writeExternal( out );
201            }
202
203            out.flush();
204
205            // Note : we don't store the ObjectClassAttribute. It has already
206            // been stored as an attribute.
207
208            if ( IS_DEBUG )
209            {
210                LOG.debug( ">------------------------------------------------" );
211                LOG.debug( "Serialize {}", entry );
212            }
213
214            return baos.toByteArray();
215        }
216        catch ( Exception e )
217        {
218            throw new RuntimeException( e );
219        }
220    }
221
222
223    public static void setSchemaManager( SchemaManager schemaManager )
224    {
225        SortedEntrySerializer.schemaManager = schemaManager;
226    }
227
228}