comments inline ...

David M Johnson wrote:

On Mar 26, 2006, at 8:34 PM, Allen Gilliland wrote:
I have updated my backend refactoring proposal to include a bit more implementation details. http://rollerweblogger.org/wiki/Wiki.jsp?page=Proposal_BackendRefactorings

I have basically listed all the manager classes which use the database and then listed each method of those classes which involves writing data.

I still stand behind my proposal that we should remove all persistence specific logic from existing outside the business/presentation layer. I am planning to begin work on this for April and hopefully will get it completed for Roller 2.3

The more I think about this idea, the more I like it. I'd like to see the Roller backend be a self-contained API and this appears to be the way to achieve that.

cool.  glad to hear my powers of persuasion aren't totally defunct ;)


But I'm still opposed to doing this work in the trunk. It involves dramatic sweeping changes to the code base and requires that almost every Struts action be rewritten. It should done done in a separate branch and not merged back until it's been tested an evaluated.

yep.  i think that's a good idea as well.


Plus, I've got a couple questions about the plan:

1) You propose to remove the setUser() and getUser() methods from the Roller interface. What is the plan for determining that the current user has permission to perform an operation?

ah. i didn't address this specifically anywhere in the proposal, but i believe there are a couple problems with trying to do permissions checking the way it is now. my feeling is that this type of check should not be happening at the persistence layer because the persistence layer has no way of forcing proper authentication.

right now the system is basically trying to force permission checks on access to java methods, and i think that is a little tough to do. i think it's better to leave the persistence layer open to anyone who has access to the java code and instead expect that our struts actions are ensuring proper permissions. so instead of trying to enforce permissions on java methods we are enforcing permissions on access to various struts actions.

also, we don't really have the right code in place to effectively enforce permissions on persistence methods for a couple reasons.

1. we never force the appropriate user to be set before a given method is called. there is nothing in the code preventing someone from just always setting the user to UserData.SYSTEM_USER. and the code that does the permissions checking just assumes that the set user is correct.

2. the user can be changed at any time and isn't required to stay the same once it is set. so even if the code does properly set the user it can just be overridden. the same way it happens in the PropertiesManagerImpl.init() method.

so we would be pushing the logic outside the persistence layer and expecting someone else to do authentication/authorization, not the persistence layer.


2) You mention that some operation are "single step persistence operations which do not require special transaction considerations." What does that mean in practice? Does each of these operations do it's own begin() and then end with a commit() or rollback()? Will these single step operations be called from the multi-step operations?

this is ultimately up to the implementor of the persistence strategy. they can do things however they want, as long as they ensure that a call to any method that writes data is properly transacted.

but yes, i imagine that a singe step operation like saveWeblogComment(comment) would just begin, then commit/rollback.

speaking specifically about Hibernate, note that calling begin() will open a new transaction if needed, or join the current transaction if one is already open. after a commit or rollback a new transaction can begin if needed, but this will all happen within the same Session.


3) What is the plan for unit testing the changes to the Struts Actions and other presentation classes?

well, most of our unit tests should still work fine, we just need to rip out the methods I mentioned in the proposal. some of the unit tests will have to be rewritten/updated though.


4) How did you come up with the list of changes? I assume you reviewed each of the Struts actions. Did you also review the MetaWeblog API and Atom protocol implementations?

I built the list initially from our current Manager interfaces and then modified based on struts actions. I didn't look extensively at the MetaWeblog API or Atom protocol.

-- Allen



- Dave



-- Allen


Allen Gilliland wrote:
Anil Gangolli wrote:
Allen:

Your suggested pattern is workable, but you'll need to make begin() and commit() conditional and smart about transaction demarcation; it is a common pattern to conditionally begin a transaction only if there isn't one on the current thread, and you'll find that in many infrastructures (see my comments on Section 2 in the design proposal).
I agree with this and believe it's fairly easy to do.

What happens if you don't have that logic? You'll find that the operations that you choose to make transactional can't be easily composed, which is a very awkward property to have. If you ever need to compose two operations, you would need to write a third method that includes all of the operations of the originals, and not just calling the two original methods, but copying their innards. I actually went through a period of trying to code apps this way in the 90's before infrastructures like EJB started to normalize the conditional demarcation patterns in Java. Lesson learned.
You are correct, that is absolutely a possible ramification and we could end up making it more difficult to properly wrap some steps together in a transaction. However, I would also argue that the reverse scenerio is true for our current setup using more open transaction demarcation. Allowing business/presentation code to control transaction demarcation allows for transactions to become overly inclusive and actually end up including more operations than they should.

The patterns for transaction demarcation in the dispatch layers involve three parts: (1) demarcation for requests on a per-request basis in a Filter or some other centralized dispatch point (e.g. in Axis or Struts apps in extensions of these servlets wrapping the entry points) (2) for scheduled tasks, wrapping the run() method in a base class (3) providing a mechanism to get an independent transaction for individual units of work at smaller granularities (REQUIRES_NEW style semantics), which I expect would be used very rarely in Roller. Patterns like this work well for two-tier web apps like Roller where the persistence container and the webapp container are the same. They don't work (or require much stronger transaction infrastructure) if you separate the webapp container from the persistence container which does not apply in Roller's case.
In order to get a better grasp on some Hibernate details I am currently skimming through "Hibernate in Action" and they say ... "We merely observe that, in our opinion, overengineering has been endemic in the Java community, and overly ambitious multilayered architectures have significanly contributed to the cose of Java development and to the perceived complexity of J2EE. On the other hand, we do agree that a dedicated persistence layer is a sensible choice for most applications and that persistence-related code shouldn't be moxed with business logic or presentation." (page 296) In their examples for designing a layered application (webapp specifically) they do not provide transaction demarcation outside of the DAO layer. Of course this is only a single opinion and does not mean it's what's best for Roller, but I would say that my opinions are more in line with the sentiments from the quote above. Speaking more specifically about applying transaction demarcation at points in the dispatch layer ... I think I still see this as allowing persistence logic into the business/presentation layer. The other reason why this makes me nervous is that I think it creates some potential for two situations ... 1. catching a persistence exception too late. if the transaction demarcation doesn't actually commit the transaction data until a while after the app thinks the data has been written then we may end up putting ourselves in a situation where we catch an exception after we have already commited some of the response and are no longer in a situation where we can respond appropriately. This seems especially true if we consider each request/response cycle as a single transaction and only flush/commit the data in a filter as we exit the app. 2. commiting data changes we don't want, specifically users being bad with their templates. I suppose our wrapper objects may take care of the potential problem with users making changes to their data that we don't want, but there may also be an issue of app code doing the same thing. It has always seemed ideal to me to only commit changes specifically when needed. If a filter simply commits data at the end of the request/response cycle there is no guarantee that someone hasn't modified an object that is not meant to be saved back to the database.

Like your proposal, the dispatch-level patterns remove begin() and commit() from app code, and we should end up with only a few places that call them.
I like the idea of the dispatch-level pattern much more than our current pattern. After I spend a bit more time on the proposal and get things more flushed out I think I'll have a better idea of which approaches make the most sense.
-- Allen

--a.


Allen Gilliland wrote:

On Thu, 2006-03-09 at 14:21, David M Johnson wrote:

On Mar 9, 2006, at 5:02 PM, Allen Gilliland wrote:

i feel like we are still a little disconnected here. my approach will not alter anything about our ability to use the open session in view pattern. the only difference i see is the difference between these 2 code blocks ...

old way:
begin();
website.store();
commit();

new way:
WeblogManager wmgr = roller.getWeblogManager();
wmgr.saveWebsite(website);


But what if saving a website is just one step in a multi-part transaction? OK, scratch that. If you start by doing the analysis and designing one of the new Manager interfaces, I think we'll have a much better understanding of this proposed refactoring.



i'll just give the short answer now. usually things that are multipart operations are still part of a single logical operation. in fact, we already do this in some places. take a look at the UserManager.createWebsite() method. that is a logical method which wraps a series of persistence operations. so the difference would be ...

old way (in struts action or servlet):
begin();
// setup website
website.store();
// setup template
template.store();
// setup category
category.store();
// etc etc
commit();

new way (logical method in manager class):
UserManager umgr = roller.getUserManager();
umgr.createWebsite(newWebsite, possible, other, data);

when i use the new way i only care wether or not the operation succeeded and i don't have to think about the persistence implications because they are hidden from me. now if it turns out that we have way too many transactions that would need their own logical method in a manager class then maybe this approach won't work, but i believe that in most request/response cycles the operations are pretty isolated and simple. in most of them you are only creating/updating/deleting a single object at a time.

-- Allen



- Dave











Reply via email to