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.client;
037:
038: import com.sun.xml.ws.api.SOAPVersion;
039: import com.sun.xml.ws.api.message.Header;
040: import com.sun.xml.ws.api.message.Headers;
041: import com.sun.xml.ws.api.message.Message;
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.WSDLPort;
045: import com.sun.xml.ws.api.pipe.Pipe;
046: import com.sun.xml.ws.api.pipe.PipeCloner;
047: import com.sun.xml.ws.assembler.ClientPipeConfiguration;
048: import com.sun.xml.ws.policy.AssertionSet;
049: import com.sun.xml.ws.policy.Policy;
050: import com.sun.xml.ws.policy.PolicyAssertion;
051: import com.sun.xml.ws.policy.PolicyException;
052: import com.sun.xml.ws.policy.PolicyMap;
053: import static com.sun.xml.ws.policy.PolicyMap.createWsdlOperationScopeKey;
054: import com.sun.xml.ws.policy.PolicyMapKey;
055: import com.sun.xml.ws.tx.at.ATCoordinator;
056: import com.sun.xml.ws.tx.common.ATAssertion;
057: import static com.sun.xml.ws.tx.common.ATAssertion.NOT_ALLOWED;
058: import static com.sun.xml.ws.tx.common.ATAssertion.ALLOWED;
059: import static com.sun.xml.ws.tx.common.ATAssertion.MANDATORY;
060: import static com.sun.xml.ws.tx.common.Constants.AT_ASSERTION;
061: import static com.sun.xml.ws.tx.common.Constants.WSAT_2004_PROTOCOL;
062: import com.sun.xml.ws.tx.common.StatefulWebserviceFactoryFactory;
063: import com.sun.xml.ws.tx.common.TransactionManagerImpl;
064: import com.sun.xml.ws.tx.common.TxBasePipe;
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.coordinator.ContextFactory;
068: import com.sun.xml.ws.tx.coordinator.CoordinationContextInterface;
069: import com.sun.xml.ws.tx.coordinator.CoordinationManager;
070: import com.sun.xml.ws.tx.webservice.member.coord.CoordinationContext;
071: import com.sun.xml.ws.tx.webservice.member.coord.CoordinationContextType;
072: import java.util.logging.Level;
073:
074: import javax.transaction.SystemException;
075: import javax.transaction.Transaction;
076: import javax.xml.bind.Marshaller;
077: import javax.xml.namespace.QName;
078: import javax.xml.ws.WebServiceException;
079: import java.util.Iterator;
080:
081: /**
082: * This class process transactional context for client outgoing message.
083: *
084: * @author Ryan.Shoemaker@Sun.COM
085: * @version $Revision: 1.12 $
086: * @since 1.0
087: */
088: // suppress known deprecation warnings about using pipes.
089: @SuppressWarnings("deprecation")
090: public class TxClientPipe extends TxBasePipe {
091:
092: static private TxLogger logger = TxLogger
093: .getCoordLogger(TxClientPipe.class);
094:
095: final QName COORD_CTX_QNAME = new QName(
096: "http://schemas.xmlsoap.org/ws/2004/10/wscoor",
097: "CoordinationContext");
098:
099: /**
100: * Contains policy assertions
101: */
102: private final ClientPipeConfiguration pipeConfig;
103:
104: private final SOAPVersion soapVersion;
105:
106: /**
107: * Interact with Security pipe that ran previously.
108: */
109: //private final SecurityPipeContext spctx;
110:
111: /**
112: * Marshaller
113: */
114: private Marshaller marshaller;
115:
116: /**
117: * Construct a new outbound tx pipe.
118: *
119: * @param pcfg ws-policy configuration
120: * @param next the next pipe in the chain
121: * param spctx security context
122: */
123: public TxClientPipe(ClientPipeConfiguration pcfg, Pipe next/*,
124: SecurityPipeContext spctx*/) {
125: super (next);
126: this .pipeConfig = pcfg;
127: this .soapVersion = pcfg.getBinding().getSOAPVersion();
128:
129: //this.spctx = spctx;
130: this .marshaller = TxJAXBContext.createMarshaller();
131: }
132:
133: /**
134: * Constructor used by copy method
135: */
136: private TxClientPipe(TxClientPipe orig, PipeCloner cloner) {
137: super (cloner.copy(orig.next));
138: cloner.add(orig, this );
139: this .pipeConfig = orig.pipeConfig;
140: this .soapVersion = orig.soapVersion;
141: this .marshaller = TxJAXBContext.createMarshaller();
142: }
143:
144: /**
145: * Creates an identical clone of this Pipe.
146: */
147: public Pipe copy(PipeCloner cloner) {
148: return new TxClientPipe(this , cloner);
149: }
150:
151: /**
152: * Process transactional context in outgoing message.
153: *
154: * Transactional context is only flowed if the following conditions are met:
155: * <ul>
156: * <li>current JTA Transaction</li>
157: * <li>wsdl:binding/wsdl:operation of this packet has wsat:ATAssertion</li>
158: * </ul>
159: *
160: * @param pkt
161: * @return null
162: */
163: public Packet process(Packet pkt) {
164: final Message msg = pkt.getMessage();
165: final WSDLPort wsdlModel = pipeConfig.getWSDLModel();
166: Packet responsePacket = null;
167:
168: // TODO: minimizing process overhead for cases that do not flow a transaction.
169: // Current assumption is it is cheaper to check if there is a current JTA transaction
170: // than it is to check if a wsdl:binding/wsdl:operation has wsat:ATAssertion
171: // If that assumption is incorrect, exchange this check with one checking for
172: // existence of wsat policy assertion.
173: //
174: Transaction currentTxn = checkCurrentJTATransaction(msg,
175: wsdlModel);
176: if (currentTxn == null) {
177: return next.process(pkt);
178: }
179:
180: // get trust plugin from security pipe
181: // encryption through security pipe
182:
183: final WSDLBoundOperation wsdlBoundOp = msg
184: .getOperation(wsdlModel);
185: ATAssertion atAssertion = getOperationATPolicy(pipeConfig
186: .getPolicyMap(), wsdlModel, wsdlBoundOp).atAssertion;
187: if (atAssertion == NOT_ALLOWED) {
188: // no ws-at policy assertion on the wsdl:binding/wsdl:operation, so no work to do here
189: if (logger.isLogging(Level.FINE)) {
190: logger.fine("process",
191: "no ws-at policy asssertion for "
192: + wsdlBoundOp.getName().toString());
193: }
194: return next.process(pkt);
195: }
196:
197: // TODO: Issue a warning if can not flow a WS-AT transaction context due to following implementation limitations
198: // 1. From an Application Container
199: // 2. On an asynchronous web service invocation (should be able to detect this statically)
200: // Recorded as wsit issue 471.
201: if ((atAssertion == MANDATORY || atAssertion == ALLOWED)) {
202: // TODO: Is it possible to determine (2) in this pipe??
203:
204: // Following conditional is true in application client OR when
205: // configuration of wstx service is invalid.
206: // Possible misconfigurations are wrong port for wstx_service,
207: // improper security certificates.
208: if (!StatefulWebserviceFactoryFactory.getInstance()
209: .isWSTXServiceAvailable()) {
210: logger.warning("TxClientPipe", LocalizationMessages
211: .WSAT_TXN_CONTEXT_NOT_FLOWED_1001(msg
212: .getOperation(wsdlModel).getName()
213: .toString()));
214: return next.process(pkt);
215: }
216: }
217:
218: // get the coordination context from JTS ThreadLocal data
219: CoordinationContextInterface context = lookupOrCreateCoordinationContext(atAssertion);
220: CoordinationContext CC = (CoordinationContext) context
221: .getValue(); // TODO: fix cast
222: Header ccHeader;
223: if (atAssertion == MANDATORY || atAssertion == ALLOWED) {
224: // flow current txn scope CC with msg
225: // Generate <wst:IssuedToken> header.
226: // The <wscoor:Identifier> is passed as <wsp:AppliesTo> in the
227: // <wst:RequestSecurityTokenResponse>. The resulting <wst:IssuedToken>
228: // must be injected into the CoodinationContext and subsequently encrypted
229: CoordinationContextType.Identifier id = CC.getIdentifier(); // coordinated activity id
230: //IssuedTokenContext itCtx = spctx.processClientInitRSCTR(msg, id, scid /* what's scid? */);
231: // ISSUE: who will inject the <wst:IssuedToken> txser???
232:
233: // add coordination context into message header
234:
235: // Add soap:MustUnderstand.
236: CC.getOtherAttributes().put(
237: new QName(soapVersion.nsUri, "mustUnderstand"),
238: "true");
239: ccHeader = Headers.create(soapVersion, marshaller,
240: COORD_CTX_QNAME, CC);
241:
242: msg.getHeaders().add(ccHeader);
243: }
244:
245: // Suspend transaction from current thread.
246: // If web service invocations is within same application server vm,
247: // then the same JTA transaction will be used and it must only be associated
248: // with one thread at any point in time. Will resume transaction when it returns.
249: try {
250: currentTxn = txnMgr.suspend();
251: } catch (SystemException ex) {
252: throw new WebServiceException(ex.getMessage(), ex);
253: }
254: Exception rethrow = null;
255: try {
256: responsePacket = next.process(pkt);
257: } catch (Exception e) {
258: rethrow = e;
259: } finally {
260: try {
261: // flow of control is transfered back from caller, resume transaction.
262: txnMgr.resume(currentTxn);
263: } catch (Exception ex) {
264: if (rethrow != null) {
265: rethrow.initCause(ex);
266: throw new WebServiceException(ex.getMessage(),
267: rethrow);
268: } else {
269: rethrow = ex;
270: }
271: }
272: if (rethrow != null) {
273: throw new WebServiceException(rethrow.getMessage(),
274: rethrow);
275: }
276: }
277: return responsePacket;
278: }
279:
280: private CoordinationContextInterface lookupOrCreateCoordinationContext(
281: ATAssertion assertion) {
282: Transaction currentTxn;
283: CoordinationContextInterface result = null;
284: try {
285: currentTxn = txnMgr.getTransaction();
286: } catch (SystemException e) {
287: throw new WebServiceException(e.getMessage(), e);
288: }
289:
290: // wsat policy assertion validation when no current txn scope CC
291: if ((currentTxn == null) && (assertion == MANDATORY)) {
292: // txn scope MANDATORY to invoke this operation, notify user
293: throw new WebServiceException(LocalizationMessages
294: .MISSING_TX_SCOPE_1000());
295: }
296:
297: if (currentTxn != null) {
298: // see if a coordination context is already associated with the current JTA transaction.
299: result = txnMgr.getCoordinationContext();
300: if (result == null) {
301: // create & associate a coordination context with current thread's transaction context
302: final long EXPIRES = txnMgr.getRemainingTimeout() * 1000L;
303: result = ContextFactory.createContext(
304: WSAT_2004_PROTOCOL, EXPIRES);
305:
306: // create a new coordinator object for this context. Associate JTA transaction with ATCoordinator.
307: ATCoordinator coord = new ATCoordinator(result);
308: coord.setTransaction(currentTxn);
309: CoordinationManager.getInstance().putCoordinator(coord);
310:
311: // cache the resulting context in the transaction context
312: txnMgr.setCoordinationContext(result);
313: }
314: }
315: return result;
316: }
317:
318: static private boolean reportCheckCurrentJTAStacktrace = true;
319:
320: private Transaction checkCurrentJTATransaction(Message msg,
321: WSDLPort wsdlModel) {
322: Transaction currentTxn = null;
323: try {
324: currentTxn = txnMgr.getTransaction();
325:
326: // workaround for glassfksh issue 2659, catch throwable instead of just SystemException.
327: // } catch (SystemException se) {
328: } catch (Throwable t) {
329: if (logger.isLogging(Level.FINEST)) {
330: if (reportCheckCurrentJTAStacktrace) {
331: reportCheckCurrentJTAStacktrace = false;
332: logger
333: .finest(
334: "checkCurrentJTATransaction",
335: "handled exception thrown during checkCurrentJTATransaction",
336: t);
337: } else {
338: logger
339: .finest("checkCurrentJTATransaction",
340: "handled exception thrown during checkCurrentJTATransaction");
341: }
342: }
343: }
344: if (currentTxn == null) {
345: // no current JTA transaction, so no work to do here
346: if (logger.isLogging(Level.FINEST)) {
347: logger.finest("process",
348: "no current JTA transaction for invoked operation "
349: + msg.getOperation(wsdlModel).getName()
350: .toString());
351: }
352: }
353: return currentTxn;
354: }
355: }
|