Developer's Guide

Implementing query handlers

This sample will provide four handlers in total:

Create the following classes for each handler under src/org.eclipse.cosmos.example.mdr.handlers.

  1. ItemTemplateHandler
  2. ItemInstanceIdHandler
  3. ItemRecordTypeHandler
  4. RelationshipTemplateHandler

In addition to the four classes above, create an interface called "ICMDBfSampleConstants" under the same package to contain constants commonly used between handlers. The content of the interface, ICMDBfSampleConstants, appears below.

package org.eclipse.cosmos.example.mdr.handlers;
 
/**
 * Constants used by this CMDBf query sample
 */
public interface ICMDBfSampleConstants
{
	/**
	 * The key for the data provider that will be
	 * stored in the initialization data of the constraint
	 * handlers
	 */
	public static final String DATA_PROVIDER = "org.eclipse.cosmos.samples.cmdbf.services.query.ICMDBfSampleConstants"; 
}

ItemTemplateHandler is there to simply process item templates that do not contain any constraints. The content of the class appears below:

package org.eclipse.cosmos.example.mdr.handlers;
 
import org.eclipse.cosmos.dc.provisional.cmdbf.services.common.CMDBfServiceException;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.impl.AbstractItemTemplateHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.IItemConvertible;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.INodes;
 
/**
 * An item template handler is invoked prior to invoking any constraint handler
 * for item templates.
 */
public class ItemTemplateHandler extends AbstractItemTemplateHandler
{
 
	/**
	 * This method is only invoked if there are no constraints included in an
	 * item template.
	 * 
	 * @param nodes The nodes element to append items to
	 */
	protected void appendAllItems(INodes nodes) throws CMDBfServiceException
	{
		XMLRepository repo = (XMLRepository)getValue(ICMDBfSampleConstants.DATA_PROVIDER);
		addItems(nodes, repo.classes);
		addItems(nodes, repo.students);
		addItems(nodes, repo.teachers);
	}
 
	
	/**
	 * A convenient method used to add individual nodes to the 
	 * INodes instance passed in.
	 * 
	 * @param nodes Container for individual nodes
	 * @param itemConvertibles Elements that are convertible to an Item
	 */
	private void addItems(INodes nodes, IItemConvertible[] itemConvertibles)
	{
		for (int i = 0; i < itemConvertibles.length; i++)
		{
			nodes.addItem(itemConvertibles[i].toItem(nodes));
		}
	}
}

The instance id constraint is used to locate items based on the id attribute of the student/teacher element or the course code defined for a class element. The implementation simply walks through students, teachers, and classes to locate items that match the instance id constraint. The content of ItemInstanceIdHandler appears below:

package org.eclipse.cosmos.example.mdr.handlers;
 
import org.eclipse.cosmos.dc.provisional.cmdbf.services.common.CMDBfServiceException;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.impl.AbstractItemConstraintHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.input.artifacts.IConstraint;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.input.artifacts.IInstanceIdConstraint;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.INodes;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.QueryOutputArtifactFactory;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IInstanceId;
import org.eclipse.cosmos.example.mdr.handlers.XMLRepository.SchoolMember;
 
/**
 * This is the handler class for instance id constraints included
 * in item templates.  Adopters can either provide a direct implementation of
 * IItemConstraintHandler or extend AbstractItemConstraintHandler
 */
public class ItemInstanceIdHandler extends AbstractItemConstraintHandler
{		
	@Override
	protected INodes handle(INodes context, IConstraint constraint) throws CMDBfServiceException
	{
		INodes result = QueryOutputArtifactFactory.getInstance().createNodes(context.getId()); 
		IInstanceIdConstraint instanceIdConstraint = (IInstanceIdConstraint)constraint;
		IInstanceId[] instanceIds = instanceIdConstraint.getInstanceIds();
		for (int i = 0; i < instanceIds.length; i++) {
			if (!XMLRepository.MDR_ID.equals(instanceIds[i].getMdrId()
					.toString())) {
				continue;
			}
			String localId = instanceIds[i].getLocalId().toString();
			XMLRepository repo = (XMLRepository) getValue(ICMDBfSampleConstants.DATA_PROVIDER);
			// Traverse the students
			traverseSchoolMembers(result, localId, repo.students);
			// Traverse the teachers
			traverseSchoolMembers(result, localId, repo.teachers);
			// Traverse the classes
			for (int j = 0; j < repo.classes.length; j++) 
			{
				if (localId.equals(repo.classes[j].courseCode)) 
				{				
					result.addItem(repo.classes[j]);
				}
			}
		}
		return result;
	}
	
