Skip to content

Commit

Permalink
Add StoreResolver to resolve conflicts to a single store
Browse files Browse the repository at this point in the history
- allow developers to add a StoreResolver through rest configuration
#201
  • Loading branch information
paulcwarren committed May 24, 2020
1 parent 88d6a8c commit fb6a06c
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
package internal.org.springframework.content.commons.storeservice;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.content.commons.repository.ContentStore;
import org.springframework.content.commons.repository.Store;
import org.springframework.content.commons.repository.factory.StoreFactory;
import org.springframework.content.commons.storeservice.*;
import org.springframework.content.commons.storeservice.StoreFilter;
import org.springframework.content.commons.storeservice.StoreInfo;
import org.springframework.content.commons.storeservice.StoreResolver;
import org.springframework.content.commons.storeservice.Stores;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

public class StoresImpl implements Stores {

private Set<StoreInfo> storeInfos = new HashSet<>();
private StoreResolver resolver;
private Map<String, StoreResolver> resolvers = new HashMap<>();

public StoresImpl() {
}
Expand Down Expand Up @@ -72,6 +72,11 @@ private Class<?> getDomainObjectClass(Class<?> contentStoreInterface) {
// return (ContentStoreInfo[]) getStores(ContentStore.class);
// }

@Override
public void addStoreResolver(String name, StoreResolver resolver) {
resolvers.put(name, resolver);
}

@Override
public StoreInfo getStore(Class<?> storeType, StoreFilter filter) {
Assert.notNull(storeType);
Expand All @@ -89,6 +94,7 @@ public StoreInfo getStore(Class<?> storeType, StoreFilter filter) {
}

if (candidates.size() > 1) {
StoreResolver resolver = resolvers.get(filter.name());
if (resolver == null) {
throw new IllegalStateException("unable to resolve store. Consider adding a StoreResolver");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.springframework.content.commons.storeservice;

public interface StoreFilter {
String name();
boolean matches(StoreInfo info);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,34 @@

@Service
public interface Stores {
StoreFilter MATCH_ALL = info -> true;
StoreFilter MATCH_ALL = new StoreFilter() {
@Override
public String name() {
return "MATCH_ALL";
}
@Override
public boolean matches(StoreInfo info) {
return true;
}
};

static StoreFilter withDomainClass(Class<?> domainClass) {
Assert.notNull(domainClass);
return info -> domainClass.equals(info.getDomainObjectClass());

return new StoreFilter() {
@Override
public String name() {
return domainClass.getCanonicalName();
}
@Override
public boolean matches(StoreInfo info) {
return domainClass.equals(info.getDomainObjectClass());
}
};
}

void addStoreResolver(String name, StoreResolver resolver);

StoreInfo getStore(Class<?> storeType, StoreFilter filter);

StoreInfo[] getStores(Class<?> storeType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ public Object answer(InvocationOnMock invocation)
It("should not return stores that dont match the filter", () -> {
StoreInfo[] infos = contentRepoService
.getStores(AssociativeStore.class, new StoreFilter() {
@Override
public String name() {
return "test";
}

@Override
public boolean matches(StoreInfo info) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ private StoreUtils() {

public static StoreFilter withStorePath(String storePath) {
return new StoreFilter() {
@Override
public String name() {
return storePath;
}
@Override
public boolean matches(StoreInfo info) {
return storePath.equals(storePath(info));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import internal.org.springframework.content.rest.mappings.StoreByteRangeHttpRequestHandler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.content.commons.storeservice.StoreResolver;
import org.springframework.content.commons.storeservice.Stores;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -64,6 +65,10 @@ public StoreCorsRegistry getCorsRegistry() {
return corsRegistry;
}

public void addStoreResolver(String name, StoreResolver resolver) {
stores.addStoreResolver(name, resolver);
}

@Bean
RequestMappingHandlerMapping contentHandlerMapping() {
ContentHandlerMapping mapping = new ContentHandlerMapping(stores, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ CollectionModel<?> searchContentInternal(RootResourceInformation repoInfo,

StoreInfo[] infos = stores.getStores(ContentStore.class,
new StoreFilter() {
@Override
public String name() {
return "test";
}

@Override
public boolean matches(StoreInfo info) {
return repoInfo.getDomainType()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package internal.org.springframework.content.rest.multistore;

import internal.org.springframework.content.rest.it.SecurityConfiguration;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.content.commons.annotations.ContentId;
import org.springframework.content.commons.annotations.ContentLength;
import org.springframework.content.commons.annotations.MimeType;
import org.springframework.content.commons.storeservice.StoreInfo;
import org.springframework.content.commons.storeservice.StoreResolver;
import org.springframework.content.fs.config.EnableFilesystemStores;
import org.springframework.content.fs.io.FileSystemResourceLoader;
import org.springframework.content.fs.store.FilesystemContentStore;
import org.springframework.content.jpa.config.EnableJpaStores;
import org.springframework.content.jpa.store.JpaContentStore;
import org.springframework.content.rest.config.ContentRestConfigurer;
import org.springframework.content.rest.config.RestConfiguration;
import org.springframework.context.annotation.*;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.UUID;


@SpringBootApplication
@ComponentScan(excludeFilters={
@Filter(type = FilterType.REGEX,
pattern = {
".*MongoConfiguration"
})
})
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@Configuration
@Import({RestConfiguration.class, SecurityConfiguration.class})
@EnableJpaRepositories(basePackages="internal.org.springframework.content.rest.multistore", considerNestedRepositories = true)
@EnableTransactionManagement
@EnableJpaStores(basePackages="internal.org.springframework.content.rest.multistore")
@EnableFilesystemStores(basePackages="internal.org.springframework.content.rest.multistore")
public static class AppConfig {

@Value("/org/springframework/content/jpa/schema-drop-h2.sql")
private ClassPathResource dropReopsitoryTables;

@Value("/org/springframework/content/jpa/schema-h2.sql")
private ClassPathResource dataReopsitorySchema;

@Bean
DataSourceInitializer datasourceInitializer(DataSource dataSource) {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();

databasePopulator.addScript(dropReopsitoryTables);
databasePopulator.addScript(dataReopsitorySchema);
databasePopulator.setIgnoreFailedDrops(true);

DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(dataSource);
initializer.setDatabasePopulator(databasePopulator);

return initializer;
}

@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.H2);
vendorAdapter.setGenerateDdl(true);

LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("internal.org.springframework.content.rest.multistore");
factory.setDataSource(dataSource());

return factory;
}

@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}

@Bean
public FileSystemResourceLoader fileSystemResourceLoader() throws IOException {
return new FileSystemResourceLoader(Files.createTempDirectory("").toFile().getAbsolutePath());
}

@Bean
public ContentRestConfigurer contentRestConfigurer() {
return new ContentRestConfigurer() {
@Override
public void configure(RestConfiguration config) {
config.addStoreResolver("tEntities", new StoreResolver() {

@Override
public StoreInfo resolve(StoreInfo... stores) {
for (StoreInfo info : stores) {
if (info.getImplementation(FilesystemContentStore.class) != null) {
return info;
}
}
return null;
}
});
}
};
}
}

@Entity
@Data
public static class TEntity {

@Id
@GeneratedValue
private Long id;

@ContentId
private String contentId;

@ContentLength
private Long contentLength;

@MimeType
private String mimeType;
}

public interface TEntityRepository extends JpaRepository<TEntity, Long> {}

public interface TEntityFsStore extends FilesystemContentStore<TEntity, String> {}
public interface TEntityJpaStore extends JpaContentStore<TEntity, String> {}
}
Loading

0 comments on commit fb6a06c

Please sign in to comment.