Hello and thanks in advance. I'm relatively new here and thus far I've been able to solve any issues I've come across from searching but this one has me stumped.
I'm writing a program that uses Rest-Assured to make API calls to get the Profiles of different objects in a cloud server. These objects are organizational file containers that we can call folders. The folders have a specific structure with a specific profile of metadata for each one. What my program does is creates java objects from the JSON that is returned and stores them in a way that allows them to still work like a folder structure so they can be manipulated to do things like: print the folder structure, get subfolders of a specific folder, view name or ID of a specific folder, add or remove a folder, etc.
I've set this up so that each folder is an object that contains an ArrayList of its subfolders and variables for the metadata where the constructor will fill all of the variables based on the JSON.
After using my recursive method to call the API and add the subfolders where they're supposed to go, within the method inside the class I can see that they were added correctly, once the method exits, if I try to access the list from Main I get a Null Pointer Exception. See code samples below.
EDIT: I have added additional tests to the code below to check if the array index is null from within the recursive method. When the object is created, the constructor prints the folder ID and the index in the ArrayList using indexOf()
. When it returns from the constructor, in the same recursive method, getSingleFolder(index).getSubfolders().get(0).getSubfolders().get(0).toString
is still null even though this should be returning the same folder that was just created and added to the parent folders ArrayList (and verified) from within the constructor.
public void saveSubfoldersRecursively(Folder folder){
if (folder.hasSubfolders()){
getFolderSubfoldersJsonArrayFromRestApi(folder);
for(int x = 0; x < folder.getFoldersJsonArray().size(); x++) {
JsonObject currentSubfolderJson = folder.getFoldersJsonArray().get(x).getAsJsonObject();
JsonObject fullSubfolderProfileObject = getFolderProfileFromRestApi(currentSubfolderJson);
Folder newSubfolder = folder.addSubfolder(fullSubfolderProfileObject);
if (newSubfolder.hasSubfolders()) {
saveSubfoldersRecursively(newSubfolder);
}
}
if (folder.getId().equals("<folderID>") ) {
System.out.println("inside saveSubfoldersRecursively"); // this prints fine
// Null Pointer Exception at next line!
System.out.println("access test 1: " + folder.getId() + " subfolder at index 0: " + folder.getSubFolders().get(0));
}
}
As requested, the getFolderProfileFromRestApi()
and getFoldersJsonArrayFromRestApi()
methods simply make a call with Rest-Assured and return a GSON JsonObject containing the information needed in the constructor or method that is needed for their function, I've used these quite a bit and have verified them to be working as intended. Calling folder.addSubfolder()
calls the folder constructor and returns the new folder so that it can then be used to get it's own folders, etc. I've used this pretty extensively and have verified that it does return the proper info and the constructor is called. If I put System.out checks within the actual folder.addSubfolder()
method, I can access the elements in the array as they are still there but seem to disappear when control goes back to main.
The addSubfolder method within the Folder class looks as below. I added the System outs to check and be sure that everything was happening and these are in fact added where they're supposed to. The outputs here are exactly as expected.
public Folder addSubfolder(JsonObject folderProfile){
System.out.println("Adding " + folderProfile.get("id") + " to subfolders of " + this.getId()); //this is a check to be sure the folderProfile passed has the right folderID and has been passed ot the right parent folder. this also returns the correct info in all cases
Folder subfolder = new Folder(folderProfile);
this.subFolders.add(subfolder);
System.out.println("subfolder added at index " + this.subFolders.indexOf(subfolder)); //This is just a check to see that the subfolder is now in the array and it does return the correct index.
return subfolder;
}
When I return to Main, I've written a small recursive method to print the structure in a standard format using the names only and it seems to work until it gets to a place where there would be a 3rd level, then it throws a null pointer exception when it tries to access that first element of that level.
Conversely, I used specific code to just call the toString for the exact nested subfolder that was just created and checked in the addSubfolder method, and it also returns a null pointer exception. i.e.
server.getSinglefolder(1).getSubFolders().get(0).getSubFolders().get(0).toString();
As best I can tell, this other StackOverflow post
Objects disappearing of ArrayList
had the same problem, but I can't seem to understand how to convert the solution for my situation or if it's not working because I shouldn't be doing it this way. Thanks for your help!
Edit: I've reviewed that post again and it seems his issue is an issue with the fact that his later code was changing it but he didn't notice because it was a reference. I don't think this is the case here since after that method returns, the object isn't accessed again until it's called in main and comes back empty. I've been searching and reading articles ALL DAY now and this has got to be something stupid that I just can't see. Thanks again for your help.
Edit 2: After further testing, I've noticed that the folder that is added to the ArrayList is verifiable within the constructor using this
to get the object and call the getter for it's ID. However, the ArrayList is then null at that index when accessed from the end of the recursive method that calls the constructor (System.out tests added to code sample). Thus the arraylist is Null again before control even returns to main, as soon as the constructor returns, actually. Could this be something to do with the fact that I declare my placeholder objects (folder, subfolder, newSubfolder) from within the method and thus they are cleared when it exits? Even if that was the case the object itself should still hold the changes even if the temporary reference is gone.
Edit 3: as requested, here are the methods for folder.hasSubFolders()
and folder constructor.
private boolean hasSubfolders;
public boolean hasSubfolders() {
return hasSubfolders;
}
Folder Constructor that is called from within addSubfolder. please note the whole constructor is about 150 lines but it follows the same format, check for member in JSON, if it exists, save value to class variable. I can post the whole thing if anyone really thinks it's necessary.
//main constructor, parses response string to JSON and pulls all data to fill class variables.
public Folder(JsonObject folderProfile) {
if (folderProfile.has("database")) {
this.database = folderProfile.get("database").getAsString();
}
if (folderProfile.has("default_security")) {
this.defaultSecurity = folderProfile.get("default_security").getAsString();
}
...}
Edit 5: ideone links for each of the parts of the program. It wont run without access to the rest server of course but unfortunately I can't share the connection info. I can assure you that the rest calls function properly and return exactly the data their supposed to, and that all of that data is handled correctly (correctly as in a way that works, but maybe not best practice) -_-
Note: I am aware that I need to clean up the abstract class and the way it is implemented, I have a LOT of work left to do for cleaning up the coding practices but this is far from complete and only a half-finished piece of program that I'm testing. However I am completely open to any suggestions in that realm as I'm always learning. Thanks
Main - https://ideone.com/Y2FYrm
IMContainer - https://ideone.com/TFdiqk
Folder - https://ideone.com/gX4nda
Template - https://ideone.com/7VfkPX
Server - https://ideone.com/geJazD