Modifying the Configuration File
We need to modify
the configuration file to represent the changes in our service. We need
to be able to store and then retrieve the information about the
properties we have defined in our FTP class. Listing 4 shows the new configuration file.
Listing 4. FTP configuration file schema.
<?xml version="1.0" encoding="utf-8" ?> <Configuration> <FTPUris> <FTPUri> <URI>FTP://mgern1/download</URI> <LocalFolder>c:\temp\ftpsave</LocalFolder> <User>anonymous</User> <Pwd>anonymous@anon.com</Pwd> <Port>21</Port> <File>mydata.dat</File> </FTPUri> </FTPUris> </Configuration>
|
The configuration file matches all the required property fields that we added to our FTP class.
Modifying the <Tutorials.ThreadFunc> Method
We once again have to modify the <ThreadFunc> method to retrieve and then assign the property values from the configuration file to the correct property of the FTP class instance that we created. Listing 5 shows the new <ThreadFunc> method.
Listing 5. The modified <ThreadFunc> method.
Private Sub ThreadFunc() Try 'Load our Configuration File Dim Doc As XmlDocument = New XmlDocument() Doc.Load(My.Settings.ConfigFile)
Dim Options As XmlNode 'Get a pointer to the Outer Node Options = Doc.SelectSingleNode("//*[local-name()=FTPUris]")
If (Not Options Is Nothing) Then Dim tmpOptions As System.Xml.XPath.XPathNavigator = Options.FirstChild.CreateNavigator() If (Not tmpOptions Is Nothing) Then Dim children As System.Xml.XPath.XPathNavigator Do Try Dim tmpFTP As New FTP(m_ThreadAction)
children = tmpOptions.SelectSingleNode("URI") tmpFTP.URI = children.Value
children = tmpOptions.SelectSingleNode("LocalFolder") tmpFTP.LocalFolder = children.Value
children = tmpOptions.SelectSingleNode("File") tmpFTP.File = children.Value
children = tmpOptions.SelectSingleNode("Port") tmpFTP.Port = children.Value
children = tmpOptions.SelectSingleNode("User") tmpFTP.User = children.Value
children = tmpOptions.SelectSingleNode("Pwd") tmpFTP.Pwd = children.Value
m_WorkerThreads.Add(tmpFTP) tmpFTP.Start() Catch ex As Exception WriteLogEvent(ex.ToString(), CONFIG_READ_ERROR, EventLogEntryType.Error, My.Resources.Source) End Try Loop While (tmpOptions.MoveToNext) End If End If Catch ex As Exception WriteLogEvent(ex.ToString(), ONSTART_ERROR, EventLogEntryType.Error, My.Resources.Source) Me.Stop() End Try End Sub
|
This new code will open the configuration file and then navigate to the starting parent node of FTPWorkerOptions.
When we have a pointer to that node, the code will read through each
sibling and populate the appropriate FTP class property with the value
read from the configuration file. After all the values have been set,
the FTP class <Start> method is called, and monitoring for files and downloading of files commences.
Updating the Tutorials <OnStop> Method
We need to update the <OnStop> method to clean up the service threads for each class instance created. The updated method is displayed in Listing 6.
Listing 6. The updated <OnStop> service method.
Protected Overrides Sub OnStop() ' Add code here to perform any tear-down necessary to stop your service. Try If (Not m_WorkerThread Is Nothing) Then Try WriteLogEvent(My.Resources.ServiceStopping, ONSTOP_INFO, _ EventLogEntryType.Information, My.Resources.Source)
m_ThreadAction.StopThread = True
For Each ftp As FTP In m_WorkerThreads Me.RequestAdditionalTime(THIRTY_SECONDS) ftp.Incoming.Join(TIME_OUT) Next Catch ex As Exception m_WorkerThread = Nothing End Try End If Catch ex As Exception 'We Catch the Exception 'to avoid any unhandled errors 'since we are stopping and 'logging an event is what failed 'we will merely write the output 'to the debug window m_WorkerThread = Nothing Debug.WriteLine("Error stopping service: " + ex.ToString()) End Try End Sub
|
Service Validation
To
test the new functionality, make sure that you have the service
installed and the properly formatted configuration.xml file. After you
run the service you should copy some files with the names that you
wanted to download into the folder. You will notice a few things
immediately.
First, you have no way to
delete the remote file, so once you download it locally it is there
forever, and you will start to see errors the next time you try to
download the file because it can’t be saved again locally. Because I
have not written my code to append to any already existing file, you
should modify the code to either delete any existing file locally,
delete the file remotely, or name the file something unique. If you
choose this last option, I suggest that you use a GUID, because it is
possible for two threads to try to write a file with the same name at
the same time. If you try to do this, the file will overwrite itself.
The second thing
you’ll notice is that the service only allows you to specify a single
file to download, even though the service itself—or more specifically
the FTP class—has the ability to download an array of passed-in file
names. You can modify the service configuration file to pass in more
than one file, and then have the <ThreadFunc> supply the FTP.Files property instead with an array of file names.
The
last thing you’ll notice is that you cannot pass in a wildcard. You can
allow for wildcards by querying the remote directory for the files in
advance and then using the <GetFiles> method to download all the queried files.