Coverage Report - org.apache.shiro.session.mgt.SimpleSession
 
Classes in this File Line Coverage Branch Coverage Complexity
SimpleSession
77%
136/176
38%
52/136
2.868
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.shiro.session.mgt;
 20  
 
 21  
 import org.apache.shiro.session.ExpiredSessionException;
 22  
 import org.apache.shiro.session.InvalidSessionException;
 23  
 import org.apache.shiro.session.StoppedSessionException;
 24  
 import org.apache.shiro.util.CollectionUtils;
 25  
 import org.slf4j.Logger;
 26  
 import org.slf4j.LoggerFactory;
 27  
 
 28  
 import java.io.IOException;
 29  
 import java.io.ObjectInputStream;
 30  
 import java.io.ObjectOutputStream;
 31  
 import java.io.Serializable;
 32  
 import java.text.DateFormat;
 33  
 import java.util.*;
 34  
 
 35  
 
 36  
 /**
 37  
  * Simple {@link org.apache.shiro.session.Session} JavaBeans-compatible POJO implementation, intended to be used on the
 38  
  * business/server tier.
 39  
  *
 40  
  * @since 0.1
 41  
  */
 42  
 public class SimpleSession implements ValidatingSession, Serializable {
 43  
 
 44  
     // Serialization reminder:
 45  
     // You _MUST_ change this number if you introduce a change to this class
 46  
     // that is NOT serialization backwards compatible.  Serialization-compatible
 47  
     // changes do not require a change to this number.  If you need to generate
 48  
     // a new number in this case, use the JDK's 'serialver' program to generate it.
 49  
     private static final long serialVersionUID = -7125642695178165650L;
 50  
 
 51  
     //TODO - complete JavaDoc
 52  1
     private transient static final Logger log = LoggerFactory.getLogger(SimpleSession.class);
 53  
 
 54  
     protected static final long MILLIS_PER_SECOND = 1000;
 55  
     protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
 56  
     protected static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
 57  
 
 58  
     //serialization bitmask fields. DO NOT CHANGE THE ORDER THEY ARE DECLARED!
 59  1
     static int bitIndexCounter = 0;
 60  1
     private static final int ID_BIT_MASK = 1 << bitIndexCounter++;
 61  1
     private static final int START_TIMESTAMP_BIT_MASK = 1 << bitIndexCounter++;
 62  1
     private static final int STOP_TIMESTAMP_BIT_MASK = 1 << bitIndexCounter++;
 63  1
     private static final int LAST_ACCESS_TIME_BIT_MASK = 1 << bitIndexCounter++;
 64  1
     private static final int TIMEOUT_BIT_MASK = 1 << bitIndexCounter++;
 65  1
     private static final int EXPIRED_BIT_MASK = 1 << bitIndexCounter++;
 66  1
     private static final int HOST_BIT_MASK = 1 << bitIndexCounter++;
 67  1
     private static final int ATTRIBUTES_BIT_MASK = 1 << bitIndexCounter++;
 68  
 
 69  
     // ==============================================================
 70  
     // NOTICE:
 71  
     //
 72  
     // The following fields are marked as transient to avoid double-serialization.
 73  
     // They are in fact serialized (even though 'transient' usually indicates otherwise),
 74  
     // but they are serialized explicitly via the writeObject and readObject implementations
 75  
     // in this class.
 76  
     //
 77  
     // If we didn't declare them as transient, the out.defaultWriteObject(); call in writeObject would
 78  
     // serialize all non-transient fields as well, effectively doubly serializing the fields (also
 79  
     // doubling the serialization size).
 80  
     //
 81  
     // This finding, with discussion, was covered here:
 82  
     //
 83  
     // http://mail-archives.apache.org/mod_mbox/shiro-user/201109.mbox/%3C4E81BCBD.8060909@metaphysis.net%3E
 84  
     //
 85  
     // ==============================================================
 86  
     private transient Serializable id;
 87  
     private transient Date startTimestamp;
 88  
     private transient Date stopTimestamp;
 89  
     private transient Date lastAccessTime;
 90  
     private transient long timeout;
 91  
     private transient boolean expired;
 92  
     private transient String host;
 93  
     private transient Map<Object, Object> attributes;
 94  
 
 95  33
     public SimpleSession() {
 96  33
         this.timeout = DefaultSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT; //TODO - remove concrete reference to DefaultSessionManager
 97  33
         this.startTimestamp = new Date();
 98  33
         this.lastAccessTime = this.startTimestamp;
 99  33
     }
 100  
 
 101  
     public SimpleSession(String host) {
 102  2
         this();
 103  2
         this.host = host;
 104  2
     }
 105  
 
 106  
     public Serializable getId() {
 107  209
         return this.id;
 108  
     }
 109  
 
 110  
     public void setId(Serializable id) {
 111  30
         this.id = id;
 112  30
     }
 113  
 
 114  
     public Date getStartTimestamp() {
 115  2
         return startTimestamp;
 116  
     }
 117  
 
 118  
     public void setStartTimestamp(Date startTimestamp) {
 119  1
         this.startTimestamp = startTimestamp;
 120  1
     }
 121  
 
 122  
     /**
 123  
      * Returns the time the session was stopped, or <tt>null</tt> if the session is still active.
 124  
      * <p/>
 125  
      * A session may become stopped under a number of conditions:
 126  
      * <ul>
 127  
      * <li>If the user logs out of the system, their current session is terminated (released).</li>
 128  
      * <li>If the session expires</li>
 129  
      * <li>The application explicitly calls {@link #stop()}</li>
 130  
      * <li>If there is an internal system error and the session state can no longer accurately
 131  
      * reflect the user's behavior, such in the case of a system crash</li>
 132  
      * </ul>
 133  
      * <p/>
 134  
      * Once stopped, a session may no longer be used.  It is locked from all further activity.
 135  
      *
 136  
      * @return The time the session was stopped, or <tt>null</tt> if the session is still
 137  
      *         active.
 138  
      */
 139  
     public Date getStopTimestamp() {
 140  222
         return stopTimestamp;
 141  
     }
 142  
 
 143  
     public void setStopTimestamp(Date stopTimestamp) {
 144  0
         this.stopTimestamp = stopTimestamp;
 145  0
     }
 146  
 
 147  
     public Date getLastAccessTime() {
 148  214
         return lastAccessTime;
 149  
     }
 150  
 
 151  
     public void setLastAccessTime(Date lastAccessTime) {
 152  13
         this.lastAccessTime = lastAccessTime;
 153  13
     }
 154  
 
 155  
     /**
 156  
      * Returns true if this session has expired, false otherwise.  If the session has
 157  
      * expired, no further user interaction with the system may be done under this session.
 158  
      *
 159  
      * @return true if this session has expired, false otherwise.
 160  
      */
 161  
     public boolean isExpired() {
 162  211
         return expired;
 163  
     }
 164  
 
 165  
     public void setExpired(boolean expired) {
 166  6
         this.expired = expired;
 167  6
     }
 168  
 
 169  
     public long getTimeout() {
 170  217
         return timeout;
 171  
     }
 172  
 
 173  
     public void setTimeout(long timeout) {
 174  32
         this.timeout = timeout;
 175  32
     }
 176  
 
 177  
     public String getHost() {
 178  29
         return host;
 179  
     }
 180  
 
 181  
     public void setHost(String host) {
 182  0
         this.host = host;
 183  0
     }
 184  
 
 185  
     public Map<Object, Object> getAttributes() {
 186  182
         return attributes;
 187  
     }
 188  
 
 189  
     public void setAttributes(Map<Object, Object> attributes) {
 190  21
         this.attributes = attributes;
 191  21
     }
 192  
 
 193  
     public void touch() {
 194  1
         this.lastAccessTime = new Date();
 195  1
     }
 196  
 
 197  
     public void stop() {
 198  18
         if (this.stopTimestamp == null) {
 199  18
             this.stopTimestamp = new Date();
 200  
         }
 201  18
     }
 202  
 
 203  
     protected boolean isStopped() {
 204  210
         return getStopTimestamp() != null;
 205  
     }
 206  
 
 207  
     protected void expire() {
 208  6
         stop();
 209  6
         this.expired = true;
 210  6
     }
 211  
 
 212  
     /**
 213  
      * @since 0.9
 214  
      */
 215  
     public boolean isValid() {
 216  4
         return !isStopped() && !isExpired();
 217  
     }
 218  
 
 219  
     /**
 220  
      * Determines if this session is expired.
 221  
      *
 222  
      * @return true if the specified session has expired, false otherwise.
 223  
      */
 224  
     protected boolean isTimedOut() {
 225  
 
 226  206
         if (isExpired()) {
 227  0
             return true;
 228  
         }
 229  
 
 230  206
         long timeout = getTimeout();
 231  
 
 232  206
         if (timeout >= 0l) {
 233  
 
 234  206
             Date lastAccessTime = getLastAccessTime();
 235  
 
 236  206
             if (lastAccessTime == null) {
 237  0
                 String msg = "session.lastAccessTime for session with id [" +
 238  
                         getId() + "] is null.  This value must be set at " +
 239  
                         "least once, preferably at least upon instantiation.  Please check the " +
 240  
                         getClass().getName() + " implementation and ensure " +
 241  
                         "this value will be set (perhaps in the constructor?)";
 242  0
                 throw new IllegalStateException(msg);
 243  
             }
 244  
 
 245  
             // Calculate at what time a session would have been last accessed
 246  
             // for it to be expired at this point.  In other words, subtract
 247  
             // from the current time the amount of time that a session can
 248  
             // be inactive before expiring.  If the session was last accessed
 249  
             // before this time, it is expired.
 250  206
             long expireTimeMillis = System.currentTimeMillis() - timeout;
 251  206
             Date expireTime = new Date(expireTimeMillis);
 252  206
             return lastAccessTime.before(expireTime);
 253  
         } else {
 254  0
             if (log.isTraceEnabled()) {
 255  0
                 log.trace("No timeout for session with id [" + getId() +
 256  
                         "].  Session is not considered expired.");
 257  
             }
 258  
         }
 259  
 
 260  0
         return false;
 261  
     }
 262  
 
 263  
     public void validate() throws InvalidSessionException {
 264  
         //check for stopped:
 265  206
         if (isStopped()) {
 266  
             //timestamp is set, so the session is considered stopped:
 267  0
             String msg = "Session with id [" + getId() + "] has been " +
 268  
                     "explicitly stopped.  No further interaction under this session is " +
 269  
                     "allowed.";
 270  0
             throw new StoppedSessionException(msg);
 271  
         }
 272  
 
 273  
         //check for expiration
 274  206
         if (isTimedOut()) {
 275  6
             expire();
 276  
 
 277  
             //throw an exception explaining details of why it expired:
 278  6
             Date lastAccessTime = getLastAccessTime();
 279  6
             long timeout = getTimeout();
 280  
 
 281  6
             Serializable sessionId = getId();
 282  
 
 283  6
             DateFormat df = DateFormat.getInstance();
 284  6
             String msg = "Session with id [" + sessionId + "] has expired. " +
 285  
                     "Last access time: " + df.format(lastAccessTime) +
 286  
                     ".  Current time: " + df.format(new Date()) +
 287  
                     ".  Session timeout is set to " + timeout / MILLIS_PER_SECOND + " seconds (" +
 288  
                     timeout / MILLIS_PER_MINUTE + " minutes)";
 289  6
             if (log.isTraceEnabled()) {
 290  6
                 log.trace(msg);
 291  
             }
 292  6
             throw new ExpiredSessionException(msg);
 293  
         }
 294  200
     }
 295  
 
 296  
     private Map<Object, Object> getAttributesLazy() {
 297  45
         Map<Object, Object> attributes = getAttributes();
 298  45
         if (attributes == null) {
 299  21
             attributes = new HashMap<Object, Object>();
 300  21
             setAttributes(attributes);
 301  
         }
 302  45
         return attributes;
 303  
     }
 304  
 
 305  
     public Collection<Object> getAttributeKeys() throws InvalidSessionException {
 306  1
         Map<Object, Object> attributes = getAttributes();
 307  1
         if (attributes == null) {
 308  0
             return Collections.emptySet();
 309  
         }
 310  1
         return attributes.keySet();
 311  
     }
 312  
 
 313  
     public Object getAttribute(Object key) {
 314  108
         Map<Object, Object> attributes = getAttributes();
 315  108
         if (attributes == null) {
 316  2
             return null;
 317  
         }
 318  106
         return attributes.get(key);
 319  
     }
 320  
 
 321  
     public void setAttribute(Object key, Object value) {
 322  45
         if (value == null) {
 323  0
             removeAttribute(key);
 324  
         } else {
 325  45
             getAttributesLazy().put(key, value);
 326  
         }
 327  45
     }
 328  
 
 329  
     public Object removeAttribute(Object key) {
 330  28
         Map<Object, Object> attributes = getAttributes();
 331  28
         if (attributes == null) {
 332  3
             return null;
 333  
         } else {
 334  25
             return attributes.remove(key);
 335  
         }
 336  
     }
 337  
 
 338  
     /**
 339  
      * Returns {@code true} if the specified argument is an {@code instanceof} {@code SimpleSession} and both
 340  
      * {@link #getId() id}s are equal.  If the argument is a {@code SimpleSession} and either 'this' or the argument
 341  
      * does not yet have an ID assigned, the value of {@link #onEquals(SimpleSession) onEquals} is returned, which
 342  
      * does a necessary attribute-based comparison when IDs are not available.
 343  
      * <p/>
 344  
      * Do your best to ensure {@code SimpleSession} instances receive an ID very early in their lifecycle to
 345  
      * avoid the more expensive attributes-based comparison.
 346  
      *
 347  
      * @param obj the object to compare with this one for equality.
 348  
      * @return {@code true} if this object is equivalent to the specified argument, {@code false} otherwise.
 349  
      */
 350  
     @Override
 351  
     public boolean equals(Object obj) {
 352  5
         if (this == obj) {
 353  5
             return true;
 354  
         }
 355  0
         if (obj instanceof SimpleSession) {
 356  0
             SimpleSession other = (SimpleSession) obj;
 357  0
             Serializable thisId = getId();
 358  0
             Serializable otherId = other.getId();
 359  0
             if (thisId != null && otherId != null) {
 360  0
                 return thisId.equals(otherId);
 361  
             } else {
 362  
                 //fall back to an attribute based comparison:
 363  0
                 return onEquals(other);
 364  
             }
 365  
         }
 366  0
         return false;
 367  
     }
 368  
 
 369  
     /**
 370  
      * Provides an attribute-based comparison (no ID comparison) - incurred <em>only</em> when 'this' or the
 371  
      * session object being compared for equality do not have a session id.
 372  
      *
 373  
      * @param ss the SimpleSession instance to compare for equality.
 374  
      * @return true if all the attributes, except the id, are equal to this object's attributes.
 375  
      * @since 1.0
 376  
      */
 377  
     protected boolean onEquals(SimpleSession ss) {
 378  0
         return (getStartTimestamp() != null ? getStartTimestamp().equals(ss.getStartTimestamp()) : ss.getStartTimestamp() == null) &&
 379  
                 (getStopTimestamp() != null ? getStopTimestamp().equals(ss.getStopTimestamp()) : ss.getStopTimestamp() == null) &&
 380  
                 (getLastAccessTime() != null ? getLastAccessTime().equals(ss.getLastAccessTime()) : ss.getLastAccessTime() == null) &&
 381  
                 (getTimeout() == ss.getTimeout()) &&
 382  
                 (isExpired() == ss.isExpired()) &&
 383  
                 (getHost() != null ? getHost().equals(ss.getHost()) : ss.getHost() == null) &&
 384  
                 (getAttributes() != null ? getAttributes().equals(ss.getAttributes()) : ss.getAttributes() == null);
 385  
     }
 386  
 
 387  
     /**
 388  
      * Returns the hashCode.  If the {@link #getId() id} is not {@code null}, its hashcode is returned immediately.
 389  
      * If it is {@code null}, an attributes-based hashCode will be calculated and returned.
 390  
      * <p/>
 391  
      * Do your best to ensure {@code SimpleSession} instances receive an ID very early in their lifecycle to
 392  
      * avoid the more expensive attributes-based calculation.
 393  
      *
 394  
      * @return this object's hashCode
 395  
      * @since 1.0
 396  
      */
 397  
     @Override
 398  
     public int hashCode() {
 399  0
         Serializable id = getId();
 400  0
         if (id != null) {
 401  0
             return id.hashCode();
 402  
         }
 403  0
         int hashCode = getStartTimestamp() != null ? getStartTimestamp().hashCode() : 0;
 404  0
         hashCode = 31 * hashCode + (getStopTimestamp() != null ? getStopTimestamp().hashCode() : 0);
 405  0
         hashCode = 31 * hashCode + (getLastAccessTime() != null ? getLastAccessTime().hashCode() : 0);
 406  0
         hashCode = 31 * hashCode + Long.valueOf(Math.max(getTimeout(), 0)).hashCode();
 407  0
         hashCode = 31 * hashCode + Boolean.valueOf(isExpired()).hashCode();
 408  0
         hashCode = 31 * hashCode + (getHost() != null ? getHost().hashCode() : 0);
 409  0
         hashCode = 31 * hashCode + (getAttributes() != null ? getAttributes().hashCode() : 0);
 410  0
         return hashCode;
 411  
     }
 412  
 
 413  
     /**
 414  
      * Returns the string representation of this SimpleSession, equal to
 415  
      * <code>getClass().getName() + &quot;,id=&quot; + getId()</code>.
 416  
      *
 417  
      * @return the string representation of this SimpleSession, equal to
 418  
      *         <code>getClass().getName() + &quot;,id=&quot; + getId()</code>.
 419  
      * @since 1.0
 420  
      */
 421  
     @Override
 422  
     public String toString() {
 423  28
         StringBuilder sb = new StringBuilder();
 424  28
         sb.append(getClass().getName()).append(",id=").append(getId());
 425  28
         return sb.toString();
 426  
     }
 427  
 
 428  
     /**
 429  
      * Serializes this object to the specified output stream for JDK Serialization.
 430  
      *
 431  
      * @param out output stream used for Object serialization.
 432  
      * @throws IOException if any of this object's fields cannot be written to the stream.
 433  
      * @since 1.0
 434  
      */
 435  
     private void writeObject(ObjectOutputStream out) throws IOException {
 436  3
         out.defaultWriteObject();
 437  3
         short alteredFieldsBitMask = getAlteredFieldsBitMask();
 438  3
         out.writeShort(alteredFieldsBitMask);
 439  3
         if (id != null) {
 440  0
             out.writeObject(id);
 441  
         }
 442  3
         if (startTimestamp != null) {
 443  3
             out.writeObject(startTimestamp);
 444  
         }
 445  3
         if (stopTimestamp != null) {
 446  0
             out.writeObject(stopTimestamp);
 447  
         }
 448  3
         if (lastAccessTime != null) {
 449  3
             out.writeObject(lastAccessTime);
 450  
         }
 451  3
         if (timeout != 0l) {
 452  3
             out.writeLong(timeout);
 453  
         }
 454  3
         if (expired) {
 455  1
             out.writeBoolean(expired);
 456  
         }
 457  3
         if (host != null) {
 458  1
             out.writeUTF(host);
 459  
         }
 460  3
         if (!CollectionUtils.isEmpty(attributes)) {
 461  0
             out.writeObject(attributes);
 462  
         }
 463  3
     }
 464  
 
 465  
     /**
 466  
      * Reconstitutes this object based on the specified InputStream for JDK Serialization.
 467  
      *
 468  
      * @param in the input stream to use for reading data to populate this object.
 469  
      * @throws IOException            if the input stream cannot be used.
 470  
      * @throws ClassNotFoundException if a required class needed for instantiation is not available in the present JVM
 471  
      * @since 1.0
 472  
      */
 473  
     @SuppressWarnings({"unchecked"})
 474  
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 475  3
         in.defaultReadObject();
 476  3
         short bitMask = in.readShort();
 477  
 
 478  3
         if (isFieldPresent(bitMask, ID_BIT_MASK)) {
 479  0
             this.id = (Serializable) in.readObject();
 480  
         }
 481  3
         if (isFieldPresent(bitMask, START_TIMESTAMP_BIT_MASK)) {
 482  3
             this.startTimestamp = (Date) in.readObject();
 483  
         }
 484  3
         if (isFieldPresent(bitMask, STOP_TIMESTAMP_BIT_MASK)) {
 485  0
             this.stopTimestamp = (Date) in.readObject();
 486  
         }
 487  3
         if (isFieldPresent(bitMask, LAST_ACCESS_TIME_BIT_MASK)) {
 488  3
             this.lastAccessTime = (Date) in.readObject();
 489  
         }
 490  3
         if (isFieldPresent(bitMask, TIMEOUT_BIT_MASK)) {
 491  3
             this.timeout = in.readLong();
 492  
         }
 493  3
         if (isFieldPresent(bitMask, EXPIRED_BIT_MASK)) {
 494  1
             this.expired = in.readBoolean();
 495  
         }
 496  3
         if (isFieldPresent(bitMask, HOST_BIT_MASK)) {
 497  1
             this.host = in.readUTF();
 498  
         }
 499  3
         if (isFieldPresent(bitMask, ATTRIBUTES_BIT_MASK)) {
 500  0
             this.attributes = (Map<Object, Object>) in.readObject();
 501  
         }
 502  3
     }
 503  
 
 504  
     /**
 505  
      * Returns a bit mask used during serialization indicating which fields have been serialized. Fields that have been
 506  
      * altered (not null and/or not retaining the class defaults) will be serialized and have 1 in their respective
 507  
      * index, fields that are null and/or retain class default values have 0.
 508  
      *
 509  
      * @return a bit mask used during serialization indicating which fields have been serialized.
 510  
      * @since 1.0
 511  
      */
 512  
     private short getAlteredFieldsBitMask() {
 513  3
         int bitMask = 0;
 514  3
         bitMask = id != null ? bitMask | ID_BIT_MASK : bitMask;
 515  3
         bitMask = startTimestamp != null ? bitMask | START_TIMESTAMP_BIT_MASK : bitMask;
 516  3
         bitMask = stopTimestamp != null ? bitMask | STOP_TIMESTAMP_BIT_MASK : bitMask;
 517  3
         bitMask = lastAccessTime != null ? bitMask | LAST_ACCESS_TIME_BIT_MASK : bitMask;
 518  3
         bitMask = timeout != 0l ? bitMask | TIMEOUT_BIT_MASK : bitMask;
 519  3
         bitMask = expired ? bitMask | EXPIRED_BIT_MASK : bitMask;
 520  3
         bitMask = host != null ? bitMask | HOST_BIT_MASK : bitMask;
 521  3
         bitMask = !CollectionUtils.isEmpty(attributes) ? bitMask | ATTRIBUTES_BIT_MASK : bitMask;
 522  3
         return (short) bitMask;
 523  
     }
 524  
 
 525  
     /**
 526  
      * Returns {@code true} if the given {@code bitMask} argument indicates that the specified field has been
 527  
      * serialized and therefore should be read during deserialization, {@code false} otherwise.
 528  
      *
 529  
      * @param bitMask      the aggregate bitmask for all fields that have been serialized.  Individual bits represent
 530  
      *                     the fields that have been serialized.  A bit set to 1 means that corresponding field has
 531  
      *                     been serialized, 0 means it hasn't been serialized.
 532  
      * @param fieldBitMask the field bit mask constant identifying which bit to inspect (corresponds to a class attribute).
 533  
      * @return {@code true} if the given {@code bitMask} argument indicates that the specified field has been
 534  
      *         serialized and therefore should be read during deserialization, {@code false} otherwise.
 535  
      * @since 1.0
 536  
      */
 537  
     private static boolean isFieldPresent(short bitMask, int fieldBitMask) {
 538  24
         return (bitMask & fieldBitMask) != 0;
 539  
     }
 540  
 
 541  
 }