22 czerwca 2008

Definicja komponentów seamowych - /WEB-INF/components.xml

Wczorajsze problemy opisane w Kolejne dni z JBoss Seam - trochę o geronimo-maven-plugin i @Name wynikały z mojej niewiedzy jak JBoss Seam zarządza komponentami - bytami dostarczającymi funkcjonalności aplikacji. Szczęśliwie, rozwiązanie należy, do tych, które określam "zachowującym czystość technologiczną" i klasy seamowe tak na prawdę są zwykłymi klasami, a ich konfiguracja została umieszczona poza nimi - w pliku xmlowym. To zawsze będzie temat dyskusji, czy adnotacje (będące przypisami do kodu) powinny być jak najbliżej niego, tj. w nim samym, czy poza nim w pliku konfiguracyjnym, który zwykle jest plikiem xmlowym. Decyzję pozostawiam zespołom projektowym, a na chwilę obecną liczyła się dla mnie po prostu możliwość szybkiego utworzenia aplikacji, więc bez zbędnego rozważania, czy właściwe, czy nie, sięgnąłem po wyniesieniu konfiguracji aplikacji seamowej na zewnętrz, do pliku xml - /WEB-INF/components.xml.

Wczoraj skończyłem moje doświadczenia seamowe utworzeniem klasy pl.jaceklaskowski.seam.PozdrowAction z adnotacją @Name (Kolejne dni z JBoss Seam - trochę o geronimo-maven-plugin i @Name).
 package pl.jaceklaskowski.seam;

import java.io.Serializable;

import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.log.Log;

@Name("pozdrow")
public class PozdrowAction implements Serializable {

private static final long serialVersionUID = 1L;

@Logger Log log;

@Create
public void init() {
log.info("Inicjacja PodrowAction za nami");
}

public String getPozdrowienie() {
return "Pozdrowienia z wnętrza PozdrowAction";
}
}
W ten sposób (wydawało mi się, że) zdefiniowałem komponent seamowy pozdrow na wzór jsfowego ziarna zarządzanego. Nic bardziej mylnego. Kiedy uruchomiłem komponent z dedykowaną dla niego stroną (za moment o niej) nic szczególnego się nie wydarzyło - zero komunikatów w dzienniku zdarzeń (log) Geronimo, a wywołanie #{pozdrow.pozdrowienie} zwracało pusty wynik. Z założenia skorzystanie z adnotacji @Name powinno zwolnić mnie z konieczności utworzenia pliku konfiguracyjnego xml, ale niedługo trwało zanim okazało się, że niestety, ale plik jest konieczny.

Nic poza zdefiniowaniem komponentu pozdrow (klasa PozdrowAction) nie zmieniałem w aplikacji i podczas jej uruchomienia żadnej zmiany - PozdrowAction nie było rozpoznane jako komponent seamowy.
 [ServletContextListener] Welcome to Seam 2.0.3.CR1
