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.client.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 Kohsuke Kawaguchi
50 */
51abstract class BodyBuilder {
52    abstract Message createMessage(Object[] methodArgs);
53
54    static final BodyBuilder EMPTY_SOAP11 = new Empty(SOAPVersion.SOAP_11);
55    static final BodyBuilder EMPTY_SOAP12 = new Empty(SOAPVersion.SOAP_12);
56
57    private static final class Empty extends BodyBuilder {
58        private final SOAPVersion soapVersion;
59
60        public Empty(SOAPVersion soapVersion) {
61            this.soapVersion = soapVersion;
62        }
63
64        Message createMessage(Object[] methodArgs) {
65            return Messages.createEmpty(soapVersion);
66        }
67    }
68
69    /**
70     * Base class for those {@link BodyBuilder}s that build a {@link Message}
71     * from JAXB objects.
72     */
73    private static abstract class JAXB extends BodyBuilder {
74        /**
75         * This object determines the binding of the object returned
76         * from {@link #build(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        final Message createMessage(Object[] methodArgs) {
88            return JAXBMessage.create( bridge, build(methodArgs), soapVersion );
89        }
90
91        /**
92         * Builds a JAXB object that becomes the payload.
93         */
94        abstract Object build(Object[] methodArgs);
95    }
96
97    /**
98     * Used to create a payload JAXB object just by taking
99     * one of the parameters.
100     */
101    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 BodyBuilder} from a bare parameter.
111         */
112        Bare(ParameterImpl p, SOAPVersion soapVersion, ValueGetter getter) {
113            super(p.getXMLBridge(), soapVersion);
114            this.methodPos = p.getIndex();
115            this.getter = getter;
116        }
117
118        /**
119         * Picks up an object from the method arguments and uses it.
120         */
121        Object build(Object[] methodArgs) {
122            return getter.get(methodArgs[methodPos]);
123        }
124    }
125
126
127    /**
128     * Used to handle a 'wrapper' style request.
129     * Common part of rpc/lit and doc/lit.
130     */
131    abstract static class Wrapped extends JAXB {
132
133        /**
134         * Where in the method argument list do they come from?
135         */
136        protected final int[] indices;
137
138        /**
139         * Abstracts away the {@link Holder} handling when touching method arguments.
140         */
141        protected final ValueGetter[] getters;
142
143        /**
144         * How does each wrapped parameter binds to XML?
145         */
146        protected XMLBridge[] parameterBridges;
147
148        /**
149         * List of Parameters packed in the body.
150         * Only used for error diagnostics.
151         */
152        protected List<ParameterImpl> children;
153
154        protected Wrapped(WrapperParameter wp, SOAPVersion soapVersion, ValueGetterFactory getter) {
155            super(wp.getXMLBridge(), soapVersion);
156            children = wp.getWrapperChildren();
157            indices = new int[children.size()];
158            getters = new ValueGetter[children.size()];
159            for( int i=0; i<indices.length; i++ ) {
160                ParameterImpl p = children.get(i);
161                indices[i] = p.getIndex();
162                getters[i] = getter.get(p);
163            }
164        }
165
166        /**
167         * Packs a bunch of arguments into a {@link WrapperComposite}.
168         */
169        protected WrapperComposite buildWrapperComposite(Object[] methodArgs) {
170            WrapperComposite cs = new WrapperComposite();
171            cs.bridges = parameterBridges;
172            cs.values = new Object[parameterBridges.length];
173
174            // fill in wrapped parameters from methodArgs
175            for( int i=indices.length-1; i>=0; i-- ) {
176                Object arg = getters[i].get(methodArgs[indices[i]]);
177                if(arg==null) {
178                    throw new WebServiceException("Method Parameter: "+
179                        children.get(i).getName()+" cannot be null. This is BP 1.1 R2211 violation.");
180                }
181                cs.values[i] = arg;
182            }
183
184            return cs;
185        }
186    }
187
188    /**
189     * Used to create a payload JAXB object by wrapping
190     * multiple parameters into one "wrapper bean".
191     */
192    final static class DocLit extends Wrapped {
193        /**
194         * How does each wrapped parameter binds to XML?
195         */
196        private final PropertyAccessor[] accessors;
197
198        /**
199         * Wrapper bean.
200         */
201        private final Class wrapper;
202
203        /**
204         * Needed to get wrapper instantiation method.
205         */
206        private BindingContext bindingContext;
207        private boolean dynamicWrapper;
208
209        /**
210         * Creates a {@link BodyBuilder} from a {@link WrapperParameter}.
211         */
212        DocLit(WrapperParameter wp, SOAPVersion soapVersion, ValueGetterFactory getter) {
213            super(wp, soapVersion, getter);
214            bindingContext = wp.getOwner().getBindingContext();
215            wrapper = (Class)wp.getXMLBridge().getTypeInfo().type;
216            dynamicWrapper = WrapperComposite.class.equals(wrapper);
217            parameterBridges = new XMLBridge[children.size()];
218            accessors = new PropertyAccessor[children.size()];
219            for( int i=0; i<accessors.length; i++ ) {
220                ParameterImpl p = children.get(i);
221                QName name = p.getName();
222                if (dynamicWrapper) {
223                    parameterBridges[i] = children.get(i).getInlinedRepeatedElementBridge();
224                    if (parameterBridges[i] == null) parameterBridges[i] = children.get(i).getXMLBridge();
225                } else {
226                    try {
227                        accessors[i] = p.getOwner().getBindingContext().getElementPropertyAccessor(
228                            wrapper, name.getNamespaceURI(), name.getLocalPart() );
229                    } catch (JAXBException e) {
230                        throw new WebServiceException(  // TODO: i18n
231                            wrapper+" do not have a property of the name "+name,e);
232                    }
233                }
234            }
235
236        }
237
238        /**
239         * Packs a bunch of arguments into a {@link WrapperComposite}.
240         */
241        Object build(Object[] methodArgs) {
242            if (dynamicWrapper) return buildWrapperComposite(methodArgs);
243            try {
244                //Object bean = wrapper.newInstance();
245                Object bean = bindingContext.newWrapperInstace(wrapper);
246
247                // fill in wrapped parameters from methodArgs
248                for( int i=indices.length-1; i>=0; i-- ) {
249                    accessors[i].set(bean,getters[i].get(methodArgs[indices[i]]));
250                }
251
252                return bean;
253            } catch (InstantiationException e) {
254                // this is irrecoverable
255                Error x = new InstantiationError(e.getMessage());
256                x.initCause(e);
257                throw x;
258            } catch (IllegalAccessException e) {
259                // this is irrecoverable
260                Error x = new IllegalAccessError(e.getMessage());
261                x.initCause(e);
262                throw x;
263            } catch (com.sun.xml.internal.ws.spi.db.DatabindingException e) {
264                // this can happen when the set method throw a checked exception or something like that
265                throw new WebServiceException(e);    // TODO:i18n
266            }
267        }
268    }
269
270
271    /**
272     * Used to create a payload JAXB object by wrapping
273     * multiple parameters into a {@link WrapperComposite}.
274     *
275     * <p>
276     * This is used for rpc/lit, as we don't have a wrapper bean for it.
277     * (TODO: Why don't we have a wrapper bean for this, when doc/lit does!?)
278     */
279    final static class RpcLit extends Wrapped {
280
281        /**
282         * Creates a {@link BodyBuilder} from a {@link WrapperParameter}.
283         */
284        RpcLit(WrapperParameter wp, SOAPVersion soapVersion, ValueGetterFactory getter) {
285            super(wp, soapVersion, getter);
286            // we'll use CompositeStructure to pack requests
287            assert wp.getTypeInfo().type==WrapperComposite.class;
288
289            parameterBridges = new XMLBridge[children.size()];
290            for( int i=0; i<parameterBridges.length; i++ )
291                parameterBridges[i] = children.get(i).getXMLBridge();
292        }
293
294        Object build(Object[] methodArgs) {
295            return buildWrapperComposite(methodArgs);
296        }
297    }
298}
299