package org.frameworkset.http.converter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.MarshalException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.frameworkset.http.HttpHeaders;
import org.frameworkset.http.HttpInputMessage;
import org.frameworkset.http.HttpOutputMessage;
import org.frameworkset.http.MediaType;
import org.frameworkset.util.Assert;
import org.frameworkset.util.ClassUtils;
import org.frameworkset.util.FileCopyUtils;
import org.frameworkset.util.annotations.ValueConstants;
import com.thoughtworks.xstream.XStream;
public class XMLHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private final ConcurrentMap<Class, JAXBContext> jaxbContexts = new ConcurrentHashMap<Class, JAXBContext>();
public static Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private final List<Charset> availableCharsets;
private boolean writeAcceptCharset = true;
public XMLHttpMessageConverter() {
super(xmlmediatypes);
this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values());
}
/**
* Indicates whether the {@code Accept-Charset} should be written to any outgoing request.
* <p>Default is {@code true}.
*/
public void setWriteAcceptCharset(boolean writeAcceptCharset) {
this.writeAcceptCharset = writeAcceptCharset;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws IOException {
try {
Unmarshaller unmarshaller = createUnmarshaller(clazz);
if (clazz.isAnnotationPresent(XmlRootElement.class)) {
return unmarshaller.unmarshal(source);
}
else {
JAXBElement jaxbElement = unmarshaller.unmarshal(source, clazz);
return jaxbElement.getValue();
}
}
catch (UnmarshalException ex) {
throw new HttpMessageNotReadableException("Could not unmarshal to [" + clazz + "]: " + ex.getMessage(), ex);
}
catch (JAXBException ex) {
throw new HttpMessageConversionException("Could not instantiate JAXBContext: " + ex.getMessage(), ex);
}
}
// @Override
// protected String readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException {
// MediaType contentType = inputMessage.getHeaders().getContentType();
// Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
// return FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), charset));
// }
@Override
public final Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException {
if(String.class.isAssignableFrom(clazz))
{
MediaType contentType = inputMessage.getHeaders().getContentType();
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
return FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), charset));
}
else
{
if (clazz.isAnnotationPresent(XmlRootElement.class)) {
return readFromSource(clazz, inputMessage.getHeaders(), new StreamSource(inputMessage.getBody()));
}
else
{
XStream xStream = new XStream();
MediaType contentType = inputMessage.getHeaders().getContentType();
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
return xStream.fromXML(new InputStreamReader(inputMessage.getBody(), charset));
}
}
}
public boolean canWrite(MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equals(mediaType)) {
return false;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
/**
* Returns true if any of the {@linkplain #setSupportedMediaTypes(List) supported media types}
* include the given media type.
* @param mediaType the media type to read, can be {@code null} if not specified. Typically the value of a
* {@code Content-Type} header.
* @return true if the supported media types include the media type, or if the media type is {@code null}
*/
public boolean canRead(MediaType mediaType) {
if (mediaType == null) {
return false;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
private void convertToXML(Object s,HttpOutputMessage outputMessage) throws IOException
{
if(s == null)
;
else if(s instanceof String)
{
MediaType contentType = outputMessage.getHeaders().getContentType();
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
FileCopyUtils.copy((String)s, new OutputStreamWriter(outputMessage.getBody(), charset));
}
else
{
try {
Class clazz = ClassUtils.getUserClass(s);
if (clazz.isAnnotationPresent(XmlRootElement.class)) {
Marshaller marshaller = createMarshaller(clazz);
try {
setCharset(outputMessage.getHeaders().getContentType(), marshaller);
marshaller.marshal(s, new StreamResult(outputMessage.getBody()));
} catch (MarshalException ex) {
throw new HttpMessageNotWritableException("Could not marshal [" + s + "]: " + ex.getMessage(), ex);
}
catch (JAXBException ex) {
throw new HttpMessageConversionException("Could not instantiate JAXBContext: " + ex.getMessage(), ex);
}
}
else
{
XStream xStream = new XStream();
MediaType contentType = outputMessage.getHeaders().getContentType();
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
xStream.toXML(s,new OutputStreamWriter(outputMessage.getBody(), charset));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public Long getContentLength(Object s, MediaType contentType) {
if(s instanceof String)
{
if (contentType != null && contentType.getCharSet() != null) {
Charset charset = contentType.getCharSet();
try {
return (long) ((String)s).getBytes(charset.name()).length;
}
catch (UnsupportedEncodingException ex) {
// should not occur
throw new InternalError(ex.getMessage());
}
}
else
return null;
}
else {
return null;
}
}
@Override
public void writeInternal(Object s, HttpOutputMessage outputMessage,HttpInputMessage inputMessage) throws IOException {
if (writeAcceptCharset) {
outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
}
convertToXML(s,outputMessage);
// if(str != null)
// {
// MediaType contentType = outputMessage.getHeaders().getContentType();
// Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
// FileCopyUtils.copy(str, new OutputStreamWriter(outputMessage.getBody(), charset));
// }
// FileCopyUtils.copy(s, new OutputStreamWriter(outputMessage.getBody(), charset));
}
/**
* Return the list of supported {@link Charset}.
*
* <p>By default, returns {@link Charset#availableCharsets()}. Can be overridden in subclasses.
*
* @return the list of accepted charsets
*/
protected List<Charset> getAcceptedCharsets() {
return this.availableCharsets;
}
public void setResponseCharset(String charset) {
super.responsecontenteype = new MediaType("application","xml",Charset.forName(charset));
}
/**
* Creates a new {@link Marshaller} for the given class.
*
* @param clazz the class to create the marshaller for
* @return the {@code Marshaller}
* @throws HttpMessageConversionException in case of JAXB errors
*/
protected final Marshaller createMarshaller(Class clazz) {
try {
JAXBContext jaxbContext = getJaxbContext(clazz);
return jaxbContext.createMarshaller();
}
catch (JAXBException ex) {
throw new HttpMessageConversionException(
"Could not create Marshaller for class [" + clazz + "]: " + ex.getMessage(), ex);
}
}
/**
* Creates a new {@link Unmarshaller} for the given class.
*
* @param clazz the class to create the unmarshaller for
* @return the {@code Unmarshaller}
* @throws HttpMessageConversionException in case of JAXB errors
*/
protected final Unmarshaller createUnmarshaller(Class clazz) throws JAXBException {
try {
JAXBContext jaxbContext = getJaxbContext(clazz);
return jaxbContext.createUnmarshaller();
}
catch (JAXBException ex) {
throw new HttpMessageConversionException(
"Could not create Unmarshaller for class [" + clazz + "]: " + ex.getMessage(), ex);
}
}
/**
* Returns a {@link JAXBContext} for the given class.
*
* @param clazz the class to return the context for
* @return the {@code JAXBContext}
* @throws HttpMessageConversionException in case of JAXB errors
*/
protected final JAXBContext getJaxbContext(Class clazz) {
Assert.notNull(clazz, "'clazz' must not be null");
JAXBContext jaxbContext = jaxbContexts.get(clazz);
if (jaxbContext == null) {
try {
jaxbContext = JAXBContext.newInstance(clazz);
jaxbContexts.putIfAbsent(clazz, jaxbContext);
}
catch (JAXBException ex) {
throw new HttpMessageConversionException(
"Could not instantiate JAXBContext for class [" + clazz + "]: " + ex.getMessage(), ex);
}
}
return jaxbContext;
}
protected boolean writeToResult(Object o, HttpHeaders headers, Result result) throws IOException {
try {
Class clazz = ClassUtils.getUserClass(o);
if (clazz.isAnnotationPresent(XmlRootElement.class)) {
Marshaller marshaller = createMarshaller(clazz);
setCharset(headers.getContentType(), marshaller);
marshaller.marshal(o, result);
return true;
}
else
{
return false;
}
}
catch (MarshalException ex) {
throw new HttpMessageNotWritableException("Could not marshal [" + o + "]: " + ex.getMessage(), ex);
}
catch (JAXBException ex) {
throw new HttpMessageConversionException("Could not instantiate JAXBContext: " + ex.getMessage(), ex);
}
}
private void setCharset(MediaType contentType, Marshaller marshaller) throws PropertyException {
if (contentType != null && contentType.getCharSet() != null) {
marshaller.setProperty(Marshaller.JAXB_ENCODING, contentType.getCharSet().name());
}
}
@Override
public String getRequetBodyDataType() {
return ValueConstants.datatype_xml;
}
/**
* 获取用户响应报文对应的数据类型:String,json,xml
* @return
*/
public String getResponseBodyDataType()
{
return ValueConstants.datatype_xml;
}
public boolean canWrite(String datatype) {
// TODO Auto-generated method stub
if(datatype == null)
return false;
if(datatype.equals(ValueConstants.datatype_xml))
return true;
else
return false;
}
}