[Initialization] Namespace: http://jboss.com/products/seam/international, package: org.jboss.seam.international, prefix: org.jboss.seam.international
[Initialization] Namespace: http://jboss.com/products/seam/security, package: org.jboss.seam.security, prefix: org.jboss.seam.security
[Initialization] Namespace: http://jboss.com/products/seam/persistence, package: org.jboss.seam.persistence, prefix: org.jboss.seam.persistence
[Initialization] Namespace: http://jboss.com/products/seam/core, package: org.jboss.seam.core, prefix: org.jboss.seam.core
[Initialization] Namespace: http://jboss.com/products/seam/captcha, package: org.jboss.seam.captcha, prefix: org.jboss.seam.captcha
[Initialization] Namespace: http://jboss.com/products/seam/async, package: org.jboss.seam.async, prefix: org.jboss.seam.async
[Initialization] Namespace: http://jboss.com/products/seam/drools, package: org.jboss.seam.drools, prefix: org.jboss.seam.drools
[Initialization] Namespace: http://jboss.com/products/seam/mail, package: org.jboss.seam.mail, prefix: org.jboss.seam.mail
[Initialization] Namespace: http://jboss.com/products/seam/transaction, package: org.jboss.seam.transaction, prefix: org.jboss.seam.transaction
[Initialization] Namespace: http://jboss.com/products/seam/web, package: org.jboss.seam.web, prefix: org.jboss.seam.web
[Initialization] Namespace: http://jboss.com/products/seam/theme, package: org.jboss.seam.theme, prefix: org.jboss.seam.theme
[Initialization] Namespace: http://jboss.com/products/seam/navigation, package: org.jboss.seam.navigation, prefix: org.jboss.seam.navigation
[Initialization] Namespace: http://jboss.com/products/seam/bpm, package: org.jboss.seam.bpm, prefix: org.jboss.seam.bpm
[Initialization] Namespace: http://jboss.com/products/seam/framework, package: org.jboss.seam.framework, prefix: org.jboss.seam.core.framework
[Initialization] Namespace: http://jboss.com/products/seam/jms, package: org.jboss.seam.jms, prefix: org.jboss.seam.jms
[Initialization] reading /WEB-INF/components.xml
[Initialization] reading
jar:file:/C:/geronimo/repository/pl/jaceklaskowski/seam/seam-richfaces-tree/1.0/seam-richfaces-tree-1.0.war \
/WEB-INF/lib/jboss-seam-2.0.3.CR1.jar!/META-INF/components.xml
[Initialization] reading
jar:file:/C:/geronimo/repository/pl/jaceklaskowski/seam/seam-richfaces-tree/1.0/seam-richfaces-tree-1.0.war \
/WEB-INF/lib/jboss-seam-ui-2.0.3.CR1.jar!/META-INF/components.xml
[Initialization] initializing Seam
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.core.expressions
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.web.isUserInRole
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.security.entityPermissionChecker
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.persistence.persistenceProvider
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.core.locale
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.core.resourceLoader
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.transaction.synchronizations
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.core.locale
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.web.userPrincipal
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.web.parameters
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.bpm.businessProcess
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.core.manager
[Initialization] two components with same name, higher precedence wins: org.jboss.seam.security.identity
[Component] Component: org.jboss.seam.core.init, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.core.Init
[Initialization] Installing components...
[Component] Component: org.jboss.seam.async.dispatcher, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.async.ThreadPoolDispatcher
[Component] Component: org.jboss.seam.captcha.captcha, scope: SESSION, type: JAVA_BEAN, class: org.jboss.seam.captcha.Captcha
[Component] Component: org.jboss.seam.captcha.captchaImage, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.captcha.CaptchaImage
[Component] Component: org.jboss.seam.core.ConversationIdGenerator, scope: APPLICATION, type: JAVA_BEAN, class:
org.jboss.seam.core.ConversationIdGenerator
[Component] Component: org.jboss.seam.core.contexts, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.core.Contexts
[Component] Component: org.jboss.seam.core.conversation, scope: CONVERSATION, type: JAVA_BEAN, class: org.jboss.seam.core.Conversation
[Component] Component: org.jboss.seam.core.conversationEntries, scope: SESSION, type: JAVA_BEAN, class: org.jboss.seam.core.ConversationEntries
[Component] Component: org.jboss.seam.core.conversationListFactory, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.core.ConversationList
[Component] Component: org.jboss.seam.core.conversationPropagation, scope: EVENT, type: JAVA_BEAN, class: org.jboss.seam.core.ConversationPropagation
[Component] Component: org.jboss.seam.core.conversationStackFactory, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.core.ConversationStack
[Component] Component: org.jboss.seam.core.events, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.core.Events
[Component] Component: org.jboss.seam.core.expressions, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.faces.FacesExpressions
[Component] Component: org.jboss.seam.core.interpolator, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.core.Interpolator
[Component] Component: org.jboss.seam.core.locale, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.international.Locale
[Component] Component: org.jboss.seam.core.manager, scope: EVENT, type: JAVA_BEAN, class: org.jboss.seam.faces.FacesManager
[Component] Component: org.jboss.seam.core.resourceBundle, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.core.ResourceBundle
[Component] Component: org.jboss.seam.core.resourceLoader, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.faces.ResourceLoader
[Component] Component: org.jboss.seam.core.validators, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.core.Validators
[Component] Component: org.jboss.seam.exception.exceptions, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.exception.Exceptions
[Component] Component: org.jboss.seam.faces.dataModels, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.faces.DataModels
[Component] Component: org.jboss.seam.faces.facesContext, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.faces.FacesContext
[Component] Component: org.jboss.seam.faces.facesMessages, scope: CONVERSATION, type: JAVA_BEAN, class: org.jboss.seam.faces.FacesMessages
[Component] Component: org.jboss.seam.faces.facesPage, scope: PAGE, type: JAVA_BEAN, class: org.jboss.seam.faces.FacesPage
[Component] Component: org.jboss.seam.faces.httpError, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.faces.HttpError
[Component] Component: org.jboss.seam.faces.redirect, scope: CONVERSATION, type: JAVA_BEAN, class: org.jboss.seam.faces.Redirect
[Component] Component: org.jboss.seam.faces.renderer, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.ui.facelet.FaceletsRenderer
[Component] Component: org.jboss.seam.faces.switcher, scope: PAGE, type: JAVA_BEAN, class: org.jboss.seam.faces.Switcher
[Component] Component: org.jboss.seam.faces.uiComponent, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.faces.UiComponent
[Component] Component: org.jboss.seam.faces.validation, scope: EVENT, type: JAVA_BEAN, class: org.jboss.seam.faces.Validation
[Component] Component: org.jboss.seam.framework.currentDate, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.framework.CurrentDate
[Component] Component: org.jboss.seam.framework.currentDatetime, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.framework.CurrentDatetime
[Component] Component: org.jboss.seam.framework.currentTime, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.framework.CurrentTime
[Component] Component: org.jboss.seam.graphicImage.image, scope: EVENT, type: JAVA_BEAN, class: org.jboss.seam.ui.graphicImage.Image
[Component] Component: org.jboss.seam.international.localeSelector, scope: SESSION, type: JAVA_BEAN, class: org.jboss.seam.international.LocaleSelector
[Component] Component: org.jboss.seam.international.messagesFactory, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.international.Messages
[Component] Component: org.jboss.seam.international.timeZone, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.international.TimeZone
[Component] Component: org.jboss.seam.international.timeZoneSelector, scope: SESSION, type: JAVA_BEAN, class:
org.jboss.seam.international.TimeZoneSelector
[Component] Component: org.jboss.seam.mail.mailSession, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.mail.MailSession
[Component] Component: org.jboss.seam.navigation.pages, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.navigation.Pages
[Component] Component: org.jboss.seam.navigation.safeActions, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.navigation.SafeActions
[Component] Component: org.jboss.seam.persistence.persistenceContexts, scope: CONVERSATION, type: JAVA_BEAN, class:
org.jboss.seam.persistence.PersistenceContexts
[Component] Component: org.jboss.seam.persistence.persistenceProvider, scope: STATELESS, type: JAVA_BEAN, class:
org.jboss.seam.persistence.HibernatePersistenceProvider
[Component] Component: org.jboss.seam.security.configurationFactory, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.security.Configuration
[Component] Component: org.jboss.seam.security.entityPermissionChecker, scope: STATELESS, type: JAVA_BEAN, class:
org.jboss.seam.security.HibernateEntityPermissionChecker
[Component] Component: org.jboss.seam.security.facesSecurityEvents, scope: APPLICATION, type: JAVA_BEAN, class:
org.jboss.seam.security.FacesSecurityEvents
[Component] Component: org.jboss.seam.security.identity, scope: SESSION, type: JAVA_BEAN, class: org.jboss.seam.security.Identity
[Component] Component: org.jboss.seam.theme.themeFactory, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.theme.Theme
[Component] Component: org.jboss.seam.theme.themeSelector, scope: SESSION, type: JAVA_BEAN, class: org.jboss.seam.theme.ThemeSelector
[Component] Component: org.jboss.seam.transaction.synchronizations, scope: EVENT, type: JAVA_BEAN, class:
org.jboss.seam.transaction.SeSynchronizations
[Component] Component: org.jboss.seam.transaction.transaction, scope: EVENT, type: JAVA_BEAN, class: org.jboss.seam.transaction.Transaction
[Component] Component: org.jboss.seam.ui.EntityConverter, scope: CONVERSATION, type: JAVA_BEAN, class: org.jboss.seam.ui.converter.EntityConverter
[Component] Component: org.jboss.seam.ui.entityIdentifierStore, scope: PAGE, type: JAVA_BEAN, class:
org.jboss.seam.ui.converter.entityConverter.EntityIdentifierStore
[Component] Component: org.jboss.seam.ui.entityLoader, scope: STATELESS, type: JAVA_BEAN, class:
org.jboss.seam.ui.converter.entityConverter.EntityLoader
[Component] Component: org.jboss.seam.ui.facelet.faceletCompiler, scope: APPLICATION, type: JAVA_BEAN, class:
org.jboss.seam.ui.facelet.FaceletCompiler
[Component] Component: org.jboss.seam.ui.graphicImage.graphicImageResource, scope: APPLICATION, type: JAVA_BEAN, class:
org.jboss.seam.ui.graphicImage.GraphicImageResource
[Component] Component: org.jboss.seam.ui.graphicImage.graphicImageStore, scope: SESSION, type: JAVA_BEAN, class:
org.jboss.seam.ui.graphicImage.GraphicImageStore
[Component] Component: org.jboss.seam.ui.hibernateEntityLoader, scope: STATELESS, type: JAVA_BEAN, class:
org.jboss.seam.ui.converter.entityConverter.HibernateEntityLoader
[Component] Component: org.jboss.seam.ui.resource.safeStyleResources, scope: APPLICATION, type: JAVA_BEAN, class:
org.jboss.seam.ui.resource.SafeStyleResources
[Component] Component: org.jboss.seam.ui.resource.styleResource, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.ui.resource.StyleResource
[Component] Component: org.jboss.seam.ui.resource.webResource, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.ui.resource.WebResource
[Component] Component: org.jboss.seam.web.exceptionFilter, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.web.ExceptionFilter
[Component] Component: org.jboss.seam.web.isUserInRole, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.faces.IsUserInRole
[Component] Component: org.jboss.seam.web.loggingFilter, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.web.LoggingFilter
[Component] Component: org.jboss.seam.web.multipartFilter, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.web.MultipartFilter
[Component] Component: org.jboss.seam.web.parameters, scope: STATELESS, type: JAVA_BEAN, class: org.jboss.seam.faces.Parameters
[Component] Component: org.jboss.seam.web.redirectFilter, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.web.RedirectFilter
[Component] Component: org.jboss.seam.web.servletContexts, scope: EVENT, type: JAVA_BEAN, class: org.jboss.seam.web.ServletContexts
[Component] Component: org.jboss.seam.web.session, scope: SESSION, type: JAVA_BEAN, class: org.jboss.seam.web.Session
[Component] Component: org.jboss.seam.web.userPrincipal, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.faces.UserPrincipal
[Contexts] starting up: org.jboss.seam.navigation.pages
[Pages] no pages.xml file found: /WEB-INF/pages.xml
[Contexts] starting up: org.jboss.seam.security.facesSecurityEvents
[Initialization] done initializing Seam
[MyfacesConfig] No context init parameter 'org.apache.myfaces.PRETTY_HTML' found, using default value true
[MyfacesConfig] No context init parameter 'org.apache.myfaces.ALLOW_JAVASCRIPT' found, using default value true
[MyfacesConfig] No context init parameter 'org.apache.myfaces.READONLY_AS_DISABLED_FOR_SELECTS' found, using default value true
[MyfacesConfig] No context init parameter 'org.apache.myfaces.RENDER_VIEWSTATE_ID' found, using default value true
[MyfacesConfig] No context init parameter 'org.apache.myfaces.STRICT_XHTML_LINKS' found, using default value true
[MyfacesConfig] No context init parameter 'org.apache.myfaces.CONFIG_REFRESH_PERIOD' found, using default value 2
[MyfacesConfig] No context init parameter 'org.apache.myfaces.VIEWSTATE_JAVASCRIPT' found, using default value false
[MyfacesConfig] Tomahawk jar not available. Autoscrolling, DetectJavascript, AddResourceClass and CheckExtensionsFilter are disabled now.
[MyfacesConfig] Starting up Tomahawk on the MyFaces-JSF-Implementation
[AbstractFacesInitializer] ServletContext
'C:\geronimo\repository\pl\jaceklaskowski\seam\seam-richfaces-tree\1.0\seam-richfaces-tree-1.0.war\' initialized.
Wszystkie komponenty rozpoznane, tylko nie mój pozdrow. Stworzyłem nawet bardzo podstawowy plik /WEB-INF/components.xml, sądząc, że to właśnie jego brak powoduje brak rozpoznania komponentu pozdrow. Jak można zauważyć wczytanie pliku components.xml faktycznie następowało, ale już aktywacja komponentu pozdrow nie.
 <?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd">

