[jboss-cvs] jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/preferences ...
Christian Bauer
christian at hibernate.org
Mon Apr 2 14:25:06 EDT 2007
User: cbauer
Date: 07/04/02 14:25:06
Added: examples/wiki/src/main/org/jboss/seam/wiki/core/preferences
WikiPreferenceProvider.java
WikiPreferenceValue.java
PreferenceValueUserType.java
Log:
Totally overengineered but definitely cool system/user/instance wiki preferences architecture
Revision Changes Path
1.1 date: 2007/04/02 18:25:06; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/preferences/WikiPreferenceProvider.java
Index: WikiPreferenceProvider.java
===================================================================
package org.jboss.seam.wiki.core.preferences;
import org.jboss.seam.annotations.*;
import org.jboss.seam.wiki.core.model.User;
import org.jboss.seam.wiki.core.model.Node;
import org.jboss.seam.wiki.preferences.*;
import org.jboss.seam.ScopeType;
import org.jboss.seam.log.Log;
import org.hibernate.Session;
import org.hibernate.validator.InvalidValue;
import javax.persistence.EntityManager;
import java.util.*;
import java.io.Serializable;
/**
* Implementation for the wiki, loads and stores <tt>WikiPreferenceValue</tt> objects.
* <p>
* This implementation tries to be as smart as possible and supports multi-level preference value overrides.
* If you load values, they are automatically resolved for system, user, and instance levels. If you store
* values, they are converted into the appropriate level (whatever the property setting of the meta model allows).
*
* @author Christian Bauer
*/
@Name("preferenceProvider")
@AutoCreate
@Scope(ScopeType.CONVERSATION)
public class WikiPreferenceProvider implements PreferenceProvider, Serializable {
@Logger Log log;
@In
EntityManager entityManager;
private Map<PreferenceComponent, Set<PreferenceValue>> currentValueMap = new HashMap<PreferenceComponent, Set<PreferenceValue>>();
private Set<PreferenceValue> queuedNewValues = new HashSet<PreferenceValue>();
@Transactional
public Set<PreferenceValue> load(PreferenceComponent component, Object user, Object instance, boolean includeSystemPreferences) {
log.debug("Loading preference values for component '" + component.getName() + "' and user '" + user + "' and instance '" + instance + "'");
entityManager.joinTransaction();
if (currentValueMap.get(component) != null) {
log.debug("Returning cached preference values of current conversation");
return currentValueMap.get(component);
} else {
log.debug("Resolving preference values and storing them in current conversation");
currentValueMap.put(component, new TreeSet<PreferenceValue>());
// Only load and instance preferences if there actually can be instance preferences
if (instance != null && ((Node)instance).getId() != null && component.allowsInstanceOverride())
loadInstanceValues(currentValueMap.get(component), component, (Node)instance);
// Only load and user preferences if there actually can be user preferences
if (user != null && component.allowsUserOverride())
loadUserValues(currentValueMap.get(component), component, (User)user);
// Always load system preferences
loadSystemValues(currentValueMap.get(component), component, includeSystemPreferences);
return currentValueMap.get(component);
}
}
@Transactional
public Set<PreferenceValue> store(PreferenceComponent component, Set<PreferenceValue> valueHolders, Object user, Object instance) {
log.debug("Storing preference values for component '" + component.getName() + "' and user '" + user + "' and instance '" + instance + "'");
entityManager.joinTransaction();
currentValueMap.put(component, new TreeSet<PreferenceValue>());
for (PreferenceValue valueHolder : valueHolders) {
log.trace("Storing preference value: " + valueHolder.getPreferenceProperty().getName());
if (instance != null &&
valueHolder.getPreferenceProperty().allowsInstanceOverride() &&
(valueHolder.isSystemAssigned() || valueHolder.isUserAssigned()) &&
valueHolder.isDirty()) {
log.trace("New preference value object at INSTANCE level for property: " + valueHolder.getPreferenceProperty());
WikiPreferenceValue newValueHolder = new WikiPreferenceValue(valueHolder.getPreferenceProperty());
newValueHolder.setNode((Node) instance);
newValueHolder.setValue(valueHolder.getValue());
newValueHolder.setDirty(false); // New object is not "dirty"
queuedNewValues.add(newValueHolder);
getSession().evict(valueHolder);
currentValueMap.get(component).add(newValueHolder);
} else if (user != null &&
valueHolder.getPreferenceProperty().allowsUserOverride() &&
valueHolder.isSystemAssigned() &&
valueHolder.isDirty()) {
log.trace("New preference value object at USER level for property: " + valueHolder.getPreferenceProperty());
WikiPreferenceValue newValueHolder = new WikiPreferenceValue(valueHolder.getPreferenceProperty());
newValueHolder.setUser((User) user);
newValueHolder.setValue(valueHolder.getValue());
newValueHolder.setDirty(false); // New object is not "dirty"
queuedNewValues.add(newValueHolder);
getSession().evict(valueHolder);
currentValueMap.get(component).add(newValueHolder);
} else {
currentValueMap.get(component).add(valueHolder);
}
}
return currentValueMap.get(component);
}
@Transactional
public void deleteUserPreferences(Object user) {
log.debug("Deleting all preference values of user '" + user + "'");
entityManager.joinTransaction();
entityManager.createQuery("delete from WikiPreferenceValue wp where wp.user = :user and wp.node is null")
.setParameter("user", user)
.executeUpdate();
}
@Transactional
public void deleteInstancePreferences(Object instance) {
log.debug("Deleting all preference values of instance '" + instance + "'");
entityManager.joinTransaction();
entityManager.createQuery("delete from WikiPreferenceValue wp where wp.user is null and wp.node = :node")
.setParameter("node", instance)
.executeUpdate();
}
@Transactional
public void flush() {
log.debug("Flushing queued preference values of this conversation to the database");
entityManager.joinTransaction();
// Persist new values (we need to do this during final flush because otherwise we have the persist()/identity generator problem
for (PreferenceValue queuedNewValue : queuedNewValues) {
log.trace("Persisting new preference value object: " + queuedNewValue);
entityManager.persist(queuedNewValue);
}
// Don't flush invalid values
// (invalid values at this point come from the plugin prefs editor, users might click Update even with wrong values in the form)
for (Map.Entry<PreferenceComponent, Set<PreferenceValue>> entry : currentValueMap.entrySet()) {
Set<PreferenceValue> invalidValues = new HashSet<PreferenceValue>();
Map<PreferenceProperty, InvalidValue[]> validatedProperties = entry.getKey().validate(entry.getValue());
for (Map.Entry<PreferenceProperty, InvalidValue[]> validationErrors: validatedProperties.entrySet()) {
if (validationErrors.getValue().length >0) {
WikiPreferenceValue dummy = new WikiPreferenceValue(validationErrors.getKey());
invalidValues.add(dummy);
}
}
for (PreferenceValue preferenceValue : entry.getValue()) {
if (invalidValues.contains(preferenceValue)) {
log.trace("Evicting invalid preference value from persistence context before flush: " + preferenceValue);
getSession().evict(preferenceValue);
}
}
}
log.trace("Flushing persistence context");
entityManager.flush();
}
/* #################### IMPLEMENTATION ######################### */
private void loadSystemValues(Set<PreferenceValue> loadedValues, PreferenceComponent preferenceComponent, boolean includeSystemPreferences) {
//noinspection unchecked
List<WikiPreferenceValue> values =
entityManager.createQuery(
"select wp from WikiPreferenceValue wp" +
" where wp.componentName = :name and wp.user is null and wp.node is null"
).setParameter("name", preferenceComponent.getName()).getResultList();
for (WikiPreferenceValue value : values) {
PreferenceProperty property = preferenceComponent.getPropertiesByName().get( value.getPropertyName() );
if (property == null)
throw new RuntimeException("Orphaned preference value found in database, please clean up: " + value);
value.setPreferenceProperty(property);
if (value.getPreferenceProperty().allowsUserOverride() ||
value.getPreferenceProperty().allowsInstanceOverride() ||
includeSystemPreferences) {
loadedValues.add(value);
}
}
}
private void loadUserValues(Set<PreferenceValue> loadedValues, PreferenceComponent preferenceComponent, User user) {
//noinspection unchecked
List<WikiPreferenceValue> values =
entityManager.createQuery(
"select wp from WikiPreferenceValue wp" +
" where wp.componentName = :name and wp.user = :user and wp.node is null"
)
.setParameter("name", preferenceComponent.getName())
.setParameter("user", user)
.getResultList();
for (WikiPreferenceValue value : values) {
value.setPreferenceProperty(preferenceComponent.getPropertiesByName().get( value.getPropertyName() ));
loadedValues.add(value);
}
}
private void loadInstanceValues(Set<PreferenceValue> loadedValues, PreferenceComponent preferenceComponent, Node node) {
//noinspection unchecked
List<WikiPreferenceValue> values =
entityManager.createQuery(
"select wp from WikiPreferenceValue wp" +
" where wp.componentName = :name and wp.user is null and wp.node = :node"
)
.setParameter("name", preferenceComponent.getName())
.setParameter("node", node)
.getResultList();
for (WikiPreferenceValue value : values) {
value.setPreferenceProperty(preferenceComponent.getPropertiesByName().get( value.getPropertyName() ));
loadedValues.add(value);
}
}
private Session getSession() {
return (Session)entityManager.getDelegate();
}
}
1.1 date: 2007/04/02 18:25:06; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/preferences/WikiPreferenceValue.java
Index: WikiPreferenceValue.java
===================================================================
package org.jboss.seam.wiki.core.preferences;
import org.hibernate.annotations.TypeDefs;
import org.hibernate.annotations.TypeDef;
import org.jboss.seam.wiki.core.model.User;
import org.jboss.seam.wiki.core.model.Node;
import org.jboss.seam.wiki.preferences.PreferenceValue;
import org.jboss.seam.wiki.preferences.PreferenceProperty;
import javax.persistence.*;
import java.io.Serializable;
/**
* Stores preference values in three tables.
* <pre>
* PREF_ID PREF_NAME ATTRIBUTE_NAME *_VALUE
* 1 fooGroup fooAttribute ...
* 2 fooGroup fooAttribute ...
* 3 fooGroup fooAttribute ...
* 4 barGroup barAttribute ...
* 5 barGroup barAttribute ...
*
* PREF_ID USER_ID
* 2 1
* 5 1
*
* PREF_ID NODE_ID
* 3 1
* </pre>
* <p>
* Can handle Long, Double, Date, Boolean, and String values with a Hibernate UserType.
* Could be rewritten to use one table (one-to-one between this entity and user, node) but
* we avoid <i>some</i> nullable columns that way.
*
* @author Christian Bauer
*/
@TypeDefs({
@TypeDef(name="preference_value_usertype", typeClass = PreferenceValueUserType.class)
})
@Entity
@Table(name = "PREFERENCE")
public class WikiPreferenceValue implements PreferenceValue, Serializable, Comparable {
@Id
@GeneratedValue
@Column(name = "PREF_ID")
private Long id;
@Version
@Column(name = "OBJ_VERSION")
private int version;
@Column(name = "COMPONENT_NAME")
private String componentName;
@Column(name = "PROPERTY_NAME")
private String propertyName;
@ManyToOne(fetch = FetchType.EAGER)
@JoinTable(
name = "PREFERENCE_USER",
joinColumns = @JoinColumn(name = "PREF_ID"),
inverseJoinColumns = @JoinColumn(name = "USER_ID")
)
private User user;
@ManyToOne(fetch = FetchType.EAGER)
@JoinTable(
name = "PREFERENCE_NODE",
joinColumns = @JoinColumn(name = "PREF_ID"),
inverseJoinColumns = @JoinColumn(name = "NODE_ID")
)
private Node node;
@org.hibernate.annotations.Type(type = "preference_value_usertype")
@org.hibernate.annotations.Columns(
columns = {
@Column( name="LONG_VALUE"),
@Column( name="DOUBLE_VALUE"),
@Column( name="TIMESTAMP_VALUE"),
@Column( name="BOOLEAN_VALUE"),
@Column( name="STRING_VALUE")
}
)
private Object value;
public WikiPreferenceValue() {}
public WikiPreferenceValue(PreferenceProperty forProperty) {
this.componentName = forProperty.getPreferenceComponent().getName();
this.propertyName = forProperty.getName();
this.property = forProperty;
}
public Long getId() {
return id;
}
public int getVersion() {
return version;
}
public String getComponentName() {
return componentName;
}
public void setComponentName(String componentName) {
this.componentName = componentName;
}
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Node getNode() {
return node;
}
public void setNode(Node node) {
this.node = node;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
if (!value.equals(this.value)) setDirty(true);
this.value = value;
}
public boolean isSystemAssigned() {
return getNode() == null && getUser() == null;
}
public boolean isUserAssigned() {
return getNode() == null && getUser() != null;
}
public boolean isInstanceAssigned() {
return getNode() != null && getUser() == null;
}
// Useful for provider
@Transient
private boolean dirty;
public boolean isDirty() { return dirty; }
public void setDirty(boolean dirty) { this.dirty = dirty; }
// Reference to meta model
@Transient
PreferenceProperty property;
public void setPreferenceProperty(PreferenceProperty property) { this.property = property; }
public PreferenceProperty getPreferenceProperty() { return property; }
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WikiPreferenceValue that = (WikiPreferenceValue) o;
if (!componentName.equals(that.componentName)) return false;
if (!propertyName.equals(that.propertyName)) return false;
return true;
}
public int hashCode() {
int result;
result = componentName.hashCode();
result = 31 * result + propertyName.hashCode();
return result;
}
public int compareTo(Object o) {
return getPreferenceProperty().getDescription().compareTo(
((PreferenceValue)o).getPreferenceProperty().getDescription()
);
}
public String toString() {
return "Value for: " + getComponentName() + "." + getPropertyName();
}
}
1.1 date: 2007/04/02 18:25:06; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/preferences/PreferenceValueUserType.java
Index: PreferenceValueUserType.java
===================================================================
package org.jboss.seam.wiki.core.preferences;
import org.hibernate.HibernateException;
import org.hibernate.Hibernate;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.engine.SessionImplementor;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.util.Date;
public class PreferenceValueUserType implements CompositeUserType {
public Class returnedClass() {
return Object.class;
}
public String[] getPropertyNames() {
return new String[]{
"LONG",
"DOUBLE",
"TIMESTAMP",
"BOOLEAN",
"STRING"
};
}
public Type[] getPropertyTypes() {
return new Type[]{
Hibernate.LONG,
Hibernate.DOUBLE,
Hibernate.TIMESTAMP,
Hibernate.BOOLEAN,
Hibernate.STRING
};
}
public Object getPropertyValue(Object component, int property) throws HibernateException {
switch (property) {
case 0:
return (component instanceof Long) ? component : null;
case 1:
return (component instanceof Double) ? component : null;
case 2:
return (component instanceof Date) ? component : null;
case 3:
return (component instanceof Boolean) ? component : null;
case 4:
return (component instanceof String) ? component : null;
}
throw new IllegalArgumentException("Preferences engine doesn't support object of type: " + component.getClass());
}
public void setPropertyValue(Object component, int property, Object value) throws HibernateException {
throw new UnsupportedOperationException();
}
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
Long intValue = (Long) Hibernate.LONG.nullSafeGet(rs, names[0], session, owner);
if (intValue != null) return intValue;
Double doubleValue = (Double) Hibernate.DOUBLE.nullSafeGet(rs, names[1], session, owner);
if (doubleValue != null) return doubleValue;
Date datetimeValue = (Date) Hibernate.TIMESTAMP.nullSafeGet(rs, names[2], session, owner);
if (datetimeValue != null) return datetimeValue;
Boolean booleanValue = (Boolean) Hibernate.BOOLEAN.nullSafeGet(rs, names[3], session, owner);
if (booleanValue != null) return booleanValue;
return Hibernate.STRING.nullSafeGet(rs, names[4], session, owner);
}
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
Hibernate.LONG.nullSafeSet(st, (value instanceof Long) ? value : null, index);
Hibernate.DOUBLE.nullSafeSet(st, (value instanceof Double) ? value : null, index + 1);
Hibernate.TIMESTAMP.nullSafeSet(st, (value instanceof Date) ? value : null, index + 2);
Hibernate.BOOLEAN.nullSafeSet(st, (value instanceof Boolean) ? value : null, index + 3);
Hibernate.STRING.nullSafeSet(st, (value instanceof String) ? value : null, index + 4);
}
public boolean isMutable() {
return false;
}
public boolean equals(Object x, Object y) throws HibernateException {
return x == null ? y == null : x.equals(y);
}
public int hashCode(Object object) throws HibernateException {
return object.hashCode();
}
public Object replace(Object original, Object target, SessionImplementor session, Object owner) {
return original;
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException {
return (Serializable) value;
}
public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException {
return cached;
}
}
More information about the jboss-cvs-commits
mailing list