blob: ebbfb4fdfaa2557c335705d120c1e84ea8097e80 [file] [log] [blame]
/*
* Portions Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.xml.internal.ws.encoding.soap;
import com.sun.xml.internal.ws.encoding.EncoderDecoderBase;
import com.sun.xml.internal.ws.encoding.jaxb.JAXBBridgeInfo;
import com.sun.xml.internal.ws.encoding.jaxb.RpcLitPayload;
import com.sun.xml.internal.ws.encoding.soap.internal.AttachmentBlock;
import com.sun.xml.internal.ws.encoding.soap.internal.HeaderBlock;
import com.sun.xml.internal.ws.encoding.soap.internal.InternalMessage;
import com.sun.xml.internal.ws.model.*;
import com.sun.xml.internal.ws.model.soap.SOAPBinding;
import com.sun.xml.internal.ws.server.RuntimeContext;
import com.sun.xml.internal.ws.handler.HandlerContext;
import com.sun.xml.internal.ws.handler.MessageContextUtil;
import com.sun.xml.internal.ws.pept.ept.MessageInfo;
import com.sun.xml.internal.ws.client.BindingProviderProperties;
import com.sun.xml.internal.ws.client.RequestContext;
import javax.activation.DataHandler;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.WebServiceException;
import java.awt.Image;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @author Vivek Pandey
*
* Base SOAP encoder decoder class.
*/
public abstract class EncoderDecoder extends EncoderDecoderBase {
/**
* Fills the data[] which is ordered list of java method parameters.
*
* @param context
* will be used if needed
* @param obj
* @param data
* @return if the parameter is a return
*/
protected Object fillData(RuntimeContext context, Parameter param, Object obj, Object[] data,
SOAPBinding binding, ParameterBinding paramBinding) {
if (param.isWrapperStyle()) {
Object resp = null;
for (Parameter p : ((WrapperParameter) param).getWrapperChildren()) {
QName name = p.getName();
Object value = null;
if (binding.isDocLit()){
value = super.getWrapperChildValue(context, ((JAXBBridgeInfo)obj).getValue(),
name.getNamespaceURI(), name.getLocalPart());
}else if (binding.isRpcLit()){
value = getWrapperChildValue(context, obj, name.getNamespaceURI(), name
.getLocalPart());
if(value == null)
value = setIfPrimitive(p.getTypeReference().type);
}
if (p.isResponse())
resp = value;
else {
if (data[p.getIndex()] != null) {
Parameter.setHolderValue(data[p.getIndex()], value);
} else {
data[p.getIndex()] = p.createHolderValue(value);
}
}
}
return resp;
}
if(!paramBinding.isAttachment()){
if(paramBinding.isUnbound())
obj = setIfPrimitive(param.getTypeReference().type);
else
obj = (obj != null)?((JAXBBridgeInfo)obj).getValue():null;
}
if (param.isResponse()) {
if(paramBinding.isUnbound())
return setIfPrimitive(param.getTypeReference().type);
return obj;
} else if (data[param.getIndex()] != null) {
Parameter.setHolderValue(data[param.getIndex()], obj);
} else {
data[param.getIndex()] = param.createHolderValue(obj);
}
return null;
}
/**
* Returns the default values of primitive types. To be called when the object referene by this type is null.
* @param type
* @return default values of primitive types if type is primitive else null
*/
private Object setIfPrimitive(Type type) {
if(type instanceof Class){
Class cls = (Class)type;
if(cls.isPrimitive()){
if(cls.getName().equals(boolean.class.getName())){
return false;
}
return 0;
}
}
return null;
}
/**
* creates the payload to be serilized over the wire. It can be constructed
* from the parameters in data[] or from the result.
*
* @param context
* runtime context. It can be used to get access to the
* JAXBContext.
* @param data
* parameters
* @param result
* it could be null if there are no response object,for example
* incase of outgoing client message.
* @return Payload - decided by the binding used
*/
protected Object createPayload(RuntimeContext context, Parameter param, Object[] data,
Object result, SOAPBinding binding, ParameterBinding paramBinding) {
if(paramBinding.isAttachment()){
Object obj = null;
if(param.isResponse())
obj = result;
else
obj = param.getHolderValue(data[param.getIndex()]);
return obj;
}
if (binding.isRpcLit() && paramBinding.isBody()) {
return createRpcLitPayload(context, (WrapperParameter) param, data, result);
}
Object obj = createDocLitPayloadValue(context, param, data, result);
RuntimeModel model = context.getModel();
return new JAXBBridgeInfo(model.getBridge(param.getTypeReference()), obj);
}
/*
* Returns the value corresponding to the localName or part accessor from
* rpclit structure.
*
* @see EncoderDecoderBase#getWrapperChildValue(RuntimeContext,
* Object, String, String)
*/
@Override
protected Object getWrapperChildValue(RuntimeContext context, Object obj, String nsURI,
String localName) {
RpcLitPayload payload = (RpcLitPayload) obj;
JAXBBridgeInfo rpcParam = payload.getBridgeParameterByName(localName);
if(rpcParam != null)
return rpcParam.getValue();
return null;
}
/**
* Gives the binding specific object to be serialized.
*
* @param context
* @param data
* @param result
*/
private Object createDocLitPayloadValue(RuntimeContext context, Parameter param, Object[] data, Object result) {
if (param.isWrapperStyle()) {
return createJAXBBeanPayload(context, (WrapperParameter) param, data, result);
}
return getBarePayload(param, data, result);
}
/**
* Gets the HeaderBlock corresponding to the given QName.
*
* @param name
* @param headers
* @return the <code>HeaderBlock</code> corresponding to the given
* <code>QName name</code>
*/
protected HeaderBlock getHeaderBlock(QName name, List<HeaderBlock> headers) {
for (HeaderBlock header : headers) {
if (name.equals(header.getName()))
return header;
}
return null;
}
/**
* Returns either the value corresponding to the parameter or result.
*
* @param param
* @param data
* @param result
* @return Either the value of response of the parameter corresponding to
* the parameter index, takes care of Holder.value.
*
*/
private Object getBarePayload(Parameter param, Object[] data, Object result) {
Object obj = null;
if (param.isResponse()) {
obj = result;
} else {
obj = param.getHolderValue(data[param.getIndex()]);
}
return obj;
}
/**
* Creates JAXB style wrapper bean from the parameters or result.
*
* @param context
* @param param
* WrapperParameter
* @param data
* @param result
* @return non-null JAXB style bean.
*/
private Object createJAXBBeanPayload(RuntimeContext context, WrapperParameter param,
Object[] data, Object result) {
Class bean = (Class) param.getTypeReference().type;
try {
Object obj = bean.newInstance();
for( Parameter p : param.getWrapperChildren() ) {
Object value;
if (p.isResponse())
value = result;
else
value = p.getHolderValue(data[p.getIndex()]);
QName name = p.getName();
setWrapperChildValue(context, obj, value,
name.getNamespaceURI(), name.getLocalPart());
}
return obj;
} catch(Exception e){
throw new SerializationException(e);
}
}
/**
* Creates RpcLitPayload from the parameters or response.
*
* @param context
* @param param
* @param data
* @param result
* @return non-null RpcLitPayload
*/
private Object createRpcLitPayload(RuntimeContext context, WrapperParameter param,
Object[] data, Object result) {
RpcLitPayload payload = new RpcLitPayload(param.getName());
for (Parameter p : param.getWrapperChildren()) {
if(p.getBinding().isUnbound())
continue;
Object value = null;
if (p.isResponse())
value = result;
else
value = p.getHolderValue(data[p.getIndex()]);
checkRPCLitNullableParameter(p, value);
RuntimeModel model = context.getModel();
JAXBBridgeInfo bi = new JAXBBridgeInfo(model.getBridge(p.getTypeReference()), value);
payload.addParameter(bi);
}
return payload;
}
protected Object getAttachment(RuntimeContext rtContext, Map<String, AttachmentBlock> attachments,
Parameter param, ParameterBinding paramBinding){
try {
for (Map.Entry<String,AttachmentBlock> entry : attachments.entrySet()) {
AttachmentBlock ab = entry.getValue();
String part = ab.getWSDLPartName();
// part can be null if the Content-Id is not encoded as per AP 1.0 R2933. Which is ok since there could be attachments
// other than WSDL MIME bound
if(part == null)
continue;
if(part.equals(param.getPartName()) || part.equals("<"+param.getPartName())){
Class type = (Class)param.getTypeReference().type;
if(DataHandler.class.isAssignableFrom(type))
return ab.asDataHandler();
if(byte[].class==type)
return ab.asByteArray();
if(Source.class.isAssignableFrom(type))
return ab.asSource();
if(Image.class.isAssignableFrom(type))
return ab.asImage();
if(InputStream.class==type)
return ab.asDataHandler().getInputStream();
if(isXMLMimeType(paramBinding.getMimeType())) {
JAXBBridgeInfo bi = (JAXBBridgeInfo)rtContext.getDecoderInfo(param.getName());
ab.deserialize(rtContext.getBridgeContext(),bi);
return bi.getValue();
}
}
}
return null;
} catch (IOException e) {
throw new SerializationException(e);
}
}
protected void addAttachmentPart(RuntimeContext rtContext, InternalMessage im, Object obj, Parameter mimeParam){
if(obj == null)
return;
RuntimeModel model = rtContext.getModel();
String mimeType = mimeParam.getBinding().getMimeType();
String contentId;
try {
contentId = URLEncoder.encode(mimeParam.getPartName(), "UTF-8")+ '=' +UUID.randomUUID()+"@jaxws.sun.com";
contentId="<"+contentId+">";
} catch (UnsupportedEncodingException e) {
throw new SerializationException(e);
}
AttachmentBlock ab;
if(obj instanceof DataHandler)
ab = AttachmentBlock.fromDataHandler(contentId,(DataHandler)obj);
else
if(obj instanceof Source)
// this is potentially broken, as there's no guarantee this will work.
// we should have our own AttachmentBlock implementation for this.
ab = AttachmentBlock.fromDataHandler(contentId, new DataHandler(obj,mimeType));
else
if(obj instanceof byte[])
ab = AttachmentBlock.fromByteArray(contentId,(byte[])obj,mimeType);
else
if(isXMLMimeType(mimeType))
ab = AttachmentBlock.fromJAXB(contentId,
new JAXBBridgeInfo(model.getBridge(mimeParam.getTypeReference()), obj),
rtContext, mimeType );
else
// this is also broken, as there's no guarantee that the object type and the MIME type
// matches. But most of the time it matches, so it mostly works.
ab = AttachmentBlock.fromDataHandler(contentId,new DataHandler(obj,mimeType));
//populate the attachment map in the message context
HandlerContext hc = rtContext.getHandlerContext();
if(hc != null){
MessageContext mc = hc.getMessageContext();
if(mc != null){
MessageContextUtil.addMessageAttachment(mc, ab.getId(), ab.asDataHandler());
}
}
im.addAttachment(ab);
}
protected void copyAttachmentProperty(Map<String, Object> ctxt, InternalMessage im) {
if(ctxt == null)
return;
Map<String, DataHandler> attMap = (Map<String, DataHandler>) ctxt.get(MessageContext.OUTBOUND_MESSAGE_ATTACHMENTS);
if (attMap == null)
return;
for (Map.Entry<String, DataHandler> att : attMap.entrySet()) {
im.addAttachment(AttachmentBlock.fromDataHandler(att.getKey(), att.getValue()));
}
}
private boolean isXMLMimeType(String mimeType){
if(mimeType.equals("text/xml") || mimeType.equals("application/xml"))
return true;
return false;
}
/**
* Checks rpclit body parts for nullability
*/
void checkRPCLitNullableParameter(Parameter param, Object value) {
if(value == null)
throw new WebServiceException("Method Parameter: "+param.getName() +" cannot be null. This is BP 1.1 R2211 violation.");
}
}