r7050 - in dumbhippo/trunk/server: src/com/dumbhippo/dm src/com/dumbhippo/dm/annotations src/com/dumbhippo/dm/schema src/com/dumbhippo/server/dm src/com/dumbhippo/server/impl tests/com/dumbhippo/dm tests/com/dumbhippo/dm/dm
- From: commits mugshot org
- To: online-desktop-list gnome org
- Subject: r7050 - in dumbhippo/trunk/server: src/com/dumbhippo/dm src/com/dumbhippo/dm/annotations src/com/dumbhippo/dm/schema src/com/dumbhippo/server/dm src/com/dumbhippo/server/impl tests/com/dumbhippo/dm tests/com/dumbhippo/dm/dm
- Date: Thu, 13 Dec 2007 11:49:05 -0600 (CST)
Author: otaylor
Date: 2007-12-13 11:49:05 -0600 (Thu, 13 Dec 2007)
New Revision: 7050
Added:
dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/DMInit.java
Modified:
dumbhippo/trunk/server/src/com/dumbhippo/dm/ChangeNotification.java
dumbhippo/trunk/server/src/com/dumbhippo/dm/ChangeNotificationSet.java
dumbhippo/trunk/server/src/com/dumbhippo/dm/DMSession.java
dumbhippo/trunk/server/src/com/dumbhippo/dm/DataModel.java
dumbhippo/trunk/server/src/com/dumbhippo/dm/LazyInitializationException.java
dumbhippo/trunk/server/src/com/dumbhippo/dm/schema/DMClassHolder.java
dumbhippo/trunk/server/src/com/dumbhippo/server/dm/BlockDMO.java
dumbhippo/trunk/server/src/com/dumbhippo/server/dm/PostDMO.java
dumbhippo/trunk/server/src/com/dumbhippo/server/dm/UserDMO.java
dumbhippo/trunk/server/src/com/dumbhippo/server/impl/PostingBoardBean.java
dumbhippo/trunk/server/src/com/dumbhippo/server/impl/StackerBean.java
dumbhippo/trunk/server/tests/com/dumbhippo/dm/BasicTests.java
dumbhippo/trunk/server/tests/com/dumbhippo/dm/InheritanceTests.java
dumbhippo/trunk/server/tests/com/dumbhippo/dm/dm/TestUserDMO.java
Log:
DMInit DMClassHolder: Add @DMInit for group-specific initialization
DMClassHolder DMSession LazyInitializationException: Move initialization code
from DMSession to generated code to allow for group-specific variants
TestUserDMO BasicTests InheritanceTests: Tests for group initialization
UserDMO: Use @DMInit to replace manually group initialization
ChangeNotificationSet ChangeNotification DataModel DMClassHolder: When
creating a ChangeNotification, create it for the correct subclass.
PostDMO: Add link and date properties
BlockDMO: Add 'public' 'clickedTimestamp', 'ignoredTimestamp' properties
PostingBoardBean StackerBean: Notify clickedTimestamp/ignoredTimestamp
properties on changes
StackerBean: Fix a bug where clickedTimestamp was not set on the block
when a block is clicked, because queryUserBlockData() wasn't handling
data2Optional
Modified: dumbhippo/trunk/server/src/com/dumbhippo/dm/ChangeNotification.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/ChangeNotification.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/ChangeNotification.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -31,11 +31,10 @@
private long[] feedTimestamps;
- public ChangeNotification(Class<T> clazz, K key) {
- this.clazz = clazz;
- this.key = key;
- }
-
+ /**
+ * DO NOT USE THIS CONSTRUCTOR DIRECTLY. Instead use model.makeChangeNotification(),
+ * which properly handles subclassing.
+ */
public ChangeNotification(Class<T> clazz, K key, ClientMatcher matcher) {
this.clazz = clazz;
this.key = key;
@@ -145,6 +144,9 @@
@Override
public String toString() {
- return clazz.getSimpleName() + "#" + key.toString();
+ if (matcher != null)
+ return clazz.getSimpleName() + "#" + key.toString() + "; matcher=" + matcher;
+ else
+ return clazz.getSimpleName() + "#" + key.toString();
}
}
Modified: dumbhippo/trunk/server/src/com/dumbhippo/dm/ChangeNotificationSet.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/ChangeNotificationSet.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/ChangeNotificationSet.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -32,7 +32,7 @@
public ChangeNotificationSet(DataModel model) {
}
- private <K, T extends DMObject<K>> ChangeNotification<K,T> getNotification(Class<T> clazz, K key, ClientMatcher matcher) {
+ private <K, T extends DMObject<K>> ChangeNotification<K,T> getNotification(DataModel model, Class<T> clazz, K key, ClientMatcher matcher) {
if (key instanceof DMKey) {
@SuppressWarnings("unchecked")
K clonedKey = (K)((DMKey)key).clone();
@@ -43,14 +43,14 @@
if (matchedNotifications == null)
matchedNotifications = new ArrayList<ChangeNotification<?,?>>();
- ChangeNotification<K,T> notification = new ChangeNotification<K,T>(clazz, key, matcher);
+ ChangeNotification<K,T> notification = model.makeChangeNotification(clazz, key, null);
matchedNotifications.add(notification);
return notification;
} else {
if (notifications == null)
notifications = new HashMap<ChangeNotification<?,?>, ChangeNotification<?,?>>();
- ChangeNotification<K,T> notification = new ChangeNotification<K,T>(clazz, key);
+ ChangeNotification<K,T> notification = model.makeChangeNotification(clazz, key, matcher);
@SuppressWarnings("unchecked")
ChangeNotification<K,T> oldNotification = (ChangeNotification<K,T>)notifications.get(notification);
if (oldNotification != null) {
@@ -64,12 +64,12 @@
}
public <K, T extends DMObject<K>> void changed(DataModel model, Class<T> clazz, K key, String propertyName, ClientMatcher matcher) {
- ChangeNotification<K,T> notification = getNotification(clazz, key, matcher);
+ ChangeNotification<K,T> notification = getNotification(model, clazz, key, matcher);
notification.addProperty(model, propertyName);
}
public <K, T extends DMObject<K>> void feedChanged(DataModel model, Class<T> clazz, K key, String propertyName, long itemTimestamp) {
- ChangeNotification<K,T> notification = getNotification(clazz, key, null);
+ ChangeNotification<K,T> notification = getNotification(model, clazz, key, null);
notification.addFeedProperty(model, propertyName, itemTimestamp);
}
Modified: dumbhippo/trunk/server/src/com/dumbhippo/dm/DMSession.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/DMSession.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/DMSession.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -92,21 +92,6 @@
fetch.visit(this, object, visitor);
}
- /**
- * For use in generated code; this isn't part of the public interface
- *
- * @param <T>
- * @param clazz
- * @param t
- */
- public <K, T extends DMObject<K>> void internalInit(T t) {
- try {
- doInit(t);
- } catch (NotFoundException e) {
- throw new LazyInitializationException("NotFoundException when lazily initializing DMO; improper use of findUnchecked() or deleted object?", e);
- }
- }
-
// This should return a StoreKey<K, ? extends T>, like classHolder.makeStoreKey(), but
// that confuses javac (Java 5) in ways I can't figure out. Practically speaking, it
// doesn't end up mattering
Modified: dumbhippo/trunk/server/src/com/dumbhippo/dm/DataModel.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/DataModel.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/DataModel.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -20,6 +20,7 @@
import com.dumbhippo.dm.schema.DMClassHolder;
import com.dumbhippo.dm.store.DMStore;
import com.dumbhippo.dm.store.StoreClient;
+import com.dumbhippo.dm.store.StoreKey;
/**
* A DataModel is a central object holding information about entire data model; this
@@ -246,6 +247,15 @@
return Timestamper.next();
}
+ // This should return a ChangeNotiifcation<K, ? extends T>, like classHolder.makeChangeNotification(), but
+ // that confuses javac (Java 5) in ways I can't figure out. Practically speaking, it
+ // doesn't end up mattering
+ @SuppressWarnings("unchecked")
+ protected <K, T extends DMObject<K>> ChangeNotification<K,T> makeChangeNotification(Class<T> clazz, K key, ClientMatcher matcher) {
+ DMClassHolder<K,T> classHolder = (DMClassHolder<K,T>)getClassHolder(clazz);
+ return (ChangeNotification<K, T>) classHolder.makeChangeNotification(key, matcher);
+ }
+
private void sendNotifications(ChangeNotificationSet notifications) {
logger.debug("Sending notifications for {}", notifications);
ClientNotificationSet clientNotifications = notifications.resolveNotifications(this);
Modified: dumbhippo/trunk/server/src/com/dumbhippo/dm/LazyInitializationException.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/LazyInitializationException.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/LazyInitializationException.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -24,6 +24,11 @@
throw new RuntimeException("Cause must be specified");
}
+ public LazyInitializationException(NotFoundException cause) {
+ this("NotFoundException when lazily initializing DMO; improper use of findUnchecked() or deleted object?", cause);
+ }
+
+
@Override
public NotFoundException getCause() {
return (NotFoundException)super.getCause();
Added: dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/DMInit.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/DMInit.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/DMInit.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -0,0 +1,16 @@
+package com.dumbhippo.dm.annotations;
+
+/**
+ * Marks a method of a DMO class as an initialization function for a group of properties
+ */
+public @interface DMInit {
+ /**
+ * The group of properties that this initialization function is for.
+ */
+ int group();
+
+ /**
+ * Whether to call the main init() function before this initialization function.
+ */
+ boolean initMain() default true;
+}
Modified: dumbhippo/trunk/server/src/com/dumbhippo/dm/schema/DMClassHolder.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/schema/DMClassHolder.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/schema/DMClassHolder.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -30,12 +30,15 @@
import com.dumbhippo.GlobalSetup;
import com.dumbhippo.dm.BadIdException;
+import com.dumbhippo.dm.ChangeNotification;
+import com.dumbhippo.dm.ClientMatcher;
import com.dumbhippo.dm.DMInjectionLookup;
import com.dumbhippo.dm.DMObject;
import com.dumbhippo.dm.DMSession;
import com.dumbhippo.dm.DMViewpoint;
import com.dumbhippo.dm.DataModel;
import com.dumbhippo.dm.annotations.DMFilter;
+import com.dumbhippo.dm.annotations.DMInit;
import com.dumbhippo.dm.annotations.DMO;
import com.dumbhippo.dm.annotations.Inject;
import com.dumbhippo.dm.annotations.MetaConstruct;
@@ -65,6 +68,15 @@
private Constructor<? extends T> wrapperConstructor;
private DMPropertyHolder<K,T,?>[] properties;
private boolean[] mustQualify;
+
+ // Groups declared in this class (not parent classes)
+ private Map<Integer, PropertyGroup> groups = new HashMap<Integer, PropertyGroup>();
+ // Copies of parent groups (with the level set to the depth of the inheritance)
+ private List<PropertyGroup> parentGroups = new ArrayList<PropertyGroup>();
+ // Map from all properties relevant to this class (properties of this class and parent classes)
+ // to the group for the property
+ private Map<DMPropertyHolder, PropertyGroup> groupsByProperty = new HashMap<DMPropertyHolder, PropertyGroup>();
+
private Map<String, Integer> propertiesMap = new HashMap<String, Integer>();
private Map<String, Integer> feedPropertiesMap = new HashMap<String, Integer>();
private DMO annotation;
@@ -316,6 +328,12 @@
return new StoreKey(subClassHolder, key);
}
+ @SuppressWarnings("unchecked")
+ public ChangeNotification<K,? extends T> makeChangeNotification(K key, ClientMatcher matcher) {
+ DMClassHolder subClassHolder = getClassHolderForKey(key);
+ return new ChangeNotification(subClassHolder.getDMOClass(), key, matcher);
+ }
+
public StoreKey<K,? extends T> makeStoreKey(String string) throws BadIdException {
if (keyClass == Guid.class) {
@@ -462,6 +480,16 @@
}
}
+ private PropertyGroup ensureGroup(int index) {
+ PropertyGroup group = groups.get(index);
+ if (!groups.containsKey(index)) {
+ group = new PropertyGroup(index);
+ groups.put(index, group);
+ }
+
+ return group;
+ }
+
private void collateProperties(CtClass baseCtClass) {
List<DMPropertyHolder<K,T,?>> foundProperties = new ArrayList<DMPropertyHolder<K,T,?>>();
Map<String, Integer> nameCount = new HashMap<String, Integer>();
@@ -474,10 +502,32 @@
nameCount.put(property.getName(), 1);
else
nameCount.put(property.getName(), 1 + nameCount.get(property.getName()));
+
+ if (property.getGroup() >= 0) {
+ PropertyGroup group = ensureGroup(property.getGroup());
+ group.addProperty(property);
+ groupsByProperty.put(property, group);
+ }
+ } else {
+ Object[] annotations;
+
+ try {
+ annotations = method.getAnnotations();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(dmoClass.getName() + ": Problem looking up annotations", e);
+ }
+
+ for (Object annotation : annotations) {
+ if (annotation instanceof DMInit) {
+ DMInit init = (DMInit)annotation;
+ ensureGroup(init.group()).setInitMethod(method, init);
+ }
+ }
}
}
Class<?> parentClass = dmoClass.getSuperclass();
+ int level = 1;
while (parentClass != null) {
DMO parentAnnotation = parentClass.getAnnotation(DMO.class);
if (parentAnnotation != null) {
@@ -497,9 +547,18 @@
nameCount.put(property.getName(), 1 + nameCount.get(property.getName()));
}
}
+
+ for (Object o : parentClassHolder.groups.values()) {
+ PropertyGroup group = (PropertyGroup)o;
+ PropertyGroup groupCopy = new PropertyGroup(group, level);
+ parentGroups.add(groupCopy);
+ for (DMPropertyHolder<K,T,?> property : group.getProperties())
+ groupsByProperty.put(property, groupCopy);
+ }
}
parentClass = parentClass.getSuperclass();
+ level++;
}
// Sort the properties based on the ordering we impose on DMPropertyHolder
@@ -532,6 +591,10 @@
field.setModifiers(Modifier.PRIVATE);
wrapperCtClass.addField(field);
+ field = new CtField(CtClass.booleanType, "_dm_injected", wrapperCtClass);
+ field.setModifiers(Modifier.PRIVATE);
+ wrapperCtClass.addField(field);
+
field = new CtField(CtClass.booleanType, "_dm_initialized", wrapperCtClass);
field.setModifiers(Modifier.PRIVATE);
wrapperCtClass.addField(field);
@@ -552,9 +615,17 @@
CtMethod method = new CtMethod(CtClass.voidType, "_dm_init", new CtClass[] {}, wrapperCtClass);
Template body = new Template(
"{" +
- " if (!_dm_initialized) {" +
- " _dm_session.internalInit($0);" +
- " _dm_initialized = true;" +
+ " if (!_dm_initialized) {" +
+ " if (!_dm_injected) {" +
+ " _dm_classHolder.processInjections(_dm_session, this);" +
+ " _dm_injected = true;" +
+ " }" +
+ " try {" +
+ " init();" +
+ " } catch (com.dumbhippo.server.NotFoundException e) {" +
+ " throw new com.dumbhippo.dm.LazyInitializationException(e);" +
+ " }" +
+ " _dm_initialized = true;" +
" }" +
"}");
method.setBody(body.toString());
@@ -568,14 +639,40 @@
wrapperCtClass.addMethod(method);
}
- private void addGroupInitMethod(CtClass wrapperCtClass, int group, List<DMPropertyHolder<K,T,?>> properties) throws CannotCompileException {
- CtMethod method = new CtMethod(CtClass.voidType, "_dm_initGroup" + group, new CtClass[] {}, wrapperCtClass);
+ private void addGroupInitMethod(CtClass wrapperCtClass, PropertyGroup group) throws CannotCompileException {
+ String groupSuffix = group.getLevel() + "_" + group.getIndex();
+
+ CtField field = new CtField(CtClass.booleanType, "_dm_initializedGroup" + groupSuffix, wrapperCtClass);
+ field.setModifiers(Modifier.PRIVATE);
+ wrapperCtClass.addField(field);
+
+ CtMethod method = new CtMethod(CtClass.voidType, "_dm_initGroup" + groupSuffix, new CtClass[] {}, wrapperCtClass);
StringBuilder body = new StringBuilder();
body.append("{" +
- " _dm_init();");
+ " if (_dm_initializedGroup" + groupSuffix + ")" +
+ " return;");
- for (DMPropertyHolder<K,T,?> property : properties) {
+ if (group.getInitMain())
+ body.append(
+ " _dm_init();");
+ else
+ body.append(
+ " if (!_dm_injected) {" +
+ " _dm_classHolder.processInjections(_dm_session, this);" +
+ " _dm_injected = true;" +
+ " }");
+
+ if (group.getInitMethod() != null) {
+ body.append(
+ " try {" +
+ " " + group.getInitMethod() + "();" +
+ " } catch (com.dumbhippo.server.NotFoundException e) {" +
+ " throw new com.dumbhippo.dm.LazyInitializationException(e);" +
+ " }");
+ }
+
+ for (DMPropertyHolder<K,T,?> property : group.getProperties()) {
Template propertyInit = new Template(
" _dm_%propertyName% = %unboxPre%_dm_session.storeAndFilter(getStoreKey(), %propertyIndex%, %boxPre%super.%methodName%()%boxPost%)%unboxPost%;" +
" _dm_%propertyName%Initialized = true;");
@@ -586,37 +683,25 @@
propertyInit.setParameter("unboxPre", property.getUnboxPrefix());
propertyInit.setParameter("unboxPost", property.getUnboxSuffix());
propertyInit.setParameter("propertyIndex", Integer.toString(getPropertyIndex(property.getName())));
- propertyInit.setParameter("group", Integer.toString(group));
propertyInit.setParameter("methodName", property.getMethodName());
body.append(propertyInit.toString());
}
- body.append("}");
+ body.append(
+ " _dm_initializedGroup" + groupSuffix + " = true;" +
+ "}");
method.setBody(body.toString());
wrapperCtClass.addMethod(method);
}
private void addGroupInitMethods(CtClass wrapperCtClass) throws CannotCompileException {
- Map<Integer, List<DMPropertyHolder<K,T,?>>> map = new HashMap<Integer, List<DMPropertyHolder<K,T,?>>>();
+ for (PropertyGroup group : groups.values())
+ addGroupInitMethod(wrapperCtClass, group);
- for (DMPropertyHolder<K,T,?> property : properties) {
- int group = property.getGroup();
- if (group >= 0) {
- List<DMPropertyHolder<K,T,?>> l = map.get(group);
- if (l == null) {
- l = new ArrayList<DMPropertyHolder<K,T,?>>();
- map.put(group, l);
- }
-
- l.add(property);
- }
- }
-
- for (int group : map.keySet()) {
- addGroupInitMethod(wrapperCtClass, group, map.get(group));
- }
+ for (PropertyGroup group : parentGroups)
+ addGroupInitMethod(wrapperCtClass, group);
}
private void addGetClassHolderMethod(CtClass wrapperCtClass) throws CannotCompileException {
@@ -663,15 +748,16 @@
CtMethod wrapperMethod = new CtMethod(property.getCtClass(), property.getMethodName(), new CtClass[] {}, wrapperCtClass);
String storeCommands;
- int group = property.getGroup();
- if (group < 0) {
+ PropertyGroup group = groupsByProperty.get(property);
+
+ if (group == null) {
storeCommands =
"_dm_init();" +
"_dm_%propertyName% = %unboxPre%_dm_session.storeAndFilter(getStoreKey(), %propertyIndex%, %boxPre%super.%methodName%()%boxPost%)%unboxPost%;" +
"_dm_%propertyName%Initialized = true;";
} else {
storeCommands =
- "_dm_initGroup%group%();";
+ "_dm_initGroup%groupSuffix%();";
}
Template body = new Template(
@@ -693,7 +779,7 @@
body.setParameter("unboxPre", property.getUnboxPrefix());
body.setParameter("unboxPost", property.getUnboxSuffix());
body.setParameter("propertyIndex", Integer.toString(propertyIndex));
- body.setParameter("group", Integer.toString(group));
+ body.setParameter("groupSuffix", group != null ? (group.getLevel() + "_" + group.getIndex()) : "~~~");
body.setParameter("methodName", property.getMethodName());
wrapperMethod.setBody(body.toString());
@@ -748,12 +834,12 @@
CtClass wrapperCtClass = classPool.makeClass(className + "_DMWrapper", baseCtClass);
try {
+ addGetClassHolderMethod(wrapperCtClass);
addCommonFields(wrapperCtClass);
addPropertyFields(wrapperCtClass);
addConstructor(wrapperCtClass);
addInitMethod(baseCtClass, wrapperCtClass);
addGroupInitMethods(wrapperCtClass);
- addGetClassHolderMethod(wrapperCtClass);
addWrapperGetters(wrapperCtClass);
Class<?> wrapperClass = wrapperCtClass.toClass();
@@ -778,4 +864,65 @@
public static DMClassHolder<?, ? extends DMObject<?>> createForClass(DataModel model, Class<?> clazz) {
return newClassHolderHack(model, DMClassInfo.getForClass(clazz));
}
+
+ private class PropertyGroup {
+ public List<DMPropertyHolder<K,T,?>> properties = new ArrayList<DMPropertyHolder<K,T,?>>();
+ public boolean initMain = true;
+ public String initMethod = null;
+ int index;
+ int level;
+
+ public PropertyGroup(int index) {
+ this.index = index;
+ this.level = 0;
+ }
+
+ /* This is used to make a copy of a group in a parent class */
+ public PropertyGroup(PropertyGroup other, int level) {
+ this.properties = other.properties;
+ this.initMain = other.initMain;
+ this.initMethod = other.initMethod;
+ this.index = other.index;
+ this.level = level;
+ }
+
+ public void addProperty(DMPropertyHolder<K,T,?> property) {
+ properties.add(property);
+ }
+
+ public void setInitMethod(CtMethod method, DMInit annotation) {
+ try {
+ if (method.getParameterTypes().length != 0)
+ throw new RuntimeException(dmoClass.getName() + ": @DMInit method cannot have parameters");
+ } catch (NotFoundException e) {
+ throw new RuntimeException(dmoClass.getName() + ": Problem looking up parameters for @DMInit method", e);
+ }
+
+ if (initMethod != null)
+ throw new RuntimeException(dmoClass.getName() + ": Cannot add DMInit method " + method.getName() + " for group " + index + " already have method " + initMethod);
+
+ initMethod = method.getName();
+ initMain = annotation.initMain();
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public boolean getInitMain() {
+ return initMain;
+ }
+
+ public String getInitMethod() {
+ return initMethod;
+ }
+
+ public List<DMPropertyHolder<K, T, ?>> getProperties() {
+ return properties;
+ }
+ }
}
Modified: dumbhippo/trunk/server/src/com/dumbhippo/server/dm/BlockDMO.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/server/dm/BlockDMO.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/server/dm/BlockDMO.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -8,6 +8,7 @@
import com.dumbhippo.dm.DMObject;
import com.dumbhippo.dm.DMSession;
import com.dumbhippo.dm.annotations.DMFilter;
+import com.dumbhippo.dm.annotations.DMInit;
import com.dumbhippo.dm.annotations.DMO;
import com.dumbhippo.dm.annotations.DMProperty;
import com.dumbhippo.dm.annotations.Inject;
@@ -15,22 +16,32 @@
import com.dumbhippo.dm.store.StoreKey;
import com.dumbhippo.identity20.Guid;
import com.dumbhippo.persistence.BlockType;
+import com.dumbhippo.persistence.UserBlockData;
+import com.dumbhippo.persistence.BlockType.BlockVisibility;
import com.dumbhippo.server.NotFoundException;
import com.dumbhippo.server.Stacker;
import com.dumbhippo.server.blocks.BlockView;
import com.dumbhippo.server.blocks.TitleBlockView;
import com.dumbhippo.server.blocks.TitleDescriptionBlockView;
import com.dumbhippo.server.views.SystemViewpoint;
+import com.dumbhippo.server.views.UserViewpoint;
+import com.dumbhippo.server.views.Viewpoint;
@DMO(classId="http://mugshot.org/p/o/block", resourceBase="/o/block")
@DMFilter("viewer.canSeeBlock(this)")
public abstract class BlockDMO extends DMObject<BlockDMOKey> {
@SuppressWarnings("unused")
private static Logger logger = GlobalSetup.getLogger(BlockDMO.class);
+
+ static final int USER_BLOCK_DATA_GROUP = 1;
protected BlockView blockView;
+ private UserBlockData userBlockData;
@Inject
+ Viewpoint viewpoint;
+
+ @Inject
DMSession session;
@EJB
@@ -115,8 +126,38 @@
return null;
}
+ @DMProperty(defaultInclude=true)
+ public boolean isPublic() {
+ return blockView.getBlockType().getBlockVisibility() == BlockVisibility.PUBLIC;
+ }
+
//////////////////////////////////////////////////////////////////////
+ @DMInit(group=USER_BLOCK_DATA_GROUP, initMain=false)
+ public void initUserBlockData() {
+ if (viewpoint instanceof UserViewpoint) {
+ try {
+ userBlockData = stacker.lookupUserBlockData((UserViewpoint)viewpoint, getKey().getBlockId());
+ } catch (NotFoundException e) {
+ }
+ }
+ }
+
+ @DMProperty(defaultInclude=true, group=USER_BLOCK_DATA_GROUP, cached=false)
+ public long getClickedTimestamp() {
+ return userBlockData.getClickedTimestampAsLong();
+ }
+
+ @DMProperty(defaultInclude=true, group=USER_BLOCK_DATA_GROUP, cached=false)
+ public long getIgnoredTimestamp() {
+ if (userBlockData.isIgnored())
+ return userBlockData.getIgnoredTimestampAsLong();
+ else
+ return -1;
+ }
+
+ //////////////////////////////////////////////////////////////////////
+
// These properties are here for the implementation of Viewpoint.canSeePrivateBlock(),
// Viewpoint.canSeeBlockDelegate()
Modified: dumbhippo/trunk/server/src/com/dumbhippo/server/dm/PostDMO.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/server/dm/PostDMO.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/server/dm/PostDMO.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -13,6 +13,7 @@
import com.dumbhippo.dm.annotations.DMO;
import com.dumbhippo.dm.annotations.DMProperty;
import com.dumbhippo.dm.annotations.Inject;
+import com.dumbhippo.dm.annotations.PropertyType;
import com.dumbhippo.identity20.Guid;
import com.dumbhippo.persistence.AccountClaim;
import com.dumbhippo.persistence.Post;
@@ -22,6 +23,7 @@
import com.dumbhippo.server.NotFoundException;
import com.dumbhippo.server.PostingBoard;
import com.dumbhippo.server.views.SystemViewpoint;
+import com.dumbhippo.server.views.Viewpoint;
@DMO(classId="http://mugshot.org/p/o/post", resourceBase="/o/post")
@DMFilter("viewer.canSeePost(this)")
@@ -32,6 +34,9 @@
@Inject
DMSession session;
+ @Inject
+ Viewpoint viewpoint;
+
private Post post;
protected PostDMO(Guid key) {
@@ -62,7 +67,17 @@
return post.getText();
}
+ @DMProperty(defaultInclude=true, type=PropertyType.URL)
+ public String getLink() {
+ return post.getUrl().toString();
+ }
+
@DMProperty(defaultInclude=true)
+ public long getDate() {
+ return post.getPostDate().getTime();
+ }
+
+ @DMProperty(defaultInclude=true)
@DMFilter("viewer.canSeePrivate(any)")
public List<UserDMO> getUserRecipients() {
List<UserDMO> result = new ArrayList<UserDMO>();
Modified: dumbhippo/trunk/server/src/com/dumbhippo/server/dm/UserDMO.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/server/dm/UserDMO.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/server/dm/UserDMO.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -21,6 +21,7 @@
import com.dumbhippo.dm.DMObject;
import com.dumbhippo.dm.DMSession;
import com.dumbhippo.dm.annotations.DMFilter;
+import com.dumbhippo.dm.annotations.DMInit;
import com.dumbhippo.dm.annotations.DMO;
import com.dumbhippo.dm.annotations.DMProperty;
import com.dumbhippo.dm.annotations.Inject;
@@ -63,7 +64,6 @@
private static final int CURRENT_TRACK_GROUP = 2;
private TrackHistory currentTrack;
- private boolean currentTrackFetched;
// people who have any opinion of this UserDMO except blocked
private Set<UserDMO> contacters; /* Includes hot/medium/cold contacters */
@@ -161,37 +161,36 @@
return result;
}
- private void ensureContacters() {
- if (contacters == null) {
- blockingContacters = new HashSet<UserDMO>();
- contacters = new HashSet<UserDMO>();
- coldContacters = new HashSet<UserDMO>();
- hotContacters = new HashSet<UserDMO>();
+ @DMInit(group=CONTACTERS_GROUP, initMain=false)
+ public void initContacters() {
+ blockingContacters = new HashSet<UserDMO>();
+ contacters = new HashSet<UserDMO>();
+ coldContacters = new HashSet<UserDMO>();
+ hotContacters = new HashSet<UserDMO>();
- for (Pair<Guid,ContactStatus> pair : identitySpider.computeContactersWithStatus(user.getGuid())) {
- Guid guid = pair.getFirst();
- ContactStatus status = pair.getSecond();
-
- UserDMO contacter = session.findUnchecked(UserDMO.class, guid);
+ for (Pair<Guid,ContactStatus> pair : identitySpider.computeContactersWithStatus(getKey())) {
+ Guid guid = pair.getFirst();
+ ContactStatus status = pair.getSecond();
- switch (status) {
- case NONCONTACT:
- break;
- case BLOCKED:
- blockingContacters.add(contacter);
- break;
- case COLD:
- coldContacters.add(contacter);
- contacters.add(contacter);
- break;
- case MEDIUM:
- contacters.add(contacter);
- break;
- case HOT:
- hotContacters.add(contacter);
- contacters.add(contacter);
- break;
- }
+ UserDMO contacter = session.findUnchecked(UserDMO.class, guid);
+
+ switch (status) {
+ case NONCONTACT:
+ break;
+ case BLOCKED:
+ blockingContacters.add(contacter);
+ break;
+ case COLD:
+ coldContacters.add(contacter);
+ contacters.add(contacter);
+ break;
+ case MEDIUM:
+ contacters.add(contacter);
+ break;
+ case HOT:
+ hotContacters.add(contacter);
+ contacters.add(contacter);
+ break;
}
}
}
@@ -199,8 +198,6 @@
@DMProperty(group=CONTACTERS_GROUP)
@DMFilter("viewer.canSeePrivate(this)")
public Set<UserDMO> getContacters() {
- ensureContacters();
-
return contacters;
}
@@ -212,24 +209,18 @@
@DMProperty(group=CONTACTERS_GROUP)
@DMFilter("false")
public Set<UserDMO> getBlockingContacters() {
- ensureContacters();
-
return blockingContacters;
}
@DMProperty(group=CONTACTERS_GROUP)
@DMFilter("false")
public Set<UserDMO> getColdContacters() {
- ensureContacters();
-
return coldContacters;
}
@DMProperty(group=CONTACTERS_GROUP)
@DMFilter("false")
public Set<UserDMO> getHotContacters() {
- ensureContacters();
-
return hotContacters;
}
@@ -429,34 +420,30 @@
return result;
}
- private void ensureCurrentTrack() {
- if (!currentTrackFetched) {
- try {
- currentTrack = musicSystem.getCurrentTrack(AnonymousViewpoint.getInstance(Site.NONE), user);
- int duration = currentTrack.getTrack().getDuration();
-
- // A negative duration means "unknown". We also treat durations of over an hour as suspect,
- // and substitute a default duration for determining if the track is still playing
- if (duration <= 0 || duration > 60 * 60) {
- duration = 30 * 60;
- }
-
- // While we don't try to notify when tracks stop playing, we want to omit past tracks
- // to avoid doing web-services work to get the details of "current" tracks that were
- // played months ago.
- if (currentTrack.getLastUpdated().getTime() + duration * 1000L < System.currentTimeMillis())
- currentTrack = null;
-
- } catch (NotFoundException e) {
+ @DMInit(group=CURRENT_TRACK_GROUP)
+ public void initCurrentTrack() {
+ try {
+ currentTrack = musicSystem.getCurrentTrack(AnonymousViewpoint.getInstance(Site.NONE), user);
+ int duration = currentTrack.getTrack().getDuration();
+
+ // A negative duration means "unknown". We also treat durations of over an hour as suspect,
+ // and substitute a default duration for determining if the track is still playing
+ if (duration <= 0 || duration > 60 * 60) {
+ duration = 30 * 60;
}
- currentTrackFetched = true;
+
+ // While we don't try to notify when tracks stop playing, we want to omit past tracks
+ // to avoid doing web-services work to get the details of "current" tracks that were
+ // played months ago.
+ if (currentTrack.getLastUpdated().getTime() + duration * 1000L < System.currentTimeMillis())
+ currentTrack = null;
+
+ } catch (NotFoundException e) {
}
}
@DMProperty(group=CURRENT_TRACK_GROUP)
public TrackDMO getCurrentTrack() {
- ensureCurrentTrack();
-
if (currentTrack != null)
return session.findUnchecked(TrackDMO.class, currentTrack.getTrack().getId());
else
@@ -465,8 +452,6 @@
@DMProperty(group=CURRENT_TRACK_GROUP)
public long getCurrentTrackPlayTime() {
- ensureCurrentTrack();
-
if (currentTrack != null)
return currentTrack.getLastUpdated().getTime();
else
Modified: dumbhippo/trunk/server/src/com/dumbhippo/server/impl/PostingBoardBean.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/server/impl/PostingBoardBean.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/server/impl/PostingBoardBean.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -71,6 +71,7 @@
import com.dumbhippo.server.PostingBoard;
import com.dumbhippo.server.RecommenderSystem;
import com.dumbhippo.server.blocks.PostBlockHandler;
+import com.dumbhippo.server.dm.DataService;
import com.dumbhippo.server.util.EJBUtil;
import com.dumbhippo.server.util.GuidNotFoundException;
import com.dumbhippo.server.views.AnonymousViewpoint;
@@ -752,6 +753,8 @@
// to write our own code for asynchronous execution
TxUtils.runInTransactionOnCommit(new TxRunnable() {
public void run() throws RetryException {
+ DataService.getModel().initializeReadWriteSession(new UserViewpoint(viewpoint.getViewerId(), viewpoint.getSite()));
+
Post attachedPost = em.find(Post.class, post.getId());
User attachedUser = em.find(User.class, viewpoint.getViewer().getId());
Modified: dumbhippo/trunk/server/src/com/dumbhippo/server/impl/StackerBean.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/server/impl/StackerBean.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/src/com/dumbhippo/server/impl/StackerBean.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -89,7 +89,10 @@
import com.dumbhippo.server.blocks.RedditBlockHandler;
import com.dumbhippo.server.blocks.TwitterPersonBlockHandler;
import com.dumbhippo.server.blocks.YouTubeBlockHandler;
+import com.dumbhippo.server.dm.BlockDMO;
+import com.dumbhippo.server.dm.BlockDMOKey;
import com.dumbhippo.server.dm.DataService;
+import com.dumbhippo.server.dm.UserClientMatcher;
import com.dumbhippo.server.dm.UserDMO;
import com.dumbhippo.server.util.EJBUtil;
import com.dumbhippo.server.views.GroupMugshotView;
@@ -321,34 +324,13 @@
}
private UserBlockData queryUserBlockData(User user, BlockKey key) throws NotFoundException {
- Guid data1 = key.getData1();
- Guid data2 = key.getData2();
- long data3 = key.getData3();
- StackInclusion inclusion = key.getInclusion();
-
- Query q;
- if (data1 != null && data2 != null) {
- q = em.createQuery("SELECT ubd FROM UserBlockData ubd, Block block WHERE ubd.block = block AND ubd.user = :user AND block.blockType=:type AND block.data1=:data1 AND block.data2=:data2 AND block.data3=:data3 " +
- "AND block.inclusion = :inclusion");
- q.setParameter("data1", data1.toString());
- q.setParameter("data2", data2.toString());
- } else if (data1 != null) {
- q = em.createQuery("SELECT ubd FROM UserBlockData ubd, Block block WHERE ubd.block = block AND ubd.user = :user AND block.blockType=:type AND block.data1=:data1 AND block.data2='' AND block.data3=:data3 " +
- "AND block.inclusion = :inclusion");
- q.setParameter("data1", data1.toString());
- } else if (data2 != null) {
- q = em.createQuery("SELECT ubd FROM UserBlockData ubd, Block block WHERE ubd.block = block AND ubd.user = :user AND block.blockType=:type AND block.data2=:data2 AND block.data1='' AND block.data3=:data3 " +
- "AND block.inclusion = :inclusion");
- q.setParameter("data2", data2);
- } else {
- throw new IllegalArgumentException("must provide either data1 or data2 in query for block " + key);
- }
- q.setParameter("data3", data3);
+ Query q = em.createQuery(
+ "SELECT ubd FROM UserBlockData ubd, Block block" +
+ " WHERE ubd.block = block" +
+ " AND ubd.user = :user " +
+ " AND " + getBlockClause(key));
+ setBlockParameters(key, q);
q.setParameter("user", user);
- q.setParameter("type", key.getBlockType());
- if (inclusion == null)
- throw new IllegalArgumentException("missing inclusion in key " + key);
- q.setParameter("inclusion", inclusion);
try {
return (UserBlockData) q.getSingleResult();
} catch (NoResultException e) {
@@ -794,6 +776,13 @@
blockClicked(ubd, clickedTime);
}
+ private void invalidateUserBlockDataProperty(UserBlockData ubd, String propertyName) {
+ DataService.currentSessionRW().changed(BlockDMO.class,
+ new BlockDMOKey(ubd.getBlock()),
+ propertyName,
+ new UserClientMatcher(ubd.getUser().getGuid()));
+ }
+
public void blockClicked(UserBlockData ubd, long clickedTime) {
// if we weren't previously clicked on, then increment the count.
// (FIXME this is not a reliable way of incrementing a count, since two transactions
@@ -801,19 +790,21 @@
if (!ubd.isClicked())
ubd.getBlock().setClickedCount(ubd.getBlock().getClickedCount() + 1);
- if (ubd.getClickedTimestampAsLong() < clickedTime)
+ if (ubd.getClickedTimestampAsLong() < clickedTime) {
ubd.setClickedTimestampAsLong(clickedTime);
+ invalidateUserBlockDataProperty(ubd, "clickedTimestamp");
+ }
// we automatically unignore anything you click on
if (ubd.isIgnored())
setBlockHushed(ubd, false);
- logger.debug("due to click, restacking block {} with new time {}",
- ubd.getBlock(), clickedTime);
-
if (!BlockView.clickedCountIsSignificant(ubd.getBlock().getClickedCount()))
return;
+ logger.debug("due to click, restacking block {} with new time {}",
+ ubd.getBlock(), clickedTime);
+
// now update the timestamp in the block (if it's newer)
// and update user caches for all users
stack(ubd.getBlock(), clickedTime, StackReason.VIEWER_COUNT);
@@ -1632,10 +1623,12 @@
public void setBlockHushed(UserBlockData userBlockData, boolean hushed) {
if (hushed != userBlockData.isIgnored()) {
userBlockData.setIgnored(hushed);
- if (hushed)
+ if (hushed) {
userBlockData.setIgnoredTimestampAsLong(userBlockData.getBlock().getTimestampAsLong());
- else
+ } else
userBlockData.setStackTimestampAsLong(userBlockData.getBlock().getTimestampAsLong());
+
+ invalidateUserBlockDataProperty(userBlockData, "ignoredTimestamp");
}
}
Modified: dumbhippo/trunk/server/tests/com/dumbhippo/dm/BasicTests.java
===================================================================
--- dumbhippo/trunk/server/tests/com/dumbhippo/dm/BasicTests.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/tests/com/dumbhippo/dm/BasicTests.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -58,7 +58,7 @@
assertTrue(groupDMO != null);
assertTrue(groupDMO.getKey().equals(guid));
assertTrue(groupDMO.getName().equals("Hippos"));
-
+
em.getTransaction().commit();
//////////////////////////////////////////////////
@@ -136,6 +136,38 @@
em.getTransaction().commit();
}
+ // Test grouping
+ public void testGrouping() throws Exception {
+ EntityManager em;
+
+ TestViewpoint viewpoint = new TestViewpoint(Guid.createNew());
+
+ /////////////////////////////////////////////////
+ // Setup
+
+ em = support.beginSessionRW(viewpoint);
+
+ TestUser bob = new TestUser("Bob");
+ Guid bobId = bob.getGuid();
+ em.persist(bob);
+
+
+ em.getTransaction().commit();
+
+ /////////////////////////////////////////////////
+
+ em = support.beginSessionRO(viewpoint);
+
+ ReadOnlySession session = support.currentSessionRO();
+
+ TestUserDMO bobDMO = session.find(TestUserDMO.class, bobId);
+
+ assertEquals("initializedA", bobDMO.getGroupedA());
+ assertEquals("initializedB", bobDMO.getGroupedB());
+
+ em.getTransaction().commit();
+ }
+
// Test looking up objects by String resource ID
public void testStringResourceId() throws NotFoundException {
EntityManager em;
Modified: dumbhippo/trunk/server/tests/com/dumbhippo/dm/InheritanceTests.java
===================================================================
--- dumbhippo/trunk/server/tests/com/dumbhippo/dm/InheritanceTests.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/tests/com/dumbhippo/dm/InheritanceTests.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -43,6 +43,10 @@
assertEquals("*The Nose*", superUserDMO.getName());
assertEquals("The ability to tell if leftovers have gone bad", superUserDMO.getSuperPower());
+ /// Test that groups in base classes are handled in inherited classes
+ assertEquals("initializedA", superUserDMO.getGroupedA());
+ assertEquals("initializedB", superUserDMO.getGroupedB());
+
em.getTransaction().commit();
}
}
Modified: dumbhippo/trunk/server/tests/com/dumbhippo/dm/dm/TestUserDMO.java
===================================================================
--- dumbhippo/trunk/server/tests/com/dumbhippo/dm/dm/TestUserDMO.java 2007-12-13 01:40:12 UTC (rev 7049)
+++ dumbhippo/trunk/server/tests/com/dumbhippo/dm/dm/TestUserDMO.java 2007-12-13 17:49:05 UTC (rev 7050)
@@ -18,6 +18,7 @@
import com.dumbhippo.dm.DMObject;
import com.dumbhippo.dm.DMSession;
import com.dumbhippo.dm.annotations.DMFilter;
+import com.dumbhippo.dm.annotations.DMInit;
import com.dumbhippo.dm.annotations.DMO;
import com.dumbhippo.dm.annotations.DMProperty;
import com.dumbhippo.dm.annotations.Inject;
@@ -76,6 +77,29 @@
return new BlogEntryFeed();
}
+ //////////////////////////////////////////
+
+ // Very basic test of grouping
+
+ private String group1Value;
+
+ @DMInit(group=1, initMain=false)
+ public void initGroup1() {
+ group1Value = "initialized";
+ }
+
+ @DMProperty(group=1)
+ public String getGroupedA() {
+ return group1Value + "A";
+ }
+
+ @DMProperty(group=1)
+ public String getGroupedB() {
+ return group1Value + "B";
+ }
+
+ //////////////////////////////////////////
+
private class BlogEntryFeed implements DMFeed<TestBlogEntryDMO> {
public Iterator<DMFeedItem<TestBlogEntryDMO>> iterator(int start, int max, long minTimestamp) {
logger.debug("Querying blog entries from database, start={}, max={}, minTimestamp={}",
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]