8

I'm reading this documentation:

http://cloud.spring.io/spring-cloud-aws/spring-cloud-aws.html

on using AWS from a Spring application. I'm particularly interested in S3, so, I set up the application and copied this snippet of code to make sure the set up is working correctly:

Resource resource = this.resourceLoader.getResource("s3://myBucket/rootFile.log");
WritableResource writableResource = (WritableResource) resource;
try (OutputStream outputStream = writableResource.getOutputStream()) {
  outputStream.write("test".getBytes());
}

but when I run it, I get this error:

java.lang.ClassCastException: org.springframework.web.context.support.ServletContextResource cannot be cast to org.springframework.core.io.WritableResource

Any ideas what's wrong? is that a setup problem? It doesn't look like to me, but I'm new to this.

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622

4 Answers4

3

I had to remove

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

from my dependencies.

When devtools is included, inside the GenericApplicationContext.getResources() method it executes the if block, but without devtools it calls the super.getResource():

public Resource getResource(String location) {
    if (this.resourceLoader != null) {
        return this.resourceLoader.getResource(location);
    }
    return super.getResource(location);
}

This will use the s3 protocol handler and give you a different type of Resource that does implement WritableResource.

accresse
  • 181
  • 8
  • Commenting out `spring-boot-devtools` also worked for me. Thanks – Fedor Oct 26 '18 at 21:36
  • I've tried this solution but without success. Using Spring Boot 2.1.7 e Spring Cloud Aws 2.1.4. I had to fix it using another approach. I'll post it here. Thanks anyway. – Felipe Desiderati Nov 27 '19 at 21:39
1

I've tried to follow the solution proposed by @accresse, but without success. My only way of solving it was declaring a new SimpleStorageProtocolResolver and adding it as a ProtocolResolver to the DefaultResourceLoader. Using Spring Boot 2.1.7 e Spring Cloud Aws 2.1.4.

@Autowired
public void configureResourceLoader(AmazonS3 amazonS3, DefaultResourceLoader resourceLoader) {
    SimpleStorageProtocolResolver simpleStorageProtocolResolver = new SimpleStorageProtocolResolver(amazonS3);
    // As we are calling it by hand, we must initialize it properly.
    simpleStorageProtocolResolver.afterPropertiesSet();
    resourceLoader.addProtocolResolver(simpleStorageProtocolResolver);
}

Links to follow:

Felipe Desiderati
  • 2,414
  • 3
  • 24
  • 42
  • 1
    Thank you, this has helped me with `Spring boot 2.3.1` and `spring-cloud-starter 2.2.2`. However, I'd still like to find out how to make the defaults to work. – Pavel Polyakov Jun 22 '20 at 10:32
0

I have the same issue, turn out that I am using the wrong lib. Try to change dependency from

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-aws</artifactId>
    </dependency>

To

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-aws</artifactId>
    </dependency>

Also do not forget to add aws config in application.yml, otherwise it won't start.

cloud.aws:
    region.static: your-region
    credentials:
      accessKey: your-key
      secretKey: your-secret

Hope this can solve your problem.

zt1983811
  • 1,011
  • 3
  • 14
  • 34
-1

Try declaring/casting Resource class as FileSystemResource instead:

FileSystemResource resource = this.resourceLoader.getResource("s3://myBucket/rootFile.log");
WritableResource writableResource = (WritableResource) resource;
try (OutputStream outputStream = writableResource.getOutputStream()) {
  outputStream.write("test".getBytes());
}
Eric K.
  • 1
  • 1