Re: [RFC] YAMF - Yet Another Model Factory
I see that most of the suggestions have been implemented and the feedback was mainly positive. What are the next steps here? Can you create a branch containing that piece of code and in parallel create a JIRA ticket for that contribution? Thanks Konrad On 07 Jan 2014, at 20:55, Justin Edelson jus...@justinedelson.com wrote: Hi Georg- Thanks for the suggestion. I think I found a way to handle this with PhantomReferences. Justin On Tue, Jan 7, 2014 at 11:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi Justin, I have a solution for the unget problem - for a recent project I have created a class OsgiServiceProxy (also used for the howm-grown injection mechanism) that allows to use a service without having access to the request (using java.lang.reflect.Proxy, see [1],[2]). The proxy gets/ungets the service for every method call (not ideal from a performance perspective, but it is surprisingly fast and we didn't have a significant performance penalty here). It could probably be rewritten to use a ServiceTracker (to be more efficient), but it works as is and the beauty in the solution is, that you can use/store the service like a service reference because the service proxy never holds on to the actual service between method calls. Regards Georg [1] Usage MyService myService = OsgiServiceProxy.create(abstractComponent.getClass(), MyService.class); [2] Impl public final class OsgiServiceProxy implements InvocationHandler { private static final Logger LOG = LoggerFactory.getLogger(OsgiServiceProxy.class); /** * Creates a proxy for a OSGI service. * * @param hostClass The class that uses the service * @param osgiServiceClass the service class * @return proxy to the service */ public static T T create(final Class? hostClass, final ClassT osgiServiceClass) { OsgiServiceProxy osgiServiceProxy = new OsgiServiceProxy(hostClass, osgiServiceClass); Object service = Proxy.newProxyInstance(hostClass.getClassLoader(), new Class[] { osgiServiceClass }, osgiServiceProxy); @SuppressWarnings(unchecked) T retVal = (T) service; return retVal; } private Class? hostClass; private Class? osgiServiceClass; private OsgiServiceProxy(final Class? hostClass, final Class? osgiServiceClass) { this.hostClass = hostClass; this.osgiServiceClass = osgiServiceClass; } @Override public Object invoke(final Object proxyObj, final Method method, final Object[] methodArgs) throws Throwable { LOG.debug(OsgiServiceProxy: Invocation on service {} method {}, this.osgiServiceClass, method); BundleContext componentTypeBundleContext = FrameworkUtil.getBundle(this.hostClass).getBundleContext(); ServiceReference serviceReference = componentTypeBundleContext.getServiceReference(this.osgiServiceClass.getName()); if (serviceReference == null) { throw new IllegalStateException(Cannot call + method.getName() + as service + osgiServiceClass + is not available); } LOG.debug(OsgiServiceProxy: Retrieved service ref for {} , this.osgiServiceClass); Object result = null; try { Object service = componentTypeBundleContext.getService(serviceReference); result = method.invoke(service, methodArgs); } finally { LOG.debug(OsgiServiceProxy: Ungetting service ref for {}, this.osgiServiceClass); componentTypeBundleContext.ungetService(serviceReference); } return result; } } That's unfortunately correct. What I'm in the process of doing is changing the OSGiServiceInjector so that if the adaptable is a request, it uses SlingScriptHelper intead of the bundle context so at least the references are released if you use a request as the adaptable. But I don't see a good way to handle this otherwise. Any ideas? Regards, Justin Cheers, Konrad On 24 Dec 2013, at 22:16, Justin Edelson jus...@justinedelson.com wrote: Thanks everyone for your feedback. I've updated both the wiki and implementation to include support for: * declaring an injection as being provided specifically by a particular injector, using the @Source annotation (as well as adding this annotation to @Filter) * composition support (without new annotations) * switched package to .annotations from .api * using BundleTracker rather than BundleListener * a new injector type for child resources I also tried to add some reference information to the wiki. I think this captured all of the feedback received so far. Thanks again. Justin On Sat, Dec 21, 2013 at 1:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi all, first of all I have to say that I'm really happy to see that effort is being made to come up with a annotation based model binding mechansim. We've been using our own-grown for a while, but
Re: [RFC] YAMF - Yet Another Model Factory
Hi Konrad, The next step IMHO is to formally rename this to Sling Models and move it to extensions/models. I'll start a separate thread about that. Regards, Justin On Fri, Jan 10, 2014 at 10:13 AM, Konrad Windszus konra...@gmx.de wrote: I see that most of the suggestions have been implemented and the feedback was mainly positive. What are the next steps here? Can you create a branch containing that piece of code and in parallel create a JIRA ticket for that contribution? Thanks Konrad On 07 Jan 2014, at 20:55, Justin Edelson jus...@justinedelson.com wrote: Hi Georg- Thanks for the suggestion. I think I found a way to handle this with PhantomReferences. Justin On Tue, Jan 7, 2014 at 11:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi Justin, I have a solution for the unget problem - for a recent project I have created a class OsgiServiceProxy (also used for the howm-grown injection mechanism) that allows to use a service without having access to the request (using java.lang.reflect.Proxy, see [1],[2]). The proxy gets/ungets the service for every method call (not ideal from a performance perspective, but it is surprisingly fast and we didn't have a significant performance penalty here). It could probably be rewritten to use a ServiceTracker (to be more efficient), but it works as is and the beauty in the solution is, that you can use/store the service like a service reference because the service proxy never holds on to the actual service between method calls. Regards Georg [1] Usage MyService myService = OsgiServiceProxy.create(abstractComponent.getClass(), MyService.class); [2] Impl public final class OsgiServiceProxy implements InvocationHandler { private static final Logger LOG = LoggerFactory.getLogger(OsgiServiceProxy.class); /** * Creates a proxy for a OSGI service. * * @param hostClass The class that uses the service * @param osgiServiceClass the service class * @return proxy to the service */ public static T T create(final Class? hostClass, final ClassT osgiServiceClass) { OsgiServiceProxy osgiServiceProxy = new OsgiServiceProxy(hostClass, osgiServiceClass); Object service = Proxy.newProxyInstance(hostClass.getClassLoader(), new Class[] { osgiServiceClass }, osgiServiceProxy); @SuppressWarnings(unchecked) T retVal = (T) service; return retVal; } private Class? hostClass; private Class? osgiServiceClass; private OsgiServiceProxy(final Class? hostClass, final Class? osgiServiceClass) { this.hostClass = hostClass; this.osgiServiceClass = osgiServiceClass; } @Override public Object invoke(final Object proxyObj, final Method method, final Object[] methodArgs) throws Throwable { LOG.debug(OsgiServiceProxy: Invocation on service {} method {}, this.osgiServiceClass, method); BundleContext componentTypeBundleContext = FrameworkUtil.getBundle(this.hostClass).getBundleContext(); ServiceReference serviceReference = componentTypeBundleContext.getServiceReference(this.osgiServiceClass.getName()); if (serviceReference == null) { throw new IllegalStateException(Cannot call + method.getName() + as service + osgiServiceClass + is not available); } LOG.debug(OsgiServiceProxy: Retrieved service ref for {} , this.osgiServiceClass); Object result = null; try { Object service = componentTypeBundleContext.getService(serviceReference); result = method.invoke(service, methodArgs); } finally { LOG.debug(OsgiServiceProxy: Ungetting service ref for {}, this.osgiServiceClass); componentTypeBundleContext.ungetService(serviceReference); } return result; } } That's unfortunately correct. What I'm in the process of doing is changing the OSGiServiceInjector so that if the adaptable is a request, it uses SlingScriptHelper intead of the bundle context so at least the references are released if you use a request as the adaptable. But I don't see a good way to handle this otherwise. Any ideas? Regards, Justin Cheers, Konrad On 24 Dec 2013, at 22:16, Justin Edelson jus...@justinedelson.com wrote: Thanks everyone for your feedback. I've updated both the wiki and implementation to include support for: * declaring an injection as being provided specifically by a particular injector, using the @Source annotation (as well as adding this annotation to @Filter) * composition support (without new annotations) * switched package to .annotations from .api * using BundleTracker rather than BundleListener * a new injector type for child resources I also tried to add some reference information to the wiki. I think this captured all of the feedback received so far. Thanks again. Justin On Sat, Dec 21, 2013 at 1:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net
Re: [RFC] YAMF - Yet Another Model Factory
Hi Justin, thanks for the additions. It would be great if you could extend the documentation in the wiki with the following information: - which injectors are available for which adaptables - the order in which the injectors are called - some more information about @Projection (when it is necessary to call it, i.e. if a desired injector can only act on a child object of the adaptable) - some example code on how to use @Source Maybe a table with the different injectors in the rows and the behaviour in terms of the injection-specific annotations like @Named, @Filter as well as the supported adaptable would be the best. Regarding the OSGiServiceInjector: I did not see the ungetService, therefore the service reference is probably never released. Cheers, Konrad On 24 Dec 2013, at 22:16, Justin Edelson jus...@justinedelson.com wrote: Thanks everyone for your feedback. I've updated both the wiki and implementation to include support for: * declaring an injection as being provided specifically by a particular injector, using the @Source annotation (as well as adding this annotation to @Filter) * composition support (without new annotations) * switched package to .annotations from .api * using BundleTracker rather than BundleListener * a new injector type for child resources I also tried to add some reference information to the wiki. I think this captured all of the feedback received so far. Thanks again. Justin On Sat, Dec 21, 2013 at 1:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi all, first of all I have to say that I'm really happy to see that effort is being made to come up with a annotation based model binding mechansim. We've been using our own-grown for a while, but a standard is better! :) I also think it would be useful to inject sub models. Using only the @Inject annotation is ambiguous though, as the class could be either an OSGi Service or a sub model. A solution for this could be to use an annotation like @SubModel and make OSGi services the default. @Inject @SubModel private ImageModel image; // using the field name as context path for the sub model as default, in this case ./image @Inject @SubModel(path=image2) // path explicitly provided, here ./image2 private ImageModel anotherImage; @Inject // assumed to be an OSGi service for non-primitive types private SomeOtherClass myService; -Georg Am 20.12.2013 15:19, schrieb Justin Edelson: Hi Konrad, Thanks for the clarification. It seems straightforward enough to be able to adapt the injected value if it is not assignable to the field's class. @Inject private ImageModel image image would be a Resource object natively which could then be adapted to the ImageModel class. Justin On Fri, Dec 20, 2013 at 8:08 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, let me give a concrete example where switching resource nodes is actually useful: I do have a composition model of two image models (i.e. the same class). Obviously they cannot share the same node, as both models are referring to the same value names. Therefore an approach similar to sling:include path=... resourceType=.. would be very useful on the model side. I admit that in the case of models it is a little bit different, because we are not doing real request dispatching here. Rather I want to have a way to tell the factory (or only the ValueMap injector) to act on a certain sub node of the request resource instead of the request resource itself. That way we could tell the instance1 of the image model to act on subnode 'image1 and the instance2 of that model to act on subnode image2. Regards, Konrad Am 20.12.2013 um 13:41 schrieb Justin Edelson jus...@justinedelson.com: Hi Konrad, This makes sense, except for the part about switch the current resource? What do you mean by this? It seems we should be treating the request resource (which is what I think of as the current resource) as immutable. Regards, Justin On Fri, Dec 20, 2013 at 5:31 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, another useful feature just came to my mind (in fact we are using it in our own annotation framework) which is composition. Would it be much effort to allow injecting one model into another? We do have the following usecase for that (although this is CQ, I guess there is a similar usecase in Sling only): You have a model for an image with title, alternative text. You have a model for multiline text fields and alignment. There exist resourceTypes for each of the models as well as a composite resourceType multilineImage. For the composite resourceType I would like to reuse the existing models, but I cannot split up the view (i.e. the JSPs and work with sling:include), because the html is somehow intertwined. Therefore I would define another composite model exposing the models for both image and multiline and use that composite model in my JSP. It
Re: [RFC] YAMF - Yet Another Model Factory
Hi Konrad, On Tue, Jan 7, 2014 at 10:42 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, thanks for the additions. It would be great if you could extend the documentation in the wiki with the following information: - which injectors are available for which adaptables done. - the order in which the injectors are called I agree that this needs better definition. - some more information about @Projection (when it is necessary to call it, i.e. if a desired injector can only act on a child object of the adaptable) @Projection has been renamed to @Via and I added a better example. - some example code on how to use @Source There's now an example of this on the wiki. Maybe a table with the different injectors in the rows and the behaviour in terms of the injection-specific annotations like @Named, @Filter as well as the supported adaptable would be the best. I've converted the list to a table. @Named isn't injector specific. It is, however, ignored for OSGI services, something which is identified in the table. Regarding the OSGiServiceInjector: I did not see the ungetService, therefore the service reference is probably never released. That's unfortunately correct. What I'm in the process of doing is changing the OSGiServiceInjector so that if the adaptable is a request, it uses SlingScriptHelper intead of the bundle context so at least the references are released if you use a request as the adaptable. But I don't see a good way to handle this otherwise. Any ideas? Regards, Justin Cheers, Konrad On 24 Dec 2013, at 22:16, Justin Edelson jus...@justinedelson.com wrote: Thanks everyone for your feedback. I've updated both the wiki and implementation to include support for: * declaring an injection as being provided specifically by a particular injector, using the @Source annotation (as well as adding this annotation to @Filter) * composition support (without new annotations) * switched package to .annotations from .api * using BundleTracker rather than BundleListener * a new injector type for child resources I also tried to add some reference information to the wiki. I think this captured all of the feedback received so far. Thanks again. Justin On Sat, Dec 21, 2013 at 1:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi all, first of all I have to say that I'm really happy to see that effort is being made to come up with a annotation based model binding mechansim. We've been using our own-grown for a while, but a standard is better! :) I also think it would be useful to inject sub models. Using only the @Inject annotation is ambiguous though, as the class could be either an OSGi Service or a sub model. A solution for this could be to use an annotation like @SubModel and make OSGi services the default. @Inject @SubModel private ImageModel image; // using the field name as context path for the sub model as default, in this case ./image @Inject @SubModel(path=image2) // path explicitly provided, here ./image2 private ImageModel anotherImage; @Inject // assumed to be an OSGi service for non-primitive types private SomeOtherClass myService; -Georg Am 20.12.2013 15:19, schrieb Justin Edelson: Hi Konrad, Thanks for the clarification. It seems straightforward enough to be able to adapt the injected value if it is not assignable to the field's class. @Inject private ImageModel image image would be a Resource object natively which could then be adapted to the ImageModel class. Justin On Fri, Dec 20, 2013 at 8:08 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, let me give a concrete example where switching resource nodes is actually useful: I do have a composition model of two image models (i.e. the same class). Obviously they cannot share the same node, as both models are referring to the same value names. Therefore an approach similar to sling:include path=... resourceType=.. would be very useful on the model side. I admit that in the case of models it is a little bit different, because we are not doing real request dispatching here. Rather I want to have a way to tell the factory (or only the ValueMap injector) to act on a certain sub node of the request resource instead of the request resource itself. That way we could tell the instance1 of the image model to act on subnode 'image1 and the instance2 of that model to act on subnode image2. Regards, Konrad Am 20.12.2013 um 13:41 schrieb Justin Edelson jus...@justinedelson.com: Hi Konrad, This makes sense, except for the part about switch the current resource? What do you mean by this? It seems we should be treating the request resource (which is what I think of as the current resource) as immutable. Regards, Justin On Fri, Dec 20, 2013 at 5:31 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, another useful feature just came to my mind (in fact we are using it in our own annotation framework) which is
Re: [RFC] YAMF - Yet Another Model Factory
Hi, On Tuesday, January 7, 2014, Justin Edelson wrote: ...What I'm in the process of doing is changing the OSGiServiceInjector so that if the adaptable is a request, it uses SlingScriptHelper intead of the bundle context so at least the references are released if you use a request as the adaptable. But I don't see a good way to handle this otherwise. Any ideas?... IIUC the general problem is making sure things are cleaned up when Sling is done processing the current request - we might design a specific mechanism for that. Maybe an object in the request attributes to which you can add Callables that are executed at the end of a request? Unless we have such a general mechanism already, but I don't think so. -Bertrand
Re: [RFC] YAMF - Yet Another Model Factory
Hi, On Tue, Jan 7, 2014 at 11:50 AM, Bertrand Delacretaz bdelacre...@apache.org wrote: Hi, On Tuesday, January 7, 2014, Justin Edelson wrote: ...What I'm in the process of doing is changing the OSGiServiceInjector so that if the adaptable is a request, it uses SlingScriptHelper intead of the bundle context so at least the references are released if you use a request as the adaptable. But I don't see a good way to handle this otherwise. Any ideas?... IIUC the general problem is making sure things are cleaned up when Sling is done processing the current request - we might design a specific mechanism for that. Maybe an object in the request attributes to which you can add Callables that are executed at the end of a request? In a request context, this already works - SlingScriptHelper keeps track of the OSGi services it obtains and calls unget on them when the request is finished. The problem is in a non-request context, like a scheduled job, where there is no obvious (at least not obvious to me) transaction boundary. The only thing I can think of is to add a special Disposer injection which then a model object needs to call via some method, i.e. @Model(adaptables = Resource.class) public class MyModel { @Inject private Disposer disposer public void dispose() { disposer.dispose() } } So a client would do: MyModel model = resource.adaptTo(MyModel.class) // do some work with model model.dispose(); Justin Unless we have such a general mechanism already, but I don't think so. -Bertrand
Re: [RFC] YAMF - Yet Another Model Factory
Hi Justin, I have a solution for the unget problem - for a recent project I have created a class OsgiServiceProxy (also used for the howm-grown injection mechanism) that allows to use a service without having access to the request (using java.lang.reflect.Proxy, see [1],[2]). The proxy gets/ungets the service for every method call (not ideal from a performance perspective, but it is surprisingly fast and we didn't have a significant performance penalty here). It could probably be rewritten to use a ServiceTracker (to be more efficient), but it works as is and the beauty in the solution is, that you can use/store the service like a service reference because the service proxy never holds on to the actual service between method calls. Regards Georg [1] Usage MyService myService = OsgiServiceProxy.create(abstractComponent.getClass(), MyService.class); [2] Impl public final class OsgiServiceProxy implements InvocationHandler { private static final Logger LOG = LoggerFactory.getLogger(OsgiServiceProxy.class); /** * Creates a proxy for a OSGI service. * * @param hostClass The class that uses the service * @param osgiServiceClass the service class * @return proxy to the service */ public static T T create(final Class? hostClass, final ClassT osgiServiceClass) { OsgiServiceProxy osgiServiceProxy = new OsgiServiceProxy(hostClass, osgiServiceClass); Object service = Proxy.newProxyInstance(hostClass.getClassLoader(), new Class[] { osgiServiceClass }, osgiServiceProxy); @SuppressWarnings(unchecked) T retVal = (T) service; return retVal; } private Class? hostClass; private Class? osgiServiceClass; private OsgiServiceProxy(final Class? hostClass, final Class? osgiServiceClass) { this.hostClass = hostClass; this.osgiServiceClass = osgiServiceClass; } @Override public Object invoke(final Object proxyObj, final Method method, final Object[] methodArgs) throws Throwable { LOG.debug(OsgiServiceProxy: Invocation on service {} method {}, this.osgiServiceClass, method); BundleContext componentTypeBundleContext = FrameworkUtil.getBundle(this.hostClass).getBundleContext(); ServiceReference serviceReference = componentTypeBundleContext.getServiceReference(this.osgiServiceClass.getName()); if (serviceReference == null) { throw new IllegalStateException(Cannot call + method.getName() + as service + osgiServiceClass + is not available); } LOG.debug(OsgiServiceProxy: Retrieved service ref for {} , this.osgiServiceClass); Object result = null; try { Object service = componentTypeBundleContext.getService(serviceReference); result = method.invoke(service, methodArgs); } finally { LOG.debug(OsgiServiceProxy: Ungetting service ref for {}, this.osgiServiceClass); componentTypeBundleContext.ungetService(serviceReference); } return result; } } That's unfortunately correct. What I'm in the process of doing is changing the OSGiServiceInjector so that if the adaptable is a request, it uses SlingScriptHelper intead of the bundle context so at least the references are released if you use a request as the adaptable. But I don't see a good way to handle this otherwise. Any ideas? Regards, Justin Cheers, Konrad On 24 Dec 2013, at 22:16, Justin Edelson jus...@justinedelson.com wrote: Thanks everyone for your feedback. I've updated both the wiki and implementation to include support for: * declaring an injection as being provided specifically by a particular injector, using the @Source annotation (as well as adding this annotation to @Filter) * composition support (without new annotations) * switched package to .annotations from .api * using BundleTracker rather than BundleListener * a new injector type for child resources I also tried to add some reference information to the wiki. I think this captured all of the feedback received so far. Thanks again. Justin On Sat, Dec 21, 2013 at 1:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi all, first of all I have to say that I'm really happy to see that effort is being made to come up with a annotation based model binding mechansim. We've been using our own-grown for a while, but a standard is better! :) I also think it would be useful to inject sub models. Using only the @Inject annotation is ambiguous though, as the class could be either an OSGi Service or a sub model. A solution for this could be to use an annotation like @SubModel and make OSGi services the default. @Inject @SubModel private ImageModel image; // using the field name as context path for the sub model as default, in this case ./image @Inject @SubModel(path=image2) // path explicitly provided, here ./image2 private ImageModel anotherImage; @Inject // assumed to be
Re: [RFC] YAMF - Yet Another Model Factory
Hi Georg- Thanks for the suggestion. I think I found a way to handle this with PhantomReferences. Justin On Tue, Jan 7, 2014 at 11:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi Justin, I have a solution for the unget problem - for a recent project I have created a class OsgiServiceProxy (also used for the howm-grown injection mechanism) that allows to use a service without having access to the request (using java.lang.reflect.Proxy, see [1],[2]). The proxy gets/ungets the service for every method call (not ideal from a performance perspective, but it is surprisingly fast and we didn't have a significant performance penalty here). It could probably be rewritten to use a ServiceTracker (to be more efficient), but it works as is and the beauty in the solution is, that you can use/store the service like a service reference because the service proxy never holds on to the actual service between method calls. Regards Georg [1] Usage MyService myService = OsgiServiceProxy.create(abstractComponent.getClass(), MyService.class); [2] Impl public final class OsgiServiceProxy implements InvocationHandler { private static final Logger LOG = LoggerFactory.getLogger(OsgiServiceProxy.class); /** * Creates a proxy for a OSGI service. * * @param hostClass The class that uses the service * @param osgiServiceClass the service class * @return proxy to the service */ public static T T create(final Class? hostClass, final ClassT osgiServiceClass) { OsgiServiceProxy osgiServiceProxy = new OsgiServiceProxy(hostClass, osgiServiceClass); Object service = Proxy.newProxyInstance(hostClass.getClassLoader(), new Class[] { osgiServiceClass }, osgiServiceProxy); @SuppressWarnings(unchecked) T retVal = (T) service; return retVal; } private Class? hostClass; private Class? osgiServiceClass; private OsgiServiceProxy(final Class? hostClass, final Class? osgiServiceClass) { this.hostClass = hostClass; this.osgiServiceClass = osgiServiceClass; } @Override public Object invoke(final Object proxyObj, final Method method, final Object[] methodArgs) throws Throwable { LOG.debug(OsgiServiceProxy: Invocation on service {} method {}, this.osgiServiceClass, method); BundleContext componentTypeBundleContext = FrameworkUtil.getBundle(this.hostClass).getBundleContext(); ServiceReference serviceReference = componentTypeBundleContext.getServiceReference(this.osgiServiceClass.getName()); if (serviceReference == null) { throw new IllegalStateException(Cannot call + method.getName() + as service + osgiServiceClass + is not available); } LOG.debug(OsgiServiceProxy: Retrieved service ref for {} , this.osgiServiceClass); Object result = null; try { Object service = componentTypeBundleContext.getService(serviceReference); result = method.invoke(service, methodArgs); } finally { LOG.debug(OsgiServiceProxy: Ungetting service ref for {}, this.osgiServiceClass); componentTypeBundleContext.ungetService(serviceReference); } return result; } } That's unfortunately correct. What I'm in the process of doing is changing the OSGiServiceInjector so that if the adaptable is a request, it uses SlingScriptHelper intead of the bundle context so at least the references are released if you use a request as the adaptable. But I don't see a good way to handle this otherwise. Any ideas? Regards, Justin Cheers, Konrad On 24 Dec 2013, at 22:16, Justin Edelson jus...@justinedelson.com wrote: Thanks everyone for your feedback. I've updated both the wiki and implementation to include support for: * declaring an injection as being provided specifically by a particular injector, using the @Source annotation (as well as adding this annotation to @Filter) * composition support (without new annotations) * switched package to .annotations from .api * using BundleTracker rather than BundleListener * a new injector type for child resources I also tried to add some reference information to the wiki. I think this captured all of the feedback received so far. Thanks again. Justin On Sat, Dec 21, 2013 at 1:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi all, first of all I have to say that I'm really happy to see that effort is being made to come up with a annotation based model binding mechansim. We've been using our own-grown for a while, but a standard is better! :) I also think it would be useful to inject sub models. Using only the @Inject annotation is ambiguous though, as the class could be either an OSGi Service or a sub model. A solution for this could be to use an annotation like @SubModel and make OSGi services the default. @Inject @SubModel
Re: [RFC] YAMF - Yet Another Model Factory
Hi Justin, Finally found time to review YAMF. On Thu, Dec 19, 2013 at 6:07 PM, Justin Edelson jus...@justinedelson.com wrote: ...I'm calling this YAMF for now, although ideally we'll just call it Sling Models... +1 for Sling Models, that's consistent with our general naming. My comments, after looking at the wiki page only: 1) Should it be @SlingModel rather than @Model? By analogy with @SlingServlet. 2) For adaptation, would my SLING-2938 adapter methods prototype make sense? 3) Is it a good idea to use @Inject for both services and resource values? Using @ResourceValue for the latter makes it easier to explain both IMO. 4) Also, would @Path make sense instead of @Named? Does the implementation then support @Path(child-node/some-property) ? 5) @Projection is not obvious to me from its name, how about @ChildPath ? Naming nitpicks mostly, YAMF looks good to me, thanks! -Bertrand
Re: [RFC] YAMF - Yet Another Model Factory
Hi Bertrand, On Fri, Dec 27, 2013 at 5:48 AM, Bertrand Delacretaz bdelacre...@apache.org wrote: Hi Justin, Finally found time to review YAMF. On Thu, Dec 19, 2013 at 6:07 PM, Justin Edelson jus...@justinedelson.com wrote: ...I'm calling this YAMF for now, although ideally we'll just call it Sling Models... +1 for Sling Models, that's consistent with our general naming. My comments, after looking at the wiki page only: 1) Should it be @SlingModel rather than @Model? By analogy with @SlingServlet. I thought about this, but honestly the Sling part here is duplicative. sling is already part of the package name, so why include it in the annotation name as well? 2) For adaptation, would my SLING-2938 adapter methods prototype make sense? Possibly, although as I wrote on the wiki page, one of the design goals was to not require changes to existing Sling bundles which AFAICT SLING-2938 does. 3) Is it a good idea to use @Inject for both services and resource values? Using @ResourceValue for the latter makes it easier to explain both IMO. There are already 5 injectors available and I would guess that we need at least one or two others in the core. And other injectors can be added by downstream projects. I don't think it makes sense to have injector-specific annotations unless absolutely necessary (@Filter being the one obvious example of where an annotation is injector-specific). 4) Also, would @Path make sense instead of @Named? Does the implementation then support @Path(child-node/some-property) ? See above. The idea (beyond the goal of using standard annotations where possible) is that @Named works across injector types. @Path doesn't make sense for a binding value or request attribute. 5) @Projection is not obvious to me from its name, how about @ChildPath ? Yeah, I'm not thrilled with @Projection either, but for the same reasons as above @ChildPath won't work consistently. How about @From as in @Model(adaptables = Resource.class) public class Model { @Inject @From(parent) @Named(jcr:title) private String parentTitle; } Which means set the parentTitle field to the jcr:title property of the parent resource (i.e. resource.getParent()). Naming nitpicks mostly, YAMF looks good to me, thanks! I need all the naming help I can get :) Thanks, Justin -Bertrand
Re: [RFC] YAMF - Yet Another Model Factory
Thanks everyone for your feedback. I've updated both the wiki and implementation to include support for: * declaring an injection as being provided specifically by a particular injector, using the @Source annotation (as well as adding this annotation to @Filter) * composition support (without new annotations) * switched package to .annotations from .api * using BundleTracker rather than BundleListener * a new injector type for child resources I also tried to add some reference information to the wiki. I think this captured all of the feedback received so far. Thanks again. Justin On Sat, Dec 21, 2013 at 1:47 AM, Georg Henzler sl...@cq-eclipse-plugin.net wrote: Hi all, first of all I have to say that I'm really happy to see that effort is being made to come up with a annotation based model binding mechansim. We've been using our own-grown for a while, but a standard is better! :) I also think it would be useful to inject sub models. Using only the @Inject annotation is ambiguous though, as the class could be either an OSGi Service or a sub model. A solution for this could be to use an annotation like @SubModel and make OSGi services the default. @Inject @SubModel private ImageModel image; // using the field name as context path for the sub model as default, in this case ./image @Inject @SubModel(path=image2) // path explicitly provided, here ./image2 private ImageModel anotherImage; @Inject // assumed to be an OSGi service for non-primitive types private SomeOtherClass myService; -Georg Am 20.12.2013 15:19, schrieb Justin Edelson: Hi Konrad, Thanks for the clarification. It seems straightforward enough to be able to adapt the injected value if it is not assignable to the field's class. @Inject private ImageModel image image would be a Resource object natively which could then be adapted to the ImageModel class. Justin On Fri, Dec 20, 2013 at 8:08 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, let me give a concrete example where switching resource nodes is actually useful: I do have a composition model of two image models (i.e. the same class). Obviously they cannot share the same node, as both models are referring to the same value names. Therefore an approach similar to sling:include path=... resourceType=.. would be very useful on the model side. I admit that in the case of models it is a little bit different, because we are not doing real request dispatching here. Rather I want to have a way to tell the factory (or only the ValueMap injector) to act on a certain sub node of the request resource instead of the request resource itself. That way we could tell the instance1 of the image model to act on subnode 'image1 and the instance2 of that model to act on subnode image2. Regards, Konrad Am 20.12.2013 um 13:41 schrieb Justin Edelson jus...@justinedelson.com: Hi Konrad, This makes sense, except for the part about switch the current resource? What do you mean by this? It seems we should be treating the request resource (which is what I think of as the current resource) as immutable. Regards, Justin On Fri, Dec 20, 2013 at 5:31 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, another useful feature just came to my mind (in fact we are using it in our own annotation framework) which is composition. Would it be much effort to allow injecting one model into another? We do have the following usecase for that (although this is CQ, I guess there is a similar usecase in Sling only): You have a model for an image with title, alternative text. You have a model for multiline text fields and alignment. There exist resourceTypes for each of the models as well as a composite resourceType multilineImage. For the composite resourceType I would like to reuse the existing models, but I cannot split up the view (i.e. the JSPs and work with sling:include), because the html is somehow intertwined. Therefore I would define another composite model exposing the models for both image and multiline and use that composite model in my JSP. It would be great if for the injection of other models it would be possible to switch the current resource as well (i.e. descent into subnode image). That do you think about that? Thanks in advance, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces)
Re: [RFC] YAMF - Yet Another Model Factory
Hi Justin, another useful feature just came to my mind (in fact we are using it in our own annotation framework) which is composition. Would it be much effort to allow injecting one model into another? We do have the following usecase for that (although this is CQ, I guess there is a similar usecase in Sling only): You have a model for an image with title, alternative text. You have a model for multiline text fields and alignment. There exist resourceTypes for each of the models as well as a composite resourceType multilineImage. For the composite resourceType I would like to reuse the existing models, but I cannot split up the view (i.e. the JSPs and work with sling:include), because the html is somehow intertwined. Therefore I would define another composite model exposing the models for both image and multiline and use that composite model in my JSP. It would be great if for the injection of other models it would be possible to switch the current resource as well (i.e. descent into subnode image). That do you think about that? Thanks in advance, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Justin Wow ! I really like this ! Still, as always, I have some comments: * I share Dan's concerns with respect to the @Inject annotation: Isn't that too generic ? (Yes, I am the strongly typed guy, so I love to know things upfront in a fail early style) * I see support for interfaces is implemented as proxies which are loaded through the interface type's class loader. But the InvocationHandler is created for each invocation of adaptTo. Would it make sense to cache the generated proxy classes and just create new instances of them on each invocation ? (well this maybe already falls into the optimization category) * I assum the value to @Filter is supposed to be a valid OSGi Filter argument. Should thus the respective sample in the wiki be fixed by having the string value be surrounded by parentheses ? Also, the existence of the @Filter annotation would stipulate the respective @Incject annotation to only be considered by the service injector. * I would think the api bundle and package should rather be called annotations, right ? * The annotations are currently RUNTIME scoped and the packaged classes are eagerly loaded on bundle start time (you might want to leverage the BundleTracker for easier tracking of bundles). This sounds like a performance problem. How about doing it like Declarative Services and tooling generate a descriptor which will be loaded on bundle startup and the respective classes loaded on-demand ? (OTOH this runtime analysis copes better with Java extensions) * YAMF model classes must be exported for them to be usable in code. Maybe that is just how this kind of thing works but we should be aware of that and that exporting these classes in fact defines API. This would fall into the Model class requirements category: Must be exported and is considered API, classes must have public default constructore, etc. Thanks Felix Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Konrad, This makes sense, except for the part about switch the current resource? What do you mean by this? It seems we should be treating the request resource (which is what I think of as the current resource) as immutable. Regards, Justin On Fri, Dec 20, 2013 at 5:31 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, another useful feature just came to my mind (in fact we are using it in our own annotation framework) which is composition. Would it be much effort to allow injecting one model into another? We do have the following usecase for that (although this is CQ, I guess there is a similar usecase in Sling only): You have a model for an image with title, alternative text. You have a model for multiline text fields and alignment. There exist resourceTypes for each of the models as well as a composite resourceType multilineImage. For the composite resourceType I would like to reuse the existing models, but I cannot split up the view (i.e. the JSPs and work with sling:include), because the html is somehow intertwined. Therefore I would define another composite model exposing the models for both image and multiline and use that composite model in my JSP. It would be great if for the injection of other models it would be possible to switch the current resource as well (i.e. descent into subnode image). That do you think about that? Thanks in advance, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Felix, On Fri, Dec 20, 2013 at 5:32 AM, Felix Meschberger fmesc...@adobe.com wrote: Hi Justin Wow ! I really like this ! Still, as always, I have some comments: * I share Dan's concerns with respect to the @Inject annotation: Isn't that too generic ? (Yes, I am the strongly typed guy, so I love to know things upfront in a fail early style) I'd love to better understand this. What is a use case in which you would need to explicitly state that an injected field should be sourced from a particular Injector? Even if there was, I'd prefer to do that as a separate annotation, i.e. @Inject @Source(osgi-service) private Something something; Keep in mind that in my worldview, 80% of cases will be handled by *just* @Model and @Inject. * I see support for interfaces is implemented as proxies which are loaded through the interface type's class loader. But the InvocationHandler is created for each invocation of adaptTo. Would it make sense to cache the generated proxy classes and just create new instances of them on each invocation ? (well this maybe already falls into the optimization category) Perhaps, but this is definitely premature optimization :) * I assum the value to @Filter is supposed to be a valid OSGi Filter argument. Should thus the respective sample in the wiki be fixed by having the string value be surrounded by parentheses ? Also, the existence of the @Filter annotation would stipulate the respective @Incject annotation to only be considered by the service injector. Filter example has been fixed. I'm not sure that there's a need to restrict the @Filter annotation to one particular Injector. * I would think the api bundle and package should rather be called annotations, right ? * The annotations are currently RUNTIME scoped and the packaged classes are eagerly loaded on bundle start time (you might want to leverage the BundleTracker for easier tracking of bundles). This sounds like a performance problem. How about doing it like Declarative Services and tooling generate a descriptor which will be loaded on bundle startup and the respective classes loaded on-demand ? (OTOH this runtime analysis copes better with Java extensions) And another Maven plugin and then an Ant task and an Eclipse plugin and an IntelliJ plugin... I really think runtime is the right approach. The better solution IMHO would to implement the wildcard adapter support we discussed when Dan introduced dynamic proxies. The YamfAdapterFactory could figure out that this was enabled and skip the whole bundle scanning bit. But since one of the design goals was to work with existing Sling bundles, I didn't deal with this now. I was also trying to use Scannotation to scan the class files instead of doing class loading. Ran into trouble, but will come back to this. * YAMF model classes must be exported for them to be usable in code. Maybe that is just how this kind of thing works but we should be aware of that and that exporting these classes in fact defines API. This would fall into the Model class requirements category: Must be exported and is considered API, classes must have public default constructore, etc. Actually, this isn't the case - YAMF model classes do not need to be exported *to be used by YAMF*. They do need to be exported to be referenced in JSPs or other bundles, but that's just normal stuff. YAMF itself doesn't put any requirements on the visibility of packages. Justin Thanks Felix Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Justin Am 20.12.2013 um 13:55 schrieb Justin Edelson jus...@justinedelson.com: Hi Felix, On Fri, Dec 20, 2013 at 5:32 AM, Felix Meschberger fmesc...@adobe.com wrote: Hi Justin Wow ! I really like this ! Still, as always, I have some comments: * I share Dan's concerns with respect to the @Inject annotation: Isn't that too generic ? (Yes, I am the strongly typed guy, so I love to know things upfront in a fail early style) I'd love to better understand this. What is a use case in which you would need to explicitly state that an injected field should be sourced from a particular Injector? Even if there was, I'd prefer to do that as a separate annotation, i.e. @Inject @Source(osgi-service) private Something something; Keep in mind that in my worldview, 80% of cases will be handled by *just* @Model and @Inject. I am fine with and if it not too much magic to cope with ;-) * I see support for interfaces is implemented as proxies which are loaded through the interface type's class loader. But the InvocationHandler is created for each invocation of adaptTo. Would it make sense to cache the generated proxy classes and just create new instances of them on each invocation ? (well this maybe already falls into the optimization category) Perhaps, but this is definitely premature optimization :) * I assum the value to @Filter is supposed to be a valid OSGi Filter argument. Should thus the respective sample in the wiki be fixed by having the string value be surrounded by parentheses ? Also, the existence of the @Filter annotation would stipulate the respective @Incject annotation to only be considered by the service injector. Filter example has been fixed. I'm not sure that there's a need to restrict the @Filter annotation to one particular Injector. * I would think the api bundle and package should rather be called annotations, right ? * The annotations are currently RUNTIME scoped and the packaged classes are eagerly loaded on bundle start time (you might want to leverage the BundleTracker for easier tracking of bundles). This sounds like a performance problem. How about doing it like Declarative Services and tooling generate a descriptor which will be loaded on bundle startup and the respective classes loaded on-demand ? (OTOH this runtime analysis copes better with Java extensions) And another Maven plugin and then an Ant task and an Eclipse plugin and an IntelliJ plugin... I really think runtime is the right approach. The better solution IMHO would to implement the wildcard adapter support we discussed when Dan introduced dynamic proxies. The YamfAdapterFactory could figure out that this was enabled and skip the whole bundle scanning bit. But since one of the design goals was to work with existing Sling bundles, I didn't deal with this now. I was also trying to use Scannotation to scan the class files instead of doing class loading. Ran into trouble, but will come back to this. * YAMF model classes must be exported for them to be usable in code. Maybe that is just how this kind of thing works but we should be aware of that and that exporting these classes in fact defines API. This would fall into the Model class requirements category: Must be exported and is considered API, classes must have public default constructore, etc. Actually, this isn't the case - YAMF model classes do not need to be exported *to be used by YAMF*. They do need to be exported to be referenced in JSPs or other bundles, but that's just normal stuff. YAMF itself doesn't put any requirements on the visibility of packages. That's what I wanted to express, actually. Regards Felix Justin Thanks Felix Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here:
Re: [RFC] YAMF - Yet Another Model Factory
Hi Justin, let me give a concrete example where switching resource nodes is actually useful: I do have a composition model of two image models (i.e. the same class). Obviously they cannot share the same node, as both models are referring to the same value names. Therefore an approach similar to sling:include path=... resourceType=.. would be very useful on the model side. I admit that in the case of models it is a little bit different, because we are not doing real request dispatching here. Rather I want to have a way to tell the factory (or only the ValueMap injector) to act on a certain sub node of the request resource instead of the request resource itself. That way we could tell the instance1 of the image model to act on subnode 'image1 and the instance2 of that model to act on subnode image2. Regards, Konrad Am 20.12.2013 um 13:41 schrieb Justin Edelson jus...@justinedelson.com: Hi Konrad, This makes sense, except for the part about switch the current resource? What do you mean by this? It seems we should be treating the request resource (which is what I think of as the current resource) as immutable. Regards, Justin On Fri, Dec 20, 2013 at 5:31 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, another useful feature just came to my mind (in fact we are using it in our own annotation framework) which is composition. Would it be much effort to allow injecting one model into another? We do have the following usecase for that (although this is CQ, I guess there is a similar usecase in Sling only): You have a model for an image with title, alternative text. You have a model for multiline text fields and alignment. There exist resourceTypes for each of the models as well as a composite resourceType multilineImage. For the composite resourceType I would like to reuse the existing models, but I cannot split up the view (i.e. the JSPs and work with sling:include), because the html is somehow intertwined. Therefore I would define another composite model exposing the models for both image and multiline and use that composite model in my JSP. It would be great if for the injection of other models it would be possible to switch the current resource as well (i.e. descent into subnode image). That do you think about that? Thanks in advance, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Konrad, Thanks for the clarification. It seems straightforward enough to be able to adapt the injected value if it is not assignable to the field's class. @Inject private ImageModel image image would be a Resource object natively which could then be adapted to the ImageModel class. Justin On Fri, Dec 20, 2013 at 8:08 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, let me give a concrete example where switching resource nodes is actually useful: I do have a composition model of two image models (i.e. the same class). Obviously they cannot share the same node, as both models are referring to the same value names. Therefore an approach similar to sling:include path=... resourceType=.. would be very useful on the model side. I admit that in the case of models it is a little bit different, because we are not doing real request dispatching here. Rather I want to have a way to tell the factory (or only the ValueMap injector) to act on a certain sub node of the request resource instead of the request resource itself. That way we could tell the instance1 of the image model to act on subnode 'image1 and the instance2 of that model to act on subnode image2. Regards, Konrad Am 20.12.2013 um 13:41 schrieb Justin Edelson jus...@justinedelson.com: Hi Konrad, This makes sense, except for the part about switch the current resource? What do you mean by this? It seems we should be treating the request resource (which is what I think of as the current resource) as immutable. Regards, Justin On Fri, Dec 20, 2013 at 5:31 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, another useful feature just came to my mind (in fact we are using it in our own annotation framework) which is composition. Would it be much effort to allow injecting one model into another? We do have the following usecase for that (although this is CQ, I guess there is a similar usecase in Sling only): You have a model for an image with title, alternative text. You have a model for multiline text fields and alignment. There exist resourceTypes for each of the models as well as a composite resourceType multilineImage. For the composite resourceType I would like to reuse the existing models, but I cannot split up the view (i.e. the JSPs and work with sling:include), because the html is somehow intertwined. Therefore I would define another composite model exposing the models for both image and multiline and use that composite model in my JSP. It would be great if for the injection of other models it would be possible to switch the current resource as well (i.e. descent into subnode image). That do you think about that? Thanks in advance, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi all, first of all I have to say that I'm really happy to see that effort is being made to come up with a annotation based model binding mechansim. We've been using our own-grown for a while, but a standard is better! :) I also think it would be useful to inject sub models. Using only the @Inject annotation is ambiguous though, as the class could be either an OSGi Service or a sub model. A solution for this could be to use an annotation like @SubModel and make OSGi services the default. @Inject @SubModel private ImageModel image; // using the field name as context path for the sub model as default, in this case ./image @Inject @SubModel(path=image2) // path explicitly provided, here ./image2 private ImageModel anotherImage; @Inject // assumed to be an OSGi service for non-primitive types private SomeOtherClass myService; -Georg Am 20.12.2013 15:19, schrieb Justin Edelson: Hi Konrad, Thanks for the clarification. It seems straightforward enough to be able to adapt the injected value if it is not assignable to the field's class. @Inject private ImageModel image image would be a Resource object natively which could then be adapted to the ImageModel class. Justin On Fri, Dec 20, 2013 at 8:08 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, let me give a concrete example where switching resource nodes is actually useful: I do have a composition model of two image models (i.e. the same class). Obviously they cannot share the same node, as both models are referring to the same value names. Therefore an approach similar to sling:include path=... resourceType=.. would be very useful on the model side. I admit that in the case of models it is a little bit different, because we are not doing real request dispatching here. Rather I want to have a way to tell the factory (or only the ValueMap injector) to act on a certain sub node of the request resource instead of the request resource itself. That way we could tell the instance1 of the image model to act on subnode 'image1 and the instance2 of that model to act on subnode image2. Regards, Konrad Am 20.12.2013 um 13:41 schrieb Justin Edelson jus...@justinedelson.com: Hi Konrad, This makes sense, except for the part about switch the current resource? What do you mean by this? It seems we should be treating the request resource (which is what I think of as the current resource) as immutable. Regards, Justin On Fri, Dec 20, 2013 at 5:31 AM, Konrad Windszus konra...@gmx.de wrote: Hi Justin, another useful feature just came to my mind (in fact we are using it in our own annotation framework) which is composition. Would it be much effort to allow injecting one model into another? We do have the following usecase for that (although this is CQ, I guess there is a similar usecase in Sling only): You have a model for an image with title, alternative text. You have a model for multiline text fields and alignment. There exist resourceTypes for each of the models as well as a composite resourceType multilineImage. For the composite resourceType I would like to reuse the existing models, but I cannot split up the view (i.e. the JSPs and work with sling:include), because the html is somehow intertwined. Therefore I would define another composite model exposing the models for both image and multiline and use that composite model in my JSP. It would be great if for the injection of other models it would be possible to switch the current resource as well (i.e. descent into subnode image). That do you think about that? Thanks in advance, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your
[RFC] YAMF - Yet Another Model Factory
Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
HI Justin, thank a lot for that. I am assuming a lot of developers were waiting for just that. Could you extend the wiki page with an example on how the model bean should then be used from within the view (i.e. the JSP) and also some words about the scope of one instance? One very good addition to the annotations would probably be a default value in case there is nothing reasonable to be injected (very useful for resource values which are not yet set). Thanks, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Konrad, Thanks for the feedback. From a client perspective (i.e. a JSP), they are just using the adapter framework. The fact that that adaption is annotation driven isn't important to the client (or visible for that matter). I'll try to make that more obvious. Defaults are a good idea. Justin On Thu, Dec 19, 2013 at 12:21 PM, Konrad Windszus konra...@gmx.de wrote: HI Justin, thank a lot for that. I am assuming a lot of developers were waiting for just that. Could you extend the wiki page with an example on how the model bean should then be used from within the view (i.e. the JSP) and also some words about the scope of one instance? One very good addition to the annotations would probably be a default value in case there is nothing reasonable to be injected (very useful for resource values which are not yet set). Thanks, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Justin, This seems like a really nice addition. I was working on something similar using Java's Dynamic Proxy to allow injection from the repository. The model is slightly different where instead of annotating member variables, you annotate methods on an interface. http://svn.apache.org/repos/asf/sling/whiteboard/dklco/dynamic-proxy/ Overall, I'd actually say I like the approach you've defined. A couple thoughts though: - Would it make sense to separate the property/service injection annotations to make it more clear what's being injected? - How about adding support for the naming as variables in the Inject annotation instead of a separate annotation? This would also go for some of the other annotations. I think from a developer's perspective it makes more sense to configure an annotation than have to look up a bunch of different unconnected annotations, - Do the filters just work on service injection? - So this one confuses me a little: @Inject @Named(log) private Logger logger; Is the @Named being used for the page / request attributes? How can one tell from where the injectable object will be loaded? Would there be any value/reason to being able to specify? Very cool though over all! -Dan On Thu, Dec 19, 2013 at 12:21 PM, Konrad Windszus konra...@gmx.de wrote: HI Justin, thank a lot for that. I am assuming a lot of developers were waiting for just that. Could you extend the wiki page with an example on how the model bean should then be used from within the view (i.e. the JSP) and also some words about the scope of one instance? One very good addition to the annotations would probably be a default value in case there is nothing reasonable to be injected (very useful for resource values which are not yet set). Thanks, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Dan, On Thu, Dec 19, 2013 at 12:37 PM, Daniel Klco dk...@apache.org wrote: Justin, This seems like a really nice addition. I was working on something similar using Java's Dynamic Proxy to allow injection from the repository. The model is slightly different where instead of annotating member variables, you annotate methods on an interface. http://svn.apache.org/repos/asf/sling/whiteboard/dklco/dynamic-proxy/ Yes - proxy based injection (of methods) works too. Most of the tests are written with classes, but there is one or two with interfaces. The SPI handles both cases. Overall, I'd actually say I like the approach you've defined. A couple thoughts though: - Would it make sense to separate the property/service injection annotations to make it more clear what's being injected? I don't think so - as a model developer, I shouldn't necessarily care where my injections are coming from, just that they are being injected. - How about adding support for the naming as variables in the Inject annotation instead of a separate annotation? This would also go for some of the other annotations. I think from a developer's perspective it makes more sense to configure an annotation than have to look up a bunch of different unconnected annotations, As I wrote, one of the design goals is to use standard annotations. @Inject and @Named are common annotations defined in the JDK. - Do the filters just work on service injection? Correct, although there's no reason a different Injector couldn't use them. - So this one confuses me a little: @Inject @Named(log) private Logger logger; Is the @Named being used for the page / request attributes? Yes - in this case, log is the name of the SlingBindings attribute. How can one tell from where the injectable object will be loaded? You can't :) Would there be any value/reason to being able to specify? I don't think so, but I'm curious for others opinions. As someone who builds a model class, why should it matter? I just say I want a String instance named 'firstProperty' and it magically appears. Too magical? Justin Very cool though over all! -Dan On Thu, Dec 19, 2013 at 12:21 PM, Konrad Windszus konra...@gmx.de wrote: HI Justin, thank a lot for that. I am assuming a lot of developers were waiting for just that. Could you extend the wiki page with an example on how the model bean should then be used from within the view (i.e. the JSP) and also some words about the scope of one instance? One very good addition to the annotations would probably be a default value in case there is nothing reasonable to be injected (very useful for resource values which are not yet set). Thanks, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Konrad, Defaults have been implemented. Check the wiki. Regards, Justin On Thu, Dec 19, 2013 at 12:21 PM, Konrad Windszus konra...@gmx.de wrote: HI Justin, thank a lot for that. I am assuming a lot of developers were waiting for just that. Could you extend the wiki page with an example on how the model bean should then be used from within the view (i.e. the JSP) and also some words about the scope of one instance? One very good addition to the annotations would probably be a default value in case there is nothing reasonable to be injected (very useful for resource values which are not yet set). Thanks, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin
Re: [RFC] YAMF - Yet Another Model Factory
Hi Justin, thanks a lot. I would like to see a section in the wiki where required injections are described in detail. I guess in case for injected Sling values which are not there the instantiation fails (i.e. adaptTo will return null). What happens in case a default value is specified? In my regard the annotations Optional and Default should not be combined. Regards, Konrad Am 19.12.2013 um 22:33 schrieb Justin Edelson jus...@justinedelson.com: Hi Konrad, Defaults have been implemented. Check the wiki. Regards, Justin On Thu, Dec 19, 2013 at 12:21 PM, Konrad Windszus konra...@gmx.de wrote: HI Justin, thank a lot for that. I am assuming a lot of developers were waiting for just that. Could you extend the wiki page with an example on how the model bean should then be used from within the view (i.e. the JSP) and also some words about the scope of one instance? One very good addition to the annotations would probably be a default value in case there is nothing reasonable to be injected (very useful for resource values which are not yet set). Thanks, Konrad Am 19.12.2013 um 18:07 schrieb Justin Edelson jus...@justinedelson.com: Hi, I've published a page to the wiki about a concept I've been working on to consolidate the various appproaches I have seen in the wild to model object creation. I'm calling this YAMF for now, although ideally we'll just call it Sling Models :) Without repeating the whole contents of the wiki page, at a high level, this is a purely annotation driven approach supporting both classes and interfaces. Your model class simply needs to declare from which other classes it can be adapted: @Model(adaptables=Resource.class) And then annotate the fields (for classes) and methods (for interfaces) which need injection: @Inject private String propertyName; You can inject properties, OSGi services, request attributes, and entries from SlingBindings. New injector types can be created through an SPI. Additional annotations are supported for special cases: @Optional - mark a field/method as optional. @Filter - provide a filter (i.e. for OSGi services) @Named - specify a name (other than the default field/method name) to use for the inejction lookup. More detail can be found here: https://cwiki.apache.org/confluence/display/SLING/YAMF+-+Yet+Another+Model+Factory The working code is up in my whiteboard: https://svn.apache.org/repos/asf/sling/whiteboard/justin/yamf/ Look forward to your feedback. Regards, Justin