在 Spring 引导应用程序中使用 Rackspace 时 Apache jclouds java.lang.NoSuchMethodError
Apache jclouds java.lang.NoSuchMethodError when using Rackspace in a Spring Boot application
我正在尝试将 Apache Jclouds 集成到我正在处理的 Spring 引导应用程序中,以便我可以将文件上传到 Rackspace Cloud Files(英国)。
我创建了一个 class,我正在将其创建为 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;
}
}
我在声明所有其他 bean(工作正常)的同一位置声明该 bean;
@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")
);
}
但每次我 运行 应用程序时,由于在尝试创建 bean 时会生成 class 未找到错误,所以无法加载 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.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.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.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.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.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.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.call(InternalInjectorCreator.java:204) ~[guice-3.0.jar:na]
at com.google.inject.internal.InternalInjectorCreator.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(<generated>) ~[classes/:na]
at com.appapi.config.AppConfiguration$$EnhancerBySpringCGLIB$$d004a038$$FastClassBySpringCGLIB$b0bc24.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
我在我的 pom 文件中包含了 1.9.2 版的 jclouds;
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-all</artifactId>
<version>1.9.2</version>
</dependency>
有人能告诉我为什么会出现 class 未找到错误吗?我没有在我的 POM 中包含任何 Google 依赖项,因此任何可用的都包含在我的一个依赖项中。
这是 jclouds 中的一个已知问题。参见 JCLOUDS-1160 and JCLOUDS-1166。在这些问题得到解决之前,如果您不能强制 Gson <= 2.5.
版本,您将无法将 jclouds 与 Spring Boot 一起使用
另一种选择是使用 maven-shade-plugin
隐藏 jclouds 依赖项并将 Gson 捆绑在其中。这样你应该能够使用你的环境需要的 Gson 版本,而 jclouds 使用阴影版本。
我最初使用 shaded 插件努力创建 ubar jar,并最终让它工作。对于任何可能像我一样挣扎的人。
首先,创建一个新的(空的)maven 项目。
这是我的 POM 文件,其中包含 apache jclouds 一个旧版本的 Gson,并重命名了 Gson 包以在最终 jar 中使用,因此它不会与我的主项目中的 Gson 冲突(请参阅 POM 的重定位部分文件)。
<?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>
在终端,构建项目以创建 jar 文件。
mvn package
最后,将 jar 文件复制到主项目的根目录中,在我的例子中是 spring 引导 Web 应用程序。
在项目的根目录中创建一个名为 'maven-local-repo' 的文件夹,然后在终端执行以下命令将新创建的着色 jar 文件安装到项目的本地存储库中(显然更改文件名,工件等以匹配您创建的阴影罐)。
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
将您的本地存储库添加到您的 POM 文件;
<repositories>
<repository>
<id>local-maven-repo</id>
<url>file:///${project.basedir}/local-maven-repo</url>
</repository>
</repositories>
最后添加一个依赖;
<dependency>
<groupId>digital.sheppard</groupId>
<artifactId>jclouds-shaded</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
对我来说就像一个魅力。
我将 Java 代码切换为使用他们的原始 HTTP API:
https://support.rackspace.com/how-to/cloud-files-curl-cookbook/
例如
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);
}
}
我对 gradle、SpringBoot 或 JClouds 几乎一无所知...
去掉这些免责声明后,我像这样破解了我的 gradle 构建文件:
dependencies {
...
// REQUIRED TO ALLOW JCLOUDS AND GSON TO PLAY NICELY.... XXX
compile 'com.google.code.gson:gson:2.5'
...
}
这似乎具有将 GSON 版本锁定为 2.5 的效果,这是适用于 JClouds 和 SpringBoot 的最新版本。有了这个 hack,我可以构建并 运行 我的应用程序。
我正在尝试将 Apache Jclouds 集成到我正在处理的 Spring 引导应用程序中,以便我可以将文件上传到 Rackspace Cloud Files(英国)。
我创建了一个 class,我正在将其创建为 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;
}
}
我在声明所有其他 bean(工作正常)的同一位置声明该 bean;
@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")
);
}
但每次我 运行 应用程序时,由于在尝试创建 bean 时会生成 class 未找到错误,所以无法加载 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.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.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.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.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.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.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.call(InternalInjectorCreator.java:204) ~[guice-3.0.jar:na]
at com.google.inject.internal.InternalInjectorCreator.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(<generated>) ~[classes/:na]
at com.appapi.config.AppConfiguration$$EnhancerBySpringCGLIB$$d004a038$$FastClassBySpringCGLIB$b0bc24.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
我在我的 pom 文件中包含了 1.9.2 版的 jclouds;
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-all</artifactId>
<version>1.9.2</version>
</dependency>
有人能告诉我为什么会出现 class 未找到错误吗?我没有在我的 POM 中包含任何 Google 依赖项,因此任何可用的都包含在我的一个依赖项中。
这是 jclouds 中的一个已知问题。参见 JCLOUDS-1160 and JCLOUDS-1166。在这些问题得到解决之前,如果您不能强制 Gson <= 2.5.
版本,您将无法将 jclouds 与 Spring Boot 一起使用另一种选择是使用 maven-shade-plugin
隐藏 jclouds 依赖项并将 Gson 捆绑在其中。这样你应该能够使用你的环境需要的 Gson 版本,而 jclouds 使用阴影版本。
我最初使用 shaded 插件努力创建 ubar jar,并最终让它工作。对于任何可能像我一样挣扎的人。
首先,创建一个新的(空的)maven 项目。
这是我的 POM 文件,其中包含 apache jclouds 一个旧版本的 Gson,并重命名了 Gson 包以在最终 jar 中使用,因此它不会与我的主项目中的 Gson 冲突(请参阅 POM 的重定位部分文件)。
<?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>
在终端,构建项目以创建 jar 文件。
mvn package
最后,将 jar 文件复制到主项目的根目录中,在我的例子中是 spring 引导 Web 应用程序。
在项目的根目录中创建一个名为 'maven-local-repo' 的文件夹,然后在终端执行以下命令将新创建的着色 jar 文件安装到项目的本地存储库中(显然更改文件名,工件等以匹配您创建的阴影罐)。
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
将您的本地存储库添加到您的 POM 文件;
<repositories>
<repository>
<id>local-maven-repo</id>
<url>file:///${project.basedir}/local-maven-repo</url>
</repository>
</repositories>
最后添加一个依赖;
<dependency>
<groupId>digital.sheppard</groupId>
<artifactId>jclouds-shaded</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
对我来说就像一个魅力。
我将 Java 代码切换为使用他们的原始 HTTP API:
https://support.rackspace.com/how-to/cloud-files-curl-cookbook/
例如
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);
}
}
我对 gradle、SpringBoot 或 JClouds 几乎一无所知...
去掉这些免责声明后,我像这样破解了我的 gradle 构建文件:
dependencies {
...
// REQUIRED TO ALLOW JCLOUDS AND GSON TO PLAY NICELY.... XXX
compile 'com.google.code.gson:gson:2.5'
...
}
这似乎具有将 GSON 版本锁定为 2.5 的效果,这是适用于 JClouds 和 SpringBoot 的最新版本。有了这个 hack,我可以构建并 运行 我的应用程序。