Vraptor3 - NullPointerException quando controller não existe [resolvido]

Quando acesso uma página que não existe, ao invés do vraptor 3.1.0 retornar um 404 retorna o seguinte erro:

WARNING: StandardWrapperValve[default]: PWC1406: Servlet.service() for servlet default threw exception java.lang.NullPointerException at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:40) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at esim.web.security.SecurityUserSession.intercept(SecurityUserSession.java:105) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at esim.web.interceptor.NoCacheInterceptor.intercept(NoCacheInterceptor.java:49) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)

Notei o erro nessa linha do InstantiatorInterceptor

public void intercept(InterceptorStack invocation, ResourceMethod method, Object resourceInstance) throws InterceptionException { Class<?> type = method.getResource().getType(); Object instance = container.instanceFor(type); invocation.next(method, instance); }

O atributo method está vindo como null, então o method.getResource está lançando uma NullPointerException.

oi garcia!

muito estranho. voce tem algum componente customizavel?

pode atualizar pro 3.1.1 (que tem mais debug) e ostrar pra gente o log de debug dessa requisicao?

isso acontece numa requisição normal, ou através de um forward?

Lucas, isso acontece com requisições normais via URL. No caso eu tenho uma URI no endereço /admin/degree/, e se eu digitar /admin/degree/xx retorna NullPointerException.

Paulo, atualizei para o 3.1.1. Tenho alguns componentes customizáveis que nada mais é que o próprio RequestExecution, onde apenas troco a chamada do ExecuteMethodInterceptor para o CustomExecuteMethodInterceptor. O log do erro está abaixo. Não coloquei todo o debug porque não tinha informações muito úteis, mas se precisar posso enviar.

O que eu noto é que quando um resource não é encontrado o method vem NULL em todos os interceptors, então no caso do InstantiateInterceptor na linha 40 há um method.getResource, porém method é NULL.

Ambiente é vraptor 3.1.1, glassfishv3, projeto web + módulo EJB remoto.

WARNING: StandardWrapperValve[default]: PWC1406: Servlet.service() for servlet default threw exception java.lang.NullPointerException at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:40) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:47) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at esim.web.security.SecurityUserSessionHolder.intercept(SecurityUserSessionHolder.java:105) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:47) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at esim.web.interceptor.NoCacheInterceptor.intercept(NoCacheInterceptor.java:49) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:47) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at esim.web.interceptor.CustomRequestExecution.execute(CustomRequestExecution.java:43) at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92) at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:47) at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215) at br.com.caelum.vraptor.resource.DefaultResourceNotFoundHandler.couldntFind(DefaultResourceNotFoundHandler.java:41) at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at esim.web.interceptor.CustomRequestExecution.execute(CustomRequestExecution.java:43) at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92) at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:56) at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:277) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188) at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641) at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97) at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185) at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:332) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:233) at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165) at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791) at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693) at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954) at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170) at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88) at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76) at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) at com.sun.grizzly.ContextTask.run(ContextTask.java:69) at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330) at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309) at java.lang.Thread.run(Thread.java:619)

[code]@Component
@RequestScoped
public class CustomRequestExecution
implements RequestExecution {
private final InterceptorStack interceptorStack;
private final InstantiateInterceptor instantiator;

public CustomRequestExecution(InterceptorStack interceptorStack, InstantiateInterceptor instantiator) {
    this.interceptorStack = interceptorStack;
    this.instantiator = instantiator;
}

public void execute()
    throws InterceptionException {
    interceptorStack.add(MultipartInterceptor.class);
    interceptorStack.add(ResourceLookupInterceptor.class);
    interceptorStack.add(FlashInterceptor.class);
    interceptorStack.add(InterceptorListPriorToExecutionExtractor.class);
    interceptorStack.add(instantiator);
    interceptorStack.add(ParametersInstantiatorInterceptor.class);
    interceptorStack.add(DeserializingInterceptor.class);
    interceptorStack.add(CustomExecuteMethodInterceptor.class);
    interceptorStack.add(OutjectResult.class);
    interceptorStack.add(DownloadInterceptor.class);
    interceptorStack.add(ForwardToDefaultViewInterceptor.class);
    interceptorStack.next(null, null);
}

}[/code]

[code]@Component
public class CustomExecuteMethodInterceptor
implements Interceptor {

private final CustomResult result;
private final MethodInfo info;
private final Validator validator;
private final Outjector outjector;

private CustomExecuteMethodInterceptor(CustomResult result, MethodInfo info, Validator validator,
        Outjector outjector) {
    this.result = result;
    this.info = info;
    this.validator = validator;
    this.outjector = outjector;
}

public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance)
    throws InterceptionException {
    try {
        Method reflectionMethod = method.getMethod();
        Object[] parameters = this.info.getParameters();
        Object result = reflectionMethod.invoke(resourceInstance, parameters);
        if (validator.hasErrors()) { // method should have thrown ValidationError
            throw new InterceptionException("There are validation errors and you forgot to specify where to go.");
        }

        if (!reflectionMethod.getReturnType().equals(Void.TYPE)) {
            this.info.setResult(result);
        }

        stack.next(method, resourceInstance);
    } catch (Exception e) {
        if (result.getErrorMethod() == null) {
            throw new RuntimeException(ExceptionUtils.getRootCause(e));
        }

        final String cause = ExceptionUtils.getRootCauseMessage(e);
        final Message message = new ValidationMessage(cause, "error");
        result.include("errors", Arrays.asList(message));

        outjector.outjectRequestMap();

        try {
            final Method errorMethod = result.getErrorMethod();
            final Object target = result.forwardTo(errorMethod.getDeclaringClass());

            errorMethod.invoke(target, result.getErrorArgs());
        } catch (Exception e0) {
            throw new InterceptionException(e0);
        }
    }
}

