Page 1 of 2 12 LastLast
Results 1 to 10 of 11

Thread: making command line args (from Java main) available to beans

  1. #1
    Join Date
    Mar 2008
    Location
    Wayzata, MN
    Posts
    15

    Default making command line args (from Java main) available to beans

    HI All.

    I'm migrating some legacy code to Spring. I have command line processing logic that already exists and is not easily ported to using -D syntax (i.e., I don't want to rewrite that code at this time ). Also, using property placeholder configuration isn't practical for my use. Having to edit files in between invocations of the application is tedious.

    My existing code looks like:

    Code:
        public static void main(String[] argv)
        {
            Thing thing = new Thing(argv);
            // snip
        }
    I'd like to replace this by fetching the Thing from a Spring application context. How can I pass in argv so that it is available when Spring creates Thing?

    Would I have to create a custom Bean Factory or something? I'm new to Spring, so I'm not sure of the right approach.

    Thanks,

    -Doug

  2. #2
    Join Date
    Dec 2006
    Posts
    1,061

    Default

    I think we may need a little bit more detail. In general, i would have to say that you can't really pass in your command-line arguments into the BeanFactory to use to create the object. The best you can do is the PropertyPlaceHolderConfigurer. If you can't use -D parameters you *could* do System.setProperty(string,string) for the same affect, assuming you did it before creating your ApplicationContext. However, I would be curious about your use case for doing this. Is it some type of batch processing logic? If so, have you looked at Spring Batch? There is some specific support for converting command line arguments into JobParameters, that are then available for processing logic.

  3. #3
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,624

    Default

    With Spring 2.5 you can pas those parameters to the bean. Specify your bean as a prototype bean with constructor args. Then pass those into the getBean method of the BeanFactory/ApplicationContext.
    Last edited by Marten Deinum; Mar 19th, 2008 at 02:56 PM.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  4. #4
    Join Date
    Mar 2008
    Location
    Wayzata, MN
    Posts
    15

    Default

    Marten: I came across that API, but in my case a prototype bean isn't appropriate. This really is a singleton and the bean itself is referenced by other beans.

    Lucas: The use case is varied. I use it for various items in my DD Poker software - the client itself (swing), the server code (custom) and various command line applications (generating keys, validating things, etc.). Some are batch in nature, but they are the exception.

    As an example, in my poker software, I have command line switches (mostly used in development) to change the screen position, pick a user profile, etc.

    I know one can create beans programatically via scripting languages like Groovy/JRuby. Is there a way to define some beans programatically before loading the application context and merging them together?

  5. #5
    Join Date
    Mar 2008
    Location
    Wayzata, MN
    Posts
    15

    Default

    I have been thinking about this all day and I can think of no clean way to pass the main args array into a bean.

    I can hack it with statics or marshalling the string array into a string in system properties (ick), but that isn't clean or in the spirit of Spring.

    So let me ask a basic question from someone still new to Spring. If it isn't possible to define all beans at config time (as in this example), is there a way to provide beans at runtime? It seems like there would be many legitimate instances where configuration outside the scope of properties files would be needed. Think of all those unix commands and command line switches.

    I may be missing a basic tenant of IOC, so maybe someone can shed some light on the core philosophy as it applies to this case.

    Thanks,

    -Doug

  6. #6
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    What I do not understand, why you can not do the following

    1. Include into your context PropertyPlaceholderConfigurer and enable for it substitution from system properties
    2. Put placeholders into the bean definition of yout "thing"
    3. Parse your command-line arguments in your main method
    4. Assign values to the system properties with System.setProperty()
    5. Create context, then placeholders will be substituted
    6. Fetch your "thing" from context


    Regards,
    Oleksandr

    Quote Originally Posted by donohoe View Post
    I have been thinking about this all day and I can think of no clean way to pass the main args array into a bean.

    I can hack it with statics or marshalling the string array into a string in system properties (ick), but that isn't clean or in the spirit of Spring.

    So let me ask a basic question from someone still new to Spring. If it isn't possible to define all beans at config time (as in this example), is there a way to provide beans at runtime? It seems like there would be many legitimate instances where configuration outside the scope of properties files would be needed. Think of all those unix commands and command line switches.

    I may be missing a basic tenant of IOC, so maybe someone can shed some light on the core philosophy as it applies to this case.

    Thanks,

    -Doug

  7. #7
    Join Date
    Mar 2008
    Location
    Wayzata, MN
    Posts
    15

    Default

    Hi Oleksandr,

    We had several a dozen or more applications that use our existing command line argument processing API. This API does validation (e.g., required arguments), type conversion, user help and default values.

    Your suggestion is a possible workaround, but it requires rewriting code I didn't want to touch. Fortunately, I have that option.

    I think what I may do is convert all the command line arguments to a string, put that in a system property. Then fetch that out, convert back to the args string array and go on from there.

    I've seen some other posts here and in other forums basically asking for the same feature: The ability to introduce pre-existing beans into a bean factory/application context.

    for example, what about a definition which allows you to specify that a property is an external reference "xref":

    Code:
    <property name="args" xref="mainArgs">
    <constructor-arg xref="mainArgs">
    Then, when creating the application context, pass in a Map with the beans:

    Code:
    public static void main(String[] args) 
    {
        Map map = new HashMap<String, Object>();
        map.put("mainArgs", args);
        ApplicationContext ctx = new ClassPathXmlApplicationContext(paths, map)
        ...
    }
    Has something like this been considered in the past?

    Thanks,

    -Doug
    Last edited by donohoe; Mar 20th, 2008 at 08:44 AM.

  8. #8
    Join Date
    Mar 2008
    Posts
    3

    Default

    How about (I haven't tried this but...)

    Code:
    <bean id="args" class="org.springframework.beans.factory.config.ListFactoryBean" />
    Code:
    public static void main(String[] args) 
    {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(paths);
        ((ListFactoryBean)ctx.getBean("args")).setSourceList(Arrays.asList(args));
    }
    Then you can reference "args" in the constructor-arg for the "Thing" bean.

    Alternatively, if you want to keep it as an array, just define your own class with a getter and setter for a String[] array, and set it in a similar way.
    Last edited by nhowes; Mar 20th, 2008 at 08:35 AM.

  9. #9
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    First of all it is possible to add beans/bean definitions to the context programmatically (what do you think - how bean definitions from XML file cones to context?).
    But adding a bean in your case would not help - as you stated that this bean is referenced by other beans, so those other beans can not be created till this bean is adde to the context, which in turn, means that context itself can not be created (this statement is somewhat wrong, but wait a little...).

    Lickily, ClassPAthXmlApplicationContext supports 2-stage creation -
    i.e. it is possible first create context without instantiation the beans in it and then instantiate a beans. It is done by calling context constructor with additional boolean parameter set to false. And then calling a refresh() on the created context.
    Between this 2 calls you may add to the context additional bean definitions (
    see org.springframework.beans.factory.support.RootBean Definition) and register then (see org.springframework.beans.factory.support.BeanDefi nitionBuilder.registerBeanDefinition() ).

    Yopu may as well want to take a look on org.springframework.context.supportGenericApplicat ionContext that allows even more flexibility.

    Regards,
    Oleksandr



    Quote Originally Posted by donohoe View Post
    Hi Oleksandr,

    We had several a dozen or more applications that use our existing command line argument processing API. This API does validation (e.g., required arguments, type conversion, user help and default values.

    Your suggestion is a possible workaround, but it requires rewriting code I didn't want to touch. Fortunately, I have that option.

    I think what I may do is convert all the command line arguments to a string, put that in a system property. Then fetch that out, convert back to the args string array and go on from there.

    I've seen some other posts here and in other forums basically asking for the same feature: The ability to introduce pre-existing beans into a bean factory/application context.

    for example, what about a definition which allows you to specify that a property is an external reference "xref":

    Code:
    <property name="args" xref="mainArgs">
    <constructor-arg xref="mainArgs">
    Then, when creating the application context, pass in a Map with the beans:

    Code:
    public static void main(String[] args) 
    {
        Map map = new HashMap<String, Object>();
        map.put("mainArgs", args);
        ApplicationContext ctx = new ClassPathXmlApplicationContext(paths, map)
        ...
    }
    Has something like this been considered in the past?

    Thanks,

    -Doug

  10. #10
    Join Date
    Mar 2008
    Location
    Wayzata, MN
    Posts
    15

    Default

    Oleksandr,

    Thanks very much - your post helps quite a bit. I didn't understand that 'refresh' delayed the singleton bean creation. Excellent!

    I browsed the code and found that the only way to get access to registering beans is to get the DefaultListableBeanFactory via a cast:

    Code:
    String[] contextPaths = new String[] {"app-context-pokerserver.xml"};
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(contextPaths);
    DefaultListableBeanFactory factory = (DefaultListableBeanFactory) ctx.getBeanFactory();
    This type of cast is generally something to avoid since it embeds knowledge of internal implementation that could change, but probably something I can live with.

    I'll also take a look at the GenericApplicationContext.

    Great post - thanks for the help.

    -Doug

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •