24 czerwca 2008

Nasza "szkapa" JBoss Seam a JPivot oraz polskie JUGi i Quo vadis studencie informatyki?

I kolejny problem z głowy! Paweł Wrzeszcz był na tyle łaskaw, że wskazał mi rozwiązanie mojego problemu braku rozpoznania klas oznaczonych adnotacją @Name jako komponentów seamowych (patrz: komentarz do Definicja komponentów seamowych - /WEB-INF/components.xml). Wystarczyło umieścić pusty (!) plik seam.properties w /WEB-INF/classes i już niepotrzebna była definicja komponentu pozdrow w /WEB-INF/components.xml. Jeszcze raz potwierdziło się moje przypuszczenie, że prowadzenie bloga ma swoje zalety, bo wiele dobrego można się z niego dowiedzieć. Ave Paweł!

Napisał do mnie pewien straceniec, który zamiast zaangażować się w ogrodnictwo czy prowadzenie własnej restauracji, albo zapisać się do harcerstwa i prowadzić jakąś grupę zuchów postanowił zniszczyć sobie kręgosłup i oczy, i w ogóle nabawić się wielu schorzeń próbując rozwiązywać problemy informatyczne ślęcząc przez komputerem godzinami (więcej o "ciekawostkach" długiego przesiadywania przy kompie w wątku Zdrowie programisty na grupie pl.comp.lang.java):

Kończę trzeci rok studiów, więc najwyższa pora dla mnie wybrać sobie dziedzinę IT w której chcę się specjalizować.
Skoro trzeba, to wybrałem. Dokładniej ( jak można się domyślić z kontekstu ) wybrałem mniej więcej to w czym już Pan orientuje się bardzo dobrze, najkrócej mówiąc programowanie w Javie po stronie serwera.
Z tego co znalazłem w internecie widzę, że dziedzina ta jest bardzo obszerna i szczerze mówiąc jej zakres jak na teraz trochę mnie przerasta.
I tu wkracza moja prośba: byłbym bardzo wdzięczny, gdyby mógł Pan napisać mi od czego zacząć przygody z tymi właśnie technologiami Javy.
Oczywiście nie jestem aż taki początkujący, programuję ( tak sobie i w czymkolwiek, ale jednak ) od dobrych sześciu lat, więc to i owo umiem.
Java sama w sobie również obca mi nie jest. Jednak sam nie jestem w stanie wskazać sobie odpowiedniej drogi.


Odpowiedziałem, bo jak tu nie pomóc bratniej duszy (mimo tego Pana!). Postanowiłem opublikować odpowiedź, gdyż nie jest to pierwszy raz, kiedy otrzymuję tego typu zapytanie i zawsze coś innego mnie w danej chwili interesuje, a to skutkuje innym propozycjami tematów do rozpoznania jako warte uwagi. Teraz na fali mam Seama, więc nie obyło się bez Seama, ale nie tylko. Oto moja odpowiedź:

Sądzę, że gdybyś zabrał się za poznawanie JBoss Seama, którego właśnie wałkuję mógłbyś wiele zyskać. Do tego dodać Groovy, GWT, Wicket czy Spring Framework i robi się niezwykle ciekawie. Gdybyś jednak potrzebował coś z wyższej półki to polecam OSGi właśnie po stronie serwera w wykonaniu chociażby S2AS - SpringSource Application Server lub jak kto woli - Spring Dynamic Modules. Dużo dobrego można spodziewać się w nadchodzącym czasie od tej technologii. OSGi jest trochę naciągane na stronę serwera, ale już SCA w wykonaniu Apache Tuscany to pierwsza liga. Zatem wybieraj coś łatwego i przyjemnego, jak Seam z Apache Geronimo (poznajesz EJB3 i JSF z innymi zabawkami), albo pierwsza liga w wykonaniu OSGi i/lub SCA.

I jak? Nie za ciężka dla początkującego programisty? Coś dodać/ująć? Za dużo hype'u?

Poza wspomnianym zapytaniem znalazłem jeszcze jedną podobną wiadomość w mojej skrzynce pocztowej z pytaniem dotyczącym liczby grup javowych (JUG = Java User Group) w Polsce:

Mam takie pytanie - ile jest dokładnie JUG'ów? Tarnów jest jakoś 9. czy źle liczę?

Pewnie, że źle, bo JUGów po JAVArsovii 2008 jak mrówków ;-) Tyle jest JUGów w Polsce, że możnaby raz w miesiącu odwiedzić jeden i wypełnić sobie wyjazdami cały rok! Niesamowite, ile się dzieje wokół inicjatyw javowych. W końcu doczekałem momentu, kiedy można bz trudu dostrzec, że polska społeczność javowa rozruszała się i wiele ciekawych wydarzeń javowych przed nami. Już oczyma wyobraźni widzę kolejne konferencje organizowane przez Warszawa JUG wespół z innymi JUGami (z Krakowem również :P).

Odpowiedź na postawione pytanie o liczbę JUGów w Polsce jest warta opublikowania (kolejność przypadkowa):
Mam nadzieję, że żadnego nie ominąłem - nie wybaczyłbym sobie.

Dziękuję tym samym MaGowi (gość od domeny jug.pl) za udostępnienie nam miejsca w przestrzeni jug.pl. Chwała i sława jego! Ave Marek!

A teraz do Seama. Postanowiłem zmienić sposób rozmieszczania aplikacji, pozbywając się konieczności podawania planu podczas deploy. Przeniesienie pliku geronimo-web.xml do katalogu WEB-INF znosi konieczność podawania go explicite - Geronimo wczyta plik /WEB-INF/geronimo-web.xml automatycznie podczas rozmieszczania aplikacji. Oczywiście zmiana wymaga usunięciem parametru konfiguracyjnego modulePlan dla geronimo-maven-plugin przy zadaniu deploy-module.
 <plugins>
<plugin>
<groupId>org.apache.geronimo.buildsupport</groupId>
<artifactId>geronimo-maven-plugin</artifactId>
<version>2.1.1</version>
...
<executions>
...
<execution>
<id>deploy-module</id>
<phase>pre-integration-test</phase>
<goals>
<goal>deploy-module</goal>
</goals>
<configuration>
<moduleArchive>target/seam-richfaces-tree.war</moduleArchive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Nadeszła w końcu pora sprawdzić działanie JPivota w środowisku seamowym. Głównym kryterium ostatniego skoncentrowania swoich wysiłków na JBoss Seam była właśnie możliwość uruchomienia JPivot, który jest zbiorem znaczników JSP do obsługi bazy OLAP (miałem użyć do zbudowania aplikacji szkieletu Apache Wicket, którym wciąż jestem zachwycony, ale właśnie z braku możliwości uruchomienia znaczników JSP byłem zmuszony do "czasowej migracji" w stronę innego rozwiązania i padło na Seama). Poza możliwością uruchomienia znaczników JSP w Seamie mam gotową integrację JSF i EJB3 wraz ze sprawdzoną integracją z gotowymi bibliotekami kontrolek jsfowych jak RichFaces, ICEFaces czy Tomahawk, więc z dostępnych szkieletów webowych umożliwiających skorzystanie z JSP wybrałem właśnie JBoss Seam. W zasadzie to nie przychodzi mi do głowy nic bardziej trafnego z dostępnych szkieletów aplikacyjnych.

Jest kilka zmian, jakie muszę wykonać, aby uruchomić JPivota w stronach XHTML. Korzystam z reprezentacji xmlowej strony JSP, która opisana jest w rozdziale 10 XML View specyfikacji JavaServer Pages 2.1. Przy takim podejściu znacznik
 <%@ taglib uri="http://www.tonbeller.com/jpivot" prefix="jp" %>
staje się atrybutem znacznika html
 <html xmlns="http://www.w3.org/1999/xhtml" 
xmlns:f="http://java.sun.com/jsf/core"
xmlns:jp="http://www.tonbeller.com/jpivot"
xmlns:wcf="http://www.tonbeller.com/wcf">
...
</html>
JPivot nie jest opublikowany w repozytoriach mavenowych, więc mam dwa podejścia do rozwiązania tematu:
  1. umieścić biblioteki w odpowiednim katalogu projektu, np. src\main\webapp\WEB-INF\lib
  2. zainstalować biblioteki w lokalnym repozytorium mavenowym
Na chwilę obecną wybieram podejście numer 1.

Okazuje się, że podczas rozmieszczenia aplikacji z bibliotekami jpivot oraz pomocniczego wcf pojawiają się błędy związane z poprawnością deskryptorów znaczników JSP - plikach tld (ang. tag library descriptor).

Koniecznie musiałem wprowadzić zmiany w wcf-tags.tld (w WEB-INF/lib/wcf.jar), gdyż:
 org.apache.xmlbeans.XmlException: Invalid deployment descriptor: errors:

c:\geronimo\jar:file:C:\geronimo\repository\pl\jaceklaskowski\seam\seam-richfaces-tree\1.0\seam-richfaces-tree-1.0.war\
WEB-INF\lib\wcf.jar!\META-INF\wcf-tags.tld:6:1: error: cvc-datatype-valid.1.1: string value 'Web Component Framework Tags'
does not match pattern for tld-canonical-nameType in namespace http://java.sun.com/xml/ns/javaee

c:\geronimo\jar:file:C:\geronimo\repository\pl\jaceklaskowski\seam\seam-richfaces-tree\1.0\seam-richfaces-tree-1.0.war\
WEB-INF\lib\wcf.jar!\META-INF\wcf-tags.tld:25:1: error: cvc-enumeration-valid: string value 'EMPTY' is not a valid enumeration
value for body-contentType in namespace http://java.sun.com/xml/ns/javaee
Wystarczyło zmienić short-name na nazwę bez spacji
 <short-name>WebComponentFrameworkTags</short-name>
oraz
 <body-content>EMPTY</body-content>
na
 <body-content>empty</body-content>
Podobnie z /WEB-INF/jpivot/jpivot-tags.tld:
 23:18:13,859 WARN  [JspModuleBuilderExtension] Invalid transformed taglib
org.apache.xmlbeans.XmlException: Invalid deployment descriptor: errors:

C:\geronimo\repository\pl\jaceklaskowski\seam\seam-richfaces-tree\1.0\seam-richfaces-tree-1.0.war\
WEB-INF\jpivot\jpivot-tags.tld:87:1: error: cvc-enumeration-valid: string value 'EMPTY' is not a valid enumeration value
for body-contentType in namespace http://java.sun.com/xml/ns/javaee
Po tym temat miałem z głowy. Ale to nie wszystko. Nie, nie.

Podczas uruchamiania aplikacji pojawił się CNFE:
 Caused by: java.lang.ClassNotFoundException: com.tonbeller.wcf.format.BooleanHandler in classloader 
org.apache.geronimo.configs/myfaces/2.1.1/car
at org.apache.commons.digester.Digester.createSAXException(Digester.java:3181)
at org.apache.commons.digester.Digester.createSAXException(Digester.java:3207)
at org.apache.commons.digester.Digester.startElement(Digester.java:1456)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:533)
at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(AbstractXMLDocumentParser.java:220)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:872)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(XMLDocumentFragmentScannerImpl.java:1693)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:368)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:834)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:148)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1242)
at org.apache.commons.digester.Digester.parse(Digester.java:1745)
at com.tonbeller.wcf.format.FormatterFactory.fillFormatter(FormatterFactory.java:63)
... 33 more
Dodałem do geronimo-web.xml element inverse-classloading, co ostatecznie dało następujący plik-plan geronimo-web.xml:
 <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1">
<environment xmlns="http://geronimo.apache.org/xml/ns/deployment-1.2">
<moduleId>
<groupId>pl.jaceklaskowski.seam</groupId>
<artifactId>seam-richfaces-tree</artifactId>
<version>1.0</version>
<type>war</type>
</moduleId>
<dependencies>
<dependency>
<groupId>org.apache.geronimo.configs</groupId>
<artifactId>system-database</artifactId>
</dependency>
</dependencies>
<inverse-classloading />
</environment>
<context-root>/seam-richfaces-tree</context-root>
<resource-ref>
<ref-name>jdbc/MondrianFoodmart</ref-name>
<resource-link>NoTxDatasource</resource-link>
</resource-ref>
</web-app>
Znaczenie <inverse-classloading /> to zmiana kolejności odszukiwania klas w zarządcach klas (ang. classloader) w Geronimo, tak że klasy poszukiwane są najpierw w zasobach aplikacji, a następnie wyżej w hierarchii zarządców, w samym Geronimo. Kolejny problem odszedł w zapomnienie.

Musiałem usunąć również jsf-api.jar, jstl.jar, junit.jar z WEB-INF/lib, które dokumentacja JPivot nakazuje, aby przekopiować z przykładów, gdzie one są zawarte. W przeciwnym przypadku można natknąć się na następujący LE:
 java.lang.LinkageError: loader constraints violated when linking javax/faces/validator/Validator class
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2395)
at java.lang.Class.getDeclaredMethods(Class.java:1763)
at org.apache.xbean.finder.ClassFinder.<init>(ClassFinder.java:162)
at org.apache.geronimo.jasper.deployment.JspModuleBuilderExtension.createJspClassFinder(JspModuleBuilderExtension.java:181)
at org.apache.geronimo.jasper.deployment.JspModuleBuilderExtension.addGBeans(JspModuleBuilderExtension.java:149)
at org.apache.geronimo.jetty6.deployment.JettyModuleBuilder.addGBeans(JettyModuleBuilder.java:518)
at org.apache.geronimo.j2ee.deployment.SwitchingModuleBuilder.addGBeans(SwitchingModuleBuilder.java:165)
at org.apache.geronimo.j2ee.deployment.EARConfigBuilder.buildConfiguration(EARConfigBuilder.java:647)
at org.apache.geronimo.deployment.Deployer.deploy(Deployer.java:254)
at org.apache.geronimo.deployment.Deployer.deploy(Deployer.java:133)
...
Po zmianie inverse-classloading mam:
 [JNDIResourceProvider] key not found: tbeller.bundles
[JNDIResourceProvider] key not found: tbeller.home
[JNDIResourceProvider] key not found: tbeller.locale
[JNDIResourceProvider] key not found: tbeller.properties
[Contexts] starting up: org.jboss.seam.web.session
[Contexts] starting up: org.jboss.seam.security.identity
[RequestFilter] >>> Request http://localhost:8080/seam-richfaces-tree/jpivot.seam[null][?null]
[RequestSynchronizer] Log normal request Thread = DefaultThreadPool 154, resultURI = null, currentThread = null
[RequestSynchronizer] Log handle-normal Thread = DefaultThreadPool 154,
resultURI = /seam-richfaces-tree/jpivot.seam, currentThread = Thread[DefaultThreadPool 154,5,m
[RequestFilter] redirecting to /seam-richfaces-tree/index.jsp
[RequestFilter] Request Execution total time: 0 ms
przy wywołaniu strony jpivot.xhtml, która prezentuje się następująco (strona oparta jest o Facelets):
 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:jp="http://www.tonbeller.com/jpivot"
xmlns:wcf="http://www.tonbeller.com/wcf"
template="szablon.xhtml">

<ui:define name="body">
<jp:mondrianQuery
id="query01" dataSource="jdbc/MondrianFoodmart" catalogUri="/WEB-INF/queries/FoodMart.xml">
select
{[Measures].[Unit Sales], [Measures].[Store Cost], [Measures].[Store Sales]} on columns,
{([Promotion Media].[All Media], [Product].[All Products])} ON rows
from Sales
where ([Time].[1997])
</jp:mondrianQuery>
</ui:define>

</ui:composition>
Mimo zmian nie udało mi się poprawnie uruchomić JPivot z Seamem na Geronimo, gdyż wciąż zamiast wykonania znacznika jp:mondrianQuery dostawałem stronę, jakby znacznik nie został rozpoznany i wypisywany był na stronie dosłownie. Coś było nie tak, ale po kilku kwadransach miałem dosyć i postanowiłem sprawdzić uruchomienie strony pochodzącej z przykładów jpivota - mondrian.jsp (znajduje się w archiwum jpivot.war w katalogu /WEB-INF/queries).

Tym razem uruchomienie http://localhost:8080/seam-richfaces-tree/mondrian.jsp było znacznie ciekawsze, bo wskazywało na jakąś pracę jpivota:
 00:57:36,281 INFO  [JNDIResourceProvider] key not found: tbeller.bundles
00:57:36,281 INFO [JNDIResourceProvider] key not found: tbeller.home
00:57:36,281 INFO [JNDIResourceProvider] key not found: tbeller.locale
00:57:36,281 INFO [JNDIResourceProvider] key not found: tbeller.properties
00:58:15,812 INFO [Contexts] starting up: org.jboss.seam.web.session
00:58:15,812 INFO [Contexts] starting up: org.jboss.seam.security.identity
00:58:16,265 INFO [RequestFilter] >>> Request http://localhost:8080/seam-richfaces-tree/mondrian.jsp[null][?null]
00:58:16,265 INFO [RequestSynchronizer] Log normal request Thread = DefaultThreadPool 151,
resultURI = null, currentThread = null
00:58:16,265 INFO [RequestSynchronizer] Log handle-normal Thread = DefaultThreadPool 151,
resultURI = /seam-richfaces-tree/mondrian.jsp, currentThread = Thread[DefaultThreadPool 151,5,main]
00:58:16,265 INFO [RequestFilter] redirecting to /seam-richfaces-tree/jpivot.seam
00:58:16,265 INFO [RequestFilter] Request Execution total time: 0 ms
00:58:16,265 INFO [RequestFilter] >>> Request http://localhost:8080/seam-richfaces-tree/jpivot.seam[null][?null]
00:58:16,265 INFO [RequestSynchronizer] Log normal request Thread = DefaultThreadPool 152,
resultURI = /seam-richfaces-tree/mondrian.jsp, currentThread = null
00:58:16,265 INFO [RequestSynchronizer] Log handle-normal Thread = DefaultThreadPool 152,
resultURI = /seam-richfaces-tree/jpivot.seam, currentThread = Thread[DefaultThreadPool 152,5,main]
...
00:58:18,296 INFO [RequestFilter] Request Execution total time: 2031 ms
00:58:59,359 INFO [RequestFilter] >>> Request http://localhost:8080/seam-richfaces-tree/mondrian.jsp[null][?null]
00:58:59,359 INFO [RequestSynchronizer] Log normal request Thread = DefaultThreadPool 153,
resultURI = /seam-richfaces-tree/jpivot.seam, currentThread = null
00:58:59,359 INFO [RequestSynchronizer] Log handle-normal Thread = DefaultThreadPool 153,
resultURI = /seam-richfaces-tree/mondrian.jsp, currentThread = Thread[DefaultThreadPool 153,5,main]
00:59:04,843 INFO [OlapModelTag] enter
00:59:04,859 INFO [MondrianModelFactory] using data source jdbc/MondrianFoodmart
00:59:04,859 INFO [MondrianModelFactory] Config[jdbcUrl=null, jdbcDriver=null, jdbcUser=null, jdbcPassword=null,
dataSource=jdbc/MondrianFoodmart,
schemaUrl="file:/C:/geronimo/repository/pl/jaceklaskowski/seam/seam-richfaces-tree/1.0/seam-richfaces-tree-1.0.war/WEB-INF/queries/FoodMart.xml",
mdxQuery=
select
{[Measures].[Unit Sales], [Measures].[Store Cost], [Measures].[Store Sales]} on columns,
{([Promotion Media].[All Media], [Product].[All Products])} ON rows
from Sales
where ([Time].[1997])
, role=null, dynResolver=null, connectionPooling=null, externalDataSource=null, dynLocale=null, dataSourceChangeListener=null]
00:59:04,859 INFO [MondrianModelFactory]
ConnectString=provider=Mondrian;DataSource=java:comp/env/jdbc/MondrianFoodmart;
Catalog="file:/C:/geronimo/repository/pl/jaceklaskowski/seam/seam-richfaces-tree/1.0/seam-richfaces-tree-1.0.war/WEB-INF/queries/FoodMart.xml"
00:59:05,140 INFO [MondrianModel] setMdxQuery:
select
{[Measures].[Unit Sales], [Measures].[Store Cost], [Measures].[Store Sales]} on columns,
{([Promotion Media].[All Media], [Product].[All Products])} ON rows
from Sales
where ([Time].[1997])
00:59:05,140 INFO [MondrianModel] connectString=provider=Mondrian;DataSource=java:comp/env/jdbc/MondrianFoodmart;
Catalog="file:/C:/geronimo/repository/pl/jaceklaskowski/seam/seam-richfaces-tree/1.0/seam-richfaces-tree-1.0.war/WEB-INF/queries/FoodMart.xml"
00:59:05,140 INFO [MondrianModel] jdbcDriver=null
00:59:05,156 INFO [Log4jStateLogger] initialize default
00:59:05,156 INFO [OlapModelProxy] initializing: com.tonbeller.jpivot.olap.model.CachingOlapModel@955df
00:59:05,156 INFO [MondrianModel] com.tonbeller.jpivot.mondrian.MondrianModel@12ff4b6
00:59:05,203 WARN [MondrianProperties] mondrian.properties can't be found under 'c:\geronimo\.' or classloader
00:59:05,203 INFO [MondrianProperties] Mondrian: loaded 0 system properties
00:59:05,218 INFO [MondrianModel] connectProperties=provider=Mondrian; DataSource=java:comp/env/jdbc/MondrianFoodmart;
Catalog=file:/C:/geronimo/repository/pl/jaceklaskowski/seam/seam-richfaces-tree/1.0/seam-richfaces-tree-1.0.war/WEB-INF/queries/FoodMart.xml
00:59:05,484 INFO [DefaultFileReplicator] Using "C:\geronimo\var\temp\vfs_cache" as temporary files store.
00:59:06,343 ERROR [OlapModelTag]
mondrian.olap.MondrianException: Mondrian Error:Internal error: while building member cache;
sql=[select "time_by_day"."the_year" from "time_by_day" as "time_by_day" group by "time_by_day"."the_year" order by "time_by_day"."the_year" ASC]
at mondrian.resource.MondrianResource$_Def0.ex(MondrianResource.java:785)
at mondrian.olap.Util.newInternal(Util.java:1340)
at mondrian.olap.Util.newError(Util.java:1356)
at mondrian.rolap.SqlStatement.handle(SqlStatement.java:211)
at mondrian.rolap.SqlStatement.execute(SqlStatement.java:142)
at mondrian.rolap.RolapUtil.executeQuery(RolapUtil.java:243)
at mondrian.rolap.RolapUtil.executeQuery(RolapUtil.java:204)
at mondrian.rolap.SqlMemberSource.getMemberChildren2(SqlMemberSource.java:719)
at mondrian.rolap.SqlMemberSource.getMemberChildren(SqlMemberSource.java:649)
at mondrian.rolap.SqlMemberSource.getMemberChildren(SqlMemberSource.java:624)
at mondrian.rolap.SmartMemberReader.readMemberChildren(SmartMemberReader.java:237)
at mondrian.rolap.SmartMemberReader.getMemberChildren(SmartMemberReader.java:201)
at mondrian.rolap.SmartMemberReader.getMemberChildren(SmartMemberReader.java:169)
at mondrian.rolap.SmartMemberReader.getMemberChildren(SmartMemberReader.java:159)
at mondrian.rolap.RolapUtil.lookupMemberInternal(RolapUtil.java:136)
at mondrian.rolap.RolapUtil.lookupMember(RolapUtil.java:102)
at mondrian.rolap.SmartMemberReader.lookupMember(SmartMemberReader.java:209)
at mondrian.rolap.RolapHierarchy.init(RolapHierarchy.java:249)
at mondrian.rolap.RolapCubeHierarchy.init(RolapCubeHierarchy.java:323)
at mondrian.rolap.RolapDimension.init(RolapDimension.java:158)
at mondrian.rolap.RolapCube.init(RolapCube.java:1103)
at mondrian.rolap.RolapCube.<init>(RolapCube.java:286)
at mondrian.rolap.RolapSchema.load(RolapSchema.java:438)
at mondrian.rolap.RolapSchema.load(RolapSchema.java:335)
at mondrian.rolap.RolapSchema.<init>(RolapSchema.java:226)
at mondrian.rolap.RolapSchema.<init>(RolapSchema.java:79)
at mondrian.rolap.RolapSchema$Pool.get(RolapSchema.java:924)
at mondrian.rolap.RolapSchema$Pool.get(RolapSchema.java:733)
at mondrian.rolap.RolapConnection.<init>(RolapConnection.java:152)
at mondrian.rolap.RolapConnection.<init>(RolapConnection.java:83)
at mondrian.olap.DriverManager.getConnection(DriverManager.java:190)
at mondrian.olap.DriverManager.getConnection(DriverManager.java:154)
at com.tonbeller.jpivot.mondrian.MondrianModel.initialize(MondrianModel.java:518)
at com.tonbeller.jpivot.olap.model.OlapModelDecorator.initialize(OlapModelDecorator.java:132)
at com.tonbeller.jpivot.tags.OlapModelProxy$MyState.initialize(OlapModelProxy.java:77)
at com.tonbeller.jpivot.tags.StackStateManager.initializeAndShow(StackStateManager.java:76)
at com.tonbeller.jpivot.tags.OlapModelProxy.initializeAndShow(OlapModelProxy.java:160)
at com.tonbeller.jpivot.tags.OlapModelTag.doEndTag(OlapModelTag.java:81)
at org.apache.jsp.mondrian_jsp._jspx_meth_jp_005fmondrianQuery_005f0(mondrian_jsp.java:137)
at org.apache.jsp.mondrian_jsp._jspService(mondrian_jsp.java:81)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:388)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.apache.geronimo.jetty6.InternalJettyServletHolder.handle(InternalJettyServletHolder.java:65)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1093)
at com.tonbeller.wcf.controller.RequestFilter$MyHandler.normalRequest(RequestFilter.java:139)
at com.tonbeller.wcf.controller.RequestSynchronizer.handleRequest(RequestSynchronizer.java:127)
at com.tonbeller.wcf.controller.RequestFilter.doFilter(RequestFilter.java:263)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.apache.geronimo.jetty6.handler.TwistyWebAppContext.access$101(TwistyWebAppContext.java:40)
at org.apache.geronimo.jetty6.handler.TwistyWebAppContext$TwistyHandler.handle(TwistyWebAppContext.java:65)
at org.apache.geronimo.jetty6.handler.ThreadClassloaderHandler.handle(ThreadClassloaderHandler.java:46)
at org.apache.geronimo.jetty6.handler.InstanceContextHandler.handle(InstanceContextHandler.java:58)
at org.apache.geronimo.jetty6.handler.UserTransactionHandler.handle(UserTransactionHandler.java:48)
at org.apache.geronimo.jetty6.handler.ComponentContextHandler.handle(ComponentContextHandler.java:47)
at org.apache.geronimo.jetty6.handler.TwistyWebAppContext.handle(TwistyWebAppContext.java:59)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:828)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:514)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
at org.apache.geronimo.pool.ThreadPool$1.run(ThreadPool.java:214)
at org.apache.geronimo.pool.ThreadPool$ContextClassLoaderRunnable.run(ThreadPool.java:344)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.sql.SQLException: Table/View 'time_by_day' does not exist.
at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.TransactionResourceImpl.wrapInSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.TransactionResourceImpl.handleException(Unknown Source)
at org.apache.derby.impl.jdbc.EmbedConnection.handleException(Unknown Source)
at org.apache.derby.impl.jdbc.ConnectionChild.handleException(Unknown Source)
at org.apache.derby.impl.jdbc.EmbedStatement.execute(Unknown Source)
at org.apache.derby.impl.jdbc.EmbedStatement.executeQuery(Unknown Source)
at org.apache.derby.iapi.jdbc.BrokeredStatement.executeQuery(Unknown Source)
at org.tranql.connector.jdbc.StatementHandle.executeQuery(StatementHandle.java:49)
at mondrian.rolap.SqlStatement.execute(SqlStatement.java:128)
... 75 more
...
00:59:06,359 INFO [RequestFilter] redirecting to error page /error.jsp
00:59:06,359 INFO [RequestFilter] Request Execution total time: 7000 ms
11:48:48,937 INFO [OlapModelProxy] session timeout
W końcu się coś ruszyło! Jutro zabieram się za dalsze usprawnianie aplikacji seamowej. Na zakończenie jeszcze prezentacja konfiguracji jpivota w środowisku webowej aplikacji seamowej:
 <?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>com.tonbeller.wcf.controller.RequestContextFactory</param-name>
