[Resolvido][VRaptor] - Um redirect que volta para o último ResourceMethod executado

50 respostas
Rafael_Guerreiro

Fala pessoal!

No VRaptor já tem algum redirect que volta para a última tela chamada?

Se não, como eu poderia implementar isso…

Eu tentei algo assim:

Um interceptor pega e guarda em uma classe @SessionScoped o método que está indo e um método que redireciona para este método pegado pelo interceptor.

Mas ficou bem confuso, ainda mais se o método anterior recebe parametros…

-Edit-

Eu consegui só com métodos que não recebem parâmetros e também tentei fazer isso usando o validator… Sem sucesso em nenhuma situação…

50 Respostas

Lucas_Cavalcanti

existe o:

result.use(Results.referer()).redirect();

mas não é 100% garantido que o referer existe.

ele vai redirecionar para a página que originou a requisição. É isso que vc quer?

Rafael_Guerreiro

Então… Mais ou menos isso… Queria poder informar quais métodos poderiam ser “retornáveis” e quando eu quisesse retornar, ele fosse para o último método retornável… Eu consegui algo parecido… Usei annotations para informar… Também estou usando o Mirror… Mas não consigo informar os parâmetros…

Lucas_Cavalcanti

use o interceptor então

para pegar os args, receba MethodInfo no construtor e guarde o info.getParameters()

detalhe: seu interceptor precisa ser @Intercepts(after=ParametersInstantiatorInterceptor.class)

Rafael_Guerreiro

Então, daí eu estou tendo o seguinte erro:

java.lang.IllegalArgumentException: Cannot invoke a method by name if one of it’s arguments is null. First reflect the method.

De fato o parametro é nulo… E deveria, neste caso… Como eu faço a parte em negrito?

Lucas_Cavalcanti

como vc tá pegando o Method pra invocar?

vc pode usar o resourceMethod.getMethod()

Rafael_Guerreiro

Estou fazendo assim… Onde recursos é a minha classe q guarda as informações do métodos.

public class Recursos { private ResourceMethod method; private ResourceClass classe; private InterceptorStack stack; private Object instance; private MethodInfo methodInfo; // Getters e Setters }

new Mirror().on(controller).invoke() .method(recursos.getMethod().getMethod().getName()) .withArgs(recursos.getMethodInfo().getParameters());

Lucas_Cavalcanti

troque para:

new Mirror().on(controller).invoke()  
                    .method(recursos.getMethod().getMethod())  
                    .withArgs(recursos.getMethodInfo().getParameters());

vc colocou o after no @Intercepts como eu te falei?

Rafael_Guerreiro

Coloquei sim… Na verdade coloquei em outro interceptor que é executado antes desse…

Funcionou sem o getName()…

Mas qual o motivo?

Lucas_Cavalcanti

qdo vc usa o getName o mirror tenta procurar qual é o método que se adequa aos parâmetros que vc passou… se o parâmetro é null ele não tem como saber, então dá a exception.

como vc já tem o Method que vc quer invocar, é só passá-lo diretamente, daí poupa o mirror do trabalho de procurá-lo

Rafael_Guerreiro

Entendi…

Que tal se implementasse isso no VRaptor? Alguma forma de result.redirectToLastAnnotated();

Lucas_Cavalcanti

se definir direitinho como isso funcionaria, não vejo pq não :wink:

pode entrar como um plugin por enquanto, que acha?

Rafael_Guerreiro

Sim sim… Então como disponibilizo isso como plugin?

Lucas_Cavalcanti

-crie um projeto no github com o código
-Forkeie https://github.com/caelum/vraptor-contrib
-git add submodule vraptor-nome-do-plugin
-commite a adição do submodulo e mande um pull request

a gente vai fazer um jeito automático de disponibilizar todos esses plugins, daí a gente comunica todo mundo =)

[]'s

G

Porque fazer uma volta tão grande se o VRaptor possui um exception handler que não apenas faz redirect, mas que popula todos os objetos de volta no request e adiciona a exception no response conforme o padrão servlets?

http://vraptor.caelum.com.br/documentacao/exception-handling/

Lucas_Cavalcanti

garcia, acho que isso não tem a ver com exceptions, pelo que eu entendi

G

OOps. Obrigado pela correção.

Rafael_Guerreiro

Lucas, eu estou tentando algo neste sentido:

Gostaria que os usuarios do plugin só colocassem o .jar no classpath e ele substituísse o result e o validator pelos meus, onde incluo mais dois métodos, sem mudar o nome.
Começei assim:

