Forums

Search results: 44

Question Authors' Forum -> Java packages -> Re: Java packages

by Peter Sander -

Ok, here's a slightly modified template to make it more generic. I've left in two template parameters for the name of the package and the student answer class, eg, {"package": "foobar", "class": "Foo"}. Basically I bailed on extracting this information from the student answer by trying to anticipate how students might construct their Java code file. Maybe later.

There's a limitation in that the student answer and supporting files must all be in the same package

import sys, os, shutil, subprocess
__student_answer__ = """{{ STUDENT_ANSWER | e('py') }}"""
# Make package directory, put support files into it
os.mkdir("{{ QUESTION.parameters.package }}")
[shutil.move(f, "{{ QUESTION.parameters.package }}") for f in os.listdir() if f.endswith(".java")]
# Put student answer class into package directory
with open("{{ QUESTION.parameters.package }}/{{ QUESTION.parameters.class}}.java", "w") as f:
    print(__student_answer__, file=f)
    
# Build test class in package directory too
tester = """package {{ QUESTION.parameters.package }};
public class __Tester__ {
    public static void main(String[] args) {
        {{TEST.testcode}}
    }
}
"""
with open("{{ QUESTION.parameters.package }}/__Tester__.java", "w") as f:
    print(tester, file=f)
# Compile and run
if (subprocess.call(["javac", "{{ QUESTION.parameters.package }}/__Tester__.java"]) != 0 or
   subprocess.call(["java", "{{ QUESTION.parameters.package }}/__Tester__"]) != 0):
    print("** Further testing aborted **", file=sys.stderr)

Question Authors' Forum -> Java packages -> Re: Java packages

by Richard Lobb -

Java packages are somewhat incompatible with the usual CodeRunner/Jobe model of a single directory in which the Twig output file plus any support files are placed prior to execution. To get around this you need to build the required directory hierarchy yourself. [And if you thought your solution was awkward, I'm not sure what you'll think of mine!] I always script tricky stuff like this in Python. Here's a solution that works for your particular problem:

  1. In Advanced customisation, set the main language to Python3, the Ace language to Java and set the memory limit to 0 (as the Java VM tries to reserve ludicrous amounts of memory).
  2. Set the test code to
    Foo foo = new Foo();
    System.out.println(foo);
    
  3. Load Bar.java as a support file
  4. Set the template to the following:
    import sys, os, shutil, subprocess
    
    __student_answer__ = """{{ STUDENT_ANSWER | e('py') }}"""
    
    # Make foobar directory, put Bar.java and Foo.java into it
    os.mkdir('foobar')
    shutil.move("Bar.java", "foobar/")
    with open("foobar/Foo.java", "w") as f:
        print(__student_answer__, file=f)
        
    # Build test class in foobar directory too
    tester = """package foobar;
    public class __Tester__ {
        public static void main(String[] args) {
            {{TEST.testcode}}
        }
    }
    """
    with open("foobar/__Tester__.java", "w") as f:
        print(tester, file=f)
    
    # Compile and run
    if (subprocess.call(["javac", "foobar/__Tester__.java"]) != 0 or
       subprocess.call(["java", "foobar/__Tester__"]) != 0):
        print("** Further testing aborted **", file=sys.stderr)
    
Proof it works:


Although that's pretty complex, if you have many questions involving packages, you could abstract this into a separate question type in which the support files specify the required package hierarchy (e.g. you might have a zip archive to be unzipped plus an additional config file).

One shortcoming worth noting: CodeRunner classifies any output to stderr as a "Runtime error" and aborts the running of further testcases. Thus compilation errors in the student's code are tagged as "Runtime errors", although the usual compiler diagnostic messages still appear in the output. You could prevent this misclassification by catching stderr output from the subprocess.call but you would then continue to run all other test cases, generating the same compile error output. There's no easy way round this, though you could use a template grader if it really worried you. 

Question Authors' Forum -> Java packages

by Peter Sander -

Hello,

I'm having trouble getting the following to work.

I have a java_class question for students to develop. My sample answer looks like this:

package foobar;
public class Foo {
    private Bar bar;
    public String toString() {return "whatever";}
}

The class Foo is in a support file called Bar.java containing:

package foobar;
public class Bar {}

Not very exciting I'll admit.

In the question preview with the above sample answer submitted as the student answer, Check gives:

Debug: source code from all test runs

Run 1

package foobar;

class Foo {
    private Bar bar;
    public String toString() {
        return "whatever";
    }
}

public class __Tester__ {

    public static void main(String[] args) {
        __Tester__ main = new __Tester__();
        main.runTests();
    }

    public void runTests() {
        System.out.println(new Foo());
    }
}

which doesn't look unreasonable. However, the compiler seems unable to find the class Bar even though it's declared in the same package.

Syntax Error(s)

__Tester__.java:4: error: cannot find symbol
    private Bar bar;
            ^
  symbol:   class Bar
  location: class Foo
1 error

Take out the package declarations and the code works as expected. I've tried diddling the template, eg. putting a package declaration at the top, with no great success. 

Take out the variable declaration Bar bar and removing the support file makes the class compile, but Check now gives a runtime error:

 

TestExpectedGot
IncorrectSystem.out.println(new Foo())
whatever
***Runtime error***
Exception in thread "main" java.lang.NoClassDefFoundError: __Tester__ (wrong name: foobar/__Tester__)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)

Has anyone gotten questions with Java classes in packages to work?

I should mention that I can strip out the package declaration from the student answer with

{{ STUDENT_ANSWER | replace({'package foobar;': ''}) | replace({'public class ': 'class ' }) }}

in the template, but that just seems awkward.