Many times over we need to implement a Label service mechanism in our projects. This post shows one such approach for DD4T Java.
The idea is to have a Labels Page published from Tridion containing the Labels Component on it. The labels are simply multi-valued Key - Value pairs represented by an Embedded Schema.
The Labels page is read in DD4T and the labels on it are parsed and loaded into a java.util.Map. The front end code employs a custom JSP tag to read the label map and display the associated value for a given key.
The load method uses specialized model view classes Label and EmbeddedLabel:
Finally, the whole mechanism can be used from a JSP Tag or function like this:
The idea is to have a Labels Page published from Tridion containing the Labels Component on it. The labels are simply multi-valued Key - Value pairs represented by an Embedded Schema.
The Labels page is read in DD4T and the labels on it are parsed and loaded into a java.util.Map. The front end code employs a custom JSP tag to read the label map and display the associated value for a given key.
Content Manager Part
We use Schema Label with a field 'label' of type Embedded Schema, multi-valued.
The Embedded Schema is 'Embedded label' and contains two field, both single value text fields: 'key' and 'value'.
We create a Component 'Labels' and populate it with our labels. This Component can be localized and translated in sub-Publications, as the Label Service can use context-aware Components to load the set of labels in the right locale.
Further, we place the Labels Component on a Page and publish it with DD4T Component and Page Templates.
DD4T Content Delivery Part
Everything starts with an interface - LabelService, which we define in the dd4t-api project, in package org.dd4t.core.resolvers:
public abstract interface LabelService {
public Map<String, String> load(int publicationId) throws IOException;
public String getLabel(String key) throws IOException;
public String getLabel(String key, int publicationId) throws IOException;
}
The method load is responsible for loading the label Map. The other two getLabel method retrieve a label for a given key potentially in a given Publication context.
An implementation of the interface is in class LabelServiceBase, part of project dd4t-core, in package org.dd4t.core.resolvers.impl:
public abstract class LabelServiceBase implements LabelService {
@Autowired
protected PublicationResolver publicationProvider;
@Autowired
protected CacheProvider cacheProvider;
@Override
public String getLabel(String key) throws IOException {
return getLabel(key, publicationProvider.getPublicationId());
}
@Override
public String getLabel(String key, int publicationId) throws IOException {
String mapKey = getMapKey(publicationId);
Map<String, String> labelMap = (Map<String, String>) cacheProvider.loadFromLocalCache(mapKey);
if (labelMap == null) {
synchronized (mapKey) {
labelMap = (Map<String, String>) cacheProvider.loadFromLocalCache(mapKey);
if (labelMap == null) {
labelMap = load(publicationId);
if (labelMap == null) {
return key;
}
}
}
}
String result = labelMap.get(key);
return result == null ? key : result;
}
protected String getMapKey(int publicationId) {
return "LabelMap-" + publicationId;
}
}
The base implementation makes use of a PublicationResolver that provides the current Publication context, when a Publication is not specified. It also uses a CacheProvider (from package org.dd4t.providers) that provides a generic way to store the label maps. These objects are provided using Spring's Dependency Injection mechanism.
The main method in the class - getLabel tries to retrieve a particular label, and if not found in cache, it attempts to (re-)load the labels for the given context Publication.
The implementation of the load method is left to be specified by the actual DD4T web application layer. Such an implementation makes use of a properties file to read the URL of the Labels page. Then the PageFactory reads the page model and we extract the Label key--value pairs from it.
The implementation of the load method is left to be specified by the actual DD4T web application layer. Such an implementation makes use of a properties file to read the URL of the Labels page. Then the PageFactory reads the page model and we extract the Label key--value pairs from it.
public class MyLabelService extends LabelServiceBase {
private static final MyLabelService _instance = new MyLabelService();
private final String labelPageURL;
private MyLabelService() throws IOException {
InputStream input = LabelServiceBase.class.getClassLoader().getResourceAsStream("myapp.properties");
Properties properties = new Properties();
properties.load(input);
labelPageURL = properties.getProperty("labelpage.url");
}
public static MyLabelService getInstance() {
return _instance;
}
@Override
public synchronized Map<String, String> load(int publicationId) throws IOException {
Map<String, String> result = new TreeMap<>();
try {
PageFactory pageFactory = PageFactory.getInstance();
GenericPage labelPage = (GenericPage) pageFactory.findPageByUrl(labelPageURL, publicationProvider.getPublicationId());
List<ComponentPresentation> componentPresentations = labelPage.getComponentPresentations();
ComponentPresentation componentPresentation = componentPresentations.get(0);
GenericComponent labelComponent = componentPresentation.getComponent();
Label label = (Label) ModelFactory.createInstance(labelComponent);
String mapKey = getMapKey(publicationId);
for (EmbeddedLabel embeddedLabel : label.getLabelList()) {
String key = embeddedLabel.getKey();
String value = embeddedLabel.getValue();
result.put(key, value);
}
int itemId = new TCMURI(labelPage.getId()).getItemId();
cacheProvider.storeInItemCache(mapKey, result, publicationId, itemId);
} catch (ItemNotFoundException | FilterException | ModelException | ParseException | SerializationException e) {
throw new IOException("Failed to load labels", e);
}
return result;
}
}
@TridionModel(rootElementName = "label")
public class Label extends BaseModel {
@TridionField(fieldName = "label", fieldType = TridionFieldType.Embedded)
private List<EmbeddedLabel> labelList;
public List<EmbeddedLabel> getLabelList() {
return labelList;
}
public void setLabelList(List<EmbeddedLabel> labelList) {
this.labelList = labelList;
}
}
@TridionModel(rootElementName = "embeddedLabel")
public class EmbeddedLabel extends BaseModel {
@TridionField
private String key;
@TridionField
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
MyLabelService labelService = MyLabelService.getInstance();
String seeDetailsLabel = labelService.getLabel("seedetails");
Comments