Upload GAE (Blobstore) entidade chegando null

65 respostas
boneazul

Estou utilizando o vraptor 3.3.1 em meu primeiro contato com o GAE.

Preciso de uma interface para fazer upload de arquivos e segundo a documentação do GAE tenho que utilizar um serviço de blob deles o BlobstoreService. Segui conforme manda a documentação o único problema é que minha entidade chega como null no controlador apesar de o resto funcionar como deveria (o upload é feito corretamente) . Vou publicar minhas classes e formulario para ver se alguem me ajuda !!

Controlador.java

package br.com.jslsolucoes.erbuss.admin.controllers;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.view.Results;
import br.com.jslsolucoes.erbuss.admin.annotations.Paginated;
import br.com.jslsolucoes.erbuss.admin.annotations.PutAccessInView;
import br.com.jslsolucoes.erbuss.admin.annotations.Rules;
import br.com.jslsolucoes.erbuss.admin.annotations.VerifyAccess;
import br.com.jslsolucoes.erbuss.admin.daos.ExplicacaoDao;
import br.com.jslsolucoes.erbuss.admin.models.Explicacao;
import br.com.jslsolucoes.erbuss.admin.util.view.VraptorViewUtil;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;

@Resource
@Path("explicacao")
public class ExplicacaoController {

	private ExplicacaoDao daoExplicacao;
	private Result result;
	private HttpServletResponse response;
	private HttpServletRequest request;
	private BlobstoreService blobstoreService;

	public ExplicacaoController(Result result, ExplicacaoDao daoExplicacao,
			HttpServletResponse response, HttpServletRequest request) {
		this.result = result;
		this.daoExplicacao = daoExplicacao;
		this.response = response;
		this.request = request;
		this.blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
	}


        //Funciona belezinha
	@Path("foto/{explicacao.id}")
	public void foto(Explicacao explicacao) throws IOException, SQLException {
		Explicacao loadExplicacao = daoExplicacao.load(explicacao.getId());
		BlobKey blobKey = new BlobKey(loadExplicacao.getUrl());
		blobstoreService.serve(blobKey, response);
		result.use(Results.nothing());
	}


	@VerifyAccess
	public void form() {
                //Aqui ele pede pra criar uma url que vai na action do formulario que sai algo do tipo "/_ah/upload/_um_hash_louco"
		this.result.include("uploadUrl",
				blobstoreService.createUploadUrl("/explicacao/saveOrUpdate"));
	}

        public void message() {

	}

	@Post
	public void saveOrUpdate(Explicacao explicacao) throws IOException, SQLException {		
		
                //Nesse ponto chega null
		System.out.println(explicacao);
		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);
		BlobKey foto = blobs.get("foto");
		if (foto != null) {
                        ImagesService imagesService = ImagesServiceFactory
					.getImagesService();
			imagesService.applyTransform(
					ImagesServiceFactory.makeResize(50, 50),
					ImagesServiceFactory.makeImageFromBlob(foto));
                        //Aqui gera um null pointer exception é claro
			explicacao.setUrl(foto.getKeyString());
		}

		result.include("operation", daoExplicacao.saveOrUpdate(explicacao2));
		
		result.redirectTo(this).message();

	}
}

E meu formulario

<form enctype="multipart/form-data" method="post" action="/_ah/upload/agxlcmJ1c3MtYWRtaW5yHAsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxipAQw" id="par_formulario">
	<input type="hidden" name="explicacao.id"/>
       Foto : <input type="file" name="foto"/>
       Título :<input type="text" name="explicacao.titulo"/>
        Descricao :<textarea name="explicacao.descricao"/>
</form>

Alguma dica ?

65 Respostas

Lucas_Cavalcanti

vc está usando o gmultipart? os dados do upload de verdade chegam completos?

boneazul

se voce se refere ao “gmultipart.jar” na lib sim estou usando sim pois ele vem no blank project gae do vraptor.

Quantos aos dados de upload de verdade chegar completos ??? não entendi muito bem a pergunta…

Se for referindo ao trecho

Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);  
BlobKey foto = blobs.get("foto");

esse objeto BlobKey foto nao chega null não …chega o objeto certinho tanto é que
consigo pegar a chave gerada com foto.getKeyString(); alem do que o arquivo é salvo na pasta appengine-generated corretamente!!

