Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Remote Application Platform (RAP) » Nice way to get a lazy loaded, session scoped model (MVC)(Domain models created by Spring consumed by RAP)
Nice way to get a lazy loaded, session scoped model (MVC) [message #1373767] Thu, 22 May 2014 10:06
Thorsten Hilker is currently offline Thorsten HilkerFriend
Messages: 31
Registered: March 2014
Member
Hi!

We had the problem to produce lazy loaded, session scoped domain models.
Our models are produced by Spring in OSGi bundles.

I want to share our solution.

Here an example model:

package com.imilia.test.model;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.imilia.test.domain.ModelObject;
import com.imilia.test.domain.Person;
import com.imilia.test.interfaces.model.IPersonModel;
import com.imilia.test.interfaces.services.IPersonService;

@Component
@Scope("prototype")
public class PersonModel extends ModelObject implements IPersonModel {
	
	private Person person;
	
	@Autowired
	private IPersonService personService;
		
	@Override
	public void add() {
		firePropertyChange("person", this.person, this.person = personService.newPerson());
	}
	
	@Override
	public void delete() {
		Person nextPerson = personService.nextPerson(person);
		personService.deletePerson(person);
		firePropertyChange("person", this.person, this.person = nextPerson);
	}
	
	@Override
	public void load(int oid) {
		firePropertyChange("person", this.person, this.person = personService.getPerson(oid));
	}
		
	@Override
	public Person getPerson() {
		return person;
	}
	
	@Override
	public void next() {
		firePropertyChange("person", this.person, this.person = personService.nextPerson(person));
	}
	
	@Override
	public void previous() {
		firePropertyChange("person", this.person, this.person = personService.previousPerson(person));
	}
	
	@Override
	public void save() {
		personService.savePerson(person);
	}

}


As you can see the scope is "prototype", because Spring is not able to produce "session" scoped beans in OSGi. (Better said it's not that easy to produce "session" scoped beans in OSGi. Or is there an easy way?)

Ok, that was the model. Here is the serviceinterface and the service:

package com.imilia.test.interfaces.model;

import java.util.Arrays;
import java.util.List;

public interface IModelService {
	
	public static final String PERSONMODEL_MAINPART = "PersonModel_MainPart";
	public static final String PERSONMODEL_MAINPART2 = "PersonModel_MainPart2";
	
	public static final List<String> models = Arrays.asList(PERSONMODEL_MAINPART,PERSONMODEL_MAINPART2);
	
	Object getModel(String modelname);

}

package com.imilia.test.model;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

import com.imilia.test.interfaces.model.IModelService;

@Service
public class ModelService implements ApplicationContextAware, IModelService {
	
	static final private Map<String,String> modelMap = new HashMap<String,String>();

	private ApplicationContext applicationContext = null;
	
	static {
		modelMap.put(IModelService.PERSONMODEL_MAINPART, "personModel");
		modelMap.put(IModelService.PERSONMODEL_MAINPART2, "personModel");
	}
	
	@Override
	public Object getModel(String modelname) {
		return applicationContext.getBean(modelMap.get(modelname));
	}
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
		
	}
	
}

The interesting part here is the map. It allows you to produce different models of the same type. I will come back to this soon.

Here are the spring config files from /META-INF/spring:


Module-context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
				http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
	<context:component-scan base-package="com.imilia.test.model" />

</beans>


Nothing special. Just Java-based configuration via annotations (as you can in the 2 classes above (@Component, @Service, ...))

osgi-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"
	xmlns:osgi="http://www.springframework.org/schema/osgi">

	<osgi:reference id="personService" interface="com.imilia.test.interfaces.services.IPersonService" />

	<osgi:service ref="modelService" interface="com.imilia.test.interfaces.model.IModelService"/>
</beans>

personService is the service which accesses the database. It comes from another bundle.

More important is the export of the modelService here.

Ok, that was the "model-bundle".

Now RAP.
I discovered the beautiful mechanism of addons in RAP.
So this is the core idea, a addon which registers the models:

package com.imilia.test.gui;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.eclipse.e4.core.contexts.ContextFunction;
import org.eclipse.e4.core.contexts.IEclipseContext;

import com.imilia.test.interfaces.model.IModelService;

public class DomainModelServiceAddon {

	@Inject
	IModelService modelService;

	@PostConstruct
	public void init(IEclipseContext context) {
		for (String modelname : IModelService.models) {
			context.set(modelname, new ModelContextFunction());
		}
	}

	private class ModelContextFunction extends ContextFunction {
		private Object model;

		public Object compute(IEclipseContext context, String contextKey) {
			if (model == null) {
				model = modelService.getModel(contextKey);
			}

			return model;
		}
	}
}


Easy code, but mighty (in my opinion Smile)

(It's really astonishing how easy it is to get the modelService from the OSGi-context!)

The idea is to register a factory(ModelContextFunction) in the rap-context instead of the model itself. So you got this lazy loading effect. (Our application will use hundreds of models. Would be a waste of resources and time to load them all at the beginning of a session.
The second idea is trivial: "Get the model if it's empty and store it. Use the stored one, if it's not empty." So you got "session-scope".

OK, but what is the sense?
Here is a part of a view:
...
	@Inject @Named(IModelService.PERSONMODEL_MAINPART) private IPersonModel personModel;
	private Label lblOid;
	private Label lblVorname;
	private Label lblNachname;
	private Button btnSpeichern;

	@PostConstruct
	public void init(Composite parent) {
		
		personModel.load(0);

		parent.setLayout(new FormLayout());



Now you can see how mighty RAPs DI-context is.
Just one line of code in the view and the domain-model is there.
(Lazy loaded, session scoped)

Now the use of the map in the service becomes clearer.
1. With @Named(IModelService.PERSONMODEL_MAINPART) a model of type "PersonModel" is loaded. If you use the same command in another view. The same model is loaded. (The same session scoped instance).
2. With @Named(IModelService.PERSONMODEL_MAINPART2) a model of type "PersonModel" is loaded. But another instance.

Hope that was a little bit helpful for you.
Comments are welcomed.

Greetings!

Thorsten



[Updated on: Thu, 22 May 2014 19:43]

Report message to a moderator

Previous Topic:Restart application with no cid
Next Topic:Focus lost event with hyperlink in Browser
Goto Forum:
  


Current Time: Wed Feb 26 05:06:15 GMT 2020

Powered by FUDForum. Page generated in 0.02414 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top