Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Java Local Development Environments Advanced Tooling Finishing TreeStory

Rylan Hilman
Rylan Hilman
3,618 Points

Code works fine in IntelliJ, crashes with OutOfMemoryError in code challenge.

Okay, so this seems to work perfectly in the compiler, but when I copy-paste the code over and try to pass this code challenge, it claims "Bummer! Try again!" and drops this on me in the output field:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3236) at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:113) at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93) at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:140) at java.io.PrintStream.write(PrintStream.java:480) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291) at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104) at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185) at java.io.PrintStream.write(PrintStream.java:527) at java.io.PrintStream.print(PrintStream.java:669) at java.io.PrintStream.append(PrintStream.java:1065) at java.io.PrintStream.append(PrintStream.java:57) at java.util.Formatter$FixedString.print(Formatter.java:2595) at java.util.Formatter.format(Formatter.java:2508) at java.io.PrintStream.format(PrintStream.java:970) at java.io.PrintStream.printf(PrintStream.java:871) at com.teamtreehouse.Prompter.promptForWord(Prompter.java:84) at com.teamtreehouse.Prompter.promptForWords(Prompter.java:60) at com.teamtreehouse.Prompter.run(Prompter.java:38) at com.teamtreehouse.Main.main(Main.java:32) at JavaTester.run(JavaTester.java:77) at JavaTester.main(JavaTester.java:39)

I looked at the source code and all it's trying to do on the line that it crashes on is "System.out.printf("Try one more.%n");" and this is somehow entering some kind of infinite loop that doesn't end until it consumes all available memory. Did I do something wrong or is this thing just busted?

com/teamtreehouse/Prompter.java
    /**
     * Prompts the user for the answer to the fill in the blank.  Value is guaranteed to be not in the censored words list.
     *
     * @param phrase The word that the user should be prompted.  eg: adjective, proper noun, name
     * @return What the user responded
     */
    public String promptForWord(String phrase) throws IOException
    {
        String retWord = "";
        while(retWord.isEmpty())
        {
            System.out.printf("Enter a %s: ", phrase);
            retWord = mReader.readLine();

            if(retWord == null || retWord.trim().isEmpty() || mCensoredWords.contains(retWord.trim()))
            {
                System.out.printf("Try one more.%n");
                retWord = "";
            }
        }
        return retWord;
    }
}

I mean, I might understand an error if it was passing in an unusual or unexpected test case that triggered something, but that particular printf line doesn't involve any user input whatsoever, so it shouldn't be overloading there regardless of what's getting passed in.

Interestingly, if I comment out that printf line, the exact error happens again, except with the previous printf line. The test case(s) could be trying to enter in incorrect answers indefinitely and never exiting the while loop, but I'm not sure why a test case would be doing that and then not expecting something to break. Or why it would run out of memory from just printing to System.out.

Or it could be that the code challenge is expecting some exact input and output format that's getting misaligned somewhere, but I'm not exactly sure what it's trying to input behind the scenes there.

3 Answers

Rylan Hilman
Rylan Hilman
3,618 Points

Okay, after reworking it again, I passed the code challenge but I'm not entirely sure why. What I did was reread the description, created a new method inside Prompter.java to do the promptForStory (where before I had done that inside the Main function, where the template story String was originally generated) and doing that with the previously implemented BufferedReader fixed everything.

It doesn't quite explain why I was getting out of memory errors on printf when that was the only change, but perhaps if there was a "TODO: Create a new method to prompt for story" inside Prompter.java I'd have found that sooner. Does the Java virtual machine here choke and time out forever if new BufferedReaders are opened in two different classes while my local compiler got through it without incident?

Craig Dennis
STAFF
Craig Dennis
Treehouse Teacher

Hey Ryan!

Usually the OutOfMemory Errors are caused by infinite loops, it could be in the code I wrote to test and provide information to your program. It's pretty difficult to fake out System.in and System.out so I might've had a bug.

