Pokazywanie postów oznaczonych etykietą NetBeans. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą NetBeans. Pokaż wszystkie posty

sobota, 7 maja 2011

Inne podejście do przechowywania danych w systemie Android, z wykorzystaniem db4o i NetBeans 6.9

W systemie Android domyślnie mamy dostęp do relacyjnej bazy danych SQLite w której możemy zapisywać informacje z naszej aplikacji. Jest to typowy sposób dostępu do danych w tym systemie. Nie jest dziwne, że do tego systemu została wybrana ta baza. Nie posiada ona wielu magicznych dodatków (jak obecnie popularne kombajny) i założenia jest „Lite” (przynajmniej z nazwy). Z innej strony jest to baza o ciekawym stosunku kodu źródłowego do testów. Odsyłam do artykułu: Extremalne testowanie w SQLite Pawła Lipińskiego. Jak skorzystać z tej bazy jest już opisane na blogu Mirosława Stanka (część 1, część 2, część 3) jak i na helloandroid.  Ja nie będę po raz kolejny opisywał tego rozwiązania, ale postaram się poniżej pokazać inne (ciekawsze?) podejście do tematu, z wykorzystaniem obiektowej bazy danych.

DataBase For Objects, zwana powszechnie db4o, to interesująca alternatywa dla SQLite w systemie Android, jeśli chodzi o składowanie danych. Od wersji 7.5 biblioteka daje wsparcie dla tej platformy. Poniżej przedstawię jak zbudować projekt w NetBeans’ie 6.9 z wykorzystaniem tej biblioteki.

Tworzymy w NetBeans’ie nowy projekt typu AndroidApplication. Następnie do bibliotek dodajemy JAR’a zawierającego db4o.


Następnie warto skorzystać z klasy Db4oHelper przedstawionej przez Damasia Maneiro (niestety nie mam pojęcia jak to po polsku odmienić) w artykule na dzone.com: Using db4o in an Android application. Jest tam sporo kodu odwołującego się do klas z tamtejszego projektu, więc pozwoliłem sobie trochę ten kod zmienić do naszych potrzeb. Listing poniżej.

public class Db4oHelper {

    private static ObjectContainer oc = null;
    private Context context;

    public Db4oHelper(Context ctx) {
        context = ctx;
    }

    public ObjectContainer db() {
        try {
            if (oc == null || oc.ext().isClosed()) {
                oc = Db4oEmbedded.openFile(dbConfig(), 
                        db4oDBFullPath(context));
            }
            return oc;
        } catch (Exception ie) {
            Log.e(Db4oHelper.class.getName(), ie.toString());
            return null;
        }
    }

    private EmbeddedConfiguration dbConfig() throws IOException {
        EmbeddedConfiguration configuration = 
                Db4oEmbedded.newConfiguration();
        return configuration;
    }

    private String db4oDBFullPath(Context ctx) {
        return ctx.getDir("data", 0) + "/" + "database.db4o";
    }

    public void close() {
        if (oc != null) {
            oc.close();
        }
    }
}

Mamy do dyspozycji publiczny konstruktor, który jako argument przyjmuje Context – podobnie jak w SQLite. Potrzebne nam to będzie do umieszczenia pliku z bazą danych w miejscu, gdzie znajdują się dane aplikacji. Następnie mamy metodę db(), która zwraca nam instancję klasy odpowiedzialnej za połączenie z bazą. Mamy jeszcze metodę zwracającą nam konfigurację bazy (domyślna nam początkowo wystarczy) i ścieżkę do pliku z bazą. Na koniec metoda do zamknięcia połączenia z bazą.

Teraz można już napisać klasy, które chcemy składować w bazie danych. Mamy kilka możliwości jak to zrobić. Ja poniżej przedstawię prosty obiekt POJO i odpowiadającą mu klasę DAO. Można oczywiście to wszystko w jedną klasę wrzucić, ale tak będzie ładniej i poprawniej. Poniżej przykładowy kod klasy User.

public class User {

    private String login;
    private String password;

    public User(String login, String password) {
        this.login = login;
        this.password = password;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

W klasie są definiowane 2 pola typu String o nazwach login i password oraz odpowiadające im gettery i settery. No i jeszcze konstruktor przyjmujący te parametry. Jeśli ktoś woli programować w stylu funkcyjnym, to można zrobić pola final i wyrzucić settery. Db4o i tak zadziała, gdyż do ustawiania wartości wykorzystuje refleksje do pól składowych, a nie do setterów.

Proszę zwrócić uwagę, że klasa nie posiada żadnego pola w stylu ID. Dodawanie do klas persystentnych identyfikatorów, to niemalże konieczność w świecie relacyjnym, gdyż dzięki identyfikatorom tworzy się relacje i złączenia przy zapytaniach. W bazach obiektowych tego nie potrzebujemy! Mamy przecież referencje i kolekcje.

Poniżej kod klasy DAO dla klasy User:

public class UserDao {

    private ObjectContainer db;

    public UserDao(Context context) {
        Db4oHelper db4oHelper = new Db4oHelper(context);
        this.db = db4oHelper.db();
    }

    public void saveOrUpdate(User user) {
        db.store(user);
    }

    public void delete(User user) {
        db.delete(user);
    }

    public List<User> getAll() {
        return db.query(User.class);
    }
    
    public List<User> getByLogin(String login) {
        User example = new User(login, null);
        return db.queryByExample(example);
    }

    public List<User> getByLoginAndPassword(
                 String login, String password) {
        User example = new User(login, password);
        return db.queryByExample(example);
    }
}


Klasa posiada konstruktor, za pomocą którego do klasy przekazujemy obiekt typu Context. Jest to klasa android’owa, za pomocą której Db4oHelper może się dostać do odpowiedniego miejsca na zapisanie pliku z bazą. Następnie pobieramy i zapamiętujemy obiekt typu ObjectContainer pochodzący z biblioteki db4o i za jego pomocą odbywa się dostęp do danych. Dalej mamy operacje tworzenia, modyfikacji, usuwania i pobierania wszystkich obiektów - nic specjalnego, ot proste wywołanie pojedynczych metod.

Trochę (ale tylko troszkę) ciekawsza jest metoda getByLogin(), która korzysta z query by example. W skrócie, ta metoda wykonywania zapytań, polega na utworzeniu najpierw obiektu, który będzie przykładem, a następnie wywołaniu odpowiedniej metody zwracającej wszystko, co do danego wzrorca pasuje. Domyślne wartości pól klasy są dopasowywane dowolnie, a te zmodyfikowane dokładnie. W tym przypadku zostaną zwrócone obiekty posiadające ustawione pole login na takie samo jak przekazane w argumencie metody. No prościej się chyba nie da. Istnieją w db4o jeszcze inne bardziej wyrafinowane metody zapytań, ale to nie temat na ten wpis.

Ludzie doświadczeniem z dużymi systemami i relacyjnymi bazami danych, widząc powyższy kod, zaraz zaczną krzyczeć: „a co z dostępem równoległym?”, „a gdzie początek i koniec transakcji?”, itd. Otóż w ogólności db4o wspiera ACID, a poziom izolacji transakcji ma ustawiony na Read Committed. Pytanie z mojej strony: czy rzeczywiście wszędzie tego potrzebujemy? Z tego, co mi wiadomo, to zdarzenia z aktywności w Androidzie są obsługiwane synchronicznie, tzn. w tym samym wątku co GUI. Jeśli nie tworzymy dodatkowych wątków do obsługi zdarzeń (ze względu na ich banalność i szybkość), to nie potrzebujemy transakcji.

Dobra, to teraz by się przydało jakieś GUI dorobić, aby można było zobaczyć jakiekolwiek efekty działania. Po uruchomieniu aplikacji będą się pojawiać dwa przyciski: Create New User i Login. Te będą kierować już do następnych ekranów. Tam będzie można utworzyć nowego użytkownika (i oczywiście go zapisać w bazie), albo się zalogować do aplikacji (na podstawie loginu i hasła zapisanego w bazie). Nie pokazuję tutaj jak utworzyć GUI, gdyż nie jest to tematem tego artykułu. Całkowity kod zamieszczam na końcu wpisu.

Przy pierwszej próbie uruchomienia tak skonstruowanej aplikacji dostaniemy błąd. Jeżeli nie zepsuliśmy niczego po drodze to problem jest jasny. Samo dołączenie biblioteki db4o do projektu nie spowoduje jej dołączenia w trakcie budowy projektu. Należy jeszcze dodać do build.xml następujący target (lub podobny):


    
        
    


Uważam, że środowisko programistyczne powinno być na tyle inteligentne, aby się troszczyło za nas takimi detalami. No ale cóż, nie można mieć wszystkiego. Teraz już powinno pójść gładko i jeśli nic nie popsuliśmy w XML’ach to powinniśmy zobaczyć naszą aplikację. Przykładowe screeny poniżej:



Pod koniec chcę zaznaczyć, ze NetBeans 6.9 nie jest idealnym środowiskiem do pisania aplikacji androidowych (może trzeba jeszcze poczekać?). W trakcie pracy nad projektem natrafiłem na sporo utrudnień. Przykładowo, podczas refaktoringu, po przeniesieniu klasy aktywności, aplikacja przestawała działać. Trzeba było wówczas w AndroidManifest.xml trochę pogrzebać i poprawić nazwy klas. Również zmiana nazwy pakietu wymuszała dodatkowe grzebanie w project.properties.

Z ciekawości sprawdziłem jeszcze jak sobie ze wsparciem Androida radzi IntelliJ IDEA (tak w końcu ją zainstalowałem :) ). Tutaj przeniesienie klasy do innego pakietu nie stanowi problemu. Podobnie z dodaniem biblioteki db4o do projektu. Co do zmiany nazwy pakietu to napiszę o tym w następnym poście (aby wszystko na raz nie było w jednym). Dodatkowo Idea od razu pokazuje Log konsolę z Androida. Chcą ją aktywować w NetBeans'ie trzeba kliknąć: Window -> Output -> ADB Log.

Jeszcze w między czasie tworzenia tego wpisu (nie powstał on w jeden dzień) pojawił się NetBeans 7.0. Tam jednak nawet nie mogłem dodać żadnej zewnętrznej biblioteki do projektu android'owego, więc ewentualna migracja do nowego środowiska musi poczekać.

Link do kodu:

db4oOnAndroid.zip

git://github.com/mstachniuk/db4oOnAndroid.git

//Update (25.09.2011)
Jak słusznie zauważył mój przyjaciel Michał, skoro otwieramy połączenie do bazy danych, to musimy je również zamknąć. Można to zrobić w MainActivity.java w przeciążając metodę onDestroy():

@Override
protected void onDestroy() {
    Db4oHelper db4oHelper = new Db4oHelper(this);
    db4oHelper.close();
    super.onDestroy();
}

poniedziałek, 2 maja 2011

Warunkowa kompilacja j2me w srodowisku NetBeans

Pisanie aplikacji na urządzenia mobilne w j2me nie jest prostą sprawą. Niby specyfikacja KVM (Kilobyte Virtual Machine) jest jedna i narzucona przez Oracle’a (dawniej Sun’a), ale konkretną implementację dostarcza producent danego telefonu komórkowego. Ponadto mamy sporo różnych modelów telefonów, różniących się od siebie wielkością ekranu, ułożeniem przycisków, szybkością procesora itd. Często przychodzi potrzeba dostarczenia aplikacji zoptymalizowanej pod konkretny telefon. Przedstawię poniżej w artykule jak można tego dokonać za pomocą warunkowej kompilacji w NetBeans’ie.

Warunkowa kompilacja to bardzo stary pomysł znany mi z czasów języka C (a może i był używany wcześniej). Polega ona na dodaniu do kodu źródłowego pewnych instrukcji, które zostaną wykonane jeszcze przed właściwą kompilacją. Spowoduje to wybranie odpowiednich fragmentów kodu, które zostaną dołączone do wynikowego programu. Projektanci języka Java całkowicie zrezygnowali z warunkowej kompilacji ze względu, że jest ona wrogiem numer 1 obiektowości. Gdy J2ME ujrzała światło dzienne i gdy coraz więcej telefonów zaczęło wspierać tą funkcjonalność, zrozumiano że jednak pewna możliwość konfigurowania kodu przed właściwą kompilacją jest niezbędna. I tak się narodziła warunkowa kompilacja w NetBeans'ie. Oczywiście wiem, że obecnie mamy XML’e, Dependency injection, Inversion of control i inne możliwości konfiguracji naszych aplikacji, ale nie mają one zastosowania w świecie j2me. Powstały co prawda projekty Signal Framework i Spring ME coś ala IoC dla j2me, ale jak dla mnie to sztuka dla sztuki i zastosowanie zerowe (i tak trzeba całość kompilować).

Dobra czas przejść do konkretów. Poniżej prezentuję fragment kodu, który używam w swojej aplikacji, z wykorzystaniem warunkowej kompilacji:

private void drawHelpMenu(Graphics g) {
//#if MobileTouchScreen
    helpSprite.setPosition(getWidth() - helpSprite.getWidth(),
            getHeight() - helpSprite.getHeight());
    helpSprite.paint(g);
//#else
    String  back = I18n.getText(I18n.LESSON_HELP);
    g.setFont(getSmallFont());
    g.drawString(back, getWidth(), getHeight(),
            Graphics.RIGHT | Graphics.BOTTOM);
//#endif
}

Interesujące fragmenty to te zaczynające się od //#. Są to instrunkcje preprocesora. Jak to działa? Gdy mamy ustawione ability MobileTouchScreen, to wówczas linijki 3-5 zostaną skompilowane, w przeciwnym wypadku linijki 7-10 zostaną wstawione w ciało metody drawHelpMenu(...). Prawda ze proste?

Teraz nadszedł czas na zarządzanie naszymi “zdolnościami” (chyba nienajlepsze tłumaczenie). Klikamy prawym przyciskiem myszy na nazwę projektu i wybieramy z menu opcję Properties. Tam w kategorii Abilities możemy zobaczyć jakie mamy ustawione właściwości dla obecnej konfiguracji naszego projektu. Właściwości mogą posiadać wartość, ale nie muszą.


Proponuję teraz utworzyć sobie nową konfigurację. Z pola Project Configuration wybieramy Add Configuration I wpisujemy nazwę nowej konfiguracji I następnie klikamy OK. Możemy tutaj jeszcze wybrać szablon, na podstawie którego zostanie utworzona konfiguracja.


Po kliknięciu OK wracamy do poprzedniego ekranu i teraz możemy modyfikować naszą konfigurację. Najpierw musimy odznaczyć pole Use Values from „DefaultConfiguration”. Dzieki temu przycisk Add staje się aktywny. Klikamy i w nowym oknie wpisujemy nasze ability MobileTouchScreen.


Klikamy OK i w oknie z właściwościami naszego projektu pojawiła się nowa pozycja w tabelce właściwościami danej konfiguracji.

Teraz chcąc skompilować projekt, używając nowej konfiguracji, należy ją wybrać z menu w górnej części okna.


Jak popatrzymy na nasz kod to zobaczymy, że NetBeans koloruje tło pod kodem nalezącym do warunkowej kompilacji:


Dodatkowo dodał komentarze na początku linii pomiędzy //#else a //#endif. Dzięki temu kod nie będzie dołączony do kompilowanej aplikacji. Zmiany te są zapisywane w pliku na dysku (polecam sprawdzić) i NetBeans nie zawsze chce się odświeżyć (w sensie widoku pliku źródłowego). Warto wtedy zamknąć i otworzyć na nowo plik, aby wiedzieć co edytować. Ponadto mechanizm ten zapewnia bezbolesny eksport do innego środowiska IDE, które nie wspiera tego mechanizmu. Całość zostanie potraktowana jako zwykły komentarz.

Z drugiej strony mechanizm może utrudniać merge’owanie i śledzenie zmian w kodzie. No chyba że wszyscy będą pamiętać, aby przed commit’em jedną, zawsze tą samą konfigurację ustawić.

Czy blok if else endif to wszystko co daje nam warunkowa kompilacja w NetBeans? Nie. Z ciekawszych opcji mamy jeszcze //#condition NAZWA_ABILITY i dzięki której decydujemy czy dany plik ma być dołączony do danego build’a, czy nie. Deklaracja musi być jednak umieszczona na początku pliku!

No dobra, mamy już skonfigurowany kod, ale co z innymi zasobami? Np, z grafiką, którą np. potrzebujemy tylko do wersji z obsługiwanym ekranem dotykowym? Mamy możliwość zdefiniowania zasobów, które mają się w takiej konfiguracji znaleźć. W tym celu przechodzimy do właściwości projektu i wybieramy Librares & Resources. Odznaczamy Use Values from „DefaultConfiguration” i już możemy dodawać katalogi, biblioteki i inne wymagane projekty.

Nie pozostaje teraz nic innego jak wziąć się do roboty i zacząć odpowiednio dostosowywać nasze mobilne aplikacje.

wtorek, 5 kwietnia 2011

Podświetlanie zmian kodu w Eclipsie

Jeśli chodzi o preferowane środowiska programistyczne, to nie jestem ani zagorzałym fanem Eclipse, ani NetBeans'a. Każde z nich ma swoje wady i zalety, każde sprawdza się inaczej w różnych zadaniach. Muszę w końcu na poważnie usiąść do poznawania IntelliJ IDEA i może wtedy sprawa ulubionego IDE się wyjaśni. Tymczasem chcę opisać pewną funkcjonalność, do której się przyzwyczaiłem w NetBeans'ie, a której mi trochę brakowało w Eclipsie. Pokażę również, jak można ją aktywować w tym drugim środowisku.

Funkcjonalność o której wspomniałem to zaznaczanie fragmentu kodu, który został zmodyfikowany (lub dodany). W NetBeans'ie wyglada to tak:


Po lewej stronie, między numeracją linii a kodem, widzimy dwa paski: niebieski i zielony. W Eclipsie trochę mi tego brakowało, ale ostatnio poszukując rozwiązania czegoś tam, przypadkiem trafiłem na potrzebną mi opcję. Podświetlanie zmian w kodzie można włączyć w Preferences w Quick Diff. Najlepiej śledzić zmiany w stosunku do repozytorium, w moim przypadku SVN'a.


Przyda się jeszcze tuning kolorów. Te z NetBeanas'a całkiem mi pasują. Zmiany są kolorowane na (RGB) 160 200 255 (#A0C8FF), a dodane linijki na (RGB) 150 255 150 (#96FF96). Wprowadzając te kolory do Eclipse, okazuje się, że są one trochę inne niż w NetBeans'ie (słabiej widoczne, zlewają się z szarym paskiem).


Nie wiem czemu, ale środowisko (Eclipse Galilieo) trochę inaczej wyświetla / interpretuje te kolory. Po kilku eksperymentach z wartościami RGB, doszedłem do zbliżonych kolorów. Zmiany należy ustawić na 18 119 255 (#1277FF), a dodane fragmenty na 0 255 0 (#00FF00). Efekt jest następujacy.


No już lepiej :) Co prawda, już na tym prostym przykładzie widać, że te dwa środowiska, w różny sposób interpretują wprowadzane zmiany, czasami trochę błędnie wyświetlając, co zostało zmienione, a co dodane. No ale trudno. Ważne, że teraz widać, w której części pliku się coś zmieniło i czy dane zmiany są nam dalej potrzebne. Mi to pomaga przeglądając zmiany przed commitem.

sobota, 19 lutego 2011

Kilka porad na temat MIDP Visual Design w Netbeans'ie

Dzisiaj chciałbym opowiedzieć o kilku sztuczkach, w MIDP Visual Design, który jest dostępny w Netbeans’ie. Tak wiem, że technologia J2ME (czy też JME) jest już trochę przestarzała, gdyż teraz mamy Androidy iPhone’y, BlackBery, Windows 7 i inne ciekawsze cuda na kiju ;) Zamierzam jednak jeszcze kilka postów o j2me na blogu opublikować, ot tak dla potomności.

