This document discusses integration patterns in Adobe Experience Manager (AEM), including using Sling Models for dependency injection and accessing resources from different locations through a single ResourceResolver. It provides examples of integrating with backend APIs, databases, file systems and forms using techniques like ResourceProviders, Sling Filters and custom SlingServlets. It also covers accessing files from bundles and the filesystem using BundleResourceProvider and FsResourceProvider respectively.
3. • What do we integrate with?
– Backend API layer
• SAP PI, Oracle Fusion or other middleware layer.
• Bespoke API
– DB
– File System
– Forms Engine
4. • First questions when arriving to the scene.
– Is it exposed to WWW?
– What authentication does the layer require?
– What can I cache?
– Protocol and output formats.
5. • Exposed to WWW?
– Client side integration
– Mixed integration
• Not exposed to the WWW
– Server Side
– Tunneled through
6.
7. • SlingMainServlet
– Outermost Request Handler
– Starts Request Processing
• ResourceResolver
– Resolves the URL to a Resource
• ServletResolver
– Resolve the Resource Type to a Servlet/Script
8. • Resolve the Resource
– Source: Request URI
• Resolve Servlet or Script
– Source: Resource Type
• sling:resourceType
• sling:resourceSuperType
• Call Servlet Filters
• Call Servlet or Script
protocol host path selector extension
http:// myhost/ tools/spy .print.a4 .html /
suffix
a/b ?
param(s)
x=12
11. • Provides access to resources from different locations
through a single ResourceResolver
• Registered to a path in the virtual resource tree
• The last fallback is always the JCR repository
12. • PlanetResourceProvider Sample
• Respond with a relevant planet resource when calling
“/planets/<planet>”
https://svn.apache.org/repos/asf/sling/trunk/launchpad/test-services/
src/main/java/org/apache/sling/launchpad/testservices/resourceprovider/
13. @Component
@Service
@Properties({
@Property(name=ResourceProvider.ROOTS, value=PlanetsResourceProvider.ROOT)
})
public class PlanetsResourceProvider implements ResourceProvider {
private static final Map<String, ValueMap> PLANETS = new HashMap<String, ValueMap>();
/** This can be configurable of course */
public static final String ROOT = "planets";
public static final String ABS_ROOT = "/" + ROOT;
static {
definePlanet("Mercury", 57910);
definePlanet("Venus", 108200);
definePlanet("Earth", 149600).put("comment", "Resources can have different sets of properties");
definePlanet("Mars", 227940);
definePlanet("Jupiter", 4332);
definePlanet("Saturn", 10759);
definePlanet("Uranus", 30685);
definePlanet("Neptune", 60190);
// Add the moon to test a two-level hierarchy
final String moonPath = ABS_ROOT + "/earth/moon";
PLANETS.put(moonPath, new PlanetResource.PlanetValueMap("Moon", 384));
}
14. public Resource getResource(ResourceResolver resolver, HttpServletRequest req, String path) {
// Synthetic resource for our root, so that /planets works
if((ABS_ROOT).equals(path)) {
return new SyntheticResource(resolver, path, PlanetResource.RESOURCE_TYPE);
}
// Not root, return a Planet if we have one
final ValueMap data = PLANETS.get(path);
return data == null ? null : new PlanetResource(resolver, path, data);
}
15. public Iterator<Resource> listChildren(Resource parent) {
if(parent.getPath().startsWith(ABS_ROOT)) {
// Not the most efficient thing...good enough for this example
final List<Resource> kids = new ArrayList<Resource>();
for(Map.Entry<String, ValueMap> e : PLANETS.entrySet()) {
if(parent.getPath().equals(parentPath(e.getKey()))) {
kids.add(new PlanetResource(parent.getResourceResolver(), e.getKey(), e.getValue()));
}
}
return kids.iterator();
} else {
return null;
}
}
16. @Adaptable(adaptableClass=Resource.class, adapters={
@Adapter({ValueMap.class})
})
public class PlanetResource extends AbstractResource implements Resource {
private final String path;
private final ResourceMetadata metadata;
private final ValueMap valueMap;
private final ResourceResolver resolver;
public static final String RESOURCE_TYPE = "sling/test-services/planet";
PlanetResource(ResourceResolver resolver, String path, ValueMap valueMap) {
this.path = path;
this.valueMap = valueMap;
this.resolver = resolver;
metadata = new ResourceMetadata();
metadata.setResolutionPath(path);
}
17. – BundleResourceProvider provides access to files and directories
contained in an OSGi bundle
– The BundleResourceProvider is responsible for mapping the
directory tree into the resource tree
– It‘s most conveniently configured as instruction in the maven-bundle-
plugin:
<configuration>
<instructions>
<Sling-Bundle-Resources>/resource/tree;path:=/bundle/tree</Sling-Bundle-Resources>
</instructions>
</configuration>
18. – FsResourceProvider, which is part of org.apache.sling.fsresource,
maps file system paths as resources.
– Requires installation.
– Configuration through fsresource.xml:
• provider.roots
• Provider.files
– http://sling.apache.org/documentation/bundles/accessing-filesystem-resources-extensions-
fsresource.html
19. • Sling Models
– Creating an adaptable class from a POJO by annotations
• Blueprints
– Dependency injection framework for OSGi
– Implementations include SpringDM (Historical) and Neba.io
• Cognifide Slice framework
– God’s speed…
20. • Creat ing an adaptable class from a POJO by annotat ions
– Resource resource = getResource();
– Return resource.adaptTo(YourCustom.class);
– @Model(adaptables = Resource.class)
– public class YourCustom {
– ...
– }
• Use Sling Models for Controller or Business Logic that is
“context-aware”
• Use Value Map to read resource data
• http://sling.apache.org/documentation/bundles/models.html
21. • Use Sling Models for Dependency
Injection
– Inject Sling Context Objects via @SlingObject
– Inject AEM Context Objects via @AemObject
– Inject Business Classes via @Self
– Inject Resource Data or Parameters
• Dependencies can be mocked with tools like
Mockito@InjectMocks
22. • Injecting
@Model(adaptables= Resource.class) public class YourCustom{
@Inject // we expected always an email-property
private String email;
@Inject @Optional // first name can be empty
private String firstName;
// read property “surname” if empty use “empty”
@Inject @Named(“surname“) @Default(values=“empty“)
private String lastName;
}
23. @Model(adaptables= Resource.class)
Public class YourCustom{
@Inject// OSGiService
private Externalizer externalizer;
@PostConstruct
Protected void init() {
// gets executed after the class is created
// set to protected, so it can be unit-tested
}
}
24. @Model(adaptables= Resource.class)
Public class YourCustom{
//option1
@Self
private Resource resource;
//option2
public YourCustom(Resource resource) {
// to get access to the adaptor
this.resource= resource;
}
}
25. • Available for AEM6
– Can be installed in CQ5.6.1
– Sling Models Content Packages
– https://github.com/Adobe-Consulting-
Services/com.adobe.acs.bundles.sling-models/
releases
• What’s new in Sling Models 1.1
– http://adapt.to/2014/en/schedule/whats-new-in-sling-models-
11.html
26. • Recommended pattern: Controller per concern,
not per component!
• Controllers are Sling Models
• Component/View can re-use multiple controllers
Basic – what is an integration
Sling – some sling architecture and request processing
Integration – ResourceProvider integration and overview
Sample – A code walkthrough of a custom resource provider