Lançada Versão 1.6 do Mentawai

Agora com:

  • Sticky Actions,formSkin tag (Dynatags) entre outros.

Mais aqui:
http://mentaframework.org/

e mudanças:
http://mentaframework.org/changes/changes_1.6.txt

Nossa…

O negocio ta indo ala XP? Uma release por semana?

Parabens, muito bom.

]['s

O que são Sticky Actions? (Sim, é preguiça de ler o FAQ)

Update: Suporte a continuations? Legal!!! Usaram o que? Rife?

http://www.theserverside.com/news/thread.tss?thread_id=42023#217253

Argh! Não são continuations então? Poxa :frowning:

A definição continuations significa algo complexo e vasto, que por coincidência também pode ser aplicado a web applications.

Não precisa embrulhar o estomago !!! :slight_smile: É algo diferente, que parece que na maioria dos casos vai resolver os mesmos problemas que continuations resolvem para web applications, ou seja, manter estado ao longo de uma sequencia de passos.

A discussão está boa lá no TSS. O desafio é: mostrar um exemplo onde super continuations, conversations, spring custom scope beans, etc. fazem algo muito melhor ou que uma Sticky Action não consegue.

Com certeza tem, só que ninguém está conseguindo citar. Talvez porque seja a grande minoria dos casos.

Olhe o exemplo do NumberGuess do Rifer e o do Menta.

O exemplo do rifers é muito mais natural, claro, limpo e simples.

Outro exêmplo, pegue o código abaixo escrito em psedu continuations e coloque em StickAction:

[code]public class MeuContinuation {

public Integer codigoDoCliente;
public Cliente cliente;

private Session s;
private Transaction t;

public void execute() {
s = …
t = s.beginTransaction();

  if (codigoDoCliente == null) {
     cliente = new Cliente();
  } else {
     cliente = s.get(codigoDoCliente, Cliente.class);
  }

  do {
     meuPause("form1");
  } until (validaForm1());

  do {
     meuPause("form2");
  } until (validaForm2());

  do {
     meuPause("form3");
  } until (validaForm2());

  t.commit();
  s.close();

}

private void meuPause(String view) {
try {
… chama template do rifers …
pause();
} (TimeOutException e) {
t.rollback();
s.close();
resume();
}
}

private void validaForm1() {

}

private void validaForm2() {

}

private void validaForm3() {

}
}[/code]

Isso é a sua opinião, não muito imparcial para quem conhece suas declarações a respeito do mentawai, mas não deixa de ser a sua opinião a qual respeito.

Lembre-se que Rife, apesar de eu já ter declarado várias vezes que acho o Rife excelente, não tem nada haver com o Mentawai. Um é component-based e outro é action-based.

Mas quanto a continuations, posto abaixo ambas as implementações para a avaliação do pessoal aqui:

Rife:

public class Game extends Element {
 	private static Random randomNumbers = new Random();
 	public void processElement() {
 		
 		Template template = getHtmlTemplate("game");
 		int answer = 0, guesses = 0, guess = -1;
 		
 		answer = randomNumbers.nextInt(101);
 		while (guess != answer) {
 			print(template);
 			
 			pause();
 			
 			template.clear();
 			
 			guess = getParameterInt("guess", -1);
 			if (guess < 0 || guess > 100) {
 				template.setBlock("warning", "invalid");
 				continue;
 			}
 			guesses++;
 			
 			if (answer < guess)      template.setBlock("msg", "lower");
 			else if (answer > guess) template.setBlock("msg", "higher");
 		}
 		
 		ContinuationContext.getActiveContext().removeContextTree();
 		
 		template = getHtmlTemplate("success");
 		template.setValue("answer", answer);
 		template.setValue("guesses", guesses);
 		print(template);
 	}
 }

Mentawai:


public class NumberGuessSticky extends BaseAction {
     
     public static final String BINGO = "bingo";
     public static final String FIRST = "first";
     public static final String WRONG = "wrong";    
     
     private int answer = -1;
     private int guesses = 0;
     
     private static final Random random = new Random();
    
     public String execute() throws Exception {

         // Check if a game is in progess, by fetching the current answer...
         if (answer == -1) {
             answer = random.nextInt(101);
             guesses = 0;
             adhere();
         }
         
         if (input.getValue("guess") != null) {

             guesses++;
             output.setValue("guesses", String.valueOf(guesses));
             int guess = input.getIntValue("guess");

             if (answer == guess) {
                 
                 output.setValue("answer", String.valueOf(answer));
                 disjoin();
                 return BINGO;
                 
             } else {
                 
                 if (guess < answer) {
                     addMessage("Number is higher!"); // of course this can be i18n...
                 } else {
                     addMessage("Number is lower!"); // of course this can be i18n...
                 }
                 return WRONG;
             }
         }
         return FIRST;
     }
 }

Dei um update no post acima e coloquei um exemplo legal de continuation. Não estou defendendo o Rifer, que nem conheço muito, só que continuations não é complicado e usado em 1% dos casos como você defende.

Seria legal você implementar a mesma lógica com StickAction.

É algo tão diferente que resolve outros problemas, como já apontaram lá, parece mais com beans do Spring em escopo de sessão do que com qualuer outra coisa.

Sobre os features que foram cidados, eu diria que principalmente suporte ao botão voltar e várias telas em diferentes “contextos” (como acontece no seam) também são features básicas.

O mentawai tb suporta componentes de IoC no escopo da sessão.

Mas repare a diferença de que um componente no escopo da sessão é criado quando requisitado e vive na sessão até a sessão acabar/espirar/invalidar.

Na verdade Sticky Actions é apenas uma maneira mais conveniente de trabalhar com stateful actions, ou seja, actions que vão persistir seu estado por algum tempo, INDEPENDENTEMENTE, da session.

Até porque ficar dando session.setAttribute, session,getAttribute e session.removeAttribute é meio pentelho!

Estamos boiando em relação a isso. Precisamos entender o problema para pensar depois numa possível solução com sticky action.

Várias telas com diferentes contextos poderia ser naturalmente uma action com várias innerActions. (*Pausa: Obrigado ao Rafael Steil pela idéia das InnerActions!). Então a classe da action (que possui as inner actions dentro) seria sticky.

Mas sobre innerAction aqui: http://www.mentaframework.org/inneraction.jsp

O próximo passo agora é suportar tags nessas sticky actions, de forma que possa haver uma cadeia de chamadas a várias actions diferentes que vão se comunicar entre si e serem stickies. (De novo se o cara não quiser/poder usar o esquema de inner actions, que é muito mais natural!)

[quote=juzepeleteiro]Olhe o exemplo do NumberGuess do Rifer e o do Menta.

O exemplo do rifers é muito mais natural, claro, limpo e simples.

Outro exêmplo, pegue o código abaixo escrito em psedu continuations e coloque em StickAction:

[code]public class MeuContinuation {

public Integer codigoDoCliente;
public Cliente cliente;

private Session s;
private Transaction t;

public void execute() {
s = …
t = s.beginTransaction();

  if (codigoDoCliente == null) {
     cliente = new Cliente();
  } else {
     cliente = s.get(codigoDoCliente, Cliente.class);
  }

  do {
     meuPause("form1");
  } until (validaForm1());

  do {
     meuPause("form2");
  } until (validaForm2());

  do {
     meuPause("form3");
  } until (validaForm2());

  t.commit();
  s.close();

}

private void meuPause(String view) {
try {
… chama template do rifers …
pause();
} (TimeOutException e) {
t.rollback();
s.close();
resume();
}
}

private void validaForm1() {

}

private void validaForm2() {

}

private void validaForm3() {

}
}[/code]
[/quote]

Seu exemplo está totalmente correto. O problema é que eu não trabalharia dessa maneira, pois prefiro um action-based framework. Mas tem gente que gosta e prefere um componente-based framework.

Ter uma transação expanando várias requisições é esquisito. Ok, tem mecanismos de controle transacional que suportam manter uma transação aberta por dias. Ótimo. Isso pra mim, nos projetos web que já participei significa destruir o banco de dados. Uma transação em aberta dá um lock nos registros, consome muito resource da máquina, é um problema em potencial.

Faz muito mais sentido ir acumulando tudo em memória e só no final fazer um write no banco de dados. Não precisa rollback e não precisa de transação. E se der um pau vc pode tentar de novo. Já com transação se deu pau vc é obrigado a dar rollback e um abraço !!!

Primeira opção: (acumulando em memória)


public class MeuContinuation extends BaseAction {
 
    private Cliente cliente;
    
    private Cliente getCliente() {
        
        if (cliente == null) {
            
            cliente = new Cliente();
            
            adhere(); // stick action!
            
        }
        
        return cliente;
    }
    
    public String form1() throws Exception {
    
        Cliente c = getCliente();

        // pega o que tem que pegar e coloca no cliente    
        
        return SUCCESS;
    
    }
    
    public String form2() throws Exception {
    
        Cliente c = getCliente();

        // pega o que tem que pegar e coloca no cliente    
        
        return SUCCESS;
    
    }

    public String form3() throws Exception {
    
        Cliente c = getCliente();

        // pega o que tem que pegar e coloca no cliente    
        
        // ihhh! que legal, temos um cliente completo e validado!
        
        try {
        
            // insere no banco !!!
            
            disjoin(); // solta a sticky action...
        
        } catch(Exception e) {
        
            return ERROR;
        
        }
        return SUCCESS;
    }    
 
 }

Segunda opção: (usar transação) - não gosto neum um pouco disso

public class MeuContinuation extends BaseAction {
 
    private Cliente cliente = null;
    
    private Session s;
    private Transaction t;
    
    private Cliente getCliente() {
        
        if (cliente == null) {
            
            cliente = new Cliente();
            
            s = ...
            t = s.beginTrasaction();
            
            adhere(); // stick action!
            
        }
        
        return cliente;
    }
    
    public String form1() throws Exception {
    
        Cliente c = getCliente();

        // pega o que tem que pegar aqui e usa a session para "inserir no banco"
        
        return SUCCESS;
    
    }
    
    public String form2() throws Exception {
    
        Cliente c = getCliente();

        // pega o que tem que pegar aqui e usa a session para "inserir no banco"
        
        return SUCCESS;
    
    }

    public String form3() throws Exception {
    
        Cliente c = getCliente();

        // pega o que tem que pegar aqui e usa a session para "inserir no banco"
        
        // ihhh! que legal, temos um cliente completo entao comita no banco de dados !!!!
        
        try {
        
            t.commit();
            
        } catch(Exception e) {
        
            // erro tentando inserir no banco...
            
            t.rollback();
            
            return ERROR;
        
        } finally {
        
            s.close();
            
            s = null;
          
            t = null;
            
            disjoin(); // solta a sticky action... // usou transaction se ferrou, pois agora não dá para tentar de novo !!!!
        }
        
        return SUCCESS;
    }    

    public void onRemoved() {

       // se o cara ignorou temos que tratar session timeout, logout, etc.

        if (t != null) t.rollbac();

        if (s != null) s.rollback();

    }
 
 }

Tirando o “chama o template do rifer” o resto é action based, na verdade a minha classe está mais para um Action do Webwork do que um componente do Rifer. Isso não tem nada haver com ser component-based ou action-based.

[quote=saoj]Ter uma transação expanando várias requisições é esquisito. Ok, tem mecanismos de controle transacional que suportam manter uma transação aberta por dias. Ótimo. Isso pra mim, nos projetos web que já participei significa destruir o banco de dados. Uma transação em aberta dá um lock nos registros, consome muito resource da máquina, é um problema em potencial.

Faz muito mais sentido ir acumulando tudo em memória e só no final fazer um write no banco de dados. Não precisa rollback e não precisa de transação. E se der um pau vc pode tentar de novo. Já com transação se deu pau vc é obrigado a dar rollback e um abraço !!![/quote]

Saoj, as transações que usei não são de banco de dados. São do Hibernate e o que ele faz é justamente guardar tudo em memoria e no final, no close do session ou no commit do transaction fazer um update no banco do que é necessário.

Só que nesse caso eu não precisei criar VOs, variaveis em sessão nem fazer o minha própria gerência do sincronismo dos dados no banco.

Foi puro e simples objeto, pra ficar na moda, POJO. Não ouve transaction de banco a não ser no commit. (aonde ele abriu uma transaction, fez todos os updates e commitou a transação).

O Hibernate só daria um transaction begin antes do commit se eu fisse lock de algum objeto (Session.lock(LockMode.Algo, obj)). E faria por que deveria, se não fisse seria um problema.

Procure fazer o mesmo exemplo sem fazer session.close (session aberta não é serializavel) a acada “metodo” com Stick action.

Lembre-se, quando eu não declaro explicitamente uma transaction ela sempre existe e sempre o Hibernate vai atualizar todos os objetos no session.close.

E ainda não estamos falando de outra coisas como lazy load (se necessário mostra algum relacionamento quando editando um cliente) e etc…

Obs: Por que criar VO?

O segunte código está errado:

Cliente c = s.get(1, Cliente.class); c.setNome(xyz); session.add("cliente", c); c.close();

Nesse código eu salvei no vanco a mudança que eu fiz no nome. Para não salvar de duas uma:

  • Eu teria que criar um VO e ir gravando os dados nele até estar pronto para colocar no objeto cliente.

  • Eu teria que retirar o objeto cliente do contexto da persistencia antes de fechar a session do hibernate. (olha só a vantagem do outro modo, eu faço tudo de forma natual). O que vai gerar um “select” a mais quando eu recolocar o objeto no contexto para o hibernate verificar a integridade do mesmo.

Obs2: Session e Transaction do hibernate abertas não são serializaveis.

Seus conhecimentos de Hibernate são muito superiores aos meus, logo não tenho como argumentar aqui.

O que o exemplo prova é que é possível também fazer aquela lógica com transações que vc postou usando Sticky Action.

Do codigo do Kit Davies, o exemplo mostra quando continuations resolvem o problema perfeitamente.

A grande sacada é manter valores de variaveis locais na memoria e continuar a execucao aonde ela parou, dando aquele ar de… continuidade. E quebrando aquela ideia assincrona da web

[code]public void runGame() {
int answer = getRandomNumber();
nextPage = “guessNumber.jsp”;

int guess = displayAndWait(nextPage); // This is where the magic happens

while (guess != answer) {
int guess = displayAndWait(nextPage);
}
}
[/code]

Acho que isso cai melhor para frameworks component-based e não para frameworks action-based.

Uma action por si só já traz o conceito de inicio e fim. É esquisito fazer com que uma action ficar sendo executada N vezes sem retornar um resultado. Para esses casos é melhor simplesmente reutilizar a instancia da action, onde as variáveis da instancia estarão ali, do que fazer esse esquema aí, de salvar variáveis locais, etc e tal.

Sei lá, isso é apenas a opinião de quem está viciado em action-model.

[quote=saoj]
Sei lá, isso é apenas a opinião de quem está viciado em action-model.[/quote]

Tambem acho… por isso que me doi a cabeca de usar continuations, mas que parece ser a solucao do futuro, muito mais que o aop pareceu no comeco, parece…

Pelo contrário Sérgio, continuations fazem todo sentido em frameworks action-oriented. Sem falar que o fato de ser component-oriented diz respeito apenas a como funciona a camada de UI, então tua colocação não tem muito fundamento.

Continuations permitem que workflows muito complexos sejam expressos de forma muito mais simples que usando um framework, spring-webflow, ou programando a máquina de estados na mão, como você fez no mentawai.

Esse lance de sticky-actions do mentawai é uma versão de pobre do StatefullSessionBean do EJB 3.0. Olhe como o SEAM usa os SFSBs do EJB 3.0 e vc vai ser o que estou falando. Olhe antes de comentar, senão vai falar besteira.

Não vou falar nada porque concordo com vc!

Sticky Actions é uma solução simples para workflows simples/medianos.

Para workflows complexos realmente deve-se usar continuations como vcs falam aí.

Pena que esses workflows complexos são 10% dos casos. Para esses 10% vc pode usar a integração do Mentawai com o Spring. :slight_smile:

Pois é… mas o spring-webflow é muito mais complexo de usar que continuations.