Spring Cloud AWS with proxy settings

In my previous article, Spring and Amazon Web Services, I provided a brief introduction to Spring Cloud AWS module and what you as a developer can expect from it at this point in time. One thing that is not obvious from the official documentation is how to use this module when your Internet connection is restricted by a proxy server. In this post I will outline how to approach passing of proxy configuration for both Java-based and XML-based configuration. This aspect of configuration will probably be addressed in the future releases however it might help you now in case you work with this module and need your application to work with a company proxy.

Spring Cloud AWS proxy configuration

Java configuration

Let’s start with ever more popular way of configuring Spring applications – Java configuration. Things are relatively simple in this case since you can provide required proxy configuration manually in code yourself. Consider the following configuration class declaring two beans – S3 client and proxy configuration (if these settings are not resolved from property files / properties default no-proxy connection will be used).

@Configuration
@EnableContextInstanceData
public final class ApplicationConfiguration {

    @Value("${proxy.host}")
    private String proxyHost;

    @Value("${proxy.port}")
    private int proxyPort;

    @Value("${proxy.username}")
    private String proxyUsername;

    @Value("${proxy.password}")
    private String proxyPassword;

    @Bean
    public AmazonS3Client amazonS3Client() {
        return new AmazonS3Client(clientConfiguration());
    }

    @Bean
    public ClientConfiguration clientConfiguration() {
        final ClientConfiguration clientConfiguration = new ClientConfiguration();

        clientConfiguration.setProxyHost(proxyHost);
        clientConfiguration.setProxyPort(proxyPort);
        clientConfiguration.setProxyUsername(proxyUsername);
        clientConfiguration.setProxyPassword(proxyPassword);

        return clientConfiguration;
    }
}

Given the implications of code like this consider marking this class with profile used to run the application on developers machine e.g. @Profile("local").

XML configuration

When it comes to proxy configuration using XML configuration certain degree of Spring configuration knowledge is required. In order to make this simple configuration work, we need to create the instance of AmazonS3Client with proxy settings stored in client configuration bean. Following XML file shows the entire configuration so lets break it down into several sections.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/cloud/aws/context http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context.xsd">

    <context:component-scan base-package="com.jakubstas.s3downloader"/>

    <!-- Bunch of simple configuration switches allowing access to instance metadata, integration of S3
         into ResourceLoader and region auto detection. Some of these are not essential for the example
         however it is always nice to have the information they provide at hand when needed. -->
    <aws-context:context-instance-data/>
    <aws-context:context-resource-loader/>
    <aws-context:context-region auto-detect="true"/>

    <!-- Configuration of Amazons credentials provider chain to allow execution on developers machine
         as well as in the Beanstalk environment. -->
    <aws-context:context-credentials>
        <aws-context:instance-profile-credentials/>
        <aws-context:simple-credentials access-key="#{systemProperties['AWS_ACCESS_KEY_ID']}" key="#{systemProperties['AWS_SECRET_KEY']}"/>
    </aws-context:context-credentials>

    <!-- Bean with client configuration with passed proxy settings (if these settings are not resolved
         from property files / properties default no-proxy connection will be used) -->

    <!-- The client instance created by hand with proxy configuration -->
    <bean id="amazonS3" class="com.amazonaws.services.s3.AmazonS3Client" autowire-candidate="true" autowire="constructor"/>

    <!-- Proxy configuration for any AWS related client code - currently used for S3 (but might as well be used for DynamoDB access, ...) -->
    <bean id="clientConfiguration" class="com.amazonaws.ClientConfiguration">
        <property name="proxyHost" value="${proxy.host}"/>
        <property name="proxyPort" value="${proxy.port}"/>
        <property name="proxyUsername" value="${proxy.username}"/>
        <property name="proxyPassword" value="${proxy.password}"/>
    </bean>
</beans>

Given the implications of code like this consider marking these beans with profile used to run the application on developers machine e.g. profile="local".

Going beyond S3

The example so far was pretty much restricted to S3. However this configuration can be used whenever applicable due to the way Amazon SDK was designed. Let’s take a look at the example of DynomoDB client. There are several clients for various Amazon AWS services that can make use of the approach discussed above.

@Configuration
@EnableContextInstanceData
public final class ApplicationConfiguration {

    @Value("${proxy.host}")
    private String proxyHost;

    @Value("${proxy.port}")
    private int proxyPort;

    @Value("${proxy.username}")
    private String proxyUsername;

    @Value("${proxy.password}")
    private String proxyPassword;

    @Bean
    public AmazonS3 amazonS3Client() {
        return new AmazonS3Client(clientConfiguration());
    }

