1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.axis2.client;
21
22 import org.apache.axiom.om.OMElement;
23 import org.apache.axiom.om.util.UUIDGenerator;
24 import org.apache.axis2.AxisFault;
25 import org.apache.axis2.addressing.EndpointReference;
26 import org.apache.axis2.client.async.AxisCallback;
27 import org.apache.axis2.client.async.Callback;
28 import org.apache.axis2.context.ConfigurationContext;
29 import org.apache.axis2.context.MessageContext;
30 import org.apache.axis2.context.OperationContext;
31 import org.apache.axis2.context.ServiceContext;
32 import org.apache.axis2.description.AxisOperation;
33 import org.apache.axis2.description.ClientUtils;
34 import org.apache.axis2.description.TransportOutDescription;
35 import org.apache.axis2.i18n.Messages;
36 import org.apache.axis2.util.TargetResolver;
37 import org.apache.axis2.wsdl.WSDLConstants;
38
39 import java.util.Iterator;
40 import java.util.Map;
41
42 import javax.xml.namespace.QName;
43
44 /**
45 * An operation client is the way an advanced user interacts with Axis2. Actual
46 * operation clients understand a specific MEP and hence their behavior is
47 * defined by their MEP. To interact with an operation client, you first get one
48 * from a specific AxisOperation. Then you set the messages into it one by one
49 * (whatever is available). Then, when you call execute() the client will
50 * execute what it can at that point. If executing the operation client results
51 * in a new message being created, then if a message receiver is registered with
52 * the client then the message will be delivered to that client.
53 */
54 public abstract class OperationClient {
55
56 protected AxisOperation axisOp;
57
58 protected ServiceContext sc;
59
60 protected Options options;
61
62 protected OperationContext oc;
63
64 protected Callback callback;
65
66 protected AxisCallback axisCallback;
67
68 /*
69 * indicates whether the MEP execution has completed (and hence ready for
70 * resetting)
71 */
72 protected boolean completed;
73
74 protected OperationClient(AxisOperation axisOp, ServiceContext sc, Options options) {
75 this.axisOp = axisOp;
76 this.sc = sc;
77 this.options = new Options(options);
78 completed = false;
79 oc = sc.createOperationContext(axisOp);
80 }
81
82 /**
83 * Sets the options that should be used for this particular client. This
84 * resets the entire set of options to use the new options - so you'd lose
85 * any option cascading that may have been set up.
86 *
87 * @param options the options
88 */
89 public void setOptions(Options options) {
90 this.options = options;
91 }
92
93 /**
94 * Return the options used by this client. If you want to set a single
95 * option, then the right way is to do getOptions() and set specific
96 * options.
97 *
98 * @return the options, which will never be null.
99 */
100 public Options getOptions() {
101 return options;
102 }
103
104 /**
105 * Add a message context to the client for processing. This method must not
106 * process the message - it only records it in the operation client.
107 * Processing only occurs when execute() is called.
108 *
109 * @param messageContext the message context
110 * @throws AxisFault if this is called inappropriately.
111 */
112 public abstract void addMessageContext(MessageContext messageContext) throws AxisFault;
113
114 /**
115 * Return a message from the client - will return null if the requested
116 * message is not available.
117 *
118 * @param messageLabel the message label of the desired message context
119 * @return the desired message context or null if its not available.
120 * @throws AxisFault if the message label is invalid
121 */
122 public abstract MessageContext getMessageContext(String messageLabel)
123 throws AxisFault;
124
125 /**
126 * Set the callback to be executed when a message comes into the MEP and the
127 * operation client is executed. This is the way the operation client
128 * provides notification that a message has been received by it. Exactly
129 * when its executed and under what conditions is a function of the specific
130 * operation client.
131 *
132 * @param callback the callback to be used when the client decides its time to
133 * use it
134 * @deprecated Please use the AxisCallback interface rather than Callback, which has been deprecated
135 */
136 public abstract void setCallback(Callback callback);
137
138 /**
139 * Set the callback to be executed when a message comes into the MEP and the
140 * operation client is executed. This is the way the operation client
141 * provides notification that a message has been received by it. Exactly
142 * when its executed and under what conditions is a function of the specific
143 * operation client.
144 *
145 * @param callback the callback to be used when the client decides its time to
146 * use it
147 */
148 public final void setCallback(AxisCallback callback) {
149 axisCallback = callback;
150 }
151
152 /**
153 * Execute the MEP. This method is final and only serves to set (if appropriate)
154 * the lastOperationContext on the ServiceContext, and then it calls
155 * executeImpl(), which does the actual work.
156 *
157 * @param block Indicates whether execution should block or return ASAP. What
158 * block means is of course a function of the specific operation
159 * client.
160 * @throws AxisFault if something goes wrong during the execution of the operation
161 * client.
162 */
163 public final void execute(boolean block) throws AxisFault {
164 sc.setLastOperationContext(oc);
165 executeImpl(block);
166 }
167
168 /**
169 * Execute the MEP. What this does depends on the specific operation client.
170 * The basic idea is to have the operation client execute and do something
171 * with the messages that have been added to it so far. For example, if its
172 * an Out-In MEP, then if the Out message has been set, then executing the
173 * client asks it to send the message and get the In message, possibly using
174 * a different thread.
175 *
176 * @param block Indicates whether execution should block or return ASAP. What
177 * block means is of course a function of the specific operation
178 * client.
179 * @throws AxisFault if something goes wrong during the execution of the operation
180 * client.
181 */
182 public abstract void executeImpl(boolean block) throws AxisFault;
183
184 /**
185 * Reset the operation client to a clean status after the MEP has completed.
186 * This is how you can reuse an operation client. NOTE: this does not reset
187 * the options; only the internal state so the client can be used again.
188 *
189 * @throws AxisFault if reset is called before the MEP client has completed an
190 * interaction.
191 */
192 public void reset() throws AxisFault {
193 if (!completed) {
194 throw new AxisFault(Messages.getMessage("cannotreset"));
195 }
196 oc = null;
197 completed = false;
198 }
199
200
201 /**
202 * To close the transport if necessary , can call this method. The main
203 * usage of this method is when client uses two tarnsports for sending and
204 * receiving , and we need to remove entries for waiting calls in the
205 * transport listener queue.
206 * Note : DO NOT call this method if you are not using two transports to
207 * send and receive
208 *
209 * @param msgCtxt : MessageContext# which has all the transport information
210 * @throws AxisFault : throws AxisFault if something goes wrong
211 */
212 public void complete(MessageContext msgCtxt) throws AxisFault {
213 TransportOutDescription trsout = msgCtxt.getTransportOut();
214 if (trsout != null) {
215 trsout.getSender().cleanup(msgCtxt);
216 }
217 }
218
219
220 /**
221 * To get the operation context of the operation client
222 *
223 * @return OperationContext
224 */
225 public OperationContext getOperationContext() {
226 return oc;
227 }
228
229 /**
230 * Create a message ID for the given message context if needed. If user gives an option with
231 * MessageID then just copy that into MessageContext , and with that there can be multiple
232 * message with same MessageID unless user call setOption for each invocation.
233 * <p/>
234 * If user want to give message ID then the better way is to set the message ID in the option and
235 * call setOption for each invocation then the right thing will happen.
236 * <p/>
237 * If user does not give a message ID then the new one will be created and set that into Message
238 * Context.
239 *
240 * @param mc the message context whose id is to be set
241 */
242 protected void setMessageID(MessageContext mc) {
243 // now its the time to put the parameters set by the user in to the
244 // correct places and to the
245 // if there is no message id still, set a new one.
246 String messageId = options.getMessageId();
247 if (messageId == null || "".equals(messageId)) {
248 messageId = UUIDGenerator.getUUID();
249 }
250 mc.setMessageID(messageId);
251 }
252
253 protected void addReferenceParameters(MessageContext msgctx) {
254 EndpointReference to = msgctx.getTo();
255 if (options.isManageSession() || (options.getParent() != null &&
256 options.getParent().isManageSession())) {
257 EndpointReference tepr = sc.getTargetEPR();
258 if (tepr != null) {
259 Map<QName, OMElement> map = tepr.getAllReferenceParameters();
260 if (map != null) {
261 Iterator<OMElement> valuse = map.values().iterator();
262 while (valuse.hasNext()) {
263 Object refparaelement = valuse.next();
264 if (refparaelement instanceof OMElement) {
265 to.addReferenceParameter((OMElement) refparaelement);
266 }
267 }
268 }
269 }
270 }
271 }
272
273 /**
274 * prepareMessageContext gets a fresh new MessageContext ready to be sent.
275 * It sets up the necessary properties, transport information, etc.
276 *
277 * @param configurationContext the active ConfigurationContext
278 * @param mc the MessageContext to be configured
279 * @throws AxisFault if there is a problem
280 */
281 protected void prepareMessageContext(ConfigurationContext configurationContext,
282 MessageContext mc)
283 throws AxisFault {
284 // set options on the message context
285 if (mc.getSoapAction() == null || "".equals(mc.getSoapAction())) {
286 mc.setSoapAction(options.getAction());
287 }
288
289 mc.setOptions(new Options(options));
290 mc.setAxisMessage(axisOp.getMessage(WSDLConstants.MESSAGE_LABEL_OUT_VALUE));
291
292 // do Target Resolution
293 TargetResolver targetResolver =
294 configurationContext.getAxisConfiguration().getTargetResolverChain();
295 if (targetResolver != null) {
296 targetResolver.resolveTarget(mc);
297 }
298 // if the transport to use for sending is not specified, try to find it
299 // from the URL
300 TransportOutDescription senderTransport = options.getTransportOut();
301 if (senderTransport == null) {
302 EndpointReference toEPR = (options.getTo() != null) ? options
303 .getTo() : mc.getTo();
304 senderTransport = ClientUtils.inferOutTransport(configurationContext
305 .getAxisConfiguration(), toEPR, mc);
306 }
307 mc.setTransportOut(senderTransport);
308 if (options.getParent() !=null && options.getParent().isManageSession()) {
309 mc.getOptions().setManageSession(true);
310 } else if (options.isManageSession()) {
311 mc.getOptions().setManageSession(true);
312 }
313 addReferenceParameters(mc);
314 }
315 }