简体   繁体   中英

Is there a way to access SlingRepository in a POJO?

Is there a way to access SlingRepository in a POJO that is not managed by OSGI?

For example, we might have a POJO named Site :

public class Site {

    private String domainName;

    public String getDomainName() { return domainName; }

    public void setDomainName(String domainName) { this.domainName = domainName; }

    public void validate() throws Exception {
        SlingRepository repo = ...;
        // validation logic dependent on repo
    }
}

That is used like this:

Site site = new Site();
site.validate();



Update (re. Tomek's answer)

The reason why I cannot use @Reference or the current request's ResourceResolver is because I am trying to implement a JSR-303 (aka Bean validation) validator.

In our Sling app, we have a bunch of servlets that receive JSON payloads from the browser. Then we convert them to pojos:

Person p = new Gson().fromJson(json, Person.class)
validate(p); // Validate p using Bean Validation

Person is a simple POJO annotated with JSR-303 annotations:

public class Person {

    @NotNull
    private String name;

    @UniqueProperty(
      name = "email", 
      path = "/content/myapp", 
      nodeType = "cq:PageContent"
    )
    private String email;

}

In short, my goal is to implement the @UniqueProperty validation. In the example above, if a node of type cq:PageContent under /content/myapp exists that has an email property with the same value as this model, the validation fails.

The validator class itself will look like this:

public class UniquePropertyValidator implements ConstraintValidator<UniqueProperty, String {

    @Override
    public void initialize(UniqueProperty constraintAnnotation) {
        ...
    }

    @Override
    public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
        // need to execute a JCR query here
    }
}

UniquePropertyValidator will be instantiated by the JSR-303 implementation (eg Hibernate Validator) as needed, and there's no way it can access the current request's resource resolver, hence why I was looking for a way to access the SlingRepository .

First of all, if you use Sling then using ResourceResolver is generally more preferable than SlingRepository . It gives you an useful Resource abstraction layer and you can still get the underlying JCR Session object using adaptTo() method.

But back to your question, POJO always lives in a context, there is some entrypoint that runs the whole thing. In Sling there is a few such places: JSP, servlet or an OSGi component. In all of these entrypoints there is at least one way to get access to the repository:

  1. JSP
    • use resourceResolver binding
    • use getService(ResourceResolverFactory.class) to create an administrative resolver,
  2. Servlet or filter
    • use request.getResourceResolver() to get the request session,
    • or see the next point.
  3. Any OSGi service (including servlet or filter)
    • use @Reference ResourceResolverFactory to get the factory and create administrative resolver.

After that you can pass the resource resolver to your POJO constructir. I think it's a better option than using hacks with the FrameworkUtil for a few reasons:

  • if you pass the ResourceResolver to the POJO constructor, it's clear that this particular class operates on the repository,
  • you don't need to worry about closing sessions (as POJO may not have a defined lifecycle),
  • if you create your POJO in servlet or component (cases 1 and 2), it's better to use the request's session to avoid working on the administrative one.

In general you should not retrieve external resources from a pojo. Simply add a contructor or setter where you inject the SlingRepository at the position where you do the new. This has the advantage that your pojo is independent of OSGi and can be used in different environments. Also unit testing is easier with this approach.

Site site = new Site(slingRepository);

Of course this just moves the problem to the class that creates the instance. I guess at some point you start at an activator where you have access to the BundleContext and can lookup the service.

In the rare cases where you really want to lookup a service directly use

FrameworkUtil.getBundle(this.getClass()).getBundleContext(); 

From there you can lookup the service.

As Christian Schneider wrote, you can use this:

BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
ServiceReference serviceReference = bundleContext.getServiceReference(SlingRepository.class);
SlingRepository slingRepository;
if (serviceReference != null) {
   slingRepository = (SlingRepository) bundleContext.getService(serviceReference);
}

Just found a solution how you can tap into the lifecycle of the ConstraintValidator.

See: http://beanvalidation.org/1.0/spec/#constraintsdefinitionimplementation-validationimplementation

Chapter 2.5. The ConstraintValidatorFactory might help you. If you register a ConstraintValidatorFactory in eg your Activator then you can supply it with the SlingRepository. The factory can then forward the SlingRepository to the Validator it creates. So you can keep the OSGi logic out of the Validator.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM