2010-08-26

Groovy 1.7.0 i Spring

W jednej z aplikacji które popełniłem, używam skryptów Groovy jak kontrolerów Spring MVC. Jest to wygodne w sytuacji, kiedy kod kontrolera często się zmienia a nie ma możliwości restartu całej aplikacji. Dzięki poniższej definicji:

komponent some.Controller będzie automatycznie przeładowywany (czyli skrypt będzie ponownie kompilowany) jeżeli plik źródłowy Script.groovy się zmieni (ale nie częściej niż podany czas - tutaj 1000 ms). Wszystko działało tak jak powinno do momentu upgrade'u Springa do wersji 3.0.0.RELEASE - wtedy aplikacja przestała zauważać zmiany w skryptach. Uznałem, że chwilowo mogę bez tego żyć a w wersji 3.0.1 pewnie poprawią. Kiedy jednak po kolejnych uaktualnieniach doszedłem do wersji 3.0.3 i dalej nie było ani trochę lepiej, zacząłem intensywniej szukać. Okazało się, że Spring jest niewinny a problemy sprawia sam Groovy, którego wersję podniosłem do 1.7.0 na fali ogólnego uaktualniania aplikacji (w zależnościach Springa jest 1.6.3). Aby dynamiczne przeładowywanie zaczęło znowu działać trzeba albo cofnąć się do wersji 1.6.x, albo użyć najnowszego Groovy (w tej chwili 1.7.4). Dla zainteresowanych zgłoszenie w JIR-ze GROOVY-3981.

2010-08-15

SyntaxHighlighter

Edycja listingów we wpisach zamieszczanych na Bloggerze to makabra. Sam edytor nie jest szczytem marzeń, ale prawdziwy problem to psucie wcięć. Nawet jeżeli umieszczę listing w tagach <pre> które teoretycznie powinny zachowywać formatowanie, to przejście w Bloggerowym edytorze z zakładki Edytuj kod HTML na Nowy post zjada mi co najmniej jedną spację z wcięcia. Początkowo myślałem że robię coś nie tak, ale w sieci można znaleźć sporo informacji o tego typu problemach. Nie znalazłem rady na zjadanie spacji (anyone?), ale znalazłem sposób na formatowanie (dzięki któremu mogę w miarę komfortowo pisać tylko w HTMLu): SyntaxHighlighter. Jest to zestaw JavaScriptów i arkuszy stylów, który automatycznie formatuje listingi w większości popularnych języków programowania. Wystarczy dołączyć do strony odpowiednie skrypty i style (dla ciekawych: prawy przycisk myszy i View Page Source) a potem umieścić listing w tagu <pre> z zaznaczeniem sposobu formatowania:
<pre class="brush: java">
public class Test {
    private String a;
    public Test() {
        // konstruktor
    }
}
</pre>
Efekt wygląda tak:
public class Test {
    private String a;
    public Test() {
        // konstruktor   
    }
}
Jedyne niedociągnięcie jakie zauważyłem, to niepotrzebne wyświetlanie pionowego scrolla w przeglądarkach opartych na silniku WebKit (Safari, Google Chrome).

[aktualizacja 2010.08.16]

Dodałem SyntaxHighlightera do posta o Google App Engine. Very nice indeed.

2010-08-08

Google App Engine

Google App Engine (for Java) umożliwia uruchamianie aplikacji webowych na serwerach Google. Używając jednego konta można zarejestrować do 10 aplikacji, każda z nich może wykorzystywać do 1GB przestrzeni dyskowej, po 1GB transferu wychodzącego i przychodzącego oraz 6.5 godziny znormalizowanego 1) czasu procesora na dobę. I to wszystko za darmo. Całkiem nieźle, prawda? Jest jednak jedno ale: Google'owa maszyna wirtualna Javy i serwer aplikacji mają trochę ograniczeń, na które można trafić próbując uruchomić coś więcej niż "Hello World".

Nie wnikając w zbędne szczegóły: moja aplikacja miała rejestrować obiekty w bazie danych (za pośrednictwem JPA) oraz udostępniać je w postaci zserializowanej do XMLa. Trafiłem na kilka mniej lub bardziej poważnych problemów.


[1] Klasa utrwalana za pomocą JPA nie może być powiązana z kilkoma obiektami tego samego typu. W poniższej sytuacji:
@Entity
class A {
}

@Entity
class B {
@ManyToOne
A pole1;
@ManyToOne
A pole2;
}
dostaniemy wyjątek:
Error in meta-data for B.pole2: Class B has multiple relationship fields of type A: pole1
and pole2. This is not yet supported.


[2] Nie jest obsługiwana strategia dziedziczenia SINGLE_TABLE. W przykładzie:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
class A {
}

@Entity
class B extends A {
}
dostaniemy wyjątek:
Found inheritance strategy 'SINGLE_TABLE' on B.  This strategy is not supported in this
context.

[3] Google App Engine dopuszcza jako identyfikatory obiektów typy Long, String oraz własny typ com.google.appengine.api.datastore.Key. Jednak gdy obiekt ma być powiązany z innym:
@Entity
class A {
@Id
Long id;
}

@Entity class B {
@ManyToOne
A a;
}
jego klucz nie może być typu Long. W powyższym przykładzie dostaniemy wyjątek:
Cannot have a java.lang.Long primary key and be a child object


[4] Nie jest obsługiwane zachłanne wczytywanie powiązanych obiektów. Przy mapowaniu:
@Entity
class A {
}

@Entity
class B {
@ManyToOne(fetch = FetchType.EAGER)
A a;
}
serwer zapisze do logu ostrzeżenie:
WARNING: Meta-data warning for B.a: The datastore does not support joins and therefore
cannot honor requests to eagerly load related objects.
The field will be fetched lazily on first access.
i zgodnie z jego treścią, obiekt powiązany trzeba będzie jawnie doczytać.


[5] Niedostępna jest klasa sun.reflect.ReflectionFactory, która wbrew nazwie nie jest specyficzna dla Sunowskiej implementacji JVM (jest dostępna także w maszynach wirtualnych IBMa i Apple). Co prawda nie próbowałem jej używać bezpośrednio, ale pośrednie owszem. Próba wykorzystania biblioteki XStream do serializacji obiektów do XMLa kończy się wyjątkiem:
java.lang.NoClassDefFoundError: sun.reflect.ReflectionFactory is a restricted class.


[6] W niektórych przypadkach uprawnienia nie pozwalają na akcje, które w typowych maszynach wirtualnych są dopuszczalne. Przykładem może być dziedziczenie z klasy java.io.ObjectOutputStream (wykorzystywane np. przez Apache Wicket i XStream) - App Engine zgłasza wyjątek:
java.security.AccessControlException: access denied
(java.io.SerializablePermission enableSubclassImplementation)


(*)Znormalizowana jednostka zużycia mocy obliczeniowej jest - dość mgliście - określona jako wydajność 1.2 GHz procesora z serii x86 Intela.
© The Useful Pot To Keep Things In
Maira Gall