New version of Slice released

16 January 2015
Maciej Majchrzak
Frink_Cognifide_2016_HeaderImages_0117

The new version of Slice - 4.1 - makes Slice 4 even more powerful and convenient to use. Slice 4.1.0 has been released recently and is available, as always, through the central maven repository. You can also download zip packages from our Download page.

Slice 4.1 is backward-compatible with Slice 4.0 and can be used on AEM 6 (with slice-aem6 v1.0.0 extension), on CQ 5.6 (with slice-cq56 v2.0.0 extension) as well as on a standalone Sling instance.

What's new?

Slice 4.1 brings a couple of very useful and long-awaited features which you can read about below.

Caching SliceResources per path

Voila! This is a feature everyone was asking for and finally we've got it. Slice 4.1 enables you to cache your SliceResources created for a given path within a single request processing.This is especially helpful if you have a heavy object (which reads a lot of properties) and you are using it a couple of times within a single request, e.g. PageModel which keeps your page properties - this model may often be referenced by other components and there's no need to re-create it each time. Please note that caching can only be applied for resources which are not changed while a request is processed, otherwise such changes won't be reflected in the cached objects.

Additionally, caching works not only within request context but also in osgi-services context. It's basically based on Slice's @ContextScope.

To make your SliceResources cacheable per path you should simply annotate them with @Cacheable annotation.

@SliceResource
@Cacheable
public class PageModel {
    @JcrProperty("jcr:title")
    private String title;
    ...
}

Simpler OSGi service support

If you want to use OSGi services within your guicified code, you can simply use the Peaberry solution which allows you to bind an interface to OSGi services (Slice is shipped with Peaberry out of the box). Normally, you bind OSGi services in some Guice module, e.g.

public class MyAppModule extends AbstractModule {
    @Override
    public void configure() {
        bind(ResourceResolverFactory.class).toProvider(Peaberry.service(ResourceResolverFactory.class)).single());
        // other bindings
    }
}
  
public class MyClass {
    @Inject
    ResourceResolverFactory factory;
    ...
}

From now on, you can simplify this declaration and Slice will do the work for you. You just need to annotate the field or the constructor argument with @OsgiService and Slice will bind the interface/class to a Peaberry provider automatically when an injector is being created. Your code should look like this:

public class MyClass {
    @Inject
    @OsgiService
    ResourceResolverFactory factory;
    ...
}

Enhanced injector registration

By convention, Slice reads the injector name from the second part of a resource type, e.g. if you request a /apps/myapp/mycomponent resource, the injector name will be myapp. This may be limiting in some cases, especially if you have multiple applications running on a single Sling/AEM instance and you want to group them meaningfully. Slice 4.1. allows you to instruct Slice where to look for an application, e.g. /apps/plugins/my-super-plugin-app. In this case your injector name will be "my-super-plugin-app" instead of "plugins". You can do this by creating InjectorRunner appropriately in your application Activator, passing applicationPath argument, e.g.:

@Override
public void start(final BundleContext bundleContext) {
    final InjectorRunner injectorRunner = new InjectorRunner(bundleContext, INJECTOR_NAME,
                "/apps/plugins/my-super-plugin-app", BUNDLE_NAME_FILTER, BASE_PACKAGE);
    //modules installation
    injectorRunner.start();
}

Injector listener

Sometimes it's necessary to use Slice bindings during the activation of the OSGi component. It can't be done in the @Activate method, as there is a race between component activation and the application activator that creates the Slice injector - the result is that the Slice injector isn't created yet, so the OSGi component can't use it. The workaround is to use lazy loading and initialise data on the first request, but it may make the OSGi service design more complex.

The new InjectorListener interface allows to overcome this problem. It contains method injectorAvailable(String) that will be invoked for each injector that has already been created. Therefore, the initialisation logic may be moved there:

@Component
@Service
public class MyComponent implements InjectorListener {
    // we need to have this atomic boolean, as the injector may become available
    // a few times during component lifecycle
    private final AtomicBoolean initialized = new AtomicBoolean();
  
    @Activate
    protected void activate() {
        initialized.set(false);
    }
 
    @Override
    public void injectorAvailable(String injectorName) {
        if ("myapp".equals(injectorName) && !initialized.getAndSet(true)) {
            InjectorWithContext injector = InjectorUtil.getInjector("myapp", getResolver());
            // do something clever with the injector
        }
    }
}

Upgrading to Slice 4.1

Slice 4.1 is backward compatible with Slice 4.0 so moving to the newer version should be as simple as updating the version numbers in your pom files. Since the migration is really straightforward it is recommended that you do so for every project running Slice 4.0.0. If you are still running Slice 3 or 4.0.0-incubator you should follow instructions on how to upgrade to version 4.

Full documentation of Slice 4.1 can be found here: https://cognifide.atlassian.net/wiki/display/SLICE/Slice+4.1

Download Slice 4.1 from: https://cognifide.atlassian.net/wiki/display/SLICE/Downloads