/*
* Copyright 1999-2009 University of Chicago
*
* 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 org.nimbustools.messaging.query;
import org.apache.axis.description.TypeDesc;
import org.apache.axis.message.MessageElement;
import org.apache.axis.encoding.SerializationContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.jaxrs.ext.MessageContext;
import org.xml.sax.Attributes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;
@Provider
@Produces({"text/xml"})
public class AxisBodyWriter implements MessageBodyWriter<Object> {
private static final Log logger =
LogFactory.getLog(AxisBodyWriter.class.getName());
private static final String GET_TYPE_DESC = "getTypeDesc";
private static final String TYPE_SUFFIX = "Type";
@Context
MessageContext messageContext;
// this is a travesty
public boolean isWriteable(Class<?> aClass, Type type,
Annotation[] annotations, MediaType mediaType) {
return getTypeDescVerySlowly(aClass) != null;
}
public long getSize(Object o, Class aClass, Type type,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
public void writeTo(Object o, Class aClass, Type type,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap multivaluedMap,
OutputStream outputStream)
throws IOException, WebApplicationException {
final TypeDesc typeDesc = getTypeDescVerySlowly(aClass);
final QName qName = typeDesc.getXmlType();
if (qName == null) {
throw new WebApplicationException(500);
}
String name = qName.getLocalPart();
// responses should not have 'Type' on end of element name. ugly.
if (name.endsWith(TYPE_SUFFIX)) {
name = name.substring(0, name.length() - TYPE_SUFFIX.length());
}
String namespace = getNamespaceUriFromContext();
if (namespace == null) {
namespace = qName.getNamespaceURI();
}
final OutputStreamWriter writer =
new OutputStreamWriter(outputStream);
//manually write the xml header deal
writer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
MessageElement element = new MessageElement(name, "", namespace);
try {
element.setObjectValue(o);
final SerializationContext context =
new CleanSerializationContext(writer, namespace);
context.setDoMultiRefs(false);
context.setPretty(true);
element.output(context);
} catch (SOAPException e) {
throw new WebApplicationException(e);
} catch (Exception e) {
throw new WebApplicationException(e);
}
writer.close();
}
private String getNamespaceUriFromContext() {
if (this.messageContext != null) {
final HttpHeaders headers = this.messageContext.getHttpHeaders();
if (headers != null) {
final List<String> versionHeaders =
headers.getRequestHeader(ElasticQuery.API_VERSION_HEADER);
if (versionHeaders != null && !versionHeaders.isEmpty()) {
final String version = versionHeaders.get(0);
if (version.length() > 0) {
return "http://ec2.amazonaws.com/doc/" + version + "/";
}
}
}
}
return null;
}
private static TypeDesc getTypeDescVerySlowly(Class aClass) {
try {
final Method method = aClass.getMethod(GET_TYPE_DESC);
return (TypeDesc) method.invoke(null);
} catch (IllegalAccessException e) {
return null;
} catch (NoSuchMethodException e) {
return null;
} catch (InvocationTargetException e) {
return null;
} catch (ClassCastException e) {
return null;
}
}
}
class CleanSerializationContext extends SerializationContext {
private final String defaultNamespace;
public CleanSerializationContext(Writer writer, String defaultNamespace) {
super(writer);
this.defaultNamespace = defaultNamespace;
}
@Override
public boolean shouldSendXSIType() {
return false;
}
@Override
public void startElement(QName qName, Attributes attributes) throws IOException {
// for some reason many of the EC2 types are coming through with an empty namespace
// override it with the one provided at constructor time
if ("".equals(qName.getNamespaceURI())) {
qName = new QName(defaultNamespace, qName.getLocalPart());
}
super.startElement(qName, attributes);
}
// override these to ensure that by default nil elements are left out of
// serialized XML. Only the first one is apparently called by MessageElement, but
// overriding both for completeness.
@Override
public void serialize(QName elemQName,
Attributes attributes,
Object value)
throws IOException {
serialize(elemQName, attributes, value, null, Boolean.FALSE, null);
}
@Override
public void serialize(QName elemQName,
Attributes attributes,
Object value,
QName xmlType)
throws IOException {
serialize(elemQName, attributes, value, xmlType, Boolean.FALSE, null);
}
}