Alguma ideia ?

Um sysout de request traz os seguintes dados… não sei se ajuda …

[VRaptorRequest POST /_ah/upload/agxlcmJ1c3MtYWRtaW5yHAsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxiuAQw HTTP/1.1
Host: localhost:8888
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; pt-BR; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: pt-br,pt;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://localhost:8888/explicacao/form
Cookie: JSESSIONID=1pf71fecu2i8o
Content-Type: multipart/form-data; boundary=---------------------------15009643022794
Content-Length: 75787

]
Lucas_Cavalcanti

tem que ver na documentação do BlobStore se pode mandar mais dados junto com o upload, e como que se pegam esses dados…

o VRaptor pega os parâmetros direto do request, talvez isso mude no BlobStore.

Rafael_Nunes

Sim, é possível enviar parâmetros de request junto ao arquivo no upload, via Blobstore.

Rafael_Nunes

Sugestão:

Ao invés de usar a APid e Image para dar um resize na imagem durante o request, usa a API de ImageServing

G

Creio que o Blobstore não seja compatível com o upload do VRaptor. Isso porque o VRaptor internamente usa as libs gmultipart e commons-fileupload para upload no GAE/J.

Quando você tem um form para upload e você lê os dados, eles ficam indisponíveis para um segundo uso. Digamos que a grosso modo essas informações sejam descartáveis. Sendo assim quando o request é feito, internamente o VRaptor faz o parse do request e monta os objetos de upload via UploadedFile. Quando o Blobstore for ler os dados, os mesmos estão indisponíveis porque o VRaptor já leu. Isso não é um problema causado pelo VRaptor, pois a spec diz que é assim.

O que você pode fazer é receber o objeto UploadedFile direto no seu método e não usar o Blobstore. Ou então remover as libs commons-fileupload para desabilitar o upload interno do VRaptor, e usar apenas o Blobstore assim como você faz.

Outra sugestão é sobrescrever o CommonsUploadMultipartInterceptor sobrescrevendo o método accept para retornar sempre FALSE.

boneazul

garcia-jj:
Creio que o Blobstore não seja compatível com o upload do VRaptor. Isso porque o VRaptor internamente usa as libs gmultipart e commons-fileupload para upload no GAE/J.

Quando você tem um form para upload e você lê os dados, eles ficam indisponíveis para um segundo uso. Digamos que a grosso modo essas informações sejam descartáveis. Sendo assim quando o request é feito, internamente o VRaptor faz o parse do request e monta os objetos de upload via UploadedFile. Quando o Blobstore for ler os dados, os mesmos estão indisponíveis porque o VRaptor já leu. Isso não é um problema causado pelo VRaptor, pois a spec diz que é assim.

O que você pode fazer é receber o objeto UploadedFile direto no seu método e não usar o Blobstore. Ou então remover as libs commons-fileupload para desabilitar o upload interno do VRaptor, e usar apenas o Blobstore assim como você faz.

Outra sugestão é sobrescrever o CommonsUploadMultipartInterceptor sobrescrevendo o método accept para retornar sempre FALSE.

Bom garcia dos dois modos não rolou . Retirando a commons upload ele reclama de classnotfound !!

Sobrescrevendo o CommonsUploadMultipartInterceptor continua a chegar null tb!!

Tinha resolvido de outra maneira. Em vez de utilizar serviço de diretório blobstoreservice estou gravando direto em uma coluna de blob do gae e parece funiconar so que me deparei com um problema com arquivos maior que 1mb ou seja voltei a estaca zero , já que a regra de negocio os arquivos podem ser musicas , videos etc . E para isso o gae pelo jeito obriga a utilizar o blobstoreservice!!

Estava procurando no forum e achei uma duvda semelhante que diz o cara ter conseguido resolver

http://www.guj.com.br/java/221168-resolvido---gae-blobstore--vraptor-/2

mas ele falou que conseguiu pegar os dados por request mesmo , coisa que eu nao consigo fazer ja que nao chega parametro algum em meu request !!!

Parece que o blobstore trata o upload e tenta mandar pra frente seguindo o que passei no createUploadUrl so que sem os dados de request original !!!

A documentação relativa a blobstore service está aqui

http://code.google.com/intl/pt-BR/appengine/docs/java/blobstore/overview.html

Tem como eu fala pro vraptor ignorar uma url especifica exemplo “/explicacao/saveOrUpdate” no web.xml e fazer uma servlet como no exemplo de implementação acima … dai trato os dados de request diretamente lá…

seria um teste a se fazer…

boneazul

Se tem algum exemplo de como pegar os dados de request no upload rafael , pois não vi metodos em blobstoreservice que faça isso!!

boneazul

Garcia eu sobrescrevi a classe CommonsUploadMultipartInterceptor mas mesmo assim no debug ela continua a ser chamada , eu sobrescrevi certo ?

22:26:28,126 INFO [br.com.caelum.vraptor.interceptor.multipart.CommonsUploadMultipartInterceptor] - Request contains multipart data. Try to parse with commons-upload.

@Component
public class CustomCommonsUploadMultipartInterceptor extends
		CommonsUploadMultipartInterceptor {

	public CustomCommonsUploadMultipartInterceptor(HttpServletRequest request,
			MutableRequest parameters, MultipartConfig cfg,
			Validator validator, ServletFileUploadCreator fileUploadCreator) {
		super(request, parameters, cfg, validator, fileUploadCreator);
	}

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

}

sobrescrevi de maneira certa ?

Lucas_Cavalcanti

isso não vai funcionar, tente apenas remover o gmultipart.jar e o commons-fileupload.jar do WEB-INF/lib

boneazul

Se eu retiro esses jar do path acontece um classnotfound exception

14/03/2011 22:39:52 com.google.apphosting.utils.jetty.JettyLogger warn
AVISO: failed vraptor: java.lang.NoClassDefFoundError: org/apache/commons/fileupload/FileUploadBase$SizeLimitExceededException
14/03/2011 22:39:52 com.google.apphosting.utils.jetty.JettyLogger warn
AVISO: failed com.google.apphosting.utils.jetty.DevAppEngineWebAppContext@e06940{/,D:\www\erbuss-gae-admin\WebContent}: java.lang.NoClassDefFoundError: org/apache/commons/fileupload/FileUploadBase$SizeLimitExceededException
14/03/2011 22:39:52 com.google.apphosting.utils.jetty.JettyLogger warn
AVISO: failed JettyContainerService$ApiProxyHandler@e576d4: java.lang.NoClassDefFoundError: org/apache/commons/fileupload/FileUploadBase$SizeLimitExceededException
14/03/2011 22:39:52 com.google.apphosting.utils.jetty.JettyLogger warn
AVISO: Error starting handlers
java.lang.NoClassDefFoundError: org/apache/commons/fileupload/FileUploadBase$SizeLimitExceededException
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:169)
	at br.com.caelum.vraptor.scan.DynamicWebAppBootstrap.configure(DynamicWebAppBootstrap.java:45)
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.start(SpringProvider.java:84)
	at br.com.caelum.vraptor.VRaptor.init(VRaptor.java:110)
	at br.com.caelum.vraptor.VRaptor.init(VRaptor.java:103)
	at org.mortbay.jetty.servlet.FilterHolder.doStart(FilterHolder.java:97)
	at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
	at org.mortbay.jetty.servlet.ServletHandler.initialize(ServletHandler.java:662)
	at org.mortbay.jetty.servlet.Context.startContext(Context.java:140)
	at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1250)
	at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:517)
	at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:467)
	at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
	at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
	at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
	at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
	at org.mortbay.jetty.Server.doStart(Server.java:224)
	at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
	at com.google.appengine.tools.development.JettyContainerService.startContainer(JettyContainerService.java:186)
	at com.google.appengine.tools.development.AbstractContainerService.startup(AbstractContainerService.java:149)
	at com.google.appengine.tools.development.DevAppServerImpl.start(DevAppServerImpl.java:219)
	at com.google.appengine.tools.development.DevAppServerMain$StartAction.apply(DevAppServerMain.java:164)
	at com.google.appengine.tools.util.Parser$ParseResult.applyArgs(Parser.java:48)
	at com.google.appengine.tools.development.DevAppServerMain.<init>(DevAppServerMain.java:113)
	at com.google.appengine.tools.development.DevAppServerMain.main(DevAppServerMain.java:89)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException
	at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
	at com.google.appengine.tools.development.IsolatedAppClassLoader.loadClass(IsolatedAppClassLoader.java:176)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
	... 26 more
