I'm using MapStore to persist the data into Database.
My application is a multi-tenant application so if new tenant comes in how to create a map at run time in mapstore..
if you see in the below code, currently I have hardcoded for tenant-1.
this is my configuration class
#Component
public class CacheConfig {
#Bean
public static Config config() {
System.err.println("config class");
Config config = new Config();
config.setInstanceName("hazelcast");
MapConfig mapCfg = new MapConfig();
mapCfg.setName("tenant-1");
mapCfg.setBackupCount(2);
mapCfg.setTimeToLiveSeconds(300);
MapStoreConfig mapStoreCfg = new MapStoreConfig();
mapStoreCfg.setClassName(PersonMapStore.class.getName()).setEnabled(true);
mapCfg.setMapStoreConfig(mapStoreCfg);
config.addMapConfig(mapCfg);
return config;
}
}
Any suggestions would be very helpful..
Thanks in advance....
you can use wildcards in the name. For example, you can set the name of the configuration to tenant-* to cover all tenants with the names like tenant-1, tenant-2 ...
Related
I have a swagger.yaml file containing definition of my API. I'm using the following piece of code to add it as a swagger resource and display it in swagger-ui:
#Primary
#Bean
public SwaggerResourcesProvider swaggerResourcesProvider(InMemorySwaggerResourcesProvider defaultResourcesProvider) {
return () -> {
SwaggerResource wsResource = new SwaggerResource();
wsResource.setName("Documentation");
wsResource.setSwaggerVersion("3.0");
wsResource.setLocation("/swagger.yaml");
List<SwaggerResource> resources = new ArrayList<>(defaultResourcesProvider.get());
resources.add(wsResource);
return resources;
};
}
However, I need to have a different security schema authorization URL for each instance of my application (dev, test, prod). The question is how to achieve it? Is it possible to programmatically add the security schema, which will be parametrized based on f.e. env variables?
You can do that using the following:
#Bean
public OpenAPI openAPI() {
return new OpenAPI()
.components(new Components().addSecuritySchemes("jwtScheme",
new SecurityScheme().type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER).name("Authorization")));
}
You can get whatever environment variables you want to parameterize this configuration. You can read more about this at https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api#10-swagger-ui-with-an-oauth-secured-api.
In order to implement some kind of namespace, I need to prefix the keys of a Redis JPA repository with a static string within a whole Spring application.
I read about the spring.cache.redis.key-prefix configuration option but it seems to be applicable to caches only.
How do I get the same behavior for JPA repositories?
In your #EnableRedisReposiories you can do:
#EnableRedisRepositories(keyspaceConfiguration = MyCustomKeyspaceConfiguration.class)
Then in the App config add a RedisMappingContext bean and the customer keyspace configuration class:
#Bean
public RedisMappingContext keyValueMappingContext() {
return new RedisMappingContext(
new MappingConfiguration(new IndexConfiguration(), new MyCustomKeyspaceConfiguration()));
}
public static class MyCustomKeyspaceConfiguration extends KeyspaceConfiguration {
#Override
protected Iterable<KeyspaceSettings> initialConfiguration() {
List<KeyspaceSetting> settings = new ArrayList<KeyspaceSetting>();
settings.add(new KeyspaceSetting(Foo.class, "my-prefix" + Foo.class.getName()));
return settings;
}
}
In the case above we're saying that for the class Foo prefix the keys with "my-prefix". KeyspaceConfiguration allows for the programmatic setup of keyspaces and time to live options for certain types.
In spring boot, I want to take data from database and store it into a bean object. This needs to be done once (cache), and for further request bean object needs to be used, not to make database call again.
Example
/*
"DataFromDB" -> bean should have the values
*/
List<Users> uList = ApplicationContext.getBean("DataFromDB");
Is there any way to achieve this ?
Thank you
During your application boot, you can simply create a bean List<Users> uList and populate it with your required info.
Bean creation will happen once and whenever you want to reuse it, just get that bean. Spring will take care of the rest.
Somewhere in a config file, declare the bean:
#Component
public class InitialConfiguration {
#Bean
public List<Users> ulist() {
List<Users> uList = null;
// ulist = populate it from db
return uList;
}
}
Spring will create a ulist bean and store it. Now whenever you want to use it, you can simply autowire it into your variables:
#Service
public class SomeRandomClass {
#Autowire
List<Users> ulist;
public void performOperationOnUList() {
ulist.get(0); // use it
}
}
you can use a caching mechanism like ehcache
To add Ehcache to your application, here is the very basic approach you can follow.
Add Ehcache through your build tool. Here is an example for Gradle.
dependencies {
compile("org.hibernate:hibernate-ehcache:5.2.12.Final")
}
Add Ehcache configuration, Here I'm using annotation-based bean configuration.
#Configuration
#EnableCaching
public class CacheConfiguration {
#Bean
public EhCacheManagerFactoryBean ehCacheManagerFactory() {
EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
cacheManagerFactoryBean.setShared(true);
return cacheManagerFactoryBean;
}
#Bean
public EhCacheCacheManager ehCacheCacheManager() {
EhCacheCacheManager cacheManager = new EhCacheCacheManager();
cacheManager.setCacheManager(ehCacheManagerFactory().getObject());
cacheManager.setTransactionAware(true);
return cacheManager;
}
}
Define cache Regions. Here you can define individual caches for each repository you want to cache. create file named ehcache.xml and place in classpath.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ehcache>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<cache name="usercache" maxElementsInMemory="100" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="3600" overflowToDisk="true"/>
</ehcache>
Add Cachable annotation to transactional methods where you want to cache the DB operation.
#Cacheable(value = "userCache", key = "#p0")
public Company find(Long id) {
//db operation in here
}
From your problem statement, what I can understand is you want to cache objects from database, caching should be done only once (preferably on application start-up) and should be accessible anywhere in the context.
For this, you can store the data from db in a static final collection. The caching operation can be done on application startup via EventListener annotation.
#Component
public class DbCache {
public static final List<Object> dbCache = new ArrayList<>();
#EventListener(value = ApplicationReadyEvent.class)
private void initCache() {
List<Object> dataFromDB = // data fetched from DB
dbCache.addAll(dataFromDB);
}
public static List<Object> getDbCache() {
return dbCache;
}
}
You can use the DbCache.getDbCache() anywhere in your code now to fetch the data.
I'm trying to configure Spring CacheManager with Hazelcast. Also, I want to configure Hazelcast's Near Cache so I can retrieve the (already deserialized) instance of my cached object.
Here is my configuration
#Bean
public HazelcastInstance hazelcastConfig() {
val config = new Config().setInstanceName("instance");
val serializationConfig = config.getSerializationConfig();
addCacheConfig(config, "USERS")
serializationConfig.addSerializerConfig(new SerializerConfig()
.setImplementation(getSerializer())
.setTypeClass(User.class)
return Hazelcast.newHazelcastInstance(config);
}
#Bean
public CacheManager cacheManager(HazelcastInstance hazelcastInstance) {
return new HazelcastCacheManager(hazelcastInstance);
}
#Bean
public PlatformTransactionManager chainedTransactionManager(PlatformTransactionManager jpaTransactionManager, HazelcastInstance hazelcastInstance) {
return new ChainedTransactionManager(
jpaTransactionManager,
new HazelcastTransactionManager(hazelcastInstance)
);
}
// Configure Near Cache
private void addCacheConfig(Config config, String cacheName) {
val nearCacheConfig = new NearCacheConfig()
.setInMemoryFormat(OBJECT)
.setCacheLocalEntries(true)
.setInvalidateOnChange(false)
.setTimeToLiveSeconds(hazelcastProperties.getTimeToLiveSeconds())
.setEvictionConfig(new EvictionConfig()
.setMaxSizePolicy(ENTRY_COUNT)
.setEvictionPolicy(EvictionPolicy.LRU)
.setSize(hazelcastProperties.getMaxEntriesSize()));
config.getMapConfig(cacheName)
.setInMemoryFormat(BINARY)
.setNearCacheConfig(nearCacheConfig);
}
Saving and retrieving from the Cache is working fine, but my object is deserialized every time I have a cache hit. I want to avoid this deserialization time using a NearCache, but it doesn´t work. I also tried BINARY memory format.
Is this possible with Hazelcast? Or is this deserialization always executed even if I have a NearCache?
Thanks
So after a few changes, it is working now. Here is my conclusion:
So in order to have NearCache working with Spring Cache, all your cached objects should be Immutable. This means final classes and final fields. Also, they all should extend the Serializable interface.
I am using AWS ECS to host my application and using DynamoDB for all database operations. So I'll have same database with different table names for different environments. Such as "dev_users" (for Dev env), "test_users" (for Test env), etc.. (This is how our company uses same Dynamo account for different environments)
So I would like to change the "tableName" of the model class using the environment variable passed through "AWS ECS task definition" environment parameters.
For Example.
My Model Class is:
#DynamoDBTable(tableName = "dev_users")
public class User {
Now I need to replace the "dev" with "test" when I deploy my container in test environment. I know I can use
#Value("${DOCKER_ENV:dev}")
to access environment variables. But I'm not sure how to use variables outside the class. Is there any way that I can use the docker env variable to select my table prefix?
My Intent is to use like this:
I know this not possible like this. But is there any other way or work around for this?
Edit 1:
I am working on the Rahul's answer and facing some issues. Before writing the issues, I'll explain the process I followed.
Process:
I have created the beans in my config class (com.myapp.users.config).
As I don't have repositories, I have given my Model class package name as "basePackage" path. (Please check the image)
For 1) I have replaced the "table name over-rider bean injection" to avoid the error.
For 2) I printed the name that is passing on to this method. But it is Null. So checking all the possible ways to pass the value here.
Check the image for error:
I haven't changed anything in my user model class as beans will replace the name of the DynamoDBTable when the beans got executed. But the table name over riding is happening. Data is pulling from the table name given at the Model Class level only.
What I am missing here?
The table names can be altered via an altered DynamoDBMapperConfig bean.
For your case where you have to Prefix each table with a literal, you can add the bean as such. Here the prefix can be the environment name in your case.
#Bean
public TableNameOverride tableNameOverrider() {
String prefix = ... // Use #Value to inject values via Spring or use any logic to define the table prefix
return TableNameOverride.withTableNamePrefix(prefix);
}
For more details check out the complete details here:
https://github.com/derjust/spring-data-dynamodb/wiki/Alter-table-name-during-runtime
I am able to achieve table names prefixed with active profile name.
First added TableNameResolver class as below,
#Component
public class TableNameResolver extends DynamoDBMapperConfig.DefaultTableNameResolver {
private String envProfile;
public TableNameResolver() {}
public TableNameResolver(String envProfile) {
this.envProfile=envProfile;
}
#Override
public String getTableName(Class<?> clazz, DynamoDBMapperConfig config) {
String stageName = envProfile.concat("_");
String rawTableName = super.getTableName(clazz, config);
return stageName.concat(rawTableName);
}
}
Then i setup DynamoDBMapper bean as below,
#Bean
#Primary
public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB) {
DynamoDBMapper mapper = new DynamoDBMapper(amazonDynamoDB,new DynamoDBMapperConfig.Builder().withTableNameResolver(new TableNameResolver(envProfile)).build());
return mapper;
}
Added variable envProfile which is an active profile property value accessed from application.properties file.
#Value("${spring.profiles.active}")
private String envProfile;
We have the same issue with regards to the need to change table names during runtime. We are using Spring-data-dynamodb 5.0.2 and the following configuration seems to provide the solutions that we need.
First I annotated my bean accessor
#EnableDynamoDBRepositories(dynamoDBMapperConfigRef = "getDynamoDBMapperConfig", basePackages = "my.company.base.package")
I also setup an environment variable called ENV_PREFIX which is Spring wired via SpEL.
#Value("#{systemProperties['ENV_PREFIX']}")
private String envPrefix;
Then I setup a TableNameOverride bean:
#Bean
public DynamoDBMapperConfig.TableNameOverride getTableNameOverride() {
return DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix(envPrefix);
}
Finally, I setup the DynamoDBMapperConfig bean using TableNameOverride injection. In 5.0.2, we had to setup a standard DynamoDBTypeConverterFactory in the DynamoDBMapperConfig builder to avoid NPE.:
#Bean
public DynamoDBMapperConfig getDynamoDBMapperConfig(DynamoDBMapperConfig.TableNameOverride tableNameOverride) {
DynamoDBMapperConfig.Builder builder = new DynamoDBMapperConfig.Builder();
builder.setTableNameOverride(tableNameOverride);
builder.setTypeConverterFactory(DynamoDBTypeConverterFactory.standard());
return builder.build();
}
In hind sight, I could have setup a DynamoDBTypeConverterFactory bean that returns a standard DynamoDBTypeConverterFactory and inject that into the getDynamoDBMapperConfig() method using the DynamoDBMapperConfig builder. But this will also do the job.
I up voted the other answer but here is an idea:
Create a base class with all your user details:
#MappedSuperclass
public abstract class AbstractUser {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
Create 2 implentations with different table names and spirng profiles:
#Profile(value= {"dev","default"})
#Entity(name = "dev_user")
public class DevUser extends AbstractUser {
}
#Profile(value= {"prod"})
#Entity(name = "prod_user")
public class ProdUser extends AbstractUser {
}
Create a single JPA respository that uses the mapped super classs
public interface UserRepository extends CrudRepository<AbstractUser, Long> {
}
Then switch the implentation with the spring profile
#RunWith(SpringJUnit4ClassRunner.class)
#DataJpaTest
#Transactional
public class UserRepositoryTest {
#Autowired
protected DataSource dataSource;
#BeforeClass
public static void setUp() {
System.setProperty("spring.profiles.active", "prod");
}
#Test
public void test1() throws Exception {
DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
ResultSet tables = metaData.getTables(null, null, "PROD_USER", new String[] { "TABLE" });
tables.next();
assertEquals("PROD_USER", tables.getString("TABLE_NAME"));
}
}