Hlavní navigace

Pohled pod kapotu JVM - sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (5.část: detekce vzniku všech výjimek)

4. 12. 2012
Doba čtení: 42 minut

Sdílet

Dnes navážeme na část předchozí, v níž jsme si popsali, jak lze v JVM TI agentovi detekovat okamžik zachycení výjimky. Dnes si (naopak) ukážeme způsob zjištění okamžiku vzniku výjimek, a to jak výjimek zachycovaných (i prázdným blokem catch), tak i výjimek, které nejsou zachyceny a povedou k ukončení aplikace.

Obsah

1. Pohled pod kapotu JVM – sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (5.část: detekce vzniku všech výjimek)

2. Callback funkce zavolaná při vzniku všech zachycovaných i nezachycovaných výjimek

3. Třináctý demonstrační příklad – základní výpis zachycovaných i nezachycovaných výjimek

4. Zjištění podrobnějších informací o třídě a o metodě, kde k výjimce došlo

5. Čtrnáctý demonstrační příklad – výpis třídy a metody, kde k výjimce došlo

6. Přečtení čísla řádku, kde k výjimce došlo

7. Patnáctý demonstrační příklad – výpis čísla řádku v rámci třídy, kde k výjimce došlo

8. Zdrojové kódy všech demonstračních příkladů

9. Odkazy na Internetu

1. Pohled pod kapotu JVM – sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (5. část: detekce výjimky, výpis zásobníku)

V dnešní části seriálu o programovacím jazyce Java i o vlastnostech JVM se budeme již popáté zabývat popisem využití rozhraní JVM TI (JVM Tool Interface) pro tvorbu agentů určených pro monitorování a taktéž pro řízení jak samotného virtuálního stroje Javy, tak i javovských aplikací. Minule jsme si popsali a na několika demonstračních příkladech ukázali, jakým způsobem může agent detekovat zachycení výjimky, tj. okamžik, kdy virtuální stroj Javy předává řízení do příslušného bloku catch (typ_výjimky). Připomeňme si, že callback funkce volaná v okamžiku zachycení výjimky musí mít následující hlavičku:

/*
 * Callback funkce zavolaná ve chvíli zachycení výjimky.
 */
static void JNICALL callback_on_exception_catch(
            jvmtiEnv *jvmti_env,
            JNIEnv* env,
            jthread thr,
            jmethodID method,
            jlocation location,
            jobject exception);

Význam jednotlivých parametrů je stručně popsán v následující tabulce:

# Parametr Typ Význam
1 jvmti_env jvmtiEnv* JVM TI prostředí agenta (je předáváno do většiny callback funkcí)
2 jni_env JNIEnv* JNI prostředí platné pro dané vlákno (je předáváno do některých callback funkcí)
3 thr jthread vlákno, v němž dojde k zachycení výjimky
4 method jmethodID metoda, ve které se výjimka zachycuje
5 location jlocation umístění kódu, který výjimku zachycuje (odpovídá indexu instrukce)
6 exception jobject identifikátor objektu, který nese informaci o výjimce

Velmi často však musí agent umět registrovat i okamžik vzniku výjimky, ať již se jedná o výjimku zachycovanou, popř. o výjimku, která není zachycena a která tedy ve svém důsledku povede k ukončení práce aplikace. I tuto situaci je samozřejmě možné v agentovi zpracovat, pouze v našem JVM TI agentovi budeme potřebovat zaregistrovat další callback funkci s poněkud složitější hlavičkou, v níž můžeme vidět další dva parametry a navíc mají některé parametry odlišný význam:

/*
 * Callback funkce zavolaná ve chvíli vzniku výjimky, resp. detekce výjimky virtuálním strojem.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv* env,
            jthread thr,
            jmethodID method,
            jlocation location,
            jobject exception,
            jmethodID catch_method,
            jlocation catch_location);

Význam některých parametrů je u obou funkcí shodný, další parametry však mají – i přes shodný název – význam poněkud odlišný:

# Parametr Typ Význam
1 jvmti_env jvmtiEnv* JVM TI prostředí agenta (je předáváno do většiny callback funkcí)
2 jni_env JNIEnv* JNI prostředí platné pro dané vlákno (je předáváno do některých callback funkcí)
3 thr jthread vlákno, v němž dojde k vyhození (vzniku) výjimky
4 method jmethodID metoda, ve které výjimka vznikla
5 location jlocation umístění kódu, v němž výjimka vznikla
6 exception jobject identifikátor objektu, který nese informaci o výjimce
7 catch_method jmethodID metoda, ve které se výjimka zachycuje
8 catch_location jlocation umístění kódu, který výjimku zachycuje (odpovídá indexu instrukce)

2. Callback funkce zavolaná při vzniku všech zachycovaných i nezachycovaných výjimek

Důležité je, že ve chvíli volání této callback funkce již virtuální stroj Javy ví, kde bude výjimka zachycena. Pokud však vznikla nezachycovaná výjimka, je v parametru catch_method předána hodnota NULL. S těmito znalostmi již můžeme vytvořit první variantu callback funkce volané při vzniku výjimky. Tato callback funkce bude pouze vypisovat třídu výjimky (tedy její typ) a taktéž informaci o tom, zda se jedná o výjimku zachycovanou či nikoli:

/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    jclass exception_class;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat signaturu vyjimky */
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) */
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* tisk ziskane informace o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}

Funkce update_class_name(), s jejíž první verzí jsme se seznámili minule, je poněkud upravena, a to takovým způsobem, aby se namísto znaků ‚/‘ (lomítko) vypisovaly tečky, které jsou čitelnější. Tato úprava se provádí v samostatné smyčce:

/*
 * Uprava jmena tridy pro tisk.
 */
char* update_class_name(char *class_name_ptr, char replace_to)
{
    char *class_name_ptr_;
    char *c;
    if (class_name_ptr != NULL)
    {
        /* odstraneni pocatecniho L na zacatku jmena tridy */
        class_name_ptr_ = class_name_ptr;
        if (class_name_ptr_[0] == 'L')
        {
            class_name_ptr_++;
        }
        /* nahrada znaku ; za tecku ci jiny definovany znak */
        char *last_char = class_name_ptr_ + strlen(class_name_ptr_) - 1;
        if (*last_char == ';')
        {
            *last_char = replace_to;
        }
        /* nahrada vsech znaku '/' za '.' */
        c = class_name_ptr_;
        for (c = class_name_ptr_; *c != 0; c++)
        {
            if (*c == '/') *c = '.';
        }
    }
    return class_name_ptr_;
}

3. Třináctý demonstrační příklad – základní výpis zachycovaných i nezachycovaných výjimek

S využitím znalostí z předchozí kapitoly je již poměrně jednoduché vytvořit JVM TI agenta, který bude vypisovat informaci o vzniku všech výjimek ve virtuálním stroji Javy. Oproti agentům popsaným minule došlo jen ke dvěma relativně malým změnám – registruje se odlišný typ callback funkce a taktéž je povolen režim notifikace pro událost typu JVMTI_EVENT_EXCEPTION:

/*
 * Demonstracni agent, ktery dokaze zaregistrovat vznik
 * zachycovane i nezachycovane vyjimky.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu k tomuto souboru
 * pres volbu -Icesta_k_jvm
 */
#include <jvmti.h>
 
/*
 * Jmeno agenta pouzite ve zpravach vypisovanych
 * na standardni vystup.
 */
#define AGENT_NAME "Agent13:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/* Zamek pouzivany agentem */
jrawMonitorID  global_lock;
 
 
/*
 * Vypis kodu chyby a chybove zpravy na standardni vystup.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem,
 * vypise se chybove hlaseni na standardni vystup.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Vytvoreni zamku.
 */
jvmtiError create_raw_monitor(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot create raw monitor");
 
    return error_code;
}
 
/*
 * Vstup do kriticke sekce.
 */
static void enter_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorEnter(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot enter with raw monitor");
}
 
/*
 * Vystup z kriticke sekce
 */
static void exit_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorExit(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot exit with raw monitor");
}
 
/*
 * Nastaveni pozadovanych schopnosti agenta.
 */
jvmtiError set_capabilities(jvmtiEnv *jvmti)
{
    jvmtiCapabilities capabilities;
    jvmtiError error_code;
 
    memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 
    /* vyuzivame pouze jednu specialni schopnost agenta */
    capabilities.can_generate_exception_events = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Uprava jmena tridy pro tisk.
 */
char* update_class_name(char *class_name_ptr, char replace_to)
{
    char *class_name_ptr_;
    char *c;
    if (class_name_ptr != NULL)
    {
        /* odstraneni pocatecniho L na zacatku jmena tridy */
        class_name_ptr_ = class_name_ptr;
        if (class_name_ptr_[0] == 'L')
        {
            class_name_ptr_++;
        }
        /* nahrada znaku ; za tecku ci jiny definovany znak */
        char *last_char = class_name_ptr_ + strlen(class_name_ptr_) - 1;
        if (*last_char == ';')
        {
            *last_char = replace_to;
        }
        /* nahrada vsech znaku '/' za '.' */
        c = class_name_ptr_;
        for (c = class_name_ptr_; *c != 0; c++)
        {
            if (*c == '/') *c = '.';
        }
    }
    return class_name_ptr_;
}
 
/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    jclass exception_class;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat signaturu vyjimky */
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) */
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* tisk ziskane informace o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}
 
/*
 * Registrace callback funkce zavolane pri zachyceni vyjimky.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_EXCEPTION */
    callbacks.Exception = &callback_on_exception;
 
    error_code = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error_code, "Cannot set JVM TI callbacks");
    return error_code;
}
 
/*
 * Nastaveni jedne udalosti, pro nez se ma zavolat callback funkce.
 */
jvmtiError set_event_notification_mode(jvmtiEnv *jvmti, int event)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event, (jthread)NULL);
    check_jvmti_error(jvmti, error_code, "Cannot set event notification");
    return error_code;
}
 
/*
 * Nastaveni udalosti, pro nez se maji zavolat callback funkce.
 */
jvmtiError set_event_notification_modes(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    /* zachytavat pouze udalost zachyceni vyjimky */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_EXCEPTION)) != JNI_OK)
    {
        return error_code;
    }
 
    return error_code;
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
    jvmtiError error_code;
 
    MSG("Agent_OnLoad");
    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL)
    {
        printf("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your J2SE a 1.5 or newer version? JNIEnv's GetEnv() returned %d\n",
                JVMTI_VERSION_1, (int)result);
        return result;
    }
    MSG("JVM TI version is correct");
 
    /* nastaveni pozadovanych schopnosti agenta */
    if ((error_code = set_capabilities(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* registrace vsech callback funkci */
    if ((error_code = register_all_callback_functions(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* nastaveni udalosti, pro nez se maji zavolat callback funkce */
    if ((error_code = set_event_notification_modes(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* vytvoreni zamku */
    if ((error_code = create_raw_monitor(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}
 
/*
 * finito
 */

Funkci našeho agenta si odzkoušíme na následujícím javovském testovacím programu, v němž se nejdříve vytvoří čtyři různé zachycované výjimky a posléze dojde k vytvoření páté výjimky, která není nikde zachycena. Kvůli tomu samozřejmě dojde k ukončení práce aplikace a tím pádem i k ukončení práce virtuálního stroje Javy:

/**
  * Testovaci trida pouzita pro test trinacteho
  * demonstracniho JVM TI agenta.
  */
public class Test13 {
 
    /**
      * Test zachyceni vyjimky typu ArrayIndexOutOfBoundsException.
      */
    public static void method1() {
        int[] a = {1,2,3};
        try {
            a[-1] = 10;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
      * Test zachyceni vyjimky typu ArithmeticException.
      */
    public static int method2(int a, int b) {
        try {
            return a/b;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }
 
    /**
      * Test zachyceni vyjimky typu ClassNotFoundException.
      */
    public static Class method3(String className) {
        try {
            return Class.forName(className);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
      * Test zachyceni vyjimky typu NullPointerException.
      */
    public static String method4(Object object) {
        try {
            return object.toString();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
 
    /**
      * Vyhozeni vyjimky.
      */
    public static String throwSomeException(Object object) {
        return object.toString();
    }
 
    /**
      * Spusteni testu.
      */
    public static void main(String[] args) {
        method1();
        method2(1, 0);
        method3("xyzzy");
        method4(null);
        throwSomeException(null);
    }
}

Překlad a spuštění agenta i testovacího programu provedeme následující sekvencí příkazů:

gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent13.so agent13.c
javac -g Test13.java
java -agentpath:./libagent13.so Test13 2> /dev/null

Po spuštění programu i s agentem můžeme vidět, že kromě čtyř zachycovaných výjimek generovaných samotným programem vzniklo i mnoho výjimek typu ClassNotFoundException (interně v JVM) a nakonec byl program ukončen kvůli vyhození výjimky typu NullPointerException (NPE):

Agent13: Agent_OnLoad
Agent13: JVM TI version is correct
Agent13: catch exception of type java.lang.ClassNotFoundException
Agent13: catch exception of type java.lang.ClassNotFoundException
Agent13: catch exception of type java.lang.ClassNotFoundException
Agent13: catch exception of type java.lang.ArrayIndexOutOfBoundsException
Agent13: catch exception of type java.lang.ArithmeticException
Agent13: catch exception of type java.lang.ClassNotFoundException
Agent13: catch exception of type java.lang.ClassNotFoundException
Agent13: catch exception of type java.lang.ClassNotFoundException
Agent13: catch exception of type java.lang.ClassNotFoundException
Agent13: catch exception of type java.lang.ClassNotFoundException
Agent13: catch exception of type java.lang.NullPointerException
Agent13: uncatch exception of type java.lang.NullPointerException
Agent13: Agent_OnUnload

4. Zjištění podrobnějších informací o třídě a o metodě, kde k výjimce došlo

Informace vypisované třináctým demonstračním agentem (tj. jméno třídy představující typ výjimky) je ještě vhodné doplnit o další důležité údaje, především o jméno třídy a jméno metody, kde k výjimce došlo. Získání a následné vypsání těchto informací je již snadné, protože můžeme využít trojici funkcí popsaných již v předchozí části tohoto seriálu. Z tohoto důvodu si již dnes tyto funkce nebudeme podrobněji popisovat, pouze si připomeneme, jak vypadají jejich hlavičky, včetně jmen parametrů:

/* Získání jména a volitelně i signatury metody */
/* na základě identifikátoru metody jmethodID. */
jvmtiError
GetMethodName(
            jvmtiEnv* env,
            jmethodID method,
            char** name_ptr,
            char** signature_ptr,
            char** generic_ptr);
/* Získání identifikátoru třídy pro metodu, jejíž */
/* identifikátor je předán v parametru jmethodID. */
jvmtiError
GetMethodDeclaringClass(
            jvmtiEnv* env,
            jmethodID method,
            jclass* declaring_class_ptr);
/* Získání signatury (standardizovaného jména) třídy */
/* na základě identifikátoru třídy předaného v jclass. */
jvmtiError
GetClassSignature(
            jvmtiEnv* env,
            jclass klass,
            char** signature_ptr,
            char** generic_ptr);

Callback funkci callback_on_exception() upravíme takovým způsobem, aby se vypisoval typ výjimky stejně jako v předchozím demonstračním agentovi a následně se pak vypsala i třída a metoda, kde k výjimce došlo. Tyto informace by měl mít virtuální stroj Javy vždy k dispozici. Na konci callback funkce nesmíme zapomenout na uvolnění všech bloků paměti s využitím funkce Deallocate(). Důvod pro použití této funkce jsme si již vysvětlovali – paměť vrácenou libovolnou JVM TI funkcí (typicky se jedná o řetězec) není možné uvolňovat pomocí free(), protože by se porušil interní mechanismus správy paměti:

/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
    jclass method_class;
    jclass exception_class;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat jmeno a signaturu metody, signaturu tridy i signaturu vyjimky */
    (*jvmti_env)->GetMethodName(jvmti_env, method, &method_name_ptr, &method_signature_ptr, NULL);
    (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &method_class);
    (*jvmti_env)->GetClassSignature(jvmti_env, method_class, &class_name_ptr, NULL);
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) i jmeno tridy */
    updated_class_name_ptr = update_class_name(class_name_ptr, '.');
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* vypis vsech informaci o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
    printf("    generated in method %s%s()\n",
            updated_class_name_ptr,
            method_name_ptr);
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_signature_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)class_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}

5. Čtrnáctý demonstrační příklad – výpis třídy a metody, kde k výjimce došlo

S využitím callback funkce callback_on_exception() uvedené v předchozí kapitole velmi snadno vytvoříme dalšího (v celkovém pořadí již čtrnáctého) demonstračního JVM TI agenta, jehož zdrojový text je vypsán pod tímto odstavcem:

/*
 * Demonstracni agent, ktery dokaze zaregistrovat vznik
 * zachycovane i nezachycovane vyjimky.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu k tomuto souboru
 * pres volbu -Icesta_k_jvm
 */
#include <jvmti.h>
 
/*
 * Jmeno agenta pouzite ve zpravach vypisovanych
 * na standardni vystup.
 */
#define AGENT_NAME "Agent14:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/* Zamek pouzivany agentem */
jrawMonitorID  global_lock;
 
 
/*
 * Vypis kodu chyby a chybove zpravy na standardni vystup.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem,
 * vypise se chybove hlaseni na standardni vystup.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Vytvoreni zamku.
 */
jvmtiError create_raw_monitor(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot create raw monitor");
 
    return error_code;
}
 
/*
 * Vstup do kriticke sekce.
 */
static void enter_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorEnter(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot enter with raw monitor");
}
 
/*
 * Vystup z kriticke sekce
 */
static void exit_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorExit(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot exit with raw monitor");
}
 
/*
 * Nastaveni pozadovanych schopnosti agenta.
 */
jvmtiError set_capabilities(jvmtiEnv *jvmti)
{
    jvmtiCapabilities capabilities;
    jvmtiError error_code;
 
    memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 
    /* vyuzivame pouze jednu specialni schopnost agenta */
    capabilities.can_generate_exception_events = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Uprava jmena tridy pro tisk.
 */
char* update_class_name(char *class_name_ptr, char replace_to)
{
    char *class_name_ptr_;
    char *c;
    if (class_name_ptr != NULL)
    {
        /* odstraneni pocatecniho L na zacatku jmena tridy */
        class_name_ptr_ = class_name_ptr;
        if (class_name_ptr_[0] == 'L')
        {
            class_name_ptr_++;
        }
        /* nahrada znaku ; za tecku ci jiny definovany znak */
        char *last_char = class_name_ptr_ + strlen(class_name_ptr_) - 1;
        if (*last_char == ';')
        {
            *last_char = replace_to;
        }
        /* nahrada vsech znaku '/' za '.' */
        c = class_name_ptr_;
        for (c = class_name_ptr_; *c != 0; c++)
        {
            if (*c == '/') *c = '.';
        }
    }
    return class_name_ptr_;
}
 
/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
    jclass method_class;
    jclass exception_class;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat jmeno a signaturu metody, signaturu tridy i signaturu vyjimky */
    (*jvmti_env)->GetMethodName(jvmti_env, method, &method_name_ptr, &method_signature_ptr, NULL);
    (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &method_class);
    (*jvmti_env)->GetClassSignature(jvmti_env, method_class, &class_name_ptr, NULL);
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) */
    updated_class_name_ptr = update_class_name(class_name_ptr, '.');
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* vypis vsech informaci o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
    printf("    generated in method %s%s()\n",
            updated_class_name_ptr,
            method_name_ptr);
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_signature_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)class_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}
 
/*
 * Registrace callback funkce zavolane pri zachyceni vyjimky.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_EXCEPTION */
    callbacks.Exception = &callback_on_exception;
 
    error_code = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error_code, "Cannot set JVM TI callbacks");
    return error_code;
}
 
/*
 * Nastaveni jedne udalosti, pro nez se ma zavolat callback funkce.
 */
jvmtiError set_event_notification_mode(jvmtiEnv *jvmti, int event)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event, (jthread)NULL);
    check_jvmti_error(jvmti, error_code, "Cannot set event notification");
    return error_code;
}
 
/*
 * Nastaveni udalosti, pro nez se maji zavolat callback funkce.
 */
jvmtiError set_event_notification_modes(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    /* zachytavat pouze udalost zachyceni vyjimky */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_EXCEPTION)) != JNI_OK)
    {
        return error_code;
    }
 
    return error_code;
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
    jvmtiError error_code;
 
    MSG("Agent_OnLoad");
    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL)
    {
        printf("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your J2SE a 1.5 or newer version? JNIEnv's GetEnv() returned %d\n",
                JVMTI_VERSION_1, (int)result);
        return result;
    }
    MSG("JVM TI version is correct");
 
    /* nastaveni pozadovanych schopnosti agenta */
    if ((error_code = set_capabilities(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* registrace vsech callback funkci */
    if ((error_code = register_all_callback_functions(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* nastaveni udalosti, pro nez se maji zavolat callback funkce */
    if ((error_code = set_event_notification_modes(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* vytvoreni zamku */
    if ((error_code = create_raw_monitor(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}
 
/*
 * finito
 */

Způsob překladu a spuštění testovacího javovského programu s agentem:

gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent14.so agent14.c
javac -g Test14.java
java -agentpath:./libagent14.so Test14 2> /dev/null

Informace vypisované čtrnáctým demonstračním JVM TI agentem jsou již mnohem užitečnější, neboť je z nich jasně patrné nejenom to, jaká výjimka vznikla, ale i kde k jejímu vzniku došlo:

Agent14: Agent_OnLoad
Agent14: JVM TI version is correct
Agent14: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass()
Agent14: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run()
Agent14: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass()
Agent14: catch exception of type java.lang.ArrayIndexOutOfBoundsException
    generated in method Test14.method1()
Agent14: catch exception of type java.lang.ArithmeticException
    generated in method Test14.method2()
Agent14: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass()
Agent14: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run()
Agent14: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass()
Agent14: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run()
Agent14: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass()
Agent14: catch exception of type java.lang.NullPointerException
    generated in method Test14.method4()
Agent14: uncatch exception of type java.lang.NullPointerException
    generated in method Test14.throwSomeException()
Agent14: Agent_OnUnload

6. Přečtení čísla řádku, kde k výjimce došlo

Informaci o tom, ve kterém místě aplikace výjimka vznikla, je ještě vhodné doplnit o přesné číslo řádku. Opět se pro nás nebude jednat o žádnou velkou novinku, protože do callback funkce callback_on_exception() je předán i parametr jlocation location, v němž je předán index instrukce v bajtkódu Javy, na němž k výjimce došlo. My již vlastně umíme převést tento údaj na číslo řádku, protože pro (většinu) metod lze s využitím JVM TI funkce nazvané GetLineNumberTable() získat tabulku, v níž jsou ve strukturách typu jvmtiLineNumberEntry uvedeny údaje o tom, na které instrukci začíná každý řádek zdrojového kódu (obecně platí, že jeden řádek javovského zdrojového kódu může být přeložen do většího množství instrukcí bajtkódu). Pro připomenutí si uveďme hlavičku funkce GetLineNumberTable() i strukturu jvmtiLineNumberEntry:

/* Vrátí tabulku obsahující dvojice číslo řádku - index instrukce. */
/* Taktéž vrátí počet záznamů uložených v této tabulce. */
jvmtiError (JNICALL *GetLineNumberTable) (
            jvmtiEnv* env,
            jmethodID method,
            jint* entry_count_ptr,
            jvmtiLineNumberEntry** table_ptr);
/* Struktura jednoho řádku v tabulce určené pro převod mezi číslem řádku */
/* a indexem první instrukce odpovídající tomuto řádku. */
struct jvmtiLineNumberEntry {
            jlocation start_location;
            jint line_number;
};

Teoreticky sice pro zjištění čísla řádku na základě informace o identifikátoru metody a umístění instrukce můžeme využít funkci get_line_number() uvedenou v posledním demonstračním příkladu v předchozí části tohoto seriálu, ale v praxi velmi rychle zjistíme, že tato funkce nekontroluje, zda je možné převodní tabulku získat pro všechny metody. Ve skutečnosti totiž nelze převodní tabulku získat například pro nativní metody (tam to ostatně dává smysl) a některé typy výjimek vznikají právě v nativních metodách, kde se pro vygenerování výjimky používají mechanismy známé z rozhraní JNI (možná se k popisu tohoto rozhraní v tomto seriálu ještě dostaneme). Nicméně úprava funkce get_line_number() tak, aby tento problém detekovala, není složitá, pouze se v případě chybového stavu vrátí speciální hodnota –1, která bude zpracována přímo v callback funkci callback_on_exception():

/*
 * Ziskani cisla radku pro zadanou metodu a index instrukce.
 */
int get_line_number(jvmtiEnv *jvmti_env, jmethodID method, jlocation location)
{
    int count;
    int line_number = 0;
    int i;
    jvmtiLineNumberEntry *location_table;
    jvmtiError error_code;
 
    if (method == NULL)
    {
        return -1;
    }
 
    /* nacteni tabulky s cisly radku a indexy instrukci */
    error_code = (*jvmti_env)->GetLineNumberTable(jvmti_env, method, &count, &location_table);
    /* v nekterych pripadech se nacist tabulku nepodari */
    if (error_code != JVMTI_ERROR_NONE)
    {
        return -1;
    }
 
    /* projit celou tabulkou */
    for (i = 0; i < count - 1; i++)
    {
        jvmtiLineNumberEntry entry1 = location_table[i];
        jvmtiLineNumberEntry entry2 = location_table[i+1];
        /* pokud se lokace nachazi mezi entry1 (vcetne) a entry2 (krome)
         * nasli jsme spravny radek */
        if (location >= entry1.start_location && location < entry2.start_location)
        {
            line_number = entry1.line_number;
            break;
        }
    }
    /* take se muze jednat o uplne posledni instrukci v metode */
    if (location >= location_table[count-1].start_location)
    {
        line_number = location_table[count-1].line_number;
    }
 
    /* dealokace tabulky */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)location_table);
    return line_number;
}

Nová verze callback funkce callback_on_exception() zjišťuje číslo řádku a posléze na základě návratové hodnoty výše popisované funkce get_line_number() vypisuje buď zjištěné číslo řádku „at line _LINE_“, nebo se pouze vypíše informace o tom, že číslo řádku nelze zjistit: „at unknown location“.

/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
    jclass method_class;
    jclass exception_class;
    int line_number;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat jmeno a signaturu metody, signaturu tridy i signaturu vyjimky */
    (*jvmti_env)->GetMethodName(jvmti_env, method, &method_name_ptr, &method_signature_ptr, NULL);
    (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &method_class);
    (*jvmti_env)->GetClassSignature(jvmti_env, method_class, &class_name_ptr, NULL);
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) i jmeno tridy */
    updated_class_name_ptr = update_class_name(class_name_ptr, '.');
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* ziskat cislo radku */
    line_number = get_line_number(jvmti_env, method, location);
 
    /* vypis vsech informaci o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
    printf("    generated in method %s%s() at ",
            updated_class_name_ptr,
            method_name_ptr);
    if (line_number == -1)
    {
        printf("unknown location\n");
    }
    else
    {
        printf("line %d\n", line_number);
    }
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_signature_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)class_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}

7. Patnáctý demonstrační příklad – výpis čísla řádku v rámci třídy, kde k výjimce došlo

Následuje výpis zdrojového kódu patnácté verze demonstračního JVM TI agenta, který dokáže vytisknout přesné umístění vzniku výjimky, tj. název třídy i metody, kde výjimka vznikla a pokud je to možné, je vytisknuto i přesné číslo řádku odpovídající zdrojovému kódu třídy:

Cloud23

/*
 * Demonstracni agent, ktery dokaze zaregistrovat vznik
 * zachycovane i nezachycovane vyjimky.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu k tomuto souboru
 * pres volbu -Icesta_k_jvm
 */
#include <jvmti.h>
 
/*
 * Jmeno agenta pouzite ve zpravach vypisovanych
 * na standardni vystup.
 */
#define AGENT_NAME "Agent15:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/* Zamek pouzivany agentem */
jrawMonitorID  global_lock;
 
 
/*
 * Vypis kodu chyby a chybove zpravy na standardni vystup.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem,
 * vypise se chybove hlaseni na standardni vystup.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Vytvoreni zamku.
 */
jvmtiError create_raw_monitor(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot create raw monitor");
 
    return error_code;
}
 
/*
 * Vstup do kriticke sekce.
 */
static void enter_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorEnter(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot enter with raw monitor");
}
 
/*
 * Vystup z kriticke sekce
 */
static void exit_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->RawMonitorExit(jvmti, global_lock);
    check_jvmti_error(jvmti, error_code, "Cannot exit with raw monitor");
}
 
/*
 * Nastaveni pozadovanych schopnosti agenta.
 */
jvmtiError set_capabilities(jvmtiEnv *jvmti)
{
    jvmtiCapabilities capabilities;
    jvmtiError error_code;
 
    memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 
    /* vyuzivame dve specialni schopnosti agenta */
    capabilities.can_generate_exception_events = 1;
    capabilities.can_get_line_numbers = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Uprava jmena tridy pro tisk.
 */
char* update_class_name(char *class_name_ptr, char replace_to)
{
    char *class_name_ptr_;
    char *c;
    if (class_name_ptr != NULL)
    {
        /* odstraneni pocatecniho L na zacatku jmena tridy */
        class_name_ptr_ = class_name_ptr;
        if (class_name_ptr_[0] == 'L')
        {
            class_name_ptr_++;
        }
        /* nahrada znaku ; za tecku ci jiny definovany znak */
        char *last_char = class_name_ptr_ + strlen(class_name_ptr_) - 1;
        if (*last_char == ';')
        {
            *last_char = replace_to;
        }
        /* nahrada vsech znaku '/' za '.' */
        c = class_name_ptr_;
        for (c = class_name_ptr_; *c != 0; c++)
        {
            if (*c == '/') *c = '.';
        }
    }
    return class_name_ptr_;
}
 
/*
 * Ziskani cisla radku pro zadanou metodu a index instrukce.
 */
int get_line_number(jvmtiEnv *jvmti_env, jmethodID method, jlocation location)
{
    int count;
    int line_number = 0;
    int i;
    jvmtiLineNumberEntry *location_table;
    jvmtiError error_code;
 
    if (method == NULL)
    {
        return -1;
    }
 
    /* nacteni tabulky s cisly radku a indexy instrukci */
    error_code = (*jvmti_env)->GetLineNumberTable(jvmti_env, method, &count, &location_table);
    /* v nekterych pripadech se nacist tabulku nepodari */
    if (error_code != JVMTI_ERROR_NONE)
    {
        return -1;
    }
 
    /* projit celou tabulkou */
    for (i = 0; i < count - 1; i++)
    {
        jvmtiLineNumberEntry entry1 = location_table[i];
        jvmtiLineNumberEntry entry2 = location_table[i+1];
        /* pokud se lokace nachazi mezi entry1 (vcetne) a entry2 (krome)
         * nasli jsme spravny radek */
        if (location >= entry1.start_location && location < entry2.start_location)
        {
            line_number = entry1.line_number;
            break;
        }
    }
    /* take se muze jednat o uplne posledni instrukci v metode */
    if (location >= location_table[count-1].start_location)
    {
        line_number = location_table[count-1].line_number;
    }
 
    /* dealokace tabulky */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)location_table);
    return line_number;
}
 
/*
 * Callback funkce zavolana pri vzniku zachycovane i nezachycovane vyjimky.
 */
static void JNICALL callback_on_exception(
            jvmtiEnv *jvmti_env,
            JNIEnv   *jni_env,
            jthread   thr,
            jmethodID method,
            jlocation location,
            jobject   exception_object,
            jmethodID catch_method,
            jlocation catch_location)
{
    char *method_name_ptr;
    char *method_signature_ptr;
    char *class_name_ptr;
    char *updated_class_name_ptr;
    char *exception_name_ptr;
    char *updated_exception_name_ptr;
    jclass method_class;
    jclass exception_class;
    int line_number;
 
    /* veskere operace se budou provadet v kriticke sekci */
    enter_critical_section(jvmti_env);
 
    /* ziskat tridu, jejiz instanci je vyjimka */
    exception_class = (*jni_env)->GetObjectClass(jni_env, exception_object);
 
    /* ziskat jmeno a signaturu metody, signaturu tridy i signaturu vyjimky */
    (*jvmti_env)->GetMethodName(jvmti_env, method, &method_name_ptr, &method_signature_ptr, NULL);
    (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &method_class);
    (*jvmti_env)->GetClassSignature(jvmti_env, method_class, &class_name_ptr, NULL);
    (*jvmti_env)->GetClassSignature(jvmti_env, exception_class, &exception_name_ptr, NULL);
 
    /* upravit jmeno vyjimky (presneji receno jeji tridy) */
    updated_class_name_ptr = update_class_name(class_name_ptr, '.');
    updated_exception_name_ptr = update_class_name(exception_name_ptr, ' ');
 
    /* ziskat cislo radku */
    line_number = get_line_number(jvmti_env, method, location);
 
    /* vypis vsech informaci o vyjimce */
    printf(AGENT_NAME " %scatch exception of type %s\n",
            catch_method == NULL ? "un" : "",
            updated_exception_name_ptr);
    printf("    generated in method %s%s() at ",
            updated_class_name_ptr,
            method_name_ptr);
    if (line_number == -1)
    {
        printf("unknown location\n");
    }
    else
    {
        printf("line %d\n", line_number);
    }
 
    /* dealokace vsech ziskanych pametovych struktur */
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)method_signature_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)class_name_ptr);
    (*jvmti_env)->Deallocate(jvmti_env, (unsigned char *)exception_name_ptr);
 
    /* a vystup z kriticke sekce */
    exit_critical_section(jvmti_env);
}
 
/*
 * Registrace callback funkce zavolane pri zachyceni vyjimky.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_EXCEPTION */
    callbacks.Exception = &callback_on_exception;
 
    error_code = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error_code, "Cannot set JVM TI callbacks");
    return error_code;
}
 
/*
 * Nastaveni jedne udalosti, pro nez se ma zavolat callback funkce.
 */
jvmtiError set_event_notification_mode(jvmtiEnv *jvmti, int event)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event, (jthread)NULL);
    check_jvmti_error(jvmti, error_code, "Cannot set event notification");
    return error_code;
}
 
/*
 * Nastaveni udalosti, pro nez se maji zavolat callback funkce.
 */
jvmtiError set_event_notification_modes(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    /* zachytavat pouze udalost zachyceni vyjimky */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_EXCEPTION)) != JNI_OK)
    {
        return error_code;
    }
 
    return error_code;
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
    jvmtiError error_code;
 
    MSG("Agent_OnLoad");
    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL)
    {
        printf("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your J2SE a 1.5 or newer version? JNIEnv's GetEnv() returned %d\n",
                JVMTI_VERSION_1, (int)result);
        return result;
    }
    MSG("JVM TI version is correct");
 
    /* nastaveni pozadovanych schopnosti agenta */
    if ((error_code = set_capabilities(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* registrace vsech callback funkci */
    if ((error_code = register_all_callback_functions(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* nastaveni udalosti, pro nez se maji zavolat callback funkce */
    if ((error_code = set_event_notification_modes(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* vytvoreni zamku */
    if ((error_code = create_raw_monitor(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}
 
/*
 * finito
 */

Patnáctý agent vypíše následující informace, které je vhodné ověřit i na zdrojovém kódu testovací třídy (především fakt, že se skutečně tisknou správná čísla řádku):

Agent15: Agent_OnLoad
Agent15: JVM TI version is correct
Agent15: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass() at unknown location
Agent15: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent15: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent15: catch exception of type java.lang.ArrayIndexOutOfBoundsException
    generated in method Test15.method1() at line 13
Agent15: catch exception of type java.lang.ArithmeticException
    generated in method Test15.method2() at line 25
Agent15: catch exception of type java.lang.ClassNotFoundException
    generated in method java.lang.ClassLoader.findBootstrapClass() at unknown location
Agent15: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent15: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent15: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader$1.run() at line 217
Agent15: catch exception of type java.lang.ClassNotFoundException
    generated in method java.net.URLClassLoader.findClass() at line 222
Agent15: catch exception of type java.lang.NullPointerException
    generated in method Test15.method4() at line 51
Agent15: uncatch exception of type java.lang.NullPointerException
    generated in method Test15.throwSomeException() at line 63
Agent15: Agent_OnUnload

8. Zdrojové kódy všech demonstračních příkladů

Kvůli snazšímu udržování všech zdrojových kódů demonstračních JVM TI agentů byl vytvořen Mercurial repositář, jenž je dostupný na adrese http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/. Poslední verze dnes popisovaných JVM TI agentů i další potřebné skripty jsou dostupné na následujících adresách:

9. Odkazy na Internetu

  1. The JVM Tool Interface (JVM TI): How VM Agents Work
    http://www.oracle.com/technet­work/articles/javase/jvm-ti-141370.html
  2. JVM Tool Interface Version 1.2
    http://docs.oracle.com/ja­vase/7/docs/platform/jvmti/jvmti­.html
  3. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  4. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  5. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  6. ClojureScript One: Index
    http://clojurescriptone.com/in­dex.html
  7. ClojureScript One: Documentation
    http://clojurescriptone.com/do­cumentation.html
  8. ClojureScript One: Wiki
    https://github.com/brento­nashworth/one/wiki
  9. ClojureScript: Quick Start
    https://github.com/clojure/clo­jurescript/wiki/Quick-Start
  10. Getting Started with ClojureScript (and FW/1)
    http://corfield.org/entry/getting-started-with-clojurescript-and-fw-1
  11. First ClojureScript experiences: using Raphaël
    http://maurits.wordpress.com/2012/02/13/fir­st-clojurescript-experiences-using-raphael/
  12. Raphaël-JavaScript Library
    http://raphaeljs.com/
  13. A detailed installation Guide for VimClojure 2.2
    http://www.duenas.at/new_ho­mepage/vimclojure
  14. VimClojure : A filetype, syntax and indent plugin for Clojure
    http://www.vim.org/scripts/scrip­t.php?script_id=2501
  15. Nailgun server
    http://www.martiansoftware­.com/nailgun/background.html
  16. SLIME (Wikipedia)
    http://en.wikipedia.org/wiki/SLIME
  17. slime.vim
    http://s3.amazonaws.com/mps/slime.vim
  18. Textový editor Vim jako IDE: 1. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide/
  19. Textový editor Vim jako IDE: 2. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-2-cast/
  20. Textový editor Vim jako IDE: 3. část (omni completion)
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-3-cast/
  21. Textový editor Vim jako IDE: 4. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-4-cast/
  22. Textový editor Vim jako IDE: 5. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-5-cast/
  23. Textový editor Vim jako IDE: 6. část – Vim Script
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-6-cast-vim-script/
  24. Textový editor Vim jako IDE: 7. část – Vim Script
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-7-cast-vim-script/
  25. Textový editor Vim jako IDE: 8. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-8-cast/
  26. Textový editor Vim jako IDE: 9. část – pluginy Netrw a snipMate
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-9-cast-pluginy-netrw-a-snipmate/
  27. Textový editor Vim jako IDE: 10. část – různé tipy a triky
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-10-cast-ruzne-tipy-a-triky/
  28. Přenos textů mezi Vimem a dalšími aplikacemi
    http://www.root.cz/clanky/prenos-textu-mezi-vimem-a-dalsimi-aplikacemi/
  29. Textový editor Vim: konfigurace pravítka a stavového řádku
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-12-cast-konfigurace-pravitka-a-stavoveho-radku/
  30. Textový editor Vim: automatické formátování textů
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-14-cast-automaticke-formatovani-textu/
  31. Textový editor Vim: automatické formátování textů
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-automaticke-formatovani-textu-dokonceni/
  32. Textový editor Vim: editace XML a HTML
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-15-cast-editace-xml-a-html/
  33. Textový editor Vim: kooperace mezi Vimem a skripty
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-16-cast-kooperace-mezi-vimem-a-skriptovacimi-jazyky/
  34. Textový editor Vim: kooperace mezi Vimem a jazykem Perl
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-17-cast-kooperace-mezi-vimem-a-jazykem-perl/
  35. Textový editor Vim: konfigurace a překlad Vimu
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-18-cast-konfigurace-a-preklad-vimu/
  36. Counterclockwise
    http://code.google.com/p/cou­nterclockwise/
  37. Clojure IDEs – The Grand Tour
    http://www.bestinclass.dk/in­dex.clj/2010/03/clojure-ides-the-grand-tour-getting-started.html
  38. Light Table – a new IDE concept
    http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/
  39. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  40. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  41. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  42. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  43. Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-i-getting.html
  44. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  45. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  46. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  47. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  48. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  49. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  50. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  51. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  52. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  53. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  54. Clojure.org: Clojure home page
    http://clojure.org/downloads
  55. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  56. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  57. Clojure.org: Atoms
    http://clojure.org/Atoms
  58. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  59. A Couple of Clojure Agent Examples
    http://lethain.com/a-couple-of-clojure-agent-examples/
  60. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  61. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  62. 4Clojure
    http://www.4clojure.com/
  63. ClojureDoc
    http://clojuredocs.org/
  64. Clojure (Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  65. Clojure (Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  66. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  67. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  68. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  69. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  70. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/
  71. JSR 223: Scripting for the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=223
  72. JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=292
  73. Java 7: A complete invokedynamic example
    http://niklasschlimm.blog­spot.com/2012/02/java-7-complete-invokedynamic-example.html
  74. InvokeDynamic: Actually Useful?
    http://blog.headius.com/2007/01/in­vokedynamic-actually-useful.html
  75. A First Taste of InvokeDynamic
    http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html
  76. Java 6 try/finally compilation without jsr/ret
    http://cliffhacks.blogspot­.com/2008/02/java-6-tryfinally-compilation-without.html
  77. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  78. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  79. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  80. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  81. Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
    http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/
  82. Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
    http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/
  83. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  84. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  85. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  86. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  87. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  88. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  89. BCEL Home page
    http://commons.apache.org/bcel/
  90. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  91. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  92. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  93. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  94. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  95. ASM Home page
    http://asm.ow2.org/
  96. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  97. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  98. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  99. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  100. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  101. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  102. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  103. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  104. Cobertura
    http://cobertura.sourceforge.net/
  105. FindBugs
    http://findbugs.sourceforge.net/
  106. GNU Classpath
    www.gnu.org/s/classpath/
  107. Java VMs Compared
    http://bugblogger.com/java-vms-compared-160/
  108. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp.org/en/jsr/de­tail?id=223
  109. Scripting for the Java Platform
    http://java.sun.com/develo­per/technicalArticles/J2SE/Des­ktop/scripting/
  110. Scripting for the Java Platform (Wikipedia)
    http://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  111. Java Community Process
    http://en.wikipedia.org/wi­ki/Java_Specification_Requ­est
  112. Java HotSpot VM Options
    http://www.oracle.com/technet­work/java/javase/tech/vmop­tions-jsp-140102.html
  113. Great Computer Language Shootout
    http://c2.com/cgi/wiki?Gre­atComputerLanguageShootout
  114. Java performance
    http://en.wikipedia.org/wi­ki/Java_performance
  115. Trying the prototype
    http://mail.openjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  116. Better closures (for Java)
    http://blogs.sun.com/jrose/en­try/better_closures
  117. Lambdas in Java: An In-Depth Analysis
    http://www.infoq.com/articles/lambdas-java-analysis
  118. Class ReflectiveOperationException
    http://download.java.net/jdk7/doc­s/api/java/lang/Reflective­OperationException.html
  119. Scala Programming Language
    http://www.scala-lang.org/
  120. Run Scala in Apache Tomcat in 10 minutes
    http://www.softwaresecret­weapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes
  121. Fast Web Development With Scala
    http://chasethedevil.blog­spot.cz/2007/09/fast-web-development-with-scala.html
  122. Top five scripting languages on the JVM
    http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855
  123. Proposal: Indexing access syntax for Lists and Maps
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  124. Proposal: Elvis and Other Null-Safe Operators
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  125. Java 7 : Oracle pushes a first version of closures
    http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/
  126. Groovy: An agile dynamic language for the Java Platform
    http://groovy.codehaus.org/Operators
  127. Better Strategies for Null Handling in Java
    http://www.slideshare.net/Step­han.Schmidt/better-strategies-for-null-handling-in-java
  128. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  129. Java Virtual Machine
    http://en.wikipedia.org/wi­ki/Java_virtual_machine
  130. ==, .equals(), compareTo(), and compare()
    http://leepoint.net/notes-java/data/expressions/22com­pareobjects.html
  131. New JDK7 features
    http://openjdk.java.net/pro­jects/jdk7/features/
  132. Project Coin: Bringing it to a Close(able)
    http://blogs.sun.com/darcy/en­try/project_coin_bring_clo­se
  133. CloseableFinder source code
    http://blogs.sun.com/darcy/re­source/ProjectCoin/Closea­bleFinder.java
  134. Joe Darcy blog about JDK
    http://blogs.sun.com/darcy
  135. Java 7 – more dynamics
    http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/
  136. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/index.html

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.