Glassfish XML (or JSON) Response for JAX-RS Object Marshalling Results in HTTP 500 Internal Server Error

不羁的心 提交于 2020-01-24 16:32:05

问题


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?


回答1:


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.




回答2:


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);
    }
}


来源:https://stackoverflow.com/questions/29596388/glassfish-xml-or-json-response-for-jax-rs-object-marshalling-results-in-http-5

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!