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 */
020package org.apache.directory.api.ldap.schema.loader;
021
022
023import java.io.File;
024import java.io.IOException;
025import java.io.InputStream;
026import java.net.URL;
027import java.util.ArrayList;
028import java.util.List;
029import java.util.Map;
030import java.util.regex.Pattern;
031
032import org.apache.directory.api.i18n.I18n;
033import org.apache.directory.api.ldap.model.constants.SchemaConstants;
034import org.apache.directory.api.ldap.model.entry.Entry;
035import org.apache.directory.api.ldap.model.exception.LdapException;
036import org.apache.directory.api.ldap.model.ldif.LdifEntry;
037import org.apache.directory.api.ldap.model.ldif.LdifReader;
038import org.apache.directory.api.ldap.model.schema.registries.AbstractSchemaLoader;
039import org.apache.directory.api.ldap.model.schema.registries.Schema;
040import org.apache.directory.api.ldap.schema.extractor.impl.DefaultSchemaLdifExtractor;
041import org.apache.directory.api.ldap.schema.extractor.impl.ResourceMap;
042import org.apache.directory.api.util.Strings;
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045
046
047/**
048 * Loads schema data from LDIF files containing entries representing schema
049 * objects, using the meta schema format.
050 * 
051 * This class is used only for tests.
052 *
053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054 */
055public class JarLdifSchemaLoader extends AbstractSchemaLoader
056{
057    /**
058     * Filesystem path separator pattern, either forward slash or backslash.
059     * java.util.regex.Pattern is immutable so only one instance is needed for all uses.
060     */
061    private static final String SEPARATOR_PATTERN = "[/\\Q\\\\E]";
062
063    /** ldif file extension used */
064    private static final String LDIF_EXT = "ldif";
065
066    /** static class logger */
067    private static final Logger LOG = LoggerFactory.getLogger( JarLdifSchemaLoader.class );
068
069    /** Speedup for DEBUG mode */
070    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
071
072    /** a map of all the resources in this jar */
073    private static final Map<String, Boolean> RESOURCE_MAP = ResourceMap.getResources( Pattern
074        .compile( "schema" + SEPARATOR_PATTERN + "ou=schema.*" ) );
075
076
077    /**
078     * Creates a new LDIF based SchemaLoader. The constructor checks to make
079     * sure the supplied base directory exists and contains a schema.ldif file
080     * and if not complains about it.
081     *
082     * @throws LdapException if the base directory does not exist or does not
083     * a valid schema.ldif file
084     * @throws IOException If we can't load the schema
085     */
086    public JarLdifSchemaLoader() throws IOException, LdapException
087    {
088        initializeSchemas();
089    }
090
091
092    private URL getResource( String resource, String msg ) throws IOException
093    {
094        if ( RESOURCE_MAP.get( resource ) )
095        {
096            return DefaultSchemaLdifExtractor.getUniqueResource( resource, msg );
097        }
098        else
099        {
100            return new File( resource ).toURI().toURL();
101        }
102    }
103
104
105    /**
106     * Scans for LDIF files just describing the various schema contained in
107     * the schema repository.
108     *
109     * @throws LdapException
110     */
111    private void initializeSchemas() throws IOException, LdapException
112    {
113        if ( IS_DEBUG )
114        {
115            LOG.debug( "Initializing schema" );
116        }
117
118        Pattern pat = Pattern.compile( "schema" + SEPARATOR_PATTERN + "ou=schema"
119            + SEPARATOR_PATTERN + "cn=[a-z0-9-_]*\\." + LDIF_EXT );
120
121        for ( String file : RESOURCE_MAP.keySet() )
122        {
123            if ( pat.matcher( file ).matches() )
124            {
125                URL resource = getResource( file, "schema LDIF file" );
126                InputStream in = resource.openStream();
127
128                try
129                {
130                    LdifReader reader = new LdifReader( in );
131                    LdifEntry entry = reader.next();
132                    reader.close();
133                    Schema schema = getSchema( entry.getEntry() );
134                    schemaMap.put( schema.getSchemaName(), schema );
135
136                    if ( IS_DEBUG )
137                    {
138                        LOG.debug( "Schema Initialized ... \n{}", schema );
139                    }
140                }
141                catch ( LdapException le )
142                {
143                    LOG.error( I18n.err( I18n.ERR_10003, file ), le );
144                    throw le;
145                }
146                finally
147                {
148                    in.close();
149                }
150            }
151        }
152    }
153
154
155    /**
156     * Utility method to get a regex.Pattern fragment for the path for a schema directory.
157     *
158     * @param schema the schema to get the path for
159     * @return the regex.Pattern fragment for the path for the specified schema directory
160     */
161    private String getSchemaDirectoryString( Schema schema )
162    {
163        return "schema" + "/" + "ou=schema" + "/"
164            + "cn=" + Strings.lowerCase( schema.getSchemaName() ) + "/";
165    }
166
167
168    /**
169     * {@inheritDoc}
170     */
171    @Override
172    public List<Entry> loadComparators( Schema... schemas ) throws LdapException, IOException
173    {
174        List<Entry> comparatorList = new ArrayList<>();
175
176        if ( schemas == null )
177        {
178            return comparatorList;
179        }
180
181        for ( Schema schema : schemas )
182        {
183            String start = getSchemaDirectoryString( schema )
184                + SchemaConstants.COMPARATORS_PATH + "/" + "m-oid=";
185            String end = "." + LDIF_EXT;
186
187            for ( String resourcePath : RESOURCE_MAP.keySet() )
188            {
189                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
190                {
191                    URL resource = getResource( resourcePath, "comparator LDIF file" );
192                    LdifReader reader = new LdifReader( resource.openStream() );
193                    LdifEntry entry = reader.next();
194                    reader.close();
195
196                    comparatorList.add( entry.getEntry() );
197                }
198            }
199        }
200
201        return comparatorList;
202    }
203
204
205    /**
206     * {@inheritDoc}
207     */
208    @Override
209    public List<Entry> loadSyntaxCheckers( Schema... schemas ) throws LdapException, IOException
210    {
211        List<Entry> syntaxCheckerList = new ArrayList<>();
212
213        if ( schemas == null )
214        {
215            return syntaxCheckerList;
216        }
217
218        for ( Schema schema : schemas )
219        {
220            String start = getSchemaDirectoryString( schema )
221                + SchemaConstants.SYNTAX_CHECKERS_PATH + "/" + "m-oid=";
222            String end = "." + LDIF_EXT;
223
224            for ( String resourcePath : RESOURCE_MAP.keySet() )
225            {
226                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
227                {
228                    URL resource = getResource( resourcePath, "syntaxChecker LDIF file" );
229                    LdifReader reader = new LdifReader( resource.openStream() );
230                    LdifEntry entry = reader.next();
231                    reader.close();
232
233                    syntaxCheckerList.add( entry.getEntry() );
234                }
235            }
236        }
237
238        return syntaxCheckerList;
239    }
240
241
242    /**
243     * {@inheritDoc}
244     */
245    @Override
246    public List<Entry> loadNormalizers( Schema... schemas ) throws LdapException, IOException
247    {
248        List<Entry> normalizerList = new ArrayList<>();
249
250        if ( schemas == null )
251        {
252            return normalizerList;
253        }
254
255        for ( Schema schema : schemas )
256        {
257            String start = getSchemaDirectoryString( schema )
258                + SchemaConstants.NORMALIZERS_PATH + "/" + "m-oid=";
259            String end = "." + LDIF_EXT;
260
261            for ( String resourcePath : RESOURCE_MAP.keySet() )
262            {
263                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
264                {
265                    URL resource = getResource( resourcePath, "normalizer LDIF file" );
266                    LdifReader reader = new LdifReader( resource.openStream() );
267                    LdifEntry entry = reader.next();
268                    reader.close();
269
270                    normalizerList.add( entry.getEntry() );
271                }
272            }
273        }
274
275        return normalizerList;
276    }
277
278
279    /**
280     * {@inheritDoc}
281     */
282    @Override
283    public List<Entry> loadMatchingRules( Schema... schemas ) throws LdapException, IOException
284    {
285        List<Entry> matchingRuleList = new ArrayList<>();
286
287        if ( schemas == null )
288        {
289            return matchingRuleList;
290        }
291
292        for ( Schema schema : schemas )
293        {
294            String start = getSchemaDirectoryString( schema )
295                + SchemaConstants.MATCHING_RULES_PATH + "/" + "m-oid=";
296            String end = "." + LDIF_EXT;
297
298            for ( String resourcePath : RESOURCE_MAP.keySet() )
299            {
300                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
301                {
302                    URL resource = getResource( resourcePath, "matchingRules LDIF file" );
303                    LdifReader reader = new LdifReader( resource.openStream() );
304                    LdifEntry entry = reader.next();
305                    reader.close();
306
307                    matchingRuleList.add( entry.getEntry() );
308                }
309            }
310        }
311
312        return matchingRuleList;
313    }
314
315
316    /**
317     * {@inheritDoc}
318     */
319    @Override
320    public List<Entry> loadSyntaxes( Schema... schemas ) throws LdapException, IOException
321    {
322        List<Entry> syntaxList = new ArrayList<>();
323
324        if ( schemas == null )
325        {
326            return syntaxList;
327        }
328
329        for ( Schema schema : schemas )
330        {
331            String start = getSchemaDirectoryString( schema )
332                + SchemaConstants.SYNTAXES_PATH + "/" + "m-oid=";
333            String end = "." + LDIF_EXT;
334
335            for ( String resourcePath : RESOURCE_MAP.keySet() )
336            {
337                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
338                {
339                    URL resource = getResource( resourcePath, "syntax LDIF file" );
340                    LdifReader reader = new LdifReader( resource.openStream() );
341                    LdifEntry entry = reader.next();
342                    reader.close();
343
344                    syntaxList.add( entry.getEntry() );
345                }
346            }
347        }
348
349        return syntaxList;
350    }
351
352
353    /**
354     * {@inheritDoc}
355     */
356    @Override
357    public List<Entry> loadAttributeTypes( Schema... schemas ) throws LdapException, IOException
358    {
359        List<Entry> attributeTypeList = new ArrayList<>();
360
361        if ( schemas == null )
362        {
363            return attributeTypeList;
364        }
365
366        for ( Schema schema : schemas )
367        {
368            // check that the attributeTypes directory exists for the schema
369            String start = getSchemaDirectoryString( schema )
370                + SchemaConstants.ATTRIBUTE_TYPES_PATH + "/" + "m-oid=";
371            String end = "." + LDIF_EXT;
372
373            // get list of attributeType LDIF schema files in attributeTypes
374            for ( String resourcePath : RESOURCE_MAP.keySet() )
375            {
376                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
377                {
378                    URL resource = getResource( resourcePath, "attributeType LDIF file" );
379                    LdifReader reader = new LdifReader( resource.openStream() );
380                    LdifEntry entry = reader.next();
381                    reader.close();
382
383                    attributeTypeList.add( entry.getEntry() );
384                }
385            }
386        }
387
388        return attributeTypeList;
389    }
390
391
392    /**
393     * {@inheritDoc}
394     */
395    @Override
396    public List<Entry> loadMatchingRuleUses( Schema... schemas ) throws LdapException, IOException
397    {
398        List<Entry> matchingRuleUseList = new ArrayList<>();
399
400        if ( schemas == null )
401        {
402            return matchingRuleUseList;
403        }
404
405        for ( Schema schema : schemas )
406        {
407            String start = getSchemaDirectoryString( schema )
408                + SchemaConstants.MATCHING_RULE_USE_PATH + "/" + "m-oid=";
409            String end = "." + LDIF_EXT;
410
411            for ( String resourcePath : RESOURCE_MAP.keySet() )
412            {
413                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
414                {
415                    URL resource = getResource( resourcePath, "matchingRuleUse LDIF file" );
416                    LdifReader reader = new LdifReader( resource.openStream() );
417                    LdifEntry entry = reader.next();
418                    reader.close();
419
420                    matchingRuleUseList.add( entry.getEntry() );
421                }
422            }
423        }
424
425        return matchingRuleUseList;
426    }
427
428
429    /**
430     * {@inheritDoc}
431     */
432    @Override
433    public List<Entry> loadNameForms( Schema... schemas ) throws LdapException, IOException
434    {
435        List<Entry> nameFormList = new ArrayList<>();
436
437        if ( schemas == null )
438        {
439            return nameFormList;
440        }
441
442        for ( Schema schema : schemas )
443        {
444            String start = getSchemaDirectoryString( schema )
445                + SchemaConstants.NAME_FORMS_PATH + "/" + "m-oid=";
446            String end = "." + LDIF_EXT;
447
448            for ( String resourcePath : RESOURCE_MAP.keySet() )
449            {
450                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
451                {
452                    URL resource = getResource( resourcePath, "nameForm LDIF file" );
453                    LdifReader reader = new LdifReader( resource.openStream() );
454                    LdifEntry entry = reader.next();
455                    reader.close();
456
457                    nameFormList.add( entry.getEntry() );
458                }
459            }
460        }
461
462        return nameFormList;
463    }
464
465
466    /**
467     * {@inheritDoc}
468     */
469    @Override
470    public List<Entry> loadDitContentRules( Schema... schemas ) throws LdapException, IOException
471    {
472        List<Entry> ditContentRulesList = new ArrayList<>();
473
474        if ( schemas == null )
475        {
476            return ditContentRulesList;
477        }
478
479        for ( Schema schema : schemas )
480        {
481            String start = getSchemaDirectoryString( schema )
482                + SchemaConstants.DIT_CONTENT_RULES_PATH + "/" + "m-oid=";
483            String end = "." + LDIF_EXT;
484
485            for ( String resourcePath : RESOURCE_MAP.keySet() )
486            {
487                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
488                {
489                    URL resource = getResource( resourcePath, "ditContentRule LDIF file" );
490                    LdifReader reader = new LdifReader( resource.openStream() );
491                    LdifEntry entry = reader.next();
492                    reader.close();
493
494                    ditContentRulesList.add( entry.getEntry() );
495                }
496            }
497        }
498
499        return ditContentRulesList;
500    }
501
502
503    /**
504     * {@inheritDoc}
505     */
506    @Override
507    public List<Entry> loadDitStructureRules( Schema... schemas ) throws LdapException, IOException
508    {
509        List<Entry> ditStructureRuleList = new ArrayList<>();
510
511        if ( schemas == null )
512        {
513            return ditStructureRuleList;
514        }
515
516        for ( Schema schema : schemas )
517        {
518            String start = getSchemaDirectoryString( schema )
519                + SchemaConstants.DIT_STRUCTURE_RULES_PATH + "/" + "m-oid=";
520            String end = "." + LDIF_EXT;
521
522            for ( String resourcePath : RESOURCE_MAP.keySet() )
523            {
524                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
525                {
526                    URL resource = getResource( resourcePath, "ditStructureRule LDIF file" );
527                    LdifReader reader = new LdifReader( resource.openStream() );
528                    LdifEntry entry = reader.next();
529                    reader.close();
530
531                    ditStructureRuleList.add( entry.getEntry() );
532                }
533            }
534        }
535
536        return ditStructureRuleList;
537    }
538
539
540    /**
541     * {@inheritDoc}
542     */
543    @Override
544    public List<Entry> loadObjectClasses( Schema... schemas ) throws LdapException, IOException
545    {
546        List<Entry> objectClassList = new ArrayList<>();
547
548        if ( schemas == null )
549        {
550            return objectClassList;
551        }
552
553        for ( Schema schema : schemas )
554        {
555            // get objectClasses directory, check if exists, return if not
556            String start = getSchemaDirectoryString( schema )
557                + SchemaConstants.OBJECT_CLASSES_PATH + "/" + "m-oid=";
558            String end = "." + LDIF_EXT;
559
560            for ( String resourcePath : RESOURCE_MAP.keySet() )
561            {
562                if ( resourcePath.startsWith( start ) && resourcePath.endsWith( end ) )
563                {
564                    URL resource = getResource( resourcePath, "objectClass LDIF file" );
565                    LdifReader reader = new LdifReader( resource.openStream() );
566                    LdifEntry entry = reader.next();
567                    reader.close();
568
569                    objectClassList.add( entry.getEntry() );
570                }
571            }
572        }
573
574        return objectClassList;
575    }
576}