001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036: package com.sun.xml.ws.tx.service;
037:
038: import com.sun.enterprise.transaction.TransactionImport;
039: import com.sun.xml.ws.api.SOAPVersion;
040: import com.sun.xml.ws.api.WSBinding;
041: import com.sun.xml.ws.api.addressing.WSEndpointReference;
042: import com.sun.xml.ws.api.message.Packet;
043: import com.sun.xml.ws.api.model.wsdl.WSDLBoundOperation;
044: import com.sun.xml.ws.api.model.wsdl.WSDLBoundPortType;
045: import com.sun.xml.ws.api.model.wsdl.WSDLOperation;
046: import com.sun.xml.ws.api.model.wsdl.WSDLPort;
047: import com.sun.xml.ws.api.pipe.Pipe;
048: import com.sun.xml.ws.api.pipe.PipeCloner;
049: import com.sun.xml.ws.policy.AssertionSet;
050: import com.sun.xml.ws.policy.Policy;
051: import com.sun.xml.ws.policy.PolicyAssertion;
052: import com.sun.xml.ws.policy.PolicyException;
053: import com.sun.xml.ws.policy.PolicyMap;
054: import com.sun.xml.ws.policy.PolicyMapKey;
055: import com.sun.xml.ws.tx.at.ATCoordinator;
056: import com.sun.xml.ws.tx.at.ATSubCoordinator;
057: import com.sun.xml.ws.tx.at.CoordinationXid;
058: import com.sun.xml.ws.tx.common.ATAssertion;
059: import static com.sun.xml.ws.tx.common.ATAssertion.*;
060: import static com.sun.xml.ws.tx.common.Constants.*;
061: import com.sun.xml.ws.tx.common.Message;
062: import com.sun.xml.ws.tx.common.TransactionManagerImpl;
063: import com.sun.xml.ws.tx.common.TxBasePipe;
064: import com.sun.xml.ws.tx.common.TxFault;
065: import com.sun.xml.ws.tx.common.TxJAXBContext;
066: import com.sun.xml.ws.tx.common.TxLogger;
067: import com.sun.xml.ws.tx.common.WsaHelper;
068: import com.sun.xml.ws.tx.coordinator.CoordinationContextInterface;
069: import com.sun.xml.ws.tx.coordinator.CoordinationManager;
070:
071: import javax.servlet.ServletContext;
072: import javax.transaction.NotSupportedException;
073: import javax.transaction.SystemException;
074: import javax.transaction.Transaction;
075: import javax.transaction.xa.Xid;
076: import javax.xml.bind.JAXBException;
077: import javax.xml.bind.Unmarshaller;
078: import javax.xml.namespace.QName;
079: import javax.xml.ws.WebServiceException;
080: import java.util.HashMap;
081: import java.util.Iterator;
082: import java.util.Map;
083: import java.util.logging.Level;
084:
085: /**
086: * Process transactional context for incoming message to server.
087: * <p/>
088: * Supports following WS-Coordination protocols: 2004 WS-Atomic Transaction protocol
089: *
090: * @version $Revision: 1.18 $
091: * @since 1.0
092: */
093: // suppress known deprecation warnings about using pipes.
094: @SuppressWarnings("deprecation")
095: public class TxServerPipe extends TxBasePipe {
096:
097: static private TxLogger logger = TxLogger
098: .getLogger(TxServerPipe.class);
099:
100: // unmarshalls supported WS-AT coordination context formats
101: final private Unmarshaller unmarshaller;
102:
103: final private WSDLPort port;
104: final private WSBinding wsbinding;
105:
106: /**
107: * Deployment time computations for WS-Atomic Tranaction processing.
108: *
109: * <p>
110: * Computes WS-Atomic Policy Assertions for all wsdl bound operations for this <code>wsbinding</code>.
111: *
112: * @param port WSDL port for this pipe
113: * @param map PolicyMap
114: * @param next Next pipe to be executed.
115: */
116: public TxServerPipe(WSDLPort port, WSBinding wsbinding,
117: PolicyMap map, Pipe next) {
118: super (next);
119: unmarshaller = TxJAXBContext.createUnmarshaller();
120: this .port = port;
121: this .wsbinding = wsbinding;
122: cacheOperationToPolicyMappings(map, port.getBinding());
123: }
124:
125: private TxServerPipe(TxServerPipe from, PipeCloner cloner) {
126: super (cloner.copy(from.next));
127: cloner.add(from, this );
128: this .port = from.port;
129: this .wsbinding = from.wsbinding;
130: this .unmarshaller = TxJAXBContext.createUnmarshaller();
131: this .opPolicyCache = from.opPolicyCache;
132: }
133:
134: /**
135: * Creates an identical clone of this Pipe.
136: */
137: public Pipe copy(PipeCloner cloner) {
138: return new TxServerPipe(this , cloner);
139: }
140:
141: /**
142: * Process WS-AT transactional context in incoming request message.
143: * <p/>
144: * <p>
145: * Transactional context processing is driven by ws-at policy assertions associated with
146: * wsdl:binding/wsdl:operation of parameter <code>pkt</code>.
147: *
148: * @param pkt a packet is an incoming request message with JAX-WS properties
149: * @return processing of pkt by next pipe in pipeline
150: */
151: public Packet process(Packet pkt) {
152: final String METHOD_NAME = "TxServerPipe.process";
153: if (logger.isLogging(Level.FINER)) {
154: Object[] params = new Object[1];
155: params[0] = pkt;
156: logger.entering(METHOD_NAME, params);
157: }
158:
159: com.sun.xml.ws.tx.common.Message msg = new Message(pkt
160: .getMessage(), wsbinding);
161: WSDLBoundOperation msgOp = msg.getOperation(port);
162: QName msgOperation = msgOp.getName();
163: OperationATPolicy msgOpATPolicy = getATPolicy(msgOperation);
164: String bindingName = port.getBinding().getName().toString();
165: CoordinationContextInterface coordTxnCtx = null;
166:
167: // Precondition check
168: assertNoCurrentTransaction(LocalizationMessages
169: .INVALID_JTA_TRANSACTION_ENTERING_5002(bindingName,
170: msgOperation.getLocalPart()));
171:
172: try {
173: coordTxnCtx = msg.getCoordinationContext(unmarshaller);
174: } catch (JAXBException je) {
175: String invalidMsg = LocalizationMessages
176: .INVALID_COORDINATION_CONTEXT_5006(msg
177: .getMessageID(), bindingName, msgOperation
178: .getLocalPart(), je.getLocalizedMessage());
179: logger.warning(METHOD_NAME, invalidMsg, je);
180:
181: // send fault S4.3 wscoor:InvalidParameters
182:
183: // DO NOT LOCALIZE englishReason
184: // 2004 WS-Coordination, S4. Coordination Faults "[Reason] The English language reason element""
185: String englishReason = "WSTX-SERVICE-5006: Unable to read CoordinationContext in request message, msgId="
186: + msg.getMessageID()
187: + ", sent to endpoint:operation "
188: + bindingName
189: + ":"
190: + msgOperation.getLocalPart()
191: + " due to JAXException " + je.getMessage();
192: WSEndpointReference replyTo = msg.getReplyTo();
193: if (replyTo != null) {
194: WsaHelper.sendFault(msg.getFaultTo(), replyTo.toSpec(),
195: SOAPVersion.SOAP_11, TxFault.InvalidParameters,
196: englishReason, msg.getMessageID());
197: }
198: }
199:
200: // verify coordination type protocol
201: if (coordTxnCtx != null) {
202: if (coordTxnCtx.getCoordinationType().equals(
203: WSAT_2004_PROTOCOL)) {
204: if (logger.isLogging(Level.FINEST)) {
205: logger.finest(METHOD_NAME,
206: "Processing wscoor:CoordinationContext(protocol="
207: + coordTxnCtx.getCoordinationType()
208: + "coordId="
209: + coordTxnCtx.getIdentifier()
210: + ") for binding:" + bindingName
211: + "operation:" + msgOp.getName());
212: }
213:
214: // let jax-ws runtime know that coordination context header was understood.
215: msg.setCoordCtxUnderstood();
216: } // else check for other supported protocols in future.
217: else { // unrecognized ws coordination protocol type
218: // Another pipe *may* process CoordinationContext with this unknown protocol type so just log this.
219: logger.info(METHOD_NAME, LocalizationMessages
220: .IGNORING_UNRECOGNIZED_PROTOCOL_5005(
221: coordTxnCtx.getCoordinationType(),
222: coordTxnCtx.getIdentifier(),
223: bindingName, msgOperation
224: .getLocalPart()));
225: coordTxnCtx = null;
226: }
227: }
228:
229: // wsat:ATAssertion policy assertion check. Note 2004 WS-Atomic Transaction does not require this check.
230: if (msgOpATPolicy.atAssertion == MANDATORY
231: && coordTxnCtx == null) {
232: String inconsistencyMsg = LocalizationMessages
233: .MUST_FLOW_WSAT_COORDINATION_CONTEXT_5000(
234: bindingName, msgOperation.getLocalPart(),
235: msg.getMessageID());
236: logger.warning(METHOD_NAME, inconsistencyMsg);
237: // TODO: complete evaluation if desire to throw this as an exception or not.
238: // Since WS-AT specification does not require this, might be best not to throw exception.
239: throw new WebServiceException(inconsistencyMsg);
240: }
241:
242: /*
243: * Absence of a ws-at ATAssertion does not forbid WS-AT CoordinationContext from flowing.
244: * OASIS WS-AT refers to this case as no claims made. Rules external to WS-AT may have
245: * caused this situation to occur.
246: */
247: if (coordTxnCtx != null
248: && msgOpATPolicy.atAssertion == NOT_ALLOWED) {
249:
250: // Not an error just log this as occuring since it would be helpful to know about this.
251: logger.info(METHOD_NAME, LocalizationMessages
252: .UNEXPECTED_FLOWED_TXN_CONTEXT_5004(bindingName,
253: msgOperation.getLocalPart(), msg
254: .getMessageID(), coordTxnCtx
255: .getIdentifier()));
256: }
257:
258: boolean importedTxn = false;
259: Packet responsePkt = null;
260: Transaction jtaTxn = null;
261: Exception rethrow = null;
262: ATCoordinator coord = null;
263:
264: if (coordTxnCtx != null) {
265: coord = (ATCoordinator) CoordinationManager.getInstance()
266: .lookupOrCreateCoordinator(coordTxnCtx);
267: assert coord != null;
268:
269: jtaTxn = coord.getTransaction();
270: if (jtaTxn != null) {
271: if (logger.isLogging(Level.FINER)) {
272: logger.finer(METHOD_NAME,
273: "Resume JTA Txn already associated with coordId="
274: + coordTxnCtx.getIdentifier());
275: }
276: coord.resumeTransaction();
277: try {
278: responsePkt = next.process(pkt);
279: } catch (Exception e) {
280: logger
281: .warning(
282: METHOD_NAME,
283: LocalizationMessages
284: .HANDLE_EXCEPTION_TO_COMPLETE_TRANSACTION_5012(
285: coordTxnCtx
286: .getIdentifier(),
287: jtaTxn.toString()),
288: e);
289: rethrow = e;
290: txnMgr.setRollbackOnly();
291: }
292: coord.suspendTransaction();
293: } else if (coord.isSubordinateCoordinator()) {
294: if (logger.isLogging(Level.FINER)) {
295: logger
296: .finer(
297: METHOD_NAME,
298: "importing ws-at activity id:"
299: + coordTxnCtx
300: .getIdentifier()
301: + " from external WS-AT coordinator");
302: }
303: importedTxn = true;
304: ((ATSubCoordinator) coord).beginImportTransaction();
305: try {
306: responsePkt = next.process(pkt);
307: } catch (Exception e) {
308: jtaTxn = coord.getTransaction();
309: final String jtaTxnString = jtaTxn == null ? ""
310: : jtaTxn.toString();
311: logger
312: .warning(
313: METHOD_NAME,
314: LocalizationMessages
315: .HANDLE_EXCEPTION_TO_RELEASE_IMPORTED_TXN_5013(
316: coordTxnCtx
317: .getIdentifier(),
318: jtaTxnString), e);
319: rethrow = e;
320: txnMgr.setRollbackOnly();
321: }
322: // Sun App Server 9.1 does not support suspend/resume with TransactionInflow, so release imported txn here.
323: //coord.suspendTransaction();
324: ((ATSubCoordinator) coord).endImportTransaction();
325: } else {
326: responsePkt = next.process(pkt);
327: }
328: } else if (msgOpATPolicy.ATAlwaysCapability == true) {
329:
330: // no Transaction context flowed with message but WS-AT policy assertion requests auto creation of txn
331: // context on server side invocation of method.
332: if (isServlet(pkt)) {
333: beginTransaction();
334:
335: if (logger.isLogging(Level.FINER)) {
336: logger
337: .finer(
338: METHOD_NAME,
339: "create JTA Transaction, no CoordinationContext flowed with operation and wsat:ATAlwaysCapability is enabled");
340: }
341: } // else allow Java EE EJB container to create transaction context
342: // for Sun Application Server: see BaseContainer.preInvokeTx
343: try {
344: responsePkt = next.process(pkt);
345: } catch (Exception e) {
346: rethrow = e;
347: logger.warning(METHOD_NAME, LocalizationMessages
348: .HANDLE_EXCEPTION_TO_COMMIT_CREATED_TXN_5015(),
349: e);
350: txnMgr.setRollbackOnly();
351: }
352: if (isServlet(pkt)) {
353: commitTransaction();
354: } // else allow Java EE EJB container to finish transaction context
355: // for Sun Application Server: see BaseContainer.postInvokeTx
356: } else {
357: responsePkt = next.process(pkt);
358: }
359:
360: // Postcondition check
361: assertNoCurrentTransaction(LocalizationMessages
362: .INVALID_JTA_TRANSACTION_RETURNING_5003(bindingName,
363: msgOperation.getLocalPart()));
364: if (rethrow != null) {
365: String exMsg = LocalizationMessages.WSTXRETHROW_5014(
366: bindingName, msgOperation.toString());
367: throw new WebServiceException(exMsg, rethrow);
368: }
369:
370: logger.exiting(METHOD_NAME, responsePkt);
371: return responsePkt;
372: }
373:
374: /**
375: * Returns true if <code>pkt</code> was sent to a servlet.
376: *
377: * @param pkt packet representing a request message
378: */
379: private boolean isServlet(Packet pkt) {
380: ServletContext sCtx = pkt.endpoint.getContainer().getSPI(
381: javax.servlet.ServletContext.class);
382: return sCtx != null;
383: }
384:
385: /**
386: * Cache of operation name to its WS-AT policies computed in constructor.
387: * An operation is not inserted into cache if its WS-AT policies are the default values.
388: * This is a space/time tradeoff. Saves time in process call by taking up
389: * more memory for pipe. Makes sense for server-side, probably not for client-side
390: */
391: private Map<QName, OperationATPolicy> opPolicyCache = new HashMap<QName, OperationATPolicy>();
392:
393: static private OperationATPolicy DEFAULT = new OperationATPolicy();
394:
395: private OperationATPolicy getDefaultATPolicy() {
396: return DEFAULT;
397: }
398:
399: /**
400: * Return the WS-AT policy assertions for wsdl bounded <code>operationName</code>.
401: *
402: * @param operationName wsdl bound operation
403: * @return WS-AT policy assertions for <code>operationName</code>
404: */
405: private OperationATPolicy getATPolicy(QName operationName) {
406: OperationATPolicy result = opPolicyCache.get(operationName);
407: if (result == null) {
408: // return default wsat policies
409: result = getDefaultATPolicy();
410: }
411: return result;
412: }
413:
414: /**
415: * This method caches WS-AT policy assertion for all binding operations for the pipe.
416: * If an operation has the default WS-AT policy assertions, nothing is inserted in cache for
417: * method, the getATPolicy() method handles this case.
418: */
419: private void cacheOperationToPolicyMappings(PolicyMap pmap,
420: WSDLBoundPortType binding) {
421:
422: // Cache wsat policy for each wsdl:binding/wsdl:operation for binding
423: for (WSDLBoundOperation bindingOp : binding
424: .getBindingOperations()) {
425: TxBasePipe.OperationATPolicy opat = DEFAULT;
426: try {
427: opat = getOperationATPolicy(pmap, port, bindingOp);
428: opPolicyCache.put(bindingOp.getName(), opat);
429: } catch (WebServiceException wse) {
430: logger.warning("cacheOperationToPolicyMappings",
431: LocalizationMessages
432: .WSAT_POLICY_PROCESSING_FAILURE_5017(
433: binding.getName(), bindingOp
434: .getName()), wse);
435: }
436: if (logger.isLogging(Level.FINE)) {
437: logger
438: .fine(
439: "cacheOperationToPolicyMappings",
440: "Operation: "
441: + binding.getName()
442: + ":"
443: + bindingOp.getName()
444: + " WS-AT Policy Assertions: ATAssertion:"
445: + opat.atAssertion
446: + " ATAlwaysCapability:"
447: + opat.ATAlwaysCapability);
448: }
449:
450: }
451: }
452:
453: /**
454: * Create a transaction context for pipe to be processed within.
455: */
456: private void beginTransaction() {
457: try {
458: txnMgr.getUserTransaction().begin();
459: } catch (NotSupportedException ex) {
460: String handlerMsg = LocalizationMessages
461: .TXN_MGR_OPERATION_FAILED_5011("getUserTransaction().begin()");
462: logger.warning("beginTransaction", handlerMsg, ex);
463: throw new WebServiceException(handlerMsg, ex);
464: } catch (SystemException ex) {
465: String handlerMsg = LocalizationMessages
466: .TXN_MGR_OPERATION_FAILED_5011("getUserTransaction().begin()");
467: logger.warning("beginTransaction", handlerMsg, ex);
468: throw new WebServiceException(handlerMsg, ex);
469: }
470: }
471:
472: /**
473: * Complete transaction context for pipe to be processed within.
474: */
475: private void commitTransaction() {
476: try {
477: txnMgr.getUserTransaction().commit();
478: } catch (Exception ex) {
479: String commitExceptionMsg = LocalizationMessages
480: .EXCEPTION_DURING_COMMIT_5008();
481: logger.warning("commitTransaction", commitExceptionMsg, ex);
482: throw new WebServiceException(commitExceptionMsg, ex);
483: }
484: }
485:
486: private void assertNoCurrentTransaction(String message) {
487: Transaction txn = null;
488: try {
489: txn = txnMgr.getTransaction();
490: } catch (SystemException ex) {
491: String handlerMsg = LocalizationMessages
492: .TXN_MGR_OPERATION_FAILED_5011("getTransaction");
493: logger.warning("assertNoCurrentTransaction", handlerMsg);
494: throw new WebServiceException(handlerMsg, ex);
495: }
496: if (txn != null) {
497: logger.severe("TxServerPipe.process", message + " "
498: + txn.toString());
499: try {
500: txnMgr.suspend();
501: } catch (SystemException ex) {
502: String handlerMsg = LocalizationMessages
503: .TXN_MGR_OPERATION_FAILED_5011("suspend");
504: logger
505: .warning("assertNoCurrentTransaction",
506: handlerMsg);
507: throw new WebServiceException(handlerMsg, ex);
508: }
509: }
510: }
511: }
|