This looks like this might be an issue. Unfortunately, I haven't had time to try and reproduce it. If you're interested in getting it fixed sooner than later, please create an issue in JIRA and attach a patch.
http://issues.appfuse.org Thanks, Matt 2010/1/4 Марат Камалов <mkamalo...@gmail.com>: > Hi Matt, > > Ok, i can reproduce the bug. > > 1) I generated a new project with Appfuse 2.0.2(Spring-MVC). > 2) Downloaded full sources to new project. (mvn appfuse:full-source) > 3) Configured database connection properties. > 4) I choose SigninController for reproducing a bug. And added a new test to > SigninControllerTest. So, I have the following code in SigninControllerTest: > > package com.mycompany.webapp.controller; > > import java.util.HashSet; > import java.util.Set; > > import javax.mail.Address; > import javax.mail.Message.RecipientType; > import javax.servlet.http.HttpServletRequest; > import javax.servlet.http.HttpServletResponse; > > import org.springframework.mock.web.MockHttpServletRequest; > import org.springframework.mock.web.MockHttpServletResponse; > import org.springframework.security.context.SecurityContextHolder; > import org.springframework.validation.BindException; > import org.springframework.validation.Errors; > import org.springframework.web.servlet.ModelAndView; > import org.subethamail.wiser.Wiser; > import org.subethamail.wiser.WiserMessage; > > import com.mycompany.Constants; > > public class SignupControllerTest extends BaseControllerTestCase { > private SignupController c = null; > > public void setSignupController(SignupController signup) { > this.c = signup; > } > > public void testDisplayForm() throws Exception { > MockHttpServletRequest request = newGet("/signup.html"); > HttpServletResponse response = new MockHttpServletResponse(); > ModelAndView mv = c.handleRequest(request, response); > assertTrue("returned correct view name", mv.getViewName().equals( > "signup")); > } > > public void testSignupUser() throws Exception { > MockHttpServletRequest request = newPost("/signup.html"); > request.addParameter("username", "self-registered"); > request.addParameter("password", "Password1"); > request.addParameter("confirmPassword", "Password1"); > request.addParameter("firstName", "First"); > request.addParameter("lastName", "Last"); > request.addParameter("address.city", "Denver"); > request.addParameter("address.province", "Colorado"); > request.addParameter("address.country", "USA"); > request.addParameter("address.postalCode", "80210"); > request.addParameter("email", "self-registe...@raibledesigns.com"); > request.addParameter("website", "http://raibledesigns.com"); > request.addParameter("passwordHint", "Password is one with you."); > > HttpServletResponse response = new MockHttpServletResponse(); > > // start SMTP Server > Wiser wiser = new Wiser(); > wiser.setPort(getSmtpPort()); > wiser.start(); > > ModelAndView mv = c.handleRequest(request, response); > Errors errors = (Errors) mv.getModel().get( > BindException.MODEL_KEY_PREFIX + "user"); > assertTrue("no errors returned in model", errors == null); > > // verify an account information e-mail was sent > wiser.stop(); > assertTrue(wiser.getMessages().size() == 1); > > // verify that success messages are in the request > assertNotNull(request.getSession().getAttribute("successMessages")); > > assertNotNull(request.getSession().getAttribute(Constants.REGISTERED)); > > SecurityContextHolder.getContext().setAuthentication(null); > } > > private HttpServletRequest buildRequest(int requestNumber) { > final String requestNumberString = String.valueOf(requestNumber); > MockHttpServletRequest request = newPost("/signup.html"); > request.addParameter("username", "self-registered" > + requestNumberString); // We should have unique username > for every request. > request.addParameter("password", "Password1"); > request.addParameter("confirmPassword", "Password1"); > request.addParameter("firstName", "First"); > request.addParameter("lastName", "Last"); > request.addParameter("address.city", "Denver"); > request.addParameter("address.province", "Colorado"); > request.addParameter("address.country", "USA"); > request.addParameter("address.postalCode", "80210"); > request.addParameter("email", "self-registered" + > requestNumberString > + "@raibledesigns.com");// We should have unique email for > every request. > request.addParameter("website", "http://raibledesigns.com"); > request.addParameter("passwordHint", "Password is one with you."); > return request; > } > > public void testSignupUserEmailMessageInConcurrentRequests() > throws Exception { > // start SMTP Server > Wiser wiser = new Wiser(); > wiser.setPort(getSmtpPort()); > wiser.start(); > > // send 200 sign in requests in two separate threads. > > Thread thread1 = new Thread(new Runnable() { > public void run() { > for (int i = 0; i < 100; i++) { > HttpServletRequest request = buildRequest(i); > HttpServletResponse response = new > MockHttpServletResponse(); > try { > ModelAndView mv = c.handleRequest(request, > response); > Errors errors = (Errors) mv.getModel().get( > BindException.MODEL_KEY_PREFIX + "user"); > assertTrue("no errors returned in model", > errors == null); > > // verify that success messages are in the request > assertNotNull(request.getSession().getAttribute( > "successMessages")); > assertNotNull(request.getSession().getAttribute( > Constants.REGISTERED)); > } catch (Exception e) { > throw new RuntimeException(e); > } > } > > } > }); > > Thread thread2 = new Thread(new Runnable() { > public void run() { > for (int i = 100; i < 200; i++) { > HttpServletRequest request = buildRequest(i); > HttpServletResponse response = new > MockHttpServletResponse(); > try { > ModelAndView mv = c.handleRequest(request, > response); > Errors errors = (Errors) mv.getModel().get( > BindException.MODEL_KEY_PREFIX + "user"); > assertTrue("no errors returned in model", > errors == null); > > // verify that success messages are in the request > assertNotNull(request.getSession().getAttribute( > "successMessages")); > assertNotNull(request.getSession().getAttribute( > Constants.REGISTERED)); > } catch (Exception e) { > throw new RuntimeException(e); > } > } > > } > }); > > thread1.start(); > thread2.start(); > > thread2.join(); > thread1.join(); > > Set<String> emailAddresses = new HashSet<String>(200); > > for (WiserMessage wiserMessage : wiser.getMessages()){ > Address[] addresses = > wiserMessage.getMimeMessage().getRecipients(RecipientType.TO); > for (Address address : addresses){ > emailAddresses.add(address.toString()); > } > } > > wiser.stop(); > > //We should have 200 unique recipients!!!! > assertTrue(emailAddresses.size() == 200); //Fails on this line... > because we have concurrent modification of email message object in > controller. And some users receive more than one email. > assertTrue(wiser.getMessages().size() == 200); > > SecurityContextHolder.getContext().setAuthentication(null); > } > } > > 5) I run a test and it failed. If you have a very fast CPU you should add > the following code to BaseFormController.java or increase requests count > from 200 to 1000. But i have failed test on my PC with 200 request every > time... > > protected void sendUserMessage(User user, String msg, String url) { > if (log.isDebugEnabled()) { > log.debug("sending e-mail to user [" + user.getEmail() + > "]..."); > } > > message.setTo(user.getFullName() + "<" + user.getEmail() + ">"); > > Map<String, Serializable> model = new HashMap<String, > Serializable>(); > model.put("user", user); > > // TODO: once you figure out how to get the global resource bundle > in > // WebWork, then figure it out here too. In the meantime, the > Username > // and Password labels are hard-coded into the template. > // model.put("bundle", getTexts()); > model.put("message", msg); > model.put("applicationURL", url); > /*try{ > Thread.sleep(Math.round(Math.random()*500)); > }catch (InterruptedException e) { > throw new RuntimeException(e); > }*/ > mailEngine.sendMessage(message, templateName, model); > } > > Marat Kamalov. > > 2010/1/4 Matt Raible <m...@raibledesigns.com> >> >> Have you been able to reproduce an issue with the current setup in >> 2.0.2? If so, please describe the steps so we can verify it is a bug. >> >> Thanks, >> >> Matt >> >> On Mon, Jan 4, 2010 at 10:25 AM, Марат Камалов <mkamalo...@gmail.com> >> wrote: >> > Hi, >> > >> > I have found this bug in 1.8.2 and 2.0.2. I haven't seen the last >> > version >> > yet. >> > >> > We have the following bean with scope prototype >> > >> > <bean id="mailMessage" >> > class="org.springframework.mail.SimpleMailMessage" >> > scope="prototype"> >> > <property name="from" value="${mail.default.from}" /> >> > </bean> >> > >> > Then we inject this bean to every controller where we want to send some >> > email message. >> > >> > <bean id="userFormController" >> > class="ru.icl.ios.mzioppd.webapp.controller.UserFormController"> >> > <property name="validator" ref="beanValidator"/> >> > <property name="formView" value="userForm"/> >> > <property name="successView" value="redirect:users.html"/> >> > <property name="cancelView" value="redirect:mainMenu.html"/> >> > <property name="userManager" ref="userManager"/> >> > <property name="roleManager" ref="roleManager"/> >> > <property name="mailEngine" ref="mailEngine"/> >> > <property name="message" ref="mailMessage"/> >> > <property name="templateName" value="accountCreated.vm"/> >> > </bean> >> > and >> > <bean id="passwordHintController" >> > class="ru.icl.ios.mzioppd.webapp.controller.PasswordHintController"> >> > <property name="userManager" ref="userManager"/> >> > <property name="messageSource" ref="messageSource"/> >> > <property name="mailEngine" ref="mailEngine"/> >> > <property name="message" ref="mailMessage"/> >> > </bean> >> > >> > So, the object mailMessage in the different controllers will be >> > different >> > too, becouse bean mailMessage has scope="prototype". But what about >> > UserFormController in concurrent requests?? UserFormController is >> > singleton, >> > becouse singleton is default scope in spring with dtd 2.0. And a custom >> > default scope isn't defined... >> > >> > <beans xmlns="http://www.springframework.org/schema/beans" >> > xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >> > xsi:schemaLocation="http://www.springframework.org/schema/beans >> > http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" >> > default-lazy-init="true"> >> > >> > Have we concurrent modification of mailMessage object??? >> > >> > I think that we have. Probably, we should use the following code... Or >> > use >> > another approach(synhronize send method or create a new message every >> > time >> > in controller). >> > >> > <bean id="mailMessage" >> > class="org.springframework.mail.SimpleMailMessage" >> > scope="request"> >> > <property name="from" value="${mail.default.from}" /> >> > <aop:scoped-proxy> >> > </bean> >> > >> > P.S >> > I'm sorry for my bad English :)) >> > >> > Marat Kamalov. >> > >> > >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: users-unsubscr...@appfuse.dev.java.net >> For additional commands, e-mail: users-h...@appfuse.dev.java.net >> > >