Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!


Интересна Java? Кликай по ссылке и изучай!
Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 

пятница, 18 января 2013 г.

Компилим Java в рантайме

Не так давно мне в руки попал портальчик http://codingbat.com (спасибо Виталик!)/ Мне очень понравилось ихняя задумка, и я захотел ее себе на свой тренинговый портал. Мы используем Moodle и все решенные учащимся (на Java тренинге) задания отправляются либо файлом, либо линком на ревизию в репозитории. Не так давно мы пришли к выводу, что заданий покаждому модулю должно быть много, они должны быть мелкими. И тут в руки попадает http://codingbat.com - я понимаю что это именно то, что мне нужно. Мне нужно точно такое же окошко в Moodle, и кнопка отправить, в ответ на нажатие которой я получу либо pass либо сообщение о fail. Задания можно выстраивать в очередь а о результатах рипортить тренеру (средствами Moodle).

Чтоб сделать это мне требуется ответить на вопрос - как копилировать исходный код java в рантайме. Ответ не заставил себя ждать - слава Google.
Я выборал родное и следуя рекомендациям (тупому copy past) из статьи Dynamic in-memory compilation. Все получилось. реализовано два режима работы - компиляция методов и копиляция классов. Вот как это выглядит.
public class CompilerTest {

    private JavaCompiler compiler = new JavaCompiler();

    @Test
    public void shouldRunMethodWithoutParameters() {
        JavaCompiler compiler = new JavaCompiler();
        JavaMethod method = compiler.getMethod(
                "public String qwe() { \n" +
                "    return \"Hello world!\";\n" +
                "}");
        String result = (String)method.run();

        assertEquals("Hello world!", result);
    }

    @Test
    public void shouldRunMethodWithTwoStrings() {
        JavaMethod method = compiler.getMethod(
                "public String qwe(String qwe, String asd) { \n" +
                "    return \"The first is '\" + qwe + \"', the second is '\" + asd + \"'\";\n" +
                "}");
        String result = (String)method.run("qwe_string", "asd_string");

        assertEquals("The first is 'qwe_string', the second is 'asd_string'", result);
    }

    @Test
    public void shouldRunMethodWithTwoInts() {
        JavaMethod method = compiler.getMethod(
                "public String qwe(int qwe, int asd) { \n" +
                "    return \"The first is '\" + qwe + \"', the second is '\" + asd + \"'\";\n" +
                "}");
        assertEquals("The first is '1', the second is '2'", (String)method.run(1, 2));
        assertEquals("The first is '2', the second is '3'", (String)method.run(2, 3));
    }

    @Test
    public void shouldCompileClass() throws Exception {
        JavaClass clazz = compiler.newClass(
                "public class SomeDynaClass {\n" +
                "    public String qwe(int qwe, int asd) { \n" +
                "        return \"The first is '\" + qwe + \"', the second is '\" + asd + \"'\";\n" +
                "    }\n" +
                "    public String asd(String zxc) { \n" +
                "        return \"The third is '\" + zxc + \"'\";\n" +
                "    }\n" +
                "}");
        Class realClass = clazz.getRealClass();
        Object object = clazz.newInstance();

        String result = (String) realClass.getMethod("qwe", int.class, int.class).invoke(object, 1, 2);
        assertEquals("The first is '1', the second is '2'", result);

        result = (String) realClass.getMethod("asd", String.class).invoke(object, "string");
        assertEquals("The third is 'string'", result);
    }

    @Test
    public void shouldCompileClassWithoutDefaultConstructor() throws Exception {
        JavaClass clazz = compiler.newClass(
                "public class SomeDynaClass {\n" +
                "    private int i;\n" +
                "    public SomeDynaClass(int i) {\n" +
                "        this.i = i;\n" +
                "    }\n" +
                "    public String qwe(int qwe, int asd) { \n" +
                "        return \"The first is '\" + (qwe+i) + \"', the second is '\" + (asd+i) + \"'\";\n" +
                "    }\n" +
                "    public String asd(String zxc) { \n" +
                "        return \"The third is '\" + zxc + i + \"'\";\n" +
                "    }\n" +
                "}");
        Class realClass = clazz.getRealClass();
        Object object = clazz.newInstance(10);

        String result = (String) realClass.getMethod("qwe", int.class, int.class).invoke(object, 1, 2);
        assertEquals("The first is '11', the second is '12'", result);

        result = (String) realClass.getMethod("asd", String.class).invoke(object, "string");
        assertEquals("The third is 'string10'", result);
    }

}
Код немного сыроват, но все же тесты работают и пока меня это устраивает. Скачать исходники можно тут.

Может кому пригодится...

Комментариев нет:

Отправить комментарий