public boolean accepts(ResourceMethod method) {
    return true;
}

}
[/code]

Esqueci da execução com debug.

FINE: VRaptor received a new request FINEST: Request: org.apache.catalina.connector.RequestFacade@1c9a680 FINE: Invoking interceptor ResourceLookupInterceptor FINE: trying to access /my/dashboard/xxxxx FINE: VRaptor received a new request FINEST: Request: [VRaptorRequest org.apache.catalina.connector.RequestFacade@1c9a680] FINE: Invoking interceptor FlashInterceptor FINE: Invoking interceptor InterceptorListPriorToExecutionExtractor FINE: Invoking interceptor NoCacheInterceptor FINE: Invoking interceptor SecurityUserSession FINE: Invoking interceptor InstantiateInterceptor WARNING: StandardWrapperValve[default]: PWC1406: Servlet.service() for servlet default threw exception java.lang.NullPointerException at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:40) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:47) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at esim.web.security.SecurityUserSessionHolder.intercept(SecurityUserSessionHolder.java:105) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:47) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at esim.web.interceptor.NoCacheInterceptor.intercept(NoCacheInterceptor.java:49) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:47) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65) at esim.web.interceptor.CustomRequestExecution.execute(CustomRequestExecution.java:44)

o ResourceLookupInterceptor deveria mandar um 404 qdo não existe nenhum method que trata a requisição…

vc tá customizando o router? ele deveria lançar um ResourceNotFoundException qdo não acha nenhum controller…

tenta debugar o ResourceLookupInterceptor, e ver o que está acontecendo nele, se for possível

Lucas, não estou sobrescrevendo o routes nesse projeto.

Fiz o debug, e ele entrou no not-found:

} catch (ResourceNotFoundException e) { resourceNotFoundHandler.couldntFind(requestInfo);

Depois disso caiu no SpringProvider:58

Depois SpringProvider:61

A partir daí nas classes do Glassfish. O stacktrace gerado é o mesmo acima.

vc tem algum ResourceNotFoundHandler implementado? ele faz forward pra algum lugar específico?

Lucas, não para ambas perguntas.

Porém eu desanexei as classes CustomExecuteMethodInterceptor e CustomRequestExecution e está agora lançando 404 corretamente. O que pode haver de errado nesses componentes que fazem com que o 404 não seja lançado, mesmo que aparentemente o debug no ResourceLookupInterceptor apontou como lançando a 404?

Você sabe me dizer o que pode estar interferindo em relação a essas duas implementações?

O mais estranho é que dá null-pointer em outro componente, e não nas minhas customizações. A impressão que eu tenho que quando dá um 404 ao invés de fazer um deferer pro container ele está seguindo a requisição pelo vraptor.

Ha! Descobri o erro.

Se notarem meu componente CustomRequestExecution é um clone de RequestExecution, só que chama o CustomExecuteMethodInterceptor ao ao invés de ExecuteMethodInterceptor.

Só que esse componente foi alterado para @PrototypeScoped, enquanto o meu estava como @RequestScoped. Bastou eu alterar a anotação e tudo funciona perfeitamente, tanto com exception handler como com erro-404. :thumbup:

Só tenho uma dúvida… eu não consigo sobrescrever o comportamento do ExecuteMethodInterceptor, por isso clonei o RequestExecution. Porque eu não consigo isso? Há alguma forma diferente de sobrescrever esse componente? Quando eu sobrescrevo ele simplesmente não é chamado em momento algum.

vc só consegue sobrescrever o ExecuteMethodInterceptor diretamente se criar uma classe filha de ExecuteMethodInterceptor

Feito e funcionando. Lembro que criei essa classe implementado interceptor e a CustomRequestExecution porque nos tempos do vraptor 3.0.0 ele não era um interceptor, ou algo assim. Agora fiz os ajustes e está tudo certo.

Abraços, e obrigado.

da-lhe garcia customizando VRaptor de cima a baixo :slight_smile:

Uma das coisas que eu mais curto no vraptor é que podemos customiza-lo facilmente. Lembro-me dos tempos do Struts 1.x, que aliás era o melhor da época, tinhamos sempre que recorrer a forças sobrenaturais quando queriamos customizar algo. Essa facilidade de pegar qualquer componente do vraptor e sobrescreve-lo apenas com um @Component é fantástico.

Por enquanto essas customizações são coisas bem especificas para o projeto, outras ainda não testei direito (como o que aconteceu aqui), mas conforme for andando os trabalhos vou passando algumas coisas para vocês avaliarem. Quem sabe alguma coisa possa ser útil.

Abraços