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.api.ldap.schema.loader;
022
023
024import java.io.IOException;
025import java.io.InputStream;
026import java.net.URL;
027import java.nio.file.Files;
028import java.nio.file.Paths;
029import java.util.ArrayList;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
033import java.util.regex.Pattern;
034
035import org.apache.directory.api.ldap.model.entry.Entry;
036import org.apache.directory.api.ldap.model.exception.LdapException;
037import org.apache.directory.api.ldap.model.ldif.LdifEntry;
038import org.apache.directory.api.ldap.model.ldif.LdifReader;
039import org.apache.directory.api.ldap.model.schema.registries.AbstractSchemaLoader;
040import org.apache.directory.api.ldap.model.schema.registries.Schema;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044
045/**
046 * A schema loader based on a single monolithic ldif file containing all the schema partition elements
047 * 
048 * Performs better than any other existing LDIF schema loaders. NOT DOCUMENTED atm
049 * 
050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
051 */
052public class SingleLdifSchemaLoader extends AbstractSchemaLoader
053{
054    /** 
055     * Pattern for start of schema Dn.
056     * java.util.regex.Pattern is immutable so only one instance is needed for all uses.
057     */
058    private static final Pattern SCHEMA_START_PATTERN = Pattern
059        .compile( "cn\\s*=\\s*[a-z0-9-_]*\\s*,\\s*ou\\s*=\\s*schema" );
060
061    /** The logger. */
062    private static final Logger LOG = LoggerFactory.getLogger( SingleLdifSchemaLoader.class );
063
064    /** The schema object Rdn attribute types. */
065    private String[] schemaObjectTypeRdns = new String[]
066        { "attributetypes", "comparators", "ditContentRules", "ditStructureRules", "matchingRules", "matchingRuleUse",
067            "nameForms", "normalizers", "objectClasses", "syntaxes", "syntaxCheckers" };
068
069    /** The map containing ... */
070    private Map<String, Map<String, List<Entry>>> scObjEntryMap = new HashMap<String, Map<String, List<Entry>>>();
071
072
073    /**
074     * Instantiates a new single LDIF schema loader.
075     */
076    public SingleLdifSchemaLoader()
077    {
078        try
079        {
080            URL resource = getClass().getClassLoader().getResource( "schema-all.ldif" );
081
082            LOG.debug( "URL of the all schema ldif file {}", resource );
083
084            for ( String s : schemaObjectTypeRdns )
085            {
086                scObjEntryMap.put( s, new HashMap<String, List<Entry>>() );
087            }
088
089            InputStream in = resource.openStream();
090
091            initializeSchemas( in );
092        }
093        catch ( LdapException | IOException e )
094        {
095            throw new RuntimeException( e );
096        }
097    }
098
099    
100    /**
101     * Instantiates a new single LDIF schema loader.
102     * 
103     * @param schemaFile The Schema to load
104     */
105    public SingleLdifSchemaLoader( String schemaFile )
106    {
107        try
108        {
109            for ( String s : schemaObjectTypeRdns )
110            {
111                scObjEntryMap.put( s, new HashMap<String, List<Entry>>() );
112            }
113
114            InputStream in = Files.newInputStream( Paths.get( schemaFile ) );
115
116            initializeSchemas( in );
117        }
118        catch ( LdapException | IOException e )
119        {
120            throw new RuntimeException( e );
121        }
122    }
123
124    
125    /**
126     * Instantiates a new single LDIF schema loader.
127     * 
128     * @param schemaUrl The URL of the schema to load
129     */
130    public SingleLdifSchemaLoader( URL schemaUrl )
131    {
132        try
133        {
134            for ( String s : schemaObjectTypeRdns )
135            {
136                scObjEntryMap.put( s, new HashMap<String, List<Entry>>() );
137            }
138
139            InputStream in = schemaUrl.openStream();
140
141            initializeSchemas( in );
142        }
143        catch ( LdapException | IOException e )
144        {
145            throw new RuntimeException( e );
146        }
147    }
148
149
150    /**
151     * Initialize the Schema object from a Single LDIF file
152     */
153    private void initializeSchemas( InputStream in ) throws LdapException, IOException
154    {
155        try ( LdifReader ldifReader = new LdifReader( in ) )
156        {
157            Schema currentSchema = null;
158    
159            while ( ldifReader.hasNext() )
160            {
161                LdifEntry ldifEntry = ldifReader.next();
162                String dn = ldifEntry.getDn().getName();
163                
164                if ( SCHEMA_START_PATTERN.matcher( dn ).matches() )
165                {
166                    Schema schema = getSchema( ldifEntry.getEntry() );
167                    schemaMap.put( schema.getSchemaName(), schema );
168                    currentSchema = schema;
169                }
170                else
171                {
172                    loadSchemaObject( currentSchema.getSchemaName(), ldifEntry );
173                }
174            }
175        }
176    }
177
178
179    /**
180     * Load all the schemaObjects
181     */
182    private void loadSchemaObject( String schemaName, LdifEntry ldifEntry ) throws LdapException
183    {
184        for ( String scObjTypeRdn : schemaObjectTypeRdns )
185        {
186            Pattern regex = Pattern.compile( "m-oid\\s*=\\s*[0-9\\.]*\\s*" + ",\\s*ou\\s*=\\s*" + scObjTypeRdn
187                + "\\s*,\\s*cn\\s*=\\s*" + schemaName
188                + "\\s*,\\s*ou=schema\\s*", Pattern.CASE_INSENSITIVE );
189
190            String dn = ldifEntry.getDn().getName();
191
192            if ( regex.matcher( dn ).matches() )
193            {
194                Map<String, List<Entry>> m = scObjEntryMap.get( scObjTypeRdn );
195                List<Entry> entryList = m.get( schemaName );
196                
197                if ( entryList == null )
198                {
199                    entryList = new ArrayList<Entry>();
200                    entryList.add( ldifEntry.getEntry() );
201                    m.put( schemaName, entryList );
202                }
203                else
204                {
205                    entryList.add( ldifEntry.getEntry() );
206                }
207
208                break;
209            }
210        }
211    }
212
213
214    private List<Entry> loadSchemaObjects( String schemaObjectType, Schema... schemas ) throws LdapException,
215        IOException
216    {
217        Map<String, List<Entry>> m = scObjEntryMap.get( schemaObjectType );
218        List<Entry> atList = new ArrayList<Entry>();
219
220        for ( Schema s : schemas )
221        {
222            List<Entry> preLoaded = m.get( s.getSchemaName() );
223            
224            if ( preLoaded != null )
225            {
226                atList.addAll( preLoaded );
227            }
228        }
229
230        return atList;
231    }
232
233
234    /**
235     * {@inheritDoc}
236     */
237    public List<Entry> loadAttributeTypes( Schema... schemas ) throws LdapException, IOException
238    {
239        return loadSchemaObjects( "attributetypes", schemas );
240    }
241
242
243    /**
244     * {@inheritDoc}
245     */
246    public List<Entry> loadComparators( Schema... schemas ) throws LdapException, IOException
247    {
248        return loadSchemaObjects( "comparators", schemas );
249    }
250
251
252    /**
253     * {@inheritDoc}
254     */
255    public List<Entry> loadDitContentRules( Schema... schemas ) throws LdapException, IOException
256    {
257        return loadSchemaObjects( "ditContentRules", schemas );
258    }
259
260
261    /**
262     * {@inheritDoc}
263     */
264    public List<Entry> loadDitStructureRules( Schema... schemas ) throws LdapException, IOException
265    {
266        return loadSchemaObjects( "ditStructureRules", schemas );
267    }
268
269
270    /**
271     * {@inheritDoc}
272     */
273    public List<Entry> loadMatchingRules( Schema... schemas ) throws LdapException, IOException
274    {
275        return loadSchemaObjects( "matchingRules", schemas );
276    }
277
278
279    /**
280     * {@inheritDoc}
281     */
282    public List<Entry> loadMatchingRuleUses( Schema... schemas ) throws LdapException, IOException
283    {
284        return loadSchemaObjects( "matchingRuleUse", schemas );
285    }
286
287
288    /**
289     * {@inheritDoc}
290     */
291    public List<Entry> loadNameForms( Schema... schemas ) throws LdapException, IOException
292    {
293        return loadSchemaObjects( "nameForms", schemas );
294    }
295
296
297    /**
298     * {@inheritDoc}
299     */
300    public List<Entry> loadNormalizers( Schema... schemas ) throws LdapException, IOException
301    {
302        return loadSchemaObjects( "normalizers", schemas );
303    }
304
305
306    /**
307     * {@inheritDoc}
308     */
309    public List<Entry> loadObjectClasses( Schema... schemas ) throws LdapException, IOException
310    {
311        return loadSchemaObjects( "objectClasses", schemas );
312    }
313
314
315    /**
316     * {@inheritDoc}
317     */
318    public List<Entry> loadSyntaxes( Schema... schemas ) throws LdapException, IOException
319    {
320        return loadSchemaObjects( "syntaxes", schemas );
321    }
322
323
324    /**
325     * {@inheritDoc}
326     */
327    public List<Entry> loadSyntaxCheckers( Schema... schemas ) throws LdapException, IOException
328    {
329        return loadSchemaObjects( "syntaxCheckers", schemas );
330    }
331
332}
333
334class SchemaMarker
335{
336    /** The start marker. */
337    private int start;
338
339    /** The end marker. */
340    private int end;
341
342
343    SchemaMarker( int start )
344    {
345        this.start = start;
346    }
347
348
349    public void setEnd( int end )
350    {
351        this.end = end;
352    }
353
354
355    public int getStart()
356    {
357        return start;
358    }
359
360
361    public int getEnd()
362    {
363        return end;
364    }
365}