14/03/2011 22:39:52 com.google.apphosting.utils.jetty.JettyLogger info
INFO: Started SelectChannelConnector@127.0.0.1:8888
14/03/2011 22:39:52 com.google.appengine.tools.development.DevAppServerImpl start
INFO: The server is running at http://localhost:8888/
G

Lucas, porque não funcionaria? Anotando com @Intercepts ao invés de @Component deveria, não?

Eu criar um projeto blank aqui para ver como funciona o blobstore.

Lucas_Cavalcanti

não funciona pq o plugin do gae já sobrescreve o CommonsUploadMultipartInterceptor, não dá pra sobrescrever duas vezes =(

o que dá pra fazer é abrir o vraptor-plugin-gae.jar e remover o AppEngineMultipartInterceptor.class, e numa próxima versão ter uma forma de desabilitá-lo facilmente

G

Ahh, é verdade, Lucas. Bem lembrado.

O blobstore está funcionando, quem não funciona é injetar o objeto Explicacao. Isso porque quando a requisição é multipart, o vraptor injeta os dados via commons-fileupload. Porém não há nada no fileupload, já que o blobstore faz isso usando request.getParameter. Creio que tenha que fazer outra abordagem para usar o blobstore.

boneazul

garcia-jj:
Ahh, é verdade, Lucas. Bem lembrado.

O blobstore está funcionando, quem não funciona é injetar o objeto Explicacao. Isso porque quando a requisição é multipart, o vraptor injeta os dados via commons-fileupload. Porém não há nada no fileupload, já que o blobstore faz isso usando request.getParameter. Creio que tenha que fazer outra abordagem para usar o blobstore.

Lucas , Garcia , alguma ideia de como poderia fazer isso funcionar ??

Lucas_Cavalcanti

abra o vraptor-plugin-gae.jar (como se fosse um .zip) e remova a classe AppEngineMultipartInterceptor

sergiolopes

O BlobStore não é um upload normal que possa ser tratado pelo VRaptor, é um serviço direto na API do GAE.

Você precisa usar a classe BlobstoreService diretamente e do jeito que tá na documentação. Isto é, precisar chamar o createUploadUrl para colocar no seu form e deixar o GAE receber o upload. Aí depois disso ele te redireciona para um página sua onde você consegue pegar o BlobKey gerado por ele e guardar em algum lugar se for o caso. Mas todo o processo de upload é pelo GAE, não o VRaptor.

E uso o Blobstore sem problemas num projeto aqui no VRaptor, sem hack nenhum, mas usando a API dele diretamente.

Abraços

boneazul

Removi esse interceptor do jar e tb removi a commons-file-upload e gmultipart , o server sobe corretamente e no console fala que a commons upload nao esta no path
mas continua a chegar null na entidade!!

boneazul

Sergio Lopes:
O BlobStore não é um upload normal que possa ser tratado pelo VRaptor, é um serviço direto na API do GAE.

Você precisa usar a classe BlobstoreService diretamente e do jeito que tá na documentação. Isto é, precisar chamar o createUploadUrl para colocar no seu form e deixar o GAE receber o upload. Aí depois disso ele te redireciona para um página sua onde você consegue pegar o BlobKey gerado por ele e guardar em algum lugar se for o caso. Mas todo o processo de upload é pelo GAE, não o VRaptor.

E uso o Blobstore sem problemas num projeto aqui no VRaptor, sem hack nenhum, mas usando a API dele diretamente.

Abraços

Poderia postar seu form e seu controlador para mim ??

Lucas_Cavalcanti

boneazul, como o Sergio disse, vc vai ter que usar a api do Blobstore diretamente nesse caso, não vai dar pra receber o parâmetro pelo VRaptor…

se vc precisar fazer isso em mais lugares, a gente pode tentar fazer uma alteração no ParametersProvider do VRaptor pra usar o BlobStore para preencher os parâmetros, quando for necessário.

sergiolopes

É bem parecido com o seu na verdade. Sem suar upload diretamente mas pela API direto do Blobstore.

@Resource
public class DownloadsController {

	private final Result result;
	private final HttpServletRequest request;
	private final HttpServletResponse response;
	private final Configuracoes config;
	private final BlobstoreService blobstoreService;

	public DownloadsController(BlobstoreService blobstoreService, Result result, 
			HttpServletRequest request, HttpServletResponse response, Configuracoes config) {
		this.blobstoreService = blobstoreService;
		this.result = result;
		this.request = request;
		this.response = response;
		this.config = config;
	}
	
	@Path("/admin/downloads/enviar")
	public void formulario () {
		String uploadUrl = blobstoreService.createUploadUrl("/admin/downloads/upload");
		result.include("uploadUrl", uploadUrl);
	}
	
	@Path("/admin/downloads/upload")
	public void upload () {
		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);
                BlobKey blobKey = blobs.get("arquivo");
        
                if (blobKey == null) {
                   result.use(Results.http()).sendError(500);
                } else {
                   result.redirectTo("/admin/");
                }
	}
}

