Expresso 5-6

com.jcorporate.expresso.core.security
Class User

java.lang.Object
  extended bycom.jcorporate.expresso.core.security.User
All Implemented Interfaces:
ContextNested, LookupInterface, Mappable
Direct Known Subclasses:
SuperUser

public class User
extends Object
implements LookupInterface, Mappable, ContextNested

This class provides a front-end for maintaining Expresso Users. This class provides an abstraction of the minimum information required for a valid User in Expresso. This class is an "Adaptor" that "adapts" the actual class that implements the User object. "Adaptee" user classes must implement the UserInfo interface. In addition, classes that implement the UserListener interface can listen in on when user objects are added, modified or deleted.

Author:
Michael Nash
See Also:
interface, interface, LookupInterface

Field Summary
static String ACTIVE_ACCOUNT_STATUS
          the string code for an active account
static String ADMIN_USER
          the admin user; user id = 3 usually
static String ANONYMOUS_ALLOWED_GUEST_USER
          the anonymous user; used in systems where anonymous guest are allowed; different than UNKNOWN because there is some kind of status/login conferred by system; user id = 1 usually
static String DISABLED_ACCOUNT_STATUS
          the string code for an disabled account
static String INACTIVE_ACCOUNT_STATUS
          the string code for an disabled account
protected static UserInfo notLoggedInUser
           
static String UNKNOWN_USER
          the unknown user; no login yet; user id = 2 usually
static String WAITING_FOR_APPROVAL_ACCOUNT_STATUS
          the string code for an account that is awaiting admin approval after registration
 
Constructor Summary
User()
          Default constructor
User(UserInfo uInfo)
          Constructs a user with a particular user info.
 
Method Summary
 void add()
          Adds this user to the underlying storage mechanism (SQL-DB, LDAP, etc.)
static void addListener(UserListener listener)
          Adds a UserListener object to list of listeners.
 void addNotify(UserInfo uif)
          Used when a UserInfo Object adds itself outside of the 'User' class.
 boolean checkEmailAuthCode(String emailAuthCode)
          This method just checks to make sure that the submitted emailAuthCode matches the real code.
 void clear()
          Uncaches the underlying UserInfo implementation object
protected  UserInfo constructNotLoggedInUser()
           
 void delete()
          Calls back registered listeners so they can cleanup and then deletes the user
 void deleteNotify(UserInfo uif)
          Used for when a UserInfo implementation deletes itself outside of the User Interface
 boolean find()
          Find a user matching the values of the properties that have been set
 String getAccountStatus()
          Returns the current status of the account.
static User getAdmin(String dbName)
          Retrieves the User instance of the current user.
static int getAdminId()
          determine admin id (usually '3') WARNING: assumes dbname = 'default'
static int getAdminId(String dbname)
          determine admin id (usually '3')
 Vector getAllUsers()
          Returns a list of all the Users in the database.
 String getDataContext()
          Returns the currently set DB context
 String getDBName()
          Retrieve the data context for this particular object implementation
 String getDisplayName()
          Returns a descriptiptive, longer name for the user this is NOT THE UNIQUE LOGIN NAME.
 String getEmail()
          Returns the email address of the user
 String getEmailAuthCode()
          Here we generate an authorization code that would be hard for someone to guess.
 String getEmailValCode()
          Returns the code required for authorization via email
 Vector getGroups()
          Return a vector of the group names that this user belongs to
 List getGroupsList()
          Return a List of the group names that this user belongs to
static int getIdFromLogin(String login, String myDBname)
          utility
 String getKeyValue(String mappedValue)
          Given a 'friendly name' get the key value for this object.
static String getLoginFromId(int uid, String dataContext)
          utility
 String getLoginName()
          Returns the string that the user needs to use to login.
 String getMappedDescription()
          It returns if the field description for the mapped value as specified by the key value parameter.
 String getMappedValue(String keyValue)
          Given a key value, return the mapped value.
 UserInfo getNotLoggedInUser()
          Returns the static constant notLogged in user.
 String getPassword()
          Returns the password string for the user.
 String getPrimaryGroup()
          the primary group of this user is appropriate for unix-like purposes, such as setting the group for a file permission
 boolean getRegComplete()
          Whether registration has been completed beyond the basic user info
 String getRegistrationDomain()
          Returns the unique integer for the registration domain that this user belongs to
 int getUid()
           
 String getUidString()
          Returns the unique integer used as a key to identify the user
static User getUser(ControllerRequest request)
          Convenience routine to find current user.
static User getUserFromId(int uid, String dataContext)
          Pull up the User object associated with the integer id
 UserInfo getUserInfo()
          Return the UserInfo object Normally you would not use this directly, except in special cases where you need to get to the UserInfo object.
 Vector getValidValues(String fieldName)
          Returns the possible values for the multivalued properties
 Vector getValues()
          Method to return a Vector of ValidValue This method may be implemented by objects that want to provide a list of valid values for other DB objects.
 boolean isAdmin()
          determine if this user is admin
static boolean isAdmin(int id)
          determine if this user is admin WARNING: assumes dbname = 'default'
static boolean isAdmin(String name)
          determine if this user is admin
static boolean isListener(UserListener listener)
          Determines if this dbobject is already a listener.
 boolean isMember(String candidategroupname)
          determine whether this user is in this group
static boolean isUnknownUser(String name)
          determine if this user is unknown
 void notify(String subject, String message)
          Send this user a notification via e-mail.
 void notify(String subject, String message, boolean htmlFormat)
          Notify the user with the optional parameter of using html format
 void notify(String subject, String message, boolean htmlFormat, ByteArrayDataSource[] attachments)
          Notify the user with the optional parameter of using html format with virtual raw data attachments.
 boolean passwordEquals(String tryPassword)
          passwordEquals - feed it a password and it will tell you if the hash of it matches the one on file.
 void postLogin()
          Called by the various objects that can log in a user to do post-login tasks
 String randomPassword()
          Generates a random plaintext password
 void retrieve()
           
 void sendAuthEmail()
           
 void sendFileTo(String subject, String message, Vector fileNames)
          Send this user an e-mail with file attachments.
 void sendFollowUpEmail()
           
 void setAccountStatus(String accountStatus)
          Sets the current status of the account
 void setDataContext(String newContext)
          Sets the data context for this particular object implementation.
 void setDBName(String newDBName)
          Switches data contexts for the User Object
 void setDisplayName(String name)
          Sets a long, descriptive name for the user
 void setEmail(String email)
          Sets the email address for the user
 void setEmailValCode(String code)
          Sets the validation code for authorization via email
 void setLoginName(String loginName)
          Sets the name to be used by this user to login
 void setPassword(String password)
          Sets the password to be used by the user.
 void setRegComplete(boolean status)
          Sets the status of whether the extended registration has been completed or not
 void setRegistrationDomain(String domain)
          Sets the registration domain that this user belongs to
 void setUid(int uid)
          Sets the uid, used for finding...
 void setUid(String uid)
          Sets the user id.
 void update()
          This method is called to update the properties of an User object.
 void updateNotify(UserInfo uif)
          Used to notify UserListeners that a UserInfo object has modified itself outside of the User object.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

notLoggedInUser

protected static UserInfo notLoggedInUser

ANONYMOUS_ALLOWED_GUEST_USER

public static final String ANONYMOUS_ALLOWED_GUEST_USER
the anonymous user; used in systems where anonymous guest are allowed; different than UNKNOWN because there is some kind of status/login conferred by system; user id = 1 usually

See Also:
Constant Field Values

UNKNOWN_USER

public static final String UNKNOWN_USER
the unknown user; no login yet; user id = 2 usually

See Also:
Constant Field Values

ADMIN_USER

public static final String ADMIN_USER
the admin user; user id = 3 usually

See Also:
Constant Field Values

ACTIVE_ACCOUNT_STATUS

public static final String ACTIVE_ACCOUNT_STATUS
the string code for an active account

See Also:
setAccountStatus(java.lang.String), Constant Field Values

DISABLED_ACCOUNT_STATUS

public static final String DISABLED_ACCOUNT_STATUS
the string code for an disabled account

See Also:
setAccountStatus(java.lang.String), Constant Field Values

INACTIVE_ACCOUNT_STATUS

public static final String INACTIVE_ACCOUNT_STATUS
the string code for an disabled account

See Also:
setAccountStatus(java.lang.String), Constant Field Values

WAITING_FOR_APPROVAL_ACCOUNT_STATUS

