1   /*
2    * Copyright 2003 - 2013 The eFaps Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Revision:        $Rev$
17   * Last Changed:    $Date$
18   * Last Changed By: $Author$
19   */
20  
21  package org.efaps.db.transaction;
22  
23  import javax.transaction.RollbackException;
24  import javax.transaction.SystemException;
25  import javax.transaction.xa.XAResource;
26  import javax.transaction.xa.Xid;
27  
28  import org.efaps.db.Context;
29  import org.efaps.util.EFapsException;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  /**
34   * Abstract class used to given an easy interface to implemented XA resources
35   * within the eFaps application.<br/>
36   * To use the implementation of such resource, it must be called first method
37   * {@link #open}. To free a resource of using, methods {@link #commit} (if all
38   * work was OK) or {@link #abort} (if the transaction must be rolled back) must
39   * be called.
40   *
41   * @author The eFaps Team
42   * @version $Id$
43   */
44  public abstract class AbstractResource
45      implements XAResource
46  {
47      /**
48       * Logging instance used in this class.
49       */
50      private static Logger LOG = LoggerFactory.getLogger(AbstractResource.class);
51  
52      /**
53       * Is set to <i>true</i> if the connection resource is already enlisted in
54       * the transaction. Otherwise the value is <i>false</i>.
55       */
56      private boolean opened = false;
57  
58      /**
59       * Opens this connection resource and enlisted this resource in the
60       * transaction.
61       *
62       * @throws EFapsException if the resource is already opened or this
63       *                        resource could not be enlisted
64       */
65      public void open() throws EFapsException
66      {
67          AbstractResource.LOG.debug("open resource:{}", this);
68          if (this.opened)  {
69              AbstractResource.LOG.error("resource already opened");
70              throw new EFapsException(AbstractResource.class, "open.AlreadyOpened");
71          }
72          try  {
73              final Context context = Context.getThreadContext();
74              context.getTransaction().enlistResource(this);
75          } catch (final RollbackException e)  {
76              AbstractResource.LOG.error("exception occurs while delisting in transaction, "
77                                                  + "commit not possible", e);
78              throw new EFapsException(AbstractResource.class,
79                                       "open.RollbackException", e);
80          } catch (final SystemException e)  {
81              AbstractResource.LOG.error("exception occurs while delisting in transaction, "
82                                                  + "commit not possible", e);
83              throw new EFapsException(AbstractResource.class,
84                                       "open.SystemException", e);
85          }
86          this.opened = true;
87      }
88  
89      /**
90       * Closes this connection resource and delisted this resource in the
91       * transaction. The method must be called if the transaction should be
92       * commited.
93       *
94       * @throws EFapsException if the resource is not opened or this resource
95       *                        could not delisted
96       */
97      public void commit() throws EFapsException
98      {
99          AbstractResource.LOG.debug("commit resource:{}", this);
100         if (!this.opened)  {
101             AbstractResource.LOG.error("resource not opened, commit not possible");
102             throw new EFapsException(AbstractResource.class, "commit.NotOpened");
103         }
104         try  {
105             final Context context = Context.getThreadContext();
106             context.getTransaction().delistResource(this, XAResource.TMSUCCESS);
107         } catch (final SystemException e)  {
108             AbstractResource.LOG.error("exception occurs while delisting in transaction, "
109                                                 + "commit not possible", e);
110             throw new EFapsException(AbstractResource.class, "commit.SystemException", e);
111         }
112         freeResource();
113         this.opened = false;
114     }
115 
116     /**
117      * Closes this XA resource and delisted this resource in the transaction.
118      * <br/>
119      * The method must be called if the transaction should be aborted (rolled
120      * back).
121      *
122      * @throws EFapsException if the resource is not opened or this resource
123      *                        could not delisted
124      */
125     public void abort()
126         throws EFapsException
127     {
128         AbstractResource.LOG.debug("abort resource:{}", this);
129         if (!this.opened)  {
130             throw new EFapsException(AbstractResource.class, "abort.NotOpened");
131         }
132         try  {
133             final Context context = Context.getThreadContext();
134             context.getTransaction().delistResource(this, XAResource.TMFAIL);
135             context.abort();
136         } catch (final SystemException e)  {
137             throw new EFapsException(AbstractResource.class, "abort.SystemException", e);
138         }
139         freeResource();
140         this.opened = false;
141     }
142 
143     /**
144      * Method used to free this resource in the eFaps context object (so that
145      * the resource instance could be reused).
146      */
147     protected abstract void freeResource();
148 
149     /**
150      * This is the getter method for instance variable {@link #opened}.
151      *
152      * @return <code>true</code> if this resource is open, otherwise
153      *         <code>false</code> is returned.
154      * @see #opened
155      */
156     public final boolean isOpened()
157     {
158         return this.opened;
159     }
160 
161     /**
162      * The method starts work on behalf of a transaction branch specified in
163      * parameter <code>_xid</code>. Normally nothing must be done, because the
164      * pre-work is already done in the contructor (and an instance of a
165      * resource is only defined for one transaction).
166      *
167      * @param _xid      global transaction identifier
168      * @param _flags    flags
169      */
170     public void start(final Xid _xid,
171                       final int _flags)
172     {
173         AbstractResource.LOG.trace("start resource {}, flags {}" + _flags, _xid, _flags);
174     }
175 
176     /**
177      * The method ends the work performed on behalf of a transaction branch.
178      * Normally nothing must be done, because an instance of a resource is only
179      * defined for one transaction.
180      *
181      * @param _xid      global transaction identifier
182      * @param _flags    flags
183      */
184     public void end(final Xid _xid,
185                     final int _flags)
186     {
187         AbstractResource.LOG.trace("end resource {}, flags {}" + _flags, _xid, _flags);
188     }
189 
190     /**
191      * The method is called from the transaction (manager) to check if the XA
192      * resource is the same as the given XA resource in the parameter
193      * <code>_xaras</code>. This is done with a string compare
194      * (method {@link String#equals(Object)}) off the
195      * {@link Object#toString()} methods of this and the XA resource in
196      * <code>_xaras</code>.
197      *
198      * @param _xares    XA resource used to test if same resource
199      * @return <code>true</code> if the string compare returns that they are
200      *         equal, otherwise <code>false</code> is returned
201      * @see XAResource#isSameRM(XAResource)
202      */
203     public boolean isSameRM(final XAResource _xares)
204     {
205         final boolean ret = _xares.toString().equals(toString());
206         AbstractResource.LOG.trace("is Same RM: {}", ret);
207         return ret;
208     }
209 }