FRAMEWORK 101: Building XCF - Lesson 03 - Implementing Convention

November 5th, 2006

acomplia online cialis cost cialis online pharmacy viagra for order cheapest accutane cheap cialis viagra uk purchase viagra online where to order cialis purchase viagra no rx cheap price cialis accutane sale find discount cialis find discount viagra online generic cialis cheap cialis in uk buy cialis from canada acomplia online stores viagra for sale lowest price for cialis buy viagra on line price of viagra drug viagra online purchase 25mg viagra viagra cost viagra cheap price purchase cialis online buy viagra lowest price pharmacy viagra best price for viagra purchase accutane cialis pills tablet viagra cheap acomplia online order viagra overnight delivery buy cialis overnight delivery viagra in bangkok cialis bangkok purchase cialis no rx cheap cialis no rx purchase acomplia online cheap generic cialis cheapest cialis viagra in us cialis in bangkok order viagra online best price viagra viagra malaysia where to buy acomplia cheapest cialis prices cialis cheap buying cialis order generic viagra viagra order buying generic viagra no prescription cialis buy accutane buy viagra no rx buy cheapest viagra buy acomplia cheap order viagra no prescription required buy viagra in us 20mg cialis order viagra in us acomplia cheap buying viagra viagra cheap buy cheap viagra online fda approved viagra cialis information cost of viagra find cheap cialis cialis from canada purchase viagra overnight delivery acomplia discount fda approved cialis viagra rx find no rx cialis viagra discount certified cialis cialis without a prescription accutane acomplia pharmacy cialis overnight shipping viagra australia sale cialis buy sildenafil citrate buy cialis from us online viagra cialis soft acomplia prices buy discount cialis viagra pill viagra prices buy viagra overnight delivery online cialis order cialis cheap online discount viagra overnight delivery viagra online without prescription compare viagra prices online cheapest generic cialis accutane online overnight cialis cheap cialis without prescription buy sildenafil in uk viagra no rx cialis cheap price 50mg viagra buy accutane online erectile dysfunction cheap viagra overnight delivery cheap cialis tablets viagra pharmacy online acomplia without a prescription cheap accutane tablets buy cialis without prescription lowest price acomplia cialis tablet buy generic accutane find cialis without prescription order accutane cheap cialis overnight delivery cialis price cialis from india cialis no rx buy discount cialis online cialis overnight buy acomplia without prescription cialis tablets cheap viagra in usa buy viagra in canada cialis pharmacy online order cialis in us discount cialis 20 mg cialis acomplia online cheap cheapest viagra online cialis prescription order cialis on internet buy sildenafil in spain buy generic cialis cheap cialis in canada viagra tablets accutane without prescription cialis canada buy cheap acomplia online cheap viagra cheap cialis pharmacy find cialis on internet acomplia prescription buy cialis cheap order cheap cialis online find cialis no prescription required viagra overnight delivery buy viagra low price compare cialis prices 25 mg viagra order viagra no rx viagra online buy cialis no rx impotence treatment impotence cure viagra overnight online pharmacy cialis viagra online cheap find cialis online generic cialis online no rx cialis accutane generic cheapest acomplia prices buy viagra online cheap buy viagra internet acomplia no prescription pfizer viagra drug cialis online purchase order discount cialis cialis 10mg best price for cialis cheap viagra from usa find discount viagra cialis pill order viagra without prescription viagra generic purchase viagra without prescription buy acomplia online accutane cheap order viagra from canada cialis online without prescription cheap accutane cialis side effects cialis for sale buy cheap accutane online cialis generic discount cialis without prescription buy viagra from canada buy cialis internet discount viagra no rx viagra without a prescription drug cialis cialis pharmacy buy cialis generic cialis prices discount accutane order cialis no prescription impotence drugs cost viagra acomplia generic buy viagra online impotence medication order cialis from canada buy discount viagra acomplia acomplia pills purchase accutane online pharmacy online buy sildenafil internet buy accutane cheap viagra canada discount cialis online order cialis overnight delivery where to buy viagra cheap accutane online find no rx viagra cheap cialis from uk lowest price for viagra cheap viagra from canada approved cialis pharmacy 10 mg cialis viagra in uk cialis vs viagra cialis internet cheapest viagra prices accutane prescription buy cialis in us low cost cialis cialis buy online pharmacy cialis buying generic cialis purchase cialis overnight delivery buy discount viagra online order no rx viagra acomplia without prescription viagra buy order viagra from us viagra internet find discount cialis online acomplia for sale purchase cialis without prescription cialis drug discount viagra accutane prices buy sildenafil canada viagra in malaysia accutane online stores generic drugs buy viagra generic cialis purchase cialis us viagra medication price of accutane cheap cialis pill viagra no prescription find viagra buy cialis online cheap cheap cialis from canada cheap generic viagra price of cialis sale viagra viagra free sample order viagra order acomplia online where to order viagra viagra approved cialis for order buy sildenafil cheap buy accutane without prescription discount viagra without prescription cheap cialis from usa buy viagra no prescription required cialis discount cheap viagra on internet order generic cialis buying cialis online accutane without a prescription buy cialis lowest price viagra no rx required buy generic viagra online purchase cialis viagra buy drug order cialis without prescription viagra us 10mg cialis cheap acomplia tablets buy sildenafil in canada find viagra without prescription cialis overnight delivery buy viagra cheap cialis cheapest price cialis sale lowest price cialis cheapest generic viagra viagra soft tab no rx viagra cheap cialis no prescription purchase viagra viagra sale viagra india find viagra no prescription required impotence pills cialis 20mg 100 mg viagra buy cheap accutane buy cialis low price compare viagra prices cheapest viagra price impotence cheap viagra tablet tablet cialis order discount viagra viagra overnight shipping order viagra on internet buy cheap cialis online cost cialis buy sildenafil low cost order cialis no rx cheap viagra in uk cialis medication generic viagra cheap buy sildenafil online buy cheapest cialis on line order cialis no prescription required viagra drug buy viagra without prescription buy cialis on internet viagra cheap drug cheap viagra no prescription cheap viagra in canada cheap generic acomplia cheap viagra internet viagra pills cheap cialis in uk order cheap viagra online buy cialis from india cheap viagra no rx cheapest generic viagra online lowest price viagra cheap cialis in usa viagra sales order accutane online buy and purchase sildenafil online online pharmacy viagra viagra in australia certified viagra cialis malaysia viagra cheapest price cialis no rx required buy cheap acomplia buy sildenafil online without a prescription discount viagra online no prescription viagra viagra tablet viagra from india viagra cheap cialis internet order cialis 50 mg viagra buy cialis in canada order discount cialis online cialis no online prescription approved viagra pharmacy cialis soft tab cialis no prescription buy cialis order cheap cialis 100mg viagra order cialis online compare cialis prices online cheap viagra tablets cheap price viagra cialis cheap viagra online find viagra on internet cialis 20 mg viagra side effects cheap acomplia cialis without rx cialis without prescription cialis 10 mg viagra bangkok purchase acomplia cialis free delivery cheapest viagra buy viagra cialis india viagra vendors low cost viagra order viagra cheap online cheap viagra from uk generic accutane cheapest accutane prices viagra vs cialis generic acomplia accutane pills buy generic viagra generic viagra online generic viagra accutane discount find viagra online find cialis accutane pharmacy viagra online stores cialis buy order viagra in canada cialis online cheap viagra soft find cheap cialis online cheap viagra without prescription order viagra no prescription buy cheapest cialis order cialis in canada cheapest sildenafil citrate cialis uk cialis free sample cialis online review buy generic acomplia price of acomplia drug viagra generic cialis viagra no online prescription free viagra viagra information buy viagra from us buy viagra on internet cheapest acomplia order acomplia viagra buy online viagra online review where to buy cialis buy cheapest viagra online viagra without rx cialis approved viagra pharmacy order cheap viagra buy cialis online discount cialis no rx cheap viagra pill buy generic cialis online cialis online buy no rx viagra viagra online pharmacy cialis online stores buy cheap cialis buy cheapest cialis online buy cheap viagra accutane for sale cialis in australia buy cheap cialis internet acomplia sale buy viagra us overnight viagra lowest price accutane online accutane online acomplia find cheap viagra online best price cialis buy cheap viagra internet viagra free delivery cheap cialis on internet buy viagra from india cialis rx buying viagra online cheap generic accutane find cheap viagra where to buy accutane cialis australia cheapest cialis online order no rx cialis viagra purchase cialis vendors discount acomplia cialis cheap drug discount cialis overnight delivery cialis sales cialis buy drug cheapest generic cialis online buy cheapest viagra on line cheap cialis online cheap viagra pharmacy accutane online cheap buy acomplia viagra without prescription cheap cialis tablet cheapest cialis price buy no rx cialis cialis in us buy cialis on line free cialis order cialis from us cialis in malaysia accutane no prescription order discount viagra online cost of cialis buy cialis us cialis order viagra from canada viagra prescription buy cialis no prescription required viagra price

