Simple resources loader tool which help to find some file in classpath and load it's content with respect to sanitizing policies.

import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Tool for resources finding and data loading
*
* @author nikelin
* @group tools
*/
public class ResourcesLoader {
    final static Pattern NON_PRINTABLE = Pattern
            .compile("[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFC]");

    public File loadFile( String path ) throws IOException {
        return this.loadFile( path, true );
    }

    public File loadFile( String path, boolean searchPath ) throws IOException {
        File file = new File(path);
        if ( file.exists() ) {
            return file;
        }

        file = new File( Registry.getRootDirectory() + "/" + path);
        if ( searchPath && !file.exists() ) {
            file = this.find(path);
        }

        if ( file == null ) {
            throw new FileNotFoundException( path );
        }

        return file;
    }

    public String loadData( File file ) throws IOException {
        return this.loadData(file, false);
    }

    public String loadData( String path ) throws IOException {
        return this.loadData(  path, false );
    }

    public String loadData( String path, boolean escapeNonpritable ) throws IOException {
        return this.loadData( this.loadFile(path), escapeNonpritable );
    }

    public String loadData( File file, boolean escapeNonprintable ) throws IOException {
        FileInputStream stream = new FileInputStream(file);
        BufferedReader reader = new BufferedReader( new InputStreamReader(stream) );

        String result = new String();

        String line;
        while( null != ( line = reader.readLine() ) ) {
            result = result.concat(line) + "\n";
        }

        if ( escapeNonprintable ) {
            Matcher matcher = NON_PRINTABLE.matcher(result);
            if ( matcher.find() ) {
                result = matcher.replaceAll("\0");
            }
        }

        return result;
    }

    public InputStream loadResource( String path ) throws IOException {
        return new FileInputStream( this.loadFile(path) );
    }

    /**
     * @TODO загрузка из JAR-classpath элемента
     * @param path
     * @return
     * @throws FileNotFoundException
     */
    protected File find( String path ) throws FileNotFoundException {
        File candidateFile = null;
        
        for( String pathPart : System.getProperty("java.class.path").split(":") ) {
            candidateFile = new File( pathPart + File.separator + path );
            if ( !candidateFile.exists() ) {
                candidateFile = null;
            } else {
                break;
            }
        }

        return candidateFile;
    }

    public String[] getList( String path ) throws IOException {
        JarFile jarFile = new JarFile(path);
        Enumeration entries = jarFile.entries();
        List<String> targetEntries = new ArrayList<String>() ;
        while( entries.hasMoreElements() ) {
            JarEntry testing = (JarEntry) entries.nextElement();

            if ( testing.getName().endsWith(".class") )  {
                targetEntries.add( testing.getName() );
            }
        }

        return targetEntries.toArray( new String[targetEntries.size()] );
    }

    private void checkPrintable(CharSequence data) throws Exception {
        Matcher em = NON_PRINTABLE.matcher(data);
        if (em.find()) {
            throw new Exception(" special characters are not allowed");
        }
    }

}

Do you have a tool that will load a resource(say an image) from a jar file that contains code that is being loaded by a custom class loader. The jar file is NOT on the class path.

To read any image from some JAR archive you need to know image path in archive context and archive path.

Then you can do reading in a such way:

String imagePath = "images/path/image.bmp";
JarFile file = new JarFile( path );
            Enumeration entries = file.entries();
            URL target = null ;
            while( entries.hasMoreElements() ) {
                JarEntry testing = (JarEntry) entries.nextElement();
 
                if ( testing.getName().endsWith(imagePath) &&
                        !testing.isDirectory() ) {
                    target = new URL("jar", path + "!/",  testing.getName() );
                    break;
                }
            }

if ( target == null ) {
   System.out.println("Image not found");
   // break proceed
}

// for now you've got an image binary stream
InputStream stream = target.openStream();
// Processing logic

Otherway problem is to transform binary stream in a raster for image. You must look at java.awt.image API to deal with this problem. Also, because images can has different formats, it's probably Java Advanced Imaging API will be useful for you.

Cyril.

Edited 6 Years Ago by nikelin: n/a

One other thing about the requirement. The class file doesn't know the name/path of the jar file it is in.

When I run code from comment (but load xml file) i have malformedURLexeption thrown, when
InputStream stream = target.openStream(); executes
When i debug target looks like:
jar://[C:\work\name.jar!/]file.xml

do you know what may cause this problem?

Because of "C:\" presents in URI, where "C:" recognized as protocol part. I really don't test this case on Windows-like platforms, so I can't give 100% right solution for problem you have.

But try to investigate on:
1. Is ":" on "C:" escaping help?
2. Is removing square brackets ( "[", "]" ) from URI help?

Edited 5 Years Ago by nikelin: n/a

square brackets are added automaticly when URL is created,
error on console looks exacli like

java.net.MalformedURLException: no ! found in url

escaping ':' for temporarty hardcoded path doesn't help

square brackets are added automaticly when URL is created,
error on console looks exacli like

java.net.MalformedURLException: no ! found in url

escaping ':' for temporarty hardcoded path doesn't help

Problem solved, there was error in URL constructor, host should be empty when You load from file, in my case it looks like:

target = new URL("jar", "", "file:" + fileName + "!/" + testing.getName());

> Here is latest version of loader: https://github.com/nikelin/Redshape-...cesLoader.java

A few observations:

  • Line 20; don't work with raw string paths. As soon as the user passes in a root directory, create a File object out of it or better yet ask the user to pass in a "real" directory in the form of a File object rather than a "string" path.
  • Line 36; this will fail on Windows platform since the path separator is not ":" but ";". That line can be better written as: System.getProperty("java.class.path").split(System.getProperty("path.separator"))
  • Line 41; similarly for "searchPath"; don't work with raw strings treating them as path.
  • Line 67; "/" is a path separator on *nix platforms while on windows it is "\". That line can be better written as: File someFile = new File(rootFile, fileNameString)
  • Line 87, loadData method; you don't close the input stream! Plus, if your aim is to read textual data line by line, use a Scanner instead. Scanner in = new Scanner(myFile); . Strings in Java are immutable and hence the concat() on line 95 will end up creating new String objects each time a concat is done. Better use a StringBuilder for incrementally creating strings. Don't manually append "\n" since the EOL (end of line) character is different for *nix, Win and Mac. You need to use the system property, line.separator.
  • Line 101; why "\0"? If you want to ignore non-printable characters, why not "" (a blank string)?

Throughout the code, you also need to verify that the user provided path is a directory and if not act accordingly though this is more of a "validation" part.

I guess that's it from a casual glance at your source code! :-)

One other thing about the requirement. The class file doesn't know the name/path of the jar file it is in.

Here's a way to do it - I found this ages ago, so I can't remember where I saw it, but it works for me...

File jarFile = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI());