blob: 1941bf3e1665ac40af3e6197d5f3eb619fdbc703 [file] [log] [blame]
Title: 13 - Controls
NavPrev: 12-cursor.html
NavPrevText: 12 - Cursor
NavUp: ../internal-design-guide.html
NavUpText: Internal Design Guide
NavNext: 14-extended-operations.html
NavNextText: 14 - Extended Operations
Notice: 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.
# 13 - Controls
Controls are extension to the protocol. They are added in messages, and can contain extra information. A **Control** contains :
* an **OID**, unique to this **Control**, as an identifier
* a **Criticality** flag, which tells if the control can be ignored or not
* a value, which might be **BER** encoded
We have 20 **Control**s declared in the **LDAP API**, and we can add more.
## Implementation
Here is the **Control** classes and interfaces hierarchy :
![Control Hierarchy](images/controls.png)
As we can see, each _Impl_ class is coupled with a _Decorator_ class, used to process teh encoding and decoding of a **Control**
Keep in mind that **Control**s have to be sent within a message, thus be encoded or decoded when the response come back.
## Package/module
We have two flavors of **Control**s, standard and 'extra'. Standard **Control**s are those supported by all he servers, extras are oly supported by a few servers. This is an arbitrary decision, we could have put all of them at teh same place.
The list of standard **Control**s is :
* _Cascade_
* _EntryChange_
* _ManageDsaIT_
* _PagedResults_
* _PersistentSearch_
* _ProxiedAuthz_
* _SortRequest_
* _SortResponse_
* _Subentries_
The list of extra **Control**s is :
* _AdDirSync_
* _AdPolicyHints_
* _AdShowDeleted_
* _ChangeNotifications_
* _PermissiveModify_
* _PasswordPolicy_
* _SyncDoneValue_
* _SyncRequestValue_
* _SyncStateValue_
* _VirtualListViewRequest_
* _VirtualListViewResponse_
The standard **Control**s are described in the _ldap/model_ module (for the classes and interfaces) and in the _ldap/codec/core_ module (for the _Decorator_ and the decoding classes), in the _org.apache.directory.api.ldap.model.message.controls_ package.
The extra **Control**s are described in the _ldap/extras/codec_ and _ldap/extras/codec-api modules (the first module contains the _classes_ and _interfaces_, the second module contains the _Decorator_s and all the decoder classes.) , in the _org.apache.directory.api.ldap.extras.controls.XXX_ packages (one sub-package per control) and in the _org.apache.directory.api.ldap.codec.controls.XXX_ package.
Any new **Control** is likely to be declared as an extra **Control**.
## Creating a new Control
The **Control** creation follows a few rules :
* It has to have a unique **OID** (this is generally the case, for **Control**s defined in RFCs)
* It has an _Interface_, a _Decorator_ and an implementation
* It must be declared
Let's see how it all works, using an example. We will add the **Transaction Specification Control**, defined in [RFC 5805(https://tools.ietf.org/html/rfc5805)], paragraphe 2.2 :
:::Text
2.2. Transaction Specification Control
A Transaction Specification Control is an LDAPControl where the
controlType is 1.3.6.1.1.21.2, the criticality is TRUE, and the
controlValue is a transaction identifier. The control is appropriate
for update requests including Add, Delete, Modify, and ModifyDN
(Rename) requests [RFC4511], as well as the Password Modify requests
[RFC3062].
As discussed in Section 4, the Transaction Specification control can
be used in conjunction with request controls appropriate for the
update request.
The _Interface_ will just expose the _Transaction Identifier_, and store the **Control** **OID** :
:::Java
package org.apache.directory.api.ldap.extras.controls.transaction;
import org.apache.directory.api.ldap.model.message.Control;
/**
* The Transaction Specification control. It's defined in RFC 5805.
* This control is sent with every update once a transaction is started.
* It contains the Transaction ID.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public interface TransactionSpecification
{
/** The Transaction Specification control OID */
String OID = "1.3.6.1.1.21.2";
/**
* @return The transaction identifier
*/
byte[] getIdentifier();
/**
* Set the transaction ID
* @param The transaction identifier, an opaque byte array
*/
void setIdentifier( byte[] identifier );
}
We now need an implementation for this **Control**. It really just a matter of having an instanciable object. Note that this class exteds the _AbstractControl_ class.
Here it is :
:::Java
package org.apache.directory.api.ldap.extras.controls.transaction;
import org.apache.directory.api.ldap.model.message.controls.AbstractControl;
import org.apache.directory.api.util.Strings;
/**
* The Transaction Specification control. It's defined in RFC 5805.
* This control is sent with every update once a transaction is started.
* It contains the Transaction ID.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class TransactionSpecificationImpl extends AbstractControl implements TransactionSpecification
{
/** The Transaction Specification identifier */
private byte[] identifier;
/**
* Default constructor
*/
public TransactionSpecificationImpl()
{
super( OID );
}
/**
* {@inheritDoc}
*/
@Override
public byte[] getIdentifier()
{
return identifier;
}
/**
* {@inheritDoc}
*/
@Override
public void setIdentifier( byte[] identifier )
{
// Copy the byte[]
if ( identifier != null )
{
this.identifier = new byte[identifier.length];
System.arraycopy( identifier, 0, this.identifier, 0, identifier.length );
}
}
/**
* @see Object#toString()
*/
@Override
public String toString()
{
if ( identifier != null )
{
return "Transaction specification ID=null";
}
else
{
return "Transaction specification ID=" + Strings.dumpBytes( identifier );
}
}
}
Nothing much to say, except that we have a default constructor that use the **Control**'s **OID** and a _toString()_ method, for convenience. The _Identifier_ is printed in hex format.
That's it for the two base _class_ and _interface_, we now have to deal with encoding and decoding.
### Encoding & Decoding
Encoding the **Control** is done by the **Decorator**. This class implements the _Asn1Object_ which defines the method _encode()_. Let's see how it works...
In order to encode the value we need to know its length, this is why we also have to implement the _computeLegth()_ method. In our case, it's superflouous, as the length is known : it's the _identifier_'s length.
Decoidng is quitre trivial : as we only need to decode the **Control** value, and as it's an opaque _byte[]_, we just need to copy this value in the instance.
In any case, we don't encode the whole **Control**, we just encode it's value : the **Control** itself is encode by the **LdapMessage**.
Here is the _Decorator_ code.
:::Java
package org.apache.directory.api.ldap.extras.controls.transaction;
import java.nio.ByteBuffer;
import org.apache.directory.api.asn1.Asn1Object;
import org.apache.directory.api.asn1.DecoderException;
import org.apache.directory.api.asn1.EncoderException;
import org.apache.directory.api.ldap.codec.api.ControlDecorator;
import org.apache.directory.api.ldap.codec.api.LdapApiService;
/**
* TransactionSpecification decorator.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class TransactionSpecificationDecorator extends ControlDecorator<TransactionSpecification> implements TransactionSpecification
{
/**
* Create a new instance of TransactionSpecificationDecorator
*
* @param codec The LDAP Service to use
* @param decoratedControl The control to decorate
*/
public TransactionSpecificationDecorator( LdapApiService codec, TransactionSpecification decoratedControl )
{
super( codec, decoratedControl );
}
/**
* {@inheritDoc}
*/
@Override
public Asn1Object decode( byte[] controlBytes ) throws DecoderException
{
// Nothing to decode, the byte array is copied as is in identifier
setIdentifier( controlBytes );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public int computeLength()
{
byte[] identifier = getDecorated().getIdentifier();
if ( identifier != null )
{
return identifier.length;
}
else
{
return -1;
}
}
/**
* {@inheritDoc}
*/
@Override
public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
{
byte[] identifier = getDecorated().getIdentifier();
if ( identifier != null )
{
ByteBuffer encoded = ByteBuffer.allocate( identifier.length );
encoded.put( identifier );
return encoded;
}
else
{
return ByteBuffer.allocate( 0 );
}
}
/**
* {@inheritDoc}
*/
@Override
public byte[] getIdentifier()
{
return getDecorated().getIdentifier();
}
/**
* {@inheritDoc}
*/
@Override
public void setIdentifier( byte[] identifier )
{
getDecorated().setIdentifier( identifier );
}
}
### The Factory
We also need a _Factory_ class that is used to register the **Control**. This class simply expose a constructor for the **Control**. It's code is pretty trival, there is nothing specific to the added **Control**.
Side note : as this class is ony invoked at startup, we could use reflection instead of having one _Factory_ per **Control**...
:::Java
package org.apache.directory.api.ldap.extras.controls.transaction;
import org.apache.directory.api.ldap.codec.api.CodecControl;
import org.apache.directory.api.ldap.codec.api.ControlFactory;
import org.apache.directory.api.ldap.codec.api.LdapApiService;
/**
* A codec {@link ControlFactory} implementation for {@link TransactionSpecification} controls.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class TransactionSpecificationFactory implements ControlFactory<TransactionSpecification>
{
/** The LDAP codec responsible for encoding and decoding Cascade Controls */
private LdapApiService codec;
/**
* Creates a new instance of TransactionSpecificationFactory.
*
* @param codec The LDAP codec
*/
public TransactionSpecificationFactory( LdapApiService codec )
{
this.codec = codec;
}
/**
* {@inheritDoc}
*/
@Override
public String getOid()
{
return TransactionSpecification.OID;
}
/**
* {@inheritDoc}
*/
@Override
public CodecControl<TransactionSpecification> newCodecControl()
{
return new TransactionSpecificationDecorator( codec, new TransactionSpecificationImpl() );
}
/**
* {@inheritDoc}
*/
@Override
public CodecControl<TransactionSpecification> newCodecControl( TransactionSpecification control )
{
return new TransactionSpecificationDecorator( codec, control );
}
}
## A more complex Control
We just shown a **Control** which was easy to encode or decode. Most of the time, the **Control**'s value is itself an **ASN/1** **BER** encoded value, and we need more classes to be able to process the decoding. Let use another **Control** as a sample :
TODO
## Adding a Control to the API
Once we have written the **Control** classes and interfaces, we need to declare it so that the **LDAP API** can use it.
The thing is that the **LDAP API** is **OSGi** compliant, so we need to expose the **Control**s and we also have to activate them.
The _ExtrasBundleActivator_ class (in the _ldap/extras/codec_ module) has to be modified to register and unregister the added **Control** :
:::Java
...
import org.apache.directory.api.ldap.extras.controls.changeNotifications.TransactionSpecification;
...
/**
* A BundleActivator for the ldap codec extras extension: extra ApacheDS and
* Apache Directory Studio specific controls and extended operations.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class ExtrasBundleActivator implements BundleActivator
{
....
/**
* Registers all the extras controls present in this control pack.
*
* @param codec The codec service.
*/
private void registerExtrasControls( LdapApiService codec )
{
ControlFactory<AdDirSync> adDirSyncFactory = new AdDirSyncFactory( codec );
codec.registerControl( adDirSyncFactory );
...
ControlFactory<TransactionSpecification> TransactionSpecificationFactory = new TransactionSpecificationFactory( codec );
codec.registerControl( TransactionSpecification );
}
...
private void unregisterExtrasControls( LdapApiService codec )
{
codec.unregisterControl( AdDirSync.OID );
codec.unregisterControl( AdShowDeleted.OID );
...
codec.unregisterControl( TransactionSpecification.OID );
}
....
Here we added the _TransactionSpecification_ **Control** at the end of thse two methods, and added the associated _import_.
Last, not least, we need to update the _loadStockControls_ method in the _CodecFactoryUtil_ class (in _ldap/codec/standalone_ module) :
:::Java
...
import org.apache.directory.api.ldap.extras.controls.changeNotifications.TransactionSpecification;
...
/**
* A utility class for adding Codec and extended operation factories.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public final class CodecFactoryUtil
{
....
/**
* Loads the Controls implement out of the box in the codec.
*
* @param controlFactories The Control factories to use
* @param apiService The LDAP Service instance to use
*/
public static void loadStockControls( Map<String, ControlFactory<?>> controlFactories, LdapApiService apiService )
{
// Standard controls
ControlFactory<Cascade> cascadeFactory = new CascadeFactory( apiService );
controlFactories.put( cascadeFactory.getOid(), cascadeFactory );
LOG.info( "Registered pre-bundled control factory: {}", cascadeFactory.getOid() );
...
ControlFactory<TransactionSpecification> transactionSpecificationFactory =
new TransactionSpecificationFactory( apiService );
controlFactories.put( transactionSpecificationFactory.getOid(), transactionSpecificationFactory );
LOG.info( "Registered pre-bundled control factory: {}", transactionSpecificationFactory.getOid() );
}
...
}
We are done ! Note that there is nothing to change in the _MANIFEST.MF_ file, as the packages are already exported.