Another question in my ongoing saga to implement a customised Deployer without requiring a Broker database.

When publishing a taxonomy I can see the childKeyword nodes listed in the XML for any given keyword. For example:

<tcmc:locations rdf:about="tcm:2260-13571-1024">
    <rdfs:label>The Middle East (ME)</rdfs:label>
    <rdfs:comment>The Middle East (ME)</rdfs:comment>
    <tcmt:metadata rdf:parseType="Literal"/>
    <tcmt:childKeyword rdf:resource="tcm:2260-13572-1024"/>
    <tcmt:childKeyword rdf:resource="tcm:2260-13573-1024"/>
    <tcmt:childKeyword rdf:resource="tcm:2260-13574-1024"/>

I'm implementing a TaxonomyDAO to store keywords within Couchbase. All of this is working fine, I've implemented the various methods to store each item in the taxonomy, and able remove the entire taxonomy.

I would like to be able to store a references to a Keyword's children within the Couchbase document. The TaxonomyItem passed to the store method has a hasChildren boolean, but no property to return the actual child items themselves.

My question: Is there a mechanism to obtain a list of child keywords for a keyword, when publishing a taxonomy? A list of Ids will do.

2 Answers 2


I've got similar issue before and could not find such methods. Then I fell back to a deployer extension for taxonomy and keyword publishing, while using storage extension for pages and components. It's been working well so far, and extending Module class gives you API accesses with taxonomy & all levels of keywords

If it's suitable here is some snippets (you probably want to do some compiling)

package com.tridion.deployer.modules;

public class YourExtensionTaxonomyDeploy extends Module {
    TransportPackage currentPackage;
    String _transactionId;
    private String storageId = "";
    private static final DocumentBuilderFactory DBF = DocumentBuilderFactory.newInstance();
    private Logger log = LoggerFactory.getLogger(YourExtensionTaxonomyDeploy.class);
public YourExtensionTaxonomyDeploy(Configuration config, Processor processor)
        throws ConfigurationException {
    super(config, processor);

protected boolean setup() {
    return true;

public void process(TransportPackage data) throws ProcessingException {
    currentPackage = data;
    Section taxonomySection = data.getProcessorInstructions().getSection("Taxonomies");
    if ((setup()) && (taxonomySection != null))
        Iterator iterator = taxonomySection.getFileItems();
        log.debug("Processing section " + taxonomySection.getName());
        while (iterator.hasNext())
            Object item = iterator.next();
            processItem(item, taxonomySection);

protected void processItem(Object item, Section taxonomySection)
        throws ProcessingException
    try {
        if ((item instanceof TaxonomyKey))
            TaxonomyKey transportItem = (TaxonomyKey)item;

            TCMURI itemURI = transportItem.getId();
            String itemName = transportItem.getName();

            boolean isInUseForIdentification = Boolean.valueOf(transportItem.isUseForIdentification()).booleanValue();

            File taxonomyFile = new File(currentPackage.getLocationPath() + taxonomySection.getRelativePath() + itemName);

            Map schemaTypeMaps = new HashMap();
            ItemDeploySchemaManager itemDeploySchemaManager = new ItemDeploySchemaManager(currentPackage);
            if (transportItem.getSchemas() != null) {
                for (SchemaKey schema : transportItem.getSchemas())
                    TCMURI schemaURI = schema.getId();
                    if ((schemaURI != null) && (!schemaURI.equals("tcm:0-0-0")))
                        String simulateDom = "<Schema Id=\"" + schemaURI.toString() + "\"/>";
                            Document schemaDoc = DBF.newDocumentBuilder().parse(new InputSource(new StringReader(simulateDom)));
                            GenericTransportItem tpItem = new GenericTransportItem(schemaDoc);
                            log.debug("Schema present for Taxonomy, loading schema information: " + schemaURI);
                            itemDeploySchemaManager.setSchemaInformationOnItem(tpItem, schemaURI);

                            schemaTypeMaps.put(tpItem.getSchemaNamespace(), tpItem.getTypeMap());
                        catch (ParserConfigurationException pce)
                            log.warn("Got configuration exception while trying to build other transport items: " + pce.getMessage());
                        catch (IOException ioe)
                            log.warn("Got IO exception while trying to build other transport items: " + ioe.getMessage());
                        catch (SAXException saxe)
                            log.warn("Got SAX exception while trying to build other transport items: " + saxe.getMessage());
            TaxonomyCMTransformer taxonomyTransformer = new TaxonomyCMTransformer(schemaTypeMaps, isInUseForIdentification);
            Collection<Keyword> taxonomyMetaFacets = taxonomyTransformer.transformTaxonomy(taxonomyFile);

            Iterator taxIterator = taxonomyMetaFacets.iterator();
            String taxonomyName = "";
            int i = 0;
            while (taxIterator.hasNext())

                Object oKeyword = taxIterator.next();
                if(oKeyword instanceof Keyword)
                    Keyword keyword = (Keyword) oKeyword;
                        //taxonomy root, do your own logic
                        processKeyword(keyword, itemURI, taxonomyName);

    } catch (Exception ex) {
        throw new ProcessingException("error " + taxonomySection, ex);


protected void processKeyword(Keyword keyword, TCMURI taxonomyURI, String taxonomyName) throws ProcessingException
    //do your thing
  • Not another extension point! Thanks Hao, I'll take a look at this, thanks for your input. Commented May 23, 2017 at 16:55

This can be achieved without a Deployer extension. Within my storage extension CouchbaseFactoryDAO there are a few useful methods which handle the transaction:

  • startTransaction
  • commitTransaction
  • rollbackTransaction
  • rollbackTimedOutTransaction

I created a map of transaction objects. Each transaction object contains a map of TaxonomyItems (as well as associated ItemMeta), that is populated by the store methods of the CouchbaseTaxonomyDAO, and CouchbaseItemDAO classes.

When the factory commitTransaction method is called, I have all the information in my transaction object. Iterate through the maps, merging the metadata, and setting the child references (as each Keyword has it's parent id).

Once this is complete, it's just a case of inserting into Couchbase.

  • Out of curiosity, is this something you're planning to open source or make otherwise widely available?
    – Nuno Linhares
    Commented May 26, 2017 at 11:55
  • Hi Nuno. It's obviously for a client, but if I get some time, I can expand upon the principle. Just understanding the concept of a transaction within the factory should be enough to get someone who builds these things, on the right track. Then again, it's not the easiest thing to find documentation on. Commented May 26, 2017 at 12:57
  • Cheers, thanks. Could be a nice one for a "Community initiative" in the future.
    – Nuno Linhares
    Commented May 26, 2017 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.