No meu caso eu não faço nada com a BlobKey, apenas vejo se foi criado direitinho.
(depois eu busco diretamente na tabela do Blobstore por um critério meu)

E o form é basicão:

<form action="${uploadUrl}" method="post" enctype="multipart/form-data"> 
			<p><input type="file" name="arquivo"></p> 
      		<p><input class="buttons" type="submit" value="Salvar"/></p> 
    	</form>
boneazul

Mas o sergio pelo visto seu form não manda nada alem de um arquivo até ai tudo bem funcionaria bem .

Mas no meu caso é um form com uma entidade junto mais o upload de 1 arquivo ate pq eu salvo essa key relacionado com a entidade toda para poder recuperar depois!!

sergiolopes

Entendi, é o outro param que vem null, isso?
Na documentação nem parece falar de usar outros params. Nunca usei mesmo.

E se você fizer request.getParamenter na mão, o parâmetro tá lá?

G

Foi o que pensei, acho que seria uma ótima idéia.

Separe as lógicas e deixe um método somente para o upload. Caso contrário você terá que instanciar manualmente o objeto e setar os valores via request.getParameter. Na documentação do blobstore tem um exemplo do uso do request.getParameter.

boneazul

Sergio Lopes:
Entendi, é o outro param que vem null, isso?
Na documentação nem parece falar de usar outros params. Nunca usei mesmo.

E se você fizer request.getParamenter na mão, o parâmetro tá lá?

chega null tb se eu tento

request.getParameter(“explicacao.titulo”) chega null tb…isso que tá pegando!!!

boneazul

Foi o que pensei, acho que seria uma ótima idéia.

Separe as lógicas e deixe um método somente para o upload. Caso contrário você terá que instanciar manualmente o objeto e setar os valores via request.getParameter. Na documentação do blobstore tem um exemplo do uso do request.getParameter.

Até ai num tenho problema em instanciar na mao Garcia o problema é que .

request.getParameter(“explicacao.titulo”) ou request.getParameter(“explicacao.descricao”) chega null…o problema ta sendo ai entendeu ??

sergiolopes

Testei aqui no meu código que postei antes. E pelo menos localmente funcionou.

Coloquei um parâmetro foo no form e peguei com request.getParameter

boneazul

Sergio Lopes:
Testei aqui no meu código que postei antes. E pelo menos localmente funcionou.

Coloquei um parâmetro foo no form e peguei com request.getParameter

vixi cara entao agora zebrou pra mim chega null…Qual será a diferença de ambientes de nois dois … e pq cargas da agua chega null pra mim será…

Qual versao de vraptor voce esta usando ?? e qual versao de sdk do gae voce usa local ??

Nesse teste estou usando a versao 3.3.1 do vraptor e sdk 1.4.2 do gae

sergiolopes

VRaptor 3.3.0 com GAE 1.4.2.
Será problema com o 3.3.1?

Lucas_Cavalcanti

pode testar por favor mudando as versões, pra confirmar se é isso?

boneazul

Estou recriando o form e controller igualzinho o do sergio pra ver com a versão 3.3.0

sergiolopes

Funcionou aqui na 3.3.1

boneazul

Cara criei um projeto zerado so com vraptor-blank-gae 3.3.0 e mesmo assim chega nulll se eu tentar pegar o request …

request.getParameter("foo"); // chega null

A unica diferenca que vi até agora é que voce injeta BlobStoreservice no construtor como voce faz a injecao ??

Eu estou fazendo assim …

this.blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

e voce ja recebe no construtor por outro componente ???

Recriei com seu controller e seu form …

sergiolopes

Tenho um ComponentFactory que faz exatamente isso também…

@RequestScoped
@Component
public class BlobstoreServiceComponentFactory implements ComponentFactory&lt;BlobstoreService&gt; {

	public final BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
	
	public BlobstoreService getInstance() {
		return blobstoreService;
	}

}
sergiolopes

Lembrei de um detalhe: uso o PicoContainer aqui ao invés do Spring. Seria isso?

boneazul

Cara troquei pro pico aqui e funcionou…alguma opinião lucas ??

Lucas_Cavalcanti

pode ser alguma ordem em que os interceptors passam =/

vc consegue me passar esse projeto zerado que vc usou, pra eu poder testar aqui, por favor?

de qqer forma, principalmente pro gae, se vc não usar nada específico do Spring, é melhor trocar pro pico ou pro guice mesmo, eles são mais rápidos

Abraços

boneazul

Lucas Cavalcanti:
pode ser alguma ordem em que os interceptors passam =/

vc consegue me passar esse projeto zerado que vc usou, pra eu poder testar aqui, por favor?

de qqer forma, principalmente pro gae, se vc não usar nada específico do Spring, é melhor trocar pro pico ou pro guice mesmo, eles são mais rápidos

Abraços

Publiquei aki o projeto que chega null!!

http://www.jslsolucoes.com.br/vraptor-gae-bug.rar

Quanto a trocar o spring pelo pico ou guice seria legal entao o zip do blank ja vir com o pico ou guice por default em vez do spring!!

Abraços e fazendo download me da um toque pra retirar esse projeto do server…

Lucas_Cavalcanti

opa, baixei aqui. estou dando uma olhada

valeu

Lucas_Cavalcanti

não consegui testar aqui (a versão do meu plugin do gae tá desatualizada)
mas testa isso:

  • remova o AppEngineMultipartInterceptor do vraptor-gae.jar
  • remova os jars commons-fileupload e commons-io
  • troque os jars do pico pelos do spring

isso deveria funcionar

boneazul

Lucas Cavalcanti:
não consegui testar aqui (a versão do meu plugin do gae tá desatualizada)
mas testa isso:

  • remova o AppEngineMultipartInterceptor do vraptor-gae.jar
  • remova os jars commons-fileupload e commons-io
  • troque os jars do pico pelos do spring

isso deveria funcionar

só trocando pelo pico esta funcionando tudo como deveria … o request não chega mais null se precisar …e o vraptor voltou a popular corretamente a entidade…é algo com o spring mesmo que ta fazendo o request ficar null …e assim atrapalhando o vraptor a popular automatico a entidade…

public void saveOrUpdate(Explicacao explicacao) throws IOException  {			
			Map<String, BlobKey> blobs = blobStoreService.getUploadedBlobs(request);
			BlobKey fotoKey = blobs.get("foto");
			if (fotoKey != null) {
				explicacao.setUrl(fotoKey.getKeyString());
			}
			result.include("operation", daoExplicacao.saveOrUpdate(explicacao));
			result.redirectTo(this).message();
	}

Resumindo a unica coisa que fiz foi trocar pelo pico container…

Abraços

boneazul

Lucas Cavalcanti:
não consegui testar aqui (a versão do meu plugin do gae tá desatualizada)
mas testa isso:

  • remova o AppEngineMultipartInterceptor do vraptor-gae.jar
  • remova os jars commons-fileupload e commons-io
  • troque os jars do pico pelos do spring

isso deveria funcionar

Ja fiz esse teste e mesmo assim chegava null utilizando o spring…

Lucas_Cavalcanti

funciona por causa de um bug do vraptor+pico :stuck_out_tongue:

esse bug que o garcia-jj corrigiu: https://github.com/caelum/vraptor/issues/335

vou tentar fazer isso funcionar de qqer forma. []'s

sergiolopes