If you have looked around this blog you know I am a fan of Ruby on Rails. In fact, for most of you, it is how you found this blog. To a large degree version 6 of XCF, the one this series of articles is about, is based on applying my lessons learned from Ruby on Rails. Two things that framework does very is well is its support of DRY (don’t repeat yourself) and its philosophy of “Convention over Configuration”. Today you will add to XCF a class that will help make DRY the path of least resistance by modeling a set of conventions.

Course outline:

NOTE: You can download the source code for today’s lesson here: Lesson 03 Source Code

Or, you can get the most up to date version of XCF available on sourceforge.

What you will be building today

Today you will build your first XCFService class. You use this class to create instances of classes that are singleton like classes: that is classes where there will only be one instance. The primary benifit this class provides is that the client will not need to know the package or class name of the object it requests. This will result in the client objects being highly decoupled from the objects created. The following shows the class, SERVICE_FlyweightFactory, in action:

  XCFFacade facade = new XCFFacade();
  SERVICE_FlyweightFactory factory = new SERVICE_FlyweightFactory();
  facade.putService(SERVICE_FlyweightFactory.XCF_TAG, factory);

  factory.addConvention("validator", ".request.parameter.VALIDATOR_");
  factory.pushScope();
  factory.addPackage("com.eternal.xcf.common");

  // this call will return an instance to com.eternal.xcf.common.request.parameter.VALIDATOR_Required
  XCFValidator required = (XCFValidator)factory.getInstance("validator", "required");

  factory.pushScope();
  factory.addPackage("com.eternal.mymodule");

  // this call will return an instance to com.eternal.mymodule.request.parameter.VALIDATOR_Required
  required = (XCFValidator)factory.getInstance("validator", "required");
    

