SCA z językami skryptowymi w wykonaniu Apache Tuscany, Jetty i Maven 2
Z Jacek Laskowski - Wiki Projektanta Java EE
Celem artykułu jest demonstracja możliwości Apache Tuscany jako środowiska wykonawczego SCA (Service Component Architecture), które umożliwia tworzenie SCAleń opartych o usługi napisane w językach skryptowych. Możliwość ich uruchamienia Tuscany zawdzięcza dostępnym implementacjom języków skryptowych w Javie, np. Groovy, Rhino, Jython, JRuby, jednakże z pojawieniem się Java 6 z obsługą standardu JSR 223 (Scripting for the Java Platform) należy się spodziewać, że stanie się to kanonem programistycznym i tworzenie rozwiązań z językami skryptowymi nie będzie niczym unikatowym. Poza możliwością programowania skryptowego do tworzenia usług, implementacja SCA w wykonaniu Apache Tuscany jest przede wszystkim darmowa. Mówi się również, że Apache Tuscany ma podjąć się roli platformy referencyjnej dla SCA i SDO.
Gotowy projekt do uruchomienia (cała struktura projektu wraz z niezbędnymi elementami składowymi opisanymi poniżej) można pobrać jako sca-dictionary-web.zip. Wystarczy jedynie wykonać polecenie mvn clean jetty:run-exploded, co spowoduje uruchomienie SCAlenia w środowisku serwera Jetty i wejść na stronę http://localhost:8080/sca-dictionary-web/.
Utworzenie projektu SCAlenia - sca-dictionary-web
Rozpoczynamy tworzenie SCAlenia od utworzenia projektu sca-dictionary-web za pomocą M2. Tworzymy aplikację internetową, więc skorzystamy z archetypu maven-archetype-webapp (domyślnie byłby to maven-archetype-quickstart, który utworzyłby jedynie bibliotekę - plik jar).
$ mvn archetype:create -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.0 -DgroupId=pl.jaceklaskowski.sca.dictionary -DartifactId=sca-dictionary-web [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'archetype'. [INFO] ---------------------------------------------------------------------------- [INFO] Building Maven Default Project [INFO] task-segment: [archetype:create] (aggregator-style) [INFO] ---------------------------------------------------------------------------- ... [INFO] Archetype created in dir: c:\sca-dictionary-web [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 12 seconds [INFO] Finished at: Fri May 25 01:02:12 CEST 2007 [INFO] Final Memory: 5M/254M [INFO] ------------------------------------------------------------------------
Przechodzimy do katalogu c:/sca-dictionary-web
$ cd sca-dictionary-web
Kolejne polecenia wydawane będą właśnie z tego katalogu.
Na bazie poprzedniego artykułu - SCAlenie (kompozyt) z Apache Tuscany i Apache Maven, w którym przygotowaliśmy środowisko do rozpoznawania SCA w wydaniu Apache Tuscany i zbudowaliśmy przykładowe SCAlenie, rozbudujemy nasz aktualny projekt internetowy. Pobierzmy aplikację dostępną jako sca-dictionary.zip i rozpakujmy ją w wybranym przez siebie katalogu, np. c:/sca-dictionary. Zaporzyczymy od niego kilka klas do naszego nowego projektu.
Kopiujemy klasy należące do pakietu pl.jaceklaskowski.sca.dictionary z katalogu src/main/java aplikacji sca-dictionary z poprzedniego artykułu do katalogu src/main.
$ cp -R ../sca-dictionary/src/main/java src/main/
Podobnie postępujemy z deskryptorem SCAlenia - Dictionary.composite.
$ cp ../sca-dictionary/src/main/resources/Dictionary.composite src/main/resources/
Utworzenie konfiguracji projektu - pom.xml
Na zakończenie zestawiania projektu zmodyfikujemy plik konfiguracyjny projektu pom.xml na następującą treść.
<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.sca.dictionary</groupId> <artifactId>sca-dictionary-web</artifactId> <packaging>war</packaging> <version>1.0</version> <name>SCAlenie Dictionary w Aplikacji Internetowej</name> <url>http://www.JacekLaskowski.pl</url> <repositories> <repository> <id>apache.snapshot</id> <url>http://people.apache.org/repo/m2-snapshot-repository</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.apache.tuscany.sca</groupId> <artifactId>tuscany-host-webapp</artifactId> <version>1.0-incubating-SNAPSHOT</version> </dependency> <dependency> <groupId>org.apache.tuscany.sca</groupId> <artifactId>tuscany-implementation-java-runtime</artifactId> <version>1.0-incubating-SNAPSHOT</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.apache.tuscany.sca</groupId> <artifactId>tuscany-implementation-script</artifactId> <version>1.0-incubating-SNAPSHOT</version> <scope>runtime</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.2</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>${artifactId}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration>
Niepoprawny język.
Musisz wybrać język w następujący sposób: <source lang="html4strict">...</source>
Języki obsługiwane w podświetlaniu składni:
abap, actionscript, actionscript3, ada, apache, applescript, apt_sources, asm, asp, autoit, avisynth, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_mac, caddcl, cadlisp, cfdg, cfm, cil, cmake, cobol, cpp, cpp-qt, csharp, css, d, dcs, delphi, diff, div, dos, dot, eiffel, email, erlang, fo, fortran, freebasic, genero, gettext, glsl, gml, gnuplot, groovy, haskell, hq9plus, html4strict, idl, ini, inno, intercal, io, java, java5, javascript, kixtart, klonec, klonecpp, latex, lisp, locobasic, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, make, matlab, mirc, modula3, mpasm, mxml, mysql, nsis, oberon2, objc, ocaml, ocaml-brief, oobas, oracle11, oracle8, pascal, per, perl, php, php-brief, pic16, pixelbender, plsql, povray, powershell, progress, prolog, properties, providex, python, qbasic, rails, rebol, reg, robots, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, sql, tcl, teraterm, text, thinbasic, tsql, typoscript, vb, vbnet, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xml, xorg_conf, xpp, z80
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.3</version>
</plugin>
</plugins>
</build>
</project>
Otwieramy projekt w wybranym przez siebie środowisku programistycznym (IDE), np. w przypadku NetBeans 6.0, który wspiera projekty M2 jak własne, sprowadza się to do otwarcia katalogu z projektem podczas, gdy dla Eclipse IDE należy utworzyć pliki konfiguracyjne dla projektu poleceniem mvn eclipse:eclipse. Podobnie jest w przypadku IntelliJ IDEA, gdzie korzystamy z polecenia mvn idea:idea. Wybór IDE nie ma większego znaczenia na dalszą pracę.
$ mvn eclipse:eclipse [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'eclipse'. [INFO] ---------------------------------------------------------------------------- [INFO] Building SCAlenie Dictionary w Aplikacji Internetowej [INFO] task-segment: [eclipse:eclipse] [INFO] ---------------------------------------------------------------------------- ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 6 seconds [INFO] Finished at: Fri May 25 01:05:16 CEST 2007 [INFO] Final Memory: 6M/254M [INFO] ------------------------------------------------------------------------
Utworzenie deskryptora aplikacji internetowej - web.xml
Modyfikujemy deskryptor aplikacji internetowej - web.xml, aby zawierał deklarację klasy przechwytującej zdarzenie uruchomienia aplikacji w kontenerze servletów i uruchomił Tuscany. Jest to typowy sposób integracji technologii niekoniecznie stricte internetowych ze środowiskiem servletów.
Plik web.xml znajduje się w katalogu src/main/webapp/WEB-INF.
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"> <display-name>SCAlenie Dictionary w Aplikacji Internetowej</display-name> <listener> <listener-class>org.apache.tuscany.sca.webapp.TuscanyContextListener</listener-class> </listener> </web-app>
Jest to jeden z dwóch opisanych w Web Application Integration story goals sposobów uruchomienia Tuscany w środowisku aplikacji internetowej.
Utworzenie deskryptora internetowego SCAlenia - sca-contribution.xml
Na koniec utworzymy plik sca-contribution.xml definiujący SCAlenie w aplikacji internetowej.
<contribution xmlns="http://www.osoa.org/xmlns/sca/1.0"> <deployable composite="Dictionary" /> </contribution>
Plik sca-contribution.xml umieszczamy w katalogu src/main/webapp/META-INF.
Utworzenie przykładowej strony internetowej - index.jsp
Modyfikujemy przykładowy plik index.jsp, który jest faktyczną częścią aplikacji internetowej, która skorzysta ze SCAlenia na poniższy. Plik znajduje się w katalogu src/main/webapp i został automatycznie stworzony przez M2.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <%@ page import="org.apache.tuscany.sca.host.embedded.SCADomain" %> <%@ page import="org.apache.tuscany.sca.webapp.SCADomainHelper" %> <%@ page import="pl.jaceklaskowski.sca.dictionary.DictionaryService" %> <%@ page contentType="text/html;charset=UTF-8" language="java"%> <% SCADomain scaDomain = (SCADomain) application.getAttribute(SCADomainHelper.SCA_DOMAIN_ATTRIBUTE); DictionaryService dictionaryService = scaDomain.getService(DictionaryService.class, "DictionaryServiceComponent"); %> <html> <head> <title>SCA z językami skryptowymi w wykonaniu Apache Tuscany, Jetty i Maven 2</title> </head> <body> <table> <tr> <th>Słownik</th> <th>Wynik tłumaczenia słowa "SCAlenie"</th> </tr> <tr> <td>Test słownika polsko-angielskiego</td> <td align="center"><%= dictionaryService.translate("SCAlenie", "pl", "en") %></td> </tr> <tr> <td>Test słownika angielsko-polskiego</td> <td align="center"><%= dictionaryService.translate("SCAlenie", "en", "pl") %></td> </tr> <tr> <td>Test słownika niemiecko-polskiego</td> <td align="center"><%= dictionaryService.translate("SCAlenie", "de", "pl") %></td> </tr> </table> </body> </html>
Uruchomienie SCAlenia w aplikacji internetowej
Uruchamiamy naszą aplikację internetową korzystając z wtyczki Jetty dla Maven.
$ mvn clean jetty:run-exploded [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'jetty'. [INFO] ---------------------------------------------------------------------------- [INFO] Building SCAlenie Dictionary w Aplikacji Internetowej [INFO] task-segment: [clean, jetty:run-exploded] [INFO] ---------------------------------------------------------------------------- ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3 minutes 11 seconds [INFO] Finished at: Fri May 25 01:12:48 CEST 2007 [INFO] Final Memory: 12M/254M [INFO] ------------------------------------------------------------------------
Otwieramy stronę http://localhost:8080/sca-dictionary-web w przeglądarce i doświadczamy wspaniałego uczucia (połowicznego) sukcesu!
Krok pierwszy uruchomienia SCAlenia w środowisku aplikacji internetowej zakończony pomyślnie. Przechodzimy do kolejnego kroku, którym jest skorzystanie z języka skryptowego do utworzenia kolejnej usługi, z której skorzysta SCAlenie.
Utworzenie usługi GermanPolishDictionaryService w Groovy
Przystąpimy do utworzenia dodatkowej usługi w Groovy, która rozszerzy możliwości SCAlenia o funkcjonalność słownika niemiecko-polskiego.
Utworzenie interfejsu usługi GermanPolishDictionaryService
Interfejs usługi nie będzie niczym zaskakującym - ot, zwykły interfejs w Javie (w zasadzie kopia poprzednich)
package pl.jaceklaskowski.sca.dictionary;
public interface GermanPolishDictionaryService {
String translate(String word);
}
Plik GermanPolishDictionaryService.java umieszczamy w katalogu src/main/java/pl/jaceklaskowski/sca/dictionary.
Utworzenie implementacji usługi GermanPolishDictionaryService
Właśnie teraz ujawni się cała siła Tuscany jako środowiska realizującego specyfikację SCA, który umożliwia budowanie SCAleń z usług implementowanych w językach skryptowych. Wybierzmy jeden - Groovy i napiszmy realizację usługi GermanPolishDictionaryService.
def translate(word) {
return "de_" + word
}
Plik GermanPolishDictionaryServiceImpl.groovy umieszczamy w katalogu src/main/resources/pl/jaceklaskowski/sca/dictionary/groovy.
Uaktywnienie usługi GermanPolishDictionaryService
Rozpoczniemy od modyfikacji implementacji komponentu DictionaryServiceComponent, tj. modyfikacji klasy pl.jaceklaskowski.sca.dictionary.DictionaryServiceImpl (plik znajduje się w katalogu src/main/java/pl/jaceklaskowski/sca/dictionary) tak, aby zawierała poniższą treść.
package pl.jaceklaskowski.sca.dictionary;
import org.osoa.sca.annotations.Reference;
public class DictionaryServiceImpl implements DictionaryService {
private PolishEnglishDictionaryService polishEnglishDictionaryService;
private EnglishPolishDictionaryService englishPolishDictionaryService;
private GermanPolishDictionaryService germanPolishDictionaryService;
@Reference
public void setPolishEnglishDictionaryService(PolishEnglishDictionaryService polishEnglishDictionaryService) {
this.polishEnglishDictionaryService = polishEnglishDictionaryService;
}
@Reference
public void setEnglishPolishDictionaryService(EnglishPolishDictionaryService englishPolishDictionaryService) {
this.englishPolishDictionaryService = englishPolishDictionaryService;
}
@Reference
public void setGermanPolishDictionaryService(GermanPolishDictionaryService germanPolishDictionaryService) {
this.germanPolishDictionaryService = germanPolishDictionaryService;
}
public String translate(String word, String fromLanguage, String toLanguage) {
if (fromLanguage.equals("pl") && toLanguage.equals("en")) {
return polishEnglishDictionaryService.translate(word);
} else if (fromLanguage.equals("en") && toLanguage.equals("pl")) {
return englishPolishDictionaryService.translate(word);
} else if (fromLanguage.equals("de") && toLanguage.equals("pl")) {
return germanPolishDictionaryService.translate(word);
}
// no translation; pass it along
return word;
}
}
Jak w przypadku poprzednio wykorzystanych usług klasa komponentu deklaruje zależność (za pomocą adnotacji @Resource) od nowej usługi GermanPolishDictionaryService, a metoda translate wywołuje ją w przypadku wystąpienia odpowiednich parametrów wejściowych.
Deskryptor SCAlenia - Dictionary.composite
Do tej pory nie skorzystaliśmy z implementacji nowej usługi napisanej w języku skryptowym Groovy. Miejscem, w którym deklarujemy (zamiast oprogramowywania) zależność od usługi i jej implementacji w Groovy jest deskryptor SCAlenia - Dictionary.composite. Zmodyfikujmy plik, który utworzyliśmy na bazie poprzedniego artykułu, na następującą treść. Plik znajduje się w katalogu src/main/resources.
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="Dictionary">
<component name="DictionaryServiceComponent">
<implementation.java class="pl.jaceklaskowski.sca.dictionary.DictionaryServiceImpl" />
<reference name="polishEnglishDictionaryService" target="PolishEnglishDictionaryServiceComponent" />
<reference name="englishPolishDictionaryService" target="EnglishPolishDictionaryServiceComponent" />
<reference name="germanPolishDictionaryService" target="GermanPolishDictionaryServiceComponent" />
</component>
<component name="PolishEnglishDictionaryServiceComponent">
<implementation.java class="pl.jaceklaskowski.sca.dictionary.EnglishPolishDictionaryServiceImpl" />
</component>
<component name="EnglishPolishDictionaryServiceComponent">
<implementation.java class="pl.jaceklaskowski.sca.dictionary.PolishEnglishDictionaryServiceImpl" />
</component>
<component name="GermanPolishDictionaryServiceComponent">
<implementation.script script="pl/jaceklaskowski/sca/dictionary/groovy/GermanPolishDictionaryServiceImpl.groovy"/>
</component>
</composite>
Deklaracja nowej usługi i związanie jej z komponentem DictionaryServiceComponent odbywa się w typowy sposób (przez typowy rozumiemy naszą znajomość SCA i Tuscany z poprzedniego artykułu). Co różni tę deklarację od poprzednich jest fakt skorzystania z elementu implementation.script, w którym wskazujemy na skrypt będący implementacją usługi przy pomocy atrybutu script (poprzednio korzystaliśmy z elementu implementation.java).
Uruchomienie SCAlenia w aplikacji internetowej
Kolejny raz uruchamiamy naszą aplikację internetową.
$ mvn clean jetty:run-exploded [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'jetty'. [INFO] ---------------------------------------------------------------------------- [INFO] Building SCAlenie Dictionary w Aplikacji Internetowej [INFO] task-segment: [clean, jetty:run-exploded] [INFO] ---------------------------------------------------------------------------- ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 18 seconds [INFO] Finished at: Fri May 25 01:22:56 CEST 2007 [INFO] Final Memory: 12M/254M [INFO] ------------------------------------------------------------------------
Otwieramy stronę http://localhost:8080/sca-dictionary-web w przeglądarce i podziwiamy wykonanie usługi napisanej w Groovy!
Pytanie, które z pewnością pojawi się, to skąd wiadomo, że usługa napisana w Groovy w ogóle została uruchomiona?! Poprzednie wykonanie usługi podczas pierwszego uruchomienia aplikacji internetowej zakończyło się wyświetleniem słowa SCAlenie, gdyż słownik niemiecko-polski jeszcze nie istniał (linia u dołu). Uruchomienie drugie zwróciło już słowo de_SCAlenie, co jest wystarczającą gwarancją na uruchomienie SCAlenia z usługą w Groovy.