Lucas Cavalcanti:
funciona por causa de um bug do vraptor+pico :stuck_out_tongue:

esse bug que o garcia-jj corrigiu: https://github.com/caelum/vraptor/issues/335

vou tentar fazer isso funcionar de qqer forma. []'s

Bem estranho… pq do ponto de vista do VRaptor, isso não é um fileupload. O upload é tratado pelo Blobstore e você só recebe a BlobKey já pronta. A URL que vai no seu form não é da sua aplicação, é do Google.

sergiolopes

Uso o GAE diariamente com VRaptor e acho melhor usar o Pico mesmo. O Spring no GAE é muito lento.
Uma outra dica seria usar o scanning estático do VRaptor, rodando ele no momento do build, ao invés de usar o scanning dinâmico default que roda quando a aplicação sobe (isso faz sua instância demorar muito no GAE).

No meu build.xml, tenho:

<target name="cpscan" depends="compile, prepare-to-production">
		<java fork="true" classname="br.com.caelum.vraptor.scan.VRaptorStaticScanning"
			  classpathref="project.classpath">
		</java>
	</target>
G

O VRaptor intercepta as requisições ah*. Sendo assim o request é lido como multipart.

sergiolopes

Verdade, tem um ano que abri essa issue: https://github.com/caelum/vraptor/issues#issue/203
Aqui no projeto tenho um hack pra ele não filtrar ah*


Mas de qualquer forma, o pau que tá dando não é no request ah* do BlobStore, é no request seguinte, quando o BlobStore redireciona pra URL da aplicação. Nesse request seguinte, acho que não é multipart (será?), porque o arquivo foi tratado no request anterior do BlobStore

G

Será que por padrão não interceptar as páginas ah* é correto? Pergunto isso porque será que não haverá casos onde precisariamos interceptar essas páginas? Pergunto para ti isso porque sei que tu tens muita experiência no GAE/J.

Uma sugestão que o Lucas deu foi de dar suporte ao blobstore como multipart-interceptor.

sergiolopes

Pro GAE, eu acho correto não filtrar _ah*. Esses endereços são usados só pelo GAE (login, blobstore, painel de admin etc).
O usuário até pode configurar uma URL da aplicação como _ah/blabla mas isso é feio; acho razoável assumir que as URLs _ah* são sempre do GAE e não da aplicação.

E do Blobstore passar pelo VRaptor, acho que não tem como. Não tem uma API pra inserir no Blobstore arbitrariamente, apenas usando o mecanismo dele de upload pelo BlobstoreService. Acho que o VRaptor não deve se meter nisso. Precisamos só descobrir direito qual foi o bug que fez os parâmetros não chegarem direito no Spring

G

O bug é porque o pico não estava processando upload no GAE. Houve uma correção há alguns dias para isso.

lorenooliveira

Oi pessoal. Não sei se esse tópico já é assunto superado, mas estou passando pelo problema e fiquei com uma dúvida.

No caso, estou conseguindo acessar todos os campos do meu formulário original via request.getParameter(). O “problema” é que eu não gostaria de perder a praticidade que o VRaptor me dá para povoar o objeto que é parâmetro de entrada do método. Então, a pergunta é: uma vez que o código do meu método começou a ser executado, há como eu passar, por exemplo, o request para alguma classe do VRaptor, de forma que essa chamada caia num outro método com as propriedades de meu objeto de entrada já setadas?

Não sei se expliquei direito a dúvida. Qualquer coisa posso tentar detalhar melhor…

Lucas_Cavalcanti

vc não pode receber o objeto que vc quer que seja populado no método do controller?

lorenooliveira

Teoricamente sim. Mas, por algum motivo que não sei explicar, se o método do controller tiver algum parâmetro, eu volto a receber a exceção lá do começo da thread. Meu código só funcionou quando comecei a usar um método sem parâmetros…

Lucas_Cavalcanti

ah, isso acontece só nos uploads…

o que a gente geralmente faz (e mtos sistemas fazem) é separar o upload do resto dos dados… ou seja, vc usa um form só pro upload e o resto dos dados vc seta em outro lugar

lorenooliveira

Pois eh, eu acabei fazendo isso, mas continuei pensando a respeito e fiquei com a impressão que o processo fica “burocrático” assim. Inclusive em outro sistema que tinha feito, os casos de upload fiz em forms separados…

