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