| | |
| | | */ |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | |
| | | import org.opends.server.api.CompressedSchema; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.protocols.asn1.ASN1Element; |
| | | import org.opends.server.protocols.asn1.ASN1Exception; |
| | | import org.opends.server.protocols.asn1.ASN1Integer; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.asn1.ASN1Sequence; |
| | | import org.opends.server.types.*; |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.zip.DataFormatException; |
| | | |
| | | /** |
| | | * Handles the disk representation of LDAP data. |
| | | */ |
| | |
| | | public static final byte TAG_DIRECTORY_SERVER_ENTRY = 0x61; |
| | | |
| | | /** |
| | | * Decode a DatabaseEntry. The encoded bytes may be compressed and/or |
| | | * encrypted. |
| | | * |
| | | * @param bytes The encoded bytes of a DatabaseEntry. |
| | | * @return The decoded bytes. |
| | | * @throws ASN1Exception If the data is not in the expected ASN.1 encoding |
| | | * format. |
| | | * @throws DataFormatException If an error occurs while trying to decompress |
| | | * compressed data. |
| | | */ |
| | | static public byte[] decodeDatabaseEntry(byte[] bytes) |
| | | throws ASN1Exception,DataFormatException |
| | | { |
| | | // FIXME: This array copy could be very costly on performance. We need to |
| | | // FIXME: find a faster way to implement this versioning feature. |
| | | // Remove version number from the encoded bytes |
| | | byte[] encodedBytes = new byte[bytes.length - 1]; |
| | | System.arraycopy(bytes, 1, encodedBytes, 0, encodedBytes.length); |
| | | |
| | | // Decode the sequence. |
| | | List<ASN1Element> elements; |
| | | elements = ASN1Sequence.decodeAsSequence(encodedBytes).elements(); |
| | | |
| | | // Decode the uncompressed size. |
| | | int uncompressedSize; |
| | | uncompressedSize = elements.get(0).decodeAsInteger().intValue(); |
| | | |
| | | // Decode the data bytes. |
| | | byte[] dataBytes; |
| | | dataBytes = elements.get(1).decodeAsOctetString().value(); |
| | | |
| | | byte[] uncompressedBytes; |
| | | if (uncompressedSize == 0) |
| | | { |
| | | // The bytes are not compressed. |
| | | uncompressedBytes = dataBytes; |
| | | } |
| | | else |
| | | { |
| | | // The bytes are compressed. |
| | | CryptoManager cryptoManager = DirectoryServer.getCryptoManager(); |
| | | uncompressedBytes = new byte[uncompressedSize]; |
| | | /* int len = */ cryptoManager.uncompress(dataBytes, uncompressedBytes); |
| | | } |
| | | |
| | | return uncompressedBytes; |
| | | } |
| | | |
| | | /** |
| | | * Decodes an entry from its database representation. |
| | | * <p> |
| | | * An entry on disk is ASN1 encoded in this format: |
| | | * |
| | | * <pre> |
| | | * DatabaseEntry ::= [APPLICATION 0] IMPLICIT SEQUENCE { |
| | | * uncompressedSize INTEGER, -- A zero value means not compressed. |
| | | * dataBytes OCTET STRING -- Optionally compressed encoding of |
| | | * the data bytes. |
| | | * } |
| | | * |
| | | * ID2EntryValue ::= DatabaseEntry |
| | | * -- Where dataBytes contains an encoding of DirectoryServerEntry. |
| | | * |
| | | * DirectoryServerEntry ::= [APPLICATION 1] IMPLICIT SEQUENCE { |
| | | * dn LDAPDN, |
| | | * objectClasses SET OF LDAPString, |
| | | * userAttributes AttributeList, |
| | | * operationalAttributes AttributeList |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @param bytes A byte array containing the encoded database value. |
| | | * @param compressedSchema The compressed schema manager to use when decoding. |
| | | * @return The decoded entry. |
| | | * @throws ASN1Exception If the data is not in the expected ASN.1 encoding |
| | | * format. |
| | | * @throws LDAPException If the data is not in the expected ASN.1 encoding |
| | | * format. |
| | | * @throws DataFormatException If an error occurs while trying to decompress |
| | | * compressed data. |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | */ |
| | | static public Entry entryFromDatabase(byte[] bytes, |
| | | CompressedSchema compressedSchema) |
| | | throws DirectoryException,ASN1Exception,LDAPException,DataFormatException |
| | | { |
| | | byte[] uncompressedBytes = decodeDatabaseEntry(bytes); |
| | | return decodeDirectoryServerEntry(uncompressedBytes, compressedSchema); |
| | | } |
| | | |
| | | /** |
| | | * Decode an entry from a ASN1 encoded DirectoryServerEntry. |
| | | * |
| | | * @param bytes A byte array containing the encoding of DirectoryServerEntry. |
| | | * @param compressedSchema The compressed schema manager to use when decoding. |
| | | * @return The decoded entry. |
| | | * @throws ASN1Exception If the data is not in the expected ASN.1 encoding |
| | | * format. |
| | | * @throws LDAPException If the data is not in the expected ASN.1 encoding |
| | | * format. |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | */ |
| | | static private Entry decodeDirectoryServerEntry(byte[] bytes, |
| | | CompressedSchema compressedSchema) |
| | | throws DirectoryException,ASN1Exception,LDAPException |
| | | { |
| | | return Entry.decode(bytes, compressedSchema); |
| | | } |
| | | |
| | | /** |
| | | * Encodes a DatabaseEntry. The encoded bytes may be compressed and/or |
| | | * encrypted. |
| | | * |
| | | * @param bytes The bytes to encode. |
| | | * @param dataConfig Compression and cryptographic options. |
| | | * @return A byte array containing the encoded DatabaseEntry. |
| | | */ |
| | | static public byte[] encodeDatabaseEntry(byte[] bytes, DataConfig dataConfig) |
| | | { |
| | | int uncompressedSize = 0; |
| | | |
| | | // Do optional compression. |
| | | CryptoManager cryptoManager = DirectoryServer.getCryptoManager(); |
| | | if (dataConfig.isCompressed() && cryptoManager != null) |
| | | { |
| | | byte[] compressedBuffer = new byte[bytes.length]; |
| | | int compressedSize = cryptoManager.compress(bytes, |
| | | compressedBuffer); |
| | | if (compressedSize != -1) |
| | | { |
| | | // Compression was successful. |
| | | uncompressedSize = bytes.length; |
| | | bytes = new byte[compressedSize]; |
| | | System.arraycopy(compressedBuffer, 0, bytes, 0, compressedSize); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("Compression %d/%d%n", |
| | | compressedSize, uncompressedSize); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | | // Encode the DatabaseEntry. |
| | | ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2); |
| | | elements.add(new ASN1Integer(uncompressedSize)); |
| | | elements.add(new ASN1OctetString(bytes)); |
| | | byte[] asn1Sequence = |
| | | new ASN1Sequence(TAG_DATABASE_ENTRY, elements).encode(); |
| | | |
| | | // FIXME: This array copy could be very costly on performance. We need to |
| | | // FIXME: find a faster way to implement this versioning feature. |
| | | // Prefix version number to the encoded bytes |
| | | byte[] encodedBytes = new byte[asn1Sequence.length + 1]; |
| | | encodedBytes[0] = FORMAT_VERSION; |
| | | System.arraycopy(asn1Sequence, 0, encodedBytes, 1, asn1Sequence.length); |
| | | |
| | | return encodedBytes; |
| | | } |
| | | |
| | | /** |
| | | * Encodes an entry to the raw database format, with optional compression. |
| | | * |
| | | * @param entry The entry to encode. |
| | | * @param dataConfig Compression and cryptographic options. |
| | | * @return A byte array containing the encoded database value. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to encode |
| | | * the entry. |
| | | */ |
| | | static public byte[] entryToDatabase(Entry entry, DataConfig dataConfig) |
| | | throws DirectoryException |
| | | { |
| | | byte[] uncompressedBytes = encodeDirectoryServerEntry(entry, |
| | | dataConfig.getEntryEncodeConfig()); |
| | | return encodeDatabaseEntry(uncompressedBytes, dataConfig); |
| | | } |
| | | |
| | | /** |
| | | * Encodes an entry to the raw database format, without compression. |
| | | * |
| | | * @param entry The entry to encode. |
| | | * @return A byte array containing the encoded database value. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to encode |
| | | * the entry. |
| | | */ |
| | | static public byte[] entryToDatabase(Entry entry) |
| | | throws DirectoryException |
| | | { |
| | | return entryToDatabase(entry, new DataConfig(false, false, null)); |
| | | } |
| | | |
| | | /** |
| | | * Encode a ASN1 DirectoryServerEntry. |
| | | * |
| | | * @param entry The entry to encode. |
| | | * @encodeConfig The configuration to use when encoding the entry. |
| | | * @return A byte array containing the encoded DirectoryServerEntry. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to encode |
| | | * the entry. |
| | | */ |
| | | static private byte[] encodeDirectoryServerEntry(Entry entry, |
| | | EntryEncodeConfig encodeConfig) |
| | | throws DirectoryException |
| | | { |
| | | return entry.encode(encodeConfig); |
| | | } |
| | | |
| | | /** |
| | | * Decode an entry ID value from its database representation. Note that |
| | | * this method will throw an ArrayIndexOutOfBoundsException if the bytes |
| | | * array length is less than 8. |
| | |
| | | |
| | | return bytes; |
| | | } |
| | | |
| | | /** |
| | | * Get the version number of the DatabaseEntry. |
| | | * |
| | | * @param bytes The encoded bytes of a DatabaseEntry. |
| | | * @return The version number. |
| | | */ |
| | | public static byte getEntryVersion(byte[] bytes) |
| | | { |
| | | return bytes[0]; |
| | | } |
| | | |
| | | } |