SeamFramework.orgCommunity Documentation
É hora de tirar as tampas e mergulhar dentro das aplicações de exemplo do Weld. Vamos começar com o mais simples dos dois exemplos, weld-numberguess
.
Na aplicação numberguess você tem 10 tentativas para adivinhar um número entre 1 e 100. Depois de cada tentativa, você é informado se seu palpite foi muito alto ou muito baixo.
O exemplo numberguess é composto de uma série de beans, arquivos de configuração e páginas em Facelets (JSF), empacotados como um módulo war. Vamos começar examinando os arquivos de configuração.
Todos os arquivos de configuração para este exemplo estão localizados no WEB-INF/
, o qual pode ser encontrado no diretório src/main/webapp
do exemplo. Primeiro, nós temos a versão JSF 2.0 de faces-config.xml
. Uma versão padronizada de Facelets é o view handler padrão em JSF 2.0, então não há realmente nada a ser configurado. Assim, a configuração consiste apenas ao elemento raíz.
<faces-config version="2.0"
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-facesconfig_2_0.xsd">
</faces-config
>
Existe também um arquivo beans.xml
vazio, que diz ao contêiner para procurar por beans nesta aplicação e ativar os serviços CDI.
Finalmente, temos o familiar web.xml
:
<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">
<display-name
ml_tag_symbols">>weld-jsf-numberguess-war</display-name>
<description
>Weld JSF numberguess example (war)</description>
<servlet>
<servlet-name
ml_tag_symbols">>Faces Servlet</servlet-name>
<servlet-class
>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup
>1</load-on-startup>
ml_plain"> </servlet>
<servlet-mapping>
<servlet-name
>Faces Servlet</servlet-name>
ml_plain"> <url-pattern
>*.jsf</url-pattern>
</servlet-mapping>
<context-param>
<param-name
>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value
>.xhtml</param-value>
</context-param>
<session-config>
<session-timeout
>10</session-timeout>
</session-config>
</web-app
>
Enable and initialize the JSF servlet | |
Configure requests for URLs ending in | |
Tell JSF that we will be giving our JSF views (Facelets templates) an extension of | |
Configure a session timeout of 10 minutes |
This demo uses JSF 2 as the view framework, but you can use Weld with any servlet-based web framework, such as JSF 1.2 or Wicket.
Let's take a look at the main JSF view, src/main/webapp/home.xhtml
.
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
ml_plain">
<ui:composition template="/template.xhtml">
<ui:define name="content">
<h1
ml_tag_symbols">>Guess a number...</h1>
<h:form id="numberGuess">
<div style="color: red">
<h:messages id="messages" globalOnly="false"/>
<h:outputText id="Higher" value="Higher!"
rendered="#{game.number gt game.guess and game.guess ne 0}"/>
<h:outputText id="Lower" value="Lower!"
rendered="#{game.number lt game.guess and game.guess ne 0}"/>
ml_plain"> </div>
<div>
I'm thinking of a number between #{game.smallest} and #{game.biggest}.
You have #{game.remainingGuesses} guesses remaining.
</div>
ml_plain"> <div>
Your guess:
ml_plain"> <h:inputText id="inputGuess" value="#{game.guess}"
ml_plain"> size="3" required="true" disabled="#{game.number eq game.guess}"
validator="#{game.validateNumberRange}"/>
<h:commandButton id="guessButton" value="Guess"
action="#{game.check}" disabled="#{game.number eq game.guess}"/>
</div>
<div>
<h:commandButton id="restartButton" value="Reset" action="#{game.reset}" immediate="true"/>
</div>
</h:form>
</ui:define>
</ui:composition>
</html
>
Facelets is the built-in templating language for JSF. Here we are wrapping our page in a template which defines the layout. | |
There are a number of messages which can be sent to the user, "Higher!", "Lower!" and "Correct!" | |
As the user guesses, the range of numbers they can guess gets smaller - this sentence changes to make sure they know the number range of a valid guess. | |
This input field is bound to a bean property using a value expression. | |
A validator binding is used to make sure the user doesn't accidentally input a number outside of the range in which they can guess - if the validator wasn't here, the user might use up a guess on an out of bounds number. | |
And, of course, there must be a way for the user to send their guess to the server. Here we bind to an action method on the bean. |
O exemplo consiste em 4 classes, as duas primeiras são qualificadores. Primeiramente temos o qualificador @Random
, usado para injetar um número aleatório:
@Qualifier
@Target( { TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
public @interface Random {}
Existe também o qualificador @MaxNumber
, usado para injetar o número máximo que pode ser gerado:
@Qualifier
@Target( { TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
public @interface MaxNumber {}
A classe Generator
com escopo de aplicação é responsável por criar o número aleatório, através de um método produtor. Ela também expõe o número máximo possível através de um método produtor:
@ApplicationScoped
public class Generator implements Serializable {
private java.util.Random random = new java.util.Random(System.currentTimeMillis());
private int maxNumber = 100;
java.util.Random getRandom() {
return random;
}
@Produces @Random int next() {
return getRandom().nextInt(maxNumber);
}
@Produces @MaxNumber int getMaxNumber() {
return maxNumber;
}
}
O bean Generator
possui escopo de aplicação, assim não obtemos uma instância de Random diferente a cada vez.
A declaração de pacote e as importações foram removidas destas listagens. A listagem completa está disponível no código fonte do exemplo.
O último bean na aplicação é a classe Game
com escopo de sessão. Este é o principal ponto de entrada da aplicação. É responsável por criar ou redefinir o jogo, capturando e validando o palpite do usuário e fornecendo resposta ao usuário com uma FacesMessage
. Nós utilizamos o método de pós-construção para inicializar o jogo recuperando um número aleatório a partir do bean @Random Instance<Integer>
.
Você notará que também adicionamos a anotação @Named
nesta classe. Esta anotação somente é necessária quando você quer tornar o bean acessível em uma página JSF por meio de EL (ou seja, #{game}).
@Named
@SessionScoped
public class Game implements Serializable {
private int number;
private int guess;
private int smallest;
private int biggest;
private int remainingGuesses;
@Inject @MaxNumber private int maxNumber;
@Inject @Random Instance<Integer
> randomNumber;
public Game() {}
public void check() {
if (guess
> number) {
biggest = guess - 1;
}
else if (guess < number) {
smallest = guess + 1;
}
else if (guess == number) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
}
remainingGuesses--;
}
@PostConstruct
public void reset() {
this.smallest = 0;
this.guess = 0;
this.remainingGuesses = 10;
this.biggest = maxNumber;
this.number = randomNumber.get();
}
public void validateNumberRange(FacesContext context, UIComponent toValidate, Object value) {
if (remainingGuesses <= 0) {
FacesMessage message = new FacesMessage("No guesses left!");
context.addMessage(toValidate.getClientId(context), message);
((UIInput) toValidate).setValid(false);
return;
}
int input = (Integer) value;
if (input < smallest || input
> biggest) {
((UIInput) toValidate).setValid(false);
FacesMessage message = new FacesMessage("Invalid guess");
context.addMessage(toValidate.getClientId(context), message);
}
}
public int getNumber() {
return number;
}
public int getGuess() {
return guess;
}
public void setGuess(int guess) {
this.guess = guess;
}
public int getSmallest() {
return smallest;
}
public int getBiggest() {
return biggest;
}
public int getRemainingGuesses() {
return remainingGuesses;
}
}
Uma série de modificações devem ser feitas no artefato numberguess para que seja implatado no Tomcat ou Jetty. Primeiro, o Weld deve ser implantado como uma biblioteca de aplicação Web em WEB-INF/lib
já que o contêiner servlet não oferece os serviços de CDI. Para sua conveniência, oferecemos um único jar suficiente para executar o Weld em qualquer contêiner servlet (incluindo o Jetty), weld-servlet.jar
.
Você deve também incluir os jars para JSF, EL e as anotações comuns (jsr250-api.jar
), todos aqueles que são fornecidos pela plataforma Java EE (um servidor de aplicação Java EE). Você está começando a entender porque uma plataforma Java EE é importante?
Segundo, precisamos especificar explicitamente o servlet listener no web.xml
, porque o contêiner não fará isto por você. O servlet listener inicia o Weld e controla sua interação com as solicitações.
<listener>
<listener-class
>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener
>
Quando o Weld inicia, ele coloca o javax.enterprise.inject.spi.BeanManager
, o SPI portável para obtenção de instâncias de bean, no ServletContext com um nome igual ao nome da interface totalmente qualificada. Normalmente você não precisa acessar esta interface, mas o Weld faz uso dela.
Este exemplo mostra como utilizar a extensão Weld SE em uma aplicação Java SE feita em Swing com nenhum EJB ou dependências com servlet. Este exemplo pode ser encontrado na pasta examples/se/numberguess
da distribuição padrão do Weld.
Para usar o exemplo numberguess com Weld SE no Eclipse, você pode abrir o exemplo normalmente utilizando o plugin m2eclipse.
If you have m2eclipse installed, you can open any Maven project directly. From within Eclipse, select File -> Import... -> Maven Projects. Then, browse to the location of the Weld SE numberguess example. You should see that Eclipse recognizes the existence of a Maven project.
Isto criará um projeto em seu workspace com o nome weld-se-numberguess
.
Se você não está usando o plugin m2eclipse, você tem que seguir diferentes passos para importar o projeto. Primeiro, entre no exemplo numberguess do Weld em SE, então execute o plugin Maven Eclipse com o profile jetty ativado, conforme a seguir:
É hora de ver o exemplo rodando!
Disable m2eclipse's Workspace Resolution, to make sure that Eclipse can find StartMain
. Right click on the project, and choose Properties -> Maven, and uncheck Resolve dependencies from Workspace projects:
Right click on the project, and choose Run As -> Run As Application:
Localize a classe StartMain
:
Agora a aplicação deve iniciar!
Garanta que o Maven 3 esteja instalado e presente em seu PATH
Garanta que a variável de ambiente JAVA_HOME
esteja apontando para sua instalação do JDK
Abra a linha de comando ou janela de terminal no diretório examples/se/numberguess
Execute o seguinte comando
mvn -Drun
Vamos dar uma olhada no código e arquivos de configuração interessantes que compõem este exemplo.
Como usual, existe um arquivo beans.xml
vazio no pacote raíz (src/main/resources/beans.xml
), o qual marca esta aplicação como uma aplicação CDI.
A lógica principal do jogo está em Game.java
. Aqui está o código para esta classe, destacando os pontos que diferem da versão web:
@ApplicationScoped
public class Game
{
public static final int MAX_NUM_GUESSES = 10;
private Integer number;
private int guess = 0;
private int smallest = 0;
@Inject
@MaxNumber
private int maxNumber;
private int biggest;
private int remainingGuesses = MAX_NUM_GUESSES;
private boolean validNumberRange = true;
@Inject
Generator rndGenerator;
public Game()
{
}...
public boolean isValidNumberRange()
{
return validNumberRange;
}
public boolean isGameWon()
{
return guess == number;
}
public boolean isGameLost()
{
return guess != number && remainingGuesses <= 0;}
public boolean check()
{
boolean result = false;
if (checkNewNumberRangeIsValid())
{
if (guess
> number)
{
biggest = guess - 1;
}
if (guess < number)
{
smallest = guess + 1;
}
if (guess == number)
{
result = true;
}
remainingGuesses--;
}
return result;
}
private boolean checkNewNumberRangeIsValid()
{return validNumberRange = ((guess
>= smallest) && (guess <= biggest));
}
@PostConstruct
public void reset()
{
this.smallest = 0;
this.guess = 0;
this.remainingGuesses = 10;
this.biggest = maxNumber;
this.number = rndGenerator.next();
}
}
The bean is application scoped rather than session scoped, since an instance of a Swing application typically represents a single 'session'. | |
Notice that the bean is not named, since it doesn't need to be accessed via EL. | |
In Java SE there is no JSF
This allows the Swing UI to query the state of the game, which it does indirectly via a class called | |
Since there is no dedicated validation phase, validation of user input is performed during the | |
The |
The MessageGenerator
class depends on the current instance of Game
and queries its state in order to determine the appropriate messages to provide as the prompt for the user's next guess and the response to the previous guess. The code for MessageGenerator
is as follows:
public class MessageGenerator
{@Inject
private Game game;public String getChallengeMessage()
{
StringBuilder challengeMsg = new StringBuilder("I'm thinking of a number between ");
challengeMsg.append(game.getSmallest());
challengeMsg.append(" and ");
challengeMsg.append(game.getBiggest());
challengeMsg.append(". Can you guess what it is?");
return challengeMsg.toString();
}public String getResultMessage()
{
if (game.isGameWon())
{
return "You guessed it! The number was " + game.getNumber();
}
else if (game.isGameLost())
{
return "You are fail! The number was " + game.getNumber();
}
else if (!game.isValidNumberRange())
{
return "Invalid number range!";
}
else if (game.getRemainingGuesses() == Game.MAX_NUM_GUESSES)
{
return "What is your first guess?";
}
else
{
String direction = null;
if (game.getGuess() < game.getNumber())
{
direction = "Higher";
}
else
{
direction = "Lower";
}
return direction + "! You have " + game.getRemainingGuesses() + " guesses left.";
}
}
}
The instance of | |
The | |
... and again to determine whether to congratulate, console or encourage the user to continue. |
Finally we come to the NumberGuessFrame
class which provides the Swing front end to our guessing game.
public class NumberGuessFrame extends javax.swing.JFrame
{@Inject
private Game game;@Inject
private MessageGenerator msgGenerator;public void start(@Observes ContainerInitialized event)
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
initComponents();
setVisible(true);
}
});
}private void initComponents()
{
buttonPanel = new javax.swing.JPanel();
mainMsgPanel = new javax.swing.JPanel();
mainLabel = new javax.swing.JLabel();
messageLabel = new javax.swing.JLabel();
guessText = new javax.swing.JTextField();
...
mainLabel.setText(msgGenerator.getChallengeMessage());
mainMsgPanel.add(mainLabel);
messageLabel.setText(msgGenerator.getResultMessage());
mainMsgPanel.add(messageLabel);
...
}private void guessButtonActionPerformed( java.awt.event.ActionEvent evt )
{
int guess = Integer.parseInt(guessText.getText());
game.setGuess( guess );
game.check();
refreshUI();
}
private void replayBtnActionPerformed(java.awt.event.ActionEvent evt)
{game.reset();
refreshUI();
}
private void refreshUI() {
mainLabel.setText( msgGenerator.getChallengeMessage() );
messageLabel.setText( msgGenerator.getResultMessage() );
guessText.setText( "" );
guessesLeftBar.setValue( game.getRemainingGuesses() );
guessText.requestFocus();
}
// swing components
private javax.swing.JPanel borderPanel;
...
private javax.swing.JButton replayBtn;
}
The injected instance of the game (logic and state). | |
The injected message generator for UI messages. | |
This application is started in the prescribed Weld SE way, by observing the | |
This method initializes all of the Swing components. Note the use of the | |
| |
|
O exemplo translator pegará qualquer sentença que vocẽ entrar e as traduzirá para Latim. (Bem, não realmente, mas a base está aí para você implementar. Boa sorte!)
O exemplo translator é construído como um ear e contém EJBs. Como resultado, sua estrutura é mais complexa do que o exemplo numberguess.
Java EE 6, que vem com EJB 3.1, permite que você empacote EJBs em um war, o que tornará sua estrutura muito mais simples! Todavia, existem outras vantagens em usar um ear.
Primeiro, vamos dar uma olhada no agregador eear, que está localizado no diretório ear
do exemplo. O Maven automaticamente gera o application.xml
para nós com esta configuração de plugin:
<plugin>
<groupId
>org.apache.maven.plugins</groupId>
<artifactId
>maven-ear-plugin</artifactId>
<configuration>
<modules>
<webModule>
<groupId
>org.jboss.weld.examples.jsf.translator</groupId>
<artifactId
>weld-jsf-translator-war</artifactId>
<contextRoot
>/weld-translator</contextRoot>
</webModule>
</modules>
</configuration>
</plugin
>
Esta configuração sobrescreve o caminho do contexto web, resultando nesta URL para a aplicação: http://localhost:8080/weld-translator.
Se você não estiver utilizando o Maven para gerar estes arquivos, você deve precisar de um META-INF/application.xml
:
<application version="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/application_5.xsd">
<display-name
>weld-jsf-translator-ear</display-name>
<description
>The Weld JSF translator example (ear)</description>
<module>
<web>
<web-uri
>weld-translator.war</web-uri>
<context-root
>/weld-translator</context-root>
</web>
</module>
<module>
<ejb
>weld-translator.jar</ejb>
</module>
</application
>
Agora, vamos dar uma olhada no war, que está localizado no diretório war
do exemplo. Da mesma forma do exemplo numberguess, temos um faces-config.xml
para JSF 2.0 e um web.xml
(para ativar JSF), ambos dentro de src/main/webapp/WEB-INF
.
O mais interessante é a visão JSF usada para traduzir texto. Como no exemplo numberguess, possuímos um template (aqui omitido por brevidade) que circunda o formulário:
<h:form id="translator">
<table>
<tr align="center" style="font-weight: bold">
<td>
Your text
</td>
<td>
Translation
</td>
</tr>
<tr>
<td>
<h:inputTextarea id="text" value="#{translator.text}" required="true" rows="5" cols="80"/>
</td>
<td>
<h:outputText value="#{translator.translatedText}"/>
</td>
</tr>
</table>
<div>
<h:commandButton id="button" value="Translate" action="#{translator.translate}"/>
</div>
</h:form
>
O usuário pode digitar algum texto no textarea à esquerda, e pressionar o botão translate para ver o resultado à direita.
Finalmente vamos dar uma olhada no módulo EJB, o qual está localizado no diretório ejb
do exemplo. Em src/main/resources/META-INF
existe apenas um beans.xml
vazio, usado para indicar que o jar possui beans.
Nós temos deixado a parte mais interessante por último, o código! O projeto possui dois simples beans, SentenceParser
e TextTranslator
, e dois session beans, TranslatorControllerBean
e SentenceTranslator
. Você já deve estar bem familiarizado com beans agora, assim destacamos apenas as partes mais interessantes.
Tanto SentenceParser
quanto TextTranslator
são beans dependentes, e TextTranslator
utiliza injeção no construtor:
public class TextTranslator implements Serializable {
private SentenceParser sentenceParser;
@EJB private Translator translator;
@Inject public TextTranslator(SentenceParser sentenceParser) {
this.sentenceParser = sentenceParser;
}
public String translate(String text) {
StringBuilder sb = new StringBuilder();
for (String sentence: sentenceParser.parse(text)) {
sb.append(translator.translate(sentence)).append(". ");
}
return sb.toString().trim();
}
}
TextTranslator
usa o bean SentenceParser
(realmente apenas uma simples classe Java!) para analisar a sentença e então chama o stateless bean com a interface local de negócio Translator
para realizar a tradução. É onde a mágica acontece. Certamente, nós não pudemos desenvolver um tradutor completo, mas é suficientemente convincente para quem não conhece Latim!
@Stateless
public class SentenceTranslator implements Translator {
public String translate(String sentence) {
return "Lorem ipsum dolor sit amet";
}
}
Finalmente, existe um controlador orientado na interface com o usuário. Este é um stateful session bean com escopo de requisição e nomeado, o qual injeta o tradutor. Ele coleta o texto do usuário e o despacha para o tradutor. O bean também possui getters e setters para todos os campos na página.
@Stateful
@RequestScoped
@Named("translator")
public class TranslatorControllerBean implements TranslatorController {
@Inject private TextTranslator translator;
private String inputText;
private String translatedText;
public void translate() {
translatedText = translator.translate(inputText);
}
public String getText() {
return inputText;
}
public void setText(String text) {
this.inputText = text;
}
public String getTranslatedText() {
return translatedText;
}
@Remove public void remove() {}
}
Isto conclui nosso rápido passeio com os exemplos de partida do Weld. Para mais informações sobre o Weld, por favor visite http://www.seamframework.org/Weld.