Thanks for the review Craig. Please find attached a cumulative patch
that addresses your review comments.
Please note that this patch contains some additional changes that I had
missed last time. Following is a summary of additional changes.
HibernateBookMarkManagerImpl:
removeBookmark(BookmarkData bookmark) now removes bookmark from the
parent folder's collection of bookmarks also
FolderData, UserData,WeblogEntryData:
Replaced the cascade="all-delete-orphan" with cascade="all"
All the business unit tests passes with the attached patch. ( I am
getting a failure while running org.apache.roller.ui.UIPluginManagerTest
with or without these changes on a freshly checked out workspace)
Thanks,
Mitesh
Craig L Russell wrote:
> Hi Mitesh,
>
> This is a good analysis of a problem that I've seen as well. Comments
> follow...
>
> On Dec 18, 2006, at 11:03 AM, Mitesh Meswani wrote:
>
>> Apache mail host seemed to remove the text of the mail.
>> Resending.........
>>
>> Hi,
>>
>> I am working to port Roller's backend code to use JPA/JDO (see
>> trunk/sandbox/jdobackend). I ran across a dependency on Hibernate
>> specific feature that can be easily removed with some refactoring.
>
> This Hibernate feature is not portable to either JDO or JPA, and I
> think it's a bit confusing when reading the code, since you have to
> either be a Hibernate expert or look up the behavior in the Hibernate
> documentation to figure out what is going on.
>
> So I support the notion of making relationship handling explicit and
> thereby removing the confusion.
>
> For Hibernate's "orphan" relationships, both sides of the relationship
> are affected by the operation, which I'd characterize as a "delete"
> operation that has a side effect of removing the deleted instance from
> the collection on the "owner" side. This is in contrast to Hibernate's
> definition of removing an instance which has a side effect of deleting
> the instance from the database.
>
> If the deleted instance had its own XXXManager, I'd favor putting the
> behavior into the XXXManager interface, since there is supposed to be
> no knowledge of the persistence layer inside the pojo layer. And in
> fact, it's unlikely that such a pojo would have such a behavior since
> the nature of the relationship is a composition relationship, and it
> makes sense that the behavior be in the aggregate instance manager.
>
> So I agree with your proposal to remove the method from the pojo and
> instead add it to the XXXManager interface that can both remove the
> owned instance from the collection and remove the owned instance from
> the database.
>
> As a performance optimization, we should also consider changing the
> relationship from a Set to a Map. This would avoid the issue of
> iterating the entire Set just to find the right entry. This is a
> portable JPA/JDO/Hibernate construct (to be further looked at later).
>
> In the code that follows, sometimes you remove the entity from the
> collection and then remove it from the database. In other cases, you
> remove it from the database and then remove it from the collection. We
> should probably be consistent in the order (although it should not
> matter; I don't believe that either JDO or JPA requires an order).
>
>>
>> ...snipped
>
>> Index: src/org/apache/roller/pojos/UserData.java
>> ===================================================================
>> --- src/org/apache/roller/pojos/UserData.java (revision 488141)
>> +++ src/org/apache/roller/pojos/UserData.java (working copy)
>> @@ -434,38 +434,14 @@
>> return false;
>> }
>> //Moved the method revokeRole to HibernateUserManagerImpl
>> - /**
>> - * Revokes specified role from user.
>> - */
>> - public void revokeRole(String roleName) throws RollerException {
>> - RoleData removeme = null;
>> - Iterator iter = roles.iterator();
>> - while (iter.hasNext()) {
>> - RoleData role = (RoleData) iter.next();
>> - if (role.getRole().equals(roleName)) {
>> - removeme = role;
>> - }
>> - }
>> - - /*
>> - * NOTE: we do this outside the loop above because we are
>> not allowed
>> - * to modify the contents of the Set while we are iterating
>> over it.
>> - * doing so causes a ConcurrentModificationException
>
> This looks like a bug to me. The algorithm iterates the collection and
> actually ignores all but the last role found. Since the semantic is
> that there should be only one such instance, it should stop at the
> first match of role name and therefore there is no
> ConcurrentModificationException.
>
>> public void savePage(WeblogTemplate data) throws RollerException;
>> Index:
>> src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
>> ===================================================================
>> ---
>> src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
>> (revision 488141)
>> +++
>> src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
>> (working copy)
>> @@ -26,6 +26,8 @@
>> import java.util.List;
>> import java.util.Map;
>> import java.util.TreeMap;
>> +import java.util.Collection;
>> +
>> import org.apache.roller.pojos.StatCount;
>> import org.hibernate.Criteria;
>> import org.hibernate.HibernateException;
>> @@ -58,6 +60,7 @@
>> import org.apache.roller.pojos.WeblogCategoryData;
>> import org.apache.roller.pojos.WeblogEntryData;
>> import org.apache.roller.pojos.WebsiteData;
>> +import org.apache.roller.pojos.RoleData;
>> import org.hibernate.Query;
>> @@ -406,6 +409,29 @@
>> website.removePermission(target);
>> this.strategy.remove(target);
>> }
>> // Method moved from UserData to here
>> +
>> + public void revokeRole(String roleName, UserData user) throws
>> RollerException {
>> + RoleData removeme = null;
>> + Collection roles = user.getRoles();
>> + Iterator iter = roles.iterator();
>> + while (iter.hasNext()) {
>> + RoleData role = (RoleData) iter.next();
>> + if (role.getRole().equals(roleName)) {
>> + removeme = role;
>
> I'd think that the remove operations should be here, followed by break;
>
> The rest of the code looks good.
>
> Craig
>
> Craig Russell
> Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
> 408 276-5638 mailto:[EMAIL PROTECTED]
> P.S. A good JDO? O, Gasp!
>
Index: src/org/apache/roller/pojos/FolderData.java
===================================================================
--- src/org/apache/roller/pojos/FolderData.java (revision 488141)
+++ src/org/apache/roller/pojos/FolderData.java (working copy)
@@ -274,7 +274,7 @@
*
* @roller.wrapPojoMethod type="pojo-collection"
class="org.apache.roller.pojos.BookmarkData"
*
- * @hibernate.set lazy="true" order-by="name" inverse="true"
cascade="all-delete-orphan"
+ * @hibernate.set lazy="true" order-by="name" inverse="true" cascade="all"
* @hibernate.collection-key column="folderid"
* @hibernate.collection-one-to-many
class="org.apache.roller.pojos.BookmarkData"
*/
@@ -296,15 +296,6 @@
getBookmarks().add(bookmark);
}
-
- /**
- * Remove a boomkark from folder.
- */
- public void removeBookmark(BookmarkData bookmark) {
- getBookmarks().remove(bookmark);
- }
-
-
/**
* @roller.wrapPojoMethod type="pojo-collection"
class="org.apache.roller.pojos.BookmarkData"
*
Index: src/org/apache/roller/pojos/UserData.java
===================================================================
--- src/org/apache/roller/pojos/UserData.java (revision 488141)
+++ src/org/apache/roller/pojos/UserData.java (working copy)
@@ -404,7 +404,7 @@
/**
- * @hibernate.set lazy="true" inverse="true" cascade="all-delete-orphan"
+ * @hibernate.set lazy="true" inverse="true" cascade="all"
* @hibernate.collection-key column="userid"
* @hibernate.collection-one-to-many
class="org.apache.roller.pojos.RoleData"
*/
@@ -434,38 +434,14 @@
return false;
}
-
/**
- * Revokes specified role from user.
- */
- public void revokeRole(String roleName) throws RollerException {
- RoleData removeme = null;
- Iterator iter = roles.iterator();
- while (iter.hasNext()) {
- RoleData role = (RoleData) iter.next();
- if (role.getRole().equals(roleName)) {
- removeme = role;
- }
- }
-
- /*
- * NOTE: we do this outside the loop above because we are not allowed
- * to modify the contents of the Set while we are iterating over it.
- * doing so causes a ConcurrentModificationException
- */
- if(removeme != null) {
- roles.remove(removeme);
- }
- }
-
-
- /**
* Grant to user role specified by role name.
*/
public void grantRole(String roleName) throws RollerException {
if (!hasRole(roleName)) {
RoleData role = new RoleData(null, this, roleName);
roles.add(role);
+ role.setUser(this);
}
}
Index: src/org/apache/roller/pojos/WeblogEntryData.java
===================================================================
--- src/org/apache/roller/pojos/WeblogEntryData.java (revision 488141)
+++ src/org/apache/roller/pojos/WeblogEntryData.java (working copy)
@@ -353,7 +353,7 @@
*
* @roller.wrapPojoMethod type="pojo-collection"
class="org.apache.roller.pojos.EntryAttributeData"
* @ejb:persistent-field
- * @hibernate.set lazy="true" order-by="name" inverse="true"
cascade="all-delete-orphan"
+ * @hibernate.set lazy="true" order-by="name" inverse="true" cascade="all"
* @hibernate.collection-key column="entryid" type="String"
* @hibernate.collection-one-to-many
class="org.apache.roller.pojos.EntryAttributeData"
*/
@@ -404,12 +404,8 @@
att.setValue(value);
}
}
- public void removeEntryAttribute(String name) throws RollerException {
- EntryAttributeData att = (EntryAttributeData)attMap.get(name);
- if (att != null) {
- attMap.remove(att);
- attSet.remove(att);
- }
+ public void onRemoveEntryAttribute(EntryAttributeData att) throws
RollerException {
+ attMap.remove(att);
}
//-------------------------------------------------------------------------
@@ -609,7 +605,7 @@
*
* @ejb:persistent-field
*
- * @hibernate.set lazy="true" order-by="name" inverse="true"
cascade="all-delete-orphan"
+ * @hibernate.set lazy="true" order-by="name" inverse="true" cascade="all"
* @hibernate.collection-key column="entryid"
* @hibernate.collection-one-to-many
class="org.apache.roller.pojos.WeblogEntryTagData"
*/
@@ -654,16 +650,10 @@
addedTags.add(name);
}
- public void removeTag(String name) throws RollerException {
- for (Iterator it = tagSet.iterator(); it.hasNext();) {
- WeblogEntryTagData tag = (WeblogEntryTagData) it.next();
- if (tag.getName().equals(name)) {
- removedTags.add(name);
- it.remove();
- }
- }
+ public void onRemoveTag(String name) throws RollerException {
+ removedTags.add(name);
}
-
+
public Set getAddedTags() {
return addedTags;
}
@@ -696,9 +686,10 @@
newTags.remove(tag.getName());
}
}
-
+
+ WeblogManager weblogManager =
RollerFactory.getRoller().getWeblogManager();
for (Iterator it = removeTags.iterator(); it.hasNext();) {
- removeTag((String) it.next());
+ weblogManager.removeWeblogEntryTag((String) it.next(), this);
}
for (Iterator it = newTags.iterator(); it.hasNext();) {
Index: src/org/apache/roller/business/UserManager.java
===================================================================
--- src/org/apache/roller/business/UserManager.java (revision 488141)
+++ src/org/apache/roller/business/UserManager.java (working copy)
@@ -27,7 +27,6 @@
import org.apache.roller.pojos.UserData;
import org.apache.roller.pojos.WebsiteData;
-
/**
* Manages users, weblogs, permissions, and weblog pages.
*/
@@ -325,8 +324,15 @@
public void retireUser(WebsiteData website, UserData user)
throws RollerException;
-
/**
+ * Retire user from a website
+ * @param roleName Name of the role to be revoked
+ * @param user User for whom the role is to be revoked
+ */
+ public void revokeRole(String roleName, UserData user)
+ throws RollerException;
+
+ /**
* Store page.
*/
public void savePage(WeblogTemplate data) throws RollerException;
Index: src/org/apache/roller/business/WeblogManager.java
===================================================================
--- src/org/apache/roller/business/WeblogManager.java (revision 488141)
+++ src/org/apache/roller/business/WeblogManager.java (working copy)
@@ -194,9 +194,22 @@
* @return Collection of WeblogEntryData objects.
*/
public List getWeblogEntriesPinnedToMain(Integer max) throws
RollerException;
-
-
+
/**
+ * Remove attribute with given name from given WeblogEntryData
+ * @param name Name of attribute to be removed
+ */
+ public void removeWeblogEntryAttribute(String name, WeblogEntryData entry)
+ throws RollerException;
+
+ /**
+ * Remove tag with given name from given WeblogEntryData
+ * @param name Name of tag to be removed
+ */
+ public void removeWeblogEntryTag(String name, WeblogEntryData entry)
+ throws RollerException;
+
+ /**
* Save weblog category.
*/
public void saveWeblogCategory(WeblogCategoryData cat) throws
RollerException;
Index:
src/org/apache/roller/business/hibernate/HibernateBookmarkManagerImpl.java
===================================================================
--- src/org/apache/roller/business/hibernate/HibernateBookmarkManagerImpl.java
(revision 488141)
+++ src/org/apache/roller/business/hibernate/HibernateBookmarkManagerImpl.java
(working copy)
@@ -81,10 +81,13 @@
public void removeBookmark(BookmarkData bookmark) throws RollerException {
+ //Remove the bookmark from its parent folder
+ bookmark.getFolder().getBookmarks().remove(bookmark);
+ //Now remove it from database
this.strategy.remove(bookmark);
-
// update weblog last modified date. date updated by saveWebsite()
-
RollerFactory.getRoller().getUserManager().saveWebsite(bookmark.getWebsite());
+ RollerFactory.getRoller().getUserManager()
+ .saveWebsite(bookmark.getWebsite());
}
Index: src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
===================================================================
--- src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
(revision 488141)
+++ src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
(working copy)
@@ -26,6 +26,8 @@
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.Collection;
+
import org.apache.roller.pojos.StatCount;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
@@ -58,6 +60,7 @@
import org.apache.roller.pojos.WeblogCategoryData;
import org.apache.roller.pojos.WeblogEntryData;
import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.pojos.RoleData;
import org.hibernate.Query;
@@ -406,6 +409,20 @@
website.removePermission(target);
this.strategy.remove(target);
}
+
+ public void revokeRole(String roleName, UserData user) throws
RollerException {
+ RoleData removeme = null;
+ Collection roles = user.getRoles();
+ Iterator iter = roles.iterator();
+ while (iter.hasNext()) {
+ RoleData role = (RoleData) iter.next();
+ if (role.getRole().equals(roleName)) {
+ iter.remove();
+ this.strategy.remove(role);
+ }
+ }
+ }
+
public WebsiteData getWebsite(String id) throws RollerException {
return (WebsiteData) this.strategy.load(id,WebsiteData.class);
Index: src/org/apache/roller/business/hibernate/HibernateWeblogManagerImpl.java
===================================================================
--- src/org/apache/roller/business/hibernate/HibernateWeblogManagerImpl.java
(revision 488141)
+++ src/org/apache/roller/business/hibernate/HibernateWeblogManagerImpl.java
(working copy)
@@ -50,6 +50,7 @@
import org.apache.roller.pojos.WeblogEntryTagData;
import org.apache.roller.pojos.WeblogEntryTagAggregateData;
import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.pojos.EntryAttributeData;
import org.apache.roller.util.DateUtil;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
@@ -60,13 +61,7 @@
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
-import org.hibernate.dialect.DerbyDialect;
-import org.hibernate.dialect.Dialect;
-import org.hibernate.dialect.OracleDialect;
-import org.hibernate.dialect.SQLServerDialect;
-import org.hibernate.engine.SessionFactoryImplementor;
-
/**
* Hibernate implementation of the WeblogManager.
*/
@@ -547,10 +542,39 @@
throw new RollerException(e);
}
}
-
-
- public WeblogEntryData getWeblogEntryByAnchor(WebsiteData website, String
anchor)
+
+ public void removeWeblogEntryAttribute(String name, WeblogEntryData entry)
throws RollerException {
+ for (Iterator it = entry.getEntryAttributes().iterator();
it.hasNext();) {
+ EntryAttributeData entryAttribute = (EntryAttributeData) it.next();
+ if (entryAttribute.getName().equals(name)) {
+ //Call back the entity to adjust its internal state
+ entry.onRemoveEntryAttribute(entryAttribute);
+ //Remove it from the collection
+ it.remove();
+ //Remove it from database
+ this.strategy.remove(entryAttribute);
+ }
+ }
+ }
+
+ public void removeWeblogEntryTag(String name, WeblogEntryData entry)
+ throws RollerException {
+ for (Iterator it = entry.getTags().iterator(); it.hasNext();) {
+ WeblogEntryTagData tag = (WeblogEntryTagData) it.next();
+ if (tag.getName().equals(name)) {
+ //Call back the entity to adjust its internal state
+ entry.onRemoveTag(name);
+ //Remove it from the collection
+ it.remove();
+ //Remove it from database
+ this.strategy.remove(tag);
+ }
+ }
+ }
+
+ public WeblogEntryData getWeblogEntryByAnchor(WebsiteData website, String
anchor)
+ throws RollerException {
if (website == null)
throw new RollerException("Website is null");
Index:
src/org/apache/roller/ui/authoring/struts/actions/WeblogEntryFormAction.java
===================================================================
---
src/org/apache/roller/ui/authoring/struts/actions/WeblogEntryFormAction.java
(revision 488141)
+++
src/org/apache/roller/ui/authoring/struts/actions/WeblogEntryFormAction.java
(working copy)
@@ -513,10 +513,11 @@
}
if (!valid || empty) {
mLogger.debug("Removing MediaCast attributes");
+ WeblogManager weblogManager =
RollerFactory.getRoller().getWeblogManager();
try {
- entry.removeEntryAttribute("att_mediacast_url");
- entry.removeEntryAttribute("att_mediacast_type");
- entry.removeEntryAttribute("att_mediacast_length");
+ weblogManager.removeWeblogEntryAttribute("att_mediacast_url",
entry);
+ weblogManager.removeWeblogEntryAttribute("att_mediacast_type",
entry);
+
weblogManager.removeWeblogEntryAttribute("att_mediacast_length", entry);
} catch (RollerException e) {
mLogger.error("ERROR removing invalid MediaCast attributes");
}
Index: src/org/apache/roller/ui/admin/struts/formbeans/UserAdminForm.java
===================================================================
--- src/org/apache/roller/ui/admin/struts/formbeans/UserAdminForm.java
(revision 488141)
+++ src/org/apache/roller/ui/admin/struts/formbeans/UserAdminForm.java
(working copy)
@@ -23,6 +23,9 @@
import org.apache.struts.action.ActionMapping;
import org.apache.roller.RollerException;
+import org.apache.roller.business.Roller;
+import org.apache.roller.business.RollerImpl;
+import org.apache.roller.business.RollerFactory;
import org.apache.roller.pojos.UserData;
import org.apache.roller.ui.authoring.struts.forms.UserForm;
@@ -118,7 +121,7 @@
}
else
{
- user.revokeRole("admin");
+ RollerFactory.getRoller().getUserManager().revokeRole("admin",
user);
}
}
Index: tests/org/apache/roller/business/WeblogEntryTest.java
===================================================================
--- tests/org/apache/roller/business/WeblogEntryTest.java (revision
488141)
+++ tests/org/apache/roller/business/WeblogEntryTest.java (working copy)
@@ -415,8 +415,8 @@
TestUtils.endSession(true);
entry = mgr.getWeblogEntry(id);
- entry.removeTag("testtag");
- entry.removeTag("testtag2");
+ mgr.removeWeblogEntryTag("testtag", entry);
+ mgr.removeWeblogEntryTag("testtag2", entry);
mgr.saveWeblogEntry(entry);
TestUtils.endSession(true);
Index: tests/org/apache/roller/business/BookmarkTest.java
===================================================================
--- tests/org/apache/roller/business/BookmarkTest.java (revision 488141)
+++ tests/org/apache/roller/business/BookmarkTest.java (working copy)
@@ -328,7 +328,7 @@
bookmarkb = (BookmarkData)testFolder.getBookmarks().iterator().next();
// Remove one bookmark
- testFolder.removeBookmark(bookmarka);
+ bmgr.removeBookmark(bookmarka);
bmgr.saveFolder(testFolder);
TestUtils.endSession(true);
Index: tests/org/apache/roller/business/UserTest.java
===================================================================
--- tests/org/apache/roller/business/UserTest.java (revision 488141)
+++ tests/org/apache/roller/business/UserTest.java (working copy)
@@ -186,7 +186,7 @@
assertTrue(user.hasRole("admin"));
// remove role
- user.revokeRole("admin");
+ mgr.revokeRole("admin",user);
mgr.saveUser(user);
TestUtils.endSession(true);