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
>>
>
>

Reply via email to