Re: Best practice for initializing page to default context

2009-12-09 Thread Kalle Korhonen
Opened https://issues.apache.org/jira/browse/TAP5-948 for it.

Kalle

On Tue, Dec 8, 2009 at 11:24 AM, Kalle Korhonen
kalle.o.korho...@gmail.com wrote:
 On Tue, Dec 8, 2009 at 10:59 AM, Howard Lewis Ship hls...@gmail.com wrote:
 I've had to solve this problem for one of my clients as well and I
 think it's something that should go into the framework.  The approach
 I took was to identify self-referential links (page render links that
 are to the same page they originate from) using an additional query
 parameter. This allows Tapestry to differentiate between requests that
 start on a new page vs. those that continue on the page. Tapestry can
 then fire a notification on components to perform initialization (on
 page render requests without the query parameter).
 This would be a new lifecycle method, like pageAttached() or
 pageLoaded().  I'm still working on the right terminology, for Widen
 it is initialized, as in method pageInitialized().

 Thank you Howard, that would excellent. Until it's in the framework,
 obviously you can get it done one way or another but framework support
 would make things easier and more consistent. I'd hate to come up with
 something that is half-there as an optional module if you are already
 working on solving it at the core framework level. Sounds like there's
 no issue open on it (?) - if not, I'll open one.

 Kalle


 On Mon, Dec 7, 2009 at 10:22 PM, Kalle Korhonen
 kalle.o.korho...@gmail.com wrote:
 Most things in T5 are delightfully simple, but I find this
 surprisingly difficult: how to best initialize a page to default
 context (and redirect to it). Imagine you have a search  result page.
 If I access the page without any context I want all records to be
 displayed. In onActivate() without parameters I set the context to
 *all* and return this to redirect, then I query the database in
 setupRender() to initialize the data for the grid. However, sorting
 the grid will also cause a call to onActivate() without parameters,
 resetting my data to the default context. The parameter-less call to
 onActivate() would be harmless if I didn't do a redirect from
 onActivate() but then I cannot set the default context and redirect.
 In setupRender() I could decide whether redirect is needed or not but
 at that time, I'm already committed to rendering the request.

 Because events cause a parameterless onActivate()  call, I tend to
 reserve onActivate() for possible component/event initialization needs
 only and always link to pages with initial context already set. I also
 find it roughly impossible to use overloaded versions of onActivate()
 and subsequently, if my page has multiple entry points, I typically
 resort to implementing it in a single onActivate(EventContext
 eventContext) operation containing a big if-else clause. Since the
 activation context is anyway sent with an event request (as in
 ?t:ac=mycontext), rather than using the encoded context for rendering,
 wouldn't it be just simpler if that context was used for activating
 the page for the event request and the following redirect for
 rendering would just use whatever context onPassivate() returns? What
 do others think, how do you handle this?

 Kalle

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





 --
 Howard M. Lewis Ship

 Creator of Apache Tapestry

 The source for Tapestry training, mentoring and support. Contact me to
 learn how I can get you up and productive in Tapestry fast!

 (971) 678-5210
 http://howardlewisship.com

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




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



Re: Best practice for initializing page to default context

2009-12-08 Thread DH
Of course, but since I am not very good at javassit, my implementation may be 
ugly and a bit long. Anyone please consummate it.
Like PageActivationContext, I introduce an PageActivationUnitWorker.
The key part is to collect the param fields and add the param map to page class 
in runtime. onActivate and onPassivate would use this map to set value or 
retrieve value.

1. For annotation:

@Target(FIELD)
@Documented
@Retention(RUNTIME)
public @interface PageActivationUnit {
 String value() default ;
}

2. For worker:
public class PageActivationUnitWorker implements ComponentClassTransformWorker {

 private TypeCoercer _typeCoercer;
 
 public PageActivationUnitWorker(TypeCoercer typeCoercer) {
  this._typeCoercer = typeCoercer;
 }
 
