View Javadoc

1   package org.csc.phynixx.xa;
2   
3   /*
4    * #%L
5    * phynixx-xa
6    * %%
7    * Copyright (C) 2014 csc
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  
24  import org.csc.phynixx.common.exceptions.DelegatedRuntimeException;
25  import org.csc.phynixx.common.exceptions.ExceptionUtils;
26  import org.csc.phynixx.common.logger.IPhynixxLogger;
27  import org.csc.phynixx.common.logger.PhynixxLogManager;
28  import org.csc.phynixx.connection.IPhynixxConnection;
29  import org.csc.phynixx.watchdog.ITimeoutCondition;
30  import org.csc.phynixx.watchdog.TimeoutCondition;
31  import org.csc.phynixx.watchdog.log.ConditionViolatedLog;
32  import org.csc.phynixx.xa.IPhynixxXAResourceListener.IPhynixxXAResourceEvent;
33  
34  import javax.transaction.TransactionManager;
35  import javax.transaction.xa.XAException;
36  import javax.transaction.xa.XAResource;
37  import javax.transaction.xa.Xid;
38  import java.util.ArrayList;
39  import java.util.List;
40  
41  
42  /**
43   * A transactional branch may be associated with differnt XAResources (TMJOIN) or a XAResource may be shared with different transaction branches
44   * <p/>
45   * Therefore a transactional branch might be associated to many XAResources or to many transactions (==XIDs)
46   * <p/>
47   * A XAresourec can be initialized with a connection. This connection is used,
48   * the first time an TX is opened on the XAResource.
49   * <p/>
50   * Any following TX get an new connection
51   *
52   * @author christoph
53   */
54  public class PhynixxXAResource<C extends IPhynixxConnection> implements IPhynixxXAResource<C> {
55  
56      private static final long DEFAULT_TIMEOUT = Long.MAX_VALUE; // msecs - no time out at all
57  
58  
59      private static final IPhynixxLogger LOG = PhynixxLogManager.getLogger(PhynixxXAResource.class);
60  
61      private Object xaId = null;
62  
63      private PhynixxXAResourceFactory<C> xaResourceFactory = null;
64  
65      /**
66       * @supplierCardinality 0..1
67       */
68      private PhynixxManagedXAConnection<C> xaConnectionHandle = null;
69  
70      private volatile boolean closed = false;
71  
72      private ITimeoutCondition timeoutCondition = null;
73  
74      private boolean supportsTimeOut= false;
75  
76      /**
77       * TODO timeOut ueber einen Listener steuern und konfigierbar machen
78       */
79      public PhynixxXAResource(
80              String xaId,
81              TransactionManager transactionManager,
82              PhynixxXAResourceFactory<C> xaResourceFactory) {
83          this.xaId = xaId;
84          this.xaResourceFactory = xaResourceFactory;
85          this.xaConnectionHandle = new PhynixxManagedXAConnection(this, transactionManager, xaResourceFactory.getXATransactionalBranchRepository(), xaResourceFactory.getManagedConnectionFactory());
86  
87          // start a watchdog to watch the timeout ...
88          // this condition is not active
89          if( this.isSupportsTimeOut()) {
90              this.timeoutCondition = new TimeoutCondition(DEFAULT_TIMEOUT) {
91                  public void conditionViolated() {
92                      PhynixxXAResource.this.conditionViolated();
93                  }
94              };
95              // it is registered at the resource xaResourceFactory's watchdog  ....
96              xaResourceFactory.registerWatchCondition(this.timeoutCondition);
97          }
98  
99      }
100 
101 
102     /**
103      * called when the current XAresource is expired (time out occurred)
104      * The current Impl. does nothing but marked the associated TX as rollback only
105      * The underlying core connection are not treated ....
106      * <p/>
107      * There are two different situation that have to be handled
108      * If the connection expires because of a long running method call the connection isn't
109      * treated and the XAResource is marked as rollback only. The rollback is done by the Transactionmanager.
110      * If the TX expires because the Transactionmanagers doesn't answer, the underlying connection is rolledback
111      * and the XAResource is marked as rollback only (N.B. rolling back the connection
112      * marks the XAResource as heuristically rolled back)
113      */
114     public void conditionViolated() {
115         try {
116             if (this.xaConnectionHandle != null) {
117                 XATransactionalBranch<C> transactionalBranch = this.xaConnectionHandle.toGlobalTransactionBranch();
118                 if (transactionalBranch != null) {
119                     transactionalBranch.setRollbackOnly(true);
120                 }
121             }
122             if (LOG.isInfoEnabled()) {
123                 String logString = "PhynixxXAResource.expired :: XAResource " + this.getId() + " is expired (time out occurred) and all associated TX are rollbacked ";
124                 LOG.info(new ConditionViolatedLog(this.timeoutCondition, logString).toString());
125             }
126 
127         } finally {
128             // no monitoring anymore
129             setTimeOutActive(false);
130         }
131 
132     }
133 
134     private void setTimeOutActive(boolean active) {
135         if(this.timeoutCondition!=null) {
136            this.timeoutCondition.setActive(false);
137         }
138     }
139 
140 
141     public boolean isSupportsTimeOut() {
142         return supportsTimeOut;
143     }
144 
145     public void setSupportsTimeOut(boolean supportsTimeOut) {
146         throw new IllegalStateException("Timeout isn't yet supported. Will be in version 2.1");
147     }
148 
149 
150 
151     /**
152      * accessing the XAConnection forces the XAResource to enlist to the Transaction. {@link #getXAConnection()} must be called in the appropriate TransactionContext
153      *
154      * @return
155      */
156     public IPhynixxXAConnection<C> getXAConnection() {
157         return this.xaConnectionHandle;
158     }
159 
160     public boolean isClosed() {
161         return closed;
162     }
163 
164     /**
165      * transaction branch is interchangeably with transaction context
166      * <p/>
167      * <p>
168      * situation     : XAResource is associtaed with XID(1)
169      * flags=TMNOFLAGS  with XID(1)
170      * result        : XAResource creates a new transactional context
171      * </p>
172      * <p/>
173      * <p>
174      * situation     : XAResource is associtaed with XID(1)
175      * flags=TMJOIN/TNRESUME  with XID(1) (may be different in branch)
176      * result        : XAResource joins the transactional context with the XAResource already associated with XID(1)
177      * If TMRESUME is specified, start is to resume a suspended transaction branch specified in xid.
178      * </p>
179      * <p/>
180      * <p/>
181      * <p/>
182      * <p/>
183      * This method starts work on behalf of a transaction branch.
184      * <p/>
185      * <p/>
186      * <p/>
187      * If TMJOIN is specified, start is for joining an exisiting transaction branch xid.
188      * <p/>
189      * If neither TMJOIN nor TMRESUME is specified and the transaction branch specified in
190      * xid already exists, the resource manager throw the XAException
191      * with XAER_DUPID error code.
192      * <p/>
193      * If the XAResource has a current connection  and flags ==TMJOIN/TMRESUME, the current connection is
194      * substituted by the connection of the existing TX.
195      *
196      * @param xid   A global transaction identifier to be associated with the resource.
197      * @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME.
198      * @throws: XAException An error has occurred.
199      * Possible exceptions are XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
200      */
201     public void start(Xid xid, int flags) throws XAException {
202         try {
203 
204             if (xid == null) {
205                 throw new XAException(XAException.XAER_INVAL);
206             }
207             // Find the current Branch
208             XATransactionalBranch<C> transactionalBranch = null;
209 
210             // if resuming or joining an existing transaction
211             if (flags == TMRESUME) {
212                 this.xaConnectionHandle.resumeTransactionalBranch(xid);
213             } else if (flags == TMJOIN || flags == TMNOFLAGS) {
214                 this.xaConnectionHandle.startTransactionalBranch(xid);
215             }
216             LOG.debug(
217                     "PhynixxXAResource[" + this.getId() + "]:start xid='"
218                             + xid
219                             + "' flags='"
220                             + ConstantsPrinter.getXAResourceMessage(flags)
221                             + "'"
222                             + " Connected to " +
223                             this.xaConnectionHandle);
224 
225 
226             // start monitoring the timeout
227         this.setTimeOutActive(true);
228 
229         } catch (XAException xaExc) {
230             LOG.error("PhynixxXAResource[" + this.getId() + "]:start xid='"
231                     + xid  // maybe null, but doesn't matter for logging
232                     + "' flags='"
233                     + ConstantsPrinter.getXAResourceMessage(flags)
234                     + "'"
235                     + " ERROR  " + ConstantsPrinter.getXAErrorCode(xaExc.errorCode));
236             throw xaExc;
237         } catch (Exception ex) {
238             LOG.error("PhynixxXAResource.start(" + xid + "," + flags + ") on XAResourceProgressState " + this.xaId + " :: " + ex + "\n" + ExceptionUtils.getStackTrace(ex));
239             throw new DelegatedRuntimeException("start(" + xid + "," + flags + ") on XAResourceProgressState " + this.xaId, ex);
240 
241         }
242     }
243 
244 
245     public void commit(Xid xid, boolean onePhase) throws XAException {
246 
247         XATransactionalBranch<C> transactionalBranch = this.xaConnectionHandle.toGlobalTransactionBranch();
248         if (xid == null) {
249             LOG.error("No XID");
250             throw new XAException(XAException.XAER_INVAL);
251         }
252 
253         if (transactionalBranch == null) {
254             LOG.error("XAConnection is not associated to a global Transaction (expected to XID=" + xid + ")");
255             throw new XAException(XAException.XAER_PROTO);
256         }
257 
258         // assert that the current xaConnection is associated to this XID
259         if (!transactionalBranch.getXid().equals(xid)) {
260             LOG.error("XAResource " + this + " isnt't active for XID=" + xid);
261             throw new XAException(XAException.XAER_PROTO);
262         }
263 
264         // Find the current Branch
265 
266         try {
267             transactionalBranch.commit(onePhase);
268         } catch (XAException xaExc) {
269             LOG.error("PhynixxXAResource[" + this.getId() + "]:end xid='"
270                     + xid
271                     + "' onePhase='" + onePhase
272                     + "            ERROR  " + ConstantsPrinter.getXAErrorCode(xaExc.errorCode));
273             throw xaExc;
274 
275         } catch (Exception ex) {
276             LOG.error("PhynixxXAResource.commit(" + xid + "," + onePhase + ") on XAResourceProgressState " + this.xaId + " :: " + ex + "\n" + ExceptionUtils.getStackTrace(ex));
277             throw new DelegatedRuntimeException("commit(" + xid + "," + onePhase + ") on XAResourceProgressState " + this.xaId, ex);
278         } finally {
279             try {
280                 // Branch isn't active for the current XAresource
281                 this.xaConnectionHandle.closeTransactionalBranch(xid);
282             } finally {
283                 // stop monitoring the timeout
284             this.setTimeOutActive(false);
285 
286             }
287         }
288 
289     }
290 
291     /**
292      * finds the transactional branch of the current XAResource associated with die XID
293      * <p/>
294      * Prepares to perform a commit. May actually perform a commit
295      * in the flag commitOnPrepare is set to true.
296      * <p/>
297      * This method is called to ask the resource manager to prepare for a
298      * transaction commit of the transaction specified in xid.
299      *
300      * @param xid A global transaction identifier.
301      * @return A value indicating the resource manager's vote on the outcome of
302      * the transaction. The possible values are: XA_RDONLY or XA_OK.
303      * If the resource manager wants to roll back the transaction, it should do so by
304      * throwing an appropriate XAException in the prepare method.
305      * @throws XAException An error has occurred.
306      *                     Possible exception values are:
307      *                     XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
308      */
309     public int prepare(Xid xid) throws XAException {
310         try {
311             LOG.debug(
312                     "PhynixxXAResource[" + this.getId() + "]:prepare prepare to perform a commit for XID=" + xid);
313 
314             XATransactionalBranch<C> transactionalBranch = this.xaConnectionHandle.toGlobalTransactionBranch();
315             if (xid == null) {
316                 LOG.error("No XID");
317                 throw new XAException(XAException.XAER_INVAL);
318             }
319 
320             if (transactionalBranch == null) {
321                 LOG.error("XAConnection is not associated to a global Transaction");
322                 throw new XAException(XAException.XAER_PROTO);
323             }
324 
325             // assert that the current xaConnection is associated to this XID
326             if (!transactionalBranch.getXid().equals(xid)) {
327                 LOG.error("XAResource " + this + " isnt't active for XID=" + xid);
328                 throw new XAException(XAException.XAER_PROTO);
329             }
330 
331 
332             // must find connection for this transaction
333             int retVal = transactionalBranch.prepare();
334 
335             if (retVal == XAResource.XA_RDONLY) {
336                 this.xaConnectionHandle.closeTransactionalBranch(xid);
337             }
338             return retVal;
339 
340         } catch (XAException xaExc) {
341             LOG.error("PhynixxXAResource[" + this.getId() + "]:prepare xid='"
342                     + xid
343                     + " ERROR  " + ConstantsPrinter.getXAErrorCode(xaExc.errorCode));
344             throw xaExc;
345         } catch (Exception ex) {
346             LOG.error("PhynixxXAResource.prepare(" + xid + ") on XAResourceProgressState " + this.xaId + " :: " + ex + "\n" + ExceptionUtils.getStackTrace(ex));
347             throw new DelegatedRuntimeException("prepare(" + xid + ") on XAResourceProgressState " + this.xaId, ex);
348 
349         }
350     }
351 
352 
353     /*
354     * finds the transactional branch of the current XAResource associated with die XID
355     *
356     * This method informs the resource manager to roll back
357     * work done on behalf of a transaction branch.
358     * Upon return, the resource manager has rolled back the branch's work and
359     * has released all held resources.
360     *
361     * @param xid A global transaction identifier.
362     * @throws XAException An error has occurred.
363     *    Possible XAExceptions are
364     *       XA_HEURHAZ, XA_HEURCOM,XA_HEURRB, XA_HEURMIX,
365     *       XAER_RMERR, XAER_RMFAIL, XAER_NOTA,XAER_INVAL, or XAER_PROTO.
366     *
367     *
368     * @see javax.transaction.xa.XAResource#rollback(javax.transaction.xa.Xid)
369     */
370     public void rollback(Xid xid) throws XAException {
371         {
372             try {
373                 LOG.debug("PhynixxXAResource[" + this.getId() + "]:rollback started xid=" + xid);
374 
375                 XATransactionalBranch<C> transactionalBranch = this.xaConnectionHandle.toGlobalTransactionBranch();
376                 if (xid == null) {
377                     LOG.error("No XID");
378                     throw new XAException(XAException.XAER_INVAL);
379                 }
380 
381                 if (transactionalBranch == null) {
382                     LOG.error("XAConnection is not associated to a global Transaction");
383                     throw new XAException(XAException.XAER_PROTO);
384                 }
385 
386                 // assert that the current xaConnection is associated to this XID
387                 if (!transactionalBranch.getXid().equals(xid)) {
388                     LOG.error("XAResource " + this + " isnt't active for XID=" + xid);
389                     throw new XAException(XAException.XAER_PROTO);
390                 }
391 
392                 // Find the current Branch
393 
394                 if (transactionalBranch.isInActive()) {
395                     throw new XAException(XAException.XAER_PROTO);
396                 }
397 
398                 try {
399                     transactionalBranch.rollback();
400                 } finally {
401                     // Branch isn't active for the current XAResource
402                     this.xaConnectionHandle.closeTransactionalBranch(xid);
403                 }
404 
405 
406             } catch (XAException xaExc) {
407                 LOG.error("PhynixxXAResource[" + this.getId() + "]:rollback xid='"
408                         + xid
409                         + " ERROR  " + ConstantsPrinter.getXAErrorCode(xaExc.errorCode));
410                 throw xaExc;
411 
412             } catch (Exception ex) {
413                 LOG.error("PhynixxXAResource.rollback(" + xid + ") on XAResourceProgressState " + this.xaId + " :: " + ex + "\n" + ExceptionUtils.getStackTrace(ex));
414                 throw new DelegatedRuntimeException("rollback(" + xid + ") on XAResourceProgressState " + this.xaId, ex);
415 
416             } finally {
417                 // stop monitoring the timeout
418             this.setTimeOutActive(false);
419 
420             }
421         }
422 
423 
424     }
425 
426     /**
427      * This method ends the work performed on behalf of a transaction branch.
428      * <p/>
429      * The resource manager dissociates the XA resource from the transaction branch specified and let the transaction
430      * be completed.
431      * If TMSUSPEND is specified in flags, the transaction branch is temporarily suspended in incomplete state.
432      * The transaction context is in suspended state and must be resumed via start with TMRESUME specified.
433      * <p/>
434      * If TMFAIL is specified, the portion of work has failed. The resource manager
435      * may mark the transaction as rollback only.
436      * <p/>
437      * <p> the spec doesn't no fix the order of commit/rollback and end and we have to take of both orders.
438      * If end comes before rollback/commit, the transactional branch has to be freeze.
439      * If rollback/commit comes before end, the transactional branch has to be closed
440      * </p>
441      * If TMSUCCESS is specified, the portion of work has completed successfully. end is called in Transaction.delistResource
442      *
443      * @param xid   A global transaction identifier.
444      * @param flags If true, the resource manager should use a one-phase commit protocol
445      *              to commit the work done on behalf of xid.
446      * @throws: XAException An error has occurred. Possible XAException values
447      * are XAER_RMERR, XAER_RMFAIL,XAER_NOTA, XAER_INVAL, XAER_PROTO,XA_RB*.
448      */
449     public void end(Xid xid, int flags) throws XAException {
450         try {
451             //not tested XS
452             LOG.debug("PhynixxXAResource:end");
453             LOG.debug(
454                     "PhynixxXAResource[" + this.getId() + "]:end xid='" + xid + "' flags='" + ConstantsPrinter.getXAResourceMessage(flags) + "'");
455 
456             if (xid == null) {
457                 LOG.error("No XID");
458                 throw new XAException(XAException.XAER_INVAL);
459             }
460 
461             XATransactionalBranch<C> transactionalBranch = this.xaConnectionHandle.toGlobalTransactionBranch();
462             if (transactionalBranch == null) {
463                 LOG.error("XAConnection is not associated to a global Transaction");
464                 throw new XAException(XAException.XAER_PROTO);
465             }
466 
467             // assert that the current xaConnection is associated to this XID
468             if (!transactionalBranch.getXid().equals(xid)) {
469                 LOG.error("XAResource " + this + " isnt't active for XID=" + xid);
470                 throw new XAException(XAException.XAER_PROTO);
471             }
472 
473             if (flags == TMSUSPEND) {
474                 this.xaConnectionHandle.suspendTransactionalBranch(xid);
475             } else if (flags == TMSUCCESS) {
476 
477                 // XAProtocol is finsihed an the branch isn't needed any longer
478                 if (transactionalBranch.isXAProtocolFinished()) {
479                     transactionalBranch.close();
480                     LOG.debug("XAResource " + this + " closed gracefully ");
481                 }
482             } else if (flags == TMFAIL) {
483                 transactionalBranch.setRollbackOnly(true);
484             }
485 
486         } catch (XAException xaExc) {
487             LOG.error("PhynixxXAResource[" + this.getId() + "]:end xid='"
488                     + xid
489                     + "' flags='"
490                     + ConstantsPrinter.getXAResourceMessage(flags)
491                     + "'"
492                     + " ERROR  " + ConstantsPrinter.getXAErrorCode(xaExc.errorCode));
493             throw xaExc;
494         } catch (Exception ex) {
495             LOG.error("PhynixxXAResource.end(" + xid + "," + flags + ") on XAResourceProgressState " + this.xaId + " :: " + ex + "\n" + ExceptionUtils.getStackTrace(ex));
496             throw new DelegatedRuntimeException("end(" + xid + "," + flags + ") on XAResourceProgressState " + this.xaId, ex);
497         }
498     }
499 
500     /**
501      * finds the transactional branch of the current XAResource associated with die XID and closes it without commit or explicit rollback
502      *
503      * @param xid
504      * @throws XAException
505      */
506     public void forget(Xid xid) throws XAException {
507         try {
508             LOG.debug("PhynixxXAResource[" + this.getId() + "]:forget forget with Xid");
509             if (xid == null)
510                 throw new XAException(XAException.XAER_INVAL);
511 
512             XATransactionalBranch<C> transactionalBranch = this.xaConnectionHandle.toGlobalTransactionBranch();
513             // must find connection for this transaction
514             if (transactionalBranch == null) {
515                 return; //
516             }
517             this.xaConnectionHandle.closeTransactionalBranch(xid);
518 
519         } finally {
520             // stop monitoring the timeout
521         this.setTimeOutActive(false);
522 
523         }
524     }
525 
526     public int getTransactionTimeout() throws XAException {
527         return (int) (this.timeoutCondition.getTimeout()) * 1000;
528     }
529 
530     /**
531      * This method is called to determine if the resource
532      * manager instance represented by the target object is the same
533      * as the resource manager instance represented by the parameter xares .
534      * <p/>
535      * The resource manager is reresented by the ResourceFactory.
536      *
537      * @param xaResource An XAResource object.
538      * @return true if same RM instance; otherwise false.
539      */
540 
541     public boolean isSameRM(XAResource xaResource) throws XAException {
542         if (this.equals(xaResource)) { // if the same object
543             LOG.debug("PhynixxXAResource[" + this.getId() + "]:isSameRM isSameRM");
544             return true; // then definitely the same RM
545         }
546         if (!(xaResource instanceof PhynixxXAResource)) {
547             // if it's not one of our wrappers
548             LOG.debug("PhynixxXAResource[" + this.getId() + "]:isSameRM not isSameRM");
549             return false; // then it's definitely not the same RM
550         }
551         PhynixxXAResource sampleXARes = (PhynixxXAResource) xaResource;
552 
553         try {
554             // cast to something more convenient
555             if (xaResourceFactory.equals(sampleXARes.xaResourceFactory)) {
556                 // if they originate from same data source
557                 LOG.debug("PhynixxXAResource[" + this.getId() + "]:isSameRM isSameRM (equal XAResourceFactory)");
558                 return true; // then they're the same RM
559             } else {
560                 LOG.debug("PhynixxXAResource[" + this.getId() + "]:isSameRM not isSameRM (not equal XAResourceFactory)");
561                 return false;
562             }
563         } catch (Exception ex) {
564             LOG.error("PhynixxXAResource.isSameRM(" + sampleXARes.xaId + ") on XAResourceProgressState " + this.xaId + " :: " + ex + "\n" + ExceptionUtils.getStackTrace(ex));
565             throw new DelegatedRuntimeException("isSameRM(" + sampleXARes.xaId + ") on XAResourceProgressState " + this.xaId, ex);
566 
567         }
568     }
569 
570 
571     /**
572      * finds the transactional branch of the current XAResource associated with die XID
573      * <p/>
574      * Close this XA XAResourceProgressState.
575      * All depending Connection are closed
576      */
577     public void close() {
578 
579         if (this.xaConnectionHandle != null) {
580             XATransactionalBranch<C> transactionalBranch = this.xaConnectionHandle.toGlobalTransactionBranch();
581             if (transactionalBranch != null) {
582                 transactionalBranch.close();
583             }
584         }
585         this.closed = true;
586         this.notifyClosed();
587 
588         LOG.debug("PhynixxXAResource[" + this.getId() + "]:closed ");
589     }
590 
591     /**
592      * the system is recovered by the xaResourceFactory representing the persistence management system,
593      */
594     public Xid[] recover(int flags) throws XAException {
595         LOG.info("PhynixxXAResource[" + this.getId() + "]:recover recover flags=" + ConstantsPrinter.getXAResourceMessage(flags));
596         if (flags != TMSTARTRSCAN && flags != TMENDRSCAN && flags != TMNOFLAGS) {
597             throw new XAException(XAException.XAER_INVAL);
598         }
599 
600         Xid[] retval = null;
601         retval = this.xaResourceFactory.recover(); // get all valid Xids
602         return retval;
603     }
604 
605 
606 
607 
608     /**
609      * This method sets the transaction timeout value for this XAResource instance.
610      * Once set, this timeout value is effective until setTransactionTimeout
611      * is invoked again with a different value.
612      * To reset the timeout value to the default value used by the resource manager,
613      * set the value to zero.
614      * If the timeout operation is performed successfully, the method returns true; otherwise false.
615      * If a resource manager does not support transaction timeout value to be set explicitly,
616      * this method returns false.
617      *
618      * @param seconds An positive integer specifying the timout value in seconds.
619      *                Zero resets the transaction timeout value to the default one used
620      *                by the resource manager. A negative value results in XAExceptio to be thrown
621      *                with XAER_INVAL error code.
622      * @throws XAException An error has occurred. Possible exception values are: XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
623      * @returns true if transaction timeout value is set successfully; otherwise false.
624      */
625     public boolean setTransactionTimeout(int seconds) throws XAException {
626 
627         return false;
628         /**
629         if(!this.isSupportsTimeOut()) {
630             throw new IllegalStateException("TimeOut is not supported  --> call setSupportsTimeOut");
631 
632         }
633 
634         if (seconds < 0) {
635             throw new XAException(XAException.XAER_INVAL);
636         }
637         long msecs = seconds * 1000;
638         if (seconds == 0) {
639             msecs = DEFAULT_TIMEOUT;
640         }
641         this.timeoutCondition.resetCondition(msecs);
642 
643         return true;
644          **/
645 
646     }
647 
648 
649 
650     public final boolean equals(Object obj) {
651         return super.equals(obj);
652     }
653 
654 
655     public final int hashCode() {
656         return super.hashCode();
657     }
658 
659     public Object getId() {
660         return this.xaId;
661     }
662 
663 	/*
664     public String toString()
665 	{
666 		StringBuffer sb = new StringBuffer();
667 		sb.append("PhynixxXAResource "+this.xaId+" :\n");
668 		sb.append("     is closed =<"+this.isClosed() + ">\n");
669 		sb.append("     next timeOut =<"+this.nextTimeout + ">\n");
670 		sb.append("     timeOut period =<"+this.timeoutPeriod + ">\n");
671 		sb.append("     timeOut secs =<"+this.timeoutSecs + ">\n");
672 		sb.append("     transaction manager=<"+this.transactionManager + ">\n");
673 		return sb.toString();	
674 	}
675 	*/
676 
677     public String toString() {
678         return this.xaResourceFactory.getId().toString() + "." + this.xaId.toString();
679     }
680 
681 
682     private List<IPhynixxXAResourceListener<C>> listeners = new ArrayList();
683 
684     public void addXAResourceListener(IPhynixxXAResourceListener listener) {
685         if (!listeners.contains(listener)) {
686             this.listeners.add(listener);
687         }
688     }
689 
690     public void removeXAResourceListener(IPhynixxXAResourceListener<C> listener) {
691 
692         this.listeners.remove(listener);
693     }
694 
695     private void notifyClosed() {
696         IPhynixxXAResourceEvent<C> event = new PhynixxXAResourceEvent(this);
697         for (int i = 0; i < listeners.size(); i++) {
698             IPhynixxXAResourceListener<C> listener = listeners.get(i);
699             listener.closed(event);
700         }
701 
702     }
703 
704 
705 }