Spring Data Solr multiple cores and repository - spring

I have apache solr with multiple cores e.g. currency, country etc... So using Spring Data Solr I can retrieve information from one core. I have got this XML configuration right now queries against 'currency' core. If I wanted to query against 'country' core how can I set this up?
<!-- Enable Solr repositories and configure repository base package -->
<solr:repositories base-package="com.acme.repository" solr-template-ref="solrCurrencyTemplate"/>
<solr:solr-server id="solrCurrencyServer" url="http://localhost:8983/solr/currency"/>
<bean id="solrCurrencyTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg ref="solrCurrencyServer" />
</bean>
and have the repository defined as
#Repository
public interface CurrencyRepository extends SolrCrudRepository<Currency, String> {
}
and from my service I can do this
#Override
public List<Currency> getCurrencies() {
Page<Currency> currencies = (Page<Currency>) currencyRepository.findAll();
return currencies.getContent();
}
I have also tried using #SolrDocument(solrCoreName = "currency") but this din't work.
#SolrDocument(solrCoreName = "currency")
public class Currency {
public static final String FIELD_CURRENCY_NAME = "currency_name";
public static final String FIELD_CURRENCY_CODE = "currency_code";
public static final String FIELD_DECIMALS = "decimals";
#Id
#Field(value = FIELD_CURRENCY_CODE)
private String currencyCode;
//currency_name,decimals
#Field(value = FIELD_CURRENCY_NAME)
private String currencyName;
#Field(value = FIELD_DECIMALS)
private String decimals;
...
...
...
}
I need help on this asap... otherwise I will have to go back to the RestTemplate Solution :-(
Hope someone can help.
Thanks
GM

Thought I would share, We spend lot of time recently configuring multiple cores. We did in java, not xml.
As part of spring #configuration add following.
#Bean(name="solrCore1Template")
public SolrTemplate solrCore1Template() throws Exception {
EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(getCoreContainer(), "core1");
return new SolrTemplate(embeddedSolrServer);
}
#Bean(name="solrCore2Template")
public SolrTemplate solrCore2Template() throws Exception {
EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(getCoreContainer(), "core2");
return new SolrTemplate(embeddedSolrServer);
}
#Bean
#Scope
public CoreContainer getCoreContainer() throws FileNotFoundException{
String dir = <path_to_solr_home>;
System.setProperty("solr.solr.home", dir);
CoreContainer.Initializer initializer = new CoreContainer.Initializer();
return initializer.initialize();
}
And to use each template use like below in service classes.
#Resource
private SolrTemplate solrCore1Template;
Embedded server can be relaced with HTTP using below code.
HttpSolrServer httpSolrServer = new HttpSolrServer(getSolrURL());
return new SolrTemplate(httpSolrServer, "core1");
Hope this helps. I know it's a very late reply for the question asked.

multicore support via namespace config is unfortunately an open issue. You'll need to have a separate SolrTemplate for each core and create repositories manually.
#Autowired
#Qualifier("solrCurrencyTemplate")
private SolrTemplate solrCurrencyTemplate;
#Autowired
#Qualifier("solrCountryTemplate")
private SolrTemplate solrCountryTemplate;
//...
CurrencyRepository currencyRepo = new SolrRepositoryFactory(this.solrCurrencyTemplate)
.getRepository(CurrencyRepository.class);
CountryRepository countryRepo = new SolrRepositoryFactory(this.solrCountryTemplate)
.getRepository(CountryRepository.class);

Spring Data now supports multiple cores with their respective repositories.
The multicoreSupport flag needs to be true in #EnableSolrRepositories annotation and the corresponding document needs to be told what core they belong to. Like:
#SolrDocument(solrCoreName = "currency")
public class Currency
{
// attributes
}
the other class should be
#SolrDocument(solrCoreName = "country")
public class Country
{
// attributes
}
The respective repositories should know what pojo they are working with.
public interface CurrencyRepository extends SolrCrudRepository<Currency,String>
{
}
and
public interface CountryRepository extends SolrCrudRepository<Country,String>
{
}
and configuration should be
#Configuration
#EnableSolrRepositories(value = "com.package.name",multicoreSupport = true)
public class SolrConfig
{
#Bean
public SolrServer solrServer() throws Exception
{
HttpSolrServerFactoryBean f = new HttpSolrServerFactoryBean();
f.setUrl("http://localhost:8983/solr");
f.afterPropertiesSet();
return f.getSolrServer();
}
#Bean
public SolrTemplate solrTemplate(SolrServer solrServer) throws Exception
{
return new SolrTemplate(solrServer());
}
}

With Spring Data Solr 1.1.0.RC1 multiple cores works as described by Christoph Strobl with #EnableSolrRepositories. It works also with an XML configuration by set multicore-support="true".
<solr:repositories base-package="your.solr.repo.package" repository-impl-postfix="Impl" multicore-support="true"/>
<solr:solr-server id="solrServer" url="${solr.server.base.connection.url}" />
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg index="0" ref="solrServer" />
</bean>

<solr:solr-server id="solrServer" timeout="1000" maxConnections="1000" url="${solr.server.1},${solr.server.2}"/>
<bean id="solrServerFactory" class="org.springframework.data.solr.server.support.MulticoreSolrServerFactory">
<constructor-arg ref="solrServer" />
<constructor-arg name="cores">
<list>
<value>${solr.index.customer}</value>
<value>${solr.index.task}</value>
</list>
</constructor-arg>
</bean>
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg ref="solrServerFactory" />
</bean>
<solr:repositories base-package="com.deve.pig.solr" multicore-support="true" solr-template-ref="solrTemplate" />

Related

Using one DataSource object for multiple DB connections - using only one connection at a time

Can we define only one DataSource object and wire it dynamically at runtime connecting to different databases ? I need to connect to only one database at a time.
I will be passing the name of the Database as argument. I will lookup the DB URL and other details from a property file and then I need to connect to the DB using the DB URL.
In Short - I do not know the number of databases I need to connect to. I will have all possible database connection details configured in the database.properties file following a certain syntax (like prefixed with DB01 etc.). The name of the DB will be passed as argument and I need to execute the query against that database.
database.properties file
DB01.driver=com.ibm.db2.jcc.DB2Driver
DB01.url=jdbc:db2://localhost:50000/SAMPLE
DB01.username=db2admin
DB01.password=db2admin
DAO class
#Autowired
#Qualifier("DB01") // how do I make this dynamic ?
private DataSource datasource;
private JdbcTemplate jdbcTemplate;
// some more code
public SqlRowSet executeQuery(String sqlQuery)
{
// can I pass the DB name here (the database.properties file will have the DB details
// with this name as given above) and set the DataSource Object accordingly ?
// so that the query will be executed against that DB ?
setJdbcTemplate(new JdbcTemplate(this.datasource));
return getJdbcTemplate().queryForRowSet(sqlQuery);
}
Using Spring v4.1.4 RELEASE. Thanks !!
You can define a Routing DataSource that redirects the getConnection method to one datasource or another based on a key, in your case, it seems to be the database name.
For instance, the spring xml:
....
<bean id="DB01DataSource" parent="parentDatasource" p:url="${DB01.url}" ... />
<bean id="DB02DataSource" parent="parentDatasource" p:url="${DB02.url}" .../>
<bean id="dataSource" class="DBRoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="DB01" value-ref="DB01DataSource"/>
<entry key="DB02" value-ref="DB02DataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="DB01DataSource"/>
</bean>
....
The DBRoutingDataSource class:
public class DBRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getDB();
}
}
And the DBContextHolder class:
public final class DBContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<String>();
private DBContextHolder() {
// empty
}
public static void setDB(final String db) {
CONTEXT.set(db);
}
public static String getDB() {
return CONTEXT.get();
}
public static void clearDB() {
CONTEXT.remove();
}
}
In your service class before calling your DAO you set the key that will enable the Routing DataSource to get the right connection:
DBContextHolder.setDB("DB01");
try{
dao.executeQuery(sqlSentence);
}finally{
DBContextHolder.clearDB();
}

How to choose spring configuration in runtime based on a tenant?

I would like to be able to choose specific Spring (or Grails) context configuration based on the tenant that user belongs to in runtime. Let's say I use Spring Security and I retrieve tenantId during login.
Imagine now I have a two tenants and they pay different commission. How to inject specific service into a controller without too much plumbing? Here are two different contexts. So, I should inject different ExchangeService based on tenant.
#Configuration
public class FooTenant{
#Bean
public ExchangeService bar() {
return new ZeroCommisionExchangeService ();
}
}
#Configuration
public class BarTenant{
#Bean
public ExchangeService bar() {
return new StandardCommisionExchangeService ();
}
}
Edit:
I am aware I can obtain reference to Spring context and ask for service "manually", but I am looking for a more generic solution where this problematic is solved by IoC framework.
A couple of years ago we needed somthing like this but only for DataSources and ViewResolvers. We developed a solution using spring' TargetSource solution. (Initially we used a HotswappableTargetSource but that wasn't adequate for our use-case.
The code we developed is availabe here in the multi-tenant directory.
It is fully configurable and flexible.
Basically what you do is you configura a ContextSwappableTargetSource and tell it what type of interface/class it needs to return.
<bean id="yourTentantBasedServiceId" class="biz.deinum.multitenant.aop.target.ContextSwappableTargetSource">
<constructor-arg value="ExchangeService" />
</bean>
The default is to lookup beans in the ApplicationContext based on the tenantId (see the BeanFactoryTargetRegistry for this). However you can specify one or more of those (we used a JndiLookupTargetRegistry to dynamically lookup datasource, which allowed use to add tenants on the fly without restarting the application).
If you explicitly configure a BeanFactoryTargetRegistry you can add a prefix and suffix.
<bean id="exchangeService" class="biz.deinum.multitenant.aop.target.ContextSwappableTargetSource">
<constructor-arg value="ExchangeService" />
<property name="targetRegistry>
<bean class="biz.deinum.multitenant.aop.target.registry.impl.BeanFactoryTargetRegistry">
<property suffix="ExchangeService"/>
</bean>
</property>
</bean>
Now for foo it would lookup a bean named fooExchangeService and for bar barExchangeService.
The tenantId is stored in a ThreadLocal which is wrapped inside the ContextHolder. You need to find a way to fill and clear this thread local (in general a servlet Filter does that trick.
In your code you can now simply use the interface ExchangeService and at runtime based on the tenantId the correct implemenation will be looked up.
Also see http://mdeinum.wordpress.com/2007/01/05/one-application-per-client-database/
Assuming you have different services already defined, you can get their bean from the context and use it. In my example, all the services have implementation of serviceMethod and based on some criteria pick your proper service. The only thing I am not sure is how Multitenancy might impact this.
import org.springframework.context.ApplicationContext
class ServiceManagerController {
def serviceManager
def index() {
ApplicationContext ctx = grails.util.Holders.grailsApplication.mainContext
serviceManager = ctx.getBean(params.serviceName); //firstService or secondService
render serviceManager.serviceMethod()
}
}
FirstService
class FirstService {
def serviceMethod() {
return "first"
}
}
SecondService:
class SecondService {
def serviceMethod() {
return "second"
}
}
While it is possible to swap beans instantiated in a spring context at runtime (HotswappableTargetSource), it is not meant for use cases such as yours.
Remember there is one Spring Context for your application, all threads use the same instances (in most cases), this implies when you swap out a bean implementation, you are affectively doing this for all your application's users. To prevent this, you run into issues of ensuring Thread Safety, employing Thread Locals, as listed in another answer.
While it is possible to continue this approach and arrive at an implementation that gets the job done, it would definitely be a very contrived way of solving this problem.
You should take a step back and look at your problem in a more wholesome, system wide design point of view. Bust out your patterns books and look at how this can be resolved, regardless of whether you use Spring or an other framework. Service Locator, Factory bean etc described in some of the answers above is a step in the correct direction.
Your Use Case is pretty common for multi-tenant applications. You need to narrow down things that are likely to change based on a tenantId versus things that are constant across.
For instance as mentioned in the question, each Tenant might have a different commission amount or even different algorithm for commission calculation. A simple solution to this would be to implement a CommissionCalculationService which accepts a tenantId, and any other domain object based on which commission is to be calculated, I would imagine this would be something like Order or Sale, whatever makes sense in your application.
You now need a CommissionServiceFactory or a ServiceLocator which will contain tenant specific implementations of the CommissionCalculationService. The Service Locator is instantiated when the Spring context loads, and is injected with implementation classes also at application startup.
When you want to calculate commission for a tenant, you basically obtain the tenantId from the user's login, pass the tenant id to your service locator, based on the tenantId passed, the service locator returns the appropriate instance of a Service Implementation. In your calling class, use this instance to calculate the commission for the tenant.
Another pattern to consider is the Strategy Pattern, or even Template Pattern.
Bottom line, even if you want tenant specific logic implemented cleanly, don't thing about changing the beans loaded in the context. Have classes in your context that can handle all your tenant specific logic. Rely on design patterns to use the correct bean from the context based on the tenant id.
I apologize if the answer was a little verbose, I felt it was needed to explain why I think updating beans in a loaded Spring Context is not the appropriate solution.
I use the following code:
public class ConfigurableProxyFactoryBean implements FactoryBean<Object>, BeanNameAware {
#Autowired
private ApplicationContextProvider applicationContextProvider;
private Class<?> proxyType;
private String beanName;
private Object object;
private Object fallbackObject;
private Object monitor = new Object();
private ConfigurableProxy proxy;
public ConfigurableProxyFactoryBean(Class<?> proxyType) {
this.proxyType = proxyType;
}
public Object getFallbackObject() {
return fallbackObject;
}
public void setFallbackObject(Object fallbackObject) {
synchronized (monitor) {
this.fallbackObject = fallbackObject;
if (proxy != null) {
proxy.setFallbackObject(fallbackObject);
}
}
}
#Override
public void setBeanName(String name) {
beanName = name;
}
#Override
public Object getObject() throws Exception {
synchronized (monitor) {
if (object == null) {
#SuppressWarnings("unchecked")
Class<Object> type = (Class<Object>)proxyType;
proxy = new ConfigurableProxy(applicationContextProvider, beanName);
proxy.setFallbackObject(fallbackObject);
object = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] { type }, proxy);
}
return object;
}
}
#Override
public Class<?> getObjectType() {
return proxyType;
}
#Override
public boolean isSingleton() {
return true;
}
}
class ConfigurableProxy implements InvocationHandler {
public ConfigurableProxy(ApplicationContextProvider appContextProvider, String beanName) {
this.appContextProvider = appContextProvider;
this.beanName = beanName;
}
private ApplicationContextProvider appContextProvider;
private String beanName;
private Object fallbackObject;
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ApplicationContext appContext = appContextProvider.getApplicationContext();
String name = "$&&#" + beanName;
Object bean = appContext.containsBean(name) ? appContext.getBean(name) : fallbackObject;
return method.invoke(bean, args);
}
public void setFallbackObject(Object fallbackObject) {
this.fallbackObject = fallbackObject;
}
}
ApplicationContextProvider has implementation, that chooses ApplicationContext according to current tennant.
In XML configuration it is used like this:
<bean class="my.package.infrastructure.ConfigurableProxyFactoryBean" name="beanName">
<constructor-arg>
<value type="java.lang.Class">my.package.model.ServiceInterface</value>
</constructor-arg>
<property name="fallbackObject">
<bean class="my.package.service.DefaultServiceImplementation"/>
</property>
</bean>
And in tennant configuration that way:
<bean class="my.package.service.ServiceImplementationA" name="$&&#beanName"/>
To inject this service somewhere you just write:
public class MyController {
#Autowired
private ServiceInterface service;
}
Also you are to implement ApplicationContextProvider, I won't share mine. It is not very hard to implement. For example, your implementation can just store context in ThreadLocal. And you create your own ServletContextListener, which for every query gets the current tennant and stores it into your ApplicationContextProvider implementation.
The new tenant scope and a servicelocator can helps
Tenant scope will guarantee than service is created one time for a tenant
Sample code:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="tenant" value="foo.TenantScope"/>
</map>
</property>
</bean>
<bean id="service" class="foo.Service" factory-bean="tenantServiceLocator" factory-method="createInstance" scope="tenant"/>
<bean id="fooService" class="FooService">
<bean id="barService" class="BarService">
<bean id="tenantServiceLocator" class="foo.TenantServiceLocator">
<property name="services">
<map>
<entry key="foo" value-ref="fooService"/>
<entry key="bar" value-ref="barService"/>
</map>
</property>
</bean>
TenantServiceLocator should know the user tenantId
public class TenantServiceLocator {
private Map<String, Service> services;
public String getTenantId() {
return "foo"; // get it from user in session
}
public Map<String, Service> getServices() {
return services;
}
public void setServices(Map<String, Service> services) {
this.services = services;
}
public Service createInstance(){
return services.get(tenantId);
}
}
public class FooController{
#Autowired
private Service service;
}
A sample TenantScope implementation
public class TenantScope implements Scope {
private static Map<String, Map<String, Object>> scopeMap = new HashMap<String, Map<String, Object>>();
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = getTenantScope(getTenantId());
Object object = scope.get(name);
if(object == null){
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
private Map<String, Object> getTenantScope(String tenantId) {
if (!scopeMap.containsKey(tenantId)) {
scopeMap.put(tenantId, new HashMap<String, Object>());
}
return scopeMap.get(tenantId);
}
private String getTenantId() {
return "foo"; // load you tenantId
}
#Override
public Object remove(String name) {
Map<String, Object> scope = getTenantScope(getTenantId());
return scope.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
#Override
public String getConversationId() {
return null;
}
}
Transforming my comment in an answer, one possible solution is to create a spring factory bean, that receive all he needs to decide which service needs to be returned when creating the instance.
Translating to Grails:
public interface ChoosableServiceIntf {
String getName();
}
class NormalService implements ChoosableServiceIntf {
public String getName() {
return getClass().name;
}
}
class ExtendedService implements ChoosableServiceIntf {
public String getName() {
return getClass().name
}
}
class ChoosableServiceFactory {
static ChoosableServiceIntf getInstance(String decisionParam) {
if(decisionParam == 'X') {
return applicationContext.getBean('extendedService')
}
return applicationContext.getBean('normalService')
}
static ApplicationContext getApplicationContext() {
return Holders.grailsApplication.mainContext
}
}
Here we have two services and ChoosableServiceFactory is responsible to know witch is the correct one.
Then you will need to use the method ApplicationContext#getBean(String, Object[]) to return the correct instance and will also make the factory prototyped scope because of the runtime params.
A controller to test it:
class MyController {
def grailsApplication
def index() {
ChoosableServiceIntf service = grailsApplication.mainContext.getBean('choosableServiceFactory', ["X"] as Object[])
ChoosableServiceIntf serviceNormal = grailsApplication.mainContext.getBean('choosableServiceFactory', ["N"] as Object[])
render text: "#1 - ${service.class.name} , #2 - ${serviceNormal.class.name}"
}
}
This will print #1 - dummy.ExtendedService , #2 - dummy.NormalService
The declaration of the beans will be:
choosableServiceFactory(ChoosableServiceFactory) { bean ->
bean.scope = 'prototype'
bean.factoryMethod = 'getInstance'
}
normalService(NormalService)
extendedService(ExtendedService)

DataNucleus: Class ... for query has not been resolved. Check the query and any imports specification

I have a problem, hopefully someone can give me some hints.
Environment:
maven project with two modules
one module is the 'model', and has DataNucleus 3.1, HSQLDB and Spring 3 dependencies. HSQLDB runs embedded, in memory, configured from spring applicationContext.xml
the other module is the 'web' and has GWT dependencies
The application is built using some Spring Roo generated code as basis, later modified and extended.
The issue is that, when starting the app and trying to load the data, I receive the exception:
Class Document for query has not been resolved. Check the query and any imports specification; nested exception is javax.persistence.PersistenceException: Class Document for query has not been resolved. Check the query and any imports specification
The weirdest thing is that the sample roo-generated aplication used as basis, with exactly the same dependencies, but a different modularization works like a charm, without this symptom, so I am puzzled now...
Please also note that I tried to replace the 'Document' with the explicit qualification 'com.myvdm.server.domain.Document' in the query, with no positive result:
return entityManager().createQuery("SELECT COUNT(o) FROM Document o", Long.class).getSingleResult();
Another thing, although it might not be relevant, on every request, this exception is thrown:
DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Unexpected exception on closing JPA EntityManager [INFO] java.lang.IllegalStateException: EntityManager is managed by a container (JEE) and so cannot be closed by calling the EM.close() method. Please read JPA2 spec 3.1.1 for the close() method.
The last exception is thrown by DataNucleus. It's also confusing, since I do not run in a Java EE container, but GWT development mode.
Here's the document entity:
#RooJavaBean
#RooToString
#RooJpaActiveRecord
public class Document {
#NotNull
private String name;
#ManyToOne
private DocumentType type;
#OneToMany(fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
private Set<Field> fields;
}
The annotation #RooJpaActiveRecord adds EntityManager operations but these are declared in a separate file - ITD(inter-type declarations)
Any suggestions, please?
Thanks a lot in advance.
----------- EDIT --------------
privileged aspect Document_Roo_Jpa_ActiveRecord {
#PersistenceContext
transient EntityManager Document.entityManager;
public static final EntityManager Document.entityManager() {
EntityManager em = new Document().entityManager;
if (em == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
return em;
}
public static long Document.countDocuments() {
return entityManager().createQuery("SELECT COUNT(o) FROM Document o", Long.class).getSingleResult();
}
public static List<Document> Document.findAllDocuments() {
return entityManager().createQuery("SELECT o FROM Document o", Document.class).getResultList();
}
public static Document Document.findDocument(Long id) {
if (id == null) return null;
return entityManager().find(Document.class, id);
}
public static List<Document> Document.findDocumentEntries(int firstResult, int maxResults) {
return entityManager().createQuery("SELECT o FROM Document o", Document.class).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
}
#Transactional
public void Document.persist() {
if (this.entityManager == null) this.entityManager = entityManager();
this.entityManager.persist(this);
}
#Transactional
public void Document.remove() {
if (this.entityManager == null) this.entityManager = entityManager();
if (this.entityManager.contains(this)) {
this.entityManager.remove(this);
} else {
Document attached = Document.findDocument(this.id);
this.entityManager.remove(attached);
}
}
#Transactional
public void Document.flush() {
if (this.entityManager == null) this.entityManager = entityManager();
this.entityManager.flush();
}
#Transactional
public void Document.clear() {
if (this.entityManager == null) this.entityManager = entityManager();
this.entityManager.clear();
}
#Transactional
public Document Document.merge() {
if (this.entityManager == null) this.entityManager = entityManager();
Document merged = this.entityManager.merge(this);
this.entityManager.flush();
return merged;
}
}
#Entity declaration
privileged aspect Document_Roo_Jpa_Entity {
declare #type: Document: #Entity;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long Document.id;
#Version
#Column(name = "version")
private Integer Document.version;
public Long Document.getId() {
return this.id;
}
public void Document.setId(Long id) {
this.id = id;
}
public Integer Document.getVersion() {
return this.version;
}
public void Document.setVersion(Integer version) {
this.version = version;
}
}
Ok, I found a fix for this problem.
As I posted earlier, using Spring's applicationContext.xml and persistence.xml files as basic configuration I could not make it work. I deleted persistence.xml and instead I used this configuration (please note the usage of packagesToScan property and passsing DataNucleus properties - and basically all the info that was traditionally inside persistence.xml):
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="packagesToScan" value="com.myvdm.server.domain"/>
<property name="persistenceProviderClass" value="org.datanucleus.api.jpa.PersistenceProviderImpl"/>
<property name="jpaPropertyMap">
<map>
<entry key="datanucleus.ConnectionDriverName" value="org.hsqldb.jdbc.JDBCDriver"/>
<entry key="datanucleus.storeManagerType" value="rdbms"/>
<entry key="datanucleus.ConnectionURL" value="jdbc:hsqldb:mem:myvdm"/>
<entry key="datanucleus.ConnectionUserName" value="sa"/>
<entry key="datanucleus.ConnectionPassword" value=""/>
<entry key="datanucleus.autoCreateSchema" value="true"/>
<entry key="datanucleus.autoCreateTables" value="true"/>
<entry key="datanucleus.autoCreateColumns" value="false"/>
<entry key="datanucleus.autoCreateConstraints" value="false"/>
<entry key="datanucleus.validateTables" value="false"/>
<entry key="datanucleus.validateConstraints" value="false"/>
<entry key="datanucleus.jpa.addClassTransformer" value="false"/>
</map>
</property>
<property name="dataSource" ref="dataSource"/>
</bean>
So this is the only way I could make it work, could this be a Spring bug?
And about that second (minor) issue, obviously the exception will be thrown since I am using Spring's LocalContainerEntityManagerFactoryBean :)
It is not a Spring bug, it is simply a "Debug" level message. To suppress that message, change the log level <logger name="org.springframework.orm.jpa.EntityManagerFactoryUtils" additivity="false" level="error"/>.
See my ORM demo here: https://github.com/gordonad/core-spring-demos/tree/master/demos/orms

Programmatic access to properties created by property-placeholder

I'm reading properties file using context:property-placeholder. How can I access them programatically (#Value doesn't work - I don't know property titles at the moment of developing)?
The main problem is I can't change applicationContext.xml file because it's setted up by "parent" framework
ps. It's strange but Environment.getProperty returns null
No you can't. PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor, it is only "alive" during bean creation. When it encounters a ${property} notation, it tries to resolve that against its internal properties, but it does not make these properties available to the container.
That said: similar questions have appeared again and again, the proposed solution is usually to subclass PropertyPlaceHolderConfigurer and make the Properties available to the context manually. Or use a PropertiesFactoryBean
We use the following approach to access properties for our applications
<util:properties id="appProperties" location="classpath:app-config.properties" />
<context:property-placeholder properties-ref="appProperties"/>
Then you have the luxury of just autowiring properties into beans using a qualifier.
#Component
public class PropertyAccessBean {
private Properties properties;
#Autowired
#Qualifier("appProperties")
public void setProperties(Properties properties) {
this.properties = properties;
}
public void doSomething() {
String property = properties.getProperty("code.version");
}
}
If you have more complex properties you can still use ignore-resource-not-found and ignore-unresolvable. We use this approach to externalise some of our application settings.
<util:properties id="appProperties" ignore-resource-not-found="true"
location="classpath:build.properties,classpath:application.properties,
file:/data/override.properties"/>
<context:property-placeholder ignore-unresolvable="true" properties-ref="appProperties"/>
#Value
annotation works on new releases of Spring (tested on v3.2.2)
Here is how it is done:
Map your properties file in spring configuration file
<!--Import Info:
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd-->
<context:property-placeholder location="classpath:/app-config.properties" />
Create app-config.properties inside (root) your source folder
my.property=test
my.property2=test2
Create a controller class
#Controller
public class XRDSBuilder
{
#Value("${my.property}")
private String myProperty;
public String getMyProperty() { return myProperty; }
}
Spring will automatically map the content of my.property to your variable inside the controller
Mapping to a list
Property value:
my.list.property=test,test2,test3
Controller class configuration:
#Value("#{'${my.list.property}'.split(',')}")
private List<String> myListProperty;
Advanced mapping
#Component("PropertySplitter")
public class PropertySplitter {
/**
* Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2
*/
public Map<String, String> map(String property) {
return this.map(property, ",");
}
/**
* Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2
*/
public Map<String, List<String>> mapOfList(String property) {
Map<String, String> map = this.map(property, ";");
Map<String, List<String>> mapOfList = new HashMap<>();
for (Entry<String, String> entry : map.entrySet()) {
mapOfList.put(entry.getKey(), this.list(entry.getValue()));
}
return mapOfList;
}
/**
* Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4
*/
public List<String> list(String property) {
return this.list(property, ",");
}
/**
* Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2
*/
public List<List<String>> groupedList(String property) {
List<String> unGroupedList = this.list(property, ";");
List<List<String>> groupedList = new ArrayList<>();
for (String group : unGroupedList) {
groupedList.add(this.list(group));
}
return groupedList;
}
private List<String> list(String property, String splitter) {
return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property);
}
private Map<String, String> map(String property, String splitter) {
return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property);
}
}
Property value:
my.complex.property=test1:value1,test2:value2
Controller class:
#Value("#{PropertySplitter.map('${my.complex.property}')}")
Map<String, String> myComplexProperty;
Spring follows Inversion Of Control approach, this means that we can simply inject particular property into POJO. But there are some cases, when you would like to access property given by name directly from your code - some might see it as anti-pattern - this is palpably true, but lets concentrate on how to do it.
The PropertiesAccessor below provides access to properties loaded by Property Placeholder and encapsulates container specific stuff. It also caches found properties because call on AbstractBeanFactory#resolveEmbeddedValue(String) is not cheap.
#Named
public class PropertiesAccessor {
private final AbstractBeanFactory beanFactory;
private final Map<String,String> cache = new ConcurrentHashMap<>();
#Inject
protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public String getProperty(String key) {
if(cache.containsKey(key)){
return cache.get(key);
}
String foundProp = null;
try {
foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
cache.put(key,foundProp);
} catch (IllegalArgumentException ex) {
// ok - property was not found
}
return foundProp;
}
}
Found answer at below site:
http://forum.spring.io/forum/spring-projects/container/106180-programmatic-access-to-properties-defined-for-the-propertyplaceholderconfigurer
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" id="propertyConfigurer">
<property name="properties" ref="props" />
</bean>
<bean id="props" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="file:C:/CONFIG/settings.properties"/>
</bean>
<util:properties id="prop" location="location of prop file" />
This return java.util.Properties object
In JAVA Code
Properties prop = (Properties) context.getBean("prop");
Now you can access ,
prop.getProperty("key");
This works if you need to scan multiple locations for your properties ...
<bean id="yourProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<array value-type="org.springframework.core.io.Resource">
<value>classpath:yourProperties.properties</value>
<value>file:../conf/yourProperties.properties</value>
<value>file:conf/yourProperties.properties</value>
<value>file:yourProperties.properties</value>
</array>
</property>
<property name="ignoreResourceNotFound" value="true" />
</bean>
<context:property-placeholder properties-ref="yourProperties" ignore-unresolvable="true"/>
And then in your actual classes ...
#Autowired
Properties yourProperties;
Tested using Spring 5.1.4
Create beans for your properties before putting them in property-placeholder to make the properties easy to access in-code.
Ex:
<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="resources" value="classpath:META-INF/spring/config.properties" />
</bean>
<context:property-placeholder properties-ref="configProperties" ignore-unresolvable="true"/>
Code:
#Autowired
private PropertiesFactoryBean configProperties;
You can also use #Resource(name="configProperties")
Let's asume that you the properties file defined in that "parent" framework
<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:main.properties" />
</bean>
You can use the #Value annotation in this way:
#Value( value = "#{applicationProperties['my.app.property']}" )
private String myProperty;

Dozer custom converter ID mapping: Object to Long and Long to Object via DozerConverter getParameter

I need help configuring my dozer mapping file.
Mainly I would like to know how to get User user obejct to convert to Long userId.
Hence map: user >> userId
But I have multiple objects such as comment >> commentId or address >> addressId
therefor I'd like to have something more elegant than just writing mapping for each of the fields. All of the object implement Loadable interface.
The bellow code is now functioning thanks to the getParameter() DozerConverter method, but if you know any better way than the converter that I wrote please let me know.
// dozer.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">
<configuration>
<custom-converters>
<converter type="project.shared.domain.dto.dozer.LoadableIdConverter" >
<class-a>project.shared.domain.Loadable</class-a>
<class-b>java.lang.Long</class-b>
</converter>
</custom-converters>
</configuration>
<mapping>
<class-a>project.shared.domain.Suggestion</class-a>
<class-b>project.shared.domain.dto.DTOSuggestion</class-b>
<field custom-converter-param="User">
<a>user</a>
<b>userId</b>
</field>
</mapping>
</mappings>\
// Spring Application context
<bean id="loadableIdConverter" class="project.shared.domain.dto.dozer.LoadableIdConverter">
<property name="userService" ref="userService"/>
<property name="commentService" ref="commentService"/>
<property name="addressService" ref="addressService"/>
</bean>
<bean id="gwtMapper" class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>classpath:/dozer.xml</value>
</list>
</property>
<property name="customConverters">
<list>
<ref bean="loadableIdConverter"/>
</list>
</property>
</bean>
//Standard hibernate object
public class Suggestion implements Serializable, Loadable {
private long id = -1;
private Date dateCreated;
private User user; //trying to use dozer to covert this bad boy to Long userId
//...
}
//DTO object
public class DTOSuggestion implements IsSerializable {
private long id = -1;
private Date dateCreated;
private Long userId; //trying to get this ID via the dozer converter
//...
}
//Loadable interface
public interface Loadable extends Serializable {
public long getId();
public void setId(long id);
}
//Dozer converter
public class LoadableIdConverter extends DozerConverter<Loadable, Long> {
private UserService userService; //configured in applicationcontext
private AddressService addressService; //configured in applicationcontext
private CommentService commentService; //configured in applicationcontext
public LoadableIdConverter() {
super(Loadable.class, Long.class);
}
public Long convertTo(Loadable object, Long id) {
return object.getId();
}
public Loadable convertFrom(Long id, Loadable object) {
if (id < 0) return null;
String loadable = getParameter();
if (loadable.equalsIgnoreCase("User"))
return userService.get(User.class, id);
if (loadable.equalsIgnoreCase("Address"))
return addressService.get(Address.class, id);
if (loadable.equalsIgnoreCase("Comment"))
return commentService.get(Comment.class, id);
return null;
}
}
There is one trick you could use to avoid converter parameters. If you fall back to older custom converter approach in Dozer, which is implementing CustomConverter interface, you will get two additional parameters: existingDestinationValue and destinationClass.
convert(Object existingDestinationFieldValue, Object sourceFieldValue, Class<?> destinationClass, Class<?> sourceClass)
By using these values you could introspect your destination field via reflection and know what is the expected concrete implementation of Loadable interface. This works only if you define the field types with concrete types of course. But you already have it in your example, so this should not be a problem. CustomConverter implementation will be more verbose as you need to determine the direction of the mapping manually, but it gives you full control of what is going on during the mapping process.

Resources