	private void traverseSchoolMembers (INodes result, String localId, SchoolMember[] members)
	{
		for (int i = 0; i < members.length; i++)
		{
			if (localId.equals(members[i].identity.id))
			{
				result.addItem(members[i]);
			}
		}
	}
}

Similarly, ItemRecordTypeHandler is used to process record type constraints. The handler supports three record types: student, teacher, and class. The content of ItemRecordTypeHandler appears below.

package org.eclipse.cosmos.example.mdr.handlers;
 
import org.eclipse.cosmos.dc.provisional.cmdbf.services.common.CMDBfServiceException;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.IItemConstraintHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.impl.AbstractItemConstraintHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.input.artifacts.IConstraint;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.input.artifacts.IRecordType;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.INodes;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.QueryOutputArtifactFactory;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IGraphElement;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IItem;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IRecord;
import org.eclipse.cosmos.example.mdr.handlers.XMLRepository.ClassSession;
import org.eclipse.cosmos.example.mdr.handlers.XMLRepository.SchoolMember;
import org.eclipse.cosmos.example.mdr.handlers.XMLRepository.Student;
import org.eclipse.cosmos.example.mdr.handlers.XMLRepository.Teacher;
 
/**
 * This is the handler for the record type constraint specified in 
 * and item template.  Adopters can either extend AbstractItemConstraintHandler
 * or provide a direct implementation of {@link IItemConstraintHandler}
 */
public class ItemRecordTypeHandler extends AbstractItemConstraintHandler
{
	/**
	 * Record type representing student
	 */
	private static final String RECORD_TYPE_STUDENT = "student";
	
	/**
	 * Record type representing teacher
	 */
	private static final String RECORD_TYPE_TEACHER = "teacher";
	
	/**
	 * Record type representing class
	 */
	private static final String RECORD_TYPE_CLASS = "class";
	
 
	@Override
	protected INodes handle(INodes context, IConstraint constraint) throws CMDBfServiceException
	{
		IRecordType recordType = (IRecordType)constraint;
		String localName = recordType.getLocalName();
		XMLRepository repo = (XMLRepository)getValue(ICMDBfSampleConstants.DATA_PROVIDER);
		INodes result = QueryOutputArtifactFactory.getInstance().createNodes(context.getId());
		
		// If the record type constraint includes all items as its context
		if (context.isStartingContext())
		{			
			if (RECORD_TYPE_STUDENT.equals(localName))
			{
				addSchoolMembers(result, repo.students);
			}
			else if (RECORD_TYPE_TEACHER.equals(localName))
			{
				addSchoolMembers(result, repo.teachers);
			}
			else if (RECORD_TYPE_CLASS.equals(localName))
			{
				for (int i = 0; i < repo.classes.length; i++)
				{
					result.addItem(repo.classes[i]);
				}
			}
			
			return result;
		}
		
		IGraphElement[] elements = context.getElements();
		for (int i = 0; i < elements.length; i++)
		{
			IRecord[] records = elements[i].getRecords();
			for (int j = 0; j < records.length; j++)
			{
				if (RECORD_TYPE_STUDENT.equals(localName) && records[j].getValue() instanceof Student)
				{
					result.addItem((IItem)elements[i]);
					continue;	
				}
				else if (RECORD_TYPE_TEACHER.equals(localName) && records[j].getValue() instanceof Teacher)
				{
					result.addItem((IItem)elements[i]);
					continue;
				}
				else if (RECORD_TYPE_CLASS.equals(localName) && records[j].getValue() instanceof ClassSession)
				{
					result.addItem((IItem)elements[i]);
					continue;
				}
			}
		}
		
		return result;
	}
 
	private void addSchoolMembers(INodes result, SchoolMember[] members)
	{
		for (int i = 0; i < members.length; i++)
		{
			result.addItem(members[i]);
		}
	}
}

Finally, RelationshipTemplateHandler is used to process one type of relationship: i.e. the relationship between a teacher and a student. Given that there is only one relationship type defined, the input is processed in a relationship template handler. Typically this would be defined in a record type constraint for relationship templates. The class always assumes that the source is a teacher and the target is a student. The relationship is always teacher X teaches student Y. The code for RelationshipTemplateHandler appears below:

package org.eclipse.cosmos.example.mdr.handlers;
 
import java.util.ArrayList;
import java.util.List;
 
import org.eclipse.cosmos.dc.provisional.cmdbf.services.common.CMDBfServiceException;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.IRelationshipTemplateHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.impl.AbstractQueryHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.input.artifacts.IRelationshipTemplate;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.IEdges;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.IQueryResult;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.QueryOutputArtifactFactory;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IItem;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IRecord;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IRelationship;
import org.eclipse.cosmos.example.mdr.handlers.XMLRepository.ClassSession;
import org.eclipse.cosmos.example.mdr.handlers.XMLRepository.Student;
import org.eclipse.cosmos.example.mdr.handlers.XMLRepository.Teacher;
 
/**
 * Represents the relationship record type handler.  There is only one type
 * of relationship that this handler accepts: "teaches".  The "teaches"
 * relationship can only exist between a teacher A and a student B (i.e.
 * A teaches B). 
 * Adopters can either extend AbstractRelationshipConstraintHandler or provide
 * a direct implementation of IRelationshipConstraintHandler
 */
public class RelationshipTemplateHandler extends AbstractQueryHandler implements IRelationshipTemplateHandler
{		
	public IEdges execute(IQueryResult context, IRelationshipTemplate relationshipTemplate, IItem source, IItem target) throws CMDBfServiceException
	{
		IEdges results = QueryOutputArtifactFactory.getInstance().createEdges(relationshipTemplate);
		IRecord[] sourceRecords = source.getRecords();
		IRecord[] targetRecords = target.getRecords();
				
		// Only one record is expected
		if (sourceRecords.length != 1 || targetRecords.length != 1)
		{
			return results;
		}
		
		if (sourceRecords[0].getValue() instanceof Teacher && targetRecords[0].getValue() instanceof Student)
		{
			XMLRepository repo = (XMLRepository)getValue(ICMDBfSampleConstants.DATA_PROVIDER);		
			String teacherId = ((Teacher)sourceRecords[0].getValue()).identity.id;
			String studentId = ((Student)targetRecords[0].getValue()).identity.id;
			
			ClassSession[] classSessions = findClass (repo, teacherId, studentId);
			for (int i = 0; i < classSessions.length; i++)
			{
				IRelationship relationship = classSessions[i].toRelationship(results);
				relationship.setSourceId(QueryOutputArtifactFactory.getInstance().createInstanceId(XMLRepository.MDR_ID, teacherId));
				relationship.setTargetId(QueryOutputArtifactFactory.getInstance().createInstanceId(XMLRepository.MDR_ID, studentId));
				results.addRelationship(relationship);
			}
		}
				
		return results;
	}
	
	private ClassSession[] findClass(XMLRepository repo, String teacherId, String studentId)
	{
		List<ClassSession> discoveredClasses = new ArrayList<ClassSession>();		
		ClassSession[] classes = repo.classes;
		for (int i = 0; i < classes.length; i++)
		{
			if (teacherId.equals(classes[i].teacher.identity.id))
			{
				Student[] students = classes[i].students;
				for (int j = 0; j < students.length; j++)
				{
					if (studentId.equals(students[j].identity.id))
					{
						discoveredClasses.add(classes[i]);
					}
				}
			}
		}
		
		return discoveredClasses.toArray(new ClassSession[discoveredClasses.size()]);
	}	
}

This completes the implementation for all supported query handlers. What remains to be done is the factory class to instantiate instances of each handler. Open the generated class org.eclipse.cosmos.example.mdr.handlers.QueryHandlerFactory and add the following content.

package org.eclipse.cosmos.example.mdr.handlers;
 
import org.eclipse.cosmos.dc.provisional.cmdbf.services.common.CMDBfServiceException;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.IItemConstraintHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.IItemTemplateHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.IRelationshipTemplateHandler;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.service.impl.AbstractQueryHandlerFactory;
 
/**
 * This factory class is used to create handlers for the selectors 
 * that are applicable to the XML repository MDR.  Adopters can either 
 * extend AbstractSelectorHandlerFactory or provide a direct 
 * implementation of ISelectorHandlerFactory
 */
public class QueryHandlerFactory extends AbstractQueryHandlerFactory
{
	private static QueryHandlerFactory instance;
	
	/**
	 * Make the constructor invisible
	 */
	private QueryHandlerFactory()
	{
	}
	
	public static QueryHandlerFactory getInstance()
	{
		if (instance == null)
		{
			instance = new QueryHandlerFactory();
		}
		return instance;
	}
	
	@Override
	protected IItemConstraintHandler createItemInstanceHandler()
	{	
		return new ItemInstanceIdHandler();
	}
	
	@Override
	protected IItemConstraintHandler createItemRecordHandler()
	{
		return new ItemRecordTypeHandler();
	}
	
	@Override
	protected IItemTemplateHandler createItemHandler() throws CMDBfServiceException
	{
		return new ItemTemplateHandler();
	}
	
	@Override
	protected IRelationshipTemplateHandler createRelationshipHandler() throws CMDBfServiceException
	{
		return new RelationshipTemplateHandler();
	}
}


[ Top of Page | Previous Page | Next Page | Table of Contents | Index ]