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

jsNewsService.js : copyright 2001, Essam Ahmed

Przykład z książki Essam Ahmed, Programowanie w języku JScript .NET (2001)

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

import System
import System.ServiceProcess;
import System.Configuration.Install;
import System.ComponentModel;
import System.Diagnostics;
import System.Data;
import System.Data.OleDb;
import System.IO;
import System.Threading;
import System.Xml;

import ServiceHelper;

class customArgs {

//////////////////////////////////// UWAGA ////////////////////////////////////
//                                                                          //
//    Poniższa ścieżka ma wskazywać istniejący plik NEWS.MDB                //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

	var mdbPath : String = "F:\\JSNetProgramming\\data\\news.mdb"

	function ToString() : String { return mdbPath;}
}

class customArgs2 {

//////////////////////////////////// NOTE ////////////////////////////////////
//                                                                          //
//  Poniższa ścieżka ma wskazywać istniejący katalog monitorowany           //
//                      ** Wymagana litera dysku **                         //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

	var watchPath : String = "F:\\JSNetProgramming\\jsNSwatch"

	function ToString() : String { return watchPath;}
}


RunInstaller(true) public class ProjectInstaller extends Installer
{
	private var serviceInstaller : ServiceInstaller;
	private var processInstaller : ServiceProcessInstaller;

	function ProjectInstaller(){

		processInstaller = new ServiceProcessInstaller();
	    serviceInstaller = new ServiceInstaller();

		// Service will run under the system account
		processInstaller.Account = ServiceAccount.LocalSystem;

		// Service will have Start Type of Manual
		// ServiceStartMode.Manual
		// ServiceStartMode.Automatic
		// ServiceStartMode.Disabled
		serviceInstaller.StartType = ServiceStartMode.Manual;

		serviceInstaller.ServiceName = "jsNewsXmlProcessor";
		

	    Installers.Add(serviceInstaller);
		Installers.Add(processInstaller);
	}
}

public class sampleNewsFeeder extends jsNetServiceBase {

	var theThread : Thread;

    function sampleNewsFeeder()
    {
        CanPauseAndContinue = true;
        ServiceName = "jsNewsXmlProcessor";
        Trace.Listeners.Add(new TextWriterTraceListener(File.Create(
			"\\JSNetProgramming\\Chapter17\\jsNewsService\\jsNewServiceTrace.txt")));
    }

    protected function OnStart()
    {
        EventLog.WriteEntry("Usługa JScript uruchomiona");
		StartMonitorThread();
    }

    protected function OnStop()
    {
        EventLog.WriteEntry("Usługa JScript zatrzymana");
		StopThread();
        Trace.Close();
    }

    protected function OnPause()
    {
        StopThread();
        EventLog.WriteEntry("Usługa JScript wstrzymana");
    }

    protected function OnContinue()
    {
        StartMonitorThread();
        EventLog.WriteEntry("Praca usługi JScript wznowiona");
    }


	function StartMonitorThread() {
		var mon: fileMon= new fileMon();
		theThread = new Thread(mon.StartMonitor)
		theThread.Start()
	}
    function  StopThread() {
        theThread.Interrupt();
        theThread.Join();
        theThread = null;
    }


}

ServiceBase.Run(new sampleNewsFeeder());

class fileMon { 
    private var dirpath : String = new customArgs2();
	static var generalTrace:TraceSwitch=new TraceSwitch("general","general sw");
    function OnFileCreated(source: Object, e : FileSystemEventArgs) {
        try {
			if(e.FullPath==""){
				Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Wykryta zmiana pliku; odczyt pominięty");
				return;
			}
			var qResult:Boolean;
            var fileinfo :FileInformation = new FileInformation(e.FullPath)
            //create new instance of importer 
            var worker: Importer= new Importer();
            Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Plik: " +e.FullPath);
            worker.ProcessXml(fileinfo);
        }   
        catch( ex: Exception) {
            Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Operacja kolejkowania pliku zakończona niepowodzeniem: " + ex.Message);
        }
        finally {
			Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Wyjście z OnFileCreated" );        
		}
    }
    
    
    public function StartMonitor() {
		// konfigurujemy FileSystemWatcher : oczekiwanie na zmiany listy nazw plików
		// wskazanego katalogu
        var fw :FileSystemWatcher= new FileSystemWatcher()
        var result: WaitForChangedResult;
        fw.Path = dirpath;
        fw.NotifyFilter = NotifyFilters.FileName;
        fw.IncludeSubdirectories = false;
        fw.Filter = "*.xml";
    
		// skojarzenie funkcji ze zdarzeniem Created...    
        fw.add_Created(OnFileCreated);
        
		// pętla nieskończona, oczekiwanie na plik
		// pętlę może zakończyć główny wątek usługi
        try {
            while(true) {
				Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Oczekiwanie na plik...");
                result = fw.WaitForChanged(WatcherChangeTypes.Created);
                Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose, "Wykryto utworzenie pliku");
            }
        }
        catch( e : Exception) {
            Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose, "Monitorowanie katalogu nie jest możliwe: " & e.Message);
        }
        finally {
            Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose, "Koniec monitorowania katalogu.");
        }
        
    }
}

class Importer extends Object {
	// klasa utworzona przez xsd.exe...
	public class newsClass {
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var ArticleID : System.String;
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var Headline : System.String;
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var Body : System.String;
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var Source : System.String;
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var NewsDate : System.String;
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var Author : System.String;
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var Category : System.String;
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var Impressions : System.String;
	    
		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var PhotoURL : System.String;

		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var RelatedURL : System.String;

		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var ExpireOn : System.String;

		public System.Xml.Serialization.XmlElementAttribute(IsNullable=false) 
		var ReleaseOn : System.String;
	}

	var newsItem : newsClass=new newsClass;
	var dbs : Database;

	public function ProcessXml( fileInfo : Object) {
		Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose, "Funkcja ProcessXml uruchomiona");
		// próba uzyskania wyłącznego dostępu do pliku
		// próbujemy do skutku...
		while(true) {
			if (isFileClosed(fileInfo.FilePath)) 
				break;
			// między próbami 1 s - można zwiększyć, jeżeli pliki są duże
			Thread.Sleep(1000);
		}
		LoadXmlData(fileInfo);
		Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Usuwanie pliku " + fileInfo.FilePath);
		File.Delete(fileInfo.FilePath);
		Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Wyjście z ProcessXml");
	}

	function isFileClosed( FilePath : String) : Boolean {
	var result:Boolean;
	var theFileX : FileStream;

		result=false;
		try {
			theFileX=new FileStream(FilePath,FileMode.Append, FileAccess.Write, FileShare.None);
			result = true;
		} catch (e : Exception) {
			Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose, "Błąd przy otwieraniu pliku [" + FilePath + "] : " + e.Message);
			result = false;
		}
		finally {
			if(theFileX!=null)
				theFileX.Close();
				theFileX=null;
			}
		return (result);
	}

	function LoadXmlData( fileInfo : FileInformation){
		try {
		// tworzenie połączenia z bazą
		// (konstruktor otwiera plik .mdb)
		dbs = new Database();
		} 
		catch (e : Exception) {
			Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Nie można otworzyć bazy danych : " + e.Message);
			return;
		}
		try {
			Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Odczyt pliku " + fileInfo.FilePath);
			// ładowanie pliku XML do obiektu DataSet...
			var ds : DataSet;
			ds=new DataSet();
			ds.ReadXml(fileInfo.FilePath);

			var dt:DataTable=ds.Tables[0];
			var rcount:int=dt.Rows.Count;
			var i:int;
			var dr:DataRow;
			for(i=0;i<rcount;i++)
			{
				dr=dt.Rows[i];
				newsItem.Headline=String(dr["Headline"]);
				newsItem.Body=dr["Body"];
				newsItem.Source=dr["Source"];
				newsItem.NewsDate =dr["NewsDate"];
				newsItem.Author=dr["Author"];
				newsItem.Category=dr["Category"];
				newsItem.Impressions=dr["Impressions"];
				newsItem.PhotoURL=dr["PhotoURL"];
				newsItem.RelatedURL=dr["RelatedURL"];
				newsItem.ExpireOn =dr["ExpireOn"];
				newsItem.ReleaseOn =dr["ReleaseOn"];
				AppendNewsItem(newsItem);
			}
			// zwalnianie zasobów...
			ds.Dispose();
			ds=null;
			dbs.Close();
			dbs=null;
		}
		catch (e : Exception) {
			Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Błąd odczytu danych XML: " + e.Message);
		}
		Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Odczyt zakończony");
	}


	function AppendNewsItem( newsItem : newsClass) {
	var sql : String;
	var execSql:String
	
		Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Wprowadzanie nowego elementu do bazy danych...");
		sql = "INSERT INTO NEWS " +
		"(Headline, Body, Source, NewsDate, Author, Category, Impressions," +
		"PhotoURL,RelatedURL,ExpireOn,ReleaseOn)" +
		" Values ('" + 
			newsItem.Headline + "','" +
			newsItem.Body + "','"  +
			newsItem.Source + "','" +
			newsItem.NewsDate  + "'," + 
			newsItem.Author  +  "," + 
			newsItem.Category + "," +
			newsItem.Impressions + ",'" +
			newsItem.PhotoURL + "','"  +
			newsItem.RelatedURL + "','"  +
			newsItem.ExpireOn + "','"  +
			newsItem.ReleaseOn  + "')"
			// poprawiamy wartości null...
			execSql=sql.Replace(",\'null\',",",null,");
			Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Instrukcja SQL:" + execSql);
			Trace.WriteLine("Insert statement is:" + execSql);
		try {
			dbs.execCommand(execSql);
		} 
		catch (e : Exception) {
			Trace.WriteLineIf(fileMon.generalTrace.TraceVerbose,"Wprowadzanie elementu do bazy danych zakończone niepowodzeniem: " + e.Message);
		}
	}
}

class FileInformation {
var FilePath : String;
static var fNum : int;
var fileNumber : int;

	function FileInformation( file : String) {
		FilePath = file;
		fNum++;
		fileNumber = fNum;
	}

	function get FileNumber() : int {
		return( fileNumber);
	}
}

class Database {
	private var m_cmd : OleDbCommand;
	private var m_ds : DataSet;		
	private var m_conn : OleDbConnection;
	private var m_connectionString : String;

	function Database()
	{
		m_connectionString=new String("Provider=Microsoft.JET.OLEDB.4.0;data source="+new customArgs());
		m_conn = new OleDbConnection (m_connectionString);
		m_conn.Open();
		m_cmd = new OleDbCommand();
		m_cmd.Connection=m_conn;
	}

	public function set databaseLocation(loc : String) {
		Trace.Assert(loc.Length > 0, "Próba użycia pustego ciągu połączenia");
		m_connectionString=loc;
	}
		
	public function get databaseLocation() : String {
		return m_connectionString;
	}
	public function queryDS(qs:String) : DataSet {
		Trace.Assert(qs.Length > 0, "Database.queryDS: otrzymano parametr pusty");
		var lds : DataSet=new DataSet();
		var dataAdapter : OleDbDataAdapter;
		m_cmd.CommandText=qs;
		dataAdapter= new OleDbDataAdapter();
		dataAdapter.SelectCommand =m_cmd;
		dataAdapter.Fill(lds);
		return lds;
	}

	public function execCommand(qs:String) : DataSet {
		Trace.Assert(qs.Length > 0, "Database.execCommand: otrzymano pusty ciąg zapytania");
		m_cmd.CommandText=qs;
		m_cmd.ExecuteNonQuery();
		
	}
	public function Close()  {
		m_conn.Close();	
	}
}

