001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.jaxws.client.async;
020:
021: import org.apache.axis2.jaxws.ExceptionFactory;
022: import org.apache.axis2.jaxws.core.MessageContext;
023: import org.apache.axis2.jaxws.description.EndpointDescription;
024: import org.apache.axis2.jaxws.handler.AttachmentsAdapter;
025: import org.apache.axis2.jaxws.handler.HandlerChainProcessor;
026: import org.apache.axis2.jaxws.handler.HandlerInvokerUtils;
027: import org.apache.axis2.jaxws.handler.TransportHeadersAdapter;
028: import org.apache.axis2.jaxws.message.attachments.AttachmentUtils;
029: import org.apache.axis2.jaxws.spi.Constants;
030: import org.apache.axis2.jaxws.spi.migrator.ApplicationContextMigratorUtil;
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033:
034: import javax.xml.ws.Response;
035:
036: import java.util.HashMap;
037: import java.util.Map;
038: import java.util.concurrent.CancellationException;
039: import java.util.concurrent.CountDownLatch;
040: import java.util.concurrent.ExecutionException;
041: import java.util.concurrent.TimeUnit;
042: import java.util.concurrent.TimeoutException;
043:
044: /**
045: * The AsyncResponse class is used to collect the response information from Axis2 and deliver it to
046: * a JAX-WS client. AsyncResponse implements the <link>javax.xml.ws.Response</link> API that is
047: * defined in the JAX-WS 2.0 specification. The <code>Response</code> object will contain both the
048: * object that is returned as the response along with a <link>java.util.Map</link> with the context
049: * information of the response.
050: */
051: public abstract class AsyncResponse implements Response {
052:
053: private static final Log log = LogFactory
054: .getLog(AsyncResponse.class);
055:
056: private boolean cancelled;
057:
058: private Throwable fault;
059: private MessageContext faultMessageContext;
060: private MessageContext response;
061:
062: private EndpointDescription endpointDescription;
063: private Map<String, Object> responseContext;
064:
065: private CountDownLatch latch;
066: private boolean cacheValid = false;
067: private Object cachedObject = null;
068:
069: protected AsyncResponse(EndpointDescription ed) {
070: endpointDescription = ed;
071: latch = new CountDownLatch(1);
072: }
073:
074: protected void onError(Throwable flt, MessageContext faultCtx) {
075: if (log.isDebugEnabled()) {
076: log
077: .debug("AsyncResponse received a fault. Counting down latch.");
078: }
079:
080: fault = flt;
081: faultMessageContext = faultCtx;
082: faultMessageContext.setEndpointDescription(endpointDescription);
083:
084: // Probably a good idea to invalidate the cache
085: cacheValid = false;
086: cachedObject = null;
087:
088: latch.countDown();
089: if (log.isDebugEnabled()) {
090: log.debug("New latch count = [" + latch.getCount() + "]");
091: }
092: }
093:
094: protected void onComplete(MessageContext mc) {
095: if (log.isDebugEnabled()) {
096: log
097: .debug("AsyncResponse received a MessageContext. Counting down latch.");
098: }
099:
100: // A new message context invalidates the cached object retrieved
101: // during the last get()
102: if (response != mc) {
103: cachedObject = null;
104: cacheValid = false;
105: }
106:
107: response = mc;
108: response.setEndpointDescription(endpointDescription);
109:
110: // Check for cached attachment file(s) if attachments exist.
111: if (response.getAxisMessageContext().getAttachmentMap() != null) {
112: AttachmentUtils.findCachedAttachment(response
113: .getAxisMessageContext().getAttachmentMap());
114: }
115:
116: latch.countDown();
117:
118: if (log.isDebugEnabled()) {
119: log.debug("New latch count = [" + latch.getCount() + "]");
120: }
121: }
122:
123: //-------------------------------------
124: // javax.xml.ws.Response APIs
125: //-------------------------------------
126:
127: public boolean cancel(boolean mayInterruptIfRunning) {
128: // The task cannot be cancelled if it has already been cancelled
129: // before or if it has already completed.
130: if (cancelled || latch.getCount() == 0) {
131: if (log.isDebugEnabled()) {
132: log.debug("Cancellation attempt failed.");
133: }
134: return false;
135: }
136:
137: cancelled = true;
138: return cancelled;
139: }
140:
141: public Object get() throws InterruptedException, ExecutionException {
142: if (cancelled) {
143: throw new CancellationException("The task was cancelled.");
144: }
145:
146: // Wait for the response to come back
147: if (log.isDebugEnabled()) {
148: log.debug("Waiting for async response delivery.");
149: }
150: latch.await();
151:
152: Object obj = processResponse();
153: return obj;
154: }
155:
156: public Object get(long timeout, TimeUnit unit)
157: throws InterruptedException, ExecutionException,
158: TimeoutException {
159: if (cancelled) {
160: throw new CancellationException("The task was cancelled.");
161: }
162:
163: // Wait for the response to come back
164: if (log.isDebugEnabled()) {
165: log
166: .debug("Waiting for async response delivery with time out.");
167: log.debug("timeout = " + timeout);
168: log.debug("units = " + unit);
169: }
170: latch.await(timeout, unit);
171:
172: // If the response still hasn't been returned, then we've timed out
173: // and must throw a TimeoutException
174: if (latch.getCount() > 0) {
175: throw new TimeoutException(
176: "The client timed out while waiting for an asynchronous response");
177: }
178:
179: Object obj = processResponse();
180: return obj;
181: }
182:
183: public boolean isCancelled() {
184: return cancelled;
185: }
186:
187: public boolean isDone() {
188: return (latch.getCount() == 0);
189: }
190:
191: public Map getContext() {
192: return responseContext;
193: }
194:
195: private Object processResponse() throws ExecutionException {
196: // If the fault object is not null, then we've received a fault message and
197: // we need to process it in one of a number of forms.
198: if (fault != null) {
199: if (log.isDebugEnabled()) {
200: log
201: .debug("A fault was found. Starting to process fault response.");
202: }
203: Throwable t = processFaultResponse();
204: // JAXWS 4.3.3 conformance bullet says to throw an ExecutionException from here
205: throw new ExecutionException(t);
206: }
207:
208: // If we don't have a fault, then we have to have a MessageContext for the response.
209: if (response == null) {
210: throw new ExecutionException(ExceptionFactory
211: .makeWebServiceException("null response"));
212: }
213:
214: // Avoid a reparse of the message. If we already retrived the object, return
215: // it now.
216: if (cacheValid) {
217: if (log.isDebugEnabled()) {
218: log.debug("Return object cached from last get()");
219: }
220: return cachedObject;
221: }
222:
223: // TODO: IMPORTANT: this is the right call here, but beware that the messagecontext may be turned into
224: // a fault context with a fault message. We need to check for this and, if necessary, make an exception and throw it.
225: // Invoke inbound handlers.
226: TransportHeadersAdapter.install(response);
227: AttachmentsAdapter.install(response);
228: HandlerInvokerUtils.invokeInboundHandlers(response
229: .getMEPContext(), response.getInvocationContext()
230: .getHandlers(), HandlerChainProcessor.MEP.RESPONSE,
231: false);
232:
233: // TODO: Check the type of the object to make sure it corresponds with
234: // the parameterized generic type.
235: Object obj = null;
236: try {
237: if (log.isDebugEnabled()) {
238: log.debug("Unmarshalling the async response message.");
239: }
240: obj = getResponseValueObject(response);
241: // Cache the object in case it is required again
242: cacheValid = true;
243: cachedObject = obj;
244: } catch (Throwable t) {
245: if (log.isDebugEnabled()) {
246: log
247: .debug("An error occurred while processing the response");
248: }
249: throw new ExecutionException(ExceptionFactory
250: .makeWebServiceException(t));
251: }
252:
253: if (log.isDebugEnabled() && obj != null) {
254: log.debug("Unmarshalled response object of type: "
255: + obj.getClass());
256: }
257:
258: responseContext = new HashMap<String, Object>();
259:
260: // Migrate the properties from the response MessageContext back
261: // to the client response context bag.
262: ApplicationContextMigratorUtil
263: .performMigrationFromMessageContext(
264: Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID,
265: responseContext, response);
266:
267: return obj;
268: }
269:
270: private Throwable processFaultResponse() {
271: // A faultMessageContext means that there could possibly be a SOAPFault
272: // on the MessageContext that we need to unmarshall.
273: if (faultMessageContext != null) {
274: // it is possible the message could be null. For example, if we gave the proxy a bad endpoint address.
275: // If it is the case that the message is null, there's no sense running through the handlers.
276: if (faultMessageContext.getMessage() != null)
277: // Invoke inbound handlers.
278: // The adapters are intentionally NOT installed here. They cause unit test failures
279: // TransportHeadersAdapter.install(faultMessageContext);
280: // AttachmentsAdapter.install(faultMessageContext);
281: HandlerInvokerUtils.invokeInboundHandlers(
282: faultMessageContext.getMEPContext(),
283: faultMessageContext.getInvocationContext()
284: .getHandlers(),
285: HandlerChainProcessor.MEP.RESPONSE, false);
286: Throwable t = getFaultResponse(faultMessageContext);
287: if (t != null) {
288: return t;
289: } else {
290: return ExceptionFactory.makeWebServiceException(fault);
291: }
292: } else {
293: return ExceptionFactory.makeWebServiceException(fault);
294: }
295: }
296:
297: public abstract Object getResponseValueObject(MessageContext mc);
298:
299: public abstract Throwable getFaultResponse(MessageContext mc);
300:
301: }
|