Problemas com VRaptor 4 - Tomcat 8 - Java 8

Olá pessoal, tudo bem?

seguinte: passei a ter problemas com um projeto que estava rodando muito bem, a partir do momento de resolvi utilizar lambdas nele… como mencionei no título, o projeto usa: VRaptor 4, Tomcat 8.0.9 e Java 8… o erro que dá é aquele mesmo de quando você tem o javassist desatualizado, por que ele não está preparado para ler as lambdas… demais códigos essencialmente Java 8 como a API de datas funcionam bem… o problema é realmente com lambdas… pois então, já tendo visto o erro do javassist e tendo pesquisado e resolvido o mesmo, fui direto nele… coloquei nas libs a ultima versão (javassist 3.18.2 GA) do mesmo… não resolveu!! mas reparando bem no stacktrace nota-se que o mesmo se refere ao paranamer:

25-Aug-2014 11:30:06.895 SEVERE [http-apr-8080-exec-7] org.apache.catalina.core.StandardContext.filterStart Exception starting filter vraptor
 java.lang.ArrayIndexOutOfBoundsException: 22967
	at com.thoughtworks.paranamer.BytecodeReadingParanamer$ClassReader.accept(BytecodeReadingParanamer.java:563)
	at com.thoughtworks.paranamer.BytecodeReadingParanamer$ClassReader.access$200(BytecodeReadingParanamer.java:338)
	at com.thoughtworks.paranamer.BytecodeReadingParanamer.lookupParameterNames(BytecodeReadingParanamer.java:103)
	at com.thoughtworks.paranamer.AnnotationParanamer.lookupParameterNames(AnnotationParanamer.java:110)
	at com.thoughtworks.paranamer.CachingParanamer.lookupParameterNames(CachingParanamer.java:90)
	at com.thoughtworks.paranamer.CachingParanamer.lookupParameterNames(CachingParanamer.java:83)
	at br.com.caelum.vraptor.http.ParanamerNameProvider.parametersFor(ParanamerNameProvider.java:50)
	at br.com.caelum.vraptor.http.ParanamerNameProvider$Proxy$_$$_WeldClientProxy.parametersFor(Unknown Source)
	at br.com.caelum.vraptor.http.route.DefaultTypeFinder.getParameterTypes(DefaultTypeFinder.java:55)
	at br.com.caelum.vraptor.http.route.DefaultTypeFinder$Proxy$_$$_WeldClientProxy.getParameterTypes(Unknown Source)
	at br.com.caelum.vraptor.http.route.DefaultRouteBuilder.addParametersInfo(DefaultRouteBuilder.java:196)
	at br.com.caelum.vraptor.http.route.DefaultRouteBuilder.is(DefaultRouteBuilder.java:185)
	at br.com.caelum.vraptor.http.route.PathAnnotationRoutesParser.registerRulesFor(PathAnnotationRoutesParser.java:118)
	at br.com.caelum.vraptor.http.route.PathAnnotationRoutesParser.rulesFor(PathAnnotationRoutesParser.java:92)
	at br.com.caelum.vraptor.http.route.PathAnnotationRoutesParser$Proxy$_$$_WeldClientProxy.rulesFor(Unknown Source)
	at br.com.caelum.vraptor.ioc.ControllerHandler.handle(ControllerHandler.java:66)

