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.ByteArrayInputStream; 024import java.io.ByteArrayOutputStream; 025import java.io.IOException; 026import java.io.ObjectInputStream; 027import java.io.ObjectOutput; 028import java.io.ObjectOutputStream; 029import java.nio.ByteBuffer; 030import java.util.Comparator; 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.mavibot.btree.serializer.AbstractElementSerializer; 043import org.apache.directory.mavibot.btree.serializer.BufferHandler; 044import org.apache.directory.server.i18n.I18n; 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048 049/** 050 * Serialize and deserialize a ServerEntry. There is a big difference with the standard 051 * Entry serialization : we don't serialize the entry's Dn, we just serialize it's Rdn. 052 * <br><br> 053 * <b>This class must *not* be used outside of the server.</b> 054 * 055 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 056 */ 057public class MavibotEntrySerializer extends AbstractElementSerializer<Entry> 058{ 059 /** The serialVersionUID */ 060 private static final long serialVersionUID = 1L; 061 062 /** the logger for this class */ 063 private static final Logger LOG = LoggerFactory.getLogger( MavibotEntrySerializer.class ); 064 065 /** 066 * Speedup for logs 067 */ 068 private static final boolean IS_DEBUG = LOG.isDebugEnabled(); 069 070 /** The schemaManager reference */ 071 private static SchemaManager schemaManager; 072 073 private static class EntryComparator implements Comparator<Entry> 074 { 075 076 @Override 077 public int compare( Entry entry1, Entry entry2 ) 078 { 079 return entry1.getDn().getName().compareTo( entry1.getDn().getName() ); 080 } 081 082 } 083 084 private static Comparator<Entry> comparator = new EntryComparator(); 085 086 087 /** 088 * Creates a new instance of ServerEntrySerializer. 089 * The schemaManager MUST be set explicitly using the static {@link #setSchemaManager(SchemaManager)} 090 */ 091 public MavibotEntrySerializer() 092 { 093 super( comparator ); 094 } 095 096 097 @Override 098 public Comparator<Entry> getComparator() 099 { 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}