Cognifide Labs
We create. We boost. We share.

Slice - make AEM development easier


Full Documentation

The full documentation documentation for Slice framework can be found at Slice Confluence. You can find there:

Slice concepts and quick start
Mapping Sling resources to Java objects
Sightly support
Upgrade and installation notes
Add-ons documentation
Slice roadmap and development processes

Slice API

API documentation is available at http://cognifide.github.io/Slice/apidocs/4-3-0/


Slice in a nutshell

Separation of concerns

No more business logic in your view (JSP, Sightly scripts) - business logic's place is in Java classes and Slice knows it!

Slice loves Sightly. Sightly loves Slice.

Both are perfect match. Seamless integration you will love:

<div data-sly-use.model="com.example.text.TextModel">
	<p>${model.text}<p>
</div>
JSPs made clean and tidy

no more these ugly scriptlets

<slice:lookup var="model" type="<%=com.example..text.TextModel%>" />
<p>${model.text}</p>
	

 

Reusable Java models which expose data for your view - note same model can be used for Sightly and JSP components - one model to feed them all!
@SliceResource
public class TextModel {

	@JcrProperty
	private String text;

	public String getText() {
		return text;
	}
}

Mapping resources into Java objects

Slice allows you to map a resource into plain Java object. It's annotation-driven, very easy to use and fully extensible, so you can write your own ways of mapping if a set of available features is not enough for your needs. Slice supports mapping of:

The following code snippet demonstrates all above features in one model. It's simple - just annotate a class with @SliceResource and its fields with @JcrProperty to get auto mapping of resource properties to class fields:

@SliceResource
public class ComplexTextModel {

    @JcrProperty
    private String text;

    @JcrProperty
    private String[] styles;

    @JcrProperty
    private AlignmentEnum alignment;

    @Children(LinkModel.class)
    @JcrProperty
    private List<LinkModel> links;

    @JcrProperty
    private ImageModel image;

    //... do whatever business logic you want in your model
}
public enum AlignmentEnum {
    LEFT, RIGHT, CENTER;
}

@SliceResource(MappingStrategy.ALL)
public class LinkModel {
    private String path;
    private String label;
    //...
}

@SliceResource(MappingStrategy.ALL)
public class ImageModel {
    private String imagePath;
    private String altText;
    //...
}

Dependency Injection with Google Guice

If your AEM components are more than simple text/image/title components (and they certainly are), then you probably need to combine their functionality with some external data or more complex business logic provided by other classes. Dependency injection allows you to do this easily and keep your code testable without boiler-plate code and unneeded arguments in methods used only to propagate a value down into the class hierarchy.

We took Guice as a DI container. Why it's awesome? Take a look here: Google I/O 2009 - Big Modular Java with Guice, and here to check motivation of Google Guice creators

To demonstrate an example of a complex component which combines use of Slice features with power of DI, take a look at the following code which is an implementation of a Twitter component.

<div data-sly-use.model="com.example.components.twitter.TwitterModel">
    <ul data-sly-list="${model.tweets}">
        <li>${item}</li>
    </ul>
</div>

The model of the component is fairly simple and fully testable because you can easily mock the TwitterHandler in your unit test case.

@SliceResource
public class TwitterModel {

    @JcrProperty
    private int limit;

    private final TwitterHandler twitterHandler;

    @Inject
    public TwitterModel(TwitterHandler twitterHandler) {
        this.twitterHandler = twitterHandler;
    }

    //returns list of tweets limited by number configurable by authors
    public List<String> getTweets() {
        List<String> tweets = twitterHandler.readTweets();
        return tweets.subList(0, Math.max(tweets.size(), limit));
    }

    //...
}

TwitterHandler is also very simple as it uses Twitter client (from Twitter4j library). Please note that this client is injected by Guice and you don't have to care about its configuration in the handler itself.

public class TwitterHandler {

    @Inject
    private Twitter twitterClient; //Twitter4j client

    public List<String> readTweets() {
        List<String> tweets = new ArrayList<String>();
        List<Status> statuses = twitterClient.getHomeTimeline();
        for (Status status : statuses) {
            tweets.add(status.getText());
        }
        return tweets;
    }
}

The configuration is set while instantiating the twitter client by Guice. To instruct Guice how to create the client object we need to create a so called provider. You can do this in module configuration. It reads some configuration properties from repository (using ModelProvider). ContextScope instructs Guice to create such an object only once per request or OSGi service call - yes, you can reuse the TwitterHandler in you OSGi services which are request/resource agnostic - that's the power of Slice!.

public class MyModule extends AbstractModule {

    private static final String TWITTER_CONFIG_PATH = "/etc/twitter/configuration/jcr:content/twitterConfig";

    @Provides
    @ContextScoped
    public Twitter getTwitter(ModelProvider modelProvider) {
        TwitterConfiguration config = modelProvider.get(TwitterConfiguration.class,
                TWITTER_CONFIG_PATH);
        ConfigurationBuilder builder = new ConfigurationBuilder(); // from Twitter4j
        builder.setOAuthConsumerKey(config.getOAuthKey())
                .setOAuthConsumerSecret(config.getOAuthSecret());
        TwitterFactory factory = new TwitterFactory(builder.build());
        return factory.getInstance();
    }

    //...

}