/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.users.model;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.AccessType;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.onebusaway.container.model.IdentityBean;
import org.onebusaway.users.services.UserDao;
import org.onebusaway.users.services.UserPropertiesMigration;
import org.onebusaway.users.services.UserPropertiesService;
/**
* Main user account object, designed to be serialized to a database.
*
* A couple of design notes:
*
* <h2>User Properties</h2>
*
* The only properites that are included in the User object itself are those
* that need be accessible from SQL/HQL queries. For the most part, there aren't
* many of those. The bulk of user properties and data is stored in the
* {@link UserProperties} object attached to each user. This is just a Java bean
* that is serialized to and from the database for each user.
*
* Why the serialized bean for user properties? This approach gives us more
* flexibility as user properties change over time, as driven by new
* functionality. The addition, modification, or deletion of a property can get
* quite complex when it means changing the underlying database schema,
* especially as the user table grows larger and you consider the need to
* upgrade production systems on the fly.
*
* Instead, the serialized bean approach allows us to lazily upgrade beans as
* they are accessed. We rely on handlers registered with the
* {@link UserPropertiesService} to manage versioning of {@link UserProperties}
* implementations and migration between them.
*
* <h2>User Indices</h2>
*
* Each user has a numeric id that uniquely identifies the user account.
* However, we often want to lookup and authenticate a user by a variety of
* other indices: username, phone number, web cookie, OpenID id, Twitter
* account, email, Facebook account. To support all those mechanisms in a
* flexible way, each {@link User} object has a set of {@link UserIndex} objects
* associated with it (see {@link #getUserIndices()}). Each user index has a
* type (ex. username) and value (ex. admin), plus some credential information
* that can be used for authenticating a user through that user index.
*
*
* @author bdferris
* @see UserIndex
* @see UserRole
* @see UserProperties
* @see UserDao
* @see UserService
* @see UserPropertiesService
* @see UserPropertiesMigration
*/
@Entity
@Table(name = "oba_users")
@AccessType("field")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User extends IdentityBean<Integer> {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@AccessType("property")
private Integer id;
private Date creationTime;
private Date lastAccessTime;
private boolean temporary;
@Lob
private UserProperties properties;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "oba_user_roles_mapping", joinColumns = @JoinColumn(name = "user_id"))
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<UserRole> roles = new HashSet<UserRole>();
@OneToMany(cascade = {CascadeType.ALL}, mappedBy = "user", fetch = FetchType.EAGER)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<UserIndex> userIndices = new HashSet<UserIndex>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getCreationTime() {
return creationTime;
}
public void setCreationTime(Date creationTime) {
this.creationTime = creationTime;
}
public Date getLastAccessTime() {
return lastAccessTime;
}
public void setLastAccessTime(Date lastAccessTime) {
this.lastAccessTime = lastAccessTime;
}
public boolean isTemporary() {
return temporary;
}
public void setTemporary(boolean temporary) {
this.temporary = temporary;
}
public UserProperties getProperties() {
return properties;
}
public void setProperties(UserProperties properties) {
this.properties = properties;
}
public Set<UserRole> getRoles() {
return roles;
}
public void setRoles(Set<UserRole> roles) {
this.roles = roles;
}
public Set<UserIndex> getUserIndices() {
return userIndices;
}
public void setUserIndices(Set<UserIndex> userIndices) {
this.userIndices = userIndices;
}
@Override
public String toString() {
return "User(id=" + id + ")";
}
}