Thursday, March 29, 2012

Project synchronization problems with Spring Roo and SpringSource Tool Suite

While working through Spring Roo In Action I encountered synchronization problems between the SpringSource Tool Suite (STS) and Spring Roo.  For example, if you use the STS Roo Project Wizard to create a new project with POM packaging the resulting project's Problems view reports missing source code folders. If you use the Roo shell to create a module the STS does not create an Eclipse project for it. If you use the Maven plugin to create a new Maven module for your Roo project, your project becomes somewhat corrupted. If you import a multi-module Roo project the parent Eclipse project is missing Roo Project Nature. 


These issues were experienced using the following configuration:
  • STS version 2.9.1
  • Spring Roo version 1.2.1
  • Maven 3.0.3
  • Java 1.6.0_b29
  • OS X 10.6.8
Multi-module project support in Roo is relatively new so I'll assume the STS team is still playing catch-up.  To assist them or anyone else interested in replicating these anomalies I've documented step-by-step procedures in this article.  But first, I'll describe a few rules we can follow to avoid these problems.


If you want to create a new Roo project:
  1. Create your new project using an external Roo shell.
  2. If this is a multi-module project, create the child modules.
  3. If applicable, setup your JPA configuration.
  4. Create at least one Java class and its associated test cases (use the --testAutomatically option).*
  5. Import your Roo project as an existing Maven project.
  6. If this is a multi-module Maven project, add Roo project nature to the parent module.**
  7. For any new modules created, import them into the STS as existing Maven projects.
*If you skip this step, you will have to explicitly configure your source folders in the STS after creating your Java classes and test cases.  Without source folders configured you will not be able to use certain STS features including push-in refactoring and auto import.  You'll notice the Package Explorer will not let you expand Java class or ITD nodes in its tree.
**This will allow you to open a single Roo shell in the STS for the parent and then change focus to any child module.

If you want to check out a Roo project from source control:
  1. Use the STS to import the project from the source control server.

The rest of this article will describe step-by-step procedures to replicate the problems I encountered.

Problem: You created a project using the STS Roo Project Wizard.  After creating your first Java class you notice certain STS features including push-in refactoring and auto-import aren't available.

Workaround: Configure your Eclipse project's build path to include src/main/java and src/test/java.

Follow these steps to demonstrate the problem and workaround:
  1. In the STS IDE select File->New->Spring Roo Project.
  2. Enter foo in the "Project name" field.
  3. Enter com.acme.foo in the "Top level package name" field.
  4. Click the Next button.
  5. Click the Finish button.
  6. Open the Problems view if it is not already open.
  7. Observe there is nothing reported in the Problems view.
  8. Observe in the Package Explorer view that the foo project icon is adorned with the graphics for both the Java and Maven project natures.
  9. Expand the foo project node in the Package Explorer view.
  10. Observe the STS has configured src/main/resources as a source code folder.
  11. Open the Navigator view.
  12. Observe the src/main/resources folder does not exist (the wizard only created src/main)!
  13. In the STS Roo shell type jpa setup --database HYPERSONIC_IN_MEMORY --provider HIBERNATE
  14. In the Roo shell type: entity jpa --class ~.model.Bar --testAutomatically
  15. In the Package Explorer, observe Roo has created the Bar entity, test classes, and associated ITD files (if you don’t see the ITD files in the Package Explorer, click the triangular symbol in the upper right-hand corner of the Package Explorer and select “Filters...”, then deselect the option “Hide generated Spring Roo ITDs”).
  16. Observe that Roo has created the new source files under src/main/java and src/main/test BUT THE STS PACKAGE EXPLORER DOES NOT SHOW THESE AS SOURCE FOLDERS.
  17. In the Roo shell type perform tests
  18. Observe all tests pass.
  19. In the Roo shell type field string --fieldName pinNumber --sizeMin 4 --sizeMax 4 --regexp "[0-9]{4}"
  20. In the Roo shell type perform tests
  21. Observe the tests fail because the the values being injected into the Bar instance's pinNumber field do not satisfy the validation rules we defined for it.
  22. We need to “push-in” refactor the BarDataOnDemand.setPinNumber method to generate a 4-digit string.  Observe that you cannot expand source code nodes in the Package Explorer view.  The STS Project has not kept up with our project configuration changes made in the Roo shell.  In my opinion STS should have been notified that we now have source code in our project and instructed to add the source folders to the Eclipse project's build path.  We need to tell the STS where our source code folders are so we can do our refactoring.
  23. In the Package Explorer view right-click the src/main/java folder.
  24. Select Build Path->Use as source folder
  25. Do the same for the src/test/java folder.
  26. Expand the src/test/java->com.acme.foo.model->BarDataOnDemand_Roo_DataOnDemand.aj node
  27. Right-click the BarDataOnDemand.setPinNumber(Bar, int) node
  28. Select Refactor->Push In...
  29. Click the OK button.
  30. Open the BarDataOnDemand java source file.
  31. Modify the code as follows:
  32. public void setPinNumber(Bar obj, int index) {
  33.    StringBuffer pinNumber = new StringBuffer();
  34.    pinNumber.append(Integer.toString(new Random().nextInt(10)));
  35.    pinNumber.append(Integer.toString(new Random().nextInt(10)));
  36.    pinNumber.append(Integer.toString(new Random().nextInt(10)));
  37.    pinNumber.append(Integer.toString(new Random().nextInt(10)));
  38.    obj.setPinNumber(pinNumber.toString());
  39. }
  40. In the Roo shell, type perform tests
  41. Observe all tests pass.
  42. Close the foo project.
After you do certain tasks inside a Roo shell you have to tell the STS what you've done so it can stay in sync with the changes.  Once we updated the STS build path we gained access to the refactoring capabilities one normally expects to be available after a new project wizard has creates a project.  Clearly there is room for improvement with regards to how the STS and Roo communicate events.

Problem: You created a project with POM packaging using the Roo Project Wizard because you want to use Maven modules.  The resulting Eclipse project has errors reported in the Problems view.

Workaround: Remove the invalid <classpathentry> elements from the .classpath file and then create your child modules with the STS Roo shell.


Follow these steps to demonstrate the problem and workaround:
  1. In STS select File->New->Spring Roo Project.
  2. Enter foo2 in the "Project name" field.
  3. Enter com.acme.foo in the "Top level package name" field.
  4. Select POM in the Packaging drop-down.
  5. Click the Next button.
  6. Click the Finish button.
  7. Open the Problems view if it is not already open.
  8. Observe the following problems reported in the Problems view:
  9. Project 'foo2' is missing required source folder: 'src/main/java'
  10. Project 'foo2' is missing required source folder: 'src/main/resources'
  11. Project 'foo2' is missing required source folder: 'src/test/java'
  12. Project 'foo2' is missing required source folder: 'src/test/resources'
  13. The project was not built since it depends on foo2, which has build path errors foo2 Unknown Java Problem
  14. Use the Navigator view to open the project's .classpath file.
  15. Observe the STS has created <classpathentry> elements for the missing folders.
  16. Delete these elements (the first four in the document) and save the file.
  17. Observe the Problems view still reports the following error: unable to find org.aspectj.lang.JoinPoint (check that aspectjrt.jar is in your classpath)
  18. In the STS Roo shell type module create --topLevelPackage com.acme.foo --moduleName foo2-core
  19. Observe the Problems view no longer reports any errors.
  20. Quit both Roo shells.
  21. Close the foo2 and foo2-core projects.
The STS Roo Project Wizard created an invalid project configuration.  The STS team should take measures to prevent this from ever happening.  Things get even worse if you mistakenly try to create new modules using the STS Maven plugin's New Maven Module function.  Don't do it, as you'll end up with some really screwed up behavior:

Problem: You created a project with POM packaging using the Roo Project Wizard because you want to use Maven modules.  Instead of creating your child modules with the Roo shell, you did so using the STS Maven plugin. As a result, you have problems switching focus between modules in the Roo shell.

Workaround: When working with Roo projects, always use the Roo shell to create your modules and then import them into the STS.



Follow these steps to demonstrate the problem:
  1. In the STS IDE select File->New->Spring Roo Project.
  2. Enter foo3 in the "Project name" field.
  3. Enter com.acme.foo in the "Top level package name" field.
  4. Select POM in the Packaging drop-down.
  5. Click the Next button.
  6. Click the Finish button.
  7. Right-click the foo3 project node in the Package Explorer view.
  8. Select Maven->New Maven Module Project
  9. Enter foo3-core in the Module Name field.
  10. Select the Create Simple project (skip archetype selection) check box.
  11. Click the Next button.
  12. Click the Finish button.
  13. Right-click the foo3-core node in the Package Explorer view.
  14. Select Spring Tools
  15. Observe that the project does not have Roo project nature.
  16. In the foo3 Roo shell type module focus --moduleName foo3-core
  17. Observe the Roo shell reports Command 'module focus --moduleName foo3-core' was found but is not currently available (type 'help' then ENTER to learn about this command)
  18. Roo will not allow us to switch modules at this time because it doesn't know we used the STS to create a child module.  The STS needs to communicate these kinds of events to Roo.
  19. Close the foo3-core project.
  20. Delete the foo3-core project (including contents on disk).
  21. Quit the foo3 Roo shell, then open it back up.
  22. Type module create --topLevelPackage com.acme.foo --moduleName foo3-core
  23. Observe the Roo shell reports Command 'module create --topLevelPackage com.acme.foo --moduleName foo3-core' was found but is not currently available (type 'help' then ENTER to learn about this command)
  24. Open the foo3 project's pom.xml file.
  25. Observer the STS reports a NullPointerException.
  26. At this point it appears the STS did something to corrupt foo3 project.  I don't have an answer as to what happened, but clearly we can damage our project if we use the Maven plugin to create child modules.
  27. Close the foo3 project.
The point of this exercise was to demonstrate how you can get yourself into trouble using features of the STS that you assume will place nice with Roo.  Let's look at the procedure I recommend for starting a new Roo project.

Problem: You want to create a Roo project and then do most of your work using the STS.

