Here's a method pulled from one of my projects. It get's the folder that the jar file is located in as opposed to the directory if was run from if invoked on the command line.
/**
* Retrieve a File representation of the folder this application is
* located in.
*
* @return
*/
private static File getApplicationRootFolder()
{
String path = FileGetter.class.getProtectionDomain().getCodeSource()
.getLocation().getPath();
try
{
String decodedPath = URLDecoder.decode(path, "UTF-8");
File jarParentFolder = new File(decodedPath).getParentFile();
if (jarParentFolder.exists() && jarParentFolder.canRead()
{
File shellScript = new File(jarParentFolder, "import.sh")
}
catch (UnsupportedEncodingException e)
{
Main.myLog.error(TAG, "Unencoding jar path failed on:\n\t" + path);
e.printStackTrace();
return null;
}
}
You can then use that directory to make a File object for your shell script File shellScript = new File(getApplicationRootFolder(), scriptFilename);
EDIT: Follow up questions to try to help you out and a solution
So you want to be able to access one file that has three locations depending on when/where you code is run. This is how I see those cases:
Case 1: Running directly from Eclipse (unpackaged code):
shell script: X:/Java/exampleProject/bin/import.sh
class file: X:/Java/exampleProject/bin/gqqnbig/YourClass.class
Case 2: Running the packaged jar (shell script inside):
shell script: X:/Java/YourJar.jar/bin/import.sh
class file: X:/Java/YourJar.jar/bin/gqqnbig/YourClass.class
Case 3: Running the packaged jar (shell script external):
shell script: X:/Java/import.sh
class file: X:/Java/YourJar.jar/bin/gqqnbig/YourClass.class
What I think you need to do is prioritise the order you look at these locations and fall back to the next one in line if the shell script isn't found. I'd guess you want:
1. external to jar
2. inside packaged jar
3. unpackaged
So to access these you will need to write each separately and move through each until you get File.exists() == true
.
Something like what follows. Note I didn't test this and there are likely errors. I'll leave you to sort them out. My code is based on the assumptions made above, again I'll leave you to modify the code based on any incorrect guesses.
So here's a class with one public method taking a filename argument and returning an InputStream. I opted for InputStream in all cases as once you package up your jar you cannot access the resources as File objects any more, only Streams.
public class FileGetter
{
private static String RESOURCE_DIRECTORY = "bin";
/**
* Retrieve an InputStream for a resource file.
*
* @param filename
* @return
*/
public InputStream getResourceFileStream(String filename)
{
// this is where you decide your preference or the priority of the locations
InputStream inputStream = null;
inputStream = getExternalFile(filename);
if (inputStream != null)
{
return inputStream;
}
inputStream = getInternalPackagedFile(filename);
if (inputStream != null)
{
return inputStream;
}
inputStream = getInternalUnpackagedFile(filename);
if (inputStream != null)
{
return inputStream;
}
// couldn't find the file anywhere so log some error or throw an exception
return null;
}
/**
* Retrieve an InputStream for a file located outside your Jar
*
* @param filename
* @return
*/
private static InputStream getExternalFile(String filename)
{
// get the jar's absolute location on disk (regardless of current 'working directory')
String appRootPath = FileGetter.class.getProtectionDomain().getCodeSource()
.getLocation().getPath();
try
{
String decodedPath = URLDecoder.decode(appRootPath, "UTF-8");
File jarfile = new File(decodedPath);
File parentDirectory = jarfile.getParentFile();
if (testExists(parentDirectory))
{
File shellScript = new File(parentDirectory, filename);
if (testExists(shellScript))
{
return new FileInputStream(shellScript);
}
}
}
catch (UnsupportedEncodingException e)
{}
catch (NullPointerException e)
{}
catch (FileNotFoundException e)
{}
// if any part fails return null
return null;
}
/**
* Retrieve an InputStream for a file located inside your Jar.
*
* @param filename
* @return
*/
private static InputStream getInternalPackagedFile(String filename)
{
// root directory is defined as the jar's root so we start with a "/".
URL resUrl = FileGetter.class.getResource(File.separator + RESOURCE_DIRECTORY
+ File.separator + filename);
String badPath = resUrl.getPath();
String goodPath = badPath.substring(badPath.indexOf("!") + 1);
InputStream input = FileGetter.class.getResourceAsStream(goodPath);
// returns null if nothing there so just
return input;
}
private static InputStream getInternalUnpackagedFile(String filename)
{
// eclipse will 'cd' to the code's directory so we use relative paths
File shellScriptFile = new File(RESOURCE_DIRECTORY + File.separator + filename);
if (testExists(shellScriptFile))
{
try
{
InputStream shellScriptStream = new FileInputStream(shellScriptFile);
if (shellScriptStream != null)
{
return shellScriptStream;
}
}
catch (FileNotFoundException e)
{}
}
return null;
}
/**
* Test that a file exists and can be read.
*
* @param file
* @return
*/
private static boolean testExists(File file)
{
return file != null && file.exists() && file.canRead();
}
}
But with all that being said a better way to sort this would be to ensure that the file exists on disk and create it if not found. Then execute the script from disk.