Reputation: 171
I am trying to get a JAX RS resource to return a response with a JSON object. When I display the response properties via println() calls in the resource class I see that the MediaType is correctly set to "application/json", that there is an entity associated with the response with the expected type (SalesOrder), status (Status.FOUND) and that the response object is an instance of OutboundJaxrsResponse. But somehow when the browser (or a Jersey client) receives the response, these response attributes seem to be "replaced" and a HTTP 500 Internal Server error is the result. My SalesOrder class is annotated with @XmlRootElement.
My resource looks like this:
@GET
@Path("{orderUid}")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response getSalesOrder(@PathParam("orderUid") Integer orderUid) {
Response response = null;
System.out.println("Entering SalesOrderResource getSalesOrder()...");
SalesOrder retrievedSalesOrder = salesOrderService.retrieveSalesOrder(orderUid);
System.out.println("Service called and found salesOrder Uid: " + retrievedSalesOrder.getSalesOrderUid());
if (retrievedSalesOrder != null) {
System.out.println("SalesOrder found with UID: " + retrievedSalesOrder.getSalesOrderUid());
response = Response.status(Status.FOUND).entity(retrievedSalesOrder).type(MediaType.APPLICATION_JSON).build();
// The following readEntity call results in a Javax.ejb.EJBException ???
// SalesOrder fetched = response.readEntity(SalesOrder.class);
} else {
response = Response.status(Status.NOT_FOUND).header("x-reason", "Order cannot be found").build();
}
System.out.println("Response status: " + response.getStatus());
System.out.println("Response status info: " + response.getStatusInfo());
System.out.println("Response class: " + response.getClass());
System.out.println("Response length: " + response.getLength());
System.out.println("Response media type: " + response.getMediaType());
System.out.println("Response entity: " + response.getEntity());
return response;
}
...which results in the following at runtime:
2015-04-12T18:08:21.803-0600|Info: Response status: 302 2015-04-12T18:08:21.803-0600|Info: Response status info: Found 2015-04-12T18:08:21.803-0600|Info: Response class: class org.glassfish.jersey.message.internal.OutboundJaxrsResponse 2015-04-12T18:08:21.803-0600|Info: Response length: -1 2015-04-12T18:08:21.803-0600|Info: Response media type: application/xml 2015-04-12T18:08:21.803-0600|Info: Response entity: business.salesOrderMgmt.entity.SalesOrder@5e49cadd
The SalesOrder entity is defined as:
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@JsonIgnoreProperties({"subTotal", "userAccount"})
@Table(name="sales_order")
@NamedQuery(name="SalesOrder.findAll", query="SELECT s FROM SalesOrder s")
public class SalesOrder implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="SALES_ORDER_UID")
private int salesOrderUid;
@Column(name="ORDER_NUMBER")
private String orderNumber;
@Column(name="BILL_TO_CITY")
private String billToCity;
@Column(name="BILL_TO_FIRST_NAME")
private String billToFirstName;
@Column(name="BILL_TO_LAST_NAME")
private String billToLastName;
@Column(name="BILL_TO_STATE")
private String billToState;
@Column(name="BILL_TO_STREET_NAME")
private String billToStreetName;
@Column(name="BILL_TO_STREET_NUMBER")
private String billToStreetNumber;
@Column(name="BILL_TO_UNIT_NUMBER")
private String billToUnitNumber;
@Column(name="BILL_TO_ZIP_CODE")
private int billToZipCode;
@Column(name="CREDIT_CARD_CSV")
private int creditCardCsv;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="CREDIT_CARD_EXPIRATION_DATE")
private Date creditCardExpirationDate;
@Column(name="CREDIT_CARD_NUMBER")
private String creditCardNumber;
@Column(name="CREDIT_CARD_TYPE")
private String creditCardType;
@Column(name="EMAIL_ADDRESS")
private String emailAddress;
@Column(name="NAME_ON_CREDIT_CARD")
private String nameOnCreditCard;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="SALES_ORDER_DATE")
private Date salesOrderDate;
@Column(name="SALES_ORDER_STATUS")
private String salesOrderStatus;
@Column(name="SHIP_TO_CITY")
private String shipToCity;
@Column(name="SHIP_TO_STATE")
private String shipToState;
@Column(name="SHIP_TO_STREET_NAME")
private String shipToStreetName;
@Column(name="SHIP_TO_STREET_NUMBER")
private String shipToStreetNumber;
@Column(name="SHIP_TO_UNIT_NUMBER")
private String shipToUnitNumber;
@Column(name="SHIP_TO_ZIP_CODE")
private int shipToZipCode;
@Column(name="PROMO_CODE")
private String promoCode;
//Calculated and persisted for data retrieval performance
@Column(name="SUB_TOTAL")
private BigDecimal subTotal;
@Column(name="DISCOUNT")
private BigDecimal discount;
@Column(name="SALES_TAX")
private BigDecimal salesTax;
@Column(name="SHIPPING")
private BigDecimal shipping;
@Column(name="TOTAL")
private BigDecimal total;
@Version
@Column(name="LAST_UPDATED_TIME")
private Timestamp lastUpdatedTime;
//bi-directional many-to-one association to UserAccount
@ManyToOne
@JoinColumn(name="USER_ACCOUNT_UID")
private UserAccount userAccount;
@OneToMany(targetEntity=SalesOrderLine.class, mappedBy = "salesOrder", cascade = CascadeType.ALL)
private List<SalesOrderLine> lineItems;
public SalesOrder() {
}
...getters and setters
Dependencies in my POM include:
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>2.5.2</version>
</dependency>
Not only does a request (http://localhost:8080/[myapp]/resources/salesOrders/13) result in an HTTP 500 Internal Server error but when I have attempted calling response.readEntity(SalesOrder.class) within the resource the Glassfish server log show a javax.ejb.EJBException warning caused by java.lang.IllegalStateException: Method not supported on an outbound message at org.glassfish.jersey.message.internal.OutboundJaxrsResponse.readEntity(OutboundJaxrsResponse.java:145).
I think there is a problem with the JAXB marshalling of the SalesOrder object but I cannot pin down the root cause. If I simply attempt the following I still get an HTTP 500 Internal Server error as well, indicating that neither XML nor JSON marshalling is taking place but I thought this was built into the latest versions (Glassfish 4, JAX-RS 2)?
Any ideas?
Upvotes: 3
Views: 2876
Reputation: 171
I figured out the problem and will post it here for the benefit of others. 1) I added the following to the resource method: @Consumes(MediaType.TEXT_PLAIN) 2) Step #1 resulted in the following error at runtime: A cycle is detected in the object graph. This will cause infinitely deep XML 3) The error led me to add @XmlTransient to all entity fields related to other entities in OneToMany, ManyToMany and ManyToOne.
Now I am able to return a response in XML and JSON.
Upvotes: 1
Reputation: 1632
I have had similar problem. To be sure, check if your log shows something like:
SEVERE: MessageBodyWriter not found for media type=application/json, type=class SalesOrder, genericType=class SalesOrder.
If so, problem is that you did not register Jackson (or another JSON writer provider). See this section of Jersey documentation for instructions:
https://jersey.java.net/documentation/latest/media.html#d0e7857
In my case, I chose Jackson. I added following to my pom.xml.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.17</version>
</dependency>
Then I had to register the provider:
public class Application extends ResourceConfig {
public Application() {
packages(<your ws package>);
register(JacksonFeature.class);
}
}
Upvotes: 0