/**
* Copyright (C) 2013 cherimojava (http://github.com/cherimojava/cherimodata/spring) Licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in
* writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.cherimojava.data.spring;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import com.github.cherimojava.data.mongo.entity.Entity;
import com.github.cherimojava.data.mongo.entity.EntityFactory;
import com.google.common.base.Charsets;
/**
* Converts a JSON HTTPMessage to and from Entity. To enable this converter you need to add it to your
* {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter}. This can happen through
* overriding the appropriate {@link org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport}
* methods like
* {@link org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#configureMessageConverters} or
* {@link org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#addDefaultHttpMessageConverters}
*
* @author philnate
* @since 1.0.0
*/
public class EntityConverter
extends AbstractHttpMessageConverter<Object>
implements GenericHttpMessageConverter<Object>
{
private final EntityFactory factory;
/**
* creates a new EntityConverter which utilizes the given @{link
* com.github.cherimojava.data.mongo.entity.EntityFactory}
*
* @param factory to be used to convert from/to HTTPMessage/Entity
*/
public EntityConverter( EntityFactory factory )
{
super( MediaType.APPLICATION_JSON );
this.factory = factory;
}
/**
* Checks if the given class can be handled by this Converter or not. Returns true for all
* {@link com.github.cherimojava.data.mongo.entity.Entity} based classes, false otherwise
*
* @param clazz to check if it's supported
* @return true if the given class can be assigned to Entity, false otherwise
*/
@Override
protected boolean supports( Class<?> clazz )
{
return Entity.class.isAssignableFrom( clazz );
}
@Override
protected Object readInternal( Class<?> clazz, HttpInputMessage inputMessage )
throws IOException, HttpMessageNotReadableException
{
return fromJson( (Class<? extends Entity>) clazz, inputMessage );
}
@Override
public Object read( Type type, Class<?> contextClass, HttpInputMessage inputMessage )
throws IOException, HttpMessageNotReadableException
{
// as this method is only called after we decided that we can decode the requested type, we only need to check
// what we have (plain entity/list of entities)
if ( type instanceof Class )
{
// simple class
return factory.readEntity( (Class<? extends Entity>) type, IOUtils.toString( inputMessage.getBody() ) );
}
else
{
// collection
return factory.readList( (Class<? extends Entity>) ( (ParameterizedType) type ).getActualTypeArguments()[0],
IOUtils.toString( inputMessage.getBody() ) );
}
}
private Entity fromJson( Class<? extends Entity> clazz, HttpInputMessage inputMessage )
throws IOException
{
return factory.readEntity( clazz, IOUtils.toString( inputMessage.getBody(), Charsets.UTF_8.name() ) );
}
@Override
protected void writeInternal( Object o, HttpOutputMessage outputMessage )
throws IOException, HttpMessageNotWritableException
{
try (OutputStreamWriter osw = new OutputStreamWriter( outputMessage.getBody() ))
{
osw.write( o.toString() );
}
}
@Override
public boolean canRead( Type type, Class<?> contextClass, MediaType mediaType )
{
if ( MediaType.APPLICATION_JSON.equals( mediaType ) )
{
if ( type instanceof Class )
{
// check if this is a simple entity class
return Entity.class.isAssignableFrom( (Class) type );
}
if ( type instanceof ParameterizedType )
{
// is this a parameterized type
ParameterizedType pt = (ParameterizedType) type;
if ( pt.getRawType() instanceof Class && Collection.class.isAssignableFrom( (Class) pt.getRawType() ) )
{
// is this rawtype a class and is this class some collection
Type generic = pt.getActualTypeArguments()[0];
if ( generic instanceof Class && Entity.class.isAssignableFrom( (Class) generic ) )
{
// is this collection generic an entity
return true;
}
}
}
}
return false;
}
@Override
public boolean canWrite( Class<?> clazz, MediaType mediaType )
{
// this is rather ugly, would be great if we could get hold of the Type rather than class, so that we can check
// for the generic type and decide upon this
return MediaType.APPLICATION_JSON.equals( mediaType )
&& ( Entity.class.isAssignableFrom( clazz ) || Collection.class.isAssignableFrom( clazz ) );
}
}