The problem of xml based configuration

Like many java frameworks, version 5 of XCF uses XML for framework configuration. This will not change in the version you are writing (version 6). What will change, however, is the format of that XML configuration. The old XML configuration was light on convention. As a result, terms and classes had to be repeated. This, of course, led to bugs as it was easy to mistype. Many frameworks share this problem. In fact, as soon as you require a class specification in an XML file, you are violating DRY. The reason is that you first specify the package and class when you define it, and then you repeat that in your XML file. In this type of setup, the path of least resistence is to put the class anywhere you see fit. This yields code that is harder to maintain by others and one where it is easy to introduce bugs.

By supporting a simple package and class naming convention for each type of object, your new XCFService class will now make the path of least resistence one where the class is created and named such that it follows a simple convention that is documented AND specified in one place.

Flyweight Structure

You use flyweight structures when you need to support large numbers of fine-grained objects efficiently. From the first two articles, you now know that you will use XCF to model functionality using a facade composed of a multitude of objects such as processing instructions, validators, and setters. Additionally:

  • The object state for each composite object in the facade is extrinsic. For all these objects we pass in the state (usually an XCFContext object)
  • Once the extrinsic state is removed, many groups of objects can be replaced by one. For example, VALIDATOR_Required only requires one instance since the data it validates is passed in as an argument.
  • Objects in the facade are decoupled from each other and do not rely on object identity. For example, a request processing instruction may need to contain an object that validates that a parameter exists. However, that class is not dependant upon the class com.eternal.xcf.common.request.parameter.VALIDATOR_Required. It is only requires that there is some class that performs the “required” functionality.

