You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Stephan Windmüller <st...@tu-dortmund.de> on 2014/07/30 12:45:20 UTC

Product-line specific component messages

Hello,

in our Tapestry application we have several product lines which require 
a different wording on the pages. My first thought was to provide my own 
implementation of the Messages service and set the product line in the 
Java Code with something like

	messages.setProductLine(ProductLine)

during onActivate and provide different keys like

	title-PRODUCT1=Product One
	title-PRODUCT2=Product Two

That would work for all messages retrieved by the injected service, but 
how can I replace the service which is used to parse messages in the TML 
files? Sadly I was not able to determine the code which is called when 
messages are parsed here. Is it handled by AbstractMessages, too?

Regards
  Stephan

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Product-line specific component messages

Posted by Thiago H de Paula Figueiredo <th...@gmail.com>.
On Wed, 30 Jul 2014 12:05:13 -0300, Stephan Windmüller  
<st...@tu-dortmund.de> wrote:

> It worked! Thank you very very much!

Yay! Never done that myself. :p

> I had to extend AbstractBinding though, because LiteralBinding stores  
> fixed values which are never altered.

Good catch. :)

> Currently I am passing both Messages and my own service to the  
> construcor of my binding class in order to access these whenever get()  
> is called. Is this the right way to do this?

I see no problem in that.

-- 
Thiago H. de Paula Figueiredo
Tapestry, Java and Hibernate consultant and developer
http://machina.com.br

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Product-line specific component messages

Posted by Stephan Windmüller <st...@tu-dortmund.de>.
On 30.07.2014, Thiago H de Paula Figueiredo wrote:

> So, if you don't override this method, AbstractBinding subclasses are
> invariant and values get cached for performance.
>
> Create a VariantLiteralBinding subclass that overrides isInvariant() to
> return false, use instead of LiteralBinding and I guess it'll work.

It worked! Thank you very very much!

I had to extend AbstractBinding though, because LiteralBinding stores 
fixed values which are never altered.

Currently I am passing both Messages and my own service to the 
construcor of my binding class in order to access these whenever get() 
is called. Is this the right way to do this?

Regards
  Stephan

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Product-line specific component messages

Posted by Thiago H de Paula Figueiredo <th...@gmail.com>.
On Wed, 30 Jul 2014 10:42:39 -0300, Stephan Windmüller  
<st...@tu-dortmund.de> wrote:

>>> Injecting the service in onActivate and setting the value does not  
>>> work.
>> Define "does not work". Did you debug your binding factory?
>
> Yes, and it seems that "newBinding" is only called once for each  
> location and the value is cached. But more importantly, newBinding is  
> called _before_ the onActivate method is evaluated.
>
> In other words: It seems that it is not possible to change evaluated  
> value from onActivate.

We can prevent that. Look at the Binding interface: it has this method:
     /**
      * Returns true if the value of the binding does not ever change.  
Components will often cache such values
      * aggressively.
      */
     boolean isInvariant();

Then at AbstractBinding:

     /**
      * Returns true. Subclasses that do not supply a fixed, read-only  
value should override this method to return
      * false.
      */
     public boolean isInvariant()
     {
         return true;
     }

So, if you don't override this method, AbstractBinding subclasses are  
invariant and values get cached for performance.

Create a VariantLiteralBinding subclass that overrides isInvariant() to  
return false, use instead of LiteralBinding and I guess it'll work.

-- 
Thiago H. de Paula Figueiredo
Tapestry, Java and Hibernate consultant and developer
http://machina.com.br

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Product-line specific component messages

Posted by Stephan Windmüller <st...@tu-dortmund.de>.
Am 30.07.2014 um 15:18 schrieb Thiago H de Paula Figueiredo:

> Don't forget productService must be a perthread service. Otherwise, any
> request for the current product will set it for all others and a mess will
> arise.

I can imagine that. ;)

To prevent this, I added "@Scope(ScopeConstants.PERTHREAD)" to the 
ProductServiceImpl class.

>> Injecting the service in onActivate and setting the value does not work.
> Define "does not work". Did you debug your binding factory?

