 		public function WeaveAnalystDataSource()
 		{
 			promise.depend(url, scriptName, inputs, hierarchyRefresh);
+			registerLinkableChild(this, outputCSV.keyType);
+			registerLinkableChild(this, outputCSV.keyColName);
 			if (!cacheFile)
 			{
 				// register static LinkableFile as a child of the globalHashMap so it will not be disposed when an instance of this data source is disposed.
 				cacheFile = registerLinkableChild(WeaveAPI.globalHashMap, new LinkableFile(URLRequestUtils.LOCAL_FILE_URL_SCHEME + CACHE_FILE_NAME), parseCacheFile);
 			}
 			registerLinkableChild(this, cacheFile, runScript);
+			getCallbackCollection(this).addGroupedCallback(this, saveCache);
 		}
 		
 		private static const CACHE_STATE:String = 'sessionState';

		/**
		 * @return true if data was loaded from cache
		 */
		private static function loadCache(source:WeaveAnalystDataSource):Boolean
		{
			var currentState:Object = getSessionState(source);
			var sourceName:String = WeaveAPI.globalHashMap.getName(source);
			if (cache && cache[sourceName])
			{
				if (WeaveAPI.SessionManager.computeDiff(currentState, cache[sourceName][CACHE_STATE]) === undefined)
				{
					setSessionState(source.outputCSV, cache[sourceName][CACHE_OUTPUT_STATE]);
					return true;
				}
			}
			return false;
		}
		private static function saveCache(source:WeaveAnalystDataSource):void
		{
			var name:String = WeaveAPI.globalHashMap.getName(source);
			if (!cache)
				cache = {};
			cache[name] = {};
			cache[name][CACHE_STATE] = getSessionState(source);
			cache[name][CACHE_OUTPUT_STATE] = getSessionState(source.outputCSV);
			var bytes:ByteArray = new ByteArray();
			bytes.writeObject(cache);
			WeaveAPI.URLRequestUtils.saveLocalFile(CACHE_FILE_NAME, bytes);
		}
		
		public const url:LinkableString = registerLinkableChild(this, new LinkableString('/WeaveAnalystServices/ComputationalServlet'), handleURLChange);
		public const scriptName:LinkableString = newLinkableChild(this, LinkableString);
		public const inputs:LinkableHashMap = newLinkableChild(this, LinkableHashMap);
		public const inputKeyFilter:LinkableDynamicObject = registerLinkableChild(this, new LinkableDynamicObject(IKeyFilter));
		
		private const promise:LinkablePromise = registerLinkableChild(this, new LinkablePromise(runScript, describePromise, true));
		private function describePromise():String { return lang("Running script {0}", scriptName.value); }

	import weave.api.registerLinkableChild;
	import weave.api.reportError;
	import weave.api.setSessionState;
	import weave.compiler.StandardLib;
	import weave.core.LinkableDynamicObject;
	import weave.core.LinkableFile;
	import weave.core.LinkableHashMap;
