Search code examples
javaspringspring-bootspring-batchspring-cloud

class org.springframework.web.context.support.ServletContextResource cannot be cast to class org.springframework.core.io.WritableResource


I was using Spring batch (3.1.0) for my project. The requirement is to write the output file directly to GCP bucket. Using a custom writer:

public class GSWriter implements ItemWriter<String> {

    private ResourceLoader resourceLoader;
    private WritableResource resource;

public GSWriter(ResourceLoader resourceLoader, String resource) {
        this.resourceLoader = resourceLoader;
        this.resource = (WritableResource) this.resourceLoader.getResource(resource);
    }

@Override
    public void write(Chunk<? extends String> items) throws Exception {

        try (OutputStream outputStream = resource.getOutputStream()) {
            items.forEach((item) -> {
                try {
                    outputStream.write(item.getBytes());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }
}

When I pass the resource URL(gs://{BUCKET_NAME}/{file.csv}), I get the ClassCastException.

java.base/java.lang.Thread.run(Thread.java:833)
Caused by: org.springframework.beans.BeanInstantiationException: 
Failed to instantiate [com.equifax.dfca.cdms.configuration.GSWriter]: 
Factory method 'writer' threw exception with message: 
    class org.springframework.web.context.support.ServletContextResource cannot be cast to 
    class org.springframework.core.io.WritableResource 
    (org.springframework.web.context.support.ServletContextResource and 
    org.springframework.core.io.WritableResource are in unnamed module of loader 'app')

at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:171)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
... 25 common frames omitted

Caused by: java.lang.ClassCastException: 
    class org.springframework.web.context.support.ServletContextResource cannot be cast to 
    class org.springframework.core.io.WritableResource 
    (org.springframework.web.context.support.ServletContextResource and 
    org.springframework.core.io.WritableResource are in unnamed module of loader 'app')
    
    at ...

Solution

  • Import the dependency,

        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>spring-cloud-gcp-storage</artifactId>
        </dependency>
    

    use this bean for the resource object,

    import com.google.cloud.spring.storage.GoogleStorageResource;
    @Bean
    public GoogleStorageResource getResource() {
        Storage storage = StorageOptions.newBuilder().setProjectId(PROJECT_ID).build().getService();
        return new GoogleStorageResource(storage, "gs://" + BUCKET_NAME + "/" + filname.txt);
    }
    

    In custom writer,

    public class GSWriter implements ItemWriter<String> {
    private final Resource resource;
    public GSWriter(WritableResource resource) {
        this.resource = resource;
    }