/*************************************************************************
Copyright © 1999-2002 Novell, Inc. All Rights Reserved. 

THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 
TREATIES. USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE
AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS
THIS WORK. PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS
TO DEVELOPER A ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S
SAMPLE CODE IN ITS PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION
RIGHTS TO MARKET, DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT
OF DEVELOPER'S PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR
DEVELOPER'S CUSTOMERS WITH RESPECT TO THIS CODE.

***************************************************************************/
/*******************************************************************
* Name: SkeletonPublisher.cpp
* Description: Platform independent code for CSkeletonDriver
* PublicationShim implementation
* Tabs: 4
*******************************************************************/


#include "SkeletonPublisher.h"
#include "CSkeletonDriver.h"
#include "InterfaceFactory.h"

static const int DEFAULT_POLLING_INTERVAL		=	2;	//2 seconds
static const int PUBLISH_INTERVAL				= 	5;	//every x polling intervals, publish a status document

//string constants: They are defined as arrays because the L" syntax doesn't work on unix, since a wchar_t is
//32-bits rather than 16-bits as on Win32 and Netware
static const unicode TEXT_PUBLISHER[]			=	{'p','u','b','l','i','s','h','e','r',0};
static const unicode TEXT_PUB_1[]				=	{'p','u','b','-','1',0};
static const unicode TEXT_POLLING_INTERVAL[]	=	{'p','o','l','l','i','n','g','-','i','n','t','e','r','v','a','l',0};
static const unicode MSG_ABORTED[]				=	{'a','b','o','r','t','e','d',0};
static const unicode MSG_PUBLISH[]				=	{'J','u','s','t',' ','t','e','s','t','i','n','g',0};

//description of example init values we want to extract from the init document
//passed to init(). See skel_options.xml
const CommonImpl::ShimParamDesc SkeletonPublisher::PUBLISHER_PARAMS[] = 
{
	{TEXT_PUB_1,STRING_TYPE,false},				//example
	{TEXT_POLLING_INTERVAL,INT_TYPE,false},		//<polling-interval>
	{0,0,false}
};

//macros required by DirXML interface definition (see define_interface.h)
DEFINE_IMPL(SkeletonPublisher,PublicationShim);
DEFINE_IMPL(SkeletonPublisher,XmlQueryProcessor);

//++
//=========================================================================
// Purpose:
//		Constructor
// Notes:
//=========================================================================
SkeletonPublisher::SkeletonPublisher(
	CommonImpl::AuthenticationParams * ap)	//pointer to auth params from driver shim
//--
:	common("SkeletonPublisher"),
	shutdown(false),
	aborted(false),
	pollingInterval(DEFAULT_POLLING_INTERVAL),
	filter(0),
	authParams(ap),
	semaphore(0)
{
	//call platform specific code to get a semaphore
	semaphore = createSemaphore();
}

//++
//=========================================================================
// Purpose:
//		Destructor
// Notes:
//=========================================================================
SkeletonPublisher::~SkeletonPublisher()
//--
{
	stop();
	destroySemaphore(semaphore);
	if (filter != 0)
	{
		DriverFilter_destroy(filter);
	}
}

//++
//=========================================================================
// Purpose:
//		Interface method, called by DirXML to allow publisher to perform any
//		required initialization.
// Notes:
//=========================================================================
XmlDocument * METHOD_CALL			//result document
SkeletonPublisher::init(
	XmlDocument * initParameters)	//document containing init parms for publisher
//--
{
#ifndef	NO_CPP_EXCEPTIONS
	try
	{
#endif	
		common.tracer->trace("init");
		//construct a driver filter for the publication shim to use for filtering application events
		//In an actual driver, the publisher would use the filter to filter events from the application
		//to avoid publishing unnecessary events to DirXML.
		Document * initDoc = initParameters->getDocument();
		//get any non-authentication options from the init document
		CommonImpl::ShimParams * params = common.getShimParams(initDoc,TEXT_PUBLISHER,PUBLISHER_PARAMS);
		//get any the polling interval that may have been passed in
		int pi = params->getIntParam(TEXT_POLLING_INTERVAL);
		if (pi != -1)
		{
			//change our default polling interval to whatever was setup using ConsoleOne
			pollingInterval = pi;
		}
		//setup a filter for use in start()
		//NOTE: the skeleton publisher doesn't actually make use of the filter, but
		//this code is here to illustrate how to create the filter based on the init
		//parameters
		Element	* filterElement = common.getFirstElementByTagName(initDoc, common.ndsDtd->TAG_DRIVER_FILTER);
		if (filterElement != 0)
		{
			filter = DriverFilter_new(filterElement);
		} else
		{
			//if weren't able to setup a filter, setup a null
			//filter so we don't have to check for filter != 0 everywhere
			filter = DriverFilter_new(0);
		}
		return common.setReturnDocument(common.createSuccessDocument());
#ifndef	NO_CPP_EXCEPTIONS
	} catch (ShimException e)
	{
		return	common.setReturnDocument(common.createStatusDocument(STATUS_LEVEL_FATAL,e.getMessage()));
	} catch (...)
	{
		//something bad happened...
		return	common.setReturnDocument(common.createStatusDocument(STATUS_LEVEL_FATAL,MSG_BAD));
	}
#endif	
}

//++
//=========================================================================
// Purpose:
//		Interface method, called by DirXML to allow publisher to listen for
//		application events and publish them to DirXML.
// Notes:
//=========================================================================
XmlDocument * METHOD_CALL			//result document
SkeletonPublisher::start(
XmlCommandProcessor * ndsExecute)	//interface through which to publish to DirXML
//--
{
	int		publishCounter = 0;
#ifndef	NO_CPP_EXCEPTIONS
	try
	{
#endif	
		//NOTE: this implements a polling method of communication with the application.
		//this may not be appropriate if the application supports an event notification system
		common.tracer->trace("start");
		//loop until we're told to shutdown (or some fatal error occurs)
		while(!shutdown && !aborted)
		{
#ifndef	NO_CPP_EXCEPTIONS
			try
			{
#endif			
				// skeleton implementation just wakes up every so often to
				// see if it needs to shutdown and return.
				common.tracer->trace("polling...");
				//send in a status document every so often, just for testing
				if (++publishCounter >= PUBLISH_INTERVAL)
				{
					publishCounter = 0;
					Element	* input = NdsDtd_newInputDocument();
					NdsDtd_addStatus(input,STATUS_LEVEL_SUCCESS,MSG_PUBLISH,0);
					XmlDocument	* pubDoc = XmlDocument_newFromDOM(input->getOwnerDocument());
					ndsExecute->execute(pubDoc,this);
					XmlDocument_destroy(pubDoc);
					//NOTE that we must destroy the XmlDocument and the DOM Document separately.
					//The XmlDocument doesn't take ownership of the DOM document
					input->getOwnerDocument()->destroy();
				}
				//In a real driver, we'd do whatever was necessary to ask the application what changed
				//and build an input document to publish the change events to DirXML.

				//wait for subscriber channel thread to wake us up, or for polling interval to
				//expire.
				//NOTE: the use of the semaphore is highly recommended. It prevents a long polling
				//interval from interfering with the orderly shutdown of the driver.
				waitSemaphore(semaphore,pollingInterval * 1000);
#ifndef	NO_CPP_EXCEPTIONS
			} catch (ShimException e)
			{
				//some sort of error happened...publish the error to DirXML but don't quit
				//(definitely don't quit if all that happened is we lost communication with
				//the app...we should simply try and reestablish it later)
				Element	* input = NdsDtd_newInputDocument();
				NdsDtd_addStatus(input,STATUS_LEVEL_ERROR,e.getMessage(),0);
				XmlDocument	* pubDoc = XmlDocument_newFromDOM(input->getOwnerDocument());
				ndsExecute->execute(pubDoc,this);
				XmlDocument_destroy(pubDoc);
				//NOTE that we must destroy the XmlDocument and the DOM Document separately.
				//The XmlDocument doesn't take ownership of the DOM document
				input->getOwnerDocument()->destroy();
				//loop around to try again
			}
#endif			
		}
		if (!aborted)
		{
			common.tracer->trace("stopping");
			common.setReturnDocument(common.createSuccessDocument());
		} else
		{
			common.tracer->trace("aborting");
			common.setReturnDocument(common.createStatusDocument(STATUS_LEVEL_FATAL,MSG_ABORTED));
		}
		return common.getReturnDocument();
#ifndef	NO_CPP_EXCEPTIONS
	} catch (...)
	{
		//something bad happened...
		return	common.setReturnDocument(common.createStatusDocument(STATUS_LEVEL_FATAL,MSG_BAD));
	}
#endif	
}

//++
//=========================================================================
// Purpose:
//		Tell publisher thread to stop.
// Notes:
//		Implementation detail.
//=========================================================================
void
SkeletonPublisher::stop(void)
//--
{
	shutdown = true;
	signalSemaphore(semaphore);
}

//++
//=========================================================================
// Purpose:
//		Tell publisher thread to abort.
// Notes:
//		Implementation detail. This is used by the NetWare implementation
//		in case the user unloads the .NLM from the command line.
//
//		This will cause the start() method to return a fatal error.
//=========================================================================
void
SkeletonPublisher::abort(void)
//--
{
	aborted = true;
	signalSemaphore(semaphore);
}

//++
//=========================================================================
// Purpose:
//		Interface method, called by DirXML to query application
// Notes:
//=========================================================================
XmlDocument * METHOD_CALL
SkeletonPublisher::query(
	XmlDocument * document)
{
	common.tracer->trace("query");
	//since this is a skeleton, and there is nothing to query, just return
	//an empty output document with a success status. The absence of an <instance>
	//element tells DirXML that nothing matched the query.
	return common.setReturnDocument(common.createSuccessDocument());
}