For these reasons, Flyweight is a good approach.

The workhorse class in flyweight is the flyweight factory. Client classes use this class to get a reference to a flyweight instance. The flyweight factory provides method that takes as input a string description of an object type. It then does the work of mapping that type to a concrete class and determining whether or not a new instance of that class needs to be created.

Flyweight Factory Design

To support this pattern, you will only need to create one class: SERVICE_FlyweightFactory. The actual flyweight objects will be all the other XCF classes such as XCFProcessingInstruction, XCFValidator, XCFSetter, etc. The idea behind this class is prior to construction of the facade, you define a set of conventions in the flyweight factory. These conventions are then used by the factory to convert the key lookup string to a concrete class during facade construction.

The other thing you will want to do is introduce the concept of scope. By having a boilerplate flyweight factory, we provide functionality that allows us to map the tag “required” to the class com.eternal.xcf.common.request.parameter.VALIDATOR_Required. However, down the road you may create modules that need to overide that class and provide a different implementation. By introducing scope and providing a mechanism for pushing and popping scope, you will support the ability to overide flyweights and add new types without adversly affecting convention or requiring configuration that violates DRY.

As you can see from the design, the factory contains a stack of scopes. Each scope contains a search path of packages and a cache of flyweight objects already created in that scope. To find a flyweight instance, the factory will ask each scope to return a flyweight. It will do this from top of the stack on down until a flyweight is returned. Each scope will first look in its cache and if one can’t be found, it will use the naming convention for the object type to generate class names. If the class exists, it will create a flyweight instance.

CONVENTION: The class name will be of the form [search path].[type-prefix][tag].

For example, if the typ prefix convention for “validator” is .request.parameter.VALIDATOR_ and in a particular scope, the search path is:

  • com.eternal.xcf.common
  • com.eternal.xcf.myapp

Then for a request of getInstance("validator","required") the scope will look (and instantiate) the first of the following two classes it finds:

  • com.eternal.xcf.myapp.request.parameter.VALIDATOR_Required
  • com.eternal.xcf.common.request.parameter.VALIDATOR_Required

Flyweight Factory Implementation: SERVICE_FlyweightFactory

package com.eternal.xcf.common.service;

import java.util.HashMap;
import java.util.Stack;

import com.eternal.xcf.core.XCFException;
import com.eternal.xcf.core.XCFFacade;
import com.eternal.xcf.core.XCFService;

/**
 * Client classes use this class to get a reference to a flyweight instance.
 * The flyweight factory provides method that takes as input a string description
 * of an object type.  It then does the work of mapping that type to a concrete class
 * and determining whether or not a new instance of that class needs to be created.
 *
 * @author sonjaya
 *
 */
public final class SERVICE_FlyweightFactory implements XCFService {
	public static String XCF_TAG = "flyweight-factory";
	private String name = XCF_TAG;

	private XCFFacade facade;

	private Stack<Scope> scopeStack = new Stack<Scope>();
	private HashMap<String, String>conventions = new HashMap<String, String>();

	/**
	 * Returns services name
	 */
	public String getName() {
		return name;
	}

	/**
	 * Set the services facade
	 */
	public void setFacade(XCFFacade facade) {
		this.facade = facade;

	}

	/**
	 * Set the services name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * No extra worked needed when the service starts
	 */
	public void start() throws XCFException {
	}

	/**
	 * No extra work needed when the service stops
	 */
	public void stop() throws XCFException {
	}