 public void transform(ClassTransformation transformation, 
MutableComponentModel model) {
  ListString fieldNames = 
transformation.findFieldsWithAnnotation(PageActivationUnit.class);
  
  if (fieldNames != null  fieldNames.size()  0) {
   
   MapString, String fieldParamMap = new HashMapString, String();
   for (String fieldName : fieldNames) {
PageActivationUnit annotation = transformation.getFieldAnnotation(fieldName,
  PageActivationUnit.class);
fieldParamMap.put(fieldName, getParameterName(fieldName, 
annotation.value()));
   }
   
   String typeCoercer = transformation.addInjectedField(TypeCoercer.class, 
typeCoercer, _typeCoercer);
   
   TransformMethodSignature activate
 = new TransformMethodSignature(Modifier.PROTECTED | Modifier.FINAL, 
boolean,
   onActivate,
   new String[] 
{EventContext.class.getName()}, null);
   
   TransformMethodSignature passivate
 = new TransformMethodSignature(Modifier.PROTECTED | 
Modifier.FINAL, java.lang.Object[],
   onPassivate,
   null, null);
   
   BodyBuilder activeBuilder = new BodyBuilder().begin();
   activeBuilder.addln(java.util.Map keyValueMap = new java.util.HashMap(););
   activeBuilder.add(for (int i = 0; i  $1.getCount(); i++));
   activeBuilder.begin();
   activeBuilder.addln(String []keyValue = ((String)$1.get(String.class, 
i)).split(\-\););
   activeBuilder.addln(String key = keyValue[0];);
   activeBuilder.addln(String value = (keyValue.length  1) ? keyValue[1] : 
null;);
   activeBuilder.addln(keyValueMap.put(key, value););
   activeBuilder.end();
   // end for
   for (int i = 0, size = fieldNames.size(); i  size; i++) {
String fieldName = fieldNames.get(i);
String fieldType = transformation.getFieldType(fieldName);
activeBuilder.addln(String fieldValue=(String)keyValueMap.get(\%s\);, 
fieldParamMap.get(fieldName));
activeBuilder.addln(if (fieldValue != null) {);
activeBuilder.addln(%s=(%s)%s.coerce(fieldValue, Class.forName(\%s\));, 
fieldName, fieldType, typeCoercer, fieldType);
activeBuilder.addln(});
   }
   activeBuilder.addln(return true;);
   // end method body
   activeBuilder.end();
 
   BodyBuilder deactiveBuilder = new BodyBuilder().begin();
   for (int i = 0, size = fieldNames.size(); i  size; i++) {
String fieldName = fieldNames.get(i);

if (i == size - 1) {
 deactiveBuilder.add(\%s-\ +  (%s != null ? (String)%s.coerce(%s, 
String.class) : \\), fieldParamMap.get(fieldName), fieldName, typeCoercer, 
fieldName);
} else {
 deactiveBuilder.add(\%s-\ +  (%s != null ? (String)%s.coerce(%s, 
String.class) : \\),, fieldParamMap.get(fieldName), fieldName, typeCoercer, 
fieldName);
}
   }
   deactiveBuilder.end();
   
   transformation.addTransformedMethod(activate, activeBuilder.toString());
   transformation.addTransformedMethod(passivate, return new Object[] + 
deactiveBuilder.toString() + ;);
  }
 }

 private String getParameterName(String fieldName, String annotatedName)
{
if (InternalUtils.isNonBlank(annotatedName)) return annotatedName;

return InternalUtils.stripMemberName(fieldName);
}
}

3. In app module

public static void contributeComponentClassTransformWorker(
OrderedConfigurationComponentClassTransformWorker configuration, 
TypeCoercer typeCoercer) {
 configuration.add(PageActivationUnit, new 
PageActivationUnitWorker(typeCoercer), before:OnEvent);
}

That's all.
One disadvantage is that you can't use primitive type for param field, so use 
Integer instead of int.

