Apache jclouds java.lang.NoSuchMethodError when using Rackspace in a Spring Boot application
Asked Answered
A

6

5

I'm trying to integrate Apache Jclouds into a Spring Boot application I'm working on so that I can upload files to Rackspace Cloud Files(UK).

I've created a class which I'm creating as a Bean;

import com.google.common.io.ByteSource;
import com.google.common.io.Files;
import org.jclouds.ContextBuilder;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.openstack.swift.v1.features.ObjectApi;
import org.jclouds.rackspace.cloudfiles.v1.CloudFilesApi;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Class WebStorage
 *
 */
public class WebStorage {

    private final String region = "lon";
    private final String provider = "rackspace-cloudfiles-uk";

    private String username;
    private String apiKey;
    private String container;
    private String url;

    private CloudFilesApi cloudFilesApi;

    public WebStorage(String username, String apiKey, String container, String url) {
        this.username = username;
        this.apiKey = apiKey;
        this.container = container;
        this.url = url;

        cloudFilesApi = ContextBuilder.newBuilder(provider)
                .credentials(this.username, this.apiKey)
                .buildApi(CloudFilesApi.class);
    }

    /**
     * Accepts a MultipartFile and returns it as a File
     * @param multipartFile the MultiPartFile to convert
     * @return a new File
     */
    public static File createFileFromMultipart(MultipartFile multipartFile) {
        File file = new File(multipartFile.getOriginalFilename());

        try {
            file.createNewFile();
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(multipartFile.getBytes());
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return file;
    }

    /**
     * Send the file to the CDN and return it's full URL
     * @param name the path/name of the file on the CDN
     * @param theFile the file to upload to the CDN
     * @return the full URL to the uploaded file on the CDN
     */
    public String put(String name, File theFile) {
        ObjectApi objectApi = cloudFilesApi.getObjectApi(region, this.container);

        /* Upload the file */
        ByteSource byteSource = Files.asByteSource(theFile);
        Payload filePayload = Payloads.newByteSourcePayload(byteSource);
        objectApi.put(name, filePayload);

        return this.url + name;
    }

}

I'm declaring the bean in the same place I'm declaring all other beans (which work fine);

@Bean
    public WebStorage storage() {
        return new WebStorage(
                env.getProperty("voila.cdn.username"),
                env.getProperty("voila.cdn.apikey"),
                env.getProperty("voila.cdn.container"),
                env.getProperty("voila.cdn.url")
        );
    }

But each time I run the application, the bean fails to load as a class not found error is generated when trying to create the bean.

2016-09-21 12:19:06.847 ERROR 14911 --- [  restartedMain] o.s.boot.SpringApplication               : Application startup failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'storage': Error creating bean with name 'storage' defined in class path resource [com/appapi/config/AppConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.appapi.helpers.WebStorage]: Factory method 'storage' threw exception; nested exception is java.lang.NoSuchMethodError: com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.<init>(Lcom/google/gson/internal/ConstructorConstructor;Lcom/google/gson/FieldNamingStrategy;Lcom/google/gson/internal/Excluder;)V; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'storage' defined in class path resource [com/appapi/config/AppConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.appapi.helpers.WebStorage]: Factory method 'storage' threw exception; nested exception is java.lang.NoSuchMethodError: com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.<init>(Lcom/google/gson/internal/ConstructorConstructor;Lcom/google/gson/FieldNamingStrategy;Lcom/google/gson/internal/Excluder;)V
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) ~[spring-context-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:369) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:313) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1185) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1174) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at com.appapi.VoilaApplication.main(VoilaApplication.java:12) [classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.4.0.RELEASE.jar:1.4.0.RELEASE]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'storage' defined in class path resource [com/appapi/config/AppConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.appapi.helpers.WebStorage]: Factory method 'storage' threw exception; nested exception is java.lang.NoSuchMethodError: com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.<init>(Lcom/google/gson/internal/ConstructorConstructor;Lcom/google/gson/FieldNamingStrategy;Lcom/google/gson/internal/Excluder;)V
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1214) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1019) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:566) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    ... 24 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.appapi.helpers.WebStorage]: Factory method 'storage' threw exception; nested exception is java.lang.NoSuchMethodError: com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.<init>(Lcom/google/gson/internal/ConstructorConstructor;Lcom/google/gson/FieldNamingStrategy;Lcom/google/gson/internal/Excluder;)V
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    ... 37 common frames omitted
Caused by: java.lang.NoSuchMethodError: com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.<init>(Lcom/google/gson/internal/ConstructorConstructor;Lcom/google/gson/FieldNamingStrategy;Lcom/google/gson/internal/Excluder;)V
    at org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactory.<init>(DeserializationConstructorAndReflectiveTypeAdapterFactory.java:116) ~[jclouds-core-1.9.2.jar:1.9.2]
    at org.jclouds.json.config.GsonModule.provideGson(GsonModule.java:129) ~[jclouds-core-1.9.2.jar:1.9.2]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at com.google.inject.internal.ProviderMethod.get(ProviderMethod.java:104) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:40) ~[guice-3.0.jar:na]
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1031) ~[guice-3.0.jar:na]
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40) ~[guice-3.0.jar:na]
    at com.google.inject.Scopes$1$1.get(Scopes.java:65) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:40) ~[guice-3.0.jar:na]
    at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38) ~[guice-3.0.jar:na]
    at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62) ~[guice-3.0.jar:na]
    at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:84) ~[guice-3.0.jar:na]
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:254) ~[guice-3.0.jar:na]
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1031) ~[guice-3.0.jar:na]
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40) ~[guice-3.0.jar:na]
    at com.google.inject.Scopes$1$1.get(Scopes.java:65) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:40) ~[guice-3.0.jar:na]
    at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:54) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InternalInjectorCreator$1.call(InternalInjectorCreator.java:204) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InternalInjectorCreator$1.call(InternalInjectorCreator.java:198) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1024) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InternalInjectorCreator.loadEagerSingletons(InternalInjectorCreator.java:198) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:179) ~[guice-3.0.jar:na]
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:109) ~[guice-3.0.jar:na]
    at com.google.inject.Guice.createInjector(Guice.java:95) ~[guice-3.0.jar:na]
    at org.jclouds.ContextBuilder.buildInjector(ContextBuilder.java:402) ~[jclouds-core-1.9.2.jar:1.9.2]
    at org.jclouds.ContextBuilder.buildInjector(ContextBuilder.java:326) ~[jclouds-core-1.9.2.jar:1.9.2]
    at org.jclouds.ContextBuilder.buildApi(ContextBuilder.java:644) ~[jclouds-core-1.9.2.jar:1.9.2]
    at org.jclouds.ContextBuilder.buildApi(ContextBuilder.java:636) ~[jclouds-core-1.9.2.jar:1.9.2]
    at com.appapi.helpers.WebStorage.<init>(WebStorage.java:49) ~[classes/:na]
    at com.appapi.config.AppConfiguration.storage(AppConfiguration.java:41) ~[classes/:na]
    at com.appapi.config.AppConfiguration$$EnhancerBySpringCGLIB$$d004a038.CGLIB$storage$2(<generated>) ~[classes/:na]
    at com.appapi.config.AppConfiguration$$EnhancerBySpringCGLIB$$d004a038$$FastClassBySpringCGLIB$$59b0bc24.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:356) ~[spring-context-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at com.appapi.config.AppConfiguration$$EnhancerBySpringCGLIB$$d004a038.storage(<generated>) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    ... 38 common frames omitted

I've included version 1.9.2 of jclouds in my pom file;

<dependency>
            <groupId>org.apache.jclouds</groupId>
            <artifactId>jclouds-all</artifactId>
            <version>1.9.2</version>
        </dependency>

Is someone able to tell my why I'm getting a class not found error? I'm not including any Google dependencies in my POM, so any that are available are being included by one of my dependencies.

Aerobiosis answered 21/9, 2016 at 11:31 Comment(2)
Can you share mvn dependency:tree outputDiocesan
Check the version of jClouds which you are using. It might be that you are referencing a method not available in the version specified in your project.Exteroceptor
U
7

This is a known issue in jclouds. See JCLOUDS-1160 and JCLOUDS-1166. Until those issues are fixed you won't be able to use jclouds with Spring Boot if you can't force a version of Gson <= 2.5.

Another option is to use the maven-shade-plugin to shade the jclouds dependencies and bundle Gson in it. This way you should be able to use the Gson version your environment needs, while jclouds uses the shaded one.

Umbrella answered 21/9, 2016 at 12:54 Comment(5)
Thanks for the comment. How would I use the maven-shade-plugin to resolve this? Sorry, I've not used the shade-plugin before.Aerobiosis
Have a look at the jclouds Jenkins plugin. It basically defines a module, jclouds-shaded, that has the jclouds dependencies and configures the maven-shade-plugin to produce an uber jar with jclouds and the conflicting dependencies. They shade Guava/Guice, but in your case you want to shade just Gson. They also configure the package relocation to avoid package collisions with the "outer" dependencies (you should do it with Gson too).Umbrella
Finally, in your project, instead of having the dependencies on jclouds you just need to depend on the "jclouds-shaded" jar you just created. It will contain everything that is needed.Umbrella
Thanks I'll attempt to give that a try. Does anyone know how long it might be before the issue is resolved in jclouds itself?Aerobiosis
Unfortunately there is no ETA for that fix. You might want to follow the discussion here: markmail.org/message/zqsj5ao4fjvalcohUmbrella
A
4

I initially struggled creating the ubar jar using the shaded plugin, and eventually got it working. For anyone else who may have struggled like me.

First, create a new (empty) maven project.

Here's my POM file which included apache jclouds an older version of Gson, and renamed the Gson package for use inside the final jar so it doesn't conflict with Gson in my main project (see the relocations section of the POM file).

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>digital.sheppard</groupId>
    <artifactId>jclouds-shaded</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.jclouds</groupId>
            <artifactId>jclouds-all</artifactId>
            <version>1.9.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.4.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <relocations>
                                <relocation>
                                    <pattern>com.google.code.gson</pattern>
                                    <shadedPattern>com.shaded.code.gson</shadedPattern>
                                </relocation>
                            </relocations>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

At the terminal, build the project to create the jar file.

mvn package

Finally, copy the jar file into the root of your main project, in my case a spring boot web application.

Create a folder in the root of your project called 'maven-local-repo', and at the terminal execute the following command to install your newly created shaded jar file into the local repository of your project (obviously change the filename, artifact etc to match the shaded jar you created).

mvn deploy:deploy-file -DgroupId=digital.sheppard -DartifactId=jclouds-shaded -Dversion=1.0-SNAPSHOT -Durl=file:./local-maven-repo/ -DrepositoryId=local-maven-repo -DupdateReleaseInfo=true -Dfile=jclouds-shaded-1.0-SNAPSHOT.jar

Add your local repository to your POM file;

<repositories>
        <repository>
            <id>local-maven-repo</id>
            <url>file:///${project.basedir}/local-maven-repo</url>
        </repository>
    </repositories>

Finally add a dependency;

<dependency>
    <groupId>digital.sheppard</groupId>
    <artifactId>jclouds-shaded</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

For me that worked like a charm.

Aerobiosis answered 3/11, 2016 at 9:0 Comment(2)
Nice! For me it was sufficient to build the shaded jar with <artifactId>jclouds-core</artifactId>Cymophane
I'll just leave it here - bintray.com/rocketraccoon/jclouds-shaded-gson-repo/…Hockenberry
G
2

For those who strumbled upon this in early 2023, my jcloud version is 2.5.0, and I solved it by forcing gradle to use Gson 2.8.9.

configurations {
    all {
        resolutionStrategy {
            force 'com.google.code.gson:gson:2.8.9'
        }
    }
}

Take a look at the release notes of newer jcloud versions to see what's the lasted supported GSON version and try to use it. Sometimes Guava can also cause compatibility issues with Spring.

Georgiegeorgina answered 13/12, 2022 at 11:16 Comment(0)
M
1

I switched my Java Code to use their raw HTTP API:

https://support.rackspace.com/how-to/cloud-files-curl-cookbook/

e.g.

public class CloudFilesClient {

  private final String username; 
  private final String apiKey;
  private final RestTemplate rest = new RestTemplate();

  private static final String AUTH_URL      = "https://auth.api.rackspacecloud.com/v1.1/auth";

  private static final String AUTH_DOCUMENT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + 
                                              "<credentials xmlns=\"http://docs.rackspacecloud.com/auth/api/v1.1\" " + 
                                              "             username=\"%s\" " + 
                                              "             key=\"%s\" />";


  private CloudFilesClient (String aUsername, String aApiKey) {
    username = aUsername;
    apiKey = aApiKey;
  }

  public List<Map<String,Object>> listObjects (String aRegionName, String aContainerName) {
    Map<String, Object> auth = auth();
    Map<String, Object> cloudFiles = cloudFiles(auth, aRegionName);
    HttpHeaders headers = new HttpHeaders();
    headers.set("X-Auth-Token", token(auth));
    HttpEntity<?> e = new HttpEntity<>(headers);
    ResponseEntity<List> exchange = rest.exchange(((String)cloudFiles.get("publicURL"))+"/"+aContainerName, HttpMethod.GET, e, List.class);
    return exchange.getBody();
  }

  public Map<String,Object> putObject (String aRegionName, String aContainerName, String aObjectName, String aLocalFilePath) throws IOException {
    Map<String, Object> auth = auth();
    Map<String, Object> cloudFiles = cloudFiles(auth, aRegionName);
    String token = token(auth);
    File file = new File(aLocalFilePath);
    Assert.isTrue(file.exists(),"File not found: " + aLocalFilePath);
    HttpHeaders headers = new HttpHeaders();
    headers.set("X-Auth-Token", token);
    headers.setContentType(MediaType.valueOf(Files.probeContentType(file.toPath())));
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    HttpEntity<Resource> requestEntity = new HttpEntity<>(new FileSystemResource(file), headers);
    ResponseEntity<Map> result = rest.exchange(((String)cloudFiles.get("publicURL"))+"/"+aContainerName+"/"+aObjectName, HttpMethod.PUT, requestEntity,Map.class);
    return result.getBody();
  }

  public String getTemporaryUrl (String aRegionName, String aContainerName, String aObjectName) throws URISyntaxException {
    String key = UUID.randomUUID().toString();
    Map<String, Object> auth = auth();
    Map<String, Object> cloudFiles = cloudFiles(auth, aRegionName);
    HttpHeaders headers = new HttpHeaders();
    headers.set("X-Auth-Token", token(auth));
    headers.set("X-Account-Meta-Temp-Url-Key", key);
    HttpEntity<?> e = new HttpEntity<>(headers);
    rest.exchange(((String)cloudFiles.get("publicURL")), HttpMethod.POST, e, List.class);
    String method = "GET";
    long expires = System.currentTimeMillis()/1000+3600;
    URI endpoint = (new URI((String)cloudFiles.get("publicURL")));
    String base = endpoint.getScheme()+"://"+endpoint.getHost();
    String path = endpoint.getPath()+"/"+aContainerName+"/"+aObjectName;
    String hmacBody = String.format("%s\n%s\n%s", method,expires,path);
    String sig = HmacUtils.hmacSha1Hex(key, hmacBody);
    return String.format("%s%s?temp_url_sig=%s&temp_url_expires=%s",base, path, sig, expires);
  }

  private Map<String,Object> auth () {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_XML);
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    HttpEntity<String> requestEntity = new HttpEntity<>(String.format(AUTH_DOCUMENT,username,apiKey), headers);
    ResponseEntity<Map> result = rest.exchange(AUTH_URL, HttpMethod.POST, requestEntity, Map.class);
    Map<String,Object> body = result.getBody();
    return (Map<String, Object>) body.get("auth");
  }

  private Map<String, Object> cloudFiles (Map<String,Object> aAuth, String aRegionName) {
    Map<String, Object> catalog = (Map<String, Object>) aAuth.get("serviceCatalog");
    List<Map<String, Object>> cloudFiles = (List<Map<String, Object>>) catalog.get("cloudFiles");
    Optional<Map<String, Object>> region = cloudFiles.stream().filter(r->r.get("region").equals(aRegionName)).findFirst();
    Assert.isTrue(region.isPresent(),"Unknown region: " + aRegionName);
    return region.get();
  }

  private String token (Map<String,Object> aAuth) {
    Map<String, Object> token = (Map<String, Object>) aAuth.get("token");
    return (String)token.get("id");
  }

  public static CloudFilesClient build (String aUsername, String aApiKey) {
    return new CloudFilesClient(aUsername, aApiKey);
  }

}
Milli answered 21/3, 2017 at 4:11 Comment(0)
O
0

I know hardly anything about gradle, SpringBoot, or JClouds...

With those disclaimers out of the way, I hacked my gradle build file like so:

dependencies {
...
    // REQUIRED TO ALLOW JCLOUDS AND GSON TO PLAY NICELY.... XXX
    compile 'com.google.code.gson:gson:2.5'
...
}

and this seemed to have the effect of locking the GSON version to 2.5 which is the newest version that works with both JClouds and SpringBoot. With this hack in place, I can build and run my application.

Oakum answered 1/11, 2017 at 17:14 Comment(1)
This does not seem to work with the current version of Spring Boot as it seems to rely on gson features introduced after 2.5. In particular 2.5 does not seem to have com.google.gson.GsonBuilder.setLenient()Lcom/google/gson/GsonBuilder;Loiseloiter
A
0

Below import worked for me:

implementation 'com.google.code.gson:gson:2.8.9'
Amblyoscope answered 3/3, 2023 at 10:44 Comment(1)
github.com/gokhandegerli/jclouds01Amblyoscope

© 2022 - 2024 — McMap. All rights reserved.