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