Solution: Use an external Roo shell to create your modules, JPA configuration, and at least one Java class before importing the project into the STS.

  1. From your command console navigate to your STS workspace folder and type mkdir foo4; cd foo4; roo
  2. Type project --topLevelPackage com.acme.foo --packaging POM --projectName foo4
  3. Type module create --topLevelPackage com.acme.foo --moduleName foo4-core
  4. Type jpa setup --database HYPERSONIC_IN_MEMORY --provider HIBERNATE
  5. Type entity jpa --class ~.model.Bar --testAutomatically
  6. Type field string --fieldName pinNumber --sizeMin 4 --sizeMax 4 --regexp "[0-9]{4}"
  7. Type quit
  8. Import the project into STS as an existing Maven project.
  9. Observe that STS creates Eclipse projects for foo4-core and foo4.
  10. Observe the STS has correctly identified the source folders of foo4-core
  11. Observe the STS Roo shell's bottom tab label is associated with foo4-core. ALSO NOTE that the shell's prompt DOES NOT show the focus at foo4-core, but instead it is roo.  I consider this to be a bug, as the prompt should always indicate the active focus.
  12. Observe the STS has not added Roo project nature to foo4.  Why is this? I think it's another bug in the STS.  It should know to do this and should have opened our Roo shell with focus on the parent module, foo4.
  13. Type entity jpa --class ~.model.Baz --testAutomatically
  14. Observe the new entity was created in the foo4-core project, confirming the Roo shell has focus on the foo4-core project.
  15. Add Roo Project Nature to the foo4 project.
  16. Open a second Roo shell for the foo4 project.
  17. In the foo4 Roo shell type module focus --moduleName foo4-core
  18. Observe the shell prompt indicates focus changed to the foo4-core module.
  19. Change focus back to the parent module: module focus --moduleName ~
  20. Let's create a new module: module create --topLevelPackage com.acme.foo --moduleName foo4-web
  21. Observe the module is created by Roo, but the STS did not create a new Eclipse project for the module.
  22. Select the foo4 project in the Package Explorer view.
  23. Select File->Import...->Maven->Existing Maven Projects
  24. Observe the STS has pre-selected the foo4-web project to be imported.
  25. Click the Finish button.
  26. Observe the STS creates a new foo4-web Eclipse project and opens a roo shell for it.
  27. In the foo4-core Roo shell type module focus --moduleName ~
  28. Observe Roo complains the command is not available.  Observe the same behavior occurs in the foo4-web shell (it too, doesn't know about it's parent). 
  29. This behavior is consistent with how Roo works.  If you open a Roo shell for a child module it does not have any context about its parent.  This is why I prefer to add Roo nature to the parent module so I can easily switch between modules.  The STS, in its current design, wants you to use a separate Roo shell for each child module and none at all for the parent.  I find this less than optimal.
  30. Close the foo4, foo4-core, and foo4-web projects in the STS.
In summary I recommend you follow the rules I posted at the beginning of the article and avoid trying to use the STS to make structural changes to your Roo project. Use the Roo shell to create new modules as needed and then import them as Existing Maven Projects.

If you have had different experiences or know of better workarounds, or if you see a problem with the way I assume the STS tool is intended to work with Roo, please add your comments.

6 comments:

  1. Most of the issues you have mentioned can be solved by simply enabling 'Refresh using native hooks or polling' option in your eclipse workspace preferences.

    The original comment on StackOverflow.

    http://stackoverflow.com/questions/9880101/whats-the-correct-way-to-create-a-new-spring-roo-project-with-multiple-maven-mo/9883520#comment12687238_9883520

    Cheers.

    ReplyDelete
    Replies
    1. @bhagyas,

      I tried your suggestion, but the results are the same with just two exceptions:

      In the first test case, step 12, the STS did correctly create the src/main/resources folder.
      In the third test case, step 12, when opening the pom.xml file, the STS did not complain of a NullPointerException.

      Other than those two differences, all the problems I describe are still encountered. Try these for yourself. If you have different results, I'd very much like to know.

      -Jeff

      Delete
  2. Really helpfull, i was about to surrender when i found this post :) Thank you!

    I published a mini guide including these and other advices and a sample Roo script.

    You can find it here: http://baldercm.blogspot.com.es/2012/04/maven-multi-module-with-spring-roo-and.html

    Best regards

    ReplyDelete
  3. Thank you Jeff! I struggled for several hours tonight trying to figure out what was with all the issues with my multi-module Roo project, and stumbled on your post describing all my problems. In my case, I had already created my project in STS. After seeing your post, I simply ran a Maven Import within STS, it listed all my submodules automatically, clicked Finish, and as far as I can see, all my problems are resolved. I had earlier removed the invalid 'missing source' folders of the top-level POM via the project Properties, Build Path, Source tab.

    Thanks again!

    Eric

    ReplyDelete
  4. Jeff,
    Have you opened a jira ticket in STS? I think that could promote these issues getting fixed.
    Thanks for the detailed account.

    ReplyDelete
  5. damn good post jeff.. !!! u saved a hell lot of my days... i just can't thank you enough... i'm grateful.

    ReplyDelete