Problema ao Intanciar Bean no VRaptor 3

Olá, pessoal.

Estou tendo o seguinte problema. Tenho uma entidade que possui chave composta. Segui a linha do JPA, criei uma sub-entidade pra armazenar essa chave composta. Porém o VRaptor não consegue fazer a conversão, e lança a seguinte exceção:

java.lang.IllegalArgumentException: Vraptor does not support this interface or abstract type: java.io.Serializable
	at br.com.caelum.vraptor.http.ognl.GenericNullHandler.instantiate(GenericNullHandler.java:65)
	at br.com.caelum.vraptor.http.ognl.ReflectionBasedNullHandler.nullPropertyValue(ReflectionBasedNullHandler.java:79)
	at ognl.ASTProperty.getValueBody(ASTProperty.java:118)
	at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
	at ognl.SimpleNode.getValue(SimpleNode.java:236)
	at ognl.ASTChain.setValueBody(ASTChain.java:222)
	at ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
	at ognl.SimpleNode.setValue(SimpleNode.java:279)
	at ognl.Ognl.setValue(Ognl.java:737)
	at ognl.Ognl.setValue(Ognl.java:783)[/code]
Meu código tá assim: 
[code]@Entity
public class OperationItem implements Identifiable<OperationItemPK> {

	private static final long serialVersionUID = 1L;
	
	@EmbeddedId
	private OperationItemPK id;
	
	@Version
	private Long version;

	@Column(nullable = false)
	private Integer quantity;

	//Getters e setters omitidos
}[/code]
[code]@Embeddable
public class OperationItemPK implements Serializable{

	private static final long serialVersionUID = 1L;
	
	@ManyToOne(cascade=CascadeType.ALL)
	@JoinColumn(nullable = false, name="material_id")
	private Material material;

	@ManyToOne(cascade=CascadeType.ALL)
	@JoinColumn(nullable = false, name="operation_id")
	private Operation operation;

	public OperationItemPK() {}
	
	/**
	 * @param material
	 * @param operation
	 */
	public OperationItemPK(final Material material, final Operation operation) {
		this.material = material;
		this.operation = operation;
	}

	//Getters e setters omitidos
}

A interface que minhas entidades implementam:

[code]public interface Identifiable extends Serializable {

public PK getId();

public void setId(final PK id);

}[/code]

Cheguei a fazer um converter, mas ele sequer é chamado:

[code]
@Convert(OperationItemPK.class)
@ApplicationScoped
public class OperationItemPKConverter implements Converter {

/**
 * 
 */
public OperationItemPKConverter() {
	System.out.println("OperationItemPKConverter.OperationItemPKConverter()");
}

/* (non-Javadoc)
 * @see br.com.caelum.vraptor.Converter#convert(java.lang.String, java.lang.Class, java.util.ResourceBundle)
 */
@Override
public OperationItemPK convert(String value,
		Class<? extends OperationItemPK> type, ResourceBundle bundle) {
	System.out.println(value);
	return null;
}

}[/code]

Alguma sugestão? Em último caso, vou adicionar acessores pros campos da PK diretamente na classe externa, fazendo delegação.

Atenciosamente.

Toda e qualquer chave composta DEVE ser Serializable.

Sim, eu sei, garcia-jj. E a minha é!! Observe que a minha entidade também é, pois a interface Identifiable que criei estende Serializable também. Ao que parece, o problema é que ele se perde ao tentar definir o conversor. Imagino que tenha a ver com o mecanismo de generics (type erasure), mas é estranho que com tipos “normais” (String, Long, Integer) não ocorre o problema… e ele sequer chega a chamar meu conversor. Se alguém puder dar alguma luz sobre o problema, agradeço. Se precisar de mais informações, é só pedir.

T+

Poxa, desculpe, eu li correndo já saindo da empresa. Isso o lucas pode te responder melhor.

Abraços

o que pode estar acontecendo é pq sua interface Identifiable tem getter e setter pra Serializable… (mesmo que vc tenha o tipo genérico, no final fica só Serializable…)
e o ognl tenta instanciar a interface Serializable, e claramente não consegue…

faz um teste: sobrescreva os getters e setters da interface, passando os tipos certos e teste… vê se funciona e me conta :wink:

lucascs,
Empreguei uma solução mais simples, que resolveu o problema: instanciei a chave no momento da criação da entidade. Dessa forma, o campo não era null e o VRaptor não teve que instanciá-lo.

Vou fazer o teste que você sugeriu e digo o resultado… mas é estranho que no caso de chaves simples, como Long, ele tenha sido capaz de instanciar sem problemas.

T+

o tipo Long ele sabe instanciar… o tipo Serializable não… o problema é que, por causa do generics, seu metodo recebia um serializable, que confundiu o OGNL

Então, o ponto é que nas outras classes eu também uso essa inferface, não deveria dar o mesmo problema? Por exemplo, a classe Operation ficou assim:

[code]@Entity
public class Operation implements Identifiable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

[...]

public Long getId() {
	return id;
}

public void setId(Long id) {
	this.id = id;
}

[/code]
Agora fica uma dúvida… pelo que entendo, o getId(), apesar de estar implementando o getId() da interface, na implementação ele tem como retorno o tipo genérico (Long nesse caso, OperationItemPK no outro), afinal o tipo de retorno da método implementado deve ser compatível com o da definição, mas não necessáriamente o mesmo tipo. Então o tipo de retorno do getId() era OperationItemPK, e não Serializable. Deu pra compreender? Nesse caso, a idéia de que o tipo genérico estaria “ocultando” o tipo correto cai por terra…

T+

Oi Duron!

Infelizmente pode ser o generics sim. O java 5 infelizmente enfia bridge methods dentro da classe por causa do generics:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ102

Entao voce tem dois metodos: um que recebe Long, e outro que recebe Serializable. O OGNL, creio que seja compativel com java de 19xx, deve estar pegando o metodo errado (seria facil de resolver evitando os Method que retornam true no isBridge).

Muito bom saber isso. Eu não tinha essa idéia que ele construia duas versões do método. Nessa caso a solução mais simples é continuar instanciando o objeto antecipadamente.

T+