<core:init debug="true"/>

</components>
Okazało się, że bez lektury dokumentacji seamowej Chapter 5. Configuring Seam components się nie obędzie. I warto było! Dużo wiedzy w przeciągu niecałych 5 minut! 5 minut za cenę mojego 30-minutowego rozpracowywania, co jest nie tak, warte jest swojej ceny.

Plikiem konfiguracyjnym komponentów seamowych jest components.xml, w którym deklaruję komponenty seamowe i ich parametry (coś ala faces-config.xml w JSF, beans.xml czy applicationContext.xml w Spring Framework, web.xml w aplikacjach webowych, czy...tu wpisz plik konfiguracyjny komponentów swojego ulubionego szkieletu aplikacyjnego). Plik components.xml poszukiwany jest w następujących katalogach aplikacji - WEB-INF, META-INF oraz pliku jar z komponentami zdefiniowanymi przez @Name.

Próbowałem zastosować regułę:

Usually, Seam components are installed when the deployment scanner discovers a class with a @Name annotation sitting in an archive with a seam.properties file or a META-INF/components.xml file.

Próby z seam.properties spełzły na niczym. Żadnej zmiany - wykonanie komponentu pozdrow wciąż kończyło się pustą stroną, której źródło jest następujące:
 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Pozdrowienia</title>
