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.extractor.impl;
021
022
023import java.io.BufferedReader;
024import java.io.File;
025import java.io.IOException;
026import java.io.InputStream;
027import java.io.InputStreamReader;
028import java.net.URL;
029import java.util.Enumeration;
030import java.util.HashMap;
031import java.util.Map;
032import java.util.regex.Pattern;
033import java.util.zip.ZipEntry;
034import java.util.zip.ZipFile;
035
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039
040/**
041 * Lists LDIF resources available from the classpath.
042 *
043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044 */
045public final class ResourceMap
046{
047    /** the system property which can be used to load schema from a user specified
048     *  resource like a absolute path to a directory or jar file.
049     *  This is useful to start embedded DirectoryService in a servlet container environment
050     *
051     *  usage: -Dschema.resource.location=/tmp/schema
052     *                OR
053     *         -Dschema.resource.location=/tmp/api-ldap-schema-1.0.0-M13.jar
054     *  */
055    private static final String SCHEMA_RESOURCE_LOCATION = "schema.resource.location";
056
057    /** The logger. */
058    private static final Logger LOG = LoggerFactory.getLogger( ResourceMap.class );
059
060
061    /**
062     * Private constructor.
063     */
064    private ResourceMap()
065    {
066    }
067
068
069    /**
070     * For all elements of java.class.path OR from the resource name set in the
071     * system property 'schema.resource.location' get a Map of resources
072     * Pattern pattern = Pattern.compile(".*").
073     * The keys represent resource names and the boolean parameter indicates
074     * whether or not the resource is in a Jar file.
075     *
076     * @param pattern the pattern to match
077     * @return the resources with markers - true if resource is in Jar
078     */
079    public static Map<String, Boolean> getResources( Pattern pattern )
080    {
081        HashMap<String, Boolean> retval = new HashMap<>();
082
083        String schemaResourceLoc = System.getProperty( SCHEMA_RESOURCE_LOCATION, "" );
084
085        if ( schemaResourceLoc.trim().length() > 0 )
086        {
087            LOG.debug( "loading from the user provider schema resource {}", schemaResourceLoc );
088
089            File file = new File( schemaResourceLoc );
090
091            if ( file.exists() )
092            {
093                getResources( retval, schemaResourceLoc, pattern );
094            }
095            else
096            {
097                LOG.error( "unable to load schema from the given resource value {}", schemaResourceLoc );
098            }
099        }
100        else
101        {
102            getResourcesFromClassloader( retval, pattern );
103        }
104
105        return retval;
106    }
107
108
109    private static void getResources( HashMap<String, Boolean> map,
110        String element, Pattern pattern )
111    {
112        File file = new File( element );
113
114        if ( !file.exists() )
115        {
116            // this may happen if the class path contains an element that doesn't exist
117            LOG.debug( "element {} does not exist", element );
118
119            return;
120        }
121
122        if ( file.isDirectory() )
123        {
124            getResourcesFromDirectory( map, file, pattern );
125        }
126        else
127        {
128            getResourcesFromJarFile( map, file, pattern );
129        }
130    }
131
132
133    private static void getResourcesFromJarFile( HashMap<String, Boolean> map,
134        File file, Pattern pattern )
135    {
136        ZipFile zf;
137
138        try
139        {
140            zf = new ZipFile( file );
141        }
142        catch ( IOException e )
143        {
144            throw new Error( e );
145        }
146
147        Enumeration<? extends ZipEntry> e = zf.entries();
148
149        while ( e.hasMoreElements() )
150        {
151            ZipEntry ze = e.nextElement();
152            String fileName = ze.getName();
153            boolean accept = pattern.matcher( fileName ).matches();
154
155            if ( accept )
156            {
157                map.put( fileName, Boolean.TRUE );
158            }
159        }
160        try
161        {
162            zf.close();
163        }
164        catch ( IOException e1 )
165        {
166            throw new Error( e1 );
167        }
168    }
169
170
171    private static void getResourcesFromDirectory(
172        HashMap<String, Boolean> map, File directory, Pattern pattern )
173    {
174        File[] fileList = directory.listFiles();
175
176        if ( fileList != null )
177        {
178            for ( File file : fileList )
179            {
180                if ( file.isDirectory() )
181                {
182                    getResourcesFromDirectory( map, file, pattern );
183                }
184                else
185                {
186                    try
187                    {
188                        String fileName = file.getCanonicalPath();
189                        boolean accept = pattern.matcher( fileName ).matches();
190    
191                        if ( accept )
192                        {
193                            map.put( fileName, Boolean.FALSE );
194                        }
195                    }
196                    catch ( IOException e )
197                    {
198                        LOG.error( "Cannot load file {} : {}", file.getAbsolutePath(), e.getMessage() );
199    
200                        // Continue...
201                    }
202                }
203            }
204        }
205    }
206
207
208    private static void getResourcesFromClassloader( HashMap<String, Boolean> map, Pattern pattern )
209    {
210        try
211        {
212            ClassLoader cl = ResourceMap.class.getClassLoader();
213            Enumeration<URL> indexes = cl.getResources( "META-INF/apacheds-schema.index" );
214
215            while ( indexes.hasMoreElements() )
216            {
217                URL index = null;
218
219                try
220                {
221                    index = indexes.nextElement();
222                    InputStream in = index.openStream();
223                    BufferedReader reader = new BufferedReader( new InputStreamReader( in, "UTF-8" ) );
224                    String line = reader.readLine();
225
226                    while ( line != null )
227                    {
228                        boolean accept = pattern.matcher( line ).matches();
229
230                        if ( accept )
231                        {
232                            map.put( line, Boolean.TRUE );
233                        }
234
235                        line = reader.readLine();
236                    }
237
238                    reader.close();
239                }
240                catch ( IOException ioe )
241                {
242                    LOG.debug( "Cannot load resource {} : {}", index, ioe.getMessage() );
243                    // Continue...
244                }
245            }
246        }
247        catch ( IOException e )
248        {
249            LOG.debug( "Error while loading  resuce from class loaded : {}", e.getMessage() );
250            throw new Error( e );
251        }
252    }
253}