baixei a versão mais nova 2.7 mas não resolveu… pesquisei mais um pouco, e acompanhando uma lista de discussão sobre isso (https://groups.google.com/forum/#!msg/caelum-vraptor/PjayRl-UJJU/frz-M-VuIt8J) alguém comentou para usar a seguinte abordagem, usando o parâmetro “-parameters” na compilação:

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Executable;
import javax.annotation.Priority;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import javax.interceptor.Interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import br.com.caelum.vraptor.http.Parameter;
import br.com.caelum.vraptor.http.ParameterNameProvider;

@ApplicationScoped
@Alternative
@Priority(Interceptor.Priority.APPLICATION + 10)
public class JavaParameterNameProvider implements ParameterNameProvider {

    private static final Logger logger = LoggerFactory.getLogger(JavaParameterNameProvider.class);

    @Override
    public Parameter[] parametersFor(AccessibleObject executable) {
        logger.debug("looking for parameter names {}", executable);

        java.lang.reflect.Parameter[] params = ((Executable) executable).getParameters();
        Parameter[] out = new Parameter[params.length];

        for (int i = 0; i < params.length; i++) {
            if (!params[i].isNamePresent()) {
                throw new IllegalStateException("No parameters found: " + executable);
            }
            out[i] = new Parameter(i, params[i].getName(), executable);
        }
        return out;
    }
}

fiz o teste e a aplicação rodou, mas dava erro em qualquer link que clicava… verifiquei mais a fundo e os parâmetros estavam vindo com nomes do tipo: “arg0, arg1”… logicamente não iria funcionar, mas eu coloquei o parâmetro (-parameters) nas “Opções adicionais do Compilador” do Netbeans… comentando as lambdas tudo roda bem…

No site do paranamer diz que não é necessário usá-lo a não ser que se queira retro compatibilidade com Java 7 e Java 6…

alguém tem uma ideia do que possa ser e como resolver?

agradeço a atenção

Quando vc coloca os lambdas, tendo esse JavaParameterNamesProvider, dá um erro no paranamer?

Não! Se eu coloco as lambdas e deixo o paranamer nas libs… ele dá esse erro ai de cima no stacktrace… faz sentido se o paranamer ainda não estiver preparado para a sintaxe das lambdas…

me foi sugerido então usar a classe JavaParameterNameProvider para substituir o paranamer (que é desnecessário caso se use java 8 e não se necessite retrocompatibilidade) e adicionar o parâmetro de compilação -parameters… fiz isso, removi a lib do paranamer das libs, coloquei a classe acima (JavaParameterNameProvider) no projeto e adicionei o parâmetro de compilação, mas para a minha surpresa, os nomes dos parâmetros não estavam como deveriam… estavam no seguinte formato: “arg0”, “arg1”, etc…

baixei aquelas classes de exemplo da oracle (MethodParameterSpy e ExampleMethods) e compile com “javac -parameters” e os nomes dos parâmetros foram pegos certinho…

então não sei se o netbeans tá ignorando meu parâmetro de compilação, se a classe que coloquei no projeto está deixando de fazer alguma coisa, se é preciso fazer mais alguma para que isso funciona, não sei…

alguma luz?

agradeço a atenção

tenta colocar o parâmetro -g no compilador tb (infos de debug)

Não funcionou Lucas, os parametros continuam sendo encontrados (pegos) como “arg0, arg1”, etc…

O único uso do paranamer é dentro da classe ParameterNameProvider?

To desconfiado que esse Netbeans tá me trolando…

Fala Lucas, seguinte, percebi uma coisa estranha agora, talvez te ajuda a entender o problema…

Quando tiro a classe JavaParameterNameProvider, coloco a lib do paranamer no projeto, coloco alguma lambda numa classe fora dos controllers (classe utilitárias, classes de persistência, etc) ele implanta e executa o projeto normalmente, inclusive o código da lambda… mas se a lambda tiver dentro de alguma das classes do controller nem implanta o projeto…

não tenho nem ideia do que é…

ajudou alguma coisa agora?

agradeço

hum… se só lambdas dentro do controller são os problemas, deve ser problema no código que procura as rotas.

o java deve criar o lambda como uma classe interna do Controller e o VRaptor tá se perdendo…

vc consegue adicionar um breakpoint nesse método:
br.com.caelum.vraptor.http.route.PathAnnotationRoutesParser.rulesFor(PathAnnotationRoutesParser.java:92)

e ver quais classes estão passando por lá, qdo vc coloca um lambda no controller?

Fala Lucas, então… fiz o debug lá… ele dá pau na seguinte linha da classe PathAnnotationRoutesParser:

rule.is(baseType, javaMethod); linha 118, para ser mais preciso…
só acontece no controller que tem a lambda, mas não especificamente no método que tem a lambda…
indo um pouco mais a fundo… o estouro se dá em:

[code]
// 1) Classe: PathAnnotationRoutesParser
// linha 118
rule.is(baseType, javaMethod);

// 2) Classe: DefaultRouteBuilder
// linha 196
Map<String, Class<?>> types = finder.getParameterTypes(method, sanitize(parameters));

// 3) Classe: DefaultTypeFinder
// Linha 56
Parameter[] parametersFor = provider.parametersFor(method);
[/code] daí o debug não passa…
ajudou um pouco? mais algum teste?

agradeço

Eu queria saber em que ponto ele passa a classe ou o método do lambda nesse fluxo… então veja se o

é chamado alguma vez com o class do lambda (vai ser uma classe bizarra com $ no meio do nome)

Bom dia Lucas, não sei se entendi muito bem sua pergunta…

mas fazendo o debug novamente, fiquei acompanhando as classes que passavam por esse método (registerRulesFor)… no momento em que a aplicação pendura ele está trabalhando com a classe que tem a lambda (LoginController)… nesse momento não importa se é o método que tem a lambda ou qualquer outro da classe… até pendurar não passa nenhuma classe maluca por ali, só os controllers…

na saída da aplicação não sai erro algum… no log do tomcat sai o erro descrito no primeiro post… e na saida do depurador sai apenas:

para você entender melhor o que estou fazendo coloquei uns souts:

    protected List<Route> registerRulesFor(Class<?> baseType) {
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++");
        System.out.println("======> Class: " + baseType.getName());

        EnumSet<HttpMethod> typeMethods = getHttpMethods(baseType);

        List<Route> routes = new ArrayList<>();
        for (Method javaMethod : baseType.getMethods()) {
            System.out.println("======> Method: "+javaMethod.getName());
            System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++");
            if (isEligible(javaMethod)) {

saida:

+++++++++++++++++++++++++++++++++++++++++++++++
======> Class: controllers.acesso.LoginController
======> Method: check
+++++++++++++++++++++++++++++++++++++++++++++++

é com essa classe que a aplicação aborta a implantação…
OBS: o método check não é o que contém a lambda… ele dá erro no primeiro método encontrado, independente de ter lambda lá ou não…

agradeço a atenção

não consegui fazer o paranamer funcionar por aqui… aparentemente ele não é mesmo para ser usado com Java8 e lambdas.

Adicione essa lib no seu projeto:

<dependency>
            <groupId>br.com.caelum.vraptor</groupId>
            <artifactId>vraptor-java8</artifactId>
            <version>4.0.0-RC2</version>
</dependency>

e adicione o parametro -parameters nos argumentos de compilação das suas classes.