/** * Copyright (C) 2010 Talend Inc. - www.talend.com */ package common.advanced; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlID; import javax.xml.bind.annotation.XmlIDREF; import javax.xml.bind.annotation.XmlRootElement; /** * This class can act as JAXB bean, JAX-RS sub-resource delegating to another * sub-resource and JAX-RS final resource dealing with the concrete HTTP verbs. * See getState() : this method is a JAX-RS resource method which also returns * the state of Person See getMother() and other methods having JAX-RS @Path * annotation only - these are JAX-RS sub-resource locators delegating to other * sub-resources. In this example they all delegate to the same instance to * handle the request via getState() methods. Also check updateAge() method. It * returns 'void' which will result in HTTP 204 being returned to client. This * method can also throw the exceptions which can be caught by JAX-RS * ExceptionMappers. */ @XmlRootElement(name = "Person", namespace = "http://org.persons") @XmlAccessorType(XmlAccessType.FIELD) @Produces({"application/xml", "application/json"}) @Entity public class Person { @Id private long id; private String name; private int age; @OneToOne private Person mother; @OneToOne private Person father; @OneToOne private Person partner; @OneToMany @XmlIDREF private Set<Person> children = new HashSet<Person>(); public Person() { this("unknown", 50); } public Person(String name, int age) { this.name = name; this.age = age; } public Person(String name, int age, Person m, Person f, Person p) { this.name = name; this.age = age; this.mother = m; this.father = f; this.partner = p; } @GET public Person getState() { return this; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public void setId(long id) { this.id = id; } public long getId() { return id; } public int getAge() { return age; } @Path("mother") public Person getMother() { return mother; } public void setMother(Person p) { if (mother != null && !mother.equals(p)) { throw new IllegalStateException(); } mother = p; } @Path("father") public Person getFather() { return father; } public void setFather(Person p) { if (father != null && !father.equals(p)) { throw new IllegalStateException(); } father = p; } @Path("partner") public Person getPartner() { return partner; } @GET @Path("children") public Set<Person> getChildren() { return children; } public void setChildren(Set<Person> all) { this.children = all; } public void addChild(Person child) { children.add(child); } @GET @Path("descendants") public Set<Person> getDescendants() { Set<Person> ds = new HashSet<Person>(); addChildren(ds, this); return ds; } @GET @Path("ancestors") public Set<Person> getAncestors() { Set<Person> as = new HashSet<Person>(); addParents(as, this); return as; } @PUT @Consumes("text/plain") @Path("age") public void updateAge(int newAge) throws PersonUpdateException { if (age > newAge) { throw new PersonUpdateException(); } setAge(newAge); } private void addParents(Set<Person> list, Person p) { Person m = p.getMother(); if (m != null) { list.add(m); addParents(list, m); } Person f = p.getFather(); if (f != null) { list.add(f); addParents(list, f); } } private void addChildren(Set<Person> list, Person p) { for (Person ch : p.getChildren()) { list.add(ch); addChildren(list, ch); } } @Override public int hashCode() { return name.hashCode() + age; } @Override public boolean equals(Object o) { if (o instanceof Person) { Person other = (Person)o; return name.equals(other.name) && age == other.age; } else { return false; } } // The following is the hack recommended to bypass JAXB RI limitation // to do with XmlId value being strictly of type 'String' @SuppressWarnings("unused") @XmlID @XmlAttribute(name = "id") private String getXmlID(){ return String.format("%s-%s", this.getClass().getSimpleName(), Long.valueOf(id)); } @SuppressWarnings("unused") private void setXmlID(String xmlid){ String prefix = String.format("%s-", this.getClass().getSimpleName()); if(xmlid.startsWith(prefix)){ this.id = Long.parseLong(xmlid.substring(prefix.length())); }else{ throw new IllegalArgumentException(xmlid+" does not look like "+prefix+"###"); } } }