[Resolvido][VRaptor] java.net.SocketException: Connection reset by peer: socket write error

Gente, bom dia.

Eu estou com a seguinte exception quando eu faço um redirect. Mas o problema é que acontece somente em 1 método.

Os outros estão fazendo redirect normalmente…

Gostaria de entender porquê que isso acontece.

Aqui está a StackTrace:

09/04/2012 07:59:18 org.apache.catalina.core.StandardContextValve throwable AVISO: Exception Processing ErrorPage[exceptionType=java.lang.Exception, location=/erro.jsp] ClientAbortException: java.net.SocketException: Connection reset by peer: socket write error at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:333) at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:299) at org.apache.catalina.connector.Response.flushBuffer(Response.java:560) at org.apache.catalina.core.StandardContextValve.throwable(StandardContextValve.java:404) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:204) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:151) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:269) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) Caused by: java.net.SocketException: Connection reset by peer: socket write error at java.net.SocketOutputStream.socketWrite0(Native Method) at java.net.SocketOutputStream.socketWrite(Unknown Source) at java.net.SocketOutputStream.write(Unknown Source) at org.apache.coyote.http11.InternalOutputBuffer.realWriteBytes(InternalOutputBuffer.java:216) at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:437) at org.apache.coyote.http11.InternalOutputBuffer.flush(InternalOutputBuffer.java:119) at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:721) at org.apache.coyote.Response.action(Response.java:170) at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:328) ... 16 more

isso acontece sempre nesse método?

Sempre. O método é responsável por salvar um determinado registro e depois dá um redirect para uma tela que lista esses registros.

Eu percebi que o erro acontece e o VRaptor manda ele refazer a requisição, ele volta certinho, tenta inserir e dá erro pois já foi inserido.
Quando dá o erro, ele faz um redirect para outra tela, o formulário. E este segundo redirect funciona direitinho.

então tão acontecendo 2 redirects, certo?

tipo dá erro ao commit da transação e daí vc redireciona pra uma página de erro?

Não, primeiro ele insere o registro no banco e manda uma mensagem na tela informando que foi inserido e depois dá um redirect, ai é lançado o ClientAbortException.
Depois o interceptor do VRaptor captura esse erro e chama o método denovo. Passando os mesmos parametros novamente.

Nesse momento, tenta fazer o insert novamente, mas por existir campos unique, dá uma HibernateException. Quando acontece uma HibernateException, eu mando uma mensagem na view e dou um redirect para o formulário.

Esse segundo redirect não lança o ClientAbortException. Só o primeiro.

public void associar(SellerDeepSea sellerDeepSea,
			CustomerPadrao customerPadrao, AccountGroup accountGroup) {
		try {
			RelCusSelAcc obj = new RelCusSelAcc();

			obj.setAccountGroup(accountGroup);
			obj.setCustomerPadrao(customerPadrao);
			obj.setSellerDeepSea(sellerDeepSea);
			obj.setSituacao(Situacao.ATIVO);

			sellerDeepSeaDAO.saveRelacionamento(obj); // aqui não dá erro na primeira vez

			service.modal("sucesso", "objAssociado",
					".dataTables_filter input:text");
			result.redirectTo(this).listCustomer(sellerDeepSea.getCodigo()); // aqui é lançado o ClientAbortException.
		} catch (HibernateException e) {
// ClientAbortException não é tratado nesse catch
			service.modal("erro", "objNaoAssociado", "#accountGroup");
			result.redirectTo(this).associacoes(sellerDeepSea.getCodigo());// na segunda vez que fizer a requisição, vai dar HibernateException e vai executar esse redirect normalmente.
		}
	}

Outra coisa que eu percebi é que se eu inserir um catch para ClientAbortException e simplesmente não fazer nada, as requisições ficam malucas.

esse service.modal tenta escrever algo no response?

o método modal insere dados pelo result.include…
Ele nunca deu problema… Mas nunca se sabe, né.

Aqui está o codigo dele.

[code]public void modal(String titulo, String mensagem, String onClickFocus) {
if (this.bundle != null) {
titulo = this.bundle.getString(titulo);
mensagem = this.bundle.getString(mensagem);
}

	if (onClickFocus != null && onClickFocus != "") {
		onClickFocus = "$('" + onClickFocus + "').focus();";
		this.result.include("onClickFocus", onClickFocus);
	}

	this.result.include("mensagemModal", mensagem);
	this.result.include("tituloModal", titulo);
	this.result.include("buttonFocus", "buttonConfirmar");
	this.result.include("modal", "modalOK");
}[/code]

