View Javadoc

    1   package org.apache.turbine.services.security.ldap;
    2
    3   /*
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
    10   * with the License.  You may obtain a copy of the License at
    11   *
    12   *   http://www.apache.org/licenses/LICENSE-2.0
    13   *
    14   * Unless required by applicable law or agreed to in writing,
    15   * software distributed under the License is distributed on an
    16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    17   * KIND, either express or implied.  See the License for the
    18   * specific language governing permissions and limitations
    19   * under the License.
    20   */
    21
    22  import java.io.ByteArrayOutputStream;
    23  import java.io.PrintWriter;
    24  import java.sql.Connection;
    25  import java.util.Hashtable;
    26
    27  import javax.naming.NamingException;
    28  import javax.naming.directory.Attribute;
    29  import javax.naming.directory.Attributes;
    30  import javax.naming.directory.BasicAttribute;
    31  import javax.naming.directory.BasicAttributes;
    32  import javax.servlet.http.HttpSessionBindingEvent;
    33
    34  import org.apache.commons.logging.Log;
    35  import org.apache.commons.logging.LogFactory;
    36  import org.apache.turbine.om.security.User;
    37  import org.apache.turbine.services.security.TurbineSecurity;
    38
    39  /**
    40   * LDAPUser implements User and provides access to a user who accesses the
    41   * system via LDAP.
    42   *
    43   * @author <a href="mailto:cberry@gluecode.com">Craig D. Berry</a>
    44   * @author <a href="mailto:tadewunmi@gluecode.com">Tracy M. Adewunmi</a>
    45   * @author <a href="mailto:lflournoy@gluecode.com">Leonard J. Flournoy </a>
    46   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
    47   * @author <a href="mailto:hhernandez@itweb.com.mx">Humberto Hernandez</a>
    48   */
    49  public class LDAPUser implements User
    50  {
    51
    52      /** Logging */
    53      private static Log log = LogFactory.getLog(LDAPUser.class);
    54
    55      /* A few attributes common to a User. */
    56
    57      /** Date when the user was created */
    58      private java.util.Date createDate = null;
    59
    60      /** Date when the user was last accessed */
    61      private java.util.Date lastAccessDate = null;
    62
    63      /** timeout */
    64      private int timeout = 15;
    65
    66      /** This is data that will survive a servlet engine restart. */
    67      private Hashtable permStorage = null;
    68
    69      /** This is data that will not survive a servlet engine restart. */
    70      private Hashtable tempStorage = null;
    71
    72      /**
    73       * Constructor.
    74       * Create a new User and set the createDate.
    75       */
    76      public LDAPUser()
    77      {
    78          createDate = new java.util.Date();
    79          tempStorage = new Hashtable(10);
    80          permStorage = new Hashtable(10);
    81          setHasLoggedIn(Boolean.FALSE);
    82      }
    83
    84      /**
    85       * Populates the user with values obtained from the LDAP Service.
    86       * This method could be redefined in subclasses.
    87       * @param attribs The attributes obtained from LDAP.
    88       * @throws NamingException if there was an error with JNDI.
    89       */
    90      public void setLDAPAttributes(Attributes attribs)
    91              throws NamingException
    92      {
    93
    94          Attribute attr;
    95          String attrName;
    96
    97          // Set the User id.
    98          attrName = LDAPSecurityConstants.getUserIdAttribute();
    99          if (attrName != null)
    100         {
    101             attr = attribs.get(attrName);
    102             if (attr != null && attr.get() != null)
    103             {
    104                 try
    105                 {
    106                     //setPrimaryKey(attr.get().toString());
    107                 }
    108                 catch (Exception ex)
    109                 {
    110                     log.error("Exception caught:", ex);
    111                 }
    112             }
    113         }
    114
    115         // Set the Username.
    116         attrName = LDAPSecurityConstants.getNameAttribute();
    117         if (attrName != null)
    118         {
    119             attr = attribs.get(attrName);
    120             if (attr != null && attr.get() != null)
    121             {
    122                 setName(attr.get().toString());
    123             }
    124         }
    125         else
    126         {
    127             log.error("There is no LDAP attribute for the username.");
    128         }
    129
    130         // Set the Firstname.
    131         attrName = LDAPSecurityConstants.getFirstNameAttribute();
    132         if (attrName != null)
    133         {
    134             attr = attribs.get(attrName);
    135             if (attr != null && attr.get() != null)
    136             {
    137                 setFirstName(attr.get().toString());
    138             }
    139         }
    140
    141         // Set the Lastname.
    142         attrName = LDAPSecurityConstants.getLastNameAttribute();
    143         if (attrName != null)
    144         {
    145             attr = attribs.get(attrName);
    146             if (attr != null && attr.get() != null)
    147             {
    148                 setLastName(attr.get().toString());
    149             }
    150         }
    151
    152         // Set the E-Mail
    153         attrName = LDAPSecurityConstants.getEmailAttribute();
    154         if (attrName != null)
    155         {
    156             attr = attribs.get(attrName);
    157             if (attr != null && attr.get() != null)
    158             {
    159                 setEmail(attr.get().toString());
    160             }
    161         }
    162     }
    163
    164     /**
    165      * Get the JNDI Attributes used to store the user in LDAP.
    166      * This method could be redefined in a subclass.
    167      *
    168      * @throws NamingException if there is a JNDI error.
    169      * @return The JNDI attributes of the user.
    170      */
    171     public Attributes getLDAPAttributes()
    172             throws NamingException
    173     {
    174         Attributes attribs = new BasicAttributes();
    175         String attrName;
    176
    177         // Set the objectClass
    178         attrName = "objectClass";
    179         if (attrName != null)
    180         {
    181             Object value = "turbineUser";
    182
    183             if (value != null)
    184             {
    185                 Attribute attr = new BasicAttribute(attrName, value);
    186
    187                 attribs.put(attr);
    188             }
    189         }
    190
    191         // Set the User id.
    192         attrName = LDAPSecurityConstants.getUserIdAttribute();
    193         if (attrName != null)
    194         {
    195             Object value = this.getIdAsObj();
    196
    197             if (value != null)
    198             {
    199                 Attribute attr = new BasicAttribute(attrName, value);
    200
    201                 attribs.put(attr);
    202             }
    203         }
    204
    205         // Set the Username.
    206         attrName = LDAPSecurityConstants.getNameAttribute();
    207         if (attrName != null)
    208         {
    209             Object value = getName();
    210
    211             if (value != null)
    212             {
    213                 Attribute attr = new BasicAttribute(attrName, value);
    214
    215                 attribs.put(attr);
    216             }
    217         }
    218
    219         // Set the Firstname.
    220         attrName = LDAPSecurityConstants.getFirstNameAttribute();
    221         if (attrName != null)
    222         {
    223             Object value = getFirstName();
    224
    225             if (value != null)
    226             {
    227                 Attribute attr = new BasicAttribute(attrName, value);
    228
    229                 attribs.put(attr);
    230             }
    231         }
    232
    233         // Set the Lastname.
    234         attrName = LDAPSecurityConstants.getLastNameAttribute();
    235         if (attrName != null)
    236         {
    237             Object value = getLastName();
    238
    239             if (value != null)
    240             {
    241                 Attribute attr = new BasicAttribute(attrName, value);
    242
    243                 attribs.put(attr);
    244             }
    245         }
    246
    247         // Set the E-Mail.
    248         attrName = LDAPSecurityConstants.getEmailAttribute();
    249         if (attrName != null)
    250         {
    251             Object value = getEmail();
    252
    253             if (value != null)
    254             {
    255                 Attribute attr = new BasicAttribute(attrName, value);
    256
    257                 attribs.put(attr);
    258             }
    259         }
    260
    261         // Set the Password
    262         attrName = LDAPSecurityConstants.getPasswordAttribute();
    263         if (attrName != null)
    264         {
    265             Object value = getPassword();
    266
    267             if (value != null)
    268             {
    269                 Attribute attr = new BasicAttribute(attrName, value);
    270
    271                 attribs.put(attr);
    272             }
    273         }
    274
    275         return attribs;
    276     }
    277
    278     /**
    279      * Gets the distinguished name (DN) of the User.
    280      * This method could be redefined in a subclass.
    281      * @return The Distinguished Name of the user.
    282      */
    283     public String getDN()
    284     {
    285         String filterAttribute = LDAPSecurityConstants.getNameAttribute();
    286         String userBaseSearch = LDAPSecurityConstants.getBaseSearch();
    287         String userName = getName();
    288
    289         String dn = filterAttribute + "=" + userName + "," + userBaseSearch;
    290
    291         return dn;
    292     }
    293
    294     /**
    295      * Gets the access counter for a user during a session.
    296      *
    297      * @return The access counter for the user for the session.
    298      */
    299     public int getAccessCounterForSession()
    300     {
    301         try
    302         {
    303             return ((Integer) getTemp(User.SESSION_ACCESS_COUNTER)).intValue();
    304         }
    305         catch (Exception e)
    306         {
    307             return 0;
    308         }
    309     }
    310
    311     /**
    312      * Gets the access counter for a user from perm storage.
    313      *
    314      * @return The access counter for the user.
    315      */
    316     public int getAccessCounter()
    317     {
    318         try
    319         {
    320             return ((Integer) getPerm(User.ACCESS_COUNTER)).intValue();
    321         }
    322         catch (Exception e)
    323         {
    324             return 0;
    325         }
    326     }
    327
    328     /**
    329      * Gets the create date for this User.  This is the time at which
    330      * the user object was created.
    331      *
    332      * @return A Java Date with the date of creation for the user.
    333      */
    334     public java.util.Date getCreateDate()
    335     {
    336         return createDate;
    337     }
    338
    339     /**
    340      * Returns the value of Confirmed variable
    341      * @return the confirm value.
    342      */
    343     public String getConfirmed()
    344     {
    345         String tmp = null;
    346
    347         tmp = (String) getPerm(User.CONFIRM_VALUE);
    348         if (tmp != null && tmp.length() == 0)
    349         {
    350             tmp = null;
    351         }
    352         return tmp;
    353     }
    354
    355     /**
    356      * Returns the Email for this user.  If this is defined, then
    357      * the user is considered logged in.
    358      *
    359      * @return A String with the user's Email.
    360      */
    361     public String getEmail()
    362     {
    363         String tmp = null;
    364
    365         tmp = (String) getPerm(User.EMAIL);
    366         if (tmp != null && tmp.length() == 0)
    367         {
    368             tmp = null;
    369         }
    370         return tmp;
    371     }
    372
    373     /**
    374      * Gets the last access date for this User.  This is the last time
    375      * that the user object was referenced.
    376      *
    377      * @return A Java Date with the last access date for the user.
    378      */
    379     public java.util.Date getLastAccessDate()
    380     {
    381         if (lastAccessDate == null)
    382         {
    383             setLastAccessDate();
    384         }
    385         return lastAccessDate;
    386     }
    387
    388     /**
    389      * Get last login date/time for this user.
    390      *
    391      * @return A Java Date with the last login date for the user.
    392      */
    393     public java.util.Date getLastLogin()
    394     {
    395         return (java.util.Date) getPerm(User.LAST_LOGIN);
    396     }
    397
    398     /**
    399      * Get password for this user.
    400      *
    401      * @return A String with the password for the user.
    402      */
    403     public String getPassword()
    404     {
    405         return (String) getPerm(User.PASSWORD);
    406     }
    407
    408     /**
    409      * Get an object from permanent storage.
    410      * @param name The object's name.
    411      * @return An Object with the given name.
    412      */
    413     public Object getPerm(String name)
    414     {
    415         return permStorage.get(name);
    416     }
    417
    418     /**
    419      * Get an object from permanent storage; return default if value
    420      * is null.
    421      *
    422      * @param name The object's name.
    423      * @param def A default value to return.
    424      * @return An Object with the given name.
    425      */
    426     public Object getPerm(String name, Object def)
    427     {
    428         try
    429         {
    430             Object val = permStorage.get(name);
    431
    432             if (val == null)
    433             {
    434                 return def;
    435             }
    436             return val;
    437         }
    438         catch (Exception e)
    439         {
    440             return def;
    441         }
    442     }
    443
    444     /**
    445      * This should only be used in the case where we want to save the
    446      * data to the database.
    447      *
    448      * @return A Hashtable.
    449      */
    450     public Hashtable getPermStorage()
    451     {
    452         if (this.permStorage == null)
    453         {
    454             this.permStorage = new Hashtable();
    455         }
    456         return this.permStorage;
    457     }
    458
    459     /**
    460      * Get an object from temporary storage.
    461      *
    462      * @param name The object's name.
    463      * @return An Object with the given name.
    464      */
    465     public Object getTemp(String name)
    466     {
    467         return tempStorage.get(name);
    468     }
    469
    470     /**
    471      * Get an object from temporary storage; return default if value
    472      * is null.
    473      *
    474      * @param name The object's name.
    475      * @param def A default value to return.
    476      * @return An Object with the given name.
    477      */
    478     public Object getTemp(String name, Object def)
    479     {
    480         Object val;
    481
    482         try
    483         {
    484             val = tempStorage.get(name);
    485             if (val == null)
    486             {
    487                 val = def;
    488             }
    489         }
    490         catch (Exception e)
    491         {
    492             val = def;
    493         }
    494         return val;
    495     }
    496
    497     /**
    498      * A User object can have a variable Timeout, which is defined in
    499      * minutes.  If the user has been timed out, then the
    500      * hasLoggedIn() value will return false.
    501      *
    502      * @return An int specifying the timeout.
    503      */
    504     public int getTimeout()
    505     {
    506         return this.timeout;
    507     }
    508
    509
    510     /**
    511      * Returns the first name for this user.  If this is defined, then
    512      * the user is considered logged in.
    513      *
    514      * @return A String with the user's first name.
    515      */
    516     public String getFirstName()
    517     {
    518         String tmp = null;
    519
    520         tmp = (String) getPerm(User.FIRST_NAME);
    521         if (tmp != null && tmp.length() == 0)
    522         {
    523             tmp = null;
    524         }
    525         return tmp;
    526     }
    527
    528     /**
    529      * Returns the last name for this user.  If this is defined, then
    530      * the user is considered logged in.
    531      *
    532      * @return A String with the user's last name.
    533      */
    534     public String getLastName()
    535     {
    536         String tmp = null;
    537
    538         tmp = (String) getPerm(User.LAST_NAME);
    539         if (tmp != null && tmp.length() == 0)
    540         {
    541             tmp = null;
    542         }
    543         return tmp;
    544     }
    545
    546     /**
    547      * The user is considered logged in if they have not timed out.
    548      *
    549      * @return True if the user has logged in.
    550      */
    551     public boolean hasLoggedIn()
    552     {
    553         Boolean tmp = getHasLoggedIn();
    554
    555         if (tmp != null && tmp.booleanValue())
    556         {
    557             return true;
    558         }
    559         else
    560         {
    561             return false;
    562         }
    563     }
    564
    565     /**
    566      * This method reports whether or not the user has been confirmed
    567      * in the system by checking the <code>CONFIRM_VALUE</code>
    568      * column to see if it is equal to <code>CONFIRM_DATA</code>.
    569      *
    570      * @return True if the user has been confirmed.
    571      */
    572     public boolean isConfirmed()
    573     {
    574         return ((String) getTemp(CONFIRM_VALUE, "")).equals(CONFIRM_DATA);
    575     }
    576
    577     /**
    578      * Increments the permanent hit counter for the user.
    579      */
    580     public void incrementAccessCounter()
    581     {
    582         setAccessCounter(getAccessCounter() + 1);
    583     }
    584
    585     /**
    586      * Increments the session hit counter for the user.
    587      */
    588     public void incrementAccessCounterForSession()
    589     {
    590         setAccessCounterForSession(getAccessCounterForSession() + 1);
    591     }
    592
    593     /**
    594      * Remove an object from temporary storage and return the object.
    595      *
    596      * @param name The name of the object to remove.
    597      * @return An Object.
    598      */
    599     public Object removeTemp(String name)
    600     {
    601         return tempStorage.remove(name);
    602     }
    603
    604     /**
    605      * Sets the access counter for a user, saved in perm storage.
    606      *
    607      * @param cnt The new count.
    608      */
    609     public void setAccessCounter(int cnt)
    610     {
    611         setPerm(User.ACCESS_COUNTER, new Integer(cnt));
    612     }
    613
    614     /**
    615      * Sets the session access counter for a user, saved in temp
    616      * storage.
    617      *
    618      * @param cnt The new count.
    619      */
    620     public void setAccessCounterForSession(int cnt)
    621     {
    622         setTemp(User.SESSION_ACCESS_COUNTER, new Integer(cnt));
    623     }
    624
    625     /**
    626      * Set the users confirmed variable
    627      *
    628      * @param confirm The new confim value.
    629      */
    630     public void setConfirmed(String confirm)
    631     {
    632         getPerm(User.CONFIRM_VALUE, confirm);
    633     }
    634
    635     /**
    636      * Sets the last access date for this User. This is the last time
    637      * that the user object was referenced.
    638      */
    639     public void setLastAccessDate()
    640     {
    641         lastAccessDate = new java.util.Date();
    642     }
    643
    644     /**
    645      * Sets the create date for this User. This is the time at which
    646      * the user object was created.
    647      *
    648      * @param date The create date.
    649      */
    650     public void setCreateDate(java.util.Date date)
    651     {
    652         createDate = date;
    653     }
    654
    655     /**
    656      * Set the users Email
    657      *
    658      * @param email The new email.
    659      */
    660     public void setEmail(String email)
    661     {
    662         setPerm(User.EMAIL, email);
    663     }
    664
    665     /**
    666      * Set the users First Name
    667      *
    668      * @param fname The new firstname.
    669      */
    670     public void setFirstName(String fname)
    671     {
    672         setPerm(User.FIRST_NAME, fname);
    673     }
    674
    675     /**
    676      * Set last login date/time.
    677      *
    678      * @param date The last login date.
    679      */
    680     public void setLastLogin(java.util.Date date)
    681     {
    682         setPerm(User.LAST_LOGIN, date);
    683     }
    684
    685     /**
    686      * Set the users Last Name
    687      * Sets the last name for this user.
    688      *
    689      * @param lname The new lastname.
    690      */
    691     public void setLastName(String lname)
    692     {
    693         setPerm(User.LAST_NAME, lname);
    694     }
    695
    696     /**
    697      * Set password.
    698      *
    699      * @param password The new password.
    700      */
    701     public void setPassword(String password)
    702     {
    703         setPerm(User.PASSWORD, password);
    704     }
    705
    706     /**
    707      * Put an object into permanent storage.
    708      *
    709      * @param name The object's name.
    710      * @param value The object.
    711      */
    712     public void setPerm(String name, Object value)
    713     {
    714         permStorage.put(name, value);
    715     }
    716
    717     /**
    718      * This should only be used in the case where we want to save the
    719      * data to the database.
    720      *
    721      * @param stuff A Hashtable.
    722      */
    723     public void setPermStorage(Hashtable stuff)
    724     {
    725         this.permStorage = stuff;
    726     }
    727
    728     /**
    729      * This should only be used in the case where we want to save the
    730      * data to the database.
    731      *
    732      * @return A Hashtable.
    733      */
    734     public Hashtable getTempStorage()
    735     {
    736         if (this.tempStorage == null)
    737         {
    738             this.tempStorage = new Hashtable();
    739         }
    740         return this.tempStorage;
    741     }
    742
    743     /**
    744      * This should only be used in the case where we want to save the
    745      * data to the database.
    746      *
    747      * @param storage A Hashtable.
    748      */
    749     public void setTempStorage(Hashtable storage)
    750     {
    751         this.tempStorage = storage;
    752     }
    753
    754     /**
    755      * This gets whether or not someone has logged in.  hasLoggedIn()
    756      * returns this value as a boolean.  This is private because you
    757      * should use hasLoggedIn() instead.
    758      *
    759      * @return True if someone has logged in.
    760      */
    761     private Boolean getHasLoggedIn()
    762     {
    763         return (Boolean) getTemp(User.HAS_LOGGED_IN);
    764     }
    765
    766     /**
    767      * This sets whether or not someone has logged in.  hasLoggedIn()
    768      * returns this value.
    769      *
    770      * @param value Whether someone has logged in or not.
    771      */
    772     public void setHasLoggedIn(Boolean value)
    773     {
    774         setTemp(User.HAS_LOGGED_IN, value);
    775     }
    776
    777     /**
    778      * Put an object into temporary storage.
    779      *
    780      * @param name The object's name.
    781      * @param value The object.
    782      */
    783     public void setTemp(String name, Object value)
    784     {
    785         tempStorage.put(name, value);
    786     }
    787
    788     /**
    789      * A User object can have a variable Timeout which is defined in
    790      * minutes.  If the user has been timed out, then the
    791      * hasLoggedIn() value will return false.
    792      *
    793      * @param time The user's timeout.
    794      */
    795     public void setTimeout(int time)
    796     {
    797         this.timeout = time;
    798     }
    799
    800     /**
    801      * Updates the last login date in the database.
    802      *
    803      * @exception Exception a generic exception.
    804      */
    805     public void updateLastLogin() throws Exception
    806     {
    807         setPerm(User.LAST_LOGIN, new java.util.Date());
    808     }
    809
    810     /**
    811      * Implement this method if you wish to be notified when the User
    812      * has been Bound to the session.
    813      *
    814      * @param hsbe The HttpSessionBindingEvent.
    815      */
    816     public void valueBound(HttpSessionBindingEvent hsbe)
    817     {
    818         // Do not currently need this method.
    819     }
    820
    821     /**
    822      * Implement this method if you wish to be notified when the User
    823      * has been Unbound from the session.
    824      *
    825      * @param hsbe The HttpSessionBindingEvent.
    826      */
    827     public void valueUnbound(HttpSessionBindingEvent hsbe)
    828     {
    829         try
    830         {
    831             if (hasLoggedIn())
    832             {
    833                 TurbineSecurity.saveUser(this);
    834             }
    835         }
    836         catch (Exception e)
    837         {
    838             log.error("BaseUser.valueUnbobund(): "
    839                     + e.getMessage());
    840             log.error(e);
    841
    842             // To prevent messages being lost in case the logging system
    843             // goes away before sessions get unbound on servlet container
    844             // shutdown, print the stcktrace to the container's console.
    845             ByteArrayOutputStream ostr = new ByteArrayOutputStream();
    846
    847             e.printStackTrace(new PrintWriter(ostr, true));
    848             String stackTrace = ostr.toString();
    849
    850             System.out.println(stackTrace);
    851         }
    852     }
    853
    854     /**
    855      * Returns the username for this user.  If this is defined, then
    856      * the user is considered logged in.
    857      *
    858      * @return A String with the username.
    859      */
    860     public String getName()
    861     {
    862         String tmp = null;
    863
    864         tmp = (String) getPerm(User.USERNAME);
    865         if (tmp != null && tmp.length() == 0)
    866         {
    867             tmp = null;
    868         }
    869         return tmp;
    870     }
    871
    872     /**
    873      * Set the users name.
    874      * @param name the name of the User.
    875      */
    876     public void setName(String name)
    877     {
    878 		setPerm(User.USERNAME, name);
    879     }
    880
    881     /**
    882      * Not implemented.
    883      * @return 0
    884      */
    885     public int getId()
    886     {
    887         return 0;
    888     }
    889
    890     /**
    891      * Not implemented.
    892      * @return null
    893      */
    894     public Integer getIdAsObj()
    895     {
    896         return new Integer(0);
    897     }
    898
    899     /**
    900      * Not implemented.
    901      *
    902      * @param id The id of the User.
    903      */
    904     public void setId(int id)
    905     {
    906     }
    907
    908     /**
    909      * Saves this object to the data store.
    910      * @throws Exception if it cannot be saved
    911      */
    912     public void save()
    913             throws Exception
    914     {
    915         if (TurbineSecurity.accountExists(this))
    916         {
    917             TurbineSecurity.saveUser(this);
    918         }
    919         else
    920         {
    921             TurbineSecurity.addUser(this, getPassword());
    922         }
    923     }
    924
    925     /**
    926      * not implemented
    927      *
    928      * @param conn the database connection
    929      * @throws Exception if there is an error
    930      */
    931     public void save(Connection conn) throws Exception
    932     {
    933         throw new Exception("not implemented");
    934     }
    935
    936     /**
    937      * not implemented
    938      *
    939      * @param dbname the database name
    940      * @throws Exception if there is an error
    941      */
    942     public void save(String dbname) throws Exception
    943     {
    944         throw new Exception("not implemented");
    945     }
    946
    947 }