1/*
2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.xml.internal.ws.server.sei;
27
28import com.sun.xml.internal.ws.api.SOAPVersion;
29import com.sun.xml.internal.ws.api.message.Message;
30import com.sun.xml.internal.ws.api.message.Messages;
31import com.sun.xml.internal.ws.message.jaxb.JAXBMessage;
32import com.sun.xml.internal.ws.model.ParameterImpl;
33import com.sun.xml.internal.ws.model.WrapperParameter;
34import com.sun.xml.internal.ws.spi.db.BindingContext;
35import com.sun.xml.internal.ws.spi.db.XMLBridge;
36import com.sun.xml.internal.ws.spi.db.PropertyAccessor;
37import com.sun.xml.internal.ws.spi.db.WrapperComposite;
38
39import javax.xml.bind.JAXBException;
40import javax.xml.namespace.QName;
41import javax.xml.ws.Holder;
42import javax.xml.ws.WebServiceException;
43import java.util.List;
44
45/**
46 * Builds a JAXB object that represents the payload.
47 *
48 * @see MessageFiller
49 * @author Jitendra Kotamraju
50 */
51public abstract class EndpointResponseMessageBuilder {
52    public abstract Message createMessage(Object[] methodArgs, Object returnValue);
53
54    public static final EndpointResponseMessageBuilder EMPTY_SOAP11 = new Empty(SOAPVersion.SOAP_11);
55    public static final EndpointResponseMessageBuilder EMPTY_SOAP12 = new Empty(SOAPVersion.SOAP_12);
56
57    private static final class Empty extends EndpointResponseMessageBuilder {
58        private final SOAPVersion soapVersion;
59
60        public Empty(SOAPVersion soapVersion) {
61            this.soapVersion = soapVersion;
62        }
63
64        public Message createMessage(Object[] methodArgs, Object returnValue) {
65            return Messages.createEmpty(soapVersion);
66        }
67    }
68
69    /**
70     * Base class for those {@link EndpointResponseMessageBuilder}s that build a {@link Message}
71     * from JAXB objects.
72     */
73    private static abstract class JAXB extends EndpointResponseMessageBuilder {
74        /**
75         * This object determines the binding of the object returned
76         * from {@link #createMessage(Object[], Object)}
77         */
78        private final XMLBridge bridge;
79        private final SOAPVersion soapVersion;
80
81        protected JAXB(XMLBridge bridge, SOAPVersion soapVersion) {
82            assert bridge!=null;
83            this.bridge = bridge;
84            this.soapVersion = soapVersion;
85        }
86
87        public final Message createMessage(Object[] methodArgs, Object returnValue) {
88            return JAXBMessage.create( bridge, build(methodArgs, returnValue), soapVersion );
89        }
90
91        /**
92         * Builds a JAXB object that becomes the payload.
93         */
94        abstract Object build(Object[] methodArgs, Object returnValue);
95    }
96
97    /**
98     * Used to create a payload JAXB object just by taking
99     * one of the parameters.
100     */
101    public final static class Bare extends JAXB {
102        /**
103         * The index of the method invocation parameters that goes into the payload.
104         */
105        private final int methodPos;
106
107        private final ValueGetter getter;
108
109        /**
110         * Creates a {@link EndpointResponseMessageBuilder} from a bare parameter.
111         */
112        public Bare(ParameterImpl p, SOAPVersion soapVersion) {
113            super(p.getXMLBridge(), soapVersion);
114            this.methodPos = p.getIndex();
115            this.getter = ValueGetter.get(p);
116        }
117
118        /**
119         * Picks up an object from the method arguments and uses it.
120         */
121        Object build(Object[] methodArgs, Object returnValue) {
122            if (methodPos == -1) {
123                return returnValue;
124            }
125            return getter.get(methodArgs[methodPos]);
126        }
127    }
128
129
130    /**
131     * Used to handle a 'wrapper' style request.
132     * Common part of rpc/lit and doc/lit.
133     */
134    abstract static class Wrapped extends JAXB {
135
136        /**
137         * Where in the method argument list do they come from?
138         */
139        protected final int[] indices;
140
141        /**
142         * Abstracts away the {@link Holder} handling when touching method arguments.
143         */
144        protected final ValueGetter[] getters;
145
146        /**
147         * How does each wrapped parameter binds to XML?
148         */
149        protected XMLBridge[] parameterBridges;
150
151        /**
152         * Used for error diagnostics.
153         */
154        protected List<ParameterImpl> children;
155
156        protected Wrapped(WrapperParameter wp, SOAPVersion soapVersion) {
157            super(wp.getXMLBridge(), soapVersion);
158
159            children = wp.getWrapperChildren();
160
161            indices = new int[children.size()];
162            getters = new ValueGetter[children.size()];
163            for( int i=0; i<indices.length; i++ ) {
164                ParameterImpl p = children.get(i);
165                indices[i] = p.getIndex();
166                getters[i] = ValueGetter.get(p);
167            }
168        }
169
170        /**
171         * Packs a bunch of arguments intoa {@link WrapperComposite}.
172         */
173        WrapperComposite buildWrapperComposite(Object[] methodArgs, Object returnValue) {
174            WrapperComposite cs = new WrapperComposite();
175            cs.bridges = parameterBridges;
176            cs.values = new Object[parameterBridges.length];
177
178            // fill in wrapped parameters from methodArgs
179            for( int i=indices.length-1; i>=0; i-- ) {
180                Object v;
181                if (indices[i] == -1) {
182                    v = getters[i].get(returnValue);
183                } else {
184                    v = getters[i].get(methodArgs[indices[i]]);
185                }
186                if(v==null) {
187                    throw new WebServiceException("Method Parameter: "+
188                        children.get(i).getName() +" cannot be null. This is BP 1.1 R2211 violation.");
189                }
190                cs.values[i] = v;
191            }
192
193            return cs;
194        }
195    }
196
197    /**
198     * Used to create a payload JAXB object by wrapping
199     * multiple parameters into one "wrapper bean".
200     */
201    public final static class DocLit extends Wrapped {
202        /**
203         * How does each wrapped parameter binds to XML?
204         */
205        private final PropertyAccessor[] accessors;
206
207        //private final RawAccessor retAccessor;
208
209        /**
210         * Wrapper bean.
211         */
212        private final Class wrapper;
213        private boolean dynamicWrapper;
214
215        /**
216         * Needed to get wrapper instantiation method.
217         */
218        private BindingContext bindingContext;
219
220        /**
221         * Creates a {@link EndpointResponseMessageBuilder} from a {@link WrapperParameter}.
222         */
223        public DocLit(WrapperParameter wp, SOAPVersion soapVersion) {
224            super(wp, soapVersion);
225            bindingContext = wp.getOwner().getBindingContext();
226            wrapper = (Class)wp.getXMLBridge().getTypeInfo().type;
227            dynamicWrapper = WrapperComposite.class.equals(wrapper);
228            children = wp.getWrapperChildren();
229            parameterBridges = new XMLBridge[children.size()];
230            accessors = new PropertyAccessor[children.size()];
231            for( int i=0; i<accessors.length; i++ ) {
232                ParameterImpl p = children.get(i);
233                QName name = p.getName();
234                if (dynamicWrapper) {
235                    parameterBridges[i] = children.get(i).getInlinedRepeatedElementBridge();
236                    if (parameterBridges[i] == null) parameterBridges[i] = children.get(i).getXMLBridge();
237                } else {
238                    try {
239                        accessors[i] = (dynamicWrapper) ? null :
240                            p.getOwner().getBindingContext().getElementPropertyAccessor(
241                            wrapper, name.getNamespaceURI(), name.getLocalPart() );
242                    } catch (JAXBException e) {
243                        throw new WebServiceException(  // TODO: i18n
244                            wrapper+" do not have a property of the name "+name,e);
245                    }
246                }
247            }
248
249        }
250
251        /**
252         * Packs a bunch of arguments into a {@link WrapperComposite}.
253         */
254        Object build(Object[] methodArgs, Object returnValue) {
255            if (dynamicWrapper) return buildWrapperComposite(methodArgs, returnValue);
256            try {
257                //Object bean = wrapper.newInstance();
258                Object bean = bindingContext.newWrapperInstace(wrapper);
259
260                // fill in wrapped parameters from methodArgs
261                for( int i=indices.length-1; i>=0; i-- ) {
262                    if (indices[i] == -1) {
263                        accessors[i].set(bean, returnValue);
264                    } else {
265                        accessors[i].set(bean,getters[i].get(methodArgs[indices[i]]));
266                    }
267                }
268
269                return bean;
270            } catch (InstantiationException e) {
271                // this is irrecoverable
272                Error x = new InstantiationError(e.getMessage());
273                x.initCause(e);
274                throw x;
275            } catch (IllegalAccessException e) {
276                // this is irrecoverable
277                Error x = new IllegalAccessError(e.getMessage());
278                x.initCause(e);
279                throw x;
280            } catch (com.sun.xml.internal.ws.spi.db.DatabindingException e) {
281                // this can happen when the set method throw a checked exception or something like that
282                throw new WebServiceException(e);    // TODO:i18n
283            }
284        }
285    }
286
287
288    /**
289     * Used to create a payload JAXB object by wrapping
290     * multiple parameters into a {@link WrapperComposite}.
291     *
292     * <p>
293     * This is used for rpc/lit, as we don't have a wrapper bean for it.
294     * (TODO: Why don't we have a wrapper bean for this, when doc/lit does!?)
295     */
296    public final static class RpcLit extends Wrapped {
297
298        /**
299         * Creates a {@link EndpointResponseMessageBuilder} from a {@link WrapperParameter}.
300         */
301        public RpcLit(WrapperParameter wp, SOAPVersion soapVersion) {
302            super(wp, soapVersion);
303            // we'll use CompositeStructure to pack requests
304            assert wp.getTypeInfo().type==WrapperComposite.class;
305
306            parameterBridges = new XMLBridge[children.size()];
307            for( int i=0; i<parameterBridges.length; i++ )
308                parameterBridges[i] = children.get(i).getXMLBridge();
309        }
310
311        /**
312         * Packs a bunch of arguments intoa {@link WrapperComposite}.
313         */
314        Object build(Object[] methodArgs, Object returnValue) {
315            return buildWrapperComposite(methodArgs, returnValue);
316        }
317    }
318}
319