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

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…

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?

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…

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)

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?

como vc tá pegando o Method pra invocar?

vc pode usar o resourceMethod.getMethod()

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());

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?

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

Funcionou sem o getName()…

Mas qual o motivo?

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

Entendi…

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

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

pode entrar como um plugin por enquanto, que acha?

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

-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

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/

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

OOps. Obrigado pela correção.

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.

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; }

[code]@Component
@SessionScoped
public class ResourceList {
private List<Resource> resourceList = new ArrayList<Resource>();

public List&lt;Resource&gt; getResourceList() {
	return resourceList;
}

}[/code]
Duas interfaces que extendem Result e Validator do VRaptor:

[code]public interface WSValidator extends Validator {
public void onErrorRedirectToLastAnnotated();

public void onErrorForwardToLastAnnotated();

}[/code]

[code]public interface WSResult extends Result {
public void redirectToLastAnnotated();

public void forwardToLastAnnotated();

}[/code]
Dois factories para os meus Defaults:

[code]@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;
}

}[/code]

[code]@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;
}

}[/code]
e os dois Defaults que implementam de fato a funcionalidade…

[code]@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 &gt; 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 &gt; 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

}[/code]

[code]@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 &gt; 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 &gt; 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

}[/code]

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 lá 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: