You are viewing a plain text version of this content. The canonical link for it is here.
Posted to api@directory.apache.org by Emmanuel Lécharny <el...@gmail.com> on 2017/03/17 10:00:44 UTC

Immutable SyntexCheckers

Hi guys,


last week-end, I worked on adding a way to get a static instance of
SyntaxCheckers, instead of having to create a new instance every now and
then. As most of those SC aren't going to be modified during the
lifetime of an application, it makes a lot fo sense to have it
constructed once, and only once.


First let me summarize their lifecycle.


SC (SyntaxChecker) are associated with a LDAP Syntax, and are helper
classes used to check that a value is correct, accordingly to the
AttributeType syntax. We have more or less a hundred of such objects.
Most of them are defined in the code base, and will never change, but a
few can take a parameter, like teh TelephoneNumber that can use a regexp
as a matcher.

We wanted to be able to 'inject' some new SC dynamiclaly in the server,
by using a Java class that implements a new checker (this actually
works, but it's not really use as of today). The whole idea was to make
it dead simple - sort of - to extend the Schema, adding application
driven Schema elements. Bottom line, the mechanism is in place.


Now, when do we use those SC ? When we need to check a value. In this
case, we know about the value' AttributeType, and this AT knwos about
its Syntax, which is associated with a SC. The instance are created when
we load the schema at startup, as we store the FQCN of each SC in the
Schema :


dn: m-oid=1.3.6.1.4.1.1466.115.121.1.7,ou=syntaxCheckers,cn=system,ou=schema
m-oid: 1.3.6.1.4.1.1466.115.121.1.7
m-fqcn:
org.apache.directory.api.ldap.model.schema.syntaxCheckers.BooleanSyntaxChecker
objectclass: metaSyntaxChecker
objectclass: metaTop
objectclass: top
creatorsname: uid=admin,ou=system

The relation between a Ldap Syntax and its associated SC is the OID :
they have the same. So when we load the LdapSyntax, we class load its
associated SC. As a matter of fact, LdapSyntax are just descriptive,
when SC are executable.


That being said, there is not much of a reason to have the SC being
mutable. Even for the very few ones that can use a configuration - like
a regexp -, once created, they don't change.


So I changed the existing SC code by adding a static INSTANCE, which is
a reference to the instanciated SC. You don't need anymore to create a
new instance when you need it, you just pull the INSTANCE.


That caused some issue in some tests, that were creating new instances
of a SC, but with a different OID. Using INSTANCE was breaking the
tests, because those tests were expecting a different OID. Actually, the
test was faulty, we should have created a totally new SC instead of
redefining and existing one, but well, that life and it raised a
concern. And this concern was even more important in the tests using teh
TelephoneNumber SC, because you can inject new regexp into it...

So we fixed the tests, and now, it's time to think about a better
solution, where the SC are completely immutable.


The idea is to use internal Builders :


public class BooleanSyntaxChecker extends SyntaxChecker
{

    /**
     * A static instance of BooleanSyntaxChecker
     */
    public static final BooleanSyntaxChecker INSTANCE = new
BooleanSyntaxChecker( SchemaConstants.BOOLEAN_SYNTAX );

    /** A static instance of the builder */
    private static final Builder BUILDER_INSTANCE = new Builder();

    /**
     * A static Builder for this class
     */
    public static class Builder
    {
        /** The BooleanSyntaxChecker OID */
        private String oid = SchemaConstants.BOOLEAN_SYNTAX;
       
        /**
         * The Builder constructor
         */
        private Builder()
        {
            // Nothing to do
        }
       
       
        /**
         * Set the SyntaxChecker's OID
         *
         * @param oid The OID
         * @return The Builder's Instance
         */
        public Builder setOid( String oid )
        {
            this.oid = oid;
           
            return this;
        }
       
       
        /**
         * Create a new instance of BooleanSyntaxChecker
         * @return A new instance of BooleanSyntaxChecker
         */
        public BooleanSyntaxChecker build()
        {
            return new BooleanSyntaxChecker( oid );
        }
    }
   
    /**
     * Creates a new instance of BooleanSyntaxChecker.
     */
    private BooleanSyntaxChecker( String oid )
    {
        super( oid );
    }

   
    /**
     * @return An instance of the Builder for this class
     */
    public static Builder builder()
    {
        return BUILDER_INSTANCE;
    }

The constructor is now private, so you can't instanciate the class. What
you have to do is either to get the static INSTANCE, and you get an
immutable and static version of the class, or you 'build' a completely
new instance using code like :


    BooleanSyntaxChecker checker = BooleanSyntaxChecker.builder().build();

(which is actually the same as the static INSTANCE, but as a different
object), or :

    BooleanSyntaxChecker checker =
BooleanSyntaxChecker.builder().setOid( "1.2.3.4" ).build();

if you want a completely different SC, with a different OID? that does
not collide with the existing statically instanciated SC.


The only requirement is that each SC has its own builder (we can't put
this code in the abstract SC, because it's abstract...)


For the RegexpSyntaxChecker, this gives :


/**
 * A SyntaxChecker implemented using Perl5 regular expressions to constrain
 * values.
 *
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory
Project</a>
 */
@SuppressWarnings("serial")
public class RegexSyntaxChecker extends SyntaxChecker
{
    /** A logger for this class */
    private static final Logger LOG = LoggerFactory.getLogger(
RegexSyntaxChecker.class );

    /** the set of regular expressions */
    private String[] expressions;
   
    /** A static instance of the builder */
    private static final Builder BUILDER_INSTANCE = new Builder();
   
    /**
     * A static Builder for this class
     */
    public static class Builder
    {
        /** The RegexSyntaxChecker OID, which is undefined by default */
        private String oid;
       
        /** the set of regular expressions */
        private String[] expressions;
       
        /**
         * The Builder constructor
         */
        private Builder()
        {
            // Nothing to do
        }
       
       
        /**
         * Set the SyntaxChecker's OID
         *
         * @param oid The OID
         * @return The Builder's Instance
         */
        public Builder setOid( String oid )
        {
            this.oid = oid;
           
            return this;
        }


        /**
         * Add a list of regexp to be applied by this SyntaxChecker
         *
         * @param expressions The regexp list to add
         */
        public Builder setExpressions( String[] expressions )
        {
            if ( ( expressions != null ) && ( expressions.length > 0 ) )
            {
                this.expressions = new String[expressions.length];
                System.arraycopy( expressions, 0, this.expressions, 0,
expressions.length );
            }
           
            return this;
        }
       
       
        /**
         * Create a new instance of RegexSyntaxChecker
         * @return A new instance of RegexSyntaxChecker
         */
        public RegexSyntaxChecker build()
        {
            return new RegexSyntaxChecker( oid, expressions );
        }
    }

   
    /**
     * Creates a Syntax validator for a specific Syntax using Perl5 matching
     * rules for validation.
     *
     * @param oid the oid of the Syntax values checked
     * @param matchExprArray the array of matching expressions
     */
    private RegexSyntaxChecker( String oid, String[] matchExprArray )
    {
        super( oid );

        this.expressions = matchExprArray;
    }

   
    /**
     * @return An instance of the Builder for this class
     */
    public static Builder builder()
    {
        return BUILDER_INSTANCE;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isValidSyntax( Object value )
    {
        String str;

        if ( value instanceof String )
        {
            str = ( String ) value;

            for ( String regexp : expressions )
            {
                if ( !str.matches( regexp ) )
                {
                    LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID,
value ) );
                   
                    return false;
                }
            }
        }

        LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );

        return true;
    }


    /**
     * Get the list of regexp stored into this SyntaxChecker
     *
     * @return AN array containing all the stored regexp
     */
    public String[] getExpressions()
    {
        if ( expressions == null )
        {
            return null;
        }
       
        String[] exprs = new String[expressions.length];
        System.arraycopy( expressions, 0, exprs, 0, expressions.length );
       
        return exprs;
    }
}


and using it looks like :



    RegexpSyntaxChecker checker = RegexpSyntaxChecker.builder().setOid(
"1.2.3.4" ).setExpressions( "*.*", "\\.*", "\\n" ).build();


Thoughts ?


-- 
Emmanuel Lecharny

Symas.com
directory.apache.org


Re: Immutable SyntexCheckers

Posted by Emmanuel Lécharny <el...@gmail.com>.

Le 20/03/2017 à 08:20, Stefan Seelmann a écrit :
> On 03/20/2017 08:12 AM, Emmanuel Lécharny wrote:
>>
>> Le 20/03/2017 à 07:37, Stefan Seelmann a écrit :
>>> On 03/20/2017 02:10 AM, Emmanuel Lécharny wrote:
>>>> public class BooleanSyntaxChecker extends SyntaxChecker
>>>> {
>>>>     /**
>>>>      * A static instance of BooleanSyntaxChecker
>>>>      */
>>>>     public static final BooleanSyntaxChecker INSTANCE = new
>>>> BooleanSyntaxChecker( SchemaConstants.BOOLEAN_SYNTAX );
>>>>    
>>>>     /** A static instance of the builder */
>>>>     private static final Builder BUILDER_INSTANCE = new Builder();
>>>>     /**
>>>>      * @return An instance of the Builder for this class
>>>>      */
>>>>     public static Builder builder()
>>>>     {
>>>>         return BUILDER_INSTANCE;
>>>>     }
>>> Hm, why a static builder? As it is not immutable there's a chance of
>>> race condition in case two threads use it concurrently.
>> That can't happen, because we have :
>>
>>     private static final Builder BUILDER_INSTANCE = new Builder();
>>
>> that is guaranteed to be built during the class loading.
> I agree about the creation of the builder instance.
>
> But if two threads *use* it:
> 1. thread 1 calls builder()
> 2. thread 2 calls builder() and gets the same builder instance
> 3. thread 1 calls setOid("1.1.1")
> 4. thread 2 calls setOid("2.2.2")
> 5. thread 1 calls build() and may get an SC with OID "2.2.2"!

Good catch...

    /**
     * @return An instance of the Builder for this class
     */
    public static Builder builder()
    {
        return new Builder();
    }

would solve the issue, correct ?

(somtime, trying to verdue is wrong).


>
>

-- 
Emmanuel Lecharny

Symas.com
directory.apache.org


Re: Immutable SyntexCheckers

Posted by Stefan Seelmann <ma...@stefan-seelmann.de>.
On 03/20/2017 08:12 AM, Emmanuel L�charny wrote:
> 
> 
> Le 20/03/2017 � 07:37, Stefan Seelmann a �crit :
>> On 03/20/2017 02:10 AM, Emmanuel L�charny wrote:
>>> public class BooleanSyntaxChecker extends SyntaxChecker
>>> {
>>>     /**
>>>      * A static instance of BooleanSyntaxChecker
>>>      */
>>>     public static final BooleanSyntaxChecker INSTANCE = new
>>> BooleanSyntaxChecker( SchemaConstants.BOOLEAN_SYNTAX );
>>>    
>>>     /** A static instance of the builder */
>>>     private static final Builder BUILDER_INSTANCE = new Builder();
>>>     /**
>>>      * @return An instance of the Builder for this class
>>>      */
>>>     public static Builder builder()
>>>     {
>>>         return BUILDER_INSTANCE;
>>>     }
>> Hm, why a static builder? As it is not immutable there's a chance of
>> race condition in case two threads use it concurrently.
> 
> That can't happen, because we have :
> 
>     private static final Builder BUILDER_INSTANCE = new Builder();
> 
> that is guaranteed to be built during the class loading.

I agree about the creation of the builder instance.

But if two threads *use* it:
1. thread 1 calls builder()
2. thread 2 calls builder() and gets the same builder instance
3. thread 1 calls setOid("1.1.1")
4. thread 2 calls setOid("2.2.2")
5. thread 1 calls build() and may get an SC with OID "2.2.2"!



Re: Immutable SyntexCheckers

Posted by Emmanuel Lécharny <el...@gmail.com>.

Le 20/03/2017 à 07:37, Stefan Seelmann a écrit :
> On 03/20/2017 02:10 AM, Emmanuel Lécharny wrote:
>> public class BooleanSyntaxChecker extends SyntaxChecker
>> {
>>     /**
>>      * A static instance of BooleanSyntaxChecker
>>      */
>>     public static final BooleanSyntaxChecker INSTANCE = new
>> BooleanSyntaxChecker( SchemaConstants.BOOLEAN_SYNTAX );
>>    
>>     /** A static instance of the builder */
>>     private static final Builder BUILDER_INSTANCE = new Builder();
>>     /**
>>      * @return An instance of the Builder for this class
>>      */
>>     public static Builder builder()
>>     {
>>         return BUILDER_INSTANCE;
>>     }
> Hm, why a static builder? As it is not immutable there's a chance of
> race condition in case two threads use it concurrently.

That can't happen, because we have :

    private static final Builder BUILDER_INSTANCE = new Builder();

that is guaranteed to be built during the class loading.

-- 
Emmanuel Lecharny

Symas.com
directory.apache.org


Re: Immutable SyntexCheckers

Posted by Stefan Seelmann <ma...@stefan-seelmann.de>.
On 03/20/2017 02:10 AM, Emmanuel L�charny wrote:
> public class BooleanSyntaxChecker extends SyntaxChecker
> {
>     /**
>      * A static instance of BooleanSyntaxChecker
>      */
>     public static final BooleanSyntaxChecker INSTANCE = new
> BooleanSyntaxChecker( SchemaConstants.BOOLEAN_SYNTAX );
>    
>     /** A static instance of the builder */
>     private static final Builder BUILDER_INSTANCE = new Builder();

>     /**
>      * @return An instance of the Builder for this class
>      */
>     public static Builder builder()
>     {
>         return BUILDER_INSTANCE;
>     }

Hm, why a static builder? As it is not immutable there's a chance of
race condition in case two threads use it concurrently.


Re: Immutable SyntexCheckers

Posted by Emmanuel Lécharny <el...@gmail.com>.

Le 19/03/2017 à 17:47, Emmanuel Lecharny a écrit :
> Thanks, Stefan.
>
> I'll have a look at generics tonite.
>
> Regarding the need of a builder for each class, I think it would help
> habing a consistent system across all the SCs.
>
> Otherwise, this idea should apply to the other schema objects.
>
>
> Le dim. 19 mars 2017 à 10:58, Stefan Seelmann <ma...@stefan-seelmann.de> a
> écrit :
>
>> On 03/17/2017 11:00 AM, Emmanuel Lécharny wrote:
>> > The only requirement is that each SC has its own builder (we can't put
>> > this code in the abstract SC, because it's abstract...)
>
>> With some generics magic it should at least be possible to move common
>> stuff (setOid) to the parent.

I come with that :

in SyntaxChecker base class :

   
public abstract class SyntaxChecker extends LoadableSchemaObject
{
    ...
    /**
     * A static Builder for this class
     */
    public abstract static class SCBuilder<SC>
    {
        /** The SyntaxChecker OID */
        protected String oid;
       
        /**
         * The Builder constructor
         */
        protected SCBuilder( String oid )
        {
            this.oid = oid;
        }
       
       
        /**
         * Set the SyntaxChecker's OID
         *
         * @param oid The OID
         * @return The Builder's Instance
         */
        public SCBuilder<SC> setOid( String oid )
        {
            this.oid = oid;
           
            return this;
        }
       
        public abstract SC build();
    }


In BooleanSyntaxChecker inherited class :

public class BooleanSyntaxChecker extends SyntaxChecker
{
    /**
     * A static instance of BooleanSyntaxChecker
     */
    public static final BooleanSyntaxChecker INSTANCE = new
BooleanSyntaxChecker( SchemaConstants.BOOLEAN_SYNTAX );
   
    /** A static instance of the builder */
    private static final Builder BUILDER_INSTANCE = new Builder();
   
    /**
     * A static Builder for this class
     */
    public static class Builder extends SCBuilder<BooleanSyntaxChecker>
    {
        /**
         * The Builder constructor
         */
        private Builder()
        {
            super( SchemaConstants.BOOLEAN_SYNTAX );
        }
       
       
        /**
         * Create a new instance of BooleanSyntaxChecker
         * @return A new instance of BooleanSyntaxChecker
         */
        @Override
        public BooleanSyntaxChecker build()
        {
            return new BooleanSyntaxChecker( oid );
        }
    }

   
    /**
     * Creates a new instance of BooleanSyntaxChecker.
     */
    private BooleanSyntaxChecker( String oid )
    {
        super( oid );
    }

   
    /**
     * @return An instance of the Builder for this class
     */
    public static Builder builder()
    {
        return BUILDER_INSTANCE;
    }

-- 
Emmanuel Lecharny

Symas.com
directory.apache.org


Re: Immutable SyntexCheckers

Posted by Emmanuel Lecharny <el...@apache.org>.
Thanks, Stefan.

I'll have a look at generics tonite.

Regarding the need of a builder for each class, I think it would help
habing a consistent system across all the SCs.

Otherwise, this idea should apply to the other schema objects.


Le dim. 19 mars 2017 à 10:58, Stefan Seelmann <ma...@stefan-seelmann.de> a
écrit :

> On 03/17/2017 11:00 AM, Emmanuel Lécharny wrote:
> > The only requirement is that each SC has its own builder (we can't put
> > this code in the abstract SC, because it's abstract...)
>
> With some generics magic it should at least be possible to move common
> stuff (setOid) to the parent.
>
> Otherwise, I'm not sure if it's worth to create builders for all the
> "simple" syntax checkers (Boolean, DirectoryString, etc.) or if just a
> singleton immutable instance is sufficient.
>
> But in general it makes sense to have immutable syntax checkers :)
>
>
> --
Regards,
Cordialement,
Emmanuel Lécharny
www.iktek.com

Re: Immutable SyntexCheckers

Posted by Stefan Seelmann <ma...@stefan-seelmann.de>.
On 03/17/2017 11:00 AM, Emmanuel L�charny wrote:
> The only requirement is that each SC has its own builder (we can't put
> this code in the abstract SC, because it's abstract...)

With some generics magic it should at least be possible to move common
stuff (setOid) to the parent.

Otherwise, I'm not sure if it's worth to create builders for all the
"simple" syntax checkers (Boolean, DirectoryString, etc.) or if just a
singleton immutable instance is sufficient.

But in general it makes sense to have immutable syntax checkers :)