public static final String WAITING_FOR_APPROVAL_ACCOUNT_STATUS
the string code for an account that is awaiting admin approval after registration

See Also:
setAccountStatus(java.lang.String), Constant Field Values
Constructor Detail

User

public User()
Default constructor


User

public User(UserInfo uInfo)
Constructs a user with a particular user info. Useful for defining some special case users where we don't want database access. See SuperUser for example usage.

Parameters:
uInfo - UserInfo
Method Detail

add

public void add()
         throws DBException
Adds this user to the underlying storage mechanism (SQL-DB, LDAP, etc.)

Throws:
DBException - If the add fails

addNotify

public void addNotify(UserInfo uif)
               throws DBException
Used when a UserInfo Object adds itself outside of the 'User' class.

Parameters:
uif - a user info object that has been modified.
Throws:
DBException - if ther's an error performing the 'add' operation

addListener

public static void addListener(UserListener listener)
Adds a UserListener object to list of listeners. The listener param is passed in as an instance, but this is a STATIC LISTENER scheme. In other words, one instance is not listening to another instance. The *classes* are listening to one another. It is generally used as a kind of a poor substitute for 'detail' records, which are deleted in a cascading manner. So when the User object is add/modified/deleted, it will call the method on the provided instance, which usually causes a search and manipulation of all records having to do with the user--not that each record has been registered as listening.

Parameters:
listener - UserListener The object whose object TYPE wants to listen in on User add/modify/delete; we will keep this instance for listening, but any other object of this type cannot register for listening--it is just a representative instance, and the hash is on class name
See Also:
for an example of a listener (it is superclass to several listeners)

isListener

public static boolean isListener(UserListener listener)
Determines if this dbobject is already a listener.

Parameters:
listener - The UserListener class
Returns:
boolean true if the class is already registered as a listener

checkEmailAuthCode

public boolean checkEmailAuthCode(String emailAuthCode)
                           throws DBException
This method just checks to make sure that the submitted emailAuthCode matches the real code. Creation date: (8/8/00 3:08:49 PM) author: Adam Rossi, PlatinumSolutions, Inc.

Parameters:
emailAuthCode - java.lang.String
Returns:
boolean
Throws:
DBException - The exception description.

clear

public void clear()
           throws DBException
Uncaches the underlying UserInfo implementation object

Throws:
DBException - The exception description.

delete

public void delete()
            throws DBException
Calls back registered listeners so they can cleanup and then deletes the user

Throws:
DBException - If the delete fails

deleteNotify

public void deleteNotify(UserInfo uif)
                  throws DBException
Used for when a UserInfo implementation deletes itself outside of the User Interface

Parameters:
uif - The user info that has been modified.
Throws:
DBException

find

public boolean find()
             throws DBException
Find a user matching the values of the properties that have been set

Returns:
true if user is found
Throws:
DBException - Thrown if the underlying implementation throws an exception

getAccountStatus

public String getAccountStatus()
                        throws DBException
Returns the current status of the account. Possible states are active, disabled, inactive.

Returns:
java.lang.String
Throws:
DBException - If the underlying User implementation throws the same

getAllUsers

public Vector getAllUsers()
                   throws DBException
Returns a list of all the Users in the database. The returned Vector contains UserInfo objects.

Returns:
java.util.Vector
Throws:
DBException - If the underlying User implementation throws the same

getDataContext

public String getDataContext()
Returns the currently set DB context

Specified by:
getDataContext in interface ContextNested
Returns:
java.util.String

getEmail

public String getEmail()
                throws DBException
Returns the email address of the user

Returns:
java.lang.String
Throws:
DBException - If the underlying User implementation throws the same

getEmailAuthCode

public String getEmailAuthCode()
                        throws DBException
Here we generate an authorization code that would be hard for someone to guess. The idea is that the person has to check the email sent to them to get this number, and then click on the specially encoded URL to ensure that he/she actually is checking the email account used at registration.

The little trick of getting the time in milliseconds that the person registered, multiplying by some constant, and then rounding, is extremely weak. We need a better method of generating a unique code that will "play nice" in a query string.

Creation date: (8/8/00 3:00:41 PM) author: Adam Rossi, PlatinumSolutions, Inc.

Returns:
java.lang.String
Throws:
DBException

getEmailValCode

public String getEmailValCode()
                       throws DBException
Returns the code required for authorization via email

Returns:
java.lang.String
Throws:
DBException - If the underlying User implementation throws the same

getGroups

public Vector getGroups()
                 throws DBException
Return a vector of the group names that this user belongs to

Returns:
Vector Group names that this user belongs to
Throws:
DBException - If an error occurs when the group info is read

getGroupsList

public List getGroupsList()
                   throws DBException
Return a List of the group names that this user belongs to

Returns:
Vector Group names that this user belongs to
Throws:
DBException - If an error occurs when the group info is read

getLoginName

public String getLoginName()
                    throws DBException
Returns the string that the user needs to use to login.

Returns:
java.lang.String
Throws:
DBException - If the underlying User implementation throws the same

getPassword

public String getPassword()
                   throws DBException
Returns the password string for the user. NOTE: The user class itself has no idea whether the password was encoded or not, that will be upto the implementation of whatever servlet/controller that sets the password in the first place. Conversely, the servlet/controller that logs the user in must know whether the password that this method retrieves was encoded or not.

Returns:
java.lang.String
Throws:
DBException - If the underlying User implementation throws the same

getNotLoggedInUser

public UserInfo getNotLoggedInUser()
                            throws DBException
Returns the static constant notLogged in user.

Returns:
An instantiated UserInfo object
Throws:
DBException

constructNotLoggedInUser

protected UserInfo constructNotLoggedInUser()
                                     throws DBException
Returns:
the 'not logged in user'
Throws:
DBException

getRegComplete

public boolean getRegComplete()
                       throws DBException
Whether registration has been completed beyond the basic user info

Returns:
java.lang.String
Throws:
DBException - If the underlying UserInfo implementation throws the same

getRegistrationDomain

public String getRegistrationDomain()
                             throws DBException
Returns the unique integer for the registration domain that this user belongs to

Returns:
java.lang.String
Throws:
DBException - If the underlying User implementation throws the same

getUidString

public String getUidString()
                    throws DBException
Returns the unique integer used as a key to identify the user

Returns:
java.lang.String
Throws:
DBException - If the underlying User implementation throws the same

getUid

public int getUid()
           throws DBException
Throws:
DBException

getUserInfo

public UserInfo getUserInfo()
                     throws DBException
Return the UserInfo object Normally you would not use this directly, except in special cases where you need to get to the UserInfo object.

Returns:
the UserInfo object
Throws:
DBException - If an error occurs when setting dbname in a newly created UserInfo object

getDisplayName

public String getDisplayName()
                      throws DBException
Returns a descriptiptive, longer name for the user this is NOT THE UNIQUE LOGIN NAME.

Returns:
java.lang.String
Throws:
DBException - If the underlying User implementation throws the same
See Also:
getLoginName()

getValidValues

public Vector getValidValues(String fieldName)
                      throws DBException
Returns the possible values for the multivalued properties

Parameters:
fieldName - The fieldname to get the valid values for
Returns:
java.util.Vector
Throws:
DBException - The exception description.

getValues

public Vector getValues()
                 throws DBException
Description copied from interface: LookupInterface
Method to return a Vector of ValidValue This method may be implemented by objects that want to provide a list of valid values for other DB objects. It is strongly recommended that the valid value list be cached (via the CacheManager) for performance. The naming convention used in Expresso is to store the ValidValue list with a cache name the same as the db objects class name with ".validValues" appended

Specified by:
getValues in interface LookupInterface
Returns:
A ValidValue vector describing what can be looked up.
Throws:
DBException

notify

public void notify(String subject,
                   String message)
            throws DBException
Send this user a notification via e-mail.

Parameters:
subject - Subject of the e-mail
message - Message to send in body of e-mail
Throws:
DBException - If the mail message cannot be sent

notify

public void notify(String subject,
                   String message,
                   boolean htmlFormat)
            throws DBException
Notify the user with the optional parameter of using html format

Parameters:
subject - the message subject
message - the message, possibly in html format
htmlFormat - true if you want the message to be html formatted.
Throws:
DBException - upon error

notify

public void notify(String subject,
                   String message,
                   boolean htmlFormat,
                   ByteArrayDataSource[] attachments)
            throws DBException
Notify the user with the optional parameter of using html format with virtual raw data attachments.