Z opcji MIDP Visual Design możemy skorzystać tworząc nowy projekt z grupy „Java ME” typu „Mobile Application” w NetBeans’ie. Wystarczy nie odznaczać (gdyż domyślnie jest zaznaczony) CheckBox’a „Create Hello MIDlet”.


Można również do istniejącego projektu dodać Visual MIDlet i uzyskamy podobny efekt.

Narzędzie pozwala nam w prosty sposób zbudować menu użytkownika, przechodzenie pomiędzy ekranami itp. Mamy ładny podgląd gdzie z danego miejsca możemy się przemieścić, czyli widzimy przepływ aplikacji (menu). Należy pamiętać, że generowane menu oparte jest na komponentach wysokopoziomowych (tych dziedziczących z klasy Screen). Przykładowy screen jak może taki przepływ wyglądać poniżej.


Oczywiście w ten sposób nie zrobimy całej aplikacji. Mamy możliwość dodawania kodu w pewnych miejscach i większość rzeczy można wyklikać. Generalnie narzędzie świetnie się nadaje do prototypowania, gdy na szybko trzeba coś pokazać. Nie widzę jednak przeciw wskazań, do użycia tego również na produkcji. Generowany kod jest całkiem dobry. Poniżej kilka wskazówek, związanych z tym oto narzędziem:

1. Możemy wybrać w jaki sposób poszczególne komponenty są inicjowane. Po wybraniu danego Screena w oknie Properties widzimy dostępne opcje. Po za zmianą wyświetlanego tekstu i innych właściwości, możemy wybrać czy mają być Lazy Initialized czy nie.


Proponuję każdemu sprawdzić, jaki kod zostaje wygenerowany przy zmianie tego komponentu. W małych aplikacjach może to nie mieć wielkiego znaczenia, czy komponent jest lazy czy nie, ale te duże / ciężkie komponenty (lub rzadko używane) powinny być leniwie inicjowane. Oczywiście część komponentów może być inicjowana tak, a druga inaczej.

2. Możemy również zmienić sposób generowania logiki, odpowiedzialnej za przechodzenie pomiędzy kolejnymi ekranami. Domyślnie to, która opcja z menu została wybrana, sprawdzane jest na podstawie etykiety (label’ki) przypisanej do danego elementu ekranu. Przykład poniżej:

public void mainMenuAction() {
    // enter pre-action user code here
    String __selectedString = getMainMenu().getString(getMainMenu().getSelectedIndex());
    if (__selectedString != null) {
        if (__selectedString.equals(“Etykieta”)) {
            // ...

Włączając opcję “Index Based Switch” powyższy kod zostanie przekształcony w coś takiego:

public void mainMenuAction() {
    // enter pre-action user code here
    switch (getMainMenu().getSelectedIndex()) {
        case 0:
            // ...

Zachowanie to może mieć znaczenie wówczas, gdy chcemy wprowadzić internacjonalizację do naszej aplikacji i podmianę tekstów na elementach „w locie”. W drugim przypadku logika aplikacji w żaden sposób nie zależy od przypisanej etykiety w danym komponencie, a jedynie na jego położeniu. Po takim zabiegu podmiana tekstów jest prostsza.

3. Odnośnie internacjonalizacji, to w większości kontrolek mamy możliwość podmiany wyświetlanego na niej tekstu (dziedziczymy z klasy Displayable metodę setTitle()). Problem jest jednak z obiektami typu Command, które są immutable, gdyż nie posiadają metod modyfikujących ich stan. Można za to skorzystać z następującej sztuczki: usunąć z danego ekranu istniejące obiekty Command, utworzyć je na nowo i podpiąć. Przykład poniżej:

getSettingsMenuList().removeCommand(getBackCommand());
backCommand = null;
getSettingsMenuList().addCommand(getBackCommand ());

W pierwszej linii usuwamy obecną powiązaną komendę z danym ekranem. W drugiej linii zerujemy referencję do obiektu zwracanego przed getBackCommand(). Na koniec tworzymy obiekt na nowo (wywolanie getBackCommand()) i przypisujemy nową komendę do żądanego elementu. Korzystamy tutaj z faktu, że dana komenda jest leniwie inicjowana i po wejściu w metodę  getBackCommand(), zostanie ona na nowo utworzona.

4. Generalnie często początkujący programiści zderzający się z MIDP Visual Design wkurzają się, że nie mogą tu wszystkiego zrobić czego chcą. Według mnie wszystko da się wyklikać (w zakresie oferowanym przez edytor), ewentualnie dopisując niewielkie kawałki kodu w miejscach, w których edytor nam na to pozwala. Na prawdę nie znalazłem niczego, w czym by edytor mnie ograniczał.

Jednakże chcąc się pozbyć wsparcia ze strony MIDP Visual Design można wykonać pewien trick. Usuwając z dysku plik .vmd i wszystkie komentarze, generowane przez edytor, z pliku .java. Oczywiście musimy zrobić to w innym edytorze tekstowym, a nie pod NetBeans’em. Nie będziemy mogli również wrócić do poprzedniego stanu, ani więcej zobaczyć przepływu aplikacji dla naszego kodu.

Usunięcie komentarzy (przynajmniej część) z pliku z kodem, jest równie ważne jak usunięcie pliku *.vmd. W przypadku usunięcia tylko tego pliku możemy napotkać na problemy z otwarciem projektu pod NetBeans’em.

Poniżej początki komentarzy dodawanych przez NetBeans’a:

//<editor-fold
//</editor-fold
//GEN-LINE
//GEN-BEGIN
//GEN-END

To tyle na dzisiaj. Zachęcam do korzystania z tego edytora. Jak było coś niejasne to piszcie!

czwartek, 12 sierpnia 2010

Podpisywanie apletu, czyli szybki sposób na aplikację osadzoną w przeglądarce

Dzisiaj opiszę pewną retrospekcję jak mi się przydarzyła jakiś czas temu. Przyszedł do mnie kolega (Kaktus) i się pyta mniej wiecej tak:

Kaktus: Ty Stachu, czy taką zwykłą aplikację okienkową napisaną w Swingu da się przenieść do appletu?
Ja: No da się.
Kaktus: A w takiej zwykłej aplikacji okienkowej da się jakoś prosto połączyć z bazą danych?
Ja: No da się.
Kaktus: No a da się więc z apletu połączyć się bezpośrednio z bazą danych?
Ja: No to to już nie koniecznie... Ale zobaczę co da się zrobić...

Aplety to był taki wynalazek który rozpoczął wielką ekspansję języka Java. Na konferencji SunWorld’95 pokazano przeglądarkę internetową potrafiącą wykonywać kod wewnątrz strony WWW – coś co dzisiaj nazywamy apletem. Potem zaczęto dodawać nowe funkcjonalności do języka Java, co było widoczne w kolejnych wersjach języka. Obecnie aplety nie są masowo wykorzystywane w przemyśle (zostały wyparte przez flash’a i inne podobne technologie). Piszą o nich jednak jeszcze w książkach i wykładają na uczelniach wyższych.

Dlaczego więc piszę o apletach skoro to taki staroć? Bo za pomocą apletu możemy w stosunkowo łatwy sposób (zakładając pewną znajomość języka Java) zrobić aplikację działającą w oknie przeglądarki. Takie aplikacje czasem są wymagane na studiach, a bez porządnego wprowadzenia do framework’ów Javowych ciężko się do tego zabrać. Mamy jeszcze .NET i Visual Studio, który posiada edytor stron HTML typu przeciągnij i upuść (drag and drop). Dzięki takiemu zestawowi można również szybko coś przygotować na przeglądarkę, aby pokazać prowadzącemu na studiach w celu zaliczenia pewnego przedmiotu. Ale ja nie dzisiaj o tym.

Java od początków swojego powstawania była reklamowana jako język bezpieczny i stabilny. Nigdy więcej wycieków pamięci, nigdy więcej sprzątania po sobie i obowiązują twarde restrykcje jak chcemy zrobić coś lepszego. Z tego względu też ograniczenia apletów są bardzo duże. Działają one w piaskownicy o następujących ograniczeniach:

  • nie można pozwalać uruchamiać programów na komputerze
  • nie można komunikować się z innym serwerem niż ten z którego zostały ściągnięte
  • nie można czytać, ani zapisywać plików na komputerze użytkownika
  • nie można zbierać informacji o komputerze użytkownika (poza kilkoma podstawowymi)

Pewien czas się zastanawiałem czy będzie możliwe połączenie z bazą danych z poziomu apletu. Po pewnym czasie mnie oświeciło, jak sobie przypomniałem, że kiedyś widziałem aplet który robił czego „normalnie” nie wolno. Przypomniało mi się wtedy o podpisywaniu apletów.

Wziąłem więc jakiś mój stary projekt (Swing + JDBC + MySQL) i przerobiłem go na szybko na aplet. Pierwsze uruchomienie zgodnie z oczekiwaniami zakończyło się fiaskiem:


java.security.AccessControlException: access denied (java.security.AllPermission )
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at NewJApplet.init(NewJApplet.java:23)
at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Exception: java.security.AccessControlException: access denied (java.security.AllPermission )

Trzeba więc podpisać aplet. Aby szybko to zrobić w NetBeans’ie należy kliknąć prawym na nazwę projektu i przejść do właściwości.


Następnie na zakładce Web Start zaznaczamy Self-signed. Oznacza to ze aplet będzie podpisany przez nas samych.

Przybudowujemy projekt i uruchamiamy w przeglądarce. Udało się! Dostajemy ostrzeżenie czy aby na pewno chcemy uruchomić aplet:

Klikamy Run i możemy się cieszyć apletem działającym w przeglądarce i łączącym się z bazą danych za pomocą JDBC :)

Przy okazji postanowiłem sprawdzić czy można się połączyć z hostem na innym komputerze niż ten z którego został załadowany aplet. Po jakimś czasie spędzonym na konfigurowanie firewall’i i uprawnień na bazie danych w końcu się udało. Możliwe jest więc wyjście poza piaskownice i poza serwer z którego ładowaliśmy aplet :)

Chciałbym jeszcze poruszyć temat uprawnień. W Javie jest możliwe dodawanie dodatkowych uprawnień niektórym aplikacjom za pomocą plików *.policy. W przypadku apletów nie można zdefiniować takiego pliku, aby był pobierany z serwera i użyty przy uruchomieniu danego apletu. Można jedynie ręcznie edytować na lokalnym komputerze plik java.policy, ale to odradzam.

Na koniec powiem, że przygotowując ten wpis opierałem się na książce core Java 2 Podstawy. Jest w niej również ładnie opisane jak zrobić aplikację która jest równocześnie apletem i aplikacją standalone. Polecam początkującym programistom.

poniedziałek, 26 lipca 2010

Dekompilacja kodu w J2ME

Kiedyś ściągnąłem pewną aplikację (napisaną w J2ME) na mój telefon i po pewnym czasie użytkowania okazało się, że należy podać do niej kod aktywacyjny (za który oczywiście należy zapłacić). Zastanawiałem się, czy da się jakoś to ominąć i poniżej opiszę rezultaty mojej rozkminki, jak sobie z tym poradzić.

Jak wiadomo kod Javy jest kompilowany do bytecode'u, a ten uruchamiany na wirtualnej maszynie Javy (eng. Java Virtual Machine, JVM). Dzięki temu kompilujemy raz, a uruchamiamy wszędzie (tzn. tam gdzie jest Java Wirtual Machine). Ilustruje to poniższy obrazek:




Skoro kod nie jest kompilowany do natywnego kodu języka danego urządzenia, to możemy skorzystać z tak zwanej wstecznej inżynierii (ang. reverse engineering). Właściwie to każdy plik wykonywalny można zdekompilować, tylko w przypadku aplikacji pisanych nie w Javie, zazwyczaj po dekompilacji mamy odczynienia z kodem asemblerowym, a ten nie należy do przyjemnych w czytaniu. W przypadku Javy mamy trochę lepiej.

W sieci dostępnych jest kilka dekompilatorów kodu Javowego (DJ Java Decompiler  Java Decompiler  Mocha i inne). Ja posłużę się tym pierwszym. Na cele tego artykułu stworzyłem niewielką aplikację, aby pokazać jak skorzystać z możliwości wstecznej dekompilacji. Nie chciałem łamać jakiejś innej aplikacji, aby nie być posądzonym o ingerencje w nią, co pewnie się wiąże z łamaniem prawa. Z własną aplikacją mogę robić co chcę :P

Zacznijmy więc. Na początek należy zawsze uruchomić aplikację i zobaczyć czego szukamy. Jak nie chce nam sie wrzucać jej na telefon, a mamy zainstalowane Java ME platform SDK możemy odpalić ją na komputerze w ten sposób:

<Java ME platform SDK path>\bin\emulator.exe -Xdescriptor:<JAD file path>

Oczywiście należy podać odpowiednie ścieżki, gdzie mamy zainstalowane SDK i gdzie leży nasza aplikacja. W innych SDK (starszych wersjach, lub innych producentów) mogą być graficzne narzędzia umożliwiające odpalenie aplikacji J2ME na komputerze. Poniżej przedstawiam jak wygląda moja aplikacja odpalona na emulatorze:



Po lewej mamy ekran startowy (specjalnie dodałem kilka opcji, aby coś było), a po prawej ekran po wybraniu opcji "Activation". W ekranie tym należy wpisać odpowiedni kod aktywacyjny. To jest miejsce na którym nam zależy i które chcemy złamać.

Odpalamy DJ Java Decompiler. Następnie File -> Open jako typ pliku wybieramy: Java Archive Files(*.jar) i wskazujemy naszego JARa. Ukazuje nam się poniżej okno podobne do poniższego:


Wciskamy Ctrl + A aby zaznaczyć wszystkie klasy i klikamy Decompile. Wskazujemy miejsce docelowe i w pojawiającym sie komunikacie klikamy Yes i po chwili w wybranym folderze otrzymujemy wynik. Plik *.class jest plikiem otrzymanym bezpośrednio przez wypakowanie JARa (gdyz de facto to zwykłe archiwum), a w pliku *.jad mamy zdekompilowany kod. Ciekaw jestem czemu akurat takie rozszerzenie zostało wybrane przez twórców aplikacji, skoro jest ono używane w procesie instalacji aplikacji komórkowych? Można było przecież utworzyć pliki *.java.

No dobra zajrzyjmy do zdekompilowanego pliku. Kod wygląda czytelnie. Porównałem go KDiff3'em z oryginalnym kodem i po za modyfikacją importów, zmianą standardu kodowania, niewielkimi modyfikacjami i dodanymi kilkoma komentarzami kod sie zasadniczo nie różnił. Utworzyłem nowy projekt i wrzuciłem ten "odzyskany" kod i się poprawnie skompilował i poprawnie działa!

Przejdźmy do analizy kodu. Na początku ukazuje nam się konstruktor klasy (część linii pominięto i zachowano oryginalne formatowanie):




Na początku są tworzone obiekty typu Command i widzimy, że to co nas interesuje ("Activation") jest dodawane do List'y. Następnie dodawane jest okCommand i co najważniejsze wywołanie setCommandListener z przekazaniem obiektu this. Oznacza to, że nasza klasa implementuje CommandListener, do którego są kierowane zdarzenia z naszej formatki. Sprawdzamy powyżej:



Rzeczywiście. Przejdźmy więc do metody zdefiniowanej przez ten interfejs:



Na początku jest sprawdzenie czy obiekt command odpowiada naszej liście opcji. Jeśli tak, to sprawdzane jest czy była to komenda okCommand. Jeśli tak to pobierany jest index aktualnie zaznaczonej opcji i coś się dalej dzieje. Jako, że opcja "Activation" byla na 3ciej pozycji w menu (widać to na screenie), to patrzymy co się dzieje pod dwójką ;)



Wywoływana jest jakaś metoda i następnie na wyświetlaczu jest ustawiane do wyświetlenia activationForm. Dowiedzmy się więcej o tym obiekcie. Wróćmy do konstruktora:



Do activationForm dodawane są dwie kontrolki i znów CommandListener ustawiony na this. Wróćmy wiec do metody commandAction() i zjedźmy trochę niżej:



Tak ten warunek to miejsce gdzie trafia wykonanie naszej aplikacji, gdy wciśniemy Ok na activationForm. Zobaczmy co się dzieje poniżej:



Najpierw pobierany jest tekst z kontrolki, która jest odpowiedzialna za wprowadzenie kodu aktywacyjnego. Następnie sprawdzana jest długość pobranego tekstu i gdy jest ona różna od 12 to wywoływana jest metoda showBadActivationCodeMessage(). Sama nazwa juz sugeruje, że nie jest to nic dobrego. Jaki wniosek z tego? Nasz kod aktywacyjny musi mieć 12 znaków długości. Sprawdźmy co się dzieje gdy warunek ten jest spełniony:



Pobierane są kolejno 3ci 8my i 11sty znak z naszego kodu aktywacyjnego. I jeśli odpowiadają one odpowiednio literom 'A', 'B' i 'C' to odnosimy sukces.

Sprawdźmy więc. Odpalamy naszą aplikację na emulatorze (lub telefonie) i wpisujemy np.: xxxAxxxxBxxCx i prosze: Code OK. Złamaliśmy w ten sposób aplikację J2ME.

Jak widać można w prosty sposób podejrzeć kod obecny w aplikacjach J2ME. Jak sie jednak ustrzec przed tego typu ingerencją w oprogramowanie? Właściwie nie można w 100% się ustrzec przed tego typu atakami. Można jedynie utrudnić śmiałkom zadanie.

Najprostszym sposobem ochrony naszego kodu jest stosowanie tzw. obfluskacji (eng. Obfuscation). Polega ona na "upiększaniu kodu", w taki sposób, aby po dekompilacji ciężej było go odczytać. Jest kilka tego typu narzędzi na rynku. Jedno jest dołączone do Netbeansa. Jeśli mamy projekt J2ME utworzony w tym środowisku klikamy prawym przyciskiem myszy na nasz projekt i przechodzimy do Properties. Następnie klikamy na Obfluscating. Tutaj możemy juz sobie ustawić żądany poziom obfluskacji:



Klikamy OK i przebudowujemy nasz projekt. Teraz po wstecznej dekompilacji kod jest mniej czytelny, np.: pole options zmieniło nazwę na a_javax_microedition_lcdui_List_fld, a inne pola skróciły się do jednoliterowych nazw. Jest to trudniejsze w czytaniu, ale narzędzia pozwalające kolorować składnię trochę nam pomagają. Tutaj pokazuję przykład działania na prostym przykładzie i może efekt nie jest powalający. Przy dużych projektach narzędzia takie potrafią namieszać tak, że kod uzyskany za pomocą wstecznej inżynierii nie uda sie ponownie skompilować!!!

Dlaczego tak sie dzieje? Otóż to co jest dozwolone z poziomu kompilatora javy jest również dozwolone w bytecode'dzie. Jednak są pewne elementy bytecode'u, które nie są dozwolone z poziomu składni języka Java. Np. w bytecode'dzie mogą być metody o tej samej nazwie, przyjmujące te same argumenty a zwracające różną wartość! Odpowiednia metoda zostanie wywołana na podstawie typu do którego będzie rezultat przypisywany, lub jeszcze jakiś innych przesłanek. Analiza takiego kodu jest o wiele trudniejsza i bez dobrego refaktoringu nie da się obejść.

Na nasze szczęście (lub nie) klasa rozszerzająca MIDlet możne być poddana tylko częściowej obfluskacji, więc zawsze łatwo będzie nam znaleźć miejsce wejścia do programu:) Również nazwy metod dostarczanych przez javę (np. w klasie String) nie mogą być zmienione, co trochę ułatwia analizę takiego kodu.

Jak jeszcze można się bronić przed wsteczną dekompilacją? W J2ME nie ma mechanizmu ClassLoader'a, więc w ten sposób nie zabezpieczymy naszej aplikacji. Jedyne co po za obfluskacją przychodzi mi do głowy to własna implementacja odpowiednich mechanizmów zabezpieczających.

Poniżej zamieszczam jeszcze spakowany projekt, na podstawie którego prezentowałem zagadnienie. Będziecie mogli się sami pobawić i zobaczyć jak to działa.


PasswordNeed.zip (17KB)