DH
http://www.gaonline.com.cn
- Original Message - 
From: Inge Solvoll 
To: Tapestry users users@tapestry.apache.org
Sent: Tuesday, December 08, 2009 4:14 PM
Subject: Re: Best practice for initializing page to default context


 Would it be possible for you to share that code with us? I don't necessarily
 want to use that approach, but it would be very helpful to see how you
 implemented it.
 
 Inge
 
 On Tue, Dec 8, 2009 at 9:10 AM, DH ningd...@gmail.com wrote:
 
 Once I found it difficult too, and I never used  EventContext because I
 think

Re: Best practice for initializing page to default context

2009-12-08 Thread Thiago H. de Paula Figueiredo
Em Tue, 08 Dec 2009 04:22:58 -0200, Kalle Korhonen  
kalle.o.korho...@gmail.com escreveu:



and subsequently, if my page has multiple entry points, I typically
resort to implementing it in a single onActivate(EventContext
eventContext) operation containing a big if-else clause.


That's the recommended way when you have a variable number of activation  
context parameters.



Since the activation context is anyway sent with an event request (as in
?t:ac=mycontext), rather than using the encoded context for rendering,
wouldn't it be just simpler if that context was used for activating
the page for the event request and the following redirect for
rendering would just use whatever context onPassivate() returns?


The activation context is always what onPassivate() returns. I don't  
understand what is the problem here.


--
Thiago H. de Paula Figueiredo
Independent Java, Apache Tapestry 5 and Hibernate consultant, developer,  
and instructor
Owner, software architect and developer, Ars Machina Tecnologia da  
Informação Ltda.

http://www.arsmachina.com.br

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



Re: Best practice for initializing page to default context

2009-12-08 Thread NingDH
Of course, but since I am not very good at javassit, my implementation may be 
ugly and a bit long. Anyone please consummate it.
Like PageActivationContext, I introduce an PageActivationUnitWorker.
The key part is to collect the param fields and add the param map to page class 
in runtime. onActivate and onPassivate would use this map to set value or 
retrieve value.

1. For annotation:

@Target(FIELD)
@Documented
@Retention(RUNTIME)
public @interface PageActivationUnit {
 String value() default ;
}

2. For worker:
public class PageActivationUnitWorker implements ComponentClassTransformWorker {

 private TypeCoercer _typeCoercer;
 
 public PageActivationUnitWorker(TypeCoercer typeCoercer) {
  this._typeCoercer = typeCoercer;
 }
 