	/**
	 * Returns the instance associated w/the tag and type
	 * This method will first look to see if the instance exists in the current scope
	 * If a flyweight exists, it will return that, otherwise it will instantiate a new one.
	 * It repeats this process for each scope on the stack.
	 *
	 * @param type identifies the type of the flyweight -- e.g. validator, saxel
	 * @param tag identifies the flyweight in that type
	 * @return the flyweight object
	 * @throws XCFException thrown if it is unable to create a flyweight object
	 */
	public Object getInstance(String type, String tag) throws XCFException {
		// get the sub-package and class name prefix for this type
		String prefix = conventions.get(type);
		if (prefix == null) throw new XCFException("No convention for type:" + type);

		// for each scope started with the top of stack,
		// find our create the flyweight
		for (int i = scopeStack.size() - 1; i >= 0; i--) {
			Scope scope = scopeStack.elementAt(i);

			// first see if we have already created the flyweight
			Object flyweight = scope.getFlyweight(type, tag);
			// if flyweight is this object, then it meant we
			// already looked and couldn't find it for this scope
			// no need to look again, so move on to the next scope
			if (flyweight == this) continue;
			// if we got a non-null flyweight that was not this object
			// we have what we need and can return it
			if (flyweight != null) return flyweight;

			// ok, we don't have a flyweight yet for this type,tag and we haven't
			// looked yet.  Search through all the packages to see if we can find
			// the flyweight class

			Stack<String> searchPath = scope.searchPath;

			for (int j = searchPath.size()-1; j>=0; j--) {
				// create the class name by appending [path][prefix][normalized name]
				String path = searchPath.elementAt(j);
				String name = normalizeName(tag);
				String className = path + prefix + name;
				facade.logDebug("Searching for: " + className);

				// try to load it and create an instance
				try {
					Class aClass = Class.forName(className);
					Object anInstance = aClass.newInstance();
					scope.putFlyweight(type, tag, anInstance);
					return anInstance;
				} catch (ClassNotFoundException cnf) {
					facade.logDebug(className + " not in classpath");
				} catch (IllegalAccessException ia) {
					facade.logDebug(className + " does not have a public constructor");
				} catch (InstantiationException ie) {
					facade.logDebug(className + " does not have a null constructor");
				}

				// if we got here, we didn't find it in this path, go on to the next one
			}

			// there is no flyweight for this context, mark it so
			// we don't bother searcing on the next request
			scope.putFlyweight(type, tag, this);
		}

		// if we get here, we can't find the flyweight class and that is a problem
		throw new XCFException("Unable to find the class for type: " + type + " and tag: " + tag);
	}

	/**
	 * Convert the tag to a name that follows the conventions.
	 * Upercase word boundaries and remove -'s and _'s
	 * @param tag
	 * @return the converted name
	 */
	String normalizeName(String tag) {
		StringBuffer buf = new StringBuffer();

		boolean newWord = true;
		//  capitalize first letter of each word
		//  remove -'s and _'s
		for (int i = 0; i < tag.length(); i++) {
			char c = tag.charAt(i);
			if (c == '-' || c == '_') {
				newWord=true;
			} else if (newWord) {
				buf.append(Character.toUpperCase(c));
				newWord = false;
			} else {
				buf.append(c);
				newWord = false;
			}
		}
		return buf.toString();
	}

	/**
	 * Push a new scope onto the scope stack
	 *
	 */
	public void pushScope() {
		scopeStack.add(new Scope());

	}

	/**
	 * Pop a scope off of the scope stack
	 *
	 */
	public void popScope() {
		scopeStack.pop();
	}

	/**
	 * Add a sub-package, prefix convention for the type
	 * @param type
	 * @param prefix  this should be of the form .[package(s)].[prefix (in caps)]. e.g. .request.processors.VALIDATOR_
	 */
	public void addConvention(String type, String prefix) {
		conventions.put(type, prefix);

	}

	/**
	 * Add a package to the search path stack for the current scope.  The search path search starts from last first
	 * @param path
	 */
	public void addPackage(String path) {
		scopeStack.peek().addPath(path);
	}

	/**
	 * A scope contains a stack of packages (called the search path)
	 * and a cache of flyweights bound to a type,tag pair
	 *
	 * @author sonjaya
	 *
	 */
	class Scope {
		Stack<String> searchPath = new Stack<String>();
		HashMap<String, Object>cache = new HashMap<String, Object>();