</head>
<body>
<f:view>
#{pozdrow.pozdrowienie}
</f:view>
</body>
</html>
Nie jest to strona oparta o Facelets. Po prostu zwykła strona JSF. Nic nadzwyczajnego dla osób zaznajomionych z tą technologią. Wciąż jednak zamiast #{pozdrow.pozdrowienie} wyświetlany był pusty ciąg znaków, czyli nie wykonał się komponent pozdrow (bo i jak miał się wykonać, skoro Seam nie rozpoznał klasy PozdrowAction jako definicję komponentu pozdrow?!).

Skoro można polegać na automatycznym rozpoznaniu klasy jako komponentu seamowego przez zastosowanie adnotacji @Name (co w moim przypadku nie działało) oraz możliwości zdefiniowania komponentu w pliku components.xml, nie pozostaje mi nic innego jak zastosować drugą możliwość. I to było to!

Plik /WEB-INF/components.xml prezentuje się następująco:
 <?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd">

<core:init debug="true"/>

<component name="pozdrow"
class="pl.jaceklaskowski.seam.PozdrowAction">
</component>

</components>
Uruchomienie aplikacji na Geronimo 2.1.1 to:
 [ServletContextListener] Welcome to Seam 2.0.3.CR1
...
[Initialization] reading /WEB-INF/components.xml
...
[Component] Component: pozdrow, scope: EVENT, type: JAVA_BEAN, class: pl.jaceklaskowski.seam.PozdrowAction
I to jest właśnie komunikat, na który czekałem! Mój komponent seamowy został rozpoznany przez Seama. Wykorzystanie komponentu na stronie pozdrow.xhtml to wykonanie strony http://localhost:8080/seam-richfaces-tree/pozdrow.seam.

co na konsoli Geronimo przedstawione zostało jako:
 [PozdrowAction] Inicjacja PodrowAction za nami
Dokładnie tak, jak sobie tego życzyłem. Temat rozwiązany. Pora zabrać się za coś poważniejszego w Seamie. Tylko, co miałoby to być? Na pewno coś w stylu tworzenia aplikacji seamowej z Groovy. Już nie mogę doczekać się tej konfiguracji. A później pozostanie stworzyć archetyp seamowy z przedstawioną dotychczas konfiguracją. To lubię! ;-)

Pytanie konkursowe: Jaki plik konfiguracyjny w JBoss Seam definiuje komponenty seamowe?