Hello, I have a problem with configuring two storage types - FS and S3 (not at the same moment) in my Spring Boot app.
I have the class with the configuration for FS:
@Configuration
@ConditionalOnProperty(value = "storage.type", havingValue = "springContentFilesystem")
@EnableFilesystemStores
public class SpringContentFilesystemStorageConfig {
@Bean
FileSystemResourceLoader storageLocationResourceLoader(StorageProperties storageProperties) {
return new FileSystemResourceLoader(storageProperties.getAbsoluteStorageLocationPath().toString());
}
@Bean
StorageService storageService(ResourceContentStore resourceContentStore) {
return new SpringContentStorageService(resourceContentStore);
}
}
and the second one for S3:
@Configuration
@ConditionalOnProperty(value = "storage.type", havingValue = "springContentS3")
@EnableS3Stores
public class SpringContentS3StorageConfig {
@Bean
public AmazonS3 s3Client(@Value("${storage.s3.region}") String s3region) {
return AmazonS3ClientBuilder.standard()
.withRegion(Regions.fromName(s3region))
.withCredentials(new EnvironmentVariableCredentialsProvider())
.build();
}
@Bean
StorageService storageService(ResourceContentStore resourceContentStore) {
return new SpringContentStorageService(resourceContentStore);
}
}
Currently, this class with the configuration is not imported / scanned anywhere - just adding spring-content-s3-boot-starter to the classpath (pom.xml) causes that the Bean of the ContentStore type is not created - context does not get up.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'storageService' defined in com.mypackage.storage.springcontent.SpringContentFilesystemStorageConfig: Unsatisfied dependency expressed through method 'storageService' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mypackage.storage.springcontent.ResourceContentStore' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
public interface ResourceContentStore extends ContentStore<Resource, String> {
}
In my opinion, the problem lies somewhere in the auto-configuration when we have these two modules simultaneously:
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-fs-boot-starter</artifactId>
<version>1.1.0.M4</version>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-s3-boot-starter</artifactId>
<version>1.1.0.M4</version>
</dependency>
But I can't figure out exactly what's going on.
ContentStore
s are for associating content with Spring Data Entities and therefore you would type your ContentStore to your Entity; e.g. something like public interface MyEntityContentStore extends ContentStore<MyEntity, String>
. If you don't want to do this then you would probably be best using a Store; e.g. public interface MyStore extends Store<String>
. Which are you trying to do?
ResourceContentStore
.
ResourceContenStore
so you will need to tell it by adding two stores that extend the implementation-specific ContentStores
; i.e. FilesystemResourceContentStore extends FilesystemContentStore
and S3ResourceContentStore extends S3ContentStore
. That will have a bit of a ripple effect into your storageService bean initializor methods but SpringContentStorageService
can be typed to just ContentStore
. I believe that might work for you.
public static <T, ID extends Serializable> T newContentCopy(LockingAndVersioningRepository<T, ID> repository, T content, Consumer<T> onNewCopy) {
content = repository.lock(content);
try {
T newContent = repository.workingCopy(content);
try {
onNewCopy.accept(newContent);
return newContent;
} finally {
repository.unlock(newContent);
}
} finally {
repository.unlock(content);
}
}
unlock
someone else could then lock content and create another working copy. I am not sure that I have tested that! My assumption has always been that the lock extends across newWorkinCopy
and version
but that said, perhaps unlock should throw an error if a working copy exists.
public static <T, ID extends Serializable> T newContentCopy(LockingAndVersioningRepository<T, ID> repository, T content, Predicate<T> predicate, Consumer<T> onNewCopy) {
content = repository.lock(content);
try {
if (predicate.test(content)){
T newContent = repository.workingCopy(content);
try {
onNewCopy.accept(newContent);
} finally {
return repository.unlock(newContent);
}
}
} finally {
repository.unlock(content);
}
return null;
}```java
'return' inside 'finally' block
version
instead on newWorkingCopy
as it looks like you have no need for a real private working copy that can be edited multiple times and saved before versioning?
version
, not workingCopy
. Working copies is a construct brought over from CMIS and is meant for use cases where you have a user that will want to edit their 'word doc' many times before versioning it. Your code suggests you dont want working copies. You just want to version
save
and setContent
and because it is handled by hibernate removes the need for the pessimistic locking. It is more of an auditing thing but I mention it because it might be a better solution for you, depending on your use case.
workingCopy
doesn't set @SuccessorId, I think I have to use this method
version
can replaced with workingCopy
?