May 13, 2020
Estimated Post Reading Time ~

Services in AEM

Services in AEM run in the OSGI framework. By default, they don't have access to the user's session as they may not have been called from within a user request.
package com.sample.core.services.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.References;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.Resource;

import com.sample.core.services.Externalizer;

@Component(immediate = true, label = "Externalizer Service", description = "Custom AEM service for getting external urls", metatype = true, enabled = true)
@Service
@Properties({
@Property(name = "path", label = "Path", description = "The path these settings are for"),
@Property(name = "author", label = "Author", description = "The author url"),
@Property(name = "dispatcher", label = "Dispatcher", description = "The dispatcher url") })
@References({
@Reference(name = "amendment", referenceInterface = ExternalizerConfigAmendment.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC, updated = "updateAmendment") })
public class ExternalizerService implements Externalizer {
private static final String SERVICE_ID = "service.id";
private static final String HTTP_LOCALHOST = "http://localhost";
private static final String HTTP_LOCALHOST_4502 = "http://localhost:4502";
private Map<Long, ExternalizerConfigAmendment^gt; amendments = new HashMap><();

private List<ExternalizerConfigAmendment> sortedMappings = new ArrayList<>();
@Activate
@Modified
void configure(final Map<String, Object> config) {
synchronized (this.amendments) {
this.updateMappings();
}
}

private void updateMappings() {
sortedMappings = new ArrayList<>();
for (final ExternalizerConfigAmendment amendment : this.amendments.values()) {
sortedMappings.add(amendment);
}
Collections.sort(sortedMappings);
}

@Override
public String getAuthor(Resource page) {
for (ExternalizerConfigAmendment config : sortedMappings) {
if (config.isValidFor(page.getPath())) {
return config.getAuthor();
}
}
return HTTP_LOCALHOST_4502;
}

@Override
public String getDispatcher(Resource page) {
for (ExternalizerConfigAmendment config : sortedMappings) {
if (config.isValidFor(page.getPath())) {
return config.getDispatcher();
}
}
return HTTP_LOCALHOST;
}

protected void bindAmendment(final ExternalizerConfigAmendment amendment, final Map<String, Object> props) {
final Long key = (Long) props.get(SERVICE_ID);
synchronized (this.amendments) {
amendments.remove(key); amendments.put(key, amendment);
this.updateMappings();
}
}

protected void unbindAmendment(final ExternalizerConfigAmendment amendment, final Map<String, Object> props) {
final Long key = (Long) props.get(SERVICE_ID);
synchronized (this.amendments) {
if (amendments.remove(key) != null) {
this.updateMappings();
}
}
}

protected void updateAmendment(final ExternalizerConfigAmendment amendment, final Map<String, Object> props) {
this.bindAmendment(amendment, props);
}
}
As mentioned on the OSGI Components page service is a special type of component that will generally have some logic.
The type of service is defined in the Service annotation, In the above service the annotation is empty so it defines it as a service of itself only, but you can define a service of a specific type as shown in the Listeners page, an event listener is of type EventHandler
@Service(value = EventHandler.class)
This is useful for when you want to get a reference to a service, you can lookup all services registered for a specific type of class using the slingScriptHelper's getServices method.
While usually, a service supports a single type, it is possible to have a service that supports multiple types as in the following example
@Service(value = {EventHandler.class, JobListener.class, OurClass.class})
While this is possible, and have may well use when the class has multiple entry points, it is recommended that each class has a specific purpose so should only be used as a single type.
Because service is a component it means that you can reference other components using the reference annotation to get access to another component
@Reference
private JobManager jobManager;
While you can write a service that doesn't have an interface, it is considered bad practice and all services should implement an interface that the service annotation references


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.