/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.everrest.core.impl.provider;
import org.everrest.core.impl.provider.json.JsonException;
import org.everrest.core.impl.provider.json.JsonGenerator;
import org.everrest.core.impl.provider.json.JsonParser;
import org.everrest.core.impl.provider.json.JsonTransient;
import org.everrest.core.impl.provider.json.JsonUtils;
import org.everrest.core.impl.provider.json.JsonUtils.Types;
import org.everrest.core.impl.provider.json.JsonValue;
import org.everrest.core.impl.provider.json.JsonWriter;
import org.everrest.core.impl.provider.json.ObjectBuilder;
import org.everrest.core.provider.EntityProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.activation.DataSource;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBElement;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
/**
* @author andrew00x
*/
@Provider
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public class JsonEntityProvider<T> implements EntityProvider<T> {
private static final Logger LOG = LoggerFactory.getLogger(JsonEntityProvider.class);
/** Do not process via JSON "known" JAX-RS types and some other. */
private static final Class<?>[] IGNORED = new Class<?>[]{byte[].class, char[].class, DataSource.class,
DOMSource.class, File.class, InputStream.class, OutputStream.class,
JAXBElement.class, MultivaluedMap.class,
Reader.class, Writer.class, SAXSource.class, StreamingOutput.class,
StreamSource.class, String.class};
private static boolean isSupported(Class<?> type) {
if (type.getAnnotation(JsonTransient.class) != null) {
return false;
}
for (Class<?> c : IGNORED) {
if (c.isAssignableFrom(type)) {
return false;
}
}
return true;
}
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return isSupported(type);
}
@SuppressWarnings({"unchecked"})
@Override
public T readFrom(Class<T> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException {
try {
JsonParser jsonParser = new JsonParser();
jsonParser.parse(entityStream);
JsonValue jsonValue = jsonParser.getJsonObject();
if (JsonValue.class.isAssignableFrom(type)) {
return (T)jsonValue;
}
Types jsonType = JsonUtils.getType(type);
if (jsonType == Types.ARRAY_BOOLEAN || jsonType == Types.ARRAY_BYTE || jsonType == Types.ARRAY_SHORT
|| jsonType == Types.ARRAY_INT || jsonType == Types.ARRAY_LONG || jsonType == Types.ARRAY_FLOAT
|| jsonType == Types.ARRAY_DOUBLE || jsonType == Types.ARRAY_CHAR || jsonType == Types.ARRAY_STRING
|| jsonType == Types.ARRAY_OBJECT) {
return (T)ObjectBuilder.createArray(type, jsonValue);
}
if (jsonType == Types.COLLECTION) {
return (T)ObjectBuilder.createCollection((Class)type, genericType, jsonValue);
}
if (jsonType == Types.MAP) {
return (T)ObjectBuilder.createObject((Class)type, genericType, jsonValue);
}
return ObjectBuilder.createObject(type, jsonValue);
} catch (JsonException e) {
LOG.debug(e.getMessage(), e);
throw new IOException(String.format("Can't read from input stream %s", e), e);
}
}
@Override
public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return isSupported(type);
}
@SuppressWarnings("unchecked")
@Override
public void writeTo(T t,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException {
try {
JsonValue jsonValue;
if (t instanceof JsonValue) {
jsonValue = (JsonValue)t;
} else {
Types jType = JsonUtils.getType(type);
if (jType == Types.ARRAY_BOOLEAN || jType == Types.ARRAY_BYTE || jType == Types.ARRAY_SHORT
|| jType == Types.ARRAY_INT || jType == Types.ARRAY_LONG || jType == Types.ARRAY_FLOAT
|| jType == Types.ARRAY_DOUBLE || jType == Types.ARRAY_CHAR || jType == Types.ARRAY_STRING
|| jType == Types.ARRAY_OBJECT) {
jsonValue = JsonGenerator.createJsonArray(t);
} else if (jType == Types.COLLECTION) {
jsonValue = JsonGenerator.createJsonArray((Collection<?>)t);
} else if (jType == Types.MAP) {
jsonValue = JsonGenerator.createJsonObjectFromMap((Map<String, ?>)t);
} else {
jsonValue = JsonGenerator.createJsonObject(t);
}
}
JsonWriter jsonWriter = new JsonWriter(entityStream);
jsonValue.writeTo(jsonWriter);
jsonWriter.flush();
} catch (JsonException e) {
LOG.debug(e.getMessage(), e);
throw new IOException(String.format("Can't write to output stream. %s", e.getMessage()), e);
}
}
}