Spring #Transactional wrapping 2 methods - spring

I'm a Spring newby. I use the #Transactional annotation for my dao methods:
#Transactional
public Person getById(long id) {
return new Person(jdbcTemplate.queryForMap(...));
}
#Transactional
public void save(Person person) {
jdbcTemplate.update(...);
}
and I've set up the transaction manager like this:
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
The problem is that when my client code calls dao.save(..) and then dao.getById(4) these happen in two separate transactions. How is it possible to wrap those 2 calls in the same database transaction? Ideally without doing it in a programmatic way.
thanks

It is bad practice to put transactional attributes in DAO layer. Also, I am not sure why do you require transaction for getById method. Even if you want to use transaction then you need to specify propagation behaviour as REQUIRES_NEW for save and getById method.

#Transactional(propagation = REQUIRES_NEW, readOnly = false)
public Person saveAndGetById(Person person, long id) {
save(person);
return getById(id);
}
#Transactional(propagation = REQUIRED)
public Person getById(long id) {
return new Person(jdbcTemplate.queryForMap(...));
}
#Transactional(propagation = REQUIRED, readOnly = false)
public void save(Person person) {
jdbcTemplate.update(...);
}
However, the best thing would be to have the "save" method return an ID, because it is hard to know beforehand which ID the Person will have once persisted.

Good practice in this case would be marking service method which invokes both these DAO methods as #Transactional. The case was clearly discussed here.

Related

Spring threadpooltaskexecutor: transaction managment

I am trying to make an asynchronous method call in my service layer code. Some pseudo code for the same is as below:
public void createXXX ()
{
dao.saveOrUpdate(entity); // save an entity
...................
...................
callAServiceXXX ()
}
...........
...........
public void callAServiceXXX()
{
SomeEntity entity = dao.getEntity(); // entity NOT NULL
this.threadPoolTaskExecutor.execute(new Runnable() {
public void run() {
try {
callAMethodXXX()
}catch()
{}
}
public void callAMethodXXX()
{
SomeEntity entity = dao.getEntity(); // entity always NULL
}
My spring config file has the following defined for the service layer bean containing the above logic:
<property name="transactionAttributes">
<props>
<prop key="callAServiceXXX">PROPAGATION_REQUIRED</prop>
<prop key="callAMethodXXX">PROPAGATION_MANDATORY</prop>
</props>
</property>
As put in the above, when I am trying to fetch the entity object which I save in the method createXXX(), it is always NULL when the dao call is executed from the callAMethodXXX() method.
I am not sure about the reason for this behavior. Did try a few other transactional attributes in the spring config file but did not get any success.
A workaround which I tried to make this work was:
1) Create a helper class. Inject it in this service layer class.
2) Shift the method callAMethodXXX() to this helper class.
3) Define <prop key="callAMethodXXX">PROPAGATION_REQUIRES_NEW</prop> as i want to make sure that callAMethodXXX() should be executed in a new transaction.
However, I do not want to use an extra helper class and want to make sure that the logic works fine from with the single service layer class.
Any inputs on the above will be helpful.
Regards,
Your Runnable thread is not aware of transaction management.
Maybe you can try to add to your managed bean a reference to itself, and add TRANSACTION_REQUIRES_NEW for method callAMethod:
#Component
public class MyService {
#Autowired
public MyService myService;
public void callAServiceXXX() {
SomeEntity entity = dao.getEntity();
this.threadPoolTaskExecutor.execute(new Runnable() {
public void run() {
try {
myService.callAMethodXXX();
}catch(Exception e){
}
}
});
}
}
<property name="transactionAttributes">
<props>
<prop key="callAServiceXXX">PROPAGATION_REQUIRED</prop>
<prop key="callAMethodXXX">PROPAGATION_REQUIRES_NEW</prop>
</props>
</property>
Disclaimer: you might still have a problem with that approach, as you are calling an async method and you are not 100% sure that saveOrUpdate method in separate transaction is finished or not.

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)

HibernateTemplate save performs inserts but not updates