 public void transform(ClassTransformation transformation, 
MutableComponentModel model) {
  ListString fieldNames = 
transformation.findFieldsWithAnnotation(PageActivationUnit.class);
  
  if (fieldNames != null  fieldNames.size()  0) {
   
   MapString, String fieldParamMap = new HashMapString, String();
   for (String fieldName : fieldNames) {
PageActivationUnit annotation = transformation.getFieldAnnotation(fieldName,
  PageActivationUnit.class);
fieldParamMap.put(fieldName, getParameterName(fieldName, 
annotation.value()));
   }
   
   String typeCoercer = transformation.addInjectedField(TypeCoercer.class, 
typeCoercer, _typeCoercer);
   
   TransformMethodSignature activate
 = new TransformMethodSignature(Modifier.PROTECTED | Modifier.FINAL, 
boolean,
   onActivate,
   new String[] 
{EventContext.class.getName()}, null);
   
   TransformMethodSignature passivate
 = new TransformMethodSignature(Modifier.PROTECTED | 
Modifier.FINAL, java.lang.Object[],
   onPassivate,
   null, null);
   
   BodyBuilder activeBuilder = new BodyBuilder().begin();
   activeBuilder.addln(java.util.Map keyValueMap = new java.util.HashMap(););
   activeBuilder.add(for (int i = 0; i  $1.getCount(); i++));
   activeBuilder.begin();
   activeBuilder.addln(String []keyValue = ((String)$1.get(String.class, 
i)).split(\-\););
   activeBuilder.addln(String key = keyValue[0];);
   activeBuilder.addln(String value = (keyValue.length  1) ? keyValue[1] : 
null;);
   activeBuilder.addln(keyValueMap.put(key, value););
   activeBuilder.end();
   // end for
   for (int i = 0, size = fieldNames.size(); i  size; i++) {
String fieldName = fieldNames.get(i);
String fieldType = transformation.getFieldType(fieldName);
activeBuilder.addln(String fieldValue=(String)keyValueMap.get(\%s\);, 
fieldParamMap.get(fieldName));
activeBuilder.addln(if (fieldValue != null) {);
activeBuilder.addln(%s=(%s)%s.coerce(fieldValue, Class.forName(\%s\));, 
fieldName, fieldType, typeCoercer, fieldType);
activeBuilder.addln(});
   }
   activeBuilder.addln(return true;);
   // end method body
   activeBuilder.end();
 
   BodyBuilder deactiveBuilder = new BodyBuilder().begin();
   for (int i = 0, size = fieldNames.size(); i  size; i++) {
String fieldName = fieldNames.get(i);

if (i == size - 1) {
 deactiveBuilder.add(\%s-\ +  (%s != null ? (String)%s.coerce(%s, 
String.class) : \\), fieldParamMap.get(fieldName), fieldName, typeCoercer, 
fieldName);
} else {
 deactiveBuilder.add(\%s-\ +  (%s != null ? (String)%s.coerce(%s, 
String.class) : \\),, fieldParamMap.get(fieldName), fieldName, typeCoercer, 
fieldName);
}
   }
   deactiveBuilder.end();
   
   transformation.addTransformedMethod(activate, activeBuilder.toString());
   transformation.addTransformedMethod(passivate, return new Object[] + 
deactiveBuilder.toString() + ;);
  }
 }

 private String getParameterName(String fieldName, String annotatedName)
{
if (InternalUtils.isNonBlank(annotatedName)) return annotatedName;

return InternalUtils.stripMemberName(fieldName);
}
}

3. In app module

public static void contributeComponentClassTransformWorker(
OrderedConfigurationComponentClassTransformWorker configuration, 
TypeCoercer typeCoercer) {
 configuration.add(PageActivationUnit, new 
PageActivationUnitWorker(typeCoercer), before:OnEvent);
}

That's all.
One disadvantage is that you can't use primitive type for param field, so use 
Integer instead of int.

DH
http://www.gaonline.com.cn
- Original Message - 
From: Inge Solvoll 
To: Tapestry users users@tapestry.apache.org
Sent: Tuesday, December 08, 2009 4:14 PM
Subject: Re: Best practice for initializing page to default context


 Would it be possible for you to share that code with us? I don't necessarily
 want to use that approach, but it would be very helpful to see how you
 implemented it.
 
 Inge
 
 On Tue, Dec 8, 2009 at 9:10 AM, DH ningd...@gmail.com wrote:
 
 Once I found it difficult too, and I never used  EventContext because I
 think

Re: Best practice for initializing page to default context

2009-12-08 Thread Kalle Korhonen
On Tue, Dec 8, 2009 at 3:39 AM, Thiago H. de Paula Figueiredo
thiag...@gmail.com wrote:
 Em Tue, 08 Dec 2009 04:22:58 -0200, Kalle Korhonen
 kalle.o.korho...@gmail.com escreveu:
 and subsequently, if my page has multiple entry points, I typically
 resort to implementing it in a single onActivate(EventContext
 eventContext) operation containing a big if-else clause.
 That's the recommended way when you have a variable number of activation
 context parameters.

DH's approach looks interesting, but maybe a bit involving with field
names encoded to the url. Thiago, I know it's the recommended approach
but I'm just saying it doesn't strike me as the ideal approach.

 Since the activation context is anyway sent with an event request (as in
 ?t:ac=mycontext), rather than using the encoded context for rendering,
 wouldn't it be just simpler if that context was used for activating
 the page for the event request and the following redirect for
 rendering would just use whatever context onPassivate() returns?
 The activation context is always what onPassivate() returns. I don't
 understand what is the problem here.

That sounds like I don't understand and don't want to hear about it
- why bother responding if that's the case? Now I'm not sure if it's
worth my time to write more details or sample code to describe what I
mean if you are not interested in explaining or exploring how to do it
better.

Kalle

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



Re: Best practice for initializing page to default context

2009-12-08 Thread ningdh
I can't think of a better way not to involve the field name encoded.

And if you don't like the field name exposed, you can also customize the param 
name by setting the annotation value, such as 

@PageActivationUnit(cat)
private Category category;

Then the url is like /searchpage/cat-VALUE

I like this style a lot and use heavily in my multiple projects.

DH

- Original Message - 
From: Kalle Korhonen 
To: Tapestry users users@tapestry.apache.org
Sent: Wednesday, December 09, 2009 1:49 AM
Subject: Re: Best practice for initializing page to default context


 On Tue, Dec 8, 2009 at 3:39 AM, Thiago H. de Paula Figueiredo
 thiag...@gmail.com wrote:
 Em Tue, 08 Dec 2009 04:22:58 -0200, Kalle Korhonen
 kalle.o.korho...@gmail.com escreveu:
 and subsequently, if my page has multiple entry points, I typically
 resort to implementing it in a single onActivate(EventContext
 eventContext) operation containing a big if-else clause.
 That's the recommended way when you have a variable number of activation
 context parameters.
 
 DH's approach looks interesting, but maybe a bit involving with field
 names encoded to the url. Thiago, I know it's the recommended approach
 but I'm just saying it doesn't strike me as the ideal approach.
 
 Since the activation context is anyway sent with an event request (as in
 ?t:ac=mycontext), rather than using the encoded context for rendering,
 wouldn't it be just simpler if that context was used for activating
 the page for the event request and the following redirect for
 rendering would just use whatever context onPassivate() returns?
 The activation context is always what onPassivate() returns. I don't
 understand what is the problem here.
 
 That sounds like I don't understand and don't want to hear about it
 - why bother responding if that's the case? Now I'm not sure if it's
 worth my time to write more details or sample code to describe what I
 mean if you are not interested in explaining or exploring how to do it
 better.
 
 Kalle
 
 -
 To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
 For additional commands, e-mail: users-h...@tapestry.apache.org
 


Best practice for initializing page to default context

2009-12-07 Thread Kalle Korhonen
Most things in T5 are delightfully simple, but I find this
surprisingly difficult: how to best initialize a page to default
context (and redirect to it). Imagine you have a search  result page.
If I access the page without any context I want all records to be
displayed. In onActivate() without parameters I set the context to
*all* and return this to redirect, then I query the database in
setupRender() to initialize the data for the grid. However, sorting
the grid will also cause a call to onActivate() without parameters,
resetting my data to the default context. The parameter-less call to
onActivate() would be harmless if I didn't do a redirect from
onActivate() but then I cannot set the default context and redirect.
In setupRender() I could decide whether redirect is needed or not but
at that time, I'm already committed to rendering the request.

Because events cause a parameterless onActivate()  call, I tend to
reserve onActivate() for possible component/event initialization needs
only and always link to pages with initial context already set. I also
find it roughly impossible to use overloaded versions of onActivate()
and subsequently, if my page has multiple entry points, I typically
resort to implementing it in a single onActivate(EventContext
eventContext) operation containing a big if-else clause. Since the
activation context is anyway sent with an event request (as in
?t:ac=mycontext), rather than using the encoded context for rendering,
wouldn't it be just simpler if that context was used for activating
the page for the event request and the following redirect for
rendering would just use whatever context onPassivate() returns? What
do others think, how do you handle this?

Kalle

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