blob: 896bb3728e0405cdb5a26b0b1f703caeee18eae3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.shared.ldap.codec;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.directory.shared.asn1.AbstractAsn1Object;
import org.apache.directory.shared.asn1.ber.tlv.TLV;
import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
import org.apache.directory.shared.asn1.ber.tlv.Value;
import org.apache.directory.shared.asn1.codec.EncoderException;
import org.apache.directory.shared.i18n.I18n;
import org.apache.directory.shared.ldap.codec.controls.CodecControl;
import org.apache.directory.shared.ldap.codec.controls.ManageDsaITControl;
import org.apache.directory.shared.ldap.codec.controls.replication.syncDoneValue.SyncDoneValueControl;
import org.apache.directory.shared.ldap.codec.controls.replication.syncInfoValue.SyncInfoValueControl;
import org.apache.directory.shared.ldap.codec.controls.replication.syncRequestValue.SyncRequestValueControl;
import org.apache.directory.shared.ldap.codec.controls.replication.syncStateValue.SyncStateValueControl;
import org.apache.directory.shared.ldap.codec.search.controls.pagedSearch.PagedResultsControl;
import org.apache.directory.shared.ldap.codec.search.controls.persistentSearch.PersistentSearchControl;
import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl;
import org.apache.directory.shared.ldap.message.control.Control;
import org.apache.directory.shared.ldap.message.control.replication.SynchronizationInfoEnum;
/**
* The main ldapObject : every Ldap Message are encapsulated in it. It contains
* a message Id, a operation (protocolOp) and one ore more Controls.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public abstract class LdapMessageCodec extends AbstractAsn1Object
{
// ~ Instance fields
// ----------------------------------------------------------------------------
/** The message ID */
private int messageId;
/** The controls */
private List<Control> controls;
/** The current control */
private Control currentControl;
/** The LdapMessage length */
protected int ldapMessageLength;
/** The controls length */
private int controlsLength;
/** The controls sequence length */
private int controlsSequenceLength;
private static Map<String, Control> codecControls = new HashMap<String, Control>();
static
{
// Initialize the different known Controls
Control control = new PersistentSearchControl();
codecControls.put( control.getOid(), control );
control = new ManageDsaITControl();
codecControls.put( control.getOid(), control );
control = new SubentriesControl();
codecControls.put( control.getOid(), control );
control = new PagedResultsControl();
codecControls.put( control.getOid(), control );
control = new SyncDoneValueControl();
codecControls.put( control.getOid(), control );
control = new SyncInfoValueControl( SynchronizationInfoEnum.NEW_COOKIE );
codecControls.put( control.getOid(), control );
control = new SyncInfoValueControl( SynchronizationInfoEnum.REFRESH_DELETE );
codecControls.put( control.getOid(), control );
control = new SyncInfoValueControl( SynchronizationInfoEnum.REFRESH_PRESENT );
codecControls.put( control.getOid(), control );
control = new SyncInfoValueControl( SynchronizationInfoEnum.SYNC_ID_SET );
codecControls.put( control.getOid(), control );
control = new SyncRequestValueControl();
codecControls.put( control.getOid(), control );
control = new SyncStateValueControl();
codecControls.put( control.getOid(), control );
}
// ~ Constructors
// -------------------------------------------------------------------------------
/**
* Creates a new LdapMessage object.
*/
public LdapMessageCodec()
{
// We should not create this kind of object directly
super();
}
// ~ Methods
// ------------------------------------------------------------------------------------
/**
* Get the Control Object at a specific index
*
* @param i The index of the Control Object to get
* @return The selected Control Object
*/
public Control getControls( int i )
{
if ( controls != null )
{
return controls.get( i );
}
else
{
return null;
}
}
/**
* Get the Control Objects
*
* @return The Control Objects
*/
public List<Control> getControls()
{
return controls;
}
/**
* Get the current Control Object
*
* @return The current Control Object
*/
public Control getCurrentControl()
{
return currentControl;
}
public Control getCodecControl( String oid )
{
return codecControls.get( oid );
}
/**
* Add a control to the Controls array
*
* @param control The Control to add
*/
public void addControl( Control control )
{
currentControl = control;
if ( controls == null )
{
controls = new ArrayList<Control>();
}
controls.add( control );
}
/**
* Set or add a list of controls to the Controls array. If the existing
* control array is not null then the given controls will be added
*
* @param controls The list of Controls to set or add
*/
public void addControls( List<Control> controls )
{
if( this.controls == null )
{
this.controls = controls;
}
else if( controls != null )
{
this.controls.addAll( controls );
}
}
/**
* Init the controls array
*/
public void initControls()
{
controls = new ArrayList<Control>();
}
/**
* Get the message ID
*
* @return The message ID
*/
public int getMessageId()
{
return messageId;
}
/**
* Set the message ID
*
* @param messageId The message ID
*/
public void setMessageId( int messageId )
{
this.messageId = messageId;
}
/**
* Get the message type
*
* @return The message type
*/
public abstract MessageTypeEnum getMessageType();
/**
* Get the message type Name
*
* @return The message type name
*/
public abstract String getMessageTypeName();
protected abstract int computeLengthProtocolOp();
/**
* Compute the LdapMessage length LdapMessage :
* 0x30 L1
* |
* +--> 0x02 0x0(1-4) [0..2^31-1] (MessageId)
* +--> protocolOp
* [+--> Controls]
*
* MessageId length = Length(0x02) + length(MessageId) + MessageId.length
* L1 = length(ProtocolOp)
* LdapMessage length = Length(0x30) + Length(L1) + MessageId length + L1
*/
public int computeLength()
{
// The length of the MessageId. It's the sum of
// - the tag (0x02), 1 byte
// - the length of the Id length, 1 byte
// - the Id length, 1 to 4 bytes
ldapMessageLength = 1 + 1 + Value.getNbBytes( messageId );
// Get the protocolOp length
int protocolOpLength = computeLengthProtocolOp();
// Add the protocol length to the message length
ldapMessageLength += protocolOpLength;
// Do the same thing for Controls, if any.
if ( controls != null )
{
// Controls :
// 0xA0 L3
// |
// +--> 0x30 L4
// +--> 0x30 L5
// +--> ...
// +--> 0x30 Li
// +--> ...
// +--> 0x30 Ln
//
// L3 = Length(0x30) + Length(L5) + L5
// + Length(0x30) + Length(L6) + L6
// + ...
// + Length(0x30) + Length(Li) + Li
// + ...
// + Length(0x30) + Length(Ln) + Ln
//
// LdapMessageLength = LdapMessageLength + Length(0x90)
// + Length(L3) + L3
controlsSequenceLength = 0;
// We may have more than one control. ControlsLength is L4.
for ( Control control:controls )
{
controlsSequenceLength += ((CodecControl)control).computeLength();
}
// Computes the controls length
controlsLength = controlsSequenceLength; // 1 + Length.getNbBytes(
// controlsSequenceLength
// ) + controlsSequenceLength;
// Now, add the tag and the length of the controls length
ldapMessageLength += 1 + TLV.getNbBytes( controlsSequenceLength ) + controlsSequenceLength;
}
// finally, calculate the global message size :
// length(Tag) + Length(length) + length
return 1 + ldapMessageLength + TLV.getNbBytes( ldapMessageLength );
}
protected abstract void encodeProtocolOp( ByteBuffer buffer ) throws EncoderException;
/**
* Generate the PDU which contains the encoded object.
*
* The generation is done in two phases :
* - first, we compute the length of each part and the
* global PDU length
* - second, we produce the PDU.
*
* <pre>
* 0x30 L1
* |
* +--> 0x02 L2 MessageId
* +--> ProtocolOp
* +--> Controls
*
* L2 = Length(MessageId)
* L1 = Length(0x02) + Length(L2) + L2 + Length(ProtocolOp) + Length(Controls)
* LdapMessageLength = Length(0x30) + Length(L1) + L1
* </pre>
*
* @param buffer The encoded PDU
* @return A ByteBuffer that contaons the PDU
* @throws EncoderException If anything goes wrong.
*/
public ByteBuffer encode() throws EncoderException
{
try
{
// Allocate the bytes buffer.
ByteBuffer bb = ByteBuffer.allocate( computeLength() );
try
{
// The LdapMessage Sequence
bb.put( UniversalTag.SEQUENCE_TAG );
// The length has been calculated by the computeLength method
bb.put( TLV.getBytes( ldapMessageLength ) );
}
catch ( BufferOverflowException boe )
{
throw new EncoderException( I18n.err( I18n.ERR_04005 ) );
}
// The message Id
Value.encode( bb, messageId );
// Add the protocolOp part
encodeProtocolOp( bb );
// Do the same thing for Controls, if any.
if ( controls != null )
{
// Encode the controls
bb.put( ( byte ) LdapConstants.CONTROLS_TAG );
bb.put( TLV.getBytes( controlsLength ) );
// Encode each control
for ( Control control:controls )
{
((CodecControl)control).encode( bb );
}
}
return bb;
}
catch ( EncoderException ee )
{
MessageEncoderException exception = new MessageEncoderException( messageId, ee.getMessage() );
throw exception;
}
}
/**
* Get a String representation of a LdapMessage
*
* @return A LdapMessage String
*/
protected String toString( String protocolOp )
{
StringBuffer sb = new StringBuffer();
sb.append( "LdapMessage\n" );
sb.append( " message Id : " ).append( messageId ).append( '\n' );
sb.append( protocolOp ).append( '\n' );
if ( controls != null )
{
for ( Control control:controls )
{
sb.append( control );
}
}
return sb.toString();
}
}