Help! I Programmatically Created a Resource in my Project, but My Code Doesn’t Find It!
Let’s say that you wanted to test the existence of a file you programmatically created in your project. While there are a lot of situations where you might create an IResource on the fly, creating a file for use in a test is not a bad example.
The test code could look like this (TestUtilities is a convenience class that does things for me):
...
import org.junit.Assert;
...
@Test
public void testFileExistsTrue() throws IOException, CoreException {
_project = TestUtilities.createProject("x"); //$NON-NLS-1$
TestUtilities.createTemplateFileInProjectAt(_project, CustomSchemaSupport.SCHEMA_FOLDER_NAME, CustomSchemaSupport.FILENAME);
boolean actual = _customSchemaSupport.fileExists(_project);
try {
Assert.assertTrue(actual);
} finally {
// always do this before returning
_project.delete(true, null);
}
}
TestUtilities.createTemplateFileInProjectAt() does the following (I have added additional comments):
public static void createTemplateFileInProjectAt(IProject project, String relativePath, String filename) throws IOException {
// Get the path where the file should be created from a properties file.
String templateFilePath = NewWizardMessages.WizardSchemaNewFileCreationPage_Schema_Template_Location;
InputStream inputStream = null;
// Read a template file contained in my plugin
inputStream = Activator.getDefault().getBundle().getEntry(templateFilePath).openStream();
// Wrap the above inputStream in a BufferedReader for better performance
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
// Create the output file
File tempFile = new File(getAbsolutePathToProject(project) + File.separator + relativePath + File.separator + CustomSchemaSupport.FILENAME);
FileWriter fileWriter = new FileWriter(tempFile);
// Write the template file to the output file
String line = null;
while((line = bufferedReader.readLine()) != null) {
fileWriter.write(line);
}
fileWriter.close();
}
The test code method testFileExistsTrue() fails even though the file exists. I know because I debugged this for over an hour.
Since I am such a wet blanket I will tell you what the problem was: after creating the file in TestUtilities.createTemplateFileInProjectAt() the project has to be refreshed or the new file will never be found. Add the following after your resource creation code:
project.refreshLocal(IResource.DEPTH_INFINITE, null); // You could also use IResource.DEPTH_ONE or IResource.DEPTH_NONE as well.
So in TestUtilities.createTemplateFileInProjectAt() the code above would go at after the creation of the file (don’t forget the CoreException declaration in the method signature as well):
public static void createTemplateFileInProjectAt(IProject project, String relativePath, String filename) throws IOException, CoreException {
String templateFilePath = NewWizardMessages.WizardSchemaNewFileCreationPage_Schema_Template_Location;
InputStream inputStream = null;
inputStream = Activator.getDefault().getBundle().getEntry(templateFilePath).openStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
File tempFile = new File(getAbsolutePathToProject(project) + File.separator + relativePath + File.separator + CustomSchemaSupport.FILENAME);
FileWriter fileWriter = new FileWriter(tempFile);
String line = null;
while((line = bufferedReader.readLine()) != null) {
fileWriter.write(line);
}
fileWriter.close();
// Add this to make the file discoverable.
project.refreshLocal(IResource.DEPTH_INFINITE, null);
}
Creating the file without the use of the Eclipse API effectively makes the file invisible until you tell the system to refresh its view of the underlying file system.
Another way to do this (with much thanks to Raphael Chaves) is to work with Eclipse and just use the IFile.create() method which keeps the view of the file system in sync with Eclipse. In addition to avoiding the need to force a manual refresh, the code becomes much simpler:
public static void createTemplateFileInProjectAt(IProject project, String relativePath, String filename) throws IOException, CoreException {
String templateFilePath = NewWizardMessages.WizardSchemaNewFileCreationPage_Schema_Template_Location;
InputStream inputStream = null;
inputStream = Activator.getDefault().getBundle().getEntry(templateFilePath).openStream();
IFolder folder = project.getFolder(SCHEMA_FOLDER_NAME);
IFile file = folder.getFile(FILENAME);
file.create(inputStream, false, null); // false means don't create the file if the file already exists
}
Not as much fun, but then eating your vegetables never is.
Woo hoo. Happy New Year!
Calling refreshLocal is only necessary if you are creating files directly in the file system (for instance, because your code is not Eclipse-aware). Alternatively, you could have used the resources API directly (IFile.setContents).
(or in the case you are creating the file, IFile.create). See http://publib.boulder.ibm.com/infocenter/rsahelp/v7r0m0/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/resources/IFile.html
First you should not swallow exceptions:
} catch (IOException e) {
// send back null
}
Ah! I love a new year with challenges! Before I respond directly take into account a couple of things:
- the above was test code
- something else, but I can’t remember it right now
Rafael: A pleasure to meet you! I have not had the need to write across the network so I cannot speak to doing anything else, but writing locally. Since I am writing the file locally (I am using FileWriter using the absolute path of the project as my guide) the call to refreshLocal() makes sense.
I do like the idea of creating the file using IFile.create() instead of my rather crude use of FileWriter. In fact, I like it so much that I will probably update the above with that code instead.
Elke: A pleasure to meet you as well.
About exceptions: they are, well, exceptional. That means that how you handle them depends on how you use them. What you should have gotten me on, but now I’ve fixed so you can’t take credit for it, is that since I was already throwing an IOException I don’t actually need the try/catch in that case. The exception should just bubble up so that JUnit displays the stack trace rather than me try to second guess what just happened. I actually always do that, but I guess I had too much eggnog. Or beer. I don’t remember.
Or perhaps that is what you meant. You appear to be a man of very few words.
Thank you both for your comments! Happy New Year!
I’m creating my eclipse plugin, and I’m using your blog as a cookbook/reference, very helpful info!.
Keep going man.
Cheers!