Yes, and it seems that "newBinding" is only called once for each 
location and the value is cached. But more importantly, newBinding is 
called _before_ the onActivate method is evaluated.

In other words: It seems that it is not possible to change evaluated 
value from onActivate.

Regards
  Stephan

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Product-line specific component messages

Posted by Thiago H de Paula Figueiredo <th...@gmail.com>.
On Wed, 30 Jul 2014 10:02:55 -0300, Stephan Windmüller  
<st...@tu-dortmund.de> wrote:

> And here is my problem: The current product is selected based on the  
> activation context. How can I modify the service based on the values of  
> the context?

@Inject
private ProductService productService;

void onActivate(...) {
	productService.setCurrentProduct(...);
}

Don't forget productService must be a perthread service. Otherwise, any  
request for the current product will set it for all others and a mess will  
arise.

> Injecting the service in onActivate and setting the value does not work.

Define "does not work". Did you debug your binding factory?

-- 
Thiago H. de Paula Figueiredo
Tapestry, Java and Hibernate consultant and developer
http://machina.com.br

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Product-line specific component messages

Posted by Stephan Windmüller <st...@tu-dortmund.de>.
Am 30.07.2014 um 14:14 schrieb Thiago H de Paula Figueiredo:

> Why don't you create your own 'productmessage' binding so you can write
> ${productmessage:title} and have it treated like you wrote
> messages.get("title")?

That looks really promising, thank you for the detailed explanation!

> Here's an untested version of your ProductMessageBindingFactory, supposing
> you have a ProductService perthread service which provides the current
> product:

And here is my problem: The current product is selected based on the 
activation context. How can I modify the service based on the values of 
the context?

Injecting the service in onActivate and setting the value does not work.

Regards
  Stephan

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Product-line specific component messages

Posted by Thiago H de Paula Figueiredo <th...@gmail.com>.
On Wed, 30 Jul 2014 07:45:20 -0300, Stephan Windmüller  
<st...@tu-dortmund.de> wrote:

> Hello,

Hi!

>
> in our Tapestry application we have several product lines which require  
> a different wording on the pages. My first thought was to provide my own  
> implementation of the Messages service and set the product line in the  
> Java Code with something like
>
> 	messages.setProductLine(ProductLine)
>
> during onActivate and provide different keys like
>
> 	title-PRODUCT1=Product One
> 	title-PRODUCT2=Product Two

Why don't you create your own 'productmessage' binding so you can write  
${productmessage:title} and have it treated like you wrote  
messages.get("title")?

> That would work for all messages retrieved by the injected service, but  
> how can I replace the service which is used to parse messages in the TML  
> files?

The Tapestry template parser does *not* parse messages. Actually, it just  
parses ${} expansions and ask their values to the specified binding. The  
'message' binding is the one who actually handles the messages. Here's its  
implementation:

public class MessageBindingFactory implements BindingFactory
{

     public Binding newBinding(String description, ComponentResources  
container, ComponentResources component,
                               String expression, Location location)
     {
         String messageValue = container.getMessages().get(expression);

         return new LiteralBinding(location, description, messageValue);
     }
}

Incredibly simple, isn't it? :)

Here's an untested version of your ProductMessageBindingFactory, supposing  
you have a ProductService perthread service which provides the current  
product:

public class ProductMessageBindingFactory implements BindingFactory
{

     final private ProductService productService;

     public ProductMessageBindingFactory(ProductService productService) {
         this.productService = productService;
     }

     public Binding newBinding(String description, ComponentResources  
container, ComponentResources component,
                               String expression, Location location)
     {
         String messageValue = container.getMessages().get(expression + "-"  
+ productService.getCurrentProductMessageSuffix());

         return new LiteralBinding(location, description, messageValue);
     }
}

Then, in your AppModule class (or any other Tapestry-IoC module class),  
contribute it to BindingSource:

public static void contributeBindingSource(MappedConfiguration<String,  
BindingFactory> configuration) {
     configuration.addInstance("productmessages",  
ProductMessageBindingFactory.class);
}

And that's it.

-- 
Thiago H. de Paula Figueiredo
Tapestry, Java and Hibernate consultant and developer
http://machina.com.br

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org