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