Skip to content

Commit 60dc744

Browse files
author
Pascal Robert
committed
Reworked caching and sync details
1 parent 9c6d300 commit 60dc744

20 files changed

+764
-40
lines changed

SimpleBlog/Sources/com/wowodc/app/Application.java

+51-13
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@
1010
import com.webobjects.eocontrol.EOQualifierEvaluation;
1111
import com.webobjects.foundation.NSArray;
1212
import com.webobjects.foundation.NSDictionary;
13-
import com.webobjects.foundation.NSLog;
1413
import com.webobjects.foundation.NSNotification;
1514
import com.webobjects.foundation.NSNotificationCenter;
1615
import com.webobjects.foundation.NSSelector;
1716
import com.webobjects.foundation.NSTimestamp;
1817
import com.wowodc.model.BlogCategory;
1918
import com.wowodc.model.BlogEntry;
19+
import com.wowodc.model.DelegatePKHistory;
2020
import com.wowodc.model.Person;
2121
import com.wowodc.model.SyncInfo;
2222
import com.wowodc.model.enums.SyncInfoStatus;
23+
import com.wowodc.rest.controllers.BlogEntryController;
2324
import com.wowodc.rest.controllers.OtherRoutesController;
2425
import com.wowodc.rest.controllers.RssController;
2526

@@ -29,7 +30,9 @@
2930
import er.extensions.eof.ERXEnterpriseObject;
3031
import er.extensions.foundation.ERXArrayUtilities;
3132
import er.extensions.foundation.ERXRandomGUID;
33+
import er.rest.ERXRestContext;
3234
import er.rest.ERXRestNameRegistry;
35+
import er.rest.IERXRestDelegate;
3336
import er.rest.routes.ERXRoute;
3437
import er.rest.routes.ERXRouteRequestHandler;
3538

