Brian Reinhold
Brian Reinhold

Reputation: 2455

How can I access a non-class file inside a jar FROM 'within' the jar

This should be simple but it has cost me hours. Everything I find on this site indicates I am doing it right but the file still cannot be found.

Inside a jar file I have two files 'CDAkeystore.jks' and 'CDAtruststore.jks' at top level. Yet when I call securityProps.setProperty("javax.net.ssl.keyStore","CDAkeystore.jks"); I get a system cannot find the file requested error. The class file calling this method is inside the same jar in the usual package arrangement.

The jar file is as follows:

com ..... (a lot more class files)
org ..... (lots of class files)
META-INF
CDAtruststore.jks
CDAkeystore.jks

How can this be SOOO difficult?!!

---------- Added INfo ------n

Since the object using the path is open source I found the routine they are using to load the file. It is:

InputStream keystoreInputStream = preBufferInputStream(new FileInputStream(keyStoreName));

which according to the documentation of FileInputStream(String name) is

Creates a FileInputStream by opening a connection to an actual file, the file named by the path name 'name' in the file system. So how should this path be expressed?

Upvotes: 2

Views: 1234

Answers (2)

Brian Reinhold
Brian Reinhold

Reputation: 2455

The answer is, in short, that you can't. At least in this situation. I am stuck with passing a path to a file to a library implementation that I have no control over. So the library method accesses the file on the assumption that the file exists in unzipped form in the OS's file system. It is getting the path from a Property.setProperty(stringKey, stringPath) So the only solution I found was an ugly hack. I need to take the resource in my jar and copy it to a file on the system. Then I would pass the path to that file in the above setProperty() method. The ugly hack is implemented as follows (if anyone else can come up with a nicer solution I would be happy). It does solve the problem. The library routine is able to find my newly created file.

/* This evil reads a file as a resource inside a jar and dumps the file where ever
 * the loader of this jar/application defines as the current directory. This pain is done
 * so the SecurityDomian class can load the file; it cannot access the file from
 * the jar. This means the 'name' passed in contains the file name inside the jar
 * prefixed with "/" so it is not read with an assumed package extension.
 */
private boolean createFileFromResource(String name)
{
    // Dont bother if the file already exists
    if(new File(name.replace("/", "")).exists())
    { 
        return true;
    }
    byte[] keystoreFile = new byte[2048];
    ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(2048);
    // Get the resource
    InputStream inputStream = this.getClass().getResourceAsStream(name);
    try
    {
        int bytesRead = 0;
        while(true)
        {
            // Read the resource into the buffer keystoreFile in 2048 byte chunks
            bytesRead = inputStream.read(keystoreFile);
            if(bytesRead < 0)
            {
                break;
            }
            // Copy and append the chunks to the ByteArrayOutputStream (this class
            // does the auto-extending of the output array as more chunks are 
            // added so you don't have to do it.
            byteArrayOut.write(keystoreFile, 0, bytesRead);
        }
        inputStream.close();
        // Now create a file at the root of where ever the loader happens to think
        // the root is. So remove the "/" in front of the file name
        FileOutputStream outputStream = new FileOutputStream(name.replace("/", ""));
        // Write out the file. Note you will be left with garbage at that location.
        byteArrayOut.writeTo(outputStream);
        outputStream.flush();
        outputStream.close();
    } 
    catch (IOException e)
    {
        e.printStackTrace();
        return false;
    }
    return true;
}

Upvotes: 0

AlexR
AlexR

Reputation: 115328

Use YourClass.class.getResourceAsStream() or this.getClass().getResourceAsStream(). You can also use class loader if you are in multiple class loaders environment.

Upvotes: 2

Related Questions