    @Bean
    public AmazonDynamoDBClient amazonDynamoDBClient() {
        return new AmazonDynamoDBClient(clientConfiguration());
    }

    @Bean
    public ClientConfiguration clientConfiguration() {
        final ClientConfiguration clientConfiguration = new ClientConfiguration();

        clientConfiguration.setProxyHost(proxyHost);
        clientConfiguration.setProxyPort(proxyPort);
        clientConfiguration.setProxyUsername(proxyUsername);
        clientConfiguration.setProxyPassword(proxyPassword);

        return clientConfiguration;
    }
}

Conclusion

Passing the application configuration to your beans is pretty standard task that should not cause a lot of trouble to an experienced Spring developer. However given the varying level of experience and hurdles of day to day life of a developer this might cause trouble. This is why I would encourage anyone to try these examples on their own, since situation modeled here uses one of the fundamental approaches to Spring configuration design. Keep practicing and keep that corporate proxy at bay. 🙂

5 thoughts on “Spring Cloud AWS with proxy settings

  1. Great article that touches on a few aspects of what I’m trying to do as well!

    I’m trying to use Spring Boot to use both SNS and SQS. Unfortunately, the first run-time error complains that an amazonS3 bean isn’t defined. I’m not even using S3 in this particular app. 🙁

    1. Hi John, thanks. I can try to put some simple code together in the evening some day next week and get back to you. I would expect it to work in similar way. This might be interesting enough to post a small post about so I will probably put the code up on GitHub (depending on how it plays out).

      1. Hi Jakub,
        Could you please help me out to get file from S3 using Spring.
        The point where I got stuck is in spring batch, instead of reading from local directory file, need to read from S3.

        At the property name resource, need to send my file from S3 bucket.
        Thanks for your help in advance

    2. Hi John,

      I played around with this and it was fairly simple and straight forward to make it work. I reused one of my older personal projects to quickly post a message to my test SQS and all went well. I am not sure what you are trying to achieve but I was able to do what I wanted simply by following Springs reference docs in less than 10 minutes. Let’s break it down. I started by creating a separate beans.xml file that looks like this:

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:aws-messaging="http://www.springframework.org/schema/cloud/aws/messaging"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/cloud/aws/messaging http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging-1.0.xsd">
      
          <aws-messaging:queue-messaging-template id="queueMessagingTemplate"/>
      </beans>

      Once that was done, I moved to update the Java config portion of my app for proxy profile:

      @Profile("proxy")
      @Configuration
      @EnableConfigurationProperties({ProxyProperties.class})
      public class ApplicationConfigProxyProfile {
      
          @Autowired
          private ProxyProperties proxyProperties;
      
          @Bean
          public AmazonSQS AmazonSQS() {
              return new AmazonSQSClient(clientConfiguration());
          }
      
          @Bean
          public ClientConfiguration clientConfiguration() {
              final ClientConfiguration clientConfiguration = new ClientConfiguration();
      
              clientConfiguration.setProxyHost(proxyProperties.getHost());
              clientConfiguration.setProxyPort(proxyProperties.getPort());
              clientConfiguration.setProxyUsername(proxyProperties.getUsername());
              clientConfiguration.setProxyPassword(proxyProperties.getPassword());
      
              return clientConfiguration;
          }
      }

      Using the example from reference docs I just copied their SQS sender implementation:

      @Component
      public class SqsQueueSender {
      
          private final QueueMessagingTemplate queueMessagingTemplate;
      
          @Autowired
          public SqsQueueSender(AmazonSQS amazonSqs) {
              this.queueMessagingTemplate = new QueueMessagingTemplate(amazonSqs);
          }
      
          public void send(String message) {
              this.queueMessagingTemplate.send("spring-boot-proxy-test", MessageBuilder.withPayload(message).build());
          }
      }

      And hooked it up to my REST endpoint:

      @Component
      @Path("/rest")
      public class RestEndpoint {
      
          @Autowired
          private SqsQueueSender sqsQueueSender;
      
          @GET
          @Path("/sqs")
          public Response getUnansweredQuestions() {
              sqsQueueSender.send("Hello world!");
      
              return Response.ok("OK").build();
          }
      }

      I ran the app using the proxy profile (passing the credentials in via environment properties and injecting them using @ConfigurationProperties) and the message was delivered. Hope this helps.

  2. Thanks for looking at this again. I did resolve this and have now having problems with proxy information. Very frustrating. I’ll try your solutions for proxy and see what happens. Thanks again for your information.

Leave a Reply

Your email address will not be published. Required fields are marked *