Minha classe WSResult implementa Result e adiciona mais 2 métodos, o void redirectToLastAnnotated() e o void forwardToLastAnnotated(); Todos os outros métodos do Result eu disparei para ele mesmo fazer… Na verdade eu coloquei uma camada em cima do result com mais 2 métodos.

Então eu criei um ComponentFactory de Result que devolve o WSResult, porém na implementação os métodos não aparecem, como esperado…

E se eu fazer uma classe abstrata que implementa Result e possua mais esses métodos e então eu crio um ComponentFactory de WSResult. Assim as pessoas poderiam sobrepor os meus métodos.

Rafael_Guerreiro

Ele não funciona como planejado... Hehehe... Deve ter algo muito errado.

Como está o projeto:

Uma annotation que serve para anotar os métodos que serão "retornáveis".

Um Interceptor que armazena esses métodos em um list.

Duas classes para resources:
public class Resource {
	private ResourceMethod resourceMethod;
	private ResourceClass resourceClass;
	private MethodInfo methodInfo;
}
@Component
@SessionScoped
public class ResourceList {
	private List<Resource> resourceList = new ArrayList<Resource>();

	public List<Resource> getResourceList() {
		return resourceList;
	}
}
Duas interfaces que extendem Result e Validator do VRaptor:
public interface WSValidator extends Validator {
	public void onErrorRedirectToLastAnnotated();

	public void onErrorForwardToLastAnnotated();
}
public interface WSResult extends Result {
	public void redirectToLastAnnotated();

	public void forwardToLastAnnotated();
}
Dois factories para os meus Defaults:
@Component
public class WSValidatorFactory implements ComponentFactory<WSValidator> {
	private final WSValidator wsValidator;

	public WSValidatorFactory(Validator validator, ResourceList resourceList) {
		this.wsValidator = new DefaultWSValidator(validator, resourceList);
	}

	@Override
	public WSValidator getInstance() {
		return wsValidator;
	}
}
@Component
public class WSResultFactory implements ComponentFactory<WSResult> {
	private final WSResult wsResult;

	public WSResultFactory(Result result, ResourceList resourceList) {
		this.wsResult = new DefaultWSResult(result, resourceList);
	}

	@Override
	public WSResult getInstance() {
		return wsResult;
	}
}
e os dois Defaults que implementam de fato a funcionalidade...
@Component
public class DefaultWSValidator implements WSValidator {
	private Validator validator;
	private Resource resource;

	public DefaultWSValidator(Validator validator,
			ResourceList resourceList) {
		this.validator = validator;
		resource = resourceList.getResourceList().get(
				resourceList.getResourceList().size() - 1);
	}

	public void onErrorRedirectToLastAnnotated() {
		Object controller = validator.onErrorRedirectTo(resource
				.getResourceClass().getType());

		try {
			if (resource.getMethodInfo().getParameters() != null
					&& resource.getMethodInfo().getParameters().length > 0) {
				new Mirror().on(controller).invoke()
						.method(resource.getResourceMethod().getMethod())
						.withArgs(resource.getMethodInfo().getParameters());
			} else {
				new Mirror().on(controller).invoke()
						.method(resource.getResourceMethod().getMethod())
						.withoutArgs();
			}
		} catch (ReflectionProviderException e) {
			throw (ValidationException) e.getCause();
		}
	}

	public void onErrorForwardToLastAnnotated() {
		Object controller = validator.onErrorForwardTo(resource
				.getResourceClass().getType());

		try {
			if (resource.getMethodInfo().getParameters() != null
					&& resource.getMethodInfo().getParameters().length > 0) {
				new Mirror().on(controller).invoke()
						.method(resource.getResourceMethod().getMethod())
						.withArgs(resource.getMethodInfo().getParameters());
			} else {
				new Mirror().on(controller).invoke()
						.method(resource.getResourceMethod().getMethod())
						.withoutArgs();
			}
		} catch (ReflectionProviderException e) {
			throw (ValidationException) e.getCause();
		}
	}
        // resto das implementações
}
@Component
public class DefaultWSResult implements WSResult {
	private Resource resource;
	private Result result;

	public DefaultWSResult(Result result, ResourceList resourceList) {
		this.result = result;
		resource = resourceList.getResourceList().get(
				resourceList.getResourceList().size() - 1);
	}