Do you remember how your code in the main method worked? Maybe I can help prevent someone else from getting the same problem.

Sorry for the frustration, and awesome job for sticking with it and figuring it out!

Rylan Hilman
Rylan Hilman
3,618 Points

My function in Prompter.java that worked went like this:

Prompter.java
    public String promptForStory()
    {
        String story;
        do
        {
            try
            {
                System.out.println("Enter your __story__: ");
                story = mReader.readLine();
            } catch( IOException e)
            {
                System.out.println("Something exploded, using default string.");
                story = "Thanks __name__ for helping me out.  You are really a __adjective__ __noun__ and I owe you a __noun__.";
            }
        } while( story == null || story.isEmpty() );
        return story;
    }

While what I was doing in the main method was the exact same thing, but just the parts in there between the do and the while (and using a new BufferedReader input stream object local to the main function). It didn't work there, but this function did work after I copy-pasted it over to Prompter.java and built a method around it.

I'm going to include the original broken files, the ones that work fine locally but cause errors in here:

Main.java
package com.teamtreehouse;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


public class Main {

    public static void main(String[] args) {
        Prompter thePrompt = new Prompter();

        String story = "";

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
       do
        {
          System.out.println("Enter String Story:");
          try
          {
              story = br.readLine();
          }
          catch(IOException e)
          {
              System.out.println("Something exploded, using default string.");
              e.printStackTrace();
              story = "Thanks __name__ for helping me out.  You are really a __adjective__ __noun__ and I owe you a __noun__.";
          };
        } while (story == null || story.isEmpty());

        Template tmpl = new Template(story);
        thePrompt.run(tmpl);
    }
}
Prompter.java
package com.teamtreehouse;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


public class Prompter
{
    private BufferedReader mReader;
    private Set<String> mCensoredWords;

    public Prompter()
    {
        mReader = new BufferedReader(new InputStreamReader(System.in));
        loadCensoredWords();
    }

    private void loadCensoredWords()
    {
        mCensoredWords = new HashSet<String>();
        Path file = Paths.get("resources", "censored_words.txt");
        List<String> words = null;
        try
        {
            words = Files.readAllLines(file);
        }
        catch (IOException e)
        {
            System.out.println("Couldn't load censored words");
            e.printStackTrace();
        }
        mCensoredWords.addAll(words);
    }

    public void run(Template tmpl)
    {
        List<String> results = null;
        try
        {
            results = promptForWords(tmpl);
        }
        catch (IOException e)
        {
            System.out.println("There was a problem prompting for words");
            e.printStackTrace();
            System.exit(0);
        }

        String outputResults = tmpl.render(results);
        System.out.printf("Your TreeStory:%n%n%s", outputResults);

    }

    /**
     * Prompts user for each of the blanks
     *
     * @param tmpl The compiled template
     * @return
     * @throws IOException
     */
    public List<String> promptForWords(Template tmpl) throws IOException {
        List<String> words = new ArrayList<String>();
        for (String phrase : tmpl.getPlaceHolders()) {
            String word = promptForWord(phrase);
            words.add(word);
        }
        return words;
    }


    /**
     * Prompts the user for the answer to the fill in the blank.  Value is guaranteed to be not in the censored words list.
     *
     * @param phrase The word that the user should be prompted.  eg: adjective, proper noun, name
     * @return What the user responded
     */
    public String promptForWord(String phrase) throws IOException
    {
        String retWord = "";
        while(retWord.isEmpty())
        {
            System.out.printf("Enter a %s: ", phrase);
            retWord = mReader.readLine();

            if(retWord == null || retWord.trim().isEmpty() || mCensoredWords.contains(retWord.trim()))
            {
                System.out.printf("Try one more.%n");
                retWord = "";
            }
        }
        return retWord;
    }
}

The only change that bumped it from Bummer! to Congrats! was building that method in Prompter.java and calling it from the same spot in Main.java instead of having that code run in Main.