@@ -51,7 +54,8 @@ public Application() {
5154

5255
ERXRouteRequestHandler restRequestHandler = new ERXRouteRequestHandler();
5356
restRequestHandler.addDefaultRoutes(BlogCategory.ENTITY_NAME);
54-
restRequestHandler.addDefaultRoutes(BlogEntry.ENTITY_NAME);
57+
restRequestHandler.insertRoute(new ERXRoute(BlogEntry.ENTITY_NAME, "/posts/{uniqueTitle:String}", ERXRoute.Method.Get, BlogEntryController.class, "show"));
58+
restRequestHandler.insertRoute(new ERXRoute(BlogEntry.ENTITY_NAME, "/posts", ERXRoute.Method.Get, BlogEntryController.class, "index"));
5559
restRequestHandler.addDefaultRoutes(Person.ENTITY_NAME);
5660
restRequestHandler.insertRoute(new ERXRoute("Other", "", ERXRoute.Method.Get, OtherRoutesController.class, "mainPage"));
5761
restRequestHandler.insertRoute(new ERXRoute("Other", "/admin", ERXRoute.Method.Get, OtherRoutesController.class, "adminPage"));
@@ -91,18 +95,42 @@ public void coordinateChanges(NSNotification notification) {
9195
EOEditingContext ec = ERXEC.newEditingContext(new EOObjectStoreCoordinator());
9296
ec.lock();
9397

94-
log.debug("Change Notification " + userInfo);
9598
try {
96-
NSLog.out.appendln("deleted" + (NSArray)userInfo.objectForKey("deleted"));
97-
NSLog.out.appendln("inserted: " + (NSArray)userInfo.objectForKey("inserted"));
98-
NSLog.out.appendln("updated: " + (NSArray)userInfo.objectForKey("updated"));
99-
100-
NSArray result = ERXArrayUtilities.filteredArrayWithQualifierEvaluation((NSArray)userInfo.objectForKey("inserted"), new EOSyncEntityFilter() );
101-
for ( Object id : result ) {
102-
EOKeyGlobalID gid = (EOKeyGlobalID)id;
103-
ERXEnterpriseObject eo = (ERXEnterpriseObject)ec.faultForGlobalID( gid, ec);
104-
105-
SyncInfo.createSyncInfo(ec, ERXRandomGUID.newGid(), new NSTimestamp(), SyncInfoStatus.INSERTED, eo.entityName() + ":" + eo.primaryKey());
99+
NSArray<Object> insertedObjects = ERXArrayUtilities.filteredArrayWithQualifierEvaluation((NSArray<Object>)userInfo.objectForKey("inserted"), new EOSyncEntityFilter() );
100+
for ( Object id : insertedObjects ) {
101+
ERXEnterpriseObject eo = eo(id, ec);
102+
SyncInfo syncDetail = SyncInfo.createSyncInfo(ec, ERXRandomGUID.newGid(), new NSTimestamp(), SyncInfoStatus.INSERTED, eo.entityName() + ":" + eo.primaryKey());
103+
syncDetail.setDelegatedPrimaryKeyValue((String)entityId(eo));
104+
}
105+
106+
NSArray<Object> deletedObjects = ERXArrayUtilities.filteredArrayWithQualifierEvaluation((NSArray<Object>)userInfo.objectForKey("deleted"), new EOSyncEntityFilter() );
107+
for ( Object id : deletedObjects ) {
108+
ERXEnterpriseObject eo = eo(id, ec);
109+
if (eo != null) {
110+
SyncInfo syncDetail = SyncInfo.fetchSyncInfo(ec, SyncInfo.TOKEN.eq(eo.entityName() + ":" + eo.primaryKey()));
111+
if (syncDetail != null) {
112+
syncDetail.setEtag(ERXRandomGUID.newGid());
113+
syncDetail.setLastModified(new NSTimestamp());
114+
syncDetail.setStatus(SyncInfoStatus.DELETED);
115+
}
116+
}
117+
}
118+
119+
NSArray<Object> updatedObjects = ERXArrayUtilities.filteredArrayWithQualifierEvaluation((NSArray<Object>)userInfo.objectForKey("updated"), new EOSyncEntityFilter() );
120+
for ( Object id : updatedObjects ) {
121+
ERXEnterpriseObject eo = eo(id, ec);
122+
if (eo != null) {
123+
SyncInfo syncDetail = SyncInfo.fetchSyncInfo(ec, SyncInfo.TOKEN.eq(eo.entityName() + ":" + eo.primaryKey()));
124+
if (syncDetail != null) {
125+
if (!(syncDetail.delegatedPrimaryKeyValue().equals((String)entityId(eo)))) {
126+
DelegatePKHistory.createDelegatePKHistory(ec, syncDetail.delegatedPrimaryKeyValue(), syncDetail);
127+
}
128+
syncDetail.setEtag(ERXRandomGUID.newGid());
129+
syncDetail.setLastModified(new NSTimestamp());
130+
syncDetail.setStatus(SyncInfoStatus.UPDATED);
131+
syncDetail.setDelegatedPrimaryKeyValue((String)entityId(eo));
132+
}
133+
}
106134
}
107135

108136
ec.saveChanges();
@@ -112,6 +140,16 @@ public void coordinateChanges(NSNotification notification) {
112140
}
113141
}
114142

143+
private ERXEnterpriseObject eo(Object id, EOEditingContext ec) {
144+
EOKeyGlobalID gid = (EOKeyGlobalID)id;
145+
ERXEnterpriseObject eo = (ERXEnterpriseObject)ec.faultForGlobalID( gid, ec);
146+
return eo;
147+
}
148+
149+
private Object entityId(ERXEnterpriseObject eo) {
150+
return IERXRestDelegate.Factory.delegateForEntityNamed(eo.entityName()).primaryKeyForObject(eo, new ERXRestContext(eo.editingContext()));
151+
}
152+
115153
public class EOSyncEntityFilter implements EOQualifierEvaluation
116154
{
117155
public boolean evaluateWithObject(Object object)

SimpleBlog/Sources/com/wowodc/rest/controllers/BlogEntryController.java

+51-8
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,24 @@
55
import java.util.Locale;
66

77
import com.webobjects.appserver.WOActionResults;
8+
import com.webobjects.appserver.WORedirect;
89
import com.webobjects.appserver.WORequest;
910
import com.webobjects.foundation.NSArray;
11+
import com.webobjects.foundation.NSTimestamp;
1012
import com.wowodc.model.BlogCategory;
1113
import com.wowodc.model.BlogEntry;
14+
import com.wowodc.model.DelegatePKHistory;
1215
import com.wowodc.model.Person;
1316
import com.wowodc.model.SyncInfo;
17+
import com.wowodc.model.enums.SyncInfoStatus;
1418
import com.wowodc.ui.components.BlogEntryDetailPage;
1519

1620
import er.extensions.appserver.ERXHttpStatusCodes;
1721
import er.extensions.appserver.ERXResponse;
1822
import er.extensions.eof.ERXKeyFilter;
1923
import er.rest.ERXRestContext;
24+
import er.rest.format.ERXRestFormat;
25+
import er.rest.routes.ERXRouteUrlUtils;
2026

2127
public class BlogEntryController extends BaseRestController {
2228

@@ -67,20 +73,55 @@ protected ERXKeyFilter filter() {
6773
@Override
6874
public WOActionResults showAction() throws Throwable {
6975
String ifModifiedSinceHeader = this.request().headerForKey("If-Modified-Since");
70-
String ifNoneMatch = this.request().headerForKey("If-None-Match");
76+
String ifNoneMatchHeader = this.request().headerForKey("If-None-Match");
7177
SyncInfo syncDetails = null;
7278
BlogEntry post = null;
79+
String uniqueTitle = routeObjectForKey("uniqueTitle");
7380

74-
if (ifNoneMatch != null) {
75-
syncDetails = SyncInfo.fetchSyncInfo(editingContext(), SyncInfo.ETAG.eq(ifNoneMatch));
81+
if (ifNoneMatchHeader != null) {
82+
syncDetails = SyncInfo.fetchSyncInfo(editingContext(), SyncInfo.ETAG.eq(ifNoneMatchHeader));
7683
if (syncDetails != null) {
77-
return errorResponse(ERXHttpStatusCodes.NOT_MODIFIED);
84+
if (syncDetails.status() == SyncInfoStatus.DELETED) {
85+
return response(ERXHttpStatusCodes.GONE);
86+
} else {
87+
return response(ERXHttpStatusCodes.NOT_MODIFIED);
88+
}
89+
} else {
90+
post = BlogEntry.fetchBlogEntry(editingContext(), BlogEntry.UNIQUE_TITLE.eq(uniqueTitle));
91+
}
92+
} else if (ifModifiedSinceHeader != null) {
93+
java.util.Date ifModifiedSince = formatter.parse(ifModifiedSinceHeader);
94+
syncDetails = SyncInfo.fetchSyncInfo(editingContext(), SyncInfo.LAST_MODIFIED.eq(new NSTimestamp(ifModifiedSince)));
95+
if (syncDetails != null) {
96+
if (syncDetails.status() == SyncInfoStatus.DELETED) {
97+
return response(ERXHttpStatusCodes.GONE);
98+
} else {
99+
return response(ERXHttpStatusCodes.NOT_MODIFIED);
100+
}
78101
} else {
79-
post = routeObjectForKey("blogEntry");
102+
post = BlogEntry.fetchBlogEntry(editingContext(), BlogEntry.UNIQUE_TITLE.eq(uniqueTitle));
80103
}
81104
} else {
82-
post = routeObjectForKey("blogEntry");
83-
syncDetails = SyncInfo.fetchSyncInfo(editingContext(), SyncInfo.TOKEN.eq(post.entityName() + ":" + post.primaryKey()));
105+
post = BlogEntry.fetchBlogEntry(editingContext(), BlogEntry.UNIQUE_TITLE.eq(uniqueTitle));
106+
if (post != null) {
107+
syncDetails = SyncInfo.fetchSyncInfo(editingContext(), SyncInfo.DELEGATED_PRIMARY_KEY_VALUE.eq(post.uniqueTitle()));
108+
if (syncDetails != null) {
109+
if (syncDetails.status() == SyncInfoStatus.DELETED) {
110+
return response(ERXHttpStatusCodes.GONE);
111+
}
112+
}
113+
} else {
114+
NSArray<DelegatePKHistory> histories = DelegatePKHistory.fetchDelegatePKHistories(editingContext(), DelegatePKHistory.DELEGATED_PRIMARY_KEY_VALUE.eq(uniqueTitle), null);
115+
if (histories.count() > 0) {
116+
syncDetails = histories.lastObject().syncInfo();
117+
post = BlogEntry.fetchBlogEntry(editingContext(), BlogEntry.UNIQUE_TITLE.eq(syncDetails.delegatedPrimaryKeyValue()));
118+
WORedirect redirect = new WORedirect(this.context());
119+
redirect.setUrl(ERXRouteUrlUtils.actionUrlForRecord(this.context(), post, "show", ERXRestFormat.html().name(), null, false, false));
120+
return redirect;
121+
} else {
122+
return errorResponse(ERXHttpStatusCodes.NOT_FOUND);
123+
}
124+
}
84125
}
85126

86127
if (syncDetails != null) {
@@ -89,7 +130,7 @@ public WOActionResults showAction() throws Throwable {
89130
try {
90131
clientDateHeader = (java.util.Date)formatter.parseObject(ifModifiedSinceHeader);
91132
if ((clientDateHeader.equals(syncDetails.lastModified())) || (clientDateHeader.after(syncDetails.lastModified()))) {
92-
return errorResponse(ERXHttpStatusCodes.NOT_MODIFIED);
133+
return response(ERXHttpStatusCodes.NOT_MODIFIED);
93134
}
94135
} catch (ParseException e) {
95136
}
@@ -107,6 +148,8 @@ public WOActionResults showAction() throws Throwable {
107148
response = (ERXResponse) response(post, filter());
108149
}
109150

151+
response.setHeader("max-age=300", "Cache-Control");
152+
110153
if (syncDetails != null) {
111154
response.setHeader(formatter.format(syncDetails.lastModified()), "Last-Modified");
112155
response.setHeader(syncDetails.etag(), "Etag");

SimpleBlog/Sources/com/wowodc/rest/delegates/BlogEntryRestDelegate.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
public class BlogEntryRestDelegate extends ERXAbstractRestDelegate {
1414

1515
public Object primaryKeyForObject(Object obj, ERXRestContext context) {
16-
return ((BlogEntry)obj).title();
16+
return ((BlogEntry)obj).uniqueTitle();
1717
}
1818

1919
public Object createObjectOfEntityWithID(EOClassDescription entity, Object id, ERXRestContext context) {
20-
return BlogEntry.createBlogEntry(ERXEC.newEditingContext(), null, new NSTimestamp(), new NSTimestamp(), null, null);
20+
return BlogEntry.createBlogEntry(ERXEC.newEditingContext(), null, new NSTimestamp(), new NSTimestamp(), null, null, null);
2121
}
2222

2323
public Object objectOfEntityWithID(EOClassDescription entity, Object id, ERXRestContext context) {
24-
NSArray<BlogEntry> entries = ERXQ.filtered(BlogEntry.fetchAllBlogEntries(ERXEC.newEditingContext()), ERXQ.is("title", id));
24+
NSArray<BlogEntry> entries = ERXQ.filtered(BlogEntry.fetchAllBlogEntries(ERXEC.newEditingContext()), ERXQ.is("uniqueTitle", id));
2525
return entries.size() == 0 ? null : entries.objectAtIndex(0);
2626
}
2727

SimpleBlogLogic/Resources/SimpleBlog.eomodeld/BlogComment.plist

+1-9
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,7 @@
3333
prototypeName = dateTime;
3434
}
3535
);
36-
attributesUsedForLocking = (
37-
blogEntryID,
38-
commentText,
39-
id,
40-
lastModifed,
41-
personID,
42-
rating,
43-
timestampCreation
44-
);
36+
attributesUsedForLocking = (blogEntryID, commentText, id, personID, rating);
4537
className = "com.wowodc.model.BlogComment";
4638
classProperties = (blogEntry, commentText, lastModifed, person, rating, timestampCreation);
4739
externalName = BlogComment;

SimpleBlogLogic/Resources/SimpleBlog.eomodeld/BlogEntry.plist

+13-3
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,21 @@
99
name = timestampCreation;
1010
prototypeName = dateTime;
1111
},
12-
{columnName = title; name = title; prototypeName = varchar255; }
12+
{columnName = title; name = title; prototypeName = varchar255; },
13+
{columnName = uniqueTitle; name = uniqueTitle; prototypeName = varchar255; }
1314
);
14-
attributesUsedForLocking = (body, id, lastModifed, personID, timestampCreation, title);
15+
attributesUsedForLocking = (body, id, personID, title, uniqueTitle);
1516
className = "com.wowodc.model.BlogEntry";
16-
classProperties = (blogComments, body, categories, lastModifed, person, timestampCreation, title);
17+
classProperties = (
18+
blogComments,
19+
body,
20+
categories,
21+
lastModifed,
22+
person,
23+
timestampCreation,
24+
title,
25+
uniqueTitle
26+
);
1727
externalName = BlogEntry;
1828
fetchSpecificationDictionary = {};
1929
name = BlogEntry;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
attributes = (
3+
{
4+
columnName = delegatedPrimaryKeyValue;
5+
name = delegatedPrimaryKeyValue;
6+
prototypeName = varchar255;
7+
},
8+
{allowsNull = N; name = id; prototypeName = id; },
9+
{
10+
allowsNull = N;
11+
columnName = syncInfoID;
12+
name = syncInfoID;
13+
prototypeName = id;
14+
}
15+
);
16+
attributesUsedForLocking = (delegatedPrimaryKeyValue, id, syncInfoID);
17+
className = "com.wowodc.model.DelegatePKHistory";
18+
classProperties = (delegatedPrimaryKeyValue, syncInfo);
19+
externalName = DelegatePKHistory;
20+
fetchSpecificationDictionary = {};
21+
name = DelegatePKHistory;
22+
primaryKeyAttributes = (id);
23+
relationships = (
24+
{
25+
destination = SyncInfo;
26+
isMandatory = Y;
27+
isToMany = N;
28+
joinSemantic = EOInnerJoin;
29+
joins = ({destinationAttribute = id; sourceAttribute = syncInfoID; });
30+
name = syncInfo;
31+
}
32+
);
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
attributes = (
3+
{
4+
allowsNull = Y;
5+
columnName = delegatedPrimaryKeyValue;
6+
name = delegatedPrimaryKeyValue;
7+
prototypeName = varchar255;
8+
},
9+
{columnName = etag; name = etag; prototypeName = varchar255; },
10+
{allowsNull = N; name = id; prototypeName = id; },
11+
{columnName = lastModified; name = lastModified; prototypeName = dateTime; },
12+
{
13+
columnName = status;
14+
name = status;
15+
prototypeName = javaEnum;
16+
valueClassName = "com.wowodc.model.enums.SyncInfoStatus";
17+
},
18+
{columnName = token; name = token; prototypeName = varchar255; }
19+
);
20+
attributesUsedForLocking = (delegatedPrimaryKeyValue, etag, id, status, token);
21+
className = "com.wowodc.model.SyncInfo";
22+
classProperties = (
23+
delegatedPrimaryKeyValue,
24+
delegatePKHistories,
25+
etag,
26+
lastModified,
27+
status,
28+
token
29+
);
30+
externalName = SyncInfo;
31+
fetchSpecificationDictionary = {};
32+
name = SyncInfo;
33+
primaryKeyAttributes = (id);
34+
relationships = (
35+
{
36+
destination = DelegatePKHistory;
37+
isToMany = Y;
38+
joinSemantic = EOInnerJoin;
39+
joins = ({destinationAttribute = syncInfoID; sourceAttribute = id; });
40+
name = delegatePKHistories;
41+
}
42+
);
43+
}

SimpleBlogLogic/Resources/SimpleBlog.eomodeld/index.eomodeld

+2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
{className = EOGenericRecord; name = BlogCategoryEntry; },
77
{className = "com.wowodc.model.BlogComment"; name = BlogComment; },
88
{className = "com.wowodc.model.BlogEntry"; name = BlogEntry; },
9+
{className = "com.wowodc.model.DelegatePKHistory"; name = DelegatePKHistory; },
910
{className = "com.wowodc.model.Person"; name = Person; },
1011
{className = "com.wowodc.model.Role"; name = Role; },
12+
{className = "com.wowodc.model.SyncInfo"; name = SyncInfo; },
1113
{className = EOGenericRecord; name = XPersonRole; }
1214
);
1315
}

SimpleBlogLogic/Sources/com/wowodc/model/BlogEntry.java

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import com.webobjects.eocontrol.EOEditingContext;
66
import com.webobjects.foundation.NSTimestamp;
77

8+
import er.extensions.foundation.ERXStringUtilities;
9+
810
public class BlogEntry extends com.wowodc.model.generated._BlogEntry {
911
@SuppressWarnings("unused")
1012
private static Logger log = Logger.getLogger(BlogEntry.class);
@@ -15,5 +17,16 @@ public void init(EOEditingContext ec) {
1517
setPersonRelationship(Person.currentUser(ec));
1618
setLastModifed(new NSTimestamp());
1719
}
20+
21+
@Override
22+
public void setTitle(String value) {
23+
super.setTitle(value);
24+
String uniqueTitle = ERXStringUtilities.toLowerCase(value.replace(" ", "-"));
25+
if (uniqueTitle.length() > 252) {
26+
uniqueTitle = uniqueTitle.substring(0, 252);
27+
}
28+
uniqueTitle = uniqueTitle + "-" + this.primaryKeyInTransaction();
29+
setUniqueTitle(uniqueTitle);
30+
}
1831

1932
}

0 commit comments

Comments
 (0)