<param-value>com.tonbeller.wcf.controller.RequestContextFactoryImpl</param-value>
</context-param>
<filter>
<filter-name>JPivotController</filter-name>
<filter-class>com.tonbeller.wcf.controller.RequestFilter</filter-class>
<init-param>
<description>forward to this page if session is new</description>
<param-name>indexJSP</param-name>
<param-value>/mondrian.jsp</param-value>
</init-param>
<init-param>
<description> This page is displayed if a the user clicks on a query before the previous query has finished
</description>
<param-name>busyJSP</param-name>
<param-value>/busy.jsp</param-value>
</init-param>
<init-param>
<description>URI of error page</description>
<param-name>errorJSP</param-name>
<param-value>/error.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>JPivotController</filter-name>
<url-pattern>*.seam</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>JPivotController</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
<listener>
<listener-class>com.tonbeller.tbutils.res.ResourcesFactoryContextListener</listener-class>
</listener>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.seam</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
<jsp-config>
<taglib>
<taglib-uri>http://www.tonbeller.com/wcf</taglib-uri>
<taglib-location>/WEB-INF/wcf/wcf-tags.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://www.tonbeller.com/jpivot</taglib-uri>
<taglib-location>/WEB-INF/jpivot/jpivot-tags.tld</taglib-location>
</taglib>
</jsp-config>
<resource-ref>
<res-ref-name>jdbc/MondrianFoodmart</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</web-app>
Pewnie wciąż jeszcze zastanawiasz się, co wspólnego ten wpis ma ze szkolną lekturą "Nasza Szkapa" i skąd to ujęcie w cudzysłowy słowa "szkapa" w tytule wpisu?! Właśnie dzisiaj przyszedł mi do głowy akronim, który postanowiłem upublicznić - SzkApa. Zwrot znaczy ni mniej ni więcej tylko Szkielet Aplikacyjny (trzy pierwsze litery z pierwszego słowa i dwa z drugiego + 'a'). I tym samym na Seama padł cień "Naszej Szkapy" ;-) A może "Nasza Szkapa" to jedynie preludium do Seama i każdy zdroworozsądkowy programista korzystający z Seama wręcz nakazane ma przeczytanie szkolnej lektury?! Ja mam to już za sobą - czytałem przed maturą. A Ty?

Pytanie konkursowe: Jaki element konfiguracyjny JBoss Seam sprawia, że w samodzielnej, webowej aplikacji seamowej rozpoznane zostaną własne komponenty seamowe oznaczone @Name? (zakłada się brak pliku /WEB-INF/components.xml).