Re: check for changes before commit
Yeah this is a familiar problem, which essentially comes down to the fact that while Cayenne has all the info for change tracking, the API to use it is somewhat complex. In Cayenne 3.1 we provide a better way (that we still need to document), based on lifecycle events and a few extensions in cayenne-lifecycle.jar. So here is how you might approach it: Thank you for your example. I've seen lifecycle mentioned somewhere before but I didn't know how to use it. Well, I still don't know how to get these two lines into my tapestry app where everything is inversion of control: runtime.getDataDomain().getEntityResolver().getCallbackRegistry().addListener(new LastModifiedListener()); runtime.getDataDomain().addFilter(new ChangeSetFilter()); I use tapestr5-cayenne so I simply put a dependency in my pom.xml and use it like e. g. @Inject ObjectContextProvider provider The only class in the TapestryCayenneModule that uses a private field ServerRuntime is DataContextProviderImpl (implements ObjectContextProvider), a service that gets not only bound but also overridden in TapestryCayenneModule, so that I can't override it in my own AppModule.java (where I could add those two lines from your example). :( Just to make sure I'm not mistaken: runtime in your example is ServerRuntime and there should be only one runtime? I need to get the runtime that is used in the TapestryCayenneModule where my Cayenne contexts are coming from? I also tried to use the Cayenne Modeler to add the listener, but it's only possible on the object entity level. Adding an entity-listener to the map.xml produced the warning that DataMap listeners are no longer supported. It would've solved only half of my problem anyway cause I need to add the filter, too. Guess I need to ask some questions on the tapestry mailing list next ... ;) BTW: Where could I find the schema for the map? The link on the cayenne website doesn't work: http://cayenne.apache.org/schema/3.0/modelMap.html -- Not Found Same for http://cayenne.apache.org/schema/3.0/modelMap.xsd -Bjello
Re: check for changes before commit
On 23/11/12 10:22pm, devnull2...@gmx.de wrote: BTW: Where could I find the schema for the map? The link on the cayenne website doesn't work: http://cayenne.apache.org/schema/3.0/modelMap.html -- Not Found Same forhttp://cayenne.apache.org/schema/3.0/modelMap.xsd H good catch. That needs to be added to our site again. It was lost in the CMS transition. Ari -- -- Aristedes Maniatis GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: check for changes before commit
On 23/11/12 10:30pm, Aristedes Maniatis wrote: On 23/11/12 10:22pm, devnull2...@gmx.de wrote: BTW: Where could I find the schema for the map? The link on the cayenne website doesn't work: http://cayenne.apache.org/schema/3.0/modelMap.html -- Not Found Same forhttp://cayenne.apache.org/schema/3.0/modelMap.xsd H good catch. That needs to be added to our site again. It was lost in the CMS transition. Fixed. Ari -- -- Aristedes Maniatis GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: check for changes before commit
It was lost in the CMS transition. the Cayenne Guide for 3.1 http://cayenne.apache.org/docs/3.1/cayenne-guide/index.html also looks quite different from the Guide for 3.0 http://cayenne.apache.org/docs/3.0/cayenne-guide.html IMHO the 3.0 version is much more user friendly than the 3.1 version. Is the change intentional or just some CSS getting lost? The css/cayenne-doc.css for 3.1 is an empty file whereas css/styles.css for 3.0 isn't empty.
Re: check for changes before commit
Hi Bjello, I haven't used the tapestry5-cayenne module, but for initializing some Cayenne (and non-Cayenne) things at application startup, you can use Tapestry's @Startup annotation in your application module: http://tapestry.apache.org/registry-startup.html If the tapestry5-cayenne module provides services, perhaps they are even passable into your startup methods. If not, I suppose you could still initialize the Cayenne runtime there. You mentioned that there should only be one instance of ServerRuntime. You can create multiple instances, but no need to do it for the same configuration. However, if you have multiple models (in the past this would be multiple DataMaps in a single Cayenne model), then you'll need to create a ServerRuntime for each. mrg On Fri, Nov 23, 2012 at 6:22 AM, devnull2...@gmx.de wrote: Yeah this is a familiar problem, which essentially comes down to the fact that while Cayenne has all the info for change tracking, the API to use it is somewhat complex. In Cayenne 3.1 we provide a better way (that we still need to document), based on lifecycle events and a few extensions in cayenne-lifecycle.jar. So here is how you might approach it: Thank you for your example. I've seen lifecycle mentioned somewhere before but I didn't know how to use it. Well, I still don't know how to get these two lines into my tapestry app where everything is inversion of control: runtime.getDataDomain().getEntityResolver().getCallbackRegistry().addListener(new LastModifiedListener()); runtime.getDataDomain().addFilter(new ChangeSetFilter()); I use tapestr5-cayenne so I simply put a dependency in my pom.xml and use it like e. g. @Inject ObjectContextProvider provider The only class in the TapestryCayenneModule that uses a private field ServerRuntime is DataContextProviderImpl (implements ObjectContextProvider), a service that gets not only bound but also overridden in TapestryCayenneModule, so that I can't override it in my own AppModule.java (where I could add those two lines from your example). :( Just to make sure I'm not mistaken: runtime in your example is ServerRuntime and there should be only one runtime? I need to get the runtime that is used in the TapestryCayenneModule where my Cayenne contexts are coming from? I also tried to use the Cayenne Modeler to add the listener, but it's only possible on the object entity level. Adding an entity-listener to the map.xml produced the warning that DataMap listeners are no longer supported. It would've solved only half of my problem anyway cause I need to add the filter, too. Guess I need to ask some questions on the tapestry mailing list next ... ;) BTW: Where could I find the schema for the map? The link on the cayenne website doesn't work: http://cayenne.apache.org/schema/3.0/modelMap.html -- Not Found Same for http://cayenne.apache.org/schema/3.0/modelMap.xsd -Bjello
Re: check for changes before commit
Hi mrg, I haven't used the tapestry5-cayenne module, but for initializing some Cayenne (and non-Cayenne) things at application startup, you can use Tapestry's @Startup annotation in your application module: http://tapestry.apache.org/registry-startup.html yes I tried that yesterday, but it didn't work. I used it like this in AppModule.java: @Startup public void init(@InjectService(DataContext) ObjectContextProvider provider) { ... } This didn't work. I got this exception: Error invoking service contribution method de.myApp.services.AppModule.init(ObjectContextProvider): Unable to delegate method invocation to property 'request' of Proxy for RequestGlobals(org.apache.tapestry5.services.RequestGlobals), because the property is null. Don't know why it's about RequestGlobals here, but I think even though the TapestryCayenneModule is defined at this point, the service ObjectContextProvider is not defined yet. If the tapestry5-cayenne module provides services, perhaps they are even passable into your startup methods. If not, I suppose you could still initialize the Cayenne runtime there. That's what I did after reading your mail and it works :-) Thanks. Now the method looks like this: @Startup public void init() { ServerRuntime runtime = new ServerRuntime(cayenne.xml); runtime.getDataDomain().getEntityResolver().getCallbackRegistry().addListener(new LastModifiedListener()); runtime.getDataDomain().addFilter(new ChangeSetFilter()); } You mentioned that there should only be one instance of ServerRuntime. You can create multiple instances, but no need to do it for the same configuration. However, if you have multiple models (in the past this would be multiple DataMaps in a single Cayenne model), then you'll need to create a ServerRuntime for each. There is only one data-map in my cayenne.map.xml therefore I was under the impression there should be only one runtime. I don't mind a second runtime as long as everything works. Will the other runtime inside the TapestryCayenneModule, which is instantiated later and has all the data contexts I use, know about the filter and listener? Should I add runtime.shutdown(); in my init method to end the first runtime which is not needed afterwards? I'm going to try some more to find out if the last modified filter actually works.
Re: check for changes before commit
Going out on a limb here ... :-) I looked at: http://t5cayenne.saiwai-solutions.com/apidocs/com/googlecode/tapestry5cayenne/services/ObjectContextProvider.html It seems to me you were perhaps injecting the wrong thing into your init()? Maybe this instead: @Startup public void init(ObjectContextProvider provider) { ... } (Don't mix DataContext with ObjectContextProvider.) You can then say provider.newContext(), assuming it passed the correct service in. Then you could have: DataContext context = provider.newContext(); context.getEntityResolver().getCallbackRegistry().addListener(new LastModifiedListener()); context.getParentDataDomain().addFilter(new ChangeSetFilter()); I don't think you need to shutdown your runtime. mrg On Fri, Nov 23, 2012 at 11:04 AM, devnull2...@gmx.de wrote: Hi mrg, I haven't used the tapestry5-cayenne module, but for initializing some Cayenne (and non-Cayenne) things at application startup, you can use Tapestry's @Startup annotation in your application module: http://tapestry.apache.org/registry-startup.html yes I tried that yesterday, but it didn't work. I used it like this in AppModule.java: @Startup public void init(@InjectService(DataContext) ObjectContextProvider provider) { ... } This didn't work. I got this exception: Error invoking service contribution method de.myApp.services.AppModule.init(ObjectContextProvider): Unable to delegate method invocation to property 'request' of Proxy for RequestGlobals(org.apache.tapestry5.services.RequestGlobals), because the property is null. Don't know why it's about RequestGlobals here, but I think even though the TapestryCayenneModule is defined at this point, the service ObjectContextProvider is not defined yet. If the tapestry5-cayenne module provides services, perhaps they are even passable into your startup methods. If not, I suppose you could still initialize the Cayenne runtime there. That's what I did after reading your mail and it works :-) Thanks. Now the method looks like this: @Startup public void init() { ServerRuntime runtime = new ServerRuntime(cayenne.xml); runtime.getDataDomain().getEntityResolver().getCallbackRegistry().addListener(new LastModifiedListener()); runtime.getDataDomain().addFilter(new ChangeSetFilter()); } You mentioned that there should only be one instance of ServerRuntime. You can create multiple instances, but no need to do it for the same configuration. However, if you have multiple models (in the past this would be multiple DataMaps in a single Cayenne model), then you'll need to create a ServerRuntime for each. There is only one data-map in my cayenne.map.xml therefore I was under the impression there should be only one runtime. I don't mind a second runtime as long as everything works. Will the other runtime inside the TapestryCayenneModule, which is instantiated later and has all the data contexts I use, know about the filter and listener? Should I add runtime.shutdown(); in my init method to end the first runtime which is not needed afterwards? I'm going to try some more to find out if the last modified filter actually works.
Re: check for changes before commit
@Startup public void init(ObjectContextProvider provider) { ... } I tried it this way but sadly I get the same exception on startup as before. (Don't mix DataContext with ObjectContextProvider.) DataContext in my example was just the id of the service. From TapestryCayenneModule: public static void bind(ServiceBinder binder) { binder.bind(ObjectContextProvider.class, DataContextProviderImpl.class) .withMarker(Cayenne.class).withId(DataContext); } ^ I'd tried it several ways @InjectService(ObjectContextProvider) ObjectContextProvider provider -- no service with this name @InjectService(DataContext) ObjectContextProvider provider -- TapestryIOCModule.RegistryStartup Construction of service RegistryStartup failed: org.apache.tapestry5.ioc.internal.OperationException @Inject ObjectContextProvider provider -- same exception as above ObjectContextProvider provider -- same exception as above On the tapestry web page you linked they say RegistryStartup occurs after eager loaded services are instantiated. I guess this means it occurs before non-eager services are instantiated and ObjectContextProvider isn't eager loaded. So I'm afraid it's just not available yet. public void init() { ServerRuntime runtime = new ServerRuntime(cayenne.xml); runtime.getDataDomain().getEntityResolver().getCallbackRegistry().addListener(new LastModifiedListener()); runtime.getDataDomain().addFilter(new ChangeSetFilter()); } This way there are no exceptions on startup. I put in some logging and the size of runtime.getDataDomain().getFilters() in the init method is 1 (no surprise). I couldn't find a method to get the number of listeners. But the lastmodified date isn't set when I edit something. So I added some logging output in the insertAudit(DataObject object) of the LastModifiedListener and in the page class in onSuccessFromEditForm(). The insertAudit method never logs anything, i. e. it doesn't get called. And in the page class dataContext.getParentDataDomain().getFilters().size() is 0. Too bad.
Re: check for changes before commit
I'm quickly running out of ideas, but perhaps one of these two would work to make sure the ObjectContextProvider is available up-front: #1 Use an AppModule constructor public class AppModule { private final ObjectContextProvider objectContextProvider; public AppModule(ObjectContextProvider objectContextProvider) { this.objectContextProvider = objectContextProvider; } @Startup { ... } } #2 Use @Inject public class AppModule { @Inject private ObjectContextProvider objectContextProvider; @Startup { ... } } If neither of those work, perhaps the T5 list would be more helpful. I'm pretty sure Robert Ziegler, who wrote the T5-Cayenne module, follows that list. mrg On Fri, Nov 23, 2012 at 1:12 PM, devnull2...@gmx.de wrote: @Startup public void init(ObjectContextProvider provider) { ... } I tried it this way but sadly I get the same exception on startup as before. (Don't mix DataContext with ObjectContextProvider.) DataContext in my example was just the id of the service. From TapestryCayenneModule: public static void bind(ServiceBinder binder) { binder.bind(ObjectContextProvider.class, DataContextProviderImpl.class) .withMarker(Cayenne.class).withId(DataContext); } ^ I'd tried it several ways @InjectService(ObjectContextProvider) ObjectContextProvider provider -- no service with this name @InjectService(DataContext) ObjectContextProvider provider -- TapestryIOCModule.RegistryStartup Construction of service RegistryStartup failed: org.apache.tapestry5.ioc.internal.OperationException @Inject ObjectContextProvider provider -- same exception as above ObjectContextProvider provider -- same exception as above On the tapestry web page you linked they say RegistryStartup occurs after eager loaded services are instantiated. I guess this means it occurs before non-eager services are instantiated and ObjectContextProvider isn't eager loaded. So I'm afraid it's just not available yet. public void init() { ServerRuntime runtime = new ServerRuntime(cayenne.xml); runtime.getDataDomain().getEntityResolver().getCallbackRegistry().addListener(new LastModifiedListener()); runtime.getDataDomain().addFilter(new ChangeSetFilter()); } This way there are no exceptions on startup. I put in some logging and the size of runtime.getDataDomain().getFilters() in the init method is 1 (no surprise). I couldn't find a method to get the number of listeners. But the lastmodified date isn't set when I edit something. So I added some logging output in the insertAudit(DataObject object) of the LastModifiedListener and in the page class in onSuccessFromEditForm(). The insertAudit method never logs anything, i. e. it doesn't get called. And in the page class dataContext.getParentDataDomain().getFilters().size() is 0. Too bad.