estranho… o client abort é qdo o browser desiste da requisição…

não tem mais nada que faça operações com o response? tipo um interceptor?

Tem sim. Isso tem a ver com o browser? Esse é um sistema corporativo e intranet que só pode ser acessado por IE8.

[code]@Intercepts(after = FuncionalidadesInterceptor.class)
public class SetFuncionalidadesInterceptor implements Interceptor {

private final UserWeb userWeb;
private final Result result;

public SetFuncionalidadesInterceptor(UserWeb userWeb, Result result) {
	this.userWeb = userWeb;
	this.result = result;
}

@Override
public void intercept(InterceptorStack stack, ResourceMethod method,
		Object resourceInstance) throws InterceptionException {
	for (TipoFuncionalidade funcionalidade : userWeb
			.getFuncionalidadeList()) {
		result.include(("FUNC_" + funcionalidade.name()), true);
	}

	stack.next(method, resourceInstance);
}

@Override
public boolean accepts(ResourceMethod method) {
	return (this.userWeb.getLogado());
}

}[/code]

[code]@Intercepts(after = ParametersInstantiatorInterceptor.class)
public class NoCacheInterceptor implements Interceptor {

private final HttpServletResponse response;

public NoCacheInterceptor(HttpServletResponse response) {
	this.response = response;
}

@Override
public void intercept(InterceptorStack stack, ResourceMethod method,
		Object resourceInstance) throws InterceptionException {
	// set the expires to past
	response.setHeader("Expires", "Wed, 31 Dec 1969 21:00:00 GMT");

	// no-cache headers for HTTP/1.1
	response.setHeader("Cache-Control",
			"no-store, no-cache, must-revalidate");

	// no-cache headers for HTTP/1.1 (IE)
	response.addHeader("Cache-Control", "post-check=0, pre-check=0");

	// no-cache headers for HTTP/1.0
	response.setHeader("Pragma", "no-cache");

	stack.next(method, resourceInstance);
}

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

}[/code]
Se for por causa do interceptor, tem grandes chances de ser pelo NoCacheInterceptor… Mas eles estão ai há muito tempo. E em todas as outras requisições tudo funciona normalmente.

qdo o client abort acontece não dá o erro do hibernate, certo?

a requisição é direto pro método associar?

a requisição chega a voltar pro browser?

Não dá, tanto que se eu voltar para a tela de listagem, o novo registro aparece la.

Quando eu envio a requisição, ela é direto sim. Assim como quando o VRaptor refaz ela.

A requisição só volta pro browser depois que o VRaptor refaz a requisição e dá HException…

o vraptor refaz a requisição? ou é a aplicação que faz isso?

é o VRaptor. Ele cai nesse finally aqui.

public <T> T provideForRequest(RequestInfo request, Execution<T> execution) { VRaptorRequestHolder.setRequestForCurrentThread(request); REQUEST.start(); try { return execution.insideRequest(container); } finally { REQUEST.stop(); VRaptorRequestHolder.resetRequestForCurrentThread(); } }

mas esse código não refaz a requisição… o REQUEST.stop(); só e o cara que finaliza o escopo de request.

VRaptorRequestHolder.resetRequestForCurrentThread();

Esse método faz o que? Pelo nome eu pensei que ele refizesse o request. Pois logo à frente (depois de muitos f6) ele volta para o método associar.

ele limpa um thread local (feio, mas necessário) só isso.

Então quem refaz a requisição? Pois quando ele sai do método, ele passa por tudo isso e depois volta para o método…

será que não é a própria aplicação fazendo isso? será que ela não tá repetindo a requisição?

vc consegue fazer a requisição em outro browser, ou, melhor ainda, sem browser? (via linha de comando ou usando o poster do firefox)

Só pode ser o browser, testei no firefox e ele funciona lindo e bonito.
Agora é pior do que eu imaginava.

Então, pela exception que está dando, significa que o browser cortou a sessão no meio… Mas por que? Como é que eu arrumo isso? Agora eu fiquei completamente perdido…