Friday, September 17, 2010

Force JPA load Identity ID after Insert

When you have an ID into your Entity Classe from Database and that is Identity, if you do an insert action the ID of this Entity isn't reloaded by default with the ID that was generated by Database, through JPA.

A sample of an ID of Identity kind:

@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "Id")
private Integer id;


To refresh the ID value with the ID that was generated by the Database on insert action, you need put more this below in your AbstractFacade.Create(EntityClass):

getEntityManager().flush();
getEntityManager().refresh(entity);


  • getEntityManager().flush() - Will do the commit to Database execute the insert action.

  • getEntityManager().refresh(entity) - Will refresh the entity object with a new ID that was inserted.


Full sample of the AbstractFacade.java:

package org.test.ejb;

import java.util.List;
import javax.persistence.EntityManager;

public abstract class AbstractFacade {
private Class entityClass;

public AbstractFacade(Class entityClass) {
this.entityClass = entityClass;
}

protected abstract EntityManager getEntityManager();

public void create(T entity) {
getEntityManager().persist(entity);
getEntityManager().flush();
getEntityManager().refresh(entity);
}

public void edit(T entity) {
getEntityManager().merge(entity);
}

public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}

public T find(Object id) {
return getEntityManager().find(entityClass, id);
}

public List findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}

public List findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0]);
q.setFirstResult(range[0]);
return q.getResultList();
}

public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}


If you publish your JPA Create action in a Web Services, you will need return to client side an Entity instance to client know what is the new ID. Cause the Entity instance of the Client side can't be refresh with changes inside Server.
Like:

@WebMethod(operationName = "create")
public BusinessEntity create(@WebParam(name = "businessEntity")
BusinessEntity businessEntity) {
ejbRef.create(businessEntity);
return businessEntity;
}


Wednesday, September 15, 2010

Web Service - A cycle is detected in the object graph. This will cause infinitely deep XML...

When a Web Service that uses an EJB that exposes an Entity Class and this Entity Class makes relationships with another Entity Class, may be you will receives this error:

Caused by: javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: org.test.data.Business[id=1] -> org.test.data.BusinessType[id=1] -> org.test.data.Business[id=1]]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:269)
at com.sun.xml.bind.v2.runtime.BridgeImpl.marshal(BridgeImpl.java:100)
at com.sun.xml.bind.api.Bridge.marshal(Bridge.java:141)
at com.sun.xml.ws.message.jaxb.JAXBMessage.writePayloadTo(JAXBMessage.java:317)
... 35 more
Caused by: com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: org.test.data.Business[id=1] -> org.test.data.BusinessType[id=1] -> org.test.data.Business[id=1]
at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:248)
at com.sun.xml.bind.v2.runtime.XMLSerializer.pushObject(XMLSerializer.java:537)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:631)
at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:65)
at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:168)
at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:155)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696)
at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:152)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696)
at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:65)
at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:168)
at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:155)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:264)
... 38 more



To this error above you can found solutions saying to use this interface:
  • com.sun.xml.internal.bind.CycleRecoverable


NetBeans recognize this package and not mark like an error. But on compile time this package does not exist to compiler side. Perhaps you will receive this error when compiles:


/home/eduveks/project/wstest/src/org/test/data/Business.java:8: package com.sun.xml.internal.bind does not exist
import com.sun.xml.internal.bind.CycleRecoverable;


On my view this error is a bug of NetBeans that recognizes this package when isn't a valid package to Java compiler. To test this bug you only need put this "import com.sun.xml.internal.bind.CycleRecoverable;" into any Java file and you will see that NetBeans don't says nothing and accept as well, but if you "Clean and Build" the error will show up.

The correct package is (without .internal):


import com.sun.xml.bind.CycleRecoverable;


To uses that package path above you need add it:

Project Properties -> Libraries -> "Add Library...", select JAXB 2.2 and "Add Library".

A sample how to implements the com.sun.xml.bind.CycleRecoverable:

    > Business.java

package org.test.data;

import com.sun.xml.bind.CycleRecoverable;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.test.data.adapter.IntegerAdapter;

@Entity
@Table(name = "Business")
@NamedQueries({
@NamedQuery(name = "Business.findAll", query = "SELECT b FROM Business b"),
@NamedQuery(name = "Business.findById", query = "SELECT b FROM Business b WHERE b.id = :id"),
@NamedQuery(name = "Business.findByNome", query = "SELECT b FROM Business b WHERE b.name = :name"),
public class Business implements Serializable, CycleRecoverable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "Id")
private Integer id;
@Basic(optional = false)
@Column(name = "Name")
private String name;
@JoinColumn(name = "BusinessTypeId", referencedColumnName = "Id")
@ManyToOne(optional = false)
private BusinessType businessType;

public Business() {
}

public Business(Integer id) {
this.id = id;
}

public Business(Integer id, String name) {
this.id = id;
this.name = name;
}

@XmlID
@XmlJavaTypeAdapter(value = IntegerAdapter.class, type = String.class)
public Integer getId() {
return id;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@XmlIDREF
public BusinessType getBusinessType() {
return businessType;
}

public void setBusinessType(BusinessType businessType) {
this.businessType = businessType;
}

@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Business)) {
return false;
}
Business other = (Business) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}

@Override
public String toString() {
return "org.test.data.Business[id=" + id + "]";
}

@Override
public Object onCycleDetected(Context cntxt) {
System.out.println("CycleRecoverable.onCycleDetected # ".concat(this.toString()));
Business n = new Business();
n.setId(this.getId());
return n;
}
}


    > BusinessType.java

package org.test.data;

import com.sun.xml.bind.CycleRecoverable;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.test.data.adapter.IntegerAdapter;

@Entity
@Table(name = "BusinessType")
@NamedQueries({
@NamedQuery(name = "BusinessType.findAll", query = "SELECT bt FROM BusinessType bt"),
@NamedQuery(name = "BusinessType.findById", query = "SELECT bt FROM BusinessType bt WHERE bt.id = :id"),
@NamedQuery(name = "BusinessType.findByName", query = "SELECT bt FROM BusinessType bt WHERE bt.name = :name")})
public class BusinessType implements Serializable, CycleRecoverable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "Id")
private Integer id;
@Basic(optional = false)
@Column(name = "Name")
private String name;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "BusinessType")
private Collection businessCollection;

public BusinessType() {
}

public BusinessType(Integer id) {
this.id = id;
}

public BusinessType(Integer id, String name) {
this.id = id;
this.name = name;
}

@XmlID
@XmlJavaTypeAdapter(value = IntegerAdapter.class, type = String.class)
public Integer getId() {
return id;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@XmlIDREF
public Collection getBusinessCollection() {
return businessCollection;
}

public void setBusinessCollection(Collection businessCollection) {
this.businessCollection = businessCollection;
}

@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof BusinessType)) {
return false;
}
BusinessType other = (BusinessType) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}

@Override
public String toString() {
return "org.test.data.BusinessType[id=" + id + "]";
}

@Override
public Object onCycleDetected(Context cntxt) {
System.out.println("CycleRecoverable.onCycleDetected # ".concat(this.toString()));
BusinessType n = new BusinessType();
n.setId(this.getId());
return n;
}
}


You need mark the IDs with @XmlID(only before the get method, before the variable declaration don't works):

@XmlID
@XmlJavaTypeAdapter(value = IntegerAdapter.class, type = String.class)
public Integer getId() {
return id;
}

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


And mark all Collection relationships methods with @XmlIDREF(only before the get method, before the variable declaration don't works):

    > BusinessType.java

@XmlIDREF
public Collection getBusinessCollection() {
return businessCollection;
}

public void setBusinessCollection(Collection businessCollection) {
this.businessCollection = businessCollection;
}


Sample of the IntegerAdapter.java:

package org.test.data.adapter;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class IntegerAdapter extends XmlAdapter {

@Override
public Integer unmarshal(String v) {
return Integer.parseInt(v);
}

@Override
public String marshal(Integer v) {
return v.toString();
}
}



Thursday, July 29, 2010

Automate Command-line Programs

I had to automate a command-line program. Then I did this script like a sample that do auto control to vi.

This way is possible control any kind of command-line program.

#!/bin/python

import subprocess
import time
import threading

class ProcessRunner(threading.Thread):
def run(self):
self.process = subprocess.Popen(["vi /tmp/xpto > /tmp/viOutput"], shell = True, bufsize = 1000, stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.STDOUT)

pr = ProcessRunner()
pr.start()

time.sleep(1)

pr.process.stdin.write("i\n")
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write("Content xpto... :P\n")
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write(chr(27))
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write(":wq\n")
pr.process.stdin.flush()


If is necessary read some content from program output, you can put the output in a temp file, in this case goes to /tmp/viOutput. See with attention the line of the subprocess.Popen. Another way is use the pr.process.stdout but it dont work fine for me.

The command pr.process.stdin.write(chr(27)) write the "character" ESC, like the keyboard key.

Thursday, February 4, 2010

NC (NetCast) vs Printers (Impressoras)

Sobrou para mim configurar as novas impressoras Samsung SCX-6345N no Solaris, e como é óbvio e uma grande pena não tem drivers, ou pelo menos que funcione bem, pois apesar de haver um driver PPD supostamente para o LP em Solaris, não funcionou nem a pau.

Fiz tudo o possível para fazer funcionar estas impressoras em Solaris via LP, pois era uma situação de urgência fazer o sistema da Reuters (Kondor) utilizar as novas impressoras.

Após muitas tentativas e alguns e-mails trocados com o suporte da Samsung, e até um programa da Samsung para instalar a impressora em modo gráfico (X11) que "configuraria automaticamente" o LP, também não funcionou. E após também de muitas folhas de erros e cabeçalho de jobs terem sido impressos sem nunca sair o conteúdo desejado. Após tudo isto e um pouco mais... o suporte da Samsung recomendou eu usar o comando:

# cat testprint.ps | nc -w 2 192.168.193.191 9100


Fiquei sético, mas como não havia muito mais a fazer, lá fui eu experimentar isto, e para a minha surpresa... não é que funcionou!

Por acaso eu já tinha pensado nisto, em abrir uma conexão com a impressora direto e mandar o conteúdo PostScript e ver se ela imprimia ou se ficava maluca. Mas não sabia que existia um comando simples para isto.

O comando NC (NetCast) não vem no Solaris 10, por isso fui ao Sunfreeware.com pegar o pacote:

http://www.sunfreeware.com/programlistsparc10.html#nc

Depois é só instalar o pacote:

#pkgadd -d nc-110-sol10-sparc-local.gz


Na configuração da impressora no Kondor (Reuters) ficou apenas assim:

nc -w 2 192.168.193.191 9100


Portanto é executar o nc passando o IP e a PORTA da impressora. O -w 2 apenas quer dizer que tem o timeout de 2 segundos.

A desvantagem desta solução é que perde-se o controle das impressoras com o LP, ou seja, para o Solaris é como se elas não existissem. Então a administração LP e a configuração das impressoras no /etc/lp e os comandos ("lpstat -a", "enable PRINTER" e "disable PRINTER"), tudo isto passa a servir para nada! :D

Como para nos a solução do NC (NetCast) é suficiente, passamos a trabalhar assim.

NC (NetCast)

netcat is a simple unix utility which reads and writes data across network connections, using TCP or UDP protocol. It is designed to be a reliable "back-end" tool that can be used directly or easily driven by other programs and scripts. At the same time, it is a feature-rich network debugging and exploration tool, since it can create almost any kind of connection you would need and has several interesting built-in capabilities. Netcat, or "nc" as the actual program is named, should have been supplied long ago as another one of those cryptic but standard Unix tools.

http://www.computerhope.com/unix/nc.htm

Esta solução de imprimir diretamente para uma impressora de rede usando o NC (NetCast) também é válida para Linux e Unix em geral.