Language:
None specified     Change language:
Pastebin: 117045
Author: Anonymous
Subject: Transfer AOP and execute() issues
Created: 2009-07-03 23:07:46
Download and save
Toggle line numbers
1Ok, so here is my setup... 
2 
3I am using an InnoDB on MySQL 5 with both CF8 and Railo and experiecing the same behavior on both platforms. 
4 
5To be sure that my setup allows for transactions, I have successfully tested using <cftransaction> around queries, which does work properly and rolls back failed transactions appropriately on both platforms. I have also successfully used AOP advise from Transfer on my service layer beans from ColdSpring and also using the execute() method from my TransferTransaction bean on a method in my handler to execute another method within the handler inside a transaction. Both working properly as expected. Transactions are supported, Transfer AOP advise is weaved and supoorted and the execute() method is supported on both CF8 and Railo on my datasource. 
6 
7So, my application that works as it should looks like this: 
8 
9My handler: ehGeneral.cfc 
10 
11I inject Transfer into the instance scope of the handler from ColdSpring: 
12 
13 <cfproperty name="Transfer" type="ioc" scope="instance"> 
14 
15I have a method in the handler: createUsers() 
16 
17In this method I have: 
18 
19 <cfscript> 
20 
21 // get userService from ColdSpring 
22 userService = getPlugin("ioc").getBean("userService"); 
23 
24 // create new user 
25 user = instance.Transfer.new("users.user"); 
26 user.setEmail("test"); 
27 user.setPassword("test"); 
28 
29 // create second user with bad information 
30 user = instance.Transfer.new("users.user"); 
31 user.setEmail("test"); 
32 // password length is too long, limit is 4 
33 // this will cause an error inside the transaction 
34 user.setPassword("testtest"); 
35 
36 // save users 
37 userService.saveusers(user,user2); 
38 
39 </cfscript> 
40 
41My userService beans looks like this: 
42 
43 <bean id="userService" class="model.users.userService"> 
44 <constructor-arg name="transfer"> 
45 <ref bean="Transfer" /> 
46 </constructor-arg> 
47 <constructor-arg name="transaction"> 
48 <ref bean="TransferTransaction" /> 
49 </constructor-arg> 
50 <constructor-arg name="userGateway"> 
51 <ref bean="userGateway"/> 
52 </constructor-arg> 
53 </bean> 
54 
55It receives TransferTransaction as a constructor argument. 
56 
57In the userService.cfc init() method I have: 
58 
59<!--- advise saveusers() and trace when the advice is applied ---> 
60 <cfset arguments.transaction.advise(this, "saveusers", true)> 
61 
62My saveusers() method is this: 
63 
64 <cffunction name="saveusers" access="public" output="false" returntype="void"> 
65 <cfargument name="user" type="any" required="true" /> 
66 <cfargument name="user2" type="any" required="true" /> 
67 
68 <cfset variables.transfer.save(arguments.user) /> 
69 <cfset variables.transfer.save(arguments.user2) /> 
70 </cffunction> 
71 
72So, as far as this setup is concerned, when the ehGeneral.createUsers event happens, the userService() bean is grabbed from ColdSpring, then two new user objects are created, one with valid information, one with a password that is too long, which will cause a failure when the userService attempts to save it. After creating the two objects, userService.saveusers() is called, passing user1 and user2 to be saved. Since the userService bean had advice applied in its init() method to advise the method saveusers() this call to the userService is wrapped in a transaction. The transaction first inserts user1 into the database, then attempts to insert user2 but fails because of the invalid password length. The transaction is rolled back and neither user shows up in the database. In the trace I see advice was weaved for userService.saveusers() as expected. So, this works perfectly on both Railo and CF8. 
73 
74 
75Alternatively, I can user the execute() method of the transaction instead of advice in my userService: 
76 
77<!--- proxy method to wrap saveusers in a transaction ---> 
78 <cffunction name="saveusers" access="public" output="false" returntype="void"> 
79 <cfargument name="user" type="any" required="true" /> 
80 <cfargument name="user2" type="any" required="true" /> 
81 
82<!--- execute _saveusers() in a transaction ---> 
83        <cfset instance.transaction.execute(this, "_saveusers", arguments)> 
84 </cffunction> 
85 
86My _saveusers() method is this: 
87 
88 <cffunction name="_saveusers" access="public" output="false" returntype="void"> 
89 <cfargument name="user" type="any" required="true" /> 
90 <cfargument name="user2" type="any" required="true" /> 
91 
92 <cfset variables.transfer.save(arguments.user) /> 
93 <cfset variables.transfer.save(arguments.user2) /> 
94 </cffunction> 
95 
96So, the same situation applies here. When I execute this using proper data, it works fine. If I use invalid data in my user2 object, the transaction fails and user1 is rolled back and doesn't show up in the database. So, using execute() works as expect on both CF8 and Railo. 
97 
98 
99I have established that AOP and execute work properly and my setup is fine. 
100 
101 
102Now, on to the problem... 
103 
104There are situations where I want to do one of two things: 
105 
1061.) Not user a userService at all, but instead call Transfer and do the save in the handler, so I need the handler method either AOP advised or wrapped in an execute. 
107 
1082.) Call multiple services (userService, blogService, emailService, etc) and have the entire handler method wrapped in a transaction, so that in case of a failure of insert on one of the services, all of the actions for all of the services during the transaction are rolled back. 
109 
110So, for situation #1: 
111 
112creatUser(): 
113 
114 <cfscript> 
115 transaction = getPlugin("ioc").getBean("TransferTransaction"); 
116 transaction.execute(this, "_createUser", arguments); 
117 </cfscript> 
118 
119Now, _createUser(): 
120 
121 <cfscript> 
122 
123 // create new user 
124 user = instance.Transfer.new("users.user"); 
125 user.setEmail("test"); 
126 user.setPassword("test"); 
127 
128 // create second user with bad information 
129 user = instance.Transfer.new("users.user"); 
130 user.setEmail("test"); 
131 // password length is too long, limit is 4 
132 // this will cause an error inside the transaction 
133 user.setPassword("testtest"); 
134 
135 // save users 
136 instance.Transfer.save(user); 
137 instance.Transfer.save(user2); 
138 
139 </cfscript> 
140 
141I am using the same IoC configuration, etc, I have only changed the handler at this point. If I run my createUser() method of the handler by calling the ehGeneral.createUser event using valid data for my user objects, it works fine and executes and inserts both users into the database. However, if I call this method with invalid information (password for user2 that is too long) then then insert of user2 fails, but the insert of user1 is not rolled back and it stays in the database, even though it was inside the same transaction since the entire _createUser() method was executed usering transaction.execute() and the save calls to Transfer are both inside this method. (I have wrapped this in a try/catch to make sure, and it still exhibits this behavior) 
142 
143So, I tried one other modification trying to use execute() on a handler method... 
144 
145In the userService I turned off the AOP advice, so none of the service methods are being advised. 
146 
147Then I changed my handler method for _createUser() to: 
148 
149 
150 <cfscript> 
151 // get user service 
152 userService = getPlugin("ioc").getBean("userService"); 
153 
154 // create new user 
155 user = instance.Transfer.new("users.user"); 
156 user.setEmail("test"); 
157 user.setPassword("test"); 
158 
159 // create second user with bad information 
160 user = instance.Transfer.new("users.user"); 
161 user.setEmail("test"); 
162 // password length is too long, limit is 4 
163 // this will cause an error inside the transaction 
164 user.setPassword("testtest"); 
165 
166 // save users 
167 userService.saveusers(user,user2); 
168 
169 </cfscript> 
170 
171So, now when I call ehGeneral.createUser, the event executes, calls userService.saveusers() and then fails on the insert of user2. However, user1 is not rolled back and stays in the database. 
172 
173I can confirm here that the execute() method does not work on ColdBox handlers. 
174 
175 
176So, the last situation I wanted to test was AOP transaction advice on a method of the handler... 
177 
178My handler: ehGeneral.cfc 
179 
180I inject TransferTransaction into the instance scope of the handler from ColdSpring: 
181 
182<cfproperty name="TransferTransaction" type="ioc" scope="instance"> 
183 
184I then advise the creatUser() method in onDIComplete(): 
185 
186 <cffunction name="onDIComplete" access="public" returntype="void" output="false"> 
187 <!--- advise createUser() method ---> 
188 <cfset instance.TransferTransaction.advise(this,"createUser", true)> 
189 </cffunction> 
190 
191createuser(): 
192 
193 <cfscript> 
194 // get user service 
195 userService = getPlugin("ioc").getBean("userService"); 
196 
197 // create new user 
198 user = instance.Transfer.new("users.user"); 
199 user.setEmail("test"); 
200 user.setPassword("test"); 
201 
202 // create second user with bad information 
203 user = instance.Transfer.new("users.user"); 
204 user.setEmail("test"); 
205 // password length is too long, limit is 4 
206 // this will cause an error inside the transaction 
207 user.setPassword("testtest"); 
208 
209 // save users 
210 userService.saveusers(user,user2); 
211 
212 </cfscript> 
213 
214In this case, userService has no AOP advice, I am relying on the advice applied to the createUser() method of the handler to manage the transaciton. 
215 
216When doing this, the system exhibits the same behavior as when trying to use the execute() method. user1 is saved and stays in the database, while user2 fails. Even though AOP advice shows that it was weaved onto ehGeneral.createUser(), when the transaction fails, user1 is not rolled back. 
217 
218I have also tried the following with the same results: 
219 
220 // save users 
221 instance.Transfer.save(user); 
222 instance.Transfer.save(user2); 
223 
224So, I can confirm that transactions applied to handler methods also do not have any effect. 
225 
226This is the identical behavior I am experiencing on both CF8 and Railo. 
227 
228(I will also make a side note here about something else. If I have a method in my userService called saveuser and then another method called securesaveuser, if I apply AOP advice to saveuser, it also gets applied to securesave user because of some regex matching I'm guessing, so I'm not sure what's causing that, but it's happening. securesaveuser does not have AOP advice applied and doesn't show that it was weaved, but if I turn on advice for saveuser, securesaveuser begins to become advised.) 
Download and save
Toggle line numbers
Thread:
[117045] Transfer AOP and execute() issues by Anonymous at 2009-07-03 23:07:46
Tip: Click the line numbers to toggle highliting on that line.

Paste followup:

Language:
Author:
Subject:


    Tabstop:     bigger biggest
Note: You can prefix a line with "@@@" to highlight it.