De qualquer maneira, separar o cadastro em dois forms me gera um problema em potencial: detectar blobs orfãos na blobstore (para o caso do formulário seguinte não ser persistido. Acho que isso é uma questão para a lista do GAE (na verdade, já postei isso lá), mas (por curiosidade) alguém aqui do forum, que desenvolve para o GAE, já se preocupou com isso?

Mr_Arthur

Olá pessoal!
Tenho um assunto que anda no mesmo caminho, então não vou criar uma nova discussão.

após chamar a url de upload gerada (_ah*) o próprio GAE me redireciona pra URL que eu informei no método createUploadUrl() certo?
Estou obtendo um 404 nessa requisição para a url de callback que informei nesse método.

03:10:54,146 DEBUG [VRaptor ] VRaptor received a new request ... Request components are ... 03:10:54,146 DEBUG [PicoComponentRegistry] There's no @SessionScoped component, so skipping session container creation 03:10:54,157 DEBUG [PicoComponentAdapter] New adapter for app.infra.CriadorDeSessaoDB 03:10:54,160 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor ResourceLookupInterceptor 03:10:54,161 DEBUG [DefaultResourceTranslator] trying to access /_ah/upload/agtjb3JyZXRvcnJtanIbCxIVX19CbG9iVXBsb2FkU2Vzc2lvbl9fGFsM Jul 18, 2011 12:10:54 AM com.google.appengine.tools.development.LocalResourceFileServlet doGet WARNING: No file found for: /fotos

Ele tenta procurar por um arquivo e não acha?
Fiz um teste básico aqui apenas com servlet. A requisição para url de callback foi feita certinho.

Valeu :slight_smile:

lorenooliveira

Isso funciona bem aqui. Cola ai o trecho de código do JSP onde vc faz o upload, e o método do seu controller que deve receber a chamada da blobstore…

Mr_Arthur

@Post("/fotos") public void create(Foto foto) { Map&lt;String, BlobKey&gt; blobs = blobService.getUploadedBlobs(request); BlobKey blobKey = blobs.get("arquivo"); }

&lt;form action="&lt;%= blobstoreService.createUploadUrl("/fotos") %&gt;&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt; <p>&lt;input type="file" name="arquivo"&gt;</p> <p>&lt;input class="buttons" type="submit" value="Salvar"/&gt;</p> &lt;/form&gt;

É isso aí.
Agora o método create() não chega nem a ser chamado.
Dá 404. Já quando eu faço uma requisição pra ele pelo form, ele é chamado normalmente.

lorenooliveira

Experimenta tirar o argumento do método create. Deixa assim:

@Post("/fotos") public void create() { Map<String, BlobKey> blobs = blobService.getUploadedBlobs(request); BlobKey blobKey = blobs.get("arquivo"); }

Mr_Arthur

hehe, na hora que postei aqui tentei esse teste.
404 do mesmo jeito :confused:

lorenooliveira

O upload está funcionando? Se vc usar a ferramenta de administração vc consegue ver seu arquivo lá no blobstore?

Mr_Arthur

Sim! toda vez que faço o upload um arquivo com o nome da chave é gerado no meu projeto, na pasta do bando de dados do GAE.

lorenooliveira

Que estranho, olhando pelo _ah/admin seu arquivo está lá?

Sua aplicação está rodando no contexto raiz mesmo (http://localhost:8888/) ou tem mais alguma coisa (http://localhost:8888/app/)?

PS: tou puxando da memória para tentar ajudar, tou sem meu código aqui por perto :slight_smile:

Mr_Arthur

Ainda não entrei no “_ah/admin” mas vou fazer isso assim que sair do horário de trabalho e pegar no meu pc denovo.
Como eu falei, o arquivo de upload é gerado. Um arquivo com a chave é gerada na pasta do datastore do gae de desenv.

minha aplicação está rodando no contexto raiz mesmo. :slight_smile:

obrigado pela disposição aí cara.

Mr_Arthur

opa, desculpe a demora.
pois é… tentei e não rolou.

vou continuar tentando aqui. obrigado.

Criado 13 de março de 2011
Ultima resposta 20 de jul. de 2011
Respostas 65
Participantes 7