	@Override
	public void redirectToLastAnnotated() {
		Object controller = result.redirectTo(resource.getResourceClass()
				.getType());

		if (resource.getMethodInfo().getParameters() != null
				&& resource.getMethodInfo().getParameters().length > 0) {
			new Mirror().on(controller).invoke()
					.method(resource.getResourceMethod().getMethod())
					.withArgs(resource.getMethodInfo().getParameters());
		} else {
			new Mirror().on(controller).invoke()
					.method(resource.getResourceMethod().getMethod())
					.withoutArgs();
		}
	}

	@Override
	public void forwardToLastAnnotated() {
		Object controller = result.forwardTo(resource.getResourceClass()
				.getType());

		if (resource.getMethodInfo().getParameters() != null
				&& resource.getMethodInfo().getParameters().length > 0) {
			new Mirror().on(controller).invoke()
					.method(resource.getResourceMethod().getMethod())
					.withArgs(resource.getMethodInfo().getParameters());
		} else {
			new Mirror().on(controller).invoke()
					.method(resource.getResourceMethod().getMethod())
					.withoutArgs();
		}
	}
        // resto das implementações
}
Lucas_Cavalcanti

Rafael, tem um jeito melhor de implementar isso, e vc não precisa repetir as implementações do Result e do Validator.

dá pra usar assim:

result.use(lastAnnotated()).redirect(); //ou
result.use(LastAnnotated.class).forward();

validator.onErrorUse(lastAnnotated()).redirect();
validator.onErrorUse(lastAnnotated()).forward();

crie a interface:

interface LastAnnotated implements View {
    void redirect();
    void forward();
}

crie a implementação:

@Component
public DefaultLastAnnotated implements LastAnnotated {
     public static Class<LastAnnotated> lastAnnotated() { //para fazer o import estático
         return LastAnnotated.class;
     }

     // código que vc tinha posto  em cima no WSResult
}

assim fica mais fácil de usar, e o cara não precisa mudar o Result, que acha?

o código do redirect e do forward está quase idêntico, dá pra fazer um extract method :wink:

Rafael_Guerreiro

Mas no caso da diferença entre o validator e o result… como eu faria?

Era exatamente isso que eu estava procurando, alguma forma da pessoa não encostar no que já estava pronto…

Eu estava fazendo um refactor para um método private… dai eu tirei… sei lá pq…

Lucas_Cavalcanti

pode usar result mesmo, vai funcionar…

no caso do validator a diferença é que ele só executa o código se houveram erros, mas as implementações continuam usando result por baixo

Rafael_Guerreiro

Eu preciso informar alguma coisa na minha interface?

Pois aparentemente o VRaptor não está encontrando…

org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [br.com.warriorsoft.interfaces.LastAnnotated] is defined: expected single bean but found 0: org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:269) org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1083) br.com.caelum.vraptor.ioc.spring.SpringBasedContainer.instanceFor(SpringBasedContainer.java:86) br.com.caelum.vraptor.core.DefaultResult.use(DefaultResult.java:57) br.com.wsacervoweb.controller.ConsultaController.aplicaFiltro(ConsultaController.java:169) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) java.lang.reflect.Method.invoke(Unknown Source) br.com.caelum.vraptor.interceptor.ExecuteMethodInterceptor.intercept(ExecuteMethodInterceptor.java:61) br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:61) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.interceptor.ExceptionHandlerInterceptor.intercept(ExceptionHandlerInterceptor.java:71) br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:89) br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:83) br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:48) br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69) br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:23) br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92) br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58) br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)

Lucas_Cavalcanti

a implementação precisa estar anotada com @Component

Rafael_Guerreiro

e está… Eu tentei até criar um component Factory… mas também não deu certo…

Lucas_Cavalcanti

o @Component é o do VRaptor? a classe implementa LastAnnotated? a classe está no WEB-INF/classes ou dentro de um jar?

Rafael_Guerreiro

O @Component é do VRaptor.
A classe implementa LastAnnotated.
E tudo está dentro de um jar.

Lucas_Cavalcanti

se está dentro do jar vc precisa declarar o pacote no web.xml,

<context-param>
        <param-name>br.com.caelum.vraptor.packages</param-name>
        <param-value>br.com.pacote.do.plugin</param-value>
    </context-param>

ou gerar o jar com o arquivo META-INF/br.com.caelum.vraptor.packages contendo br.com.pacote.do.plugin

Rafael_Guerreiro

Fiz a configuração do web-xml, funcionou lindo lindo!! mas como que eu faço essa coisa ai no META-INF…

Gostaria de entregar o jar e a pessoa não precisar fazer nadinha…

-Edit-

E a parte do import estático não funcionou…

Lucas_Cavalcanti

como vc fez o import estático?

Rafael_Guerreiro

Ele só funciona (static) se eu importar a DefaultLastAnnotated…

Lucas_Cavalcanti

sim, é isso mesmo :wink:

vc pode fazer:

import static br......DefaultLastAnnotated.lastAnnotated;
...
result.use(lastAnnotated())....
Rafael_Guerreiro

É…

Agora eu vou começar a procurar bugs e fazer o JavaDoc… Você se importa se eu colocar o seu nome na maioria (quase todas) as classes?

Existe alguma forma de saber quem foi que chamou o método?
Por exemplo, se a pessoa anotar um método e nele pedir para redirecionar para o anterior, vai voltar para o mesmo…

Lucas_Cavalcanti

como assim quem chamou o método?

o método que rodou o result.use(lastAnnotation())?

Rafael_Guerreiro

Isso, por exemplo se esse método for anotado com @CaptureMethod, ele será o alvo do redirecionamento.

Lucas_Cavalcanti

tem com a stacktrace, mas é bem zoado fazer isso…

eu achei que vc estava sempre voltando pra última requisição para algum método anotado com @CaptureMethod

Rafael_Guerreiro

Então, eu estou, mas para pra pensar no seguinte caso:

@CaptureMethod @Get("/path") public void fazAlgo(){ result.use(lastAnnotated()).redirect(); }

Lucas_Cavalcanti

é só vc capturar o método sempre na volta…

ao invés de:

captura

stack.next(...)

faça

stack.next(...)

captura
Rafael_Guerreiro

Eu tinha pensado nisso… Mas não tinha certeza se funcionaria…

Então, como faço o arquivo dentro de META-INF?
E posso colocar seu nome no javadoc do projeto?

Lucas_Cavalcanti

pode colocar meu nome se quiser sim :wink:

qdo for criar o jar, precisa existir esse arquivo na pasta META-INF

META-INF/br.com.caelum.vraptor.packages

e o conteúdo desse arquivo precisa ser o pacote base do plugin.

vc consegue fazer isso colocando esse arquivo na sua pasta src (ou src/main/resources)

e dependendo de como vc está gerando o jar, vc precisa mandar incluir esse arquivo no jar. Como vc está gerando?

Rafael_Guerreiro

Perfeito, cara! Funciona certinho… Como estou no trabalho, não consigo abrir o GitHub… Mas quando chegar em casa eu mando o jar pra lá.
Vou disponibilizar o jar no primeiro post… Se quiser dar uma olhada.

-Edit-

Só não entendi uma coisa… Eu coloquei mais dois métodos além do redirect e do forward… mas eles não aparecem…

Lucas_Cavalcanti

só vai aparecer se eles estiverem na interface

Rafael_Guerreiro

Eles estão na interface… eu criei mais um só para teste… E ele também não aparece na hora de usar… Diz “method teste() is undefined”

Lucas_Cavalcanti

provavelmente é diferença do que está dentro do jar e o que tá no código… tente atualizar o jar…

Rafael_Guerreiro

Então, eu atualizei o jar, dei clean… Nada ainda…

Lucas_Cavalcanti

bom, se tá na interface deveria funcionar…

talvez tenha um jar antigo com a mesma interface… ou o .class dela direto

Rafael_Guerreiro

Deu certo… Depois de trocentos cleans… Vou disponibilizá-lo…

Rafael_Guerreiro

Esse negócio do GitHub está muito complicado… pouco intuitivo…

Vou postar o .jar aqui mesmo…

Lucas_Cavalcanti

como assim pouco intuitivo?
ele te explica direitinho o que vc tem que fazer, te dá até as linhas que vc tem que executar…

quer ajuda para criar o repositório?

Rafael_Guerreiro

Consegui criar o repositório… mas não achei nada para subir os arquivos…

G

Hmm, quando você cria o repositório ele te dá na página inicial do repositório as instruções de como criar um repositório do git. Antes de mais nada você precisa instalar git (é no console) ou talvez uma ferramenta gráfica como o gitk/gitg (Linux) ou TortoiseGIT (Windows).

Criado 7 de junho de 2011
Ultima resposta 8 de jun. de 2011
Respostas 50
Participantes 3