Parameters:
subject - the message subject
message - the message, possibly in html format
htmlFormat - true if you want the message to be html formatted.
attachments - an primitive array of raw data byte array data sources.
Throws:
DBException - upon error from database
See Also:
ByteArrayDataSource

passwordEquals

public boolean passwordEquals(String tryPassword)
                       throws DBException
passwordEquals - feed it a password and it will tell you if the hash of it matches the one on file.

Parameters:
tryPassword - The value the user input for an attempted login.
Returns:
boolean
Throws:
DBException

postLogin

public void postLogin()
               throws DBException,
                      LogException
Called by the various objects that can log in a user to do post-login tasks

Throws:
DBException
LogException

randomPassword

public String randomPassword()
Generates a random plaintext password

Returns:
java.lang.String

retrieve

public void retrieve()
              throws DBException
Throws:
DBException - The exception description.

sendAuthEmail

public void sendAuthEmail()
                   throws DBException
Throws:
DBException

sendFileTo

public void sendFileTo(String subject,
                       String message,
                       Vector fileNames)
                throws DBException,
                       LogException
Send this user an e-mail with file attachments.

Parameters:
subject - Subject of the e-mail
message - Message to send in body of e-mail
fileNames - of the files to attach
Throws:
DBException - If the mail message cannot be sent
LogException

sendFollowUpEmail

public void sendFollowUpEmail()
                       throws DBException
Throws:
DBException

setAccountStatus

public void setAccountStatus(String accountStatus)
                      throws DBException
Sets the current status of the account

Parameters:
accountStatus - java.lang.String One of the values "A" (active), "I" (inactive), "D" (disabled)
Throws:
DBException - If the underlying UserInfo implementation throws the same

setDBName

public void setDBName(String newDBName)
               throws DBException
Switches data contexts for the User Object

Parameters:
newDBName - The new data context to use this security object for
Throws:
DBException - The exception description.

setEmail

public void setEmail(String email)
              throws DBException
Sets the email address for the user

Parameters:
email - the new email address of this user
Throws:
DBException - If the underlying User implementation throws the same

setEmailValCode

public void setEmailValCode(String code)
                     throws DBException
Sets the validation code for authorization via email

Parameters:
code - the new validation code
Throws:
DBException

setLoginName

public void setLoginName(String loginName)
                  throws DBException
Sets the name to be used by this user to login

Parameters:
loginName - the new login name for this user object
Throws:
DBException - If the underlying User implementation throws the same

setPassword

public void setPassword(String password)
                 throws DBException
Sets the password to be used by the user. The password is expected to be plaintext

Parameters:
password - the new password to set for this user
Throws:
DBException - If the underlying User implementation throws the same

setRegComplete

public void setRegComplete(boolean status)
                    throws DBException
Sets the status of whether the extended registration has been completed or not

Parameters:
status - java.lang.String Valid values are "Y" or "N"
Throws:
DBException - If the underlying UserInfo implementation throws the same

setRegistrationDomain

public void setRegistrationDomain(String domain)
                           throws DBException
Sets the registration domain that this user belongs to

Parameters:
domain - The registration domain that this user belongs to.
Throws:
DBException - If the underlying User implementation throws the same

setUid

public void setUid(int uid)
            throws DBException
Sets the uid, used for finding...

Parameters:
uid - The new UID integer to set this user object to.
Throws:
DBException - If the underlying User implementation throws the same

setUid

public void setUid(String uid)
            throws DBException
Sets the user id. Parsing the string to an integer

Parameters:
uid - a string containing an integer value
Throws:
DBException - upon parse error

setDisplayName

public void setDisplayName(String name)
                    throws DBException
Sets a long, descriptive name for the user

Parameters:
name - the new 'full name' of the user
Throws:
DBException - If the underlying User implementation throws the same

update

public void update()
            throws DBException
This method is called to update the properties of an User object. All changed and unchanged fields must have their values filled in The best way is to do a find() on the User first, and then to set the modified fields before calling this method.

Throws:
DBException - The exception description.

updateNotify

public void updateNotify(UserInfo uif)
                  throws DBException
Used to notify UserListeners that a UserInfo object has modified itself outside of the User object.

Parameters:
uif - The new user info to update
Throws:
DBException

isAdmin

public boolean isAdmin()
                throws DBException
determine if this user is admin

Returns:
true if this user is the Administrator account
Throws:
DBException

isAdmin

public static boolean isAdmin(int id)
                       throws DBException
determine if this user is admin WARNING: assumes dbname = 'default'

Parameters:
id - of user
Returns:
true if the id is an Admin user.
Throws:
DBException - upon error

getAdminId

public static int getAdminId()
                      throws DBException
determine admin id (usually '3') WARNING: assumes dbname = 'default'

Returns:
the uid of user "Admin"
Throws:
DBException - upon error

getAdmin

public static User getAdmin(String dbName)
                     throws DBException
Retrieves the User instance of the current user.

Parameters:
dbName - String the data context to check.
Returns:
User a User instance.
Throws:
DBException - if there was no admin user found or other database errors.

getAdminId

public static int getAdminId(String dbname)
                      throws DBException
determine admin id (usually '3')

Parameters:
dbname - the Database context to use
Returns:
the id of the admin
Throws:
DBException - upon error

isAdmin

public static boolean isAdmin(String name)
determine if this user is admin

Parameters:
name - the login name to test
Returns:
true if the name is the Admin user.

isUnknownUser

public static boolean isUnknownUser(String name)
determine if this user is unknown

Parameters:
name - the login name of the user
Returns:
true if the name is UNKNOWN_USER ("NONE")

getPrimaryGroup

public String getPrimaryGroup()
                       throws DBException
the primary group of this user is appropriate for unix-like purposes, such as setting the group for a file permission

Returns:
name of the primary group of this user; null if no group is primary
Throws:
DBException - upon database access error

getIdFromLogin

public static int getIdFromLogin(String login,
                                 String myDBname)
                          throws DBException
utility

Parameters:
login - the login name
myDBname - the DataContext to use.
Returns:
the integer uid of the given login
Throws:
DBException - upon databaes access error or if the uid cannot be found.

getUserFromId

public static User getUserFromId(int uid,
                                 String dataContext)
                          throws DBException
Pull up the User object associated with the integer id

Parameters:
uid - the integer user id
dataContext - data context to look in.
Returns:
a built User object
Throws:
DBException - if the user is unable to be found.

getLoginFromId

public static String getLoginFromId(int uid,
                                    String dataContext)
                             throws DBException
utility

Parameters:
uid - the uid of the user
dataContext - the data context to use
Returns:
the Login for the given UID
Throws:
DBException - upon database access error or if the given id cannot be found.

getKeyValue

public String getKeyValue(String mappedValue)
Given a 'friendly name' get the key value for this object. Example, mappedValue = "Admin", then getKeyValue("Admin") will return "3"

Specified by:
getKeyValue in interface Mappable
Parameters:
mappedValue - the value to convert to a key field. It will return null if the mapped value does not exist.
Returns:
java.lang.String or null.

getMappedValue

public String getMappedValue(String keyValue)
Given a key value, return the mapped value. So for example: getMappedValue("3") will return "Admin"

Specified by:
getMappedValue in interface Mappable
Parameters:
keyValue - the key value to map
Returns:
the mapped value. It will return null if the key does not exist

getMappedDescription

public String getMappedDescription()
It returns if the field description for the mapped value as specified by the key value parameter.

Total Hack warning: This function assumes you're running a DataObject with the login name field called: LoginName

Specified by:
getMappedDescription in interface Mappable
Returns:
String the mapped description

setDataContext

public void setDataContext(String newContext)
Sets the data context for this particular object implementation.

Specified by:
setDataContext in interface ContextNested
Parameters:
newContext - the new Data context.
Throws:
IllegalArgumentException - if the data context is null.

getDBName

public String getDBName()
Retrieve the data context for this particular object implementation

Returns:
java.lang.String. Should never be null.

getUser

public static User getUser(ControllerRequest request)
                    throws DBException
Convenience routine to find current user.

Parameters:
request - current request
Returns:
current user
Throws:
DBException - upon database access error

isMember

public boolean isMember(String candidategroupname)
                 throws DBException
determine whether this user is in this group

Parameters:
candidategroupname - the name to check against.
Returns:
true if the provided group name is one of the groups that this user is a member of
Throws:
DBException - upon database access error

Expresso 5-6

Please see www.jcorporate.com for information about new Expresso releases.