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 <[email protected]> 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 <[email protected]> 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 >> <[email protected]> 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 Class<T> >>> 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 <[email protected]> >>>>> 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 >>>>>> <[email protected]> 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 <[email protected]> >>>>>>>> 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 >>>>>>>>> <[email protected]>: >>>>>>>>> >>>>>>>>>> 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 <[email protected]> >>>>>>>>>> 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 >>>>>>>>>>> <[email protected]>: >>>>>>>>>>> >>>>>>>>>>>> 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 >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>> >>>>>>> >>>>> >>> >
