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 */
020
021package org.apache.directory.server.core.shared;
022
023
024import java.time.Duration;
025
026import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
027import org.apache.directory.api.ldap.model.name.Dn;
028import org.apache.directory.api.ldap.model.schema.SchemaManager;
029import org.apache.directory.server.core.api.DnFactory;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033import com.github.benmanes.caffeine.cache.Cache;
034import com.github.benmanes.caffeine.cache.Caffeine;
035
036
037/**
038 * The default Dn factory implementation.
039 *
040 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
041 */
042public class DefaultDnFactory implements DnFactory
043{
044    private static final Logger LOG = LoggerFactory.getLogger( DefaultDnFactory.class );
045
046    /** The cache for DNs */
047    private Cache<String, Dn> dnCache;
048
049    /** The schema manager */
050    private SchemaManager schemaManager;
051
052    /** Flag to enable stats */
053    private boolean enableStats = false;
054
055    // stat counters
056    private int hitCount = 0;
057    private int missCount = 0;
058
059
060    /**
061     * Instantiates a new default Dn factory.
062     *
063     * @param schemaManager The SchemaManager instance
064     * @param cacheSize The cache size used to store DNs
065     */
066    public DefaultDnFactory( SchemaManager schemaManager, int cacheSize )
067    {
068        this.schemaManager = schemaManager;
069        this.dnCache = Caffeine.newBuilder().maximumSize( cacheSize ).expireAfterAccess( Duration.ofMinutes( 10L ) )
070            .build();
071    }
072
073
074    /**
075     * {@inheritDoc}
076     */
077    @Override
078    public Dn create( String dn ) throws LdapInvalidDnException
079    {
080        if ( dn == null )
081        {
082            return null;
083        }
084
085        if ( dn.trim().length() == 0 )
086        {
087            return Dn.ROOT_DSE;
088        }
089
090        Dn cachedDn = null;
091
092        // read the explanation at the above DN_CACHE variable declaration
093        // for the reason for performing this check
094        if ( dnCache != null )
095        {
096            cachedDn = dnCache.getIfPresent( dn );
097        }
098
099        if ( cachedDn == null )
100        {
101            LOG.debug( "Dn {} not found in the cache, creating", dn );
102
103            cachedDn = new Dn( schemaManager, dn );
104
105            if ( dnCache != null )
106            {
107                dnCache.put( dn, cachedDn );
108            }
109
110            if ( enableStats )
111            {
112                missCount++;
113            }
114        }
115        else
116        {
117            if ( enableStats )
118            {
119                hitCount++;
120            }
121        }
122
123        LOG.debug( "Dn {} found in the cache", dn );
124
125        if ( enableStats )
126        {
127            LOG.debug( "Dn cache hit - {} , miss - {} and is normalized = {}", hitCount, missCount, cachedDn.isSchemaAware() );
128        }
129
130        return cachedDn;
131    }
132
133
134    /**
135     * {@inheritDoc}
136     */
137    @Override
138    public Dn create( String... upRdns ) throws LdapInvalidDnException
139    {
140        StringBuilder sb = new StringBuilder();
141        boolean isFirst = true;
142        
143        for ( String s : upRdns )
144        {
145            if ( isFirst )
146            {
147                isFirst = false;
148            }
149            else
150            {
151                sb.append( ',' );
152            }
153            
154            sb.append( s );
155        }
156
157        String dn = sb.toString();
158        
159        return create( dn );
160    }
161
162}