IKVM.NET, czyli uruchamianie kodu Java w .NET na przykładzie biblioteki Morfologik i prostego korektora pisowni

09.09.2013

IKVM.NET to wirtualna maszyna Javy napisana w .NET oraz zbiór implementacji podstawowych klasy z Javy dzięki którym możliwe jest uruchamianie kodu Javy bezpośrednio w środowisku .NET/Mono.

Paczka IKVM.NET zawiera dwa główne narzędzia ikvm.exe (maszyna wirtualna) oraz ikvmc.exetranslator bytekodu Javy do .NET IL. IKVM.NET (ikvmc) umożliwia wykonanie szybkiego portu (translacji) biblioteki Javy do .NET na poziomie binarnym! W moim przypadku była to biblioteka Morofologik – analizator morfologiczny, słownik morfologiczny i korektor gramatyczny w postaci pliku JAR (biblioteka Java).

Kilka słów odnośnie samego Morfologika. Jeżeli rozwiązujesz próbujesz rozwiązać problemy związane z analizą, klasyfikacją (lematyzacja, stemming) czy sprawdzaniem poprawności tekstu języka polskiego to będziesz mieć duży problem ze znalezieniem odpowiednich narzędzi (bibliotek) do realizacji tego zadania. Przeglądając Google natkniesz się zapewne na dwa rozwiązania, niestety dostępne tylko dla języka Java. Korektor pisowni - Morfologik oraz stemmery - stempel i morfologik-stemmer (na bazie Morfologika). Tylko jak to pogodzić je z .NET? Przy pomocy IKVM.NET!

Jako królik doświadczalny posłuży mi biblioteka Morfologik. Celem będzie napisanie prostego korektora tekstu języka polskiego w postaci aplikacji konsolowej przyjmującej niepoprawny tekst na wejściu i drukującej jego poprawioną wersję na wyjściu.

Przygotowanie

Źródła biblioteki Morofologik dostępne są tutaj. Ściągamy najnowszą wersję Maven oraz JDK. Konfigurujemy Windows’a tak jak pokazano tutaj. Dodajemy ścieżkę do IVKM.NET do zmiennej systemowej PATH (u mnie C:\ikvm\bin). Instalujemy w GAC’u wszystkie biblioteki z katalogu ikvm (gacutil –i IKVM.AWT.WinForms.dll, gacutil –i IKVM.OpenJDK.Beans.dll itd.).

Kompilacja Morfologik

Jeżeli działa nam już Maven, uruchamiamy linię komend, przechodzimy do katalogu w którym rozpakowaliśmy Morfologika i wykonujemy komendę:

mvn

Po zakończeniu kompilacji w podkatalogach powinny znajdować się następujące pliki (oznaczenie wersji może się różnić):

/morfologik-fsa/target/morfologik-fsa-1.8.0-SNAPSHOT.jar

/morfologik-polish/target/morfologik-polish-1.8.0-SNAPSHOT.jar

/morfologik-speller/target/morfologik-speller-1.8.0-SNAPSHOT.jar

/morfologik-stemming/target/morfologik-stemming-1.8.0-SNAPSHOT.jar

/morfologik-tools/target/morfologik-tools-1.8.0-SNAPSHOT.jard

/morfologik-tools/target/morfologik-tools-1.8.0-SNAPSHOT-standalone.jar

Kopiujemy wszystkie pliki do jednego katalogu np. morfologik.net.

Konwersja przy użyciu ikvmc.exe

Przechodzimy do katalogu gdzie przekopiowaliśmy pliki *.jar i uruchamiamy komendę:

ikvmc -out:morfologik-1.8.0.dll -target:library morfologik-tools-1.8.0-SNAPSHOT-standalone.jar morfologik-speller-1.8.0-SNAPSHOT.jar

Komenda ta skonwertuje dwa pliki jar do postaci biblioteki .NET. Wybrałem paczkę tools-standalone ponieważ zawiera ona wszystko (łącznie ze słownikiem). Można też próbować łączyć fsa/polish/stemming pliki jar, zamiast korzystać z paczki tools.

Aplikacja

Tworzymy nową aplikację konsolową. Dodajemy referencję morfologik-1.8.0.dll oraz IKVM.OpenJDK.Core (z GAC lub katalogu IKVM). Kod aplikacji:

class Program
{
    static void Main(string[] args)
    {
        var speller = new Speller(Dictionary.getForLanguage("pl"));
        var text = args.Aggregate(new StringBuilder(), (builder, s) =>
            {
                var replacements = speller.findReplacements(s);
                var word = replacements.isEmpty()
                            ? s
                            : replacements.get(0);
                builder.AppendFormat("{0} ", word);
                return builder;
            }, builder => builder.ToString().TrimEnd());

        Console.WriteLine(text);
        Console.ReadLine();
    }
}

Na wejście podajemy “W szczebzeszynie chżaszcz bzmi w tżcinie”, zaś na wyjściu dostajemy “W Szczebrzeszynie Chrząszcz brzmi w trzcinie”. Tadam!

Jeżeli podczas uruchamiania programu wykorzystującego przekonwertowaną bibliotekę (lub wywoływaniu przekonwertowanej biblioteki z biblioteki .NET) pojawi się błąd podczas odczytu zasobów aplikacji to należy w programie korzystającym z biblioteki wywołać następujący kod:

Thread.currentThread().setContextClassLoader(((Class)typeof(some_class_from_converted_lib)).getClassLoader());

Jak można podejrzewać, nie ze wszystkimi bibliotekami/programami z Javy pójdzie nam tak łatwo. Nie należy się jednak zrażać, tylko poszperać na Stacku czy w mailing list projektu. Najprawdopodobniej "ktoś miał już taki błąd". Na zachętę dodam, że gdzieś tam w Internecie dev-śmiałkowie przekonwertowali orginalnego Hibernata (tylko po co?), a jeszcze inni pracują nad konwersją Solr'a!