I have a typical Spring / Hibernate setup. Here's my spring config:
<context:annotation-config />
<context:component-scan base-package="com.myco.myapp.modules" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sessionFactory"
...
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
I have a BaseRepository:
#Transactional(propagation = Propagation.MANDATORY)
public final T save(final T entity) throws RepositoryException {
try {
getHibernateTemplate().save(entity);
return entity;
} catch (DataAccessException e) {
throw new EntityCouldNotBeSavedException(getPersistentClass(),
e);
}
}
And a Repository class that extends it:
#Repository
public class PersonRepositoryImpl extends BaseRepositoryImpl<Person, String>
And a Service:
#Service
public class PersonServiceImpl {
#Autowired
private PersonRepository _personRespository;
I call the following method, saveSomeStuff(), an when I insert using BaseRepository.save() it works perfectly. But when I try to update, it doesn't make the change:
#Override
#Transactional
public void saveSomeStuff() {
try {
Person existingPerson = _personRespository.findById("1");
existingPerson.setName("John");
_personRespository.save(existingPerson);
Person dbExistingPerson = _personRespository.findById("1");
// This prints "John".
System.out.println(dbExistingPerson.getName());
Person newPerson = new Person();
newPerson.setName("Jack");
_personRespository.save(newPerson);
} catch (RepositoryException e) {
e1.printStackTrace();
}
}
I thought I might have a transaccionality problem, but as I said, upon leaving the Service method the new Person is persisted in the database. In the log I see:
insert into person ...
However, the update I made is not persisted, and there is no error and no 'update' sql statement in the log. I thought the HibernateTemplate.save() method might be the problem but from within the saveSomeStuff() method, after loading the Person from the database, I do a System.out, and the Person loaded from the database has the updated name.
What am I missing here?
There is a separate method, saveOrUpdate(entity). You can use it if you don't want hibernate to generate id while saving.
Save method will Persists an entity. Will assign an identifier if one doesn't exist. If one does, it's essentially doing an update. Returns the generated ID of the entity.
Figured out the problem. If I had included my Entity class, someone probably would have seen it sooner than me.
#Entity
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
#Immutable
#Table(name = "PEOPLE")
public class Person {
...
}
Initially I was getting a cache error:
java.lang.UnsupportedOperationException: Can't write to a readonly object
The quick solution? Add the #Immutable annotation. But if you read the docs for it:
An immutable entity may not be updated by the application.
Updates to an immutable entity will be ignored, but no exception is thrown.
Which explains why 1) updates were being ignored and 2) no exceptions were being thrown.
So I got rid of the #Immutable annotation and changed Cache to:
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
And now everything works fine.
In summary: rtfm.
I had stumbled upon the same problem. The entity was getting inserted into the database, but while updating some of the columns where not getting updated and there were no errors in the log. After going through the entity class, I figured out that I had annotated some of my fields as below
#Column(name = "CREATED_DT", updatable = false)
private Date createdOn;
After removing the updatable attribute from the annotation, the update was working fine.

Spring: Inject bean depended on context (session/web or local thread/background process)

Is it possible to create a factory or proxy that can decide if thread is running in (Web)Request or background-process (ie. scheduler) and then depending on that information, it creates a session bean or a prototype bean?
Example (pseudo Spring config :)
<bean id="userInfoSession" scope="session" />
<bean id="userInfoStatic" scope="prototype" />
<bean id="currentUserInfoFactory" />
<bean id="someService" class="...">
<property name="userInfo" ref="currentUserInfoFactory.getCurrentUserInfo()" />
</bean>
I hope this makes my question easier to understand...
My Solution
It's never to late to update own questions ;). I solved it with two different instances of client session, one SessionScoped client session and one SingletonScoped session. Both are normal beans.
<bean id="sessionScopedClientSession" class="com.company.product.session.SessionScopedClientSession" scope="session">
<aop:scoped-proxy />
</bean>
<bean id="singletonScopedClientSession" class="com.company.product.session.SingletonScopedClientSession" />
<bean id="clientSession" class="com.company.product.session.ClientSession">
<property name="sessionScopedClientSessionBeanName" value="sessionScopedClientSession" />
<property name="singletonScopedClientSessionBeanName" value="singletonScopedClientSession" />
</bean>
The ClientSession will then decide if singleton or session scope:
private IClientSession getSessionAwareClientData() {
String beanName = (isInSessionContext() ? sessionScopedClientSessionBeanName : singletonScopedClientSessionBeanName);
return (IClientSession) ApplicationContextProvider.getApplicationContext().getBean(beanName);
}
Where session type could be gathered through this:
private boolean isInSessionContext() {
return RequestContextHolder.getRequestAttributes() != null;
}
All the classes implement a interface called IClientSession. Both singletonScoped and sessionScoped beans extends from a BaseClientSession where the implementation is found.
Every service then can use the client session ie:
#Resource
private ClientSession clientSession;
...
public void doSomething() {
Long orgId = clientSession.getSomethingFromSession();
}
Now if we go one step further we can write something like a Emulator for the session. This could be done by initializing the clientSession (which is in no context of a request) the singleton session. Now all services can use the same clientSession and we still can "emulate" a user ie:
clientSessionEmulator.startEmulateUser( testUser );
try {
service.doSomething();
} finally {
clientSessionEmulator.stopEmulation();
}
One more advice: take care about threading in SingletonScoped clientSession instance! Wouw, I thought I could do it with less lines ;) If you like to know more about this approach feel free to contact me.
I created small universal workaround to inject beans depends on context.
Guess we have two beans:
<bean class="xyz.UserInfo" id="userInfo" scope="session" />
<bean class="xyz.UserInfo" id="userInfoSessionLess" />
We want to use "userInfo" bean for web user actions and "userInfoSessionLess" bean for background services for example.
Wa also want to write code and don't want to think about context, for example:
#Autowired
//You will get "java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request?" for session less services.
//We can fix it and autowire "userInfo" or "userInfoSessionLess" depends on context...
private UserInfo userInfo;
public save(Document superSecureDocument) {
...
superSecureDocument.lastModifier = userInfo.getUser();
...
}
Now we need create custom session scope to make it worked:
public class MYSessionScope extends SessionScope implements ApplicationContextAware {
private static final String SESSION_LESS_POSTFIX = "SessionLess";
private ApplicationContext applicationContext;
public Object get(String name, ObjectFactory objectFactory) {
if (isInSessionContext()) {
log.debug("Return session Bean... name = " + name);
return super.get(name, objectFactory);
} else {
log.debug("Trying to access session Bean outside of Request Context... name = " + name + " return bean with name = " + name + SESSION_LESS_POSTFIX);
return applicationContext.getBean(name.replace("scopedTarget.", "") + SESSION_LESS_POSTFIX);
}
}
private boolean isInSessionContext() {
return RequestContextHolder.getRequestAttributes() != null;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Register new scope:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="mySession">
<bean class="com.galantis.gbf.web.MYSessionScope" />
</entry>
</map>
</property>
</bean>
Now we need modify beans definions like this:
<bean class="xyz.UserInfo" id="userInfo" scope="mySession" autowire-candidate="true"/>
<bean class="xyz.UserInfo" id="userInfoSessionLess" autowire-candidate="false"/>
That's all. Bean with name "SessionLess" will be used for all "mySession" scoped beans if we use bean outside of actual web request thread.
Your rephrase is indeed considerably simpler :)
Your currentUserInfoFactory could make use of RequestContextHolder.getRequestAttributes(). If a session is present and associated with the calling thread, then this will return a non-null object, and you can then safely retrieve the session-scoped bean from the context. If it returns a null, then you should fetch the prototype-scoped bean instead.
It's not very neat, but it's simple, and should work.
Create two custom context loaders that bind the same scope defintion to different implementations:
public final class SessionScopeContextLoader extends GenericXmlContextLoader {
protected void customizeContext(final GenericApplicationContext context) {
final SessionScope testSessionScope = new SessionScope();
context.getBeanFactory().registerScope("superscope", testSessionScope);
}
...
}
Then you make a corresponding one for singleton (make your own scope with just statics)
Then you just specify the appropriate context loader in the xml startup for each of the two contexts.

How to bind a bean property to another one and observe changes in Spring Framework

I'm wondering that if there is a way for binding a spring bean's property to another bean's property so if any change on binded property occurs in runtime, what i expect is referencing bean's property also changes. I'll explain more with a little code snippet.
<bean id="johnHome" class="example.Contact">
<property name="phone" value="5551333" />
</bean>
<bean id="johnWork" class="example.Contact">
<property name="phone">
<util:property-path path="johnHome.phone" />
</property>
</bean>
OK. This works at initial bean wiring but what i exactly want is to bind property so if the property changes at runtime the referencing bean also changes. If i should like to show with a metaphor it will seem like this.
<bean id="johnHome" class="example.Contact">
<property name="phone" value="5551333" />
</bean>
<bean id="johnWork" class="example.Contact">
<property name="phone">
<util:bind path="johnHome.phone" />
</property>
</bean>
Am i overloading the spring's concept too much or is this possible without a lot of tricks?
Thanks..
Simplest way - make that property a bean which is referenced by the two other beans, e.g. for a String value have a StringHolder class:
public class StringHolder {
private String value;
// setter and getter elided due to author's lazyness
}
The whole idea behind Spring is (was?) to keep a clean object-oriented design consisting of plain old java objects and use the spring framework to handle the tedious object creation. As for AOP, this should only handle cross-cutting concerns. I'm not at all convinced that this is one of those cases where AOP is a good idea. Your application relies on the behaviour of these phone numbers getting synced to each other, it's one of the main functionalities. As such, your design should reflect this.
Probably the most logical way to handle this specific problem is to make phone numbers their own class (which is also handy if you ever want to distinguish different types of phone numbers).
If you have a PhoneNumber object which takes the number as a constructor argument the mapping becomes trivial:
<bean id="johnFirstPhone" class="example.PhoneNumber">
<constructor-arg value="5551333" />
</bean>
<bean id="johnHome" class="example.Contact">
<property name="phone" ref="johnFirstPhone" />
</bean>
<bean id="johnWork" class="example.Contact">
<property name="phone" ref="johnFirstPhone" />
</bean>
Of course whether you'd map it like this in a static file is another matter, but the thing is in this situation you pretty clearly just need a reference/pointer.
I don't think what you're doing is possible in Spring 2.5. It may be possible in Spring 3, using the new expression syntax, but I don't think so.
Even if it were, it'd be confusing, I think. Better to stick your shared value into its own class and inject an instance of that class into the other beans that need to share it.
I can think of two possibilities.
One is (it is kind of a hack), if you don't have very many beans that need to be linked like the ones in your example, you could inject johnWork into the johnHome bean, and in johnHome.setPhone you could update the johnWork phone property, something like:
public class Contact {
private Contact myWorkContact;
private String phone;
public void setPhone(String phone) {
this.phone = phone;
if (this.myWorkContact != null) {
this.myWorkContact.setPhone(phone);
}
}
public void setWorkContact(Contact c) {
this.myWorkContact = c;
}
}
Or you could have HomeContact and WorkContact both extend a class Contact and do the same injection with that.
If you have tons and tons of beans that will need this (like if your application actually IS dealing with contact information), with AOP (you'll need AspectJ for the example given) I think you could do something like this (it will be a bit memory intensive if you get a ton of objects, but you can see how something like it would work):
Warning: this actually got complicated fast, but I'm pretty sure it would work after you worked out a few kinks
public class Contact {
...
private String phone;
private String name;
private Integer id;
public Contact(Integer id, String name, String phone) {
this.phone = phone;
this.name = name;
this.id = id;
}
public void setPhone(String phone) {
this.phone = phone.
}
//Other getters, setters, etc
...
}
#Aspect
public class ContactPhoneSynchronizer {
//there is probably a more efficient way to keep track of contact objects
//but right now i can't think of one, because for things like a tree, we need to
//be able to identify objects with the same name (John Smith), but that
//have different unique ids, since we only want one of each Contact object
//in this cache.
private List<Contact> contacts = Collections.synchronizedList(new ArrayList<Contact>());
/**
This method will execute every time someone makes a new Contact object.
If it already exists, return it from the cache in this.contacts. Otherwise,
proceed with the object construction and put that object in the cache.
**/
#Around("call(public Contact.new(Integer,String,String)) && args(id,name,phone)")
public Object cacheNewContact(ProceedingJoinPoint joinPoint, Integer id, String name, String phone) {
Contact contact = null;
for (Contact c : contacts) {
if (id.equals(c.getId()) {
contact = c;
break;
}
}
if (contact == null) {
contact = (Contact) joinPoint.proceed();
this.contacts.add(contact);
}
return contact;
}
/**This should execute every time a setPhone() method is executed on
a contact object. The method looks for all Contacts of the same
name in the cache and then sets their phone number to the one being passed
into the original target class.
Because objects are passed by reference until you do a reassociation,
calling c.setPhone on the object in the cache should update the actual
instance of the object in memory, so whoever has that reference will
get the updated information.
**/
#After("execution(example.Contact.setPhone(String) && args(phone)")
public void syncContact(JoinPoint joinPoint, String phone) {
Contact contact = joinPoint.getTarget();
for (Contact c : this.contacts) {
if (c.getName().equals(contact.getName()) {
c.setPhone(phone);
}
}
}
}
Again, there is probably 100 ways you could optimize this, since I'm typing it off the top of my head; that is, if you wanted to go this route in the first place. In theory it should work but I haven't tested it at all.
Anyway, Happy Springing!

Resources