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.jdbm; 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.exception.LdapInvalidDnException; 038import org.apache.directory.api.ldap.model.name.Dn; 039import org.apache.directory.api.ldap.model.name.Rdn; 040import org.apache.directory.api.ldap.model.schema.AttributeType; 041import org.apache.directory.api.ldap.model.schema.SchemaManager; 042import org.apache.directory.server.i18n.I18n; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046 047/** 048 * Serialize and deserialize a ServerEntry. There is a big difference with the standard 049 * Entry serialization : we don't serialize the entry's Dn, we just serialize it's Rdn. 050 * <br><br> 051 * <b>This class must *not* be used outside of the server.</b> 052 * 053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 054 */ 055public class EntrySerializer implements Serializer 056{ 057 /** The serialVersionUID */ 058 private static final long serialVersionUID = 1L; 059 060 /** the logger for this class */ 061 private static final Logger LOG = LoggerFactory.getLogger( EntrySerializer.class ); 062 063 /** 064 * Speedup for logs 065 */ 066 private static final boolean IS_DEBUG = LOG.isDebugEnabled(); 067 068 /** The schemaManager reference */ 069 private transient SchemaManager schemaManager; 070 071 072 /** 073 * Creates a new instance of ServerEntrySerializer. 074 * 075 * @param schemaManager The reference to the global schemaManager 076 */ 077 public EntrySerializer( SchemaManager schemaManager ) 078 { 079 this.schemaManager = schemaManager; 080 } 081 082 083 /** 084 * <p> 085 * 086 * This is the place where we serialize entries, and all theirs 087 * elements. the reason why we don't call the underlying methods 088 * (<code>ServerAttribute.write(), Value.write()</code>) is that we need 089 * access to the registries to read back the values. 090 * <p> 091 * The structure used to store the entry is the following : 092 * <ul> 093 * <li><b>[a byte]</b> : if the Dn is empty 0 will be written else 1</li> 094 * <li><b>[Rdn]</b> : The entry's Rdn.</li> 095 * <li><b>[numberAttr]</b> : the bumber of attributes. Can be 0</li> 096 * <li>For each Attribute : 097 * <ul> 098 * <li><b>[attribute's oid]</b> : The attribute's OID to get back 099 * the attributeType on deserialization</li> 100 * <li><b>[Attribute]</b> The attribute</li> 101 * </ul> 102 * </li> 103 * </ul> 104 */ 105 public byte[] serialize( Object object ) throws IOException 106 { 107 Entry entry = ( Entry ) object; 108 109 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 110 ObjectOutput out = new ObjectOutputStream( baos ); 111 112 // First, the Dn 113 Dn dn = entry.getDn(); 114 115 // Write the Rdn of the Dn 116 if ( dn.isEmpty() ) 117 { 118 out.writeByte( 0 ); 119 } 120 else 121 { 122 out.writeByte( 1 ); 123 Rdn rdn = dn.getRdn(); 124 rdn.writeExternal( out ); 125 } 126 127 // Then the attributes. 128 out.writeInt( entry.getAttributes().size() ); 129 130 // Iterate through the keys. We store the Attribute 131 // here, to be able to restore it in the readExternal : 132 // we need access to the registries, which are not available 133 // in the ServerAttribute class. 134 for ( Attribute attribute : entry.getAttributes() ) 135 { 136 AttributeType attributeType = attribute.getAttributeType(); 137 138 // Write the oid to be able to restore the AttributeType when deserializing 139 // the attribute 140 String oid = attributeType.getOid(); 141 142 out.writeUTF( oid ); 143 144 // Write the attribute 145 attribute.writeExternal( out ); 146 } 147 148 out.flush(); 149 150 // Note : we don't store the ObjectClassAttribute. It has already 151 // been stored as an attribute. 152 153 if ( IS_DEBUG ) 154 { 155 LOG.debug( ">------------------------------------------------" ); 156 LOG.debug( "Serialize {}", entry ); 157 } 158 159 return baos.toByteArray(); 160 } 161 162 163 /** 164 * Deserialize a Entry. 165 * 166 * @param bytes the byte array containing the serialized entry 167 * @return An instance of a Entry object 168 * @throws IOException if we can't deserialize the Entry 169 */ 170 public Object deserialize( byte[] bytes ) throws IOException 171 { 172 ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( bytes ) ); 173 174 try 175 { 176 Entry entry = new DefaultEntry( schemaManager ); 177 178 // Read the Dn, if any 179 byte hasDn = in.readByte(); 180 181 if ( hasDn == 1 ) 182 { 183 Rdn rdn = new Rdn( schemaManager ); 184 rdn.readExternal( in ); 185 186 try 187 { 188 entry.setDn( new Dn( schemaManager, rdn ) ); 189 } 190 catch ( LdapInvalidDnException lide ) 191 { 192 IOException ioe = new IOException( lide.getMessage() ); 193 ioe.initCause( lide ); 194 throw ioe; 195 } 196 } 197 else 198 { 199 entry.setDn( Dn.EMPTY_DN ); 200 } 201 202 // Read the number of attributes 203 int nbAttributes = in.readInt(); 204 205 // Read the attributes 206 for ( int i = 0; i < nbAttributes; i++ ) 207 { 208 // Read the attribute's OID 209 String oid = in.readUTF(); 210 211 try 212 { 213 AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( oid ); 214 215 // Create the attribute we will read 216 Attribute attribute = new DefaultAttribute( attributeType ); 217 218 // Read the attribute 219 attribute.readExternal( in ); 220 221 entry.add( attribute ); 222 } 223 catch ( LdapException ne ) 224 { 225 // We weren't able to find the OID. The attribute will not be added 226 throw new ClassNotFoundException( ne.getMessage(), ne ); 227 } 228 } 229 230 return entry; 231 } 232 catch ( ClassNotFoundException cnfe ) 233 { 234 LOG.error( I18n.err( I18n.ERR_134, cnfe.getLocalizedMessage() ) ); 235 throw new IOException( cnfe.getLocalizedMessage() ); 236 } 237 } 238}