Coverage Report - org.melati.poem.PoemThread
 
Classes in this File Line Coverage Branch Coverage Complexity
PoemThread
92%
94/102
76%
20/26
2.05
 
 1  
 /*
 2  
  * $Source$
 3  
  * $Revision$
 4  
  *
 5  
  * Copyright (C) 2000 William Chesters
 6  
  *
 7  
  * Part of Melati (http://melati.org), a framework for the rapid
 8  
  * development of clean, maintainable web applications.
 9  
  *
 10  
  * Melati is free software; Permission is granted to copy, distribute
 11  
  * and/or modify this software under the terms either:
 12  
  *
 13  
  * a) the GNU General Public License as published by the Free Software
 14  
  *    Foundation; either version 2 of the License, or (at your option)
 15  
  *    any later version,
 16  
  *
 17  
  *    or
 18  
  *
 19  
  * b) any version of the Melati Software License, as published
 20  
  *    at http://melati.org
 21  
  *
 22  
  * You should have received a copy of the GNU General Public License and
 23  
  * the Melati Software License along with this program;
 24  
  * if not, write to the Free Software Foundation, Inc.,
 25  
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
 26  
  * GNU General Public License and visit http://melati.org to obtain the
 27  
  * Melati Software License.
 28  
  *
 29  
  * Feel free to contact the Developers of Melati (http://melati.org),
 30  
  * if you would like to work out a different arrangement than the options
 31  
  * outlined here.  It is our intention to allow Melati to be used by as
 32  
  * wide an audience as possible.
 33  
  *
 34  
  * This program is distributed in the hope that it will be useful,
 35  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 36  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 37  
  * GNU General Public License for more details.
 38  
  *
 39  
  * Contact details for copyright holder:
 40  
  *
 41  
  *     William Chesters <williamc At paneris.org>
 42  
  *     http://paneris.org/~williamc
 43  
  *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
 44  
  */
 45  
 
 46  
 package org.melati.poem;
 47  
 
 48  
 import java.util.Vector;
 49  
 import java.util.Enumeration;
 50  
 import java.util.Map;
 51  
 import java.util.HashMap;
 52  
 import org.melati.poem.transaction.ToTidyList;
 53  
 
 54  
 /**
 55  
  * A Poem Thread.
 56  
  * 
 57  
  */
 58  
 public final class PoemThread {
 59  
 
 60  0
   private PoemThread() {
 61  0
   }
 62  
 
 63  1
   private static Vector<SessionToken> sessionTokens = new Vector<SessionToken>();
 64  
 
 65  1
   private static Vector<Integer> freeSessionTokenIndices = new Vector<Integer>();
 66  
 
 67  
   /**
 68  
    * The maximum number of Threads. Must be &lt; Char.MAX_VALUE = 64k
 69  
    */
 70  
   public static final int threadsMax = 100;
 71  
 
 72  
   static Integer allocatedSessionToken(AccessToken accessToken,
 73  
           PoemTransaction transaction, PoemTask task) {
 74  3442
     synchronized (freeSessionTokenIndices) {
 75  
       Integer index;
 76  3442
       if (freeSessionTokenIndices.size() == 0) {
 77  3
         int i = sessionTokens.size();
 78  3
         if (i >= threadsMax)
 79  0
           throw new TooManyThreadsPoemException();
 80  3
         sessionTokens.setSize(i + 1);
 81  3
         index = new Integer(i);
 82  3
       } else {
 83  3439
         index = (Integer)freeSessionTokenIndices.lastElement();
 84  3439
         freeSessionTokenIndices.setSize(freeSessionTokenIndices.size() - 1);
 85  
       }
 86  
 
 87  3442
       SessionToken token = new SessionToken(Thread.currentThread(),
 88  
               transaction, accessToken, task);
 89  3442
       sessionTokens.setElementAt(token, index.intValue());
 90  
 
 91  3442
       return index;
 92  0
     }
 93  
   }
 94  
 
 95  
   /** Keep track of the old thread names. */
 96  1
   private static Map<Integer, String> threadOldNames = new HashMap<Integer, String>();
 97  
 
 98  
   /**
 99  
    * Do the processing to start a db session.
 100  
    * 
 101  
    * @param accessToken
 102  
    *          The session's token
 103  
    * @param transaction
 104  
    *          The PoemTransaction to run in
 105  
    * @param task
 106  
    *          The PoemTask to run
 107  
    * @throws PoemException
 108  
    *           if we are already in a Session
 109  
    */
 110  
   static void beginSession(AccessToken accessToken,
 111  
           PoemTransaction transaction, PoemTask task) throws PoemException {
 112  3443
     if (inSession())
 113  1
       throw new AlreadyInSessionPoemException();
 114  3442
     Integer token = allocatedSessionToken(accessToken, transaction, task);
 115  3442
     String oldname = Thread.currentThread().getName();
 116  3442
     Thread.currentThread().setName("" + (char)token.intValue());
 117  
     // Save the old thread name for later use
 118  3442
     threadOldNames.put(token, oldname);
 119  3441
   }
 120  
 
 121  
   static void beginSession(AccessToken accessToken, PoemTransaction transaction)
 122  
           throws PoemException {
 123  3
     beginSession(accessToken, transaction, null);
 124  2
   }
 125  
 
 126  
   /**
 127  
    * End a db session.
 128  
    * 
 129  
    * @throws PoemException
 130  
    *           if we are not in a Session
 131  
    */
 132  
   static void endSession() throws PoemException {
 133  3442
     char tokenChar = Thread.currentThread().getName().charAt(0);
 134  3442
     Integer token = new Integer(tokenChar);
 135  3442
     String oldname = (String)threadOldNames.get(token);
 136  3442
     if (oldname == null)
 137  0
       throw new NotInSessionPoemException(Thread.currentThread().getName()
 138  
               + " has null old name");
 139  
 
 140  3442
     Thread.currentThread().setName(oldname);
 141  3442
       synchronized (freeSessionTokenIndices) {
 142  3442
         ((SessionToken)sessionTokens.elementAt(token.intValue())).close();
 143  3442
         sessionTokens.setElementAt(null, token.intValue());
 144  3442
         freeSessionTokenIndices.addElement(token);
 145  3442
       }
 146  3442
     }
 147  
 
 148  
   /**
 149  
    * Perform the specified task in the current thread session.
 150  
    * 
 151  
    * @throws PoemException
 152  
    *           if there is a problem starting or ending the session or if there
 153  
    *           is a problem running the task.
 154  
    */
 155  
   static void inSession(PoemTask task, AccessToken accessToken,
 156  
           PoemTransaction transaction) throws PoemException {
 157  3440
     beginSession(accessToken, transaction, task);
 158  
     try {
 159  3439
       task.run();
 160  
     } finally {
 161  3440
       endSession();
 162  3440
     }
 163  3440
   }
 164  
 
 165  
   /**
 166  
    * Retrieve the open sessions.
 167  
    * 
 168  
    * @return a Vector of open {@link SessionToken}s
 169  
    */
 170  
   public static Vector<SessionToken> openSessions() {
 171  1
     Vector<SessionToken> open = new Vector<SessionToken>();
 172  1
     Enumeration<SessionToken> e = null;
 173  
     // synchronized(sessionTokens) {
 174  1
     e = sessionTokens.elements();
 175  
     // }
 176  2
     while (e.hasMoreElements()) {
 177  1
       SessionToken token = e.nextElement();
 178  1
       if (token != null)
 179  1
         open.addElement(token);
 180  1
     }
 181  1
     return open;
 182  
   }
 183  
 
 184  
   static SessionToken _sessionToken() {
 185  
     // If we are not in a PoemThread then the name is likely
 186  
     // to be "main" or "Thread-1", "Thread-2" etc
 187  246401
     if (Thread.currentThread().getName().length() != 1)
 188  3691
       return null;
 189  485420
     SessionToken context = (SessionToken)sessionTokens.elementAt(Thread
 190  242710
             .currentThread().getName().charAt(0));
 191  242710
     if (context.thread == Thread.currentThread())
 192  242710
       return context;
 193  
     else
 194  0
       return null;
 195  
   }
 196  
 
 197  
   /**
 198  
    * @return the current SessionToken
 199  
    * @throws NotInSessionPoemException if there is no current SessionToken
 200  
    */
 201  
   public static SessionToken sessionToken() throws NotInSessionPoemException {
 202  215809
     SessionToken it = _sessionToken();
 203  215809
     if (it == null)
 204  0
       throw new NotInSessionPoemException();
 205  215809
     return it;
 206  
   }
 207  
 
 208  
   /**
 209  
    * Retrieve the {@link ToTidyList} for this session.
 210  
    * 
 211  
    * @return the {@link ToTidyList} for this {@link PoemThread}.
 212  
    */
 213  
   public static ToTidyList toTidy() throws NotInSessionPoemException {
 214  1
     return sessionToken().toTidy();
 215  
   }
 216  
 
 217  
   /**
 218  
    * Retrieve the {@link PoemTransaction} for this PoemThread.
 219  
    * 
 220  
    * @return the {@link PoemTransaction} for this {@link PoemThread}.
 221  
    */
 222  
   public static PoemTransaction transaction() {
 223  14366
     return sessionToken().transaction;
 224  
   }
 225  
 
 226  
   /**
 227  
    * Whether we are currently in a session.
 228  
    * 
 229  
    * @return whether we are currently in a session
 230  
    */
 231  
   public static boolean inSession() {
 232  30489
     return _sessionToken() != null;
 233  
   }
 234  
 
 235  
   /**
 236  
    * @return the access token under which your thread is running.
 237  
    * @throws NotInSessionPoemException
 238  
    *           if we are not in a session
 239  
    * @throws NoAccessTokenPoemException
 240  
    *           if we do not have an AccessToken
 241  
    */
 242  
   public static AccessToken accessToken() throws NotInSessionPoemException,
 243  
           NoAccessTokenPoemException {
 244  118
     AccessToken it = sessionToken().accessToken;
 245  118
     if (it == null)
 246  0
       throw new NoAccessTokenPoemException();
 247  118
     return it;
 248  
   }
 249  
 
 250  
   /**
 251  
    * Change the access token under which your thread is operating. You can't do
 252  
    * this unless the current token is <TT>root</TT>.
 253  
    * 
 254  
    * @see AccessToken#root
 255  
    */
 256  
   public static void setAccessToken(AccessToken token)
 257  
           throws NonRootSetAccessTokenPoemException {
 258  15
     SessionToken context = sessionToken();
 259  15
     AccessToken old = context.accessToken;
 260  15
     if (old != AccessToken.root)
 261  1
       throw new NonRootSetAccessTokenPoemException(old);
 262  14
     context.accessToken = token;
 263  14
   }
 264  
 
 265  
   /**
 266  
    * Run a {@link PoemTask} under a specified {@link AccessToken}, typically
 267  
    * <tt>Root</tt>.
 268  
    * 
 269  
    * @param token
 270  
    *          the token to run with
 271  
    * @param task
 272  
    *          the task to run
 273  
    */
 274  
   public static void withAccessToken(AccessToken token, PoemTask task) {
 275  4
     SessionToken context = sessionToken();
 276  4
     AccessToken old = context.accessToken;
 277  4
     context.accessToken = token;
 278  
     try {
 279  4
       task.run();
 280  
     } finally {
 281  4
       context.accessToken = old;
 282  4
     }
 283  4
   }
 284  
 
 285  
   /**
 286  
    * Check that we have the given {@link Capability}, throw an
 287  
    * {@link AccessPoemException} if we don't.
 288  
    * 
 289  
    * @param capability
 290  
    *          to check
 291  
    */
 292  
   public static void assertHasCapability(Capability capability)
 293  
           throws NotInSessionPoemException, NoAccessTokenPoemException,
 294  
           AccessPoemException {
 295  3
     AccessToken token = accessToken();
 296  3
     if (!token.givesCapability(capability))
 297  1
       throw new AccessPoemException(token, capability);
 298  2
   }
 299  
 
 300  
   /**
 301  
    * Retrieve the {@link Database} associated with this thread.
 302  
    * 
 303  
    * @return the {@link Database} associated with this thread.
 304  
    */
 305  
   public static Database database() throws NotInSessionPoemException {
 306  6
     return transaction().getDatabase();
 307  
   }
 308  
 
 309  
   /**
 310  
    * Write to the underlying DBMS.
 311  
    * 
 312  
    */
 313  
   public static void writeDown() {
 314  725
     transaction().writeDown();
 315  725
   }
 316  
 
 317  
   /**
 318  
    * Commit to the underlying DBMS.
 319  
    * 
 320  
    */
 321  
   public static void commit() {
 322  1362
     SessionToken token = sessionToken();
 323  
     try {
 324  1362
       token.transaction.commit();
 325  
     } finally {
 326  1362
       token.toTidy().close();
 327  1362
     }
 328  1362
   }
 329  
 
 330  
   /**
 331  
    * Rollback the underlying DBMS.
 332  
    * 
 333  
    */
 334  
   public static void rollback() {
 335  13
     SessionToken token = sessionToken();
 336  
     try {
 337  13
       token.transaction.rollback();
 338  
     } finally {
 339  13
       token.toTidy().close();
 340  13
     }
 341  13
   }
 342  
 }