/******************************************************************************* | |
* Copyright (c) 2011, 2013 Oracle and/or its affiliates. All rights reserved. | |
* This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 | |
* which accompanies this distribution. | |
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | |
* and the Eclipse Distribution License is available at | |
* http://www.eclipse.org/org/documents/edl-v10.php. | |
* | |
* Contributors: | |
* dclarke/tware - initial | |
* tware | |
******************************************************************************/ | |
package org.eclipse.persistence.jpa.rs.util; | |
import java.beans.PropertyChangeListener; | |
import java.io.ByteArrayOutputStream; | |
import java.io.IOException; | |
import java.io.ObjectOutputStream; | |
import java.io.OutputStream; | |
import java.io.OutputStreamWriter; | |
import java.util.List; | |
import javax.ws.rs.WebApplicationException; | |
import javax.ws.rs.core.HttpHeaders; | |
import javax.ws.rs.core.MediaType; | |
import javax.ws.rs.core.StreamingOutput; | |
import javax.xml.bind.JAXBContext; | |
import javax.xml.bind.JAXBException; | |
import javax.xml.bind.Marshaller; | |
import org.eclipse.persistence.dynamic.DynamicEntity; | |
import org.eclipse.persistence.internal.dynamic.DynamicEntityImpl; | |
import org.eclipse.persistence.jaxb.MarshallerProperties; | |
import org.eclipse.persistence.jpa.rs.PersistenceContext; | |
import org.eclipse.persistence.jpa.rs.exceptions.UnsupportedMediaTypeException; | |
import org.eclipse.persistence.jpa.rs.util.list.MultiResultQueryList; | |
import org.eclipse.persistence.jpa.rs.util.xmladapters.LinkAdapter; | |
/** | |
* Simple {@link StreamingOutput} implementation that uses the provided | |
* {@link JAXBContext} to marshal the result when requested to either XML or | |
* JSON based on the accept media provided. | |
* | |
* @author dclarke | |
* @since EclipseLink 2.4.0 | |
*/ | |
public class StreamingOutputMarshaller implements StreamingOutput { | |
public static final String NO_ROUTE_JAXB_ELEMENT_LABEL = "result"; | |
private PersistenceContext context; | |
private Object result; | |
private MediaType mediaType; | |
public StreamingOutputMarshaller(PersistenceContext context, Object result, MediaType acceptedType) { | |
this.context = context; | |
this.result = result; | |
this.mediaType = acceptedType; | |
} | |
public StreamingOutputMarshaller(PersistenceContext context, Object result, List<MediaType> acceptedTypes) { | |
this(context, result, mediaType(acceptedTypes)); | |
} | |
@Override | |
public void write(OutputStream output) throws IOException, WebApplicationException { | |
if (result instanceof byte[] && this.mediaType.equals(MediaType.APPLICATION_OCTET_STREAM_TYPE)) { | |
output.write((byte[])result); | |
output.flush(); | |
output.close(); | |
} else if (result instanceof String){ | |
OutputStreamWriter writer = new OutputStreamWriter(output); | |
writer.write((String)result); | |
writer.flush(); | |
writer.close(); | |
} else { | |
if ((this.context != null && this.context.getJAXBContext() != null && this.result != null) && | |
(this.mediaType.equals(MediaType.APPLICATION_JSON_TYPE) || this.mediaType.equals(MediaType.APPLICATION_XML_TYPE))) { | |
try { | |
if (result instanceof MultiResultQueryList) { | |
if (mediaType == MediaType.APPLICATION_JSON_TYPE) { | |
// avoid outer QueryResultList class (outer grouping name) in JSON responses | |
context.marshallEntity(((MultiResultQueryList) result).getItems(), mediaType, output); | |
} else { | |
context.marshallEntity(result, mediaType, output); | |
} | |
} else { | |
context.marshallEntity(result, mediaType, output); | |
} | |
return; | |
} catch (Exception e) { | |
JPARSLogger.exception("jpars_caught_exception", new Object[]{}, e); | |
throw new WebApplicationException(); | |
} | |
} | |
if (this.mediaType.equals(MediaType.APPLICATION_OCTET_STREAM_TYPE)){ | |
// could not marshall, try serializing | |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
ObjectOutputStream oos = new ObjectOutputStream(baos); | |
oos.writeObject(result); | |
oos.flush(); | |
oos.close(); | |
output.write(baos.toByteArray()); | |
} else { | |
JPARSLogger.fine("jpars_could_marshal_requested_result_to_requested_type", new Object[]{result}); | |
throw new WebApplicationException(); | |
} | |
} | |
} | |
/** | |
* Identify the preferred {@link MediaType} from the list provided. This | |
* will check for JSON string or {@link MediaType} first then XML. | |
* | |
* @param types | |
* List of {@link MediaType} values; | |
* @return selected {@link MediaType} | |
* @throws WebApplicationException | |
* with Status.UNSUPPORTED_MEDIA_TYPE if neither the JSON, XML | |
* or OCTET_STREAM values are found. | |
*/ | |
public static MediaType mediaType(List<MediaType> types) { | |
MediaType aMediaType = null; | |
if ((types != null) && (!types.isEmpty())) { | |
JPARSLogger.fine("jpars_requested_type", new Object[] { types }); | |
for (int i = 0; i < types.size(); i++) { | |
aMediaType = types.get(i); | |
if (aMediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE)) { | |
return MediaType.APPLICATION_JSON_TYPE; | |
} | |
if (aMediaType.isCompatible(MediaType.APPLICATION_XML_TYPE)) { | |
return MediaType.APPLICATION_XML_TYPE; | |
} | |
if (aMediaType.isCompatible(MediaType.APPLICATION_OCTET_STREAM_TYPE)) { | |
return MediaType.APPLICATION_OCTET_STREAM_TYPE; | |
} | |
} | |
} | |
throw new UnsupportedMediaTypeException((aMediaType != null) ? aMediaType.toString() : "unknown"); | |
} | |
public static Marshaller createMarshaller(PersistenceContext context, MediaType mediaType) throws JAXBException{ | |
Marshaller marshaller = context.getJAXBContext().createMarshaller(); | |
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, mediaType.toString()); | |
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false); | |
marshaller.setAdapter(new LinkAdapter(context.getBaseURI().toString(), context)); | |
marshaller.setListener(new Marshaller.Listener() { | |
@Override | |
public void beforeMarshal(Object source) { | |
if (source instanceof DynamicEntity){ | |
DynamicEntityImpl sourceImpl = (DynamicEntityImpl)source; | |
PropertyChangeListener listener = sourceImpl._persistence_getPropertyChangeListener(); | |
sourceImpl._persistence_setPropertyChangeListener(null); | |
((DynamicEntity)source).set("self", source); | |
sourceImpl._persistence_setPropertyChangeListener(listener); | |
} | |
} | |
}); | |
return marshaller; | |
} | |
public static MediaType getResponseMediaType(HttpHeaders headers) { | |
MediaType mediaType = MediaType.TEXT_PLAIN_TYPE; | |
if (headers != null) { | |
List<MediaType> accepts = headers.getAcceptableMediaTypes(); | |
if (accepts != null && accepts.size() > 0) { | |
try { | |
mediaType = StreamingOutputMarshaller.mediaType(accepts); | |
} catch (Exception ex) { | |
} | |
} | |
} | |
return mediaType; | |
} | |
} |