Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 339203

Summary: Invalid evaluation of Java Service wrapper when using constant parameters
Product: [Modeling] Acceleo Reporter: Rainer Menke <rainer>
Component: CoreAssignee: Project Inbox <acceleo-inbox>
Status: CLOSED INVALID QA Contact:
Severity: minor    
Priority: P3 CC: stephane.begaudeau
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Windows XP   
Whiteboard:

Description Rainer Menke CLA 2011-03-08 07:17:14 EST
Build Identifier: I20100608-0911

Wrote a simple ecore-Model for property-Files, and made a simple test whether the definition and usage of variables is possible. I used Acceleo 3.1.0M5 for this. In the template I store for some reasons values temporary to access them for a later usage. If I use the java service with a static parameter, it seems like this java service call is optimized to be executed just one time.

First write a simple template, where two java services are used, one is for setting, the other for reading internal values:
[comment encoding = UTF-8 /]
[module generate('properties')/]

[query public setGlobalProperty(property: String , value: String) : Boolean = invoke('org.eclipse.acceleo.module.sample.services.GlobalProperties','setProperty(java.lang.String, java.lang.String)',Sequence{property,value})/]
[query public getGlobalProperty(property: String) : String = invoke('org.eclipse.acceleo.module.sample.services.GlobalProperties','getProperty(java.lang.String)',Sequence{property})/]

[template public generate(p : PropertiesType)]
	
[file ('props.txt', false, 'UTF-8')]
	[for ( pp : PropertyType | p.property ) ]
		[setGlobalProperty('lastProperty',pp.name)/]
		[setGlobalProperty(pp.name, pp.value)/]
Save of '[pp.name/]' with '[pp.value/]'
		[let last : String = pp.name ]
1.) read '[last/]' with '[getGlobalProperty(last)/]'
		[/let]
		[let last : String = getGlobalProperty('lastProperty') ]
2.) read '[last/]' with '[getGlobalProperty(last)/]'
		[/let]
	[/for]
[/file]
	
[/template]

2.) Implement the java services:
public class GlobalProperties {
	private static Properties properties = new Properties();
	public static String getProperty( String property ) {
		String value = properties.getProperty( property );
		System.out.println( "getProperty: property '" + property + "' value '" + value + "'" );
		return value;
	}
	public static void setProperty(String property,String value) {
		String _value = properties.getProperty( property );
		System.out.println( "setProperty: property '" + property + "' value '" + value + "' replacing '" + _value + "'" );
		properties.setProperty( property, value );
		_value = properties.getProperty( property );
		System.out.println( "setProperty: property '" + property + "' value '" + _value + "'" );
	}
}

Under the console you see:

setProperty: property 'lastProperty' value 'a' replacing 'null'
setProperty: property 'lastProperty' value 'a'
setProperty: property 'a' value 'b' replacing 'null'
setProperty: property 'a' value 'b'
getProperty: property 'a' value 'b'
getProperty: property 'lastProperty' value 'a'
setProperty: property 'lastProperty' value 'b' replacing 'a'
setProperty: property 'lastProperty' value 'b'
setProperty: property 'b' value 'c' replacing 'null'
setProperty: property 'b' value 'c'
getProperty: property 'b' value 'c'
setProperty: property 'lastProperty' value 'd' replacing 'b'
setProperty: property 'lastProperty' value 'd'
setProperty: property 'd' value 'e' replacing 'null'
setProperty: property 'd' value 'e'
getProperty: property 'd' value 'e'

As result the following is produced:

Save of 'a' with 'b'
1.) read 'a' with 'b'
2.) read 'a' with 'b'
		
Save of 'b' with 'c'
1.) read 'b' with 'c'
2.) read 'a' with 'b'
		
		
Save of 'd' with 'e'
1.) read 'd' with 'e'
2.) read 'a' with 'b'


Reproducible: Always

Steps to Reproduce:
1.) Take some input model (independent)
2.) Build two java services, one for storing, one for accessing 
3.) Write a template where the values of some variables are exchanged during the generation phase

See the details for implementation ;-)
Comment 1 Rainer Menke CLA 2011-03-08 07:44:08 EST
Same works, if a "random" function with no parameter is used. Only iff a service wrapper has at least one parameter, which is not constant, the service wrapper is invoked multiple times.
Comment 2 Stephane Begaudeau CLA 2011-03-08 07:58:37 EST
I haven't read in detail your example but this sentence seems to explain your whole problem: "it seems like this java service call is optimized to be executed just one time." The problem is not the Java service, the problem comes from the query.

Your Java service is inside of a query. A query has its result stored in a cache, if you call it twice in a row with the same argument, we won't execute it again, we will look for the result in the cache. You should try with the [invoke(...)/] in a template like this:

[template public setGlobalProperty(property: String , value: String)][invoke('org.eclipse.acceleo.module.sample.services.GlobalProperties','setProperty(java.lang.String, java.lang.String)',Sequence{property,value})/][/template]

and

[template public getGlobalProperty(property: String)]
[invoke('org.eclipse.acceleo.module.sample.services.GlobalProperties','getProperty(java.lang.String)',Sequence{property})/][/template]

With a query, "getGlobalProperty('lastProperty')" will always be evaluated only one time since it always uses the same parameter.

If you want to improve the performances of a generator, you can use some well chosen queries for the big operations on your model like MyModelRoot.eAllContents() for example.