Announcement Announcement Module
Collapse
No announcement yet.
Migration strategy from PicoContainer? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Migration strategy from PicoContainer?

    I'm considering migrating an application from PicoContainer and wanted to ask for some advice from the Spring community. Has anyone gone through this process before and have any advice from their experience?

    I have many questions, but I'll start first with the bean container/DI questions. In particular, I'm not sure whether I should use Spring 2.5 annotations or JavaConfig to register my beans.

    Scenario:
    • JDK 5 (soon 6) on Tomcat 5.5.
    • Webapp running WebWork 2.1, planning to upgrade to Struts 2.
    • Data access using Hibernate 3 with Annotations.
    • Quartz to schedule and run jobs

    I'm using PicoContainer 1.x with NanoContainer 1.x, which provides a hierarchy of connected app, request, and session scope containers. I'm using these scopes but only a handful of objects actually are tied to the object (e.g. some objects use the HttpSession).

    I'm using a NanoContainer XWorkObjectFactory which instantiates my WebWork Actions with all the right dependencies. I don't have to explicitly register my actions; the ObjectFactory finds them. The only other times I explicitly use Pico are for instantiating/injecting Quartz jobs and in the integration tests.

    All bean registration is done programmatically via a bootstrap loader, either registering a bean identified either by a String name or by its class. The application has around 450 beans registered.

    Questions:

    1. I'd like to keep using Constructor Injection, which sounds like is possible. I'll also want to auto-wire by default, since that's what most of my Pico beans use. In addition, there are some lifecycle methods that Pico provides like Start() and Stop() which we use for resource management (e.g. disposing of Hibernate Sessions, etc.) It looks like Spring has this also via the init-method and destroy-method callbacks.

    2. It seems like a simple first step would be to convert the object where I register my beans with Pico to instead use a Spring container. Does this mean using JavaConfig, or is there some other way to do this? In particular, this class has many lines of code like this:

    // args are scope, key, and implementing class
    registerImplementation(requestContainer, BookRepository.class, HibernateBookRepository.class);
    registerImplementation(sessionContainer, User.class, SessionBasedUser.class);
    // etc.


    In addition you can register beans with specific parameters, which includes calling other Java objects to find those parameters:

    // args = scope, key, implementation, and specify the constructor dependencies -- either find the default one, or some specific constant.

    registerNamedImplementation(requestContainer, OvernightJob.class.getSimpleName(), OvernightJob.class,
    new BasicComponentParameter(), new ConstantParameter(ReportingExportJobs.values()));


    Of all the ~450 beans, the majority are simple registration (just key and value), and about 100 specify specific parameters.

    3. I use that same configuration with three scopes for my integration tests and Quartz jobs, but instead 'flatten' the scopes myself -- I cache the 'app' container (which holds the HibernateSessionFactory, etc.) and make a child container which holds the request and session components. From my understanding of Spring's app/request/session scopes, they must have a real web counterpart around to work (and wouldn't work in Quartz or tests).

    4. One difficulty I can see is that my integration tests sometimes grab a PicoContainer with the default registered dependencies, replace a registered bean with a fake one, and then get the component under test. My understanding is that Spring does not let you replace or remove components from a BeanFactory. I'm not really sure what to do here; I understand that the Spring test classes let you specify a bean configuration to use and you typically chain together XML configuration files to generate permutations of bean configurations. Is there any other way?

    Thanks for your advice!

    Dan

  • #2
    Hi Dan,

    I don't have time to reply to your thread at length just now. In short, however, you're correct in your assessment that Spring has most or all of what you're use to in PicoContainer. Some of the mechanisms will feel a bit different, but you'll be able to achieve the same ends, if by somewhat different (and hopefully better!) means.

    One particular note - in your post you mentioned you aren't sure whether you "should use Spring 2.5 annotations or JavaConfig" to register your beans". Fortunately, this is not an either/or situation. JavaConfig integrates seamlessly with Spring's @DI support.

    For example: You may have a number of classes that you wish to pick up and have autowired via component scanning (Spring 2.5 annotations), but you may have a number of other objects that you wish to manually wire up. You can use JavaConfig to wire the manual objects and at the same time kick off the process of component scanning. See the latest documentation on JavaConfig (1.0.0.M4) for full details (search for @ComponentScan).

    Finally, should you choose to involve JavaConfig in your porting effort, I'll be happy to address particular questions as they come up. Post any such questions to the JavaConfig forum for best response time.

    Comment


    • #3
      More on Web Scopes and Testing Configuration

      Chris,

      Thanks for your follow-up. I didn't know that you could use both annotation configuration and JavaConfig. I'll take a look and as I get questions, I'll post to the JavaConfig group.

      So I feel like #1 and #2 are answered, but I still have some questions on #3 and #4.


      Re: Web Scopes (#3)

      My sense is that people use "request" scope objects in Spring if they truly need to be bound to the HttpRequest, not to simulate a "one-per-request" strategy, which is what we're doing with Hibernate Session setup.

      So then should the goal to be to migrate these types to either singleton or prototype scope, and then leave the very few remaining requests that really *are* Session-based (such as the User as bound to the HttpSession) in place?


      Re: Testing (#4)

      The scenario here is that in a test class, the bean container should be the same for each test, except each test will register a slightly different version of the dependency. So if I understand right, this would mean registering a context configuration which had everything but the one component I want to replace, then add it in each test method (since you can't remove or replace components in a BeanFactory). Is this true?


      Thanks for your advice!
      Dan

      Comment


      • #4
        Re: #3

        Yes - if you truly want a 'new object every time' behavior, go with prototype scope.


        Re: #4

        Spring makes a strong distinction between 'isolated unit testing' and 'system testing'. Only in the latter is Spring involved in any way. In unit tests, Spring should not be involved in at all. The idea is that you should write your code and tests in such a way that it is relatively easy to wire up your own objects without the need for Spring. When it comes time to do system testing, it's easy to design configuration definitions with Spring in a modular fashion so that you can wire up just one part of the system and test it, or test the entire network of dependencies together.

        So, to be clear, it isn't particularly straightforward to swap out a registered bean with a fake one, but Spring discourages doing this for the purpose of testing on philosophical grounds anyway. With that said, it is possible to do what you're talking about. You could instantiate an anonymous subclass of an application context and customize its bean factory, overriding bean definitions at will:

        Code:
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(AppConfig.class) {
            @Override
            protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
                BeanDefinition beanDef = new RootBeanDefinition();
                beanDef.setBeanClassName(TestBean.class.getName());
                beanFactory.registerBeanDefinition("testBean", beanDef);
            }
        };

        Comment


        • #5
          More on Request scope and Testing

          Chris,

          Thanks for your follow-up.

          Re: #3 (Request scope)
          I don't actually want a new bean every for each component in the graph, I want only one bean to be created and re-used for each request. This is all tied to how we handle Hibernate, which create a Hibernate Session for each request scope container -- which effectively gives us one session per request. So in my example, if I have an Action A which depends upon DAO B and C, and both DAO's depend on a Hibernate Session, using prototype will give B and C and different HibernateSession, whereas I want them to each have the same Session. (If I understand prototype correctly!)

          It looks like most of the Spring Hibernate support is built around using a Hibernate SessionFactory, which we could switch to.

          Re: #4 (Testing)
          Thanks for explaining it that way, now that I shift my perspective a bit I might be able to rewrite them as unit tests. In a pinch, it sounds like I can use the code you presented, but I'd like to get away from funny edge cases as much as possible.

          Thanks for the prompt response,
          Dan

          Comment


          • #6
            Regarding #3, the preferred approach is to declare a SessionFactory bean (as a singleton) and pass that around to your DAOs, calling getCurrentSession() as necessary. Take a look at LocalSessionFactoryBean / AnnotationSessionFactoryBean and HibernateTransactionManager. It's well-documented how to best wire Spring+Hibernate together, so check the reference docs as well. Everything you'll see there will be in XML, but you can easily translate that over to JavaConfig.

            Comment

            Working...
            X