Announcement Announcement Module
Collapse
No announcement yet.
Some questions about Spring integration with AMQP Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    OK good thanks for the lightning fast response

    Comment


    • #17
      Ok, so now I'm trying to configure my application to be able to spawn consumers dynamically.

      I looked at the example Gary suggested at https://github.com/SpringSource/spri...ed/dynamic-ftp

      However I'm still having some problems. It seems that my properties placeholders are not recognized and I get the following exception:

      Code:
      java.lang.reflect.InvocationTargetException
              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
              at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
              at java.lang.reflect.Method.invoke(Method.java:606)
              at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297)
              at java.lang.Thread.run(Thread.java:724)
      Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'providerConnectionFactory' defined in null: Could not resolve placeholder 'providerHost' in string value "${providerHost}"

      My config is as following for the dynamic inbound channel part (child application context).

      Code:
      providerContext.xml 
      
      ...
      	<context:property-placeholder />
      	
      	<bean
          	class="com.ericsson.ericloud.commander.notifications.NotificationManager"
          	id="notificationManager"/>
      	    
          <int-amqp:inbound-channel-adapter
          	id="providerChannelAdapter"
          	channel="providerChannel"
      		queue-names="providerQueue"
      		connection-factory="providerConnectionFactory"/>
      		
      	<int:channel
      		id="providerChannel">
      		<int:interceptors>
      			<int:wire-tap channel="loggingChannel" />
      		</int:interceptors>
      	</int:channel>		
      
      	<int:service-activator
      		id="providerServiceActivator"
      		input-channel="providerChannel"
      		ref="notificationReceiver"/>
          <bean
          	class="com.ericsson.ericloud.commander.notifications.NotificationReceiver"
          	id="notificationReceiver"/>
      
      	<rabbit:admin
      		connection-factory="providerConnectionFactory" />
      		
          <rabbit:template
      		id="amqpTemplate"
      		connection-factory="providerConnectionFactory" />
      		
          <rabbit:connection-factory
          	id="providerConnectionFactory"
          	host="${providerHost}"
          	port="${providerPort}"
          	username="${providerUsername}"
          	password="${providerPassword}"
          	virtual-host="${providerVirtualHost}"/>
      
      	<rabbit:queue
      		name="${providerQueue}"/>
      I've also modified the parent applicationContext to ignore unresolvable property placeholders

      Code:
      applicationContext.xml
      ...
      <context:property-placeholder 
          	location="classpath*:META-INF/spring/*.properties"
          	ignore-unresolvable="true"/>
      ...

      The class that set the properties dynamically

      Code:
      NotificationManager.java
      ...
      
      @Component
      public class NotificationManager {
      
      	private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class);
      
      	private final Map<String, MessageChannel> channels = new HashMap<String, MessageChannel>();
      
      	private final Map<MessageChannel, ConfigurableApplicationContext> contexts = new HashMap<MessageChannel, ConfigurableApplicationContext>();
      
      	public synchronized MessageChannel createNewProviderChannel(String provider) {
      		MessageChannel channel = this.channels.get(provider);
      		if (channel == null) {
      			ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
      					new String[] { "/META-INF/spring/providerContext.xml" }, CommanderMain.mainContext);
      			setEnvironmentForProvider(ctx, provider);
      			ctx.refresh();
      			channel = ctx.getBean("providerChannel", MessageChannel.class);
      			this.channels.put(provider, channel);
      			//Will works as the same reference is presented always
      			this.contexts.put(channel, ctx);
      		}
      		return channel;
      	}
      
      	/**
      	 * Use Spring 3.1. environment support to set properties for the customer-specific application context.
      	 * 
      	 * @param ctx
      	 * @param customer
      	 */
      	private void setEnvironmentForProvider(ConfigurableApplicationContext ctx, String provider) {
      		StandardEnvironment env = new StandardEnvironment();
      		Properties props = new Properties();
      		// Set properties for cloud provider (TODO, replace hard coded value with the correct values from the DB)
      		props.setProperty("providerHost", "localhost");
      		props.setProperty("providerPort", "5672");
      		props.setProperty("providerUsername", "guest");
      		props.setProperty("providerPassword", "guest");
      		props.setProperty("providerVirtualHost", "/");
      		props.setProperty("providerQueue", "providerQueue");
      		PropertiesPropertySource pps = new PropertiesPropertySource("providerProps", props);
      		env.getPropertySources().addLast(pps);
      		ctx.setEnvironment(env);
      	}
      
      }
      Finally the main entry point

      Code:
      public static void main(String[] args) throws Exception {
      
      		AbstractApplicationContext mainContext = new ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml");
      		NotificationManager notificationManager = mainContext.getBean(NotificationManager.class);
      		notificationManager.createNewProviderChannel("test");
      		mainContext.registerShutdownHook();
      	}

      Anything I'm missing?

      Thanks
      /Sebastien

      Comment


      • #18
        Actually there was no problem, 5the exception was just thrown when creating the child application context. Chaning the property-placeholder to

        <contextroperty-placeholder ignore-unresolvable="true" />

        in the child providerContext.xml did the trick.

        Now one last thing, and it's driving me crazy, I get a numberFormatException fo the placeholder resolution of field "port" because it is expecting an int and we provide a String

        Code:
        <rabbit:connection-factory
            	id="providerConnectionFactory"
            	host="${providerHost}"
            	port= "${providerPort}"
            	username="${providerUsername}"
            	password="${providerPassword}"
            	virtual-host="${providerVirtualHost}"/>
        How can you parse the ${providerPort} placeholder value to provide and int or Integer instead of the string?? This must be simple! Interestingly, putting a number is String format (e.g. "5672") instead of "${providerPort}" works fine, so the error I get does not make sense to me.

        Thanks
        /Sebastien

        Comment


        • #19
          Turn on debug logging for org.springframework - you'll get lots of useful information about context initialization, including placeholder resolution.

          Comment


          • #20
            Thanks Gary I figured it out (see above). But can you tell me how to convert the port to an int, as stated in reply #18 above? This is really the last step and I get 100% of what I want!

            Many thanks
            /Sebastien

            Comment


            • #21
              Sorry - our posts crossed.

              However, the debug logging should help with this port issue - there's no reason I can see that would prevent a placeholder for the port attribute.

              Try ${providerPort:5672}

              If that works, it means the providerPort property is not found.

              Comment


              • #22
                After some investigations, I realized that the original problem was the correct one i.e. it seems that the properties I define in the NotificationManager class are never seen what parsing the provicerContext.xml file, so the placeholders are not expanded, but are seen as litteral strings.

                I based myself on the ftp example as I mention above so I'm at a loss on how to get what I want done. Maybe my <contextroperty-placeholder/> statement need a reference to some property object?

                I did find something on the web at http://moi.vonos.net/2013/02/propertyplaceholderfail/ that seem to be related to the problem I'm experiencing.

                Any ideas?

                Thx,
                /Sebastien

                Comment


                • #23
                  I was able to make it work by dumping the dynamically generated properties into a temporary properties files that I then use as a property location when creating the child application context.

                  This is ugly and I don't like it but at least it works.

                  If anybody has a better idea please let me know.

                  Thx,
                  /Sebastien

                  Comment


                  • #24
                    As I suggested, a DEBUG log should show what's happening.

                    If you can reproduce it in a simple stand-alone app, I can probably help you figure out what's going on.

                    Comment


                    • #25
                      Hi,

                      I looked at the debug log and what seems to happen is that the providerContext.xml file is first parsed when the application context is created. At this moment the properties are not available (they are defined after the application context is created). And since they cannot be resolved, it generates a parsing error, which prevent the post-processor to parse the file again with the properties now defined. This is a similar situation at what I described here.

                      So it seems the properties need to be available before the xml file is parsed, either by dumping them to a file, like I did, or to a java properties object in memory and reference the properties source in the xml file. I think this could be an issue with spring integration actually.

                      In any case, I have a working solution for now and I have to move on. I'll revisit the implementation later.

                      Thanks a lot for your help

                      BR,
                      /Sebastien

                      Comment


                      • #26
                        As long as the properties object is added to the environment before the ctx.refresh(); (which it appears to be) all should be OK.

                        Glad you have a solution for now; don't hesitate to post back here if you decide to revisit.

                        Comment

                        Working...
                        X