Search code examples
javamysqlrecursionstack-overflow

How can I fix this recursive StackOverFlowException error?


So I have data for a sort of friends system stored on a MySQL database, and a sort of API to retrieve the data in Java objects.

The Java object (MPlayer) contains things like the username, online status, friends (ids separated by ":"). The MPlayer object takes a player's unique ID as a constructor.

The data is saved in the object when created and when the reload() method is called. This is instead of accessing the database every time I want to get something like the username. The reason for this is that I need to get the data in a loop to be displayed on a GUI, and I obviously don't want to download the data every frame. Instead, I just use the reload method every 6 seconds or so.

One of the functions is getFriends() and returns a list of MPlayer. The list is stored when the MPlayer object is created. The problem is, when each of the MPlayer friends are created, it creates a list for their friends, which in turn creates a list for their friends, ending up with a StackOverFlowException due to recursion.

What would be a good solution to avoid the error?

Code in question:

The MPlayer constructor/load method:

public MPlayer(String player){
    this.uuid = player;
    try {
        st.setString(1, uuid);
    } catch (SQLException e) {

        e.printStackTrace();
    }
    this.reload();
}

public void reload(){
    try {
        ResultSet set = st.executeQuery();
        if(set.next()){
            if(set.getString("server").equals("none")){
                isConnected = false;
            }else{
                isConnected = true;
            }
        }
    } catch (SQLException e) {

        e.printStackTrace();
    }

    try {
        ResultSet set = st.executeQuery();
        if(set.next()){
            this.serverIP = set.getString("server");
        }
    } catch (SQLException e) {

        e.printStackTrace();
    }



    try {

        ResultSet set = st.executeQuery();
        if(set.next()){
            this.username = set.getString("username");
        }
    } catch (SQLException e) {

        e.printStackTrace();
    }


    try {
        ResultSet get = st.executeQuery();
        if(get.next()){
            this.online = get.getBoolean("status");
        }
    } catch (SQLException e) {

        e.printStackTrace();
    }
    try {


        List<MPlayer> list = new ArrayList<MPlayer>();
        ResultSet get = st.executeQuery();
        if(get.next()){
            for(String str : get.getString("friends").split(":")){
                if(!str.equalsIgnoreCase("none")){
                    MPlayer player = new MPlayer(str);
                    if(player.isOnline()){
                        list.add(0,player);
                    }else{
                        list.add(player);
                    }
                }
            }

        }
        this.friends = list;
    } catch (SQLException e) {

        e.printStackTrace();
    }


    this.settings = new Settings(this);


    PreparedStatement state = Main.getPreparedStatement("SELECT * FROM updates WHERE uuid=?");
    try {
        state.setString(1, this.getUUID());
        ResultSet set2 = state.executeQuery();
        List<StatusUpdate> updates = new ArrayList<StatusUpdate>();

        while(set2.next()){
            updates.add(new StatusUpdate(set2.getInt(1)));
        }
            Collections.sort(updates, new Comparator<StatusUpdate>() {
                @Override
                public int compare(StatusUpdate r1, StatusUpdate r2) {

                    return -1 * r1.getDate().compareTo(r2.getDate());
                }
            }); 

        this.updates = updates;
    } catch (SQLException e) {

        e.printStackTrace();
    }


    List<StatusUpdate> updates = new ArrayList<StatusUpdate>();

    for(MPlayer p : this.getFriends()){
        updates.addAll(p.getStatusUpdates());

    }
    updates.addAll(this.getStatusUpdates());
    Collections.sort(updates, new Comparator<StatusUpdate>() {
        public int compare(StatusUpdate m1, StatusUpdate m2) {

            return -1 * m1.getDate().compareTo(m2.getDate());
        }
    }); 
    this.timeline = updates;



}

Solution

  • The obvious answer is "quit doing it recursively with no termination".

    You don't want unbounded closure on the friends list -- but what do you want? Just the list of friends? In that case, perhaps you need a separate object type, such as "FriendList". In creating this, you merely list the friend IDs; don't load a friend record until it's specifically accessed.

    Another way is to code the number of levels you want to activate, making this a parameter of the object load. For isntance, load your primary MPlayer with a depth of N=2. For each friend of your primary, load to a depth of N-1. When you hit 0, list the ID without loading the record (as above).

    Does this get you moving toward a solution?