Skoro można pisać programy javowe w Scali, tj. piszę je w Scali, a wynik uruchamiam jakby były napisane w Javie za pomocą interpretera javy, to do pełni szczęścia pozostaje odnaleźć wtyczki wspierające tworzenie aplikacji w Scali dla Eclipse bądź NetBeans oraz możliwość automatycznej kompilacji i uruchamiania aplikacji wspartej o Apache Maven 2. Pamiętałem, że gdzieś kiedyś czytałem o wtyczce Maven 2 do kompilacji aplikacji napisanych w Scala, więc chwila z Google i mam -
maven-scala-plugin. Więcej informacji o samym repozytorium m2 dla narzędzi Scali w wątku
[ANN] scala-tools.org maven2 repository. Wymaganiem do uruchomienia aplikacji scala z użyciem wtyczki m2 jest jedynie sam Maven oraz Java 5. Jak napisano w
Requirements -
No need to install scala! Dodatkowo jako zachęta do użycia wtyczki maven-scala-plugin napisano, że
The plugin is used by /Lift/, który jest szkieletem webowym napisanym w Scali. Jakby tego było mało już na stronie Lifta -
Lift borrows from the best of existing frameworks, providing: Wicket's designer-friendly templating style (see Lift View First). I już mi się podoba!
Zacznę spokojniej - od utworzenia środowiska opartego o maven, gdzie skorzystam z wtyczki maven-scala-plugin do kompilacji i uruchomienia aplikacji. Zacznę od utworzenia projektu mavenowego z użyciem Eclipse (mógłbym i z ręki, ale po co?! Mógłbym również w NetBeans, ale trochę czasu minęło od ostatniego spotkania z Eclipse, więc będzie tym razem o nim).
Po zainstalowaniu wtyczki
m2eclipse -
Maven Integration for Eclipse - Eclipse umożliwia stworzenie projektu mavenowego.
Najpierw przedstawienie wersji Eclipse.
Ctrl+N i wpisuję
maven i wybieram
Maven project.
Wciskam przycisk
Next dwukrotnie i w kolejnym panelu wybieram
maven-archetype-quickstart.
Next i podaję dane projektu:
- Group Id: pl.jaceklaskowski.scala
- Artifact Id: scala-witaj-swiecie
- Version: 1.0.0
- Package: pl.jaceklaskowski.scala
Finish i projekt
scala-witaj-swiecie utworzony.
Pora na zmiany w związku z wykorzystaniem Scali jako języka programowania w projekcie. W pliku pom.xml dodaję zmiany zgodnie z wytycznymi na
Using the plugin (common).
Zawsze cieszy mnie, kiedy poznając jedną rzecz dowiaduję się innych, równie istotnych informacji, i tym razem nie obyło się inaczej. W dokumentacji maven-scala-plugin wspomniano o kolejnej cesze mavena, gdzie brak określenia wersji wtyczki przy jej braku w lokalnym repozytorium jest możliwe wyłącznie, jeśli uruchomiono mvn z opcją
-U. Pamiętam, że kiedyś miałem z tym problem i właśnie byłem zmuszony określić wersję wtyczki korzystając z
http://mvnrepository.com/. Teraz mam i na to rozwiązanie.
I kolejna rzecz poboczna, o której wiedziałem, ale nigdy nie stosowałem. W Eclipse można edytować pliki xmlowe w edytorze XML, który zazwyczaj włączony jest w trybie
Source, gdzie do dyspozycji mamy
Ctrl+Spacja upraszczająca dodanie nowych elementów, bądź rozszerzenie istniejących. Tym razem postanowiłem przyjrzeć się trybowi (zakładce)
Design (zakładki dostępne są w lewym dolnym rogu edytora). Jeśli teraz wybiorę element
project i wcisnę prawy klawisz myszy pojawi się menu, za pomocą którego mogę dodać nowy element do pliku (a mam ich kilka do dodania związanych z wtyczką maven-scala-plugin). Przełączając się między trybami
Design i
Source można połączyć wyznaczenie odpowiedniego miejsca dla nowego elementu z pomocą trybu
Design, a uzupełnić go techniką Copy-Paste mając gwarancję, że plik jest zgodny z DTD czy XML Schema (po kilku chwilach okazało się, że nowy element zawsze dodawany jest na końcu pliku, więc moja wiara o poprawne ułożenie elementów zgodnie z xsd zdaje się być nieuprawniona).
Ostatecznie plik
pom.xml projektu prezentuje się następująco:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.jaceklaskowski.scala</groupId>
<artifactId>scala-witaj-swiecie</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>scala-witaj-swiecie</name>
<url>http://www.jaceklaskowski.pl</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</pluginRepository>
</pluginRepositories>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Kolejnym krokiem opisanym w
Using the plugin (common) jest
Displaying scala help and version. Tutaj wydaje mi się, że szybciej uruchomiłbym polecenie z poziomu linii poleceń, ale dla dobra sprawy przemogłem się i zdefiniowałem wykonanie polecenia w Eclipse w
External Tools Configurations (
Run > External Tools). Definiuję nowe polecenie
z moją własną instalacją
Apache Maven 2.0.9 w
c:/apps/apache-maven.
Wciśnięcie
Run i...
czyli pracuję z
Scala compiler version 2.6.0-final -- (c) 2002-2007 LAMP/EPFL. Dla zwrócenia uwagi ostatnia dostępna wersja to Scala 2.7.1.RC2 (z 21. kwietnia 2008), a w międzyczasie pojawiła się i Scala 2.7.0-final (z 6. marca 2008).
Tak mi się przypomniało, kiedy przeglądałem ustawienia Eclipse, że podczas ostatniego szkolenia IBM WebSphere Process Servera i IBM WebSphere Integration Developera, o którym wspominałem w
Doświadczenia szkoleniowe i trochę o jpcap oraz Ubuntu Beryl poznałem kolejną cechę Eclipse, gdzie przełączanie się widoków w trakcie uruchamiania serwera można wyłączyć w sekcji
Console (
Window > Preferences > Run/Debug).
Jeśli wyłączyłbym
Show when program writes to standard out oraz
Show when program writes to standard error to ciągłe przełączanie się okien zostałoby raz na zawsze wyłączone. Tym razem nie ma to wielkiej wartości, bo nie będę uruchamiał żadnego serwera, ale w przyszłości na pewno i wrócę do tego ustawienia.
A wracając do Scali, jest jeszcze krok
Displaying the command line usedco nie zakończyło się czymś imponującym
oraz
Changing the scala version, gdzie korzysta się ze zmiennej
${scala.version}, ale nic poza tym, gdzie i jak się ją ustawia (za moment o tym).
W
Usage zaprezentowano również sposób konfiguracji wtyczki do tworzenia raportów, ale na razie jakie raporty są dostępne jest poza moją wiedzą i zainteresowaniem. Mimo wszystko dodaję sekcję
reporting do pom.xml.
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
</plugin>
</plugins>
</reporting>
Okazało się, że więcej informacji jest na stronie
Scaladoc. Jak dla mnie zaczyna mi to wyglądać na lekki bałagan w dokumentacji wtyczki.
Podczas kopiowania zawartości
reporting ze strony okazało się, że brakuje w niej elementów końcowych dla
groupId,
artifactId oraz
version. Już miałem zgłosić błąd w bazie błędów, ale strona
Project Information > Issue Tracking kończy się
No issue management system is defined. Please check back at a later date. Niech będzie zatem, zgłoszę się później.
Zgodnie z
Compiling źródła umieszczam w
src/main/scala, a źródła testów w
src/test/scala (sekcja
sourceDirectory oraz
testSourceDirectory w
build w pom.xml). Do uruchomienia aplikacji korzystam z
Running, gdzie w konfiguracji maven-scala-plugin określa się klasę główną za pomocą parametru konfiguracyjnego
mainClass.
<mainClass>pl.jaceklaskowski.scala.WitajSwiecie</mainClass>
Definiuję skrót
mvn scala-run w Eclipse
i mam już gotowe środowisko do pracy. Na razie wykonanie
mvn scala:run kończy się BUILD FAILURE
ale i tym się zaraz zajmę.
Definiuję nowy katalog ze źródłami - src/main/scala, gdzie tworzę klasę
pl.jaceklaskowski.scala.WitajSwiecie. Bez specjalizowanej wtyczki dla Eclipse dla projektów Scala na razie obchodzę tę niedogodność tworząc katalog, a w nim zwykły plik o rozszerzeniu
scala. Jako wsparcie merytoryczne skorzystam z
Getting Started with Scala oraz
Scala By Example.
package pl.jaceklaskowski.scala
object WitajSwiecie {
def main(args: Array[String]) {
println("Witaj Świecie!")
}
}
W międzyczasie zdefiniowałem zmienną
scala.version z wartością
2.7.1-rc2.
<properties>
<scala.version>2.7.1-rc2</scala.version>
</properties>
Jestem gotów do uruchomienia pierwszej aplikacji WitajSwiecie z pakietu pl.jaceklaskowski.scala (dodanie pakietu było konieczne, aby umieścić klasę w katalogu pl/jaceklaskowski/scala, podobnie jak miałoby to miejsce w Javie).
Wybieram
mvn scala-run z External Tools
i po pobraniu 6-ciomegowego scala-compiler-2.7.1-rc2.jar oraz 3-megowego scala-library-2.7.1-rc2.jar uruchomiłem pierwszą aplikację Scala z poziomu Eclipse.
Nie tak prędko jednak przyszło mi zobaczyć poprawnie uruchomioną aplikację, bo świadomie użyłem polskiej litery w Witaj Świecie i kompilacja zakończyła się błędem:
[INFO] IO error while decoding
C:\.eclipse\scala-witaj-swiecie\src\main\scala\pl\jaceklaskowski\scala\WitajSwiecie.scala with UTF-8
[INFO] Please try specifying another one using the -encoding option
I tutaj zaskoczenie, że mimo, że kompilacja nie zakończyła się poprawnie, to komunikat jest na poziomie INFO i nie kończy wykonania wtyczki (!) Zmieniam ustawienia Eclipse, tak aby domyślnym kodowaniem znaków był
UTF-8 (
Window > Preferences... > General > Workspace - parametr
Text file encoding).
i ponownie uruchomienie
mvn scala-run.
Znowu BUILD FAILURE (!)
[INFO] [scala:run]
[WARNING] Exception in thread "main" java.lang.NoClassDefFoundError: scala/ScalaObject
[WARNING] at java.lang.ClassLoader.defineClass1(Native Method)
[WARNING] at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
[WARNING] at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
[WARNING] at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
[WARNING] at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
[WARNING] at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
[WARNING] at java.security.AccessController.doPrivileged(Native Method)
[WARNING] at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
[WARNING] at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
[WARNING] at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
[WARNING] at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
[WARNING] at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
[WARNING] at pl.jaceklaskowski.scala.WitajSwiecie.main(WitajSwiecie.scala)
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
To już jednak wygląda znajomo po wczorajszym spotkaniu ze Scalą -
Pierwsze spotkanie ze Scalą - lektura Scala Tutorial for Java programmers - brakuje
scala-library.jar, ale skoro jestem w środowisku m2, to wystarczy jedynie zadeklarować zależność i voila...powinno wystartować poprawnie. Moment, przecież do uruchomienia Scali korzystam z narzędzia scala, które powinno mi zadbać o tego typu zależności automatycznie (poprzednio błąd pojawiał się jedynie w momencie uruchomienia za pomocą polecenia java).
Uruchomienie polecenia mvn scala-run z ustawionym parametrem <displayCmd>true</displayCmd> w pom.xml:
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- http://scala-tools.org/mvnsites/maven-scala-plugin/run-mojo.html -->
<displayCmd>true</displayCmd>
<mainClass>pl.jaceklaskowski.scala.WitajSwiecie</mainClass>
<scalaVersion>${scala.version}</scalaVersion>
</configuration>
</plugin>
</plugins>
ukazuje jednak całą prawdę i tylko prawdę:
[INFO] [scala:run]
[INFO] cmd: c:\apps\java5\jre\bin\java
-classpath C:\.eclipse\scala-witaj-swiecie\target\classes
pl.jaceklaskowski.scala.WitajSwiecie
[WARNING] Exception in thread "main" java.lang.NoClassDefFoundError: scala/ScalaObject
[WARNING] at java.lang.ClassLoader.defineClass1(Native Method)
[WARNING] at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
[WARNING] at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
[WARNING] at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
[WARNING] at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
[WARNING] at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
[WARNING] at java.security.AccessController.doPrivileged(Native Method)
[WARNING] at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
[WARNING] at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
[WARNING] at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
[WARNING] at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
[WARNING] at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
[WARNING] at pl.jaceklaskowski.scala.WitajSwiecie.main(WitajSwiecie.scala)
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
czyli uruchomienie aplikacji WitajSwiecie odbywa się z interpreterem java bez pliku scala-library.jar. Dodaję zależność
org.scala-lang:scala-library:2.7.1-rc2 oraz przy okazji podnoszę wersję junit do 4.4.
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.7.1-rc2</version>
</dependency>
</dependencies>
Tym razem uruchomienie mvn scala-run zakończyło się pomyślnie!
[INFO] [scala:run]
[INFO] cmd: c:\apps\java5\jre\bin\java
-classpath C:\.eclipse\scala-witaj-swiecie\target\classes;C:\.m2\org\scala-lang\scala-library\2.7.1-rc2\scala-library-2.7.1-rc2.jar
pl.jaceklaskowski.scala.WitajSwiecie
[INFO] Witaj Świecie!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
Pora spróbować zbudować ziarno EJB z wykorzystaniem Scali - to już nie powinno stanowić problemu skoro wynikiem kompilacji jest postać binarna zrozumiała dla interpretera javy. Zostawiam jako zadanie domowe. Aby podnieść stawkę pierwszemu kto wyśle mi gotowy projekt m2 z ziarnem EJB wystawię podziękowania w moim Notatniku i opublikuję rozwiązanie (jak są inne propozycje nagród chętnie ich wysłucham).
Kompletny plik
pom.xml wygląda następująco:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.jaceklaskowski.scala</groupId>
<artifactId>scala-witaj-swiecie</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>scala-witaj-swiecie</name>
<url>http://www.jaceklaskowski.pl</url>
<properties>
<scala.version>2.7.1-rc2</scala.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.7.1-rc2</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- http://scala-tools.org/mvnsites/maven-scala-plugin/run-mojo.html -->
<displayCmd>true</displayCmd>
<mainClass>pl.jaceklaskowski.scala.WitajSwiecie</mainClass>
<scalaVersion>${scala.version}</scalaVersion>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</pluginRepository>
</pluginRepositories>
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
</plugin>
</plugins>
</reporting>
</project>
Z lektury Scala By Example dzisiaj nici, ale jeszcze do niej wrócę. Pora zabrać się za trochę książek, których właśnie stałem się szczęśliwym posiadaczem, a w nich ta wymarzona
Wicket in Action autorstwa Martijn Dashorst oraz Eelco Hillenius z Manning.
I na koniec pytanie, tym razem opatrzone przykładem napisanym w Scali:
Dlaczego poniższa aplikacja zakończyła się komunikatem HelloWorld.main is not static? jlaskowski@work /cygdrive/c/scala-sandbox
$ c:/apps/scala/bin/scalac HelloWorld.scala
jlaskowski@work /cygdrive/c/scala-sandbox
$ c:/apps/scala/bin/scala HelloWorld
java.lang.NoSuchMethodException: HelloWorld.main is not static
Odpowiedź można umieścić w komentarzu bądź wysłać bezpośrednio do mnie. Nagród nie ma, ale możemy wspólnie o nich pomyśleć (ciekawie byłoby zaangażować jakąś firmę, która byłaby sponsorem niektórych z pytań, w zamian oferując jakąś ciekawą nagrodę dla aktywnych. Ech, się rozmarzyłem).
Gotowy projekt eclipsowy
scala-witaj-swiecie jest do pobrania jako
scala-witaj-swiecie.zip.