		/**
		 * Add a path to search path stack
		 * @param path
		 */
		void addPath(String path) {
			searchPath.add(path);
		}

		/**
		 * Lookup the flyweight object bound to type,tag return null
		 * if it doesn't exist
		 * @param type
		 * @param tag
		 * @return
		 */
		Object getFlyweight(String type, String tag) {
			return cache.get(type + "." + tag);
		}

		/**
		 * Bind a flyweight object to a type,tag
		 * @param type
		 * @param tag
		 * @param flyweight
		 */
		void putFlyweight(String type, String tag, Object flyweight) {
			cache.put(type + "." + tag, flyweight);
		}
	}
}

Testing it out

For this test to work, you will need to move the class VALIDATOR_Required that you created in the last lesson to the package com.eternal.xcf.common.request.parameter. You will also need to create the following classes:

  • com.eternal.stubs.request.parameter.SETTER_Custom (implements XCFSetter)
  • com.eternal.scopeTwo.request.parameter.SETTER_Custom (implements XCFSetter)
  • com.eternal.scopeThree.request.parameter.VALIDATOR_Custom (implements XCFValidator)
  • com.eternal.scopeThree.request.parameter.VALIDATOR_Required (implements XCFValidator)
  • com.eternal.scopeThree.request.parameter.SETTER_WithLongName (implements XCFSetter)

As we won’t actually be invoking these classes they can have empty implementations. Now write the following JUnit test class:

package com.eternal.xcf.common.service;

import com.eternal.xcf.common.service.SERVICE_FlyweightFactory;
import com.eternal.xcf.core.XCFException;
import com.eternal.xcf.core.XCFFacade;
import com.eternal.xcf.core.XCFLogger;
import com.eternal.xcf.request.parameter.XCFSetter;
import com.eternal.xcf.request.parameter.XCFValidator;

import junit.framework.TestCase;

public class TestFlywieghtFactory extends TestCase {

	public void testFlywieghtFactoryScope() throws Exception {
		XCFFacade facade = new XCFFacade();
		SERVICE_FlyweightFactory factory = new SERVICE_FlyweightFactory();
		facade.getLogManager().setLogger(XCFLogger.LogTypes.DEBUG, facade.getLogManager().getLogger(XCFLogger.LOG_TO_CONSOLE));

		facade.putService(SERVICE_FlyweightFactory.XCF_TAG, factory);

		XCFValidator requiredScope1 = null; // exists in common package
		XCFValidator requiredScope2 = null; // same as scope 1
		XCFValidator requiredScope3 = null; // exists in scope 3 package

		XCFSetter customSetterScope1 = null; // exists in stub package
		XCFSetter customSetterScope2 = null; // exists in scope 2 package
		XCFSetter customSetterScope3 = null; // same as scope 2

		XCFValidator customValidatorScope3 = null; // exists in scope 3 package
		XCFSetter    setterWithLongNameScope3 = null; // exists in scope 3 package

		// add a rule that binds validator to .request.processors.VALIDATOR_
		factory.addConvention("validator", ".request.parameter.VALIDATOR_");

		// add a rule that binds setter to .request.processors.SETTER_
		factory.addConvention("setter", ".request.parameter.SETTER_");

		/////////////////////////////////////////////////////
		// SCOPE 1 TEST
		// push scope 1
		factory.pushScope();
		factory.addPackage("com.eternal.xcf.common");
		factory.addPackage("com.eternal.stubs");

		// ok, get the scope 1 flyweights
		requiredScope1 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope1 = (XCFSetter)factory.getInstance("setter", "custom");

		// these next two shouldn't be there
		try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {}
		try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {}

		assertNotNull(requiredScope1);
		assertNotNull(customSetterScope1);

		assertTrue(requiredScope1 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope1 instanceof com.eternal.stubs.request.parameter.SETTER_Custom);

		assertNull(customValidatorScope3);
		assertNull(setterWithLongNameScope3);

		/////////////////////////////////////////////////////
		// SCOPE 2 TEST
		// push scope 2
		factory.pushScope();
		factory.addPackage("com.eternal.scopeTwo");

		// ok, get the scope 2 flyweights
		requiredScope2 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope2 = (XCFSetter)factory.getInstance("setter", "custom");

		// these next two shouldn't be there
		try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {}
		try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {}

		assertEquals(requiredScope1, requiredScope2);
		assertNotNull(customSetterScope2);
		assertFalse(customSetterScope1 == customSetterScope2);

		assertTrue(requiredScope2 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope2 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);

		assertNull(customValidatorScope3);
		assertNull(setterWithLongNameScope3);

		/////////////////////////////////////////////////////
		// SCOPE 3 TEST
		// push scope 3
		factory.pushScope();
		factory.addPackage("com.eternal.scopeThree");

		// ok, get the scope 3 flyweights
		requiredScope3 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope3 = (XCFSetter)factory.getInstance("setter", "custom");
		customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");
		setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");	

		assertNotNull(requiredScope3);
		assertFalse(requiredScope1 == requiredScope3);
		assertEquals(customSetterScope2, customSetterScope3);

		assertNotNull(customValidatorScope3);
		assertNotNull(setterWithLongNameScope3);

		assertTrue(requiredScope3 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope3 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
		assertTrue(customValidatorScope3 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Custom);
		assertTrue(setterWithLongNameScope3 instanceof com.eternal.scopeThree.request.parameter.SETTER_WithLongName);

		/////////////////////////////////////////////////////
		// SCOPE 2 TEST after pop of Scope 3
		// pop scope 3
		factory.popScope();

		// ok, get the scope 2 flyweights
		requiredScope2 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope2 = (XCFSetter)factory.getInstance("setter", "custom");

		// these next two shouldn't be there
		try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
		try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}

		assertEquals(requiredScope1, requiredScope2);
		assertNotNull(customSetterScope2);
		assertFalse(customSetterScope1 == customSetterScope2);

		assertTrue(requiredScope2 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope2 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);

		assertNull(customValidatorScope3);
		assertNull(setterWithLongNameScope3);

		/////////////////////////////////////////////////////
		// SCOPE 3 TEST with a bogus path
		// push a new scope 3 with a non-existent package
		factory.pushScope();
		factory.addPackage("com.eternal.bogus");

		requiredScope3 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope3 = (XCFSetter)factory.getInstance("setter", "custom");

		// these next two shouldn't be there
		try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
		try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}

		assertEquals(requiredScope1, requiredScope3);
		assertEquals(customSetterScope2, customSetterScope3);

		assertTrue(requiredScope3 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope3 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);

		assertNull(customValidatorScope3);
		assertNull(setterWithLongNameScope3);

		/////////////////////////////////////////////////////
		// SCOPE 2 TEST after pop of Scope 3
		// pop scope 3
		factory.popScope();
		// ok, get the scope 2 flyweights
		requiredScope2 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope2 = (XCFSetter)factory.getInstance("setter", "custom");

		// these next two shouldn't be there
		try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
		try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}

		assertEquals(requiredScope1, requiredScope2);
		assertNotNull(customSetterScope2);
		assertFalse(customSetterScope1 == customSetterScope2);

		assertTrue(requiredScope2 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope2 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);

		assertNull(customValidatorScope3);
		assertNull(setterWithLongNameScope3);

		/////////////////////////////////////////////////////
		// SCOPE 3 TEST
		// push scope 3 with old package
		factory.pushScope();
		factory.addPackage("com.eternal.scopeThree");

		// ok, get the scope 3 flyweights
		requiredScope3 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope3 = (XCFSetter)factory.getInstance("setter", "custom");
		customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");
		setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");	

		assertNotNull(requiredScope3);
		assertFalse(requiredScope1 == requiredScope3);
		assertEquals(customSetterScope2, customSetterScope3);

		assertNotNull(customValidatorScope3);
		assertNotNull(customValidatorScope3);

		assertTrue(requiredScope3 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope3 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);
		assertTrue(customValidatorScope3 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Custom);
		assertTrue(setterWithLongNameScope3 instanceof com.eternal.scopeThree.request.parameter.SETTER_WithLongName);

		/////////////////////////////////////////////////////
		// SCOPE 2 TEST after pop of Scope 3
		// pop scope 3
		factory.popScope();
		requiredScope2 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope2 = (XCFSetter)factory.getInstance("setter", "custom");

		// these next two shouldn't be there
		try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
		try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}

		assertEquals(requiredScope1, requiredScope2);
		assertNotNull(customSetterScope2);
		assertFalse(customSetterScope1 == customSetterScope2);

		assertTrue(requiredScope2 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope2 instanceof com.eternal.scopeTwo.request.parameter.SETTER_Custom);

		assertNull(customValidatorScope3);
		assertNull(setterWithLongNameScope3);

		/////////////////////////////////////////////////////
		// SCOPE 1 TEST after pop of Scope 2
		// pop scope 2
		factory.popScope();
		// ok, get the scope 1 flyweights
		requiredScope1 = (XCFValidator)factory.getInstance("validator", "required");
		customSetterScope1 = (XCFSetter)factory.getInstance("setter", "custom");

		// these next two shouldn't be there
		try {customValidatorScope3 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {customValidatorScope3 = null;}
		try {setterWithLongNameScope3 = (XCFSetter)factory.getInstance("setter", "with-long-name");} catch (XCFException e) {setterWithLongNameScope3 = null;}

		assertNotNull(requiredScope1);
		assertNotNull(customSetterScope1);

		assertTrue(requiredScope1 instanceof com.eternal.xcf.common.request.parameter.VALIDATOR_Required);
		assertTrue(customSetterScope1 instanceof com.eternal.stubs.request.parameter.SETTER_Custom);

		assertNull(customValidatorScope3);
		assertNull(setterWithLongNameScope3);

		/////////////////////////////////////////////////////
		// NULL SCOPE TEST
		// pop scope 1
		factory.popScope();
		try {requiredScope1 = (XCFValidator)factory.getInstance("validator", "custom");} catch (XCFException e) {requiredScope1 = null;}
		assertNull(requiredScope1);
	}

	public void testFlyweightFactorySearchPath() throws Exception {
		XCFFacade facade = new XCFFacade();
		SERVICE_FlyweightFactory factory = new SERVICE_FlyweightFactory();
		facade.getLogManager().setLogger(XCFLogger.LogTypes.DEBUG, facade.getLogManager().getLogger(XCFLogger.LOG_TO_CONSOLE));

		facade.putService(SERVICE_FlyweightFactory.XCF_TAG, factory);

		// add a rule that binds validator to .request.processors.VALIDATOR_
		factory.addConvention("validator", ".request.parameter.VALIDATOR_");

		XCFValidator requiredScope1 = null; // exists in common package

		factory.pushScope();
		factory.addPackage("com.eternal.xcf.common");
		factory.addPackage("com.eternal.scopeThree");
		requiredScope1 = (XCFValidator)factory.getInstance("validator", "required");

		assertTrue(requiredScope1 instanceof com.eternal.scopeThree.request.parameter.VALIDATOR_Required);

	}
}

Conclusion

In lesson one you created the concept of a facade. In lesson two you created the interfaces that will be used o model the request processing in that facade. Today, you created a class that will critical to the process you will use to build the facade. This class will greatly reduce configuration by modeling a convention that not only dictates how classes are names but also what sub-package they are placed. Because of this you have made the path of least resistence one where people will name classes in a manner where their function is easier to intuite and place them in a package future engineers will expect them. In the next lessons we will move on to the actual act of building the facade using an XML file as the director.

Entry Filed under: Software Development, XCF

Trackbacks

4 Comments Add your own

Leave a Comment

You must be logged in to post a comment.

Trackback this post  |  Subscribe to the comments via RSS Feed


Calendar

November 2006
M T W T F S S
« Oct   Dec »
 12345
6789101112
13141516171819
20212223242526
27282930  

Most Recent Posts