[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