figleaf-developer Mailing List for Figleaf (Page 2)
Status: Alpha
Brought to you by:
steckman
You can subscribe to this list here.
| 2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(78) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2006 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(1) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|
From: <sam...@ma...> - 2004-07-07 09:22:27
|
Quoting Greg Steckman <ste...@on...>:
> >1. It exposes an implementation detail - what if I want my property's setter
> to
> >call several methods for example?
> >
> You could write a setter method (call it methodA) that calls the several
> methods. Then specify methodA as the property write method.
You could, but you don't have to if you hide what is actually being done.
> >2. It completely bypasses all validation logic that might apply to a
> property -
> >you would have to hope that any UI layer validates before calling read or
> write,
> >resulting in the developer not being able to guarantee that their
> (business)
> >validation logic would ever be invoked.
> >
>
> Yes, this is why I preferred the other approach of having validation
> occur automatically. It would either be implemented directly by the
> writer of the Informative, or we could do some type of proxying to
> implement it.
Which you cannot enforce if you expose a Method without some complex proxying.
What benifit does exposing the Method have over having the PropertDescriptor
execute the method for us?
>
> >3. Calling a property becomes more complex than needed
> >
> >I prefer the following solution - we add the following method to
> PropertyDescriptor:
> >
> > /**
> > * Invokes validation without setting the value - useful in UI's to
> provide
> > * immediate feedback to the user of invalid values etc.
> > */
> > boolean isValid(Object newValue, ValidationCallback callback);
> >
> >Next, get rid of getReadMethod and getWriteMethod and instead use:
> >
> > Object read();
> >
> > /**
> > * Sets a new value if valid, else callback.invalid() will be called
> > */
> > void write(Object newValue, ValidationCallback callback);
> >
> >For this PropertyDescriptor would have to become a per-instance object
> (which I
> >don't think is a big problem). If we wanted to keep PropertyDescriptor as
> >per-class, then we'd need to pass in an additional parameter into
> read/write.
> >Also note that there is now no need to expose ValidationRules outside of
> the
> >PropertyDescriptor, helping keep the responsibility for determining an
> Objects
> >validity inside the Object itself.
> Let's consider an implementation of a class that implements the
> Informative interface itself.
>
> Case A: Current implementation + modify PropertyDescriptor to add the
> methods:
> void addValidationListener(ValidationListener listener);
> void removeValidationListener(ValidationListener listener);
What would listen to validation events? The GUI - in fact parts of the GUI would
only need to listen to specific properties. As such I think a more fine-grained
validation listener approach would benifit us - allow validation callbacks on
specific property calls. By passing a callback into a validation call (or even
as a result of calling get) the Informative object doesn't need to manage
multiple ValidationListeners, and the GUI doesn't have to worry about detaching
listeners
> public class MyInformative implements Informative{
> PropertyDescriptor[] pds;
> Object propA;
>
> public void setPropertyA(Object value){
> boolean invalid=true;
>
> //do some validation logic
> //set invalid as required
> if(invalid){
> pds[0].fireValidationInvalid("reason string goes here");
> }else{
> propA=value;
> pds[0].fireValidationValid();
> }
> }
You are now poluting property accesing methods with figleaf dependant code.
Developers now need to explicitly invoke validation for all their set methods,
and have to do so in a way that ties their object to figleaf. I prefer to deal
with validation at the point we describe the properties (or general 'action'
methods) to the figleaf system, that is in the PropertyDescriptor.
> public Object getPropertyA(){return propA;}
>
> PropertyDescriptor[] getPropertyDesctiptors(){
> pds=new PropertyDescriptor[1];
> pds[0]=new PropertyDescriptorImpl(Object.class, getClass(),
>
> getMethod("getPropertyA", null).
>
> getMethod("setPropertyA", new Class[]{Object.class}),
>
> "Property A",
>
> "Description of Property A");
> return pds;
> }
> }
>
>
> Case B: The implementation you suggest:
>
> public class MyInformative implements Informative{
> public void setPropertyA(Object value){...}
> public Object getPropertyA(){...}
>
> PropertyDescriptor[] getPropertyDesctiptors(){
> PropertyDescriptor[] pds=new PropertyDescriptor[1];
> pds[0]=new MyInformativePropertyDescriptor(
>
> getMethod("getPropertyA", null).
>
> getMethod("setPropertyA", new Class[]{Object.class}),
>
> "Property A",
>
> "Description of Property A");
> return pds;
> }
>
> class MyInformativePropertyDescriptor implements PropertyDescriptor{
> private Method readMethod, writeMethod;
> private String name, description;
>
> MyInformativePropertyDescriptor(Method readMethod, Method
> writeMethod, String name, String desc){
> this.readMethod=readMethod;
> this.writeMethod=writeMethod;
> this.name=name;
> this.description=desc;
> }
>
> public Object read(){
> return readMethod.invoke(this.MyInformative, null);
> }
>
> public void write(Object o, ValidationCallback callback){
> //do validation
>
> if(valid){
> writeMethod.invoke(this.MyInformative, new Object[]{o});
> }
> }
> }
> }
I wouldn't call methods like that - I'd call them directly on the object to gain
compile-time checking and simpler looking code.
> In the first case I used an object that's defined elsewhere so that
> makes the code look shorter but in reality it probably won't be. I
> prefer the listener approach of Case A because I think it has more
> flexibility and is a standard pattern so users will understand the API
> right away.
But users have to define validation logic and invoke it themselves - my approach
just has them define validation rules, and everything else is handled for them,
making thw system simpler for them to use.
> If we do that, I don't see the benefit of not using Methods
> in the PropertyDescriptor.
You then require that figleaf has to proxy method calls. You also expose
implementation detail. The UI then has to invoke the method itself, catch the
exceptions etc. What about remoting? What if invoking the property actually
invokes a webservices call? Now we have to create a fake Method which we can't
easily do (Method is a final class).
> I also prefer using the Methods because I
> think they are more flexible. They can reference any method in any class
> without having to implement an extra class just for redirection.
I don't agree really - where would you need an object to expose the method of
another class?
> If a class doesn't implement Informative itself, then everything needs
> to be handled by the Proxy in either case.
>
> Greg
Which I already do - exposing the method is no simpler than directly invoking it
inside the property, I've had proxies working in both ways.
In reality validation at a property-by-property basis is simple from developer
terms, but is often not good enough, for example what if a customer bean has 3
telephone number fields, and one of them has to be non-null? Neither of our
approaches work, as we are still validating on a property-by-property basis -
even a final-mile validation check on the object as a whole happens to late, as
by then the object might be in an invalid state.
I say lets rethink the problem. We need to not only validate a single property
change, but the validaity of a set of changes on an object. Lets add the
following method to Informative:
boolean commitChanges(ChangeSet set)
Lets keep your add and remove validation listeners on Informative. A ChangeSet
is a set of changes on an object, and now becomes the only mechanism by which
figleaf writes changes to an object - it consists of a series of property change
messages. I'll knock up a prototype in the next day or so and see if its a
workable idea.
sam
http://www.magpiebrain.com/
|
|
From: Greg S. <ste...@on...> - 2004-07-06 17:03:21
|
sam...@ma... wrote:
>In the light of my work on Validation, I think we need to re-evaluate the
>PropertyDescriptor, at least the way in which it exposes the read and write methods.
>
>If we take the view that Validation is used to verify whether or not a value is
>valid for a domain object, then it is fair to say the validation is business
>logic. Currently, to invoke a property, you call one of these two methods on
>PropertyDescriptor:
>
> Method getReadMethod();
>
> Method getWriteMethod();
>
>I dislike this for a variety of reasons:
>
>1. It exposes an implementation detail - what if I want my property's setter to
>call several methods for example?
>
You could write a setter method (call it methodA) that calls the several
methods. Then specify methodA as the property write method.
>2. It completely bypasses all validation logic that might apply to a property -
>you would have to hope that any UI layer validates before calling read or write,
>resulting in the developer not being able to guarantee that their (business)
>validation logic would ever be invoked.
>
Yes, this is why I preferred the other approach of having validation
occur automatically. It would either be implemented directly by the
writer of the Informative, or we could do some type of proxying to
implement it.
>3. Calling a property becomes more complex than needed
>
>I prefer the following solution - we add the following method to PropertyDescriptor:
>
> /**
> * Invokes validation without setting the value - useful in UI's to provide
> * immediate feedback to the user of invalid values etc.
> */
> boolean isValid(Object newValue, ValidationCallback callback);
>
>Next, get rid of getReadMethod and getWriteMethod and instead use:
>
> Object read();
>
> /**
> * Sets a new value if valid, else callback.invalid() will be called
> */
> void write(Object newValue, ValidationCallback callback);
>
>For this PropertyDescriptor would have to become a per-instance object (which I
>don't think is a big problem). If we wanted to keep PropertyDescriptor as
>per-class, then we'd need to pass in an additional parameter into read/write.
>Also note that there is now no need to expose ValidationRules outside of the
>PropertyDescriptor, helping keep the responsibility for determining an Objects
>validity inside the Object itself.
>
>sam
>
>
>
>
Let's consider an implementation of a class that implements the
Informative interface itself.
Case A: Current implementation + modify PropertyDescriptor to add the
methods:
void addValidationListener(ValidationListener listener);
void removeValidationListener(ValidationListener listener);
public class MyInformative implements Informative{
PropertyDescriptor[] pds;
Object propA;
public void setPropertyA(Object value){
boolean invalid=true;
//do some validation logic
//set invalid as required
if(invalid){
pds[0].fireValidationInvalid("reason string goes here");
}else{
propA=value;
pds[0].fireValidationValid();
}
}
public Object getPropertyA(){return propA;}
PropertyDescriptor[] getPropertyDesctiptors(){
pds=new PropertyDescriptor[1];
pds[0]=new PropertyDescriptorImpl(Object.class, getClass(),
getMethod("getPropertyA", null).
getMethod("setPropertyA", new Class[]{Object.class}),
"Property A",
"Description of Property A");
return pds;
}
}
Case B: The implementation you suggest:
public class MyInformative implements Informative{
public void setPropertyA(Object value){...}
public Object getPropertyA(){...}
PropertyDescriptor[] getPropertyDesctiptors(){
PropertyDescriptor[] pds=new PropertyDescriptor[1];
pds[0]=new MyInformativePropertyDescriptor(
getMethod("getPropertyA", null).
getMethod("setPropertyA", new Class[]{Object.class}),
"Property A",
"Description of Property A");
return pds;
}
class MyInformativePropertyDescriptor implements PropertyDescriptor{
private Method readMethod, writeMethod;
private String name, description;
MyInformativePropertyDescriptor(Method readMethod, Method
writeMethod, String name, String desc){
this.readMethod=readMethod;
this.writeMethod=writeMethod;
this.name=name;
this.description=desc;
}
public Object read(){
return readMethod.invoke(this.MyInformative, null);
}
public void write(Object o, ValidationCallback callback){
//do validation
if(valid){
writeMethod.invoke(this.MyInformative, new Object[]{o});
}
}
}
}
In the first case I used an object that's defined elsewhere so that
makes the code look shorter but in reality it probably won't be. I
prefer the listener approach of Case A because I think it has more
flexibility and is a standard pattern so users will understand the API
right away. If we do that, I don't see the benefit of not using Methods
in the PropertyDescriptor. I also prefer using the Methods because I
think they are more flexible. They can reference any method in any class
without having to implement an extra class just for redirection.
If a class doesn't implement Informative itself, then everything needs
to be handled by the Proxy in either case.
Greg
|
|
From: Greg S. <ste...@on...> - 2004-07-06 16:10:01
|
sam...@ma... wrote:
>I've been kicking around some ideas for the validation mechanism.
>
>My plan is for a ValidationRule object, which is associated with a
>ProperyDescriptor. A CompositeValidationRule will allow for multiple
>ValidationRules to apply to a single property. The interface defines one method:
>
>boolean validate(Object value, ValidationCallback callback);
>
>If it validates, true is returned. If it doesn't, false is returned. In either
>case a callbackmethod will be called - ValidationCallback looks like this:
>
>void valid(PropertyDescriptor property, Object validValue);
>void invalid(PropertyDescriptor property, Object invalidValue, String errorMessage);
>
>The method names suck, but I think the idea is sound. The GUI can then register
>a callback to automatically seed the UI with validation messages.
>
>
First let me make sure I understand correctly:
Say the GUI has a new value just entered into a text field for a
property. It will call validate(newValue, mycallback) on the
ValidationRule returned by the PropertyDescriptor. If true is returned,
it will proceed to call the PropertyDescriptor's write method. If false,
it'll do nothing, but the invalid() callback should display an error
message (or some other action).
>We could take this a step further - when calling the write methods,
>automatically invoke the validation firing callback methods as required.
>
I think this is actually a nicer approach. Then the client code doesn't
have to worry about calling validate(). It just will use the write
method, and if the data is valid it is accepted, if not it is rejected.
Then the listener will register itself with the PropertyDescriptor
before doing anything such as:
propertyDescriptor.addValidationListener(
new ValidationListener(){
public void valid(PropertyDescriptor property, Object validValue){
//do something
}
public void invalid(PropertyDescriptor property, Object
invalidValue, String errorMessage){
//do something
//display error message
}
}
);
Also this allows multiple validation listeners to be registered for any
given property descriptor. That would be useful if there should be a
generic listener set on all properties for logging or something else.
Finally, I would suggest a global validation check which would have to
be explicitly called on the object. This would be to validate the object
before final saving, to handle validation checks that can only be
performed on a combination of properties. Then the gui/client code would
look something like:
public void saveObject(Informative inf){
String[] ErrorMessages=inf.validate();
if(ErrorMessages != null){
//display errors
}else{
//no errors
objectManager.save(inf);
}
}
>
>Ideas for ValidationRules:
>
>RegexpValidationRule <- Passes if string value of Object matches the whole regexp
>CompositeValidationRule <- Wraps several rules
>DateValidationRule <- Validates date regions
>NumberValidationRule <- Ditto for dates
>
>
For a composite rule, should we only allow logical AND's of rules? That
is they must all pass for validation to succeed. Or should we also
support logical combinations of rules? That'll make the API more complex
but also more powerful. We can always start with AND only, and then add
on to it later.
>We'll also want to add an AbstractValidationRule to make it easy for users to
>add their own - it'll simply add the callback support.
>
>This is all fairly easy to write and test so shouldn't take me too long to write
>- I can probably check in the interfaces for you to hook into before the weekend
>(and probably at least one concrete implementation).
>
>The only question to answer now, is where do we get the rules from? The obvious
>place is a PropertyDescriptor - lets add this method:
>
>ValidationRule getValidationRule();
>
>sam
>http://www.magpiebrain.com/
>
>
Yes I think that is the logical place to get it from.
And the "global" check could be placed in the Validatable interface as
it was at some point in the past.
Greg
|
|
From: <sam...@ma...> - 2004-07-06 12:07:04
|
In the light of my work on Validation, I think we need to re-evaluate the PropertyDescriptor, at least the way in which it exposes the read and write methods. If we take the view that Validation is used to verify whether or not a value is valid for a domain object, then it is fair to say the validation is business logic. Currently, to invoke a property, you call one of these two methods on PropertyDescriptor: Method getReadMethod(); Method getWriteMethod(); I dislike this for a variety of reasons: 1. It exposes an implementation detail - what if I want my property's setter to call several methods for example? 2. It completely bypasses all validation logic that might apply to a property - you would have to hope that any UI layer validates before calling read or write, resulting in the developer not being able to guarantee that their (business) validation logic would ever be invoked. 3. Calling a property becomes more complex than needed I prefer the following solution - we add the following method to PropertyDescriptor: /** * Invokes validation without setting the value - useful in UI's to provide * immediate feedback to the user of invalid values etc. */ boolean isValid(Object newValue, ValidationCallback callback); Next, get rid of getReadMethod and getWriteMethod and instead use: Object read(); /** * Sets a new value if valid, else callback.invalid() will be called */ void write(Object newValue, ValidationCallback callback); For this PropertyDescriptor would have to become a per-instance object (which I don't think is a big problem). If we wanted to keep PropertyDescriptor as per-class, then we'd need to pass in an additional parameter into read/write. Also note that there is now no need to expose ValidationRules outside of the PropertyDescriptor, helping keep the responsibility for determining an Objects validity inside the Object itself. sam |
|
From: <sam...@ma...> - 2004-07-06 09:01:30
|
I've been kicking around some ideas for the validation mechanism. My plan is for a ValidationRule object, which is associated with a ProperyDescriptor. A CompositeValidationRule will allow for multiple ValidationRules to apply to a single property. The interface defines one method: boolean validate(Object value, ValidationCallback callback); If it validates, true is returned. If it doesn't, false is returned. In either case a callbackmethod will be called - ValidationCallback looks like this: void valid(PropertyDescriptor property, Object validValue); void invalid(PropertyDescriptor property, Object invalidValue, String errorMessage); The method names suck, but I think the idea is sound. The GUI can then register a callback to automatically seed the UI with validation messages. We could take this a step further - when calling the write methods, automatically invoke the validation firing callback methods as required. Ideas for ValidationRules: RegexpValidationRule <- Passes if string value of Object matches the whole regexp CompositeValidationRule <- Wraps several rules DateValidationRule <- Validates date regions NumberValidationRule <- Ditto for dates We'll also want to add an AbstractValidationRule to make it easy for users to add their own - it'll simply add the callback support. This is all fairly easy to write and test so shouldn't take me too long to write - I can probably check in the interfaces for you to hook into before the weekend (and probably at least one concrete implementation). The only question to answer now, is where do we get the rules from? The obvious place is a PropertyDescriptor - lets add this method: ValidationRule getValidationRule(); sam http://www.magpiebrain.com/ |
|
From: <sam...@ma...> - 2004-07-05 15:57:55
|
Quoting Greg Steckman <ste...@on...>: > I haven't had a chance to look at your introspection stuff yet, but now > it will proxy both superinterfaces of Informative? Erm, give it an object, and it returns an object which subclasses the original, and implements Informative. > The next thing might > be to put the validatable interface back in as a super of Informative. I'm guessing Validatable at an Object/class level needs to be backed up with validation information on a property by property basis - a library of default validation types would be good (e.g. String at least this long). > The other thing would be to cover the case where a class implements some > but not all of the superinterfaces of Informaive. For example, I > implement ClassDescriptor but not Observable. Then have the proxy just > do its magic for Observable and defer ClassDescriptor calls to the real > object. Currently I use Informative if I can find it, or else I create a default implementation (this is working - also if you have DomainObject and I can find DomainObjectInformative in the same package I use that too). Next step would be to add the same support for Observable, that is provide the default implementation if the given domain object doesn't implement Observable - currently I always implement Observable for the user. Once I get that working I'll start looking at validation - I guess I can quickly create a library of useful validation types. I won't handle validation of the String->Integer nature (that is typing text into a UI that maps to an Integer property) as that is something the UI should do itself. We also need to consider how the validation information impacts on the UI - for example a simple "String should be X characters long" could be implemented in the UI using a fixed input text field, but "If this other object is X, this value has to be greater than Y" cannot easily be constrained - in such a case it will simply have to call "property.valaidate()" or whatever and return the error message. I think the simple approach will be to have individual properties validatable, and have an isValid:String method which returns an error message if invalid. We could then throw a runtime ValidationException when invoking the write method if its not valid. sam http://www.magpiebrain.com/ |
|
From: Greg S. <ste...@on...> - 2004-07-05 15:40:27
|
sam...@ma... wrote: >Quoting Greg Steckman <ste...@on...>: > > > > >>>Well I don't think it can hurt to have this as a seperate class which >>> >>> >>different >> >> >>>UI mechanisms can reuse - I'll take your code as a base and write one with >>> >>> >>full >> >> >>>test coverage so we can work out any little kinks that might exist. >>> >>> >>>sam >>>http://www.magpiebrain.com/ >>> >>> > > > >>I'll break it out. I was just getting started on the tests. >> >>Greg >> >> > >OK, cool - I'd started a little bit of work on this but it sounds like you've >got it all in hand. What do we need next? > >sam >http://www.magpiebrain.com/ > > > > I haven't had a chance to look at your introspection stuff yet, but now it will proxy both superinterfaces of Informative? The next thing might be to put the validatable interface back in as a super of Informative. The other thing would be to cover the case where a class implements some but not all of the superinterfaces of Informaive. For example, I implement ClassDescriptor but not Observable. Then have the proxy just do its magic for Observable and defer ClassDescriptor calls to the real object. Greg |
|
From: <sam...@ma...> - 2004-07-05 15:28:02
|
Quoting Greg Steckman <ste...@on...>: > >Well I don't think it can hurt to have this as a seperate class which > different > >UI mechanisms can reuse - I'll take your code as a base and write one with > full > >test coverage so we can work out any little kinks that might exist. > > > > > >sam > >http://www.magpiebrain.com/ > I'll break it out. I was just getting started on the tests. > > Greg OK, cool - I'd started a little bit of work on this but it sounds like you've got it all in hand. What do we need next? sam http://www.magpiebrain.com/ |
|
From: <sam...@ma...> - 2004-07-05 15:26:03
|
Quoting Greg Steckman <ste...@on...>: > > > >We really need to consider if we require a PersistenceStrategy - e.g. save > on > >edit (e.g. call a set and its persisted ala Entity beans) or save on > specific > >call (hit the save button). This can probably wait till 0.2 or later, but > we > >need to fix on a default strategy for this version. > > > > > > > For now I put in a save button on the Form view. I think this meshes > well with validation, because when the user wants to save, the object > can be validated once and if it fails an error presented. I can imagine > situations where an object needs to be validated after all its data is > entered, rather than after each field is changed. Agreed - for this version at least it's a sensible approach. It also meshes well with the persistence implementations we are looking at providing (Hibernate, Prevalyer etc). > >Do we want to implement our own lazy loading mechanism, or can we just > leave > >this down to whichever persistence mechanism we want? Thinking about the UI, > its > >up to specific implementations as to what objects they want to load at any > one > >time - one UI when loading an object may want just that object, another > might > >want to load all members of that object. > > > I think when we get to implementing remoting, we'll have to put in some > type of lazy loading and caching implementation. That implementation > should then be usable on the server or remote end. It's certainly > something I don't want to start implementing at this point yet. Since > I've used Hibernate, I think there will be some issues with how it does > lazy loading and the requirement to keep sessions open. Also there is > the case of needing to introspect to make informative objects when > lazily loaded. If our container doesn't control the loading, I don't > know how to know when to introspect the object. Sounds sensible - lets worry about it when it happens sam http://www.magpiebrain.com/ |
|
From: <sam...@ma...> - 2004-07-05 15:24:06
|
Quoting Greg Steckman <ste...@on...>: > >Definately. A case in point would be the Image manager app I'd like to code > by > >way of an acceptance test - in once object you have a String which > represents a > >filename, but I want the GUI to provide a file browser. > > > > > > > What I meant is, do you have any ideas of how to do the configuration > while still hard coding defaults? Sure - internall the SwingViewer has its default mapping (e.g. java.lang.String -> JTextField etc). On construction you pass in your own map - if the key already exists for a mapping, it gets replaced, otherwise it gets added. sam http://www.magpiebrain.com/ |
|
From: Greg S. <ste...@on...> - 2004-07-05 15:21:41
|
sam...@ma... wrote: >Quoting Greg Steckman <ste...@on...>: > > > >>sam...@ma... wrote: >> >> >> >>>Playing around with your SwingViewer last night, I had some problems getting >>> >>> >>my >> >> >>>registered views to be picked up. I think this is down to the fact that I >>> >>> >>didn't >> >> >>>register specific classes with views - that is I registered Informative not >>> >>> >>my >> >> >>>applications ImageAlbum class. I think we could do with defining a >>> >>> >>ViewRegistry >> >> >>>class which could be used with any viewing mechanism. It would define the >>>following methods: >>> >>> >>> > ><snip> > > > >>The implementation is already written to do precisely this. It first >>traverses the complete class hierarchy, if none found it then traverses >>the interface hierarchy in a way to try and find the closest >>superinterface (in terms of inheritence distance) for which there is a >>view registered. I was going to write some test cases for it next >>though...because it hasn't been tested on anything but the >>InformativeDescriptorImpl class. That said, I registered a view for >>Informative, and it found it for my InformativeDesctiprorImpl which >>implements Informative directly, so I'm not sure why it didn't work on >>your ImageAlbum class. Perhaps once I write a test I will see... >> >> > >Well I don't think it can hurt to have this as a seperate class which different >UI mechanisms can reuse - I'll take your code as a base and write one with full >test coverage so we can work out any little kinks that might exist. > > >sam >http://www.magpiebrain.com/ > > > > I'll break it out. I was just getting started on the tests. Greg |
|
From: Greg S. <ste...@on...> - 2004-07-05 15:20:02
|
sam...@ma... wrote: > >Talking to a colleague he had an idea about multiple views matching for a single >class (e.g. its super class and its interfaces have view mappings) that we could >decide to display both - each view represents some facet of the underlying >object (there is no overlap in behaviour). Not sure how feasible that is though >- I think we should keep things simple for this version! > > > > Yes, let's keep it simple for now! Greg |
|
From: Greg S. <ste...@on...> - 2004-07-05 15:18:37
|
sam...@ma... wrote: >Quoting Greg Steckman <ste...@on...>: > > > >>These two are almost identical now. The difference I can see is that >>Bootstrapper is a singleton while ConfigurableBootstrapper is not, and >>ConfigurableBootstrapper runs the bean named Runnable rather than from a >>command line parameter. >> >>Bootstrapper is a singleton because I needed a way in the GUI code to >>get the ObjectManager without relying on the Spring ApplicationContext. >> >>Greg >> >> >> > >We can use Spring to 'inject' the ObjectManager into your SwingViewer - this is >how I handle my ConfigurationViewer. I don't rely on Spring for this either, I >just rely on being able to get an ObjectManager from whatever >ApplicationConfiguration I'm using. > >sam >http://www.magpiebrain.com/ > > > > I will kill ConfigurableBootstrapper then. Spring does inject ObjectManager into SwingViewer. The problem was with objects not created by Spring that still needed access to the ObjectManager. How does it get it? I opted to make Bootstrapper a singleton for that purpose. I needed access to ObjectManager for the "save" button so it could persist objects. Greg |
|
From: <sam...@ma...> - 2004-07-05 15:17:33
|
Quoting Greg Steckman <ste...@on...>: > sam...@ma... wrote: > > >Playing around with your SwingViewer last night, I had some problems getting > my > >registered views to be picked up. I think this is down to the fact that I > didn't > >register specific classes with views - that is I registered Informative not > my > >applications ImageAlbum class. I think we could do with defining a > ViewRegistry > >class which could be used with any viewing mechanism. It would define the > >following methods: > > <snip> > The implementation is already written to do precisely this. It first > traverses the complete class hierarchy, if none found it then traverses > the interface hierarchy in a way to try and find the closest > superinterface (in terms of inheritence distance) for which there is a > view registered. I was going to write some test cases for it next > though...because it hasn't been tested on anything but the > InformativeDescriptorImpl class. That said, I registered a view for > Informative, and it found it for my InformativeDesctiprorImpl which > implements Informative directly, so I'm not sure why it didn't work on > your ImageAlbum class. Perhaps once I write a test I will see... Well I don't think it can hurt to have this as a seperate class which different UI mechanisms can reuse - I'll take your code as a base and write one with full test coverage so we can work out any little kinks that might exist. sam http://www.magpiebrain.com/ |
|
From: Greg S. <ste...@on...> - 2004-07-05 15:14:41
|
sam...@ma... wrote: >Quoting Greg Steckman <ste...@on...>: > > >>Maybe. But because I had the "Informative introspect(Object obj);" >>method in there that brought it out of being purely for persistence. >>Also I figured there's probably going to be non-persistence stuff we'll >>need to put somewhere (logins, security?). >> >> > > >If that happens we'll need to abstract out the two layers - developers might >want to change persistence mechanisms and security systems independantly of each >other. > > > Very possible. >>The introspect method was because I thought about the case if a POJO >>creates an object (using new) and returns it from a method call. Then >>the UI will need to get the ObjectManager to proxy it and turn it into >>an Informative so it can be displayed. Some thought still needs to be >>given to when such an object should be persisted: as soon as its called, >>force the user to press a save button, or never...? >> >> > >We really need to consider if we require a PersistenceStrategy - e.g. save on >edit (e.g. call a set and its persisted ala Entity beans) or save on specific >call (hit the save button). This can probably wait till 0.2 or later, but we >need to fix on a default strategy for this version. > > > For now I put in a save button on the Form view. I think this meshes well with validation, because when the user wants to save, the object can be validated once and if it fails an error presented. I can imagine situations where an object needs to be validated after all its data is entered, rather than after each field is changed. >>It'll probably be needed by lazy initializing proxies and collections. >>They will only know the id and which class they represent. When they >>need to load their data they can call this method and get a fully >>populated object back. Perhaps it doesn't belong in the ObjectManager >>"layer" but rather in a persistence and proxy layer. >> >> > >Do we want to implement our own lazy loading mechanism, or can we just leave >this down to whichever persistence mechanism we want? Thinking about the UI, its >up to specific implementations as to what objects they want to load at any one >time - one UI when loading an object may want just that object, another might >want to load all members of that object. > > > > I think when we get to implementing remoting, we'll have to put in some type of lazy loading and caching implementation. That implementation should then be usable on the server or remote end. It's certainly something I don't want to start implementing at this point yet. Since I've used Hibernate, I think there will be some issues with how it does lazy loading and the requirement to keep sessions open. Also there is the case of needing to introspect to make informative objects when lazily loaded. If our container doesn't control the loading, I don't know how to know when to introspect the object. Greg |
|
From: Greg S. <ste...@on...> - 2004-07-05 15:05:58
|
sam...@ma... wrote: > >I have a few concerns with using setter-based injection - primarily the fact >that you can end up creating a bean which cannot then be used without setters >being called (sometimes in a certain, special order). A case in point would be >the FormView, where if you call setViewObject before setSwingViewer, you get a >null pointer exception. By passing in all dependencies at construction not only >do you ensure the object starts in a valid, ready to use state, but you also >make sure that setters which should not be used at any other time other that >initialisation are not exposed - what happens for example if something calls >setSwingViewer after the FormView has already been displayed? > > > Yeah I did some cleanup of that code last night. Should be a little harder to get a NPE now. There are still two setters required if you want to use a view as a field as it requires two extra properties, so the constructor for all views only have 2 parameters to be consistent (since they are created via reflection). I'll have to think about this one more if it's a problem still. >>Do you have in mind how to allow the developer to override the default >>view types and to provide additional view types? >> >> > >Definately. A case in point would be the Image manager app I'd like to code by >way of an acceptance test - in once object you have a String which represents a >filename, but I want the GUI to provide a file browser. > > > What I meant is, do you have any ideas of how to do the configuration while still hard coding defaults? Greg |
|
From: Greg S. <ste...@on...> - 2004-07-05 14:57:35
|
sam...@ma... wrote: >Playing around with your SwingViewer last night, I had some problems getting my >registered views to be picked up. I think this is down to the fact that I didn't >register specific classes with views - that is I registered Informative not my >applications ImageAlbum class. I think we could do with defining a ViewRegistry >class which could be used with any viewing mechanism. It would define the >following methods: > >void addViewMapping(Class clazz, View view); > >View getView(Class clazz); > >And would define the following behaviour: > >1. If a direct mapping exists, it returns that view >2. If no direct mapping exists, it looks for mappings for its immediate >superclass, then interfaces >3. If no mapping exists for its immediate superclass or interfaces, it >recursively traverses the superclass structure looking for a match >4. Finally if no match can be found null is returned. > >This allows us to assume a mapping for Informative but let the user override >this. It also lets us map to classes or interfaces - your code seems to only >support mapping to interfaces. This should be a very simple thing to write and >can drop right into your code (it should even make your code a little simpler) - >I'm happy to work on this while you concentrate on the UI if you want. > >sam >http://www.magpiebrain.com/ > > > > The implementation is already written to do precisely this. It first traverses the complete class hierarchy, if none found it then traverses the interface hierarchy in a way to try and find the closest superinterface (in terms of inheritence distance) for which there is a view registered. I was going to write some test cases for it next though...because it hasn't been tested on anything but the InformativeDescriptorImpl class. That said, I registered a view for Informative, and it found it for my InformativeDesctiprorImpl which implements Informative directly, so I'm not sure why it didn't work on your ImageAlbum class. Perhaps once I write a test I will see... Greg |
|
From: <sam...@ma...> - 2004-07-05 10:50:30
|
Quoting sam...@ma...: > Playing around with your SwingViewer last night, I had some problems getting > my > registered views to be picked up. I think this is down to the fact that I > didn't > register specific classes with views - that is I registered Informative not > my > applications ImageAlbum class. I think we could do with defining a > ViewRegistry > class which could be used with any viewing mechanism. It would define the > following methods: > > void addViewMapping(Class clazz, View view); > > View getView(Class clazz); > > And would define the following behaviour: > > 1. If a direct mapping exists, it returns that view > 2. If no direct mapping exists, it looks for mappings for its immediate > superclass, then interfaces > 3. If no mapping exists for its immediate superclass or interfaces, it > recursively traverses the superclass structure looking for a match > 4. Finally if no match can be found null is returned. > > This allows us to assume a mapping for Informative but let the user override > this. It also lets us map to classes or interfaces - your code seems to only > support mapping to interfaces. This should be a very simple thing to write > and > can drop right into your code (it should even make your code a little > simpler) - > I'm happy to work on this while you concentrate on the UI if you want. Talking to a colleague he had an idea about multiple views matching for a single class (e.g. its super class and its interfaces have view mappings) that we could decide to display both - each view represents some facet of the underlying object (there is no overlap in behaviour). Not sure how feasible that is though - I think we should keep things simple for this version! sam http://www.magpiebrain.com/ |
|
From: <sam...@ma...> - 2004-07-05 08:36:56
|
Quoting Greg Steckman <ste...@on...>: > These two are almost identical now. The difference I can see is that > Bootstrapper is a singleton while ConfigurableBootstrapper is not, and > ConfigurableBootstrapper runs the bean named Runnable rather than from a > command line parameter. > > Bootstrapper is a singleton because I needed a way in the GUI code to > get the ObjectManager without relying on the Spring ApplicationContext. > > Greg > We can use Spring to 'inject' the ObjectManager into your SwingViewer - this is how I handle my ConfigurationViewer. I don't rely on Spring for this either, I just rely on being able to get an ObjectManager from whatever ApplicationConfiguration I'm using. sam http://www.magpiebrain.com/ |
|
From: <sam...@ma...> - 2004-07-05 08:35:29
|
Quoting Greg Steckman <ste...@on...>: > My last two e-mails didn't seem to make it to the list for some reason... > Feel free to kill it then - I'm assuming you are using the SpringApplicationConfiguration in Bootstrapper now? sam http://www.magpiebrain.com/ |
|
From: <sam...@ma...> - 2004-07-05 08:34:10
|
Quoting Greg Steckman <ste...@on...>:
> sam...@ma... wrote:
>
> >Firstly, is this better off being named something like PersistenceLayer?
> >
> >
> >
> Maybe. But because I had the "Informative introspect(Object obj);"
> method in there that brought it out of being purely for persistence.
> Also I figured there's probably going to be non-persistence stuff we'll
> need to put somewhere (logins, security?).
If that happens we'll need to abstract out the two layers - developers might
want to change persistence mechanisms and security systems independantly of each
other.
> >Secondly, there seems to be some overlapping methods, for example (this from
> my
> >TransientObjectManager):
> >
> >public Informative newInstance(Class clazz) {
> > return informativeFactory.createInformative(clazz.newInstance());
> >}
> >
> >public Informative introspect(Object obj) {
> > return factory.createInformative(obj);
> >}
> >
> >Are both needed? I'm keen not to put in a requirement for methods to be
> >implemented that we might not even end up using.
> >
> >
>
> The introspect method was because I thought about the case if a POJO
> creates an object (using new) and returns it from a method call. Then
> the UI will need to get the ObjectManager to proxy it and turn it into
> an Informative so it can be displayed. Some thought still needs to be
> given to when such an object should be persisted: as soon as its called,
> force the user to press a save button, or never...?
We really need to consider if we require a PersistenceStrategy - e.g. save on
edit (e.g. call a set and its persisted ala Entity beans) or save on specific
call (hit the save button). This can probably wait till 0.2 or later, but we
need to fix on a default strategy for this version.
> Then there's the
> issue of what if that happens in a transaction. I'm not sure it's
> practical to expect all objects to know nothing about ObjectManager and
> just use new. If we require them to always use the ObjectManager when
> creating a new instance, then the introspect method probably isn't needed.
I would really, really like to have the POJOs not know about an ObjectManager -
it gives them far more than they need. We might choose to expose an
ObjectFactorty to them however (which might just be part of the current
ObjectManager).
> >As for the method:
> >
> >Informative getObject(Class clazz, Serializable id);
> >
> >Do we need this? Surely this should be handled by whatever query mechanism
> we use.
> >
> It'll probably be needed by lazy initializing proxies and collections.
> They will only know the id and which class they represent. When they
> need to load their data they can call this method and get a fully
> populated object back. Perhaps it doesn't belong in the ObjectManager
> "layer" but rather in a persistence and proxy layer.
Do we want to implement our own lazy loading mechanism, or can we just leave
this down to whichever persistence mechanism we want? Thinking about the UI, its
up to specific implementations as to what objects they want to load at any one
time - one UI when loading an object may want just that object, another might
want to load all members of that object.
sam
http://www.magpiebrain.com/
|
|
From: <sam...@ma...> - 2004-07-05 08:28:36
|
Quoting Greg Steckman <ste...@on...>: > sam...@ma... wrote: > > >Right, the InfromativeFactoryImpl now creates proxies as requested. > > > > > We've got an Introspector, InformativeFactory, and ObjectIntrospector > interfaces. I think that's at least 1 too many. ObjectIntrospector has a > method "ClassDescriptor createInformative(Object o)" that doesn't seem > to be right. Shouldn't it return an Informative? > > Is it necessary for the ObjectIntrospector interface to have methods > getDescription, getDisplayName, getProperties and getProperty? Those are > all things that the ClassDescriptor interface provides. I think not, so > it then is essentially the same as Introspector. You're right - this happened due to me having proxies then removing them. I'll look at simplifying this. > >By way of a proof of concept, I've written a new ConfigurableBootstrapper > based > >on your original code. The only difference being that it now hands-off the > >determination of which beans to use to a new ApplicationConfiguration class > >(more specifically a SpringApplicationCnfiguration). The only other > difference > >is that rather than having the user specify which bean is runnable, I've > assumed > >a bean called Runnable - my aim here is for things to be simple. I also > added a > >TransientObjectManager which will hold objects in memory, although at > present it > >doesn't do anything. The SimpleApplicationContext I use looks like this: <snip> > I guess your main objective is to get ConfigurableBootstrapper to the > point to where it can use something other than > SpringApplicationConfiguration? Yes - so the developer can decide to use a SpringConfiguration that lets them do fancy Spring-stuff (they may want to seed the ApplicationContext with their own factories etc) or they may want a dead simple Java-codes configuration to get somethong running quickly (or for testing with). > >Next, I've created a slightly different SwingViewer - my version takes all > >dependencies via constructors (which I think is neater than using setters > to > >define dependencies). I've also defined internally a default view map as I > think > >it's OK for us to assume default views (e.g. text entry fields) and then > allow > >the developer to override it. I've also changed it so the list of > registered > >classes to be passed in. All of this is by way of a test to see how simple > we > >can make application setup - I'd appreciate your comments. My > >ConfigurableSwingViewer isn't as functional as yours - I originally based > the > >code on yours but then realised I didn't need to reinvent the wheel, just > show > >how it could be invoked. > > > Setters somehow seemed more flexible when using dependency injection, > perhaps not. I have a few concerns with using setter-based injection - primarily the fact that you can end up creating a bean which cannot then be used without setters being called (sometimes in a certain, special order). A case in point would be the FormView, where if you call setViewObject before setSwingViewer, you get a null pointer exception. By passing in all dependencies at construction not only do you ensure the object starts in a valid, ready to use state, but you also make sure that setters which should not be used at any other time other that initialisation are not exposed - what happens for example if something calls setSwingViewer after the FormView has already been displayed? > Do you have in mind how to allow the developer to override the default > view types and to provide additional view types? Definately. A case in point would be the Image manager app I'd like to code by way of an acceptance test - in once object you have a String which represents a filename, but I want the GUI to provide a file browser. > Now to the classes. We have to think about this carefully. Currently a > class is represented by an InformativeDescriptor instance. It includes > information about the class (it's class name, a display name, and a > description) as well as methods that can be used to do things with that > class, such as get a new instance of it. Of course that can be done > other ways as long as you have the class name. But I wanted to > eventually be able to associate security information with the class, > such as restricting which users could create a new instance of the class > represented by a given instance of InformativeDescriptor, who can delete > an instance, etc. Additionally I wanted to be able create "context > groups" that are collections of InformativeDescriptors. So for instance > if there are classes A, B, C, and D, I can assign A and B to one context > group and C and D to a second context group. Then I can assign one user > so that on startup he sees the classes in the first context group but > not the second, and vice-versa for a second user. A user could be > allowed to see multiple context groups, for example an administrator or > other super user would have access to all context groups. This all sounds sensible. > I believe this kind of complex security and relational model of classes > is only possible if there is a first-class object representing the > class. My approach was to need only 1 class seeded in the database, and > that was an InformativeDescriptor that describes itself. With that you > can create objects for all other classes needed and setup their > security, assign them to context groups, etc. > > I think the best approach is to write a "system initializer" program > that would take some config file or even user input and setup the system > the first time. This would include classes for which to create default > InformativeDescptors, a super-user account, etc. Long term I think this > is right way to do it because user passwords should be stored in the > database using a 1-way hash or some other scrambling method, not in > clear text, which means we need to write a program to get the first user > in the system so they can log in and create all the others. Which I agree with, however I want to limit the amount of system initialization needed. Systems which require passwords for the first user will typically ship with a default one specified which has to be changed (MySql for example). On the security front, I'm hoping to attend a lecture in the next week or two by Ivan Flechais who has done a lot of research on the AEGIS security system (for which Spring support exists). I'll feedback anything of interest. sam http://www.magpiebrain.com/ |
|
From: <sam...@ma...> - 2004-07-05 08:18:00
|
Playing around with your SwingViewer last night, I had some problems getting my registered views to be picked up. I think this is down to the fact that I didn't register specific classes with views - that is I registered Informative not my applications ImageAlbum class. I think we could do with defining a ViewRegistry class which could be used with any viewing mechanism. It would define the following methods: void addViewMapping(Class clazz, View view); View getView(Class clazz); And would define the following behaviour: 1. If a direct mapping exists, it returns that view 2. If no direct mapping exists, it looks for mappings for its immediate superclass, then interfaces 3. If no mapping exists for its immediate superclass or interfaces, it recursively traverses the superclass structure looking for a match 4. Finally if no match can be found null is returned. This allows us to assume a mapping for Informative but let the user override this. It also lets us map to classes or interfaces - your code seems to only support mapping to interfaces. This should be a very simple thing to write and can drop right into your code (it should even make your code a little simpler) - I'm happy to work on this while you concentrate on the UI if you want. sam http://www.magpiebrain.com/ |
|
From: Greg S. <ste...@on...> - 2004-07-05 06:53:45
|
These two are almost identical now. The difference I can see is that Bootstrapper is a singleton while ConfigurableBootstrapper is not, and ConfigurableBootstrapper runs the bean named Runnable rather than from a command line parameter. Bootstrapper is a singleton because I needed a way in the GUI code to get the ObjectManager without relying on the Spring ApplicationContext. Greg |
|
From: Greg S. <ste...@on...> - 2004-07-05 04:40:56
|
I don't think we need both now. Bootstrapper now runs a "Runnable" automatically rather than having a -bean parameter. Also it is a singleton, which is needed by some of the GUI code at the moment. Greg |