/*************************************************************************
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.

***************************************************************************/

package com.novell.nds.dirxml.driver.skeleton;

import com.novell.nds.dirxml.driver.*;
import org.w3c.dom.*;

/**
 * A basic skeleton for implementing a <code>PublicationShim</code>.
 * <p>
 * The <code>PublicationShim</code> is an interface used to start the application
 * driver publication process.
 * <p>
 *	A <code>PublicationShim</code> will almost always also implement <code>XmlQueryProcessor</code>
 * but it could also delegate it to another object
 * <p>
 * Note that the publisher init() and start() methods are called on a thread separate from
 * the thread used for calling the DriverShim and SubscriptionShim methods.
 *
 * @version 	2.0 28Jun2000
 */
public class SkeletonPublicationShim
	extends CommonImpl
	implements PublicationShim, XmlQueryProcessor
{
	/**
	 * Trace level at or above which the "polling..." message will be displayed
	 */
	private static final int	POLLING_TRACE_LEVEL	= 5;
	/**
	 * Variable used in <code>start()</code> to determine when to return from <code>start()</code>.
	 */
	boolean shutdown = false;
	/**
	 * Variable used to control how often the thread in <code>start()</code> wakes up to
	 * poll the application. Value is in seconds.
	 */
	int pollingInterval = 2;
	/**
	 * Object used as a semaphore so subscriber-channel thread can wake publisher thread
	 * up to tell it to shutdown.
	 */
	Object semaphore = new Object();
	/**
	 * Filter used to filter application events before sending them
	 * to DirXML.
	 */
	DriverFilter filter = null;
	/**
	 * Authentication info initialized by driver shim at init()
	 */
	private AuthenticationParams authParams = null;

	/**
	 * constructor, not strictly required, but a good idea
	 */
	SkeletonPublicationShim(AuthenticationParams authParams)
	{
		//set up the Trace object with the name of this object
		super("SkeletonPublicationShim");
		this.authParams = authParams;
	}

	/**
	 * <code>init</code> will be called before the invocation of start.
	 *
	 * @param initParameters XML document that contains the publisher initialization
	 *	parameters.
	 * @return XML document containing status/messages from init operation.
	 */
	public XmlDocument init(XmlDocument initParameters )
	{
		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.getDocumentNS();
		//get any non-authentication options from the init document
		ShimParams params = getShimParams(initDoc,"publisher",PUBLISHER_PARAMS);
		//get any the polling interval that may have been passed in
		int pi = params.getIntParam("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
		NodeList filterList = initDoc.getElementsByTagNameNS(null, "driver-filter");
		int		i = 0;
		Element	filterElement;
		while ((filterElement = (Element)filterList.item(i++)) != null)
		{
			String	type = filterElement.getAttributeNS(null, "type");
			if (type.length() == 0 || type.equals("publisher"))
			{
				filter = new DriverFilter(filterElement);
				break;
			}
		}
		if (filter == null)
		{
			//if weren't able to setup a filter, setup a null
			//filter so we don't have to check for filter != 0 everywhere
			filter = new DriverFilter();
		}
		return createSuccessDocument();
	}

	/**
	 * <code>start()</code> starts the <code>PublicationShim</code>.  The publisher shim should not return
	 * from start until DriverShim.shutdown() is called, or a fatal error occurs. Returning
	 * prematurely from <code>start()</code> will cause DirXML to shut down the driver.
	 *
	 * @param execute <code>XmlCommandProcessor</code> that can invoked in order to publish
	 * information to NDS on behalf of the application. execute must only be invoked
	 * from the thread on which <code>start()</code> was invoked.
	 *
	 * @return XML document containing status/messages from start operation
	 */
	public XmlDocument start( XmlCommandProcessor execute)
	{
		//NOTE: this implements a polling method of communication with the application.
		//this may not be appropriate if the application supports an event notification system
		tracer.trace("start");
		//loop until we're told to shutdown (or some fatal error occurs)
		while(!shutdown)
		{
			// skeleton implementation just wakes up every so often to
			// see if it needs to shutdown and return.
			try
			{
				tracer.trace("polling...",POLLING_TRACE_LEVEL);
				//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.
				synchronized(semaphore)
				{
					//our pollingInterval value is in seconds, Object.wait() takes milliseconds
					semaphore.wait(pollingInterval * 1000);
				}
			}
			catch(InterruptedException ie)
			{
			}
		}
		tracer.trace("stopping");
		return createSuccessDocument();
	}

	/**
	 * <code>stop()</code> is not part of the PublicationShim interface but is an implementation detail of
	 * the skeleton driver. <code>SkeletonDriver.shutdown()</code> calls <code>stop()</code> to
	 * signal the publisher thread that it needs to exit from <code>start()</code>.
	 */
	 void stop()
	 {
		//tell publisher thread it's time to exit
		shutdown = true;
		//tell the publisher thread to wake up, if it happens to be sleeping
	 	synchronized(semaphore)
	 	{
	 		semaphore.notifyAll();
	 	}
	 }

	// implementation of XmlQueryProcessor

	/**
	 * <code>query</code> will accept an XML-encoded query and
	 * return the results
	 *
	 * @param doc	A document containing an XDS encoded query
	 * @return The results of the query
	 */
	public XmlDocument query(XmlDocument doc)
	{
		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 createSuccessDocument();
	}

	/**
	 * Table of parameters that this publisher shim wants to get from the &lt;publisher-options> element
	 * of the init-params.
	 * <p>
	 * This is illustrative, of course, an actual driver would want parameters specific to its
	 * functionality.
	 */
	private static final ShimParamDesc[] PUBLISHER_PARAMS =
	{
		//options
		new ShimParamDesc("pub-1",ShimParamDesc.STRING_TYPE,false),
		new ShimParamDesc("polling-interval",ShimParamDesc.INT_TYPE,false)
	};

}

