Signiant Support

13.3 CTE Developers Guide Print



CTE Integration

This document provides information to help you integrate the Signiant Content Transfer Engine (CTE) SDK with various development platforms in order to provide custom applications that incorporate the Signiant transfer technology.

The information in this guide is divided into the following sections:

  • Overview describes the principals of CTE and the perquisites that this guide assumes.

  • Java Implementation describes how to integrate the CTE into a Java application or applet.
  • HTML/JavaScript Implementation describes how to integrate the CTE into a standard HTML Web page using JavaScript.  In this implementation, a "wrapper" applet is used to provide a completely custom interface.

  • Flex Implementation describes how to integrate the CTE into a Flex application.  This implementation builds on the previous implementations to create a completely custom Flash interface.
  • C# Implementation describes how to create Windows Form applications using C#.

Overview

The Content Transfer Engine (CTE) SDK is a component of the Signiant software suite that allows you to exchange files with users or organizations without requiring both parties to have a Signiant agent installed. The CTE provides a Java API for initiating, monitoring, and controlling transfers using Signiant's patented transport technology.  The Java API is provided via the webclient.jar which can also be used as a stand-alone applet in a browser.  In this configuration, however, the user must interact with the user interface provided in the JAR.  A detailed description of the CTE is provided in the CTE SDK Developer's Guide.

This guide is meant to provide the details necessary for using the CTE within a fully customized user interface in several of the most common implementations.  This is ideal when wanting to integrate transport within an already existing application and maintaining a consistent user interface. The overall architecture is as follows:

Each chapter in this guide builds upon the preceding such that code and principles from previous chapters are used in subsequent chapters to achieve the desired results.

Prerequisites

The following prerequisites are assumed throughout this guide:
  1. Active license of CTE:

    You must have  an active license for the CTE in order to integrate it with other applications.  Please contact your Signiant Sales Representative if you have any questions about your license.

  2. The Signiant infrastructure is configured and working properly:

    The CTE requires communication with a remote Signiant Agent.  While not required to write the integration code, a working Signiant Agent is required for testing.

  3. Working knowledge of the tools/technologies:

    This guide is intended for application developers - not end users.  It is assumed that the reader has a working knowledge of the tools and technologies discussed in this document.

  4. Current versions of development tools. The reader must have current versions of the respective development tools referenced in this document.  This includes:
    • Eclipse IDE (or equivalent Java development tool)
    • Flex Builder

    • JDK 1.7


Java Implementation

This chapter focuses on the creation of a Java applet.  The Java applet "wraps" the existing webclient.jar and provides additional functionality including:

  • Completely invisible transports - no applet UI is exposed
  • Additional callback functionality
  • Multi-Threaded execution

It is assumed that the reader has launched Eclipse (or equivalent Java development tool) and has created a new Java project.

A compiled version of the applet described in this chapter is available from Signiant.  The resulting JAR does not include the webclient.jar so that updates and enhancements to each are completely independent.

Imports

In the interest of clarity this example contains two classes: CTEApplet - contains the UI logic and CTETransfer contains the code that interacts with the Content Transfer Engine. Optionally, a third class implementing the TransferListener protocol to receive callbacks from the CTE could be created to further separate the logic.  

The first step after creating your new project is to add the Signiant webclient.jar and Netscape plugin.jar (used to facilitate java to javascript communication via JSObject calls) to the projec's Java Build Path.

The class that makes calls to the webclient methods (e.g.: CTETransfer) uses the following imports from the com.signiant.interactivetransfer.engine package in the webclient.jar:

FileTransfer
FileTransfer.DirectoryHandlingMode
StatusEvent
TransferEngine
TransferListener (only required in the class implementing a transfer listener)
TransferMode
Transport
Port.SSLMode

 

The class that will facilitate the applet UI (e.g.: CTEApplet) will use the following imported packages:

java.applet.*;
java.awt.*;
java.io.*
java.util.*
javax.swing.*

netscape.javascript.* (from the plugin.jar)

Setting Configuration Parameters

There are a number of configuration parameters that can be set prior to initiating a transport with CTE. The following parameters are available:

Parameter Description
Transfer The type of transfer for this configuration. Choose from "Send" (files are transferred to the selected remote agent) or "Receive" (files are transferred from the selected remote agent).
User The name of the user as which the transfer is running on the Content Transfer Engine-enabled server. With Local authentication, the transfer uses the username and password of the user on the Content Transfer Engine-enabled server. With SOAP authentication, the username and password are passed to the SOAP server, effectively ignored by the Content Transfer Engine-enabled server, since the transfer runs as the default user. The SOAP server can use the username and password to perform authentication.

Note that if you use the %user% and %password% keywords for the Fallback URL List (see above), the user and password you specify here and in the following field are substituted. With FTP transfers, you need to specify the user name and password here for the Fallback URL List, otherwise "anonymous" is assumed.

Password The password for the user as which the transfer is running on the Content Transfer Engine-enabled server. With Local authentication, the transfer uses the username and password of the user on the Content Transfer Engine- enabled server. With SOAP authentication, the username and password are passed to the SOAP server, effectively ignored by the Content Transfer Engine-enabled server, since the transfer runs as the default user. The SOAP server can use the username and password to perform authentication. The field displays the password masked as asterisks.
Directory Handling Mode Specifies the way the directory structure will be replicated on transfer. Choose from the following:
  • Full (replicates the parent directory, as well as the specified directory. For example, if the specified directory is "\my files", on transfer, the directory structure would include the parent, similar to "c:\parent directory\my files").
  • Flat (all the files appear in a single root directory, regardless of sub- directory structure on the source)
  • Path (the directory structure is replicated from the specified directory downwards. For example, "\my files\new\file 1, file 2")
C.A. Certificate Displays the CA certificate the agent uses for server authentication. The certificate that appears by default is the one generated by the Manager. You may want to use a different CA certificate if you are using a different Certificate Authority. The certificate must be from the Manager from which the agent was installed. To extract a CA, follow these steps:
  1. Login as root/Administrator to a host that is populated with the Manager trusted CA whose certificate you are trying to obtain. For example, if you are interested in obtaining a Manager trusted CA certificate for the site MySite.com, then logon to an agent that belongs to MySite.com (i.e., MyHost01.MySite.com).
  2. Run the administrative command: dds_cert extract. This will extract certificates from the credentials store, and among them will exist the Manager trusted CA certificate named ddsCA_cert.pem.
  3. Copy ddsCA_cert.pem to a location where agents wishing to import it into their local credentials store can access it.
  4. Logout
  5. Paste the contents of the ddsCA_cert.pem file in the CA Certificate field. OR e. Click Browse to select the location of the CA cert file.
Number of Retries The number of times the source attempts to transfer a file after an initial failure, such as "file in use". Default value is 3. A value of 0 indicates no retries. Maximum value you can specify is 99.
Number of Restarts Specifies the maximum number of times the SDK attempts to restart a transfer after a recoverable failure such as a network outage. If set to zero ("0"), no transfer agent restarts are attempted. Maximum value you can specify is 99. The default value is 3.
Transport The method to use for transferring the files. Choose from the following: WAN Accelerator then Parallel Streams - Use the WAN accelerator (UDP) initially, but if communication cannot be established, try parallel streams (TCP). WAN Accelerator - Use the WAN Accelerator (UDP) only. Parallel Streams - Use Parallel Streams (TCP) only. Note that if you specify a Fallback URL List (see above), then the transport will go to the fallback option if none of the transport option(s) specified here works. If a fallback URL is specified, but an agent is not, this field is ignored.
Number of Streams Specifies the number of files to transfer at one time. The default (and maximum) value is 4. Specifying zero will cause an error. The default value becomes the lower of the number of files to be transferred or the number of streams specified. For example, if you specify two files, only two streams are used.
Bandwidth Limit Limit this job to the amount of bandwidth specified. You set this limit in bytes, kilobytes or megabytes per second to a maximum of whatever the CPU can handle, or a percentage of a selected network connection (for example, 75% of 128 Kbytes/s). A bandwidth level below 128 Kbytes/sec is not supported, as performance is not reliable. Note that if you specify a Target Transfer Rate for WAN Accelerator parameters (see below), the Bandwidth Limit setting is not the maximum bandwidth limit. The Target Transfer rate becomes the de facto maximum bandwidth rate, as it provides a ceiling for the transfer rate, factoring in retransmission rate and network sharing. (For example, if the transfer had to go below the bandwidth limit to allow for shared network usage, once the network is less busy, the transfer may go above the Bandwidth Limit, to a maximum of the specified Target Transfer Rate until the transmission averages to the specified Bandwidth Limit, at which point the bandwidth limit becomes effective again.)

Note that bandwidth throttles may also be employed by other network devices and policies (e.g., QoS), therefore, a bandwidth throttle (or target maximum) defined here may not be achievable. If you are having difficulty achieving a particular bandwidth target, ensure that other policies are not impacting your ability to reach the desired throughput. Note also that bandwidth throttles and pause/resume are honored for all transfers, regardless of protocol. Some FTP servers cannot support resume. If the FTP server advertises that it cannot, the user is informed immediately, and pause/resume will cause the current transfer to start over at the beginning.

Encrypt Allows users to specify the encryption level from the following values: "Strong encryption", "No encryption, signed" or "No encryption, unsigned". The default value is "Strong encryption". Note that server authentication is always used regardless of the encryption level specified. The "No encryption, signed" option transfers unencrypted (plain text) data, but includes the SSL protocol's message digest calculation and digital signing to ensure data stream integrity.

The "No encryption, unsigned" option allows the transfer of data, after the initial SSL authentication of the endpoints to proceed without encryption, message digest computation or digital signing. This mode of operation is only for raw performance, since it makes no guarantee of the integrity of the data stream other than what is provided by the network channel. The underlying TCP protocol can guarantee the integrity of messages across each single network hop, but has no facilities for detecting a man-in-the-middle attack.

Agent List A space-separated list of the agent(s) with which the client can communicate, as well as any relay agents those agents may need. When more than one agent is listed, the transfer engine attempts to launch a transfer on all of the agents at the same time. The first agent with which it connects continues with the transfer. All other connections immediately end. If the agent has a relay, use the following format: agent1[relay1 relay2(portForRelay2)] agent2[relay3(port) relay4] agent3 agent4 Note that port numbers are optional, but the square brackets are required. Both the source and the relay agent must use the same port number. In addition, you must enable the Content Transfer Engine SDK on the relay agent. 
Fallback URL List This is a list of URLs that the transfer will attempt to use once the agent list is exhausted, or if it has not been specified. The following protocols are supported: UDP (WAN Acceleration), TCP (parallel streams), HTTP (parallel streams over HTTP), or FTP. Use the following format for the fallback URL list, separating each URL with a new line "\n" or the pipe "|" character. With the pipe character as separator, the Content Transfer Engine SDK tries all of the addresses at the same time and uses the quickest to respond. URLs separated by new lines are processed in the order in which they appear:protocol://[username@ | username:password@]host[:port]/path [ | ] [ protocol://host/path]Protocol is one of: mxtcp (TCP authentication), mxwan (WAN acceleration), HTTP or FTP. The host and path are implementation dependent, and the port is an optional value that may be specified in order to use a non-standard port. The user name and password are optional and if specified, can either be clear text, or the keywords %user% and %password%, which are substituted for the currently-supplied Content Transfer Engine SDK user name and password.

Note that the native Content Transfer Engine SDK format is as follows:protocol://target.host@relay.host:portWhere "protocol" can be â"mxtcp" for a parallel streams (TCP) transfer, or "mxwan" for a udp transfer, "target.host" is the name passed to the relay (if present), "relay.host" is the agent to which a direct connection is made, and "port" is the port on which the agent is listening. Note that with FTP transfers, you need to use the user name and password in the Fallback URL List, otherwise "anonymous" is assumed. For sample Fallback URL implementations, see Sample Fallback URL Implementations.

Logging Specifies the level of detail that appears in the log files for a job run. Choose from "None", "Info", "Fine", "Finer", or "Finest". "Finest" provides the greatest amount of detail, while "Info" provides the least. The default is "Info". Note "Finest" logging may have a significant impact on performance.
Trace Whether to create a trace file for the connection on the agent. Use in consultation with a technical support person to define agent trace flags.
WAN Acceleration Specific Parameters
Target Transfer Rate The rate at which the agent should attempt to send data. Typically this is the maximum speed of the network (i.e., 100Mbps, 10Mbps, and so on). You no longer have to set a definitive target transfer rate, as the transfer will now attempt to discover the target throughput by analyzing packet loss and adjusting its transmission rate accordingly. Each transfer will continually adjust itself to maximize its throughput across all network access points between the source and the target. You may specify the target transfer rate for use under conditions where you know the actual link throughput in advance. If specified, the transfer uses the target transfer rate as a bandwidth ceiling. It is the rate of transfer including retransmission. Note that if you plan to set a Minimum Transfer Rate (see below) you must specify a Target Transfer Rate that is larger than the Minimum Transfer Rate you specify.
Minimum Transfer Rate The minimum rate at which the transfer should send the data. Note that the Minimum Transfer Rate works only if you set a Target Transfer Rate (see above), and if that Target Transfer Rate value is larger than the specified minimum transfer rate. Limits the slowest transmission rate to which the transfer can back off, regardless of packet loss. Be careful when setting the Minimum Transfer Rate variable, as it can flood the network.
Aggressiveness Indicates how sensitive the job will be to other network traffic when running. "High" aggressiveness yields the least to other network traffic. Choose from one of the following:

High: The agent will always attempt to send data at the Target Transfer rate (specified or dynamically calculated) and not share with other network traffic. It is recommended that you specify a Target Transfer rate with the High aggressiveness setting. If you do not, it is possible for the transfer to drive other traffic off the network, possibly resulting in a denial of service. By setting a reasonable target transfer rate, this setting can be most effective at overcoming packet loss on less reliable networks.

Medium: The agent will always attempt to send data at the Target transfer rate (specified or dynamically calculated), however, if other traffic is detected, the agent will share the network resources but never fall below the Minimum Transfer Rate (if specified). Use the Medium aggressiveness setting as the default setting, along with specifying a Target Transfer rate value where possible. Setting the target achieves a balance between network sharing and overcoming packet loss.

Low: The agent will always attempt to send data at the Target Transfer Rate (specified or dynamically calculated), however, if other traffic is detected, the agent will drop its transfer rate more quickly and attempt to creep back up to the Target Transfer rate more slowly. Use the Low aggressiveness setting for clean networks (little or no packet loss between endpoints) where sharing the network is the primary goal of the transfer.

Agents involved in a Content Transfer Engine SDK transfer must be configured as Content Transfer Engine-enabled servers. Make sure the agents you specify are properly configured.

In our Java applet example, we'll create setters and getters for each of these parameters, with public scope, thus exposing them externally so that they can be set outside of the applet itself (see Setters/Getters below). Additionally these will be initially configured, where appropriate, via a configuration file.

Directly via Obfuscated String

The Signiant CTE can have its entire configuration settings set via a single obfuscated string. This is the most secure way to pass the configuration settings to the engine. The obfuscator can be run dynamically on the server or, if the configuration settings are not likely to change often, the obfuscator can be run manually from the command line. See Running the Obfuscator section in Chapter 7 in the CTE SDK Developer's Guide for details.

The following code loads the configuration file contents into local variables:

try{
private Properties config = new Properties();
private String configFile = "CTEConfig.txt";

config.load(this.getClass().getClassLoader().getResourceAsStream(configFile));

mxAgentList = config.getProperty("mxAgentList");
mxAgentUser = config.getProperty("mxAgentUser");
mxAgentPwd = config.getProperty("mxAgentPwd");
mxSourceDir = config.getProperty("mxSourceDir");
mxDestinationDir = config.getProperty("mxDestinationDir");
mxLogging = config.getProperty("mxLogging");
mxTransport = config.getProperty("mxTransport");
mxTargetRate = Long.parseLong(config.getProperty("mxTargetRate"));
mxTrace	= config.getProperty("mxTrace");
mxAgentCert = config.getProperty("mxAgentCert");
mxURLList = config.getProperty("mxFallbackURLList");
mxEncryption = config.getProperty("mxEncryption");
mxDirHandlingMode = config.getProperty("mxDirHandlingMode");

try{
mxAggressiveness = Integer.parseInt(config.getProperty("mxAggressiveness"));
}catch (NumberFormatException nfe){
System.out.println("mxAggressiveness config setting cannot be converted to an int");
}

try{
mxBandwidthThrottle = Long.parseLong(config.getProperty("mxBandwidthThrottle"));
}catch (NumberFormatException nfe){
System.out.println("mxBandwidthThrottle config setting cannot be converted to a long");
}

try{
mxRestarts = Integer.parseInt(config.getProperty("mxRestarts"));
}catch (NumberFormatException nfe){
System.out.println("mxRestarts config setting cannot be converted to an int");
}

try{
mxStreams = Integer.parseInt(config.getProperty("mxStreams"));
}catch (NumberFormatException nfe){
System.out.println("mxStreams config setting cannot be converted to an int");
}

try{
mxRetries = Integer.parseInt(config.getProperty("mxRetries"));
}catch (NumberFormatException nfe){
System.out.println("mxRetries config setting cannot be converted to an int");
		}

try{
mxUDPMinimumRate = Long.parseLong(config.getProperty("mxUDPMinimumRate"));
}catch (NumberFormatException nfe){
System.out.println("mxUDPMinimumRate config setting cannot be converted to a long");
}

}catch (Exception ex){
System.out.println("Exception loading config file: " + ex.toString());
}

 

Once the configuration file has been loaded and the values parsed to local variables, the following code is used to set the corresponding parameters in the Signiant CTE:

//assign self as the listener
	CTETransfer listener = new CTETransfer();

	//set engine properties
	try{
		engine.setCertificate(mxAgentCert);
		engine.setMode(mxTransferMode);

		if (mxTransport.equalsIgnoreCase("Transport.TCP")){
			engine.setTransport(Transport.TCP);
		}else if (mxTransport.equalsIgnoreCase("Transport.UDP")){
			engine.setTransport(Transport.UDP);
}else if (mxTransport.equalsIgnoreCase("Transport.UDP_THEN_TCP")){
			engine.setTransport(Transport.UDP_THEN_TCP);
		}

		if (mxTargetRate > 0){
			engine.setTargetRate(mxTargetRate);
		}

		if (mxBandwidthThrottle > 0){
			engine.setBandwidthThrottle(mxBandwidthThrottle);
		}

		if (mxRestarts > 0){
			engine.setRestarts(mxRestarts);
		}

		if (mxStreams > 0){
			engine.setNumberOfStreams(mxStreams);
		}

		if (mxRetries > 0){
			engine.setRetries(mxRetries);
		}

		if (mxEncryption != null && mxEncryption.length() > 0){
			if (mxEncryption.equals("STRONG")){
				engine.setEncryption(SSLMode.STRONG);
			}
			if (mxEncryption.equals("SIGNED")){
				engine.setEncryption(SSLMode.NONE);
			}
			if (mxEncryption.equals("UNSIGNED")){
				engine.setEncryption(SSLMode.UNSIGNED);
			}
		}

if (mxDirHandlingMode != null
&& mxDirHandlingMode.length() > 0){
			if (mxDirHandlingMode.equals("PATH")){
				engine.setDirectoryHandlingMode(DirectoryHandlingMode.PATH);
			}
			if (mxDirHandlingMode.equals("FLAT")){
					engine.setDirectoryHandlingMode(DirectoryHandlingMode.FLAT);
			}
			if (mxDirHandlingMode.equals("FULL")){
					engine.setDirectoryHandlingMode(DirectoryHandlingMode.FULL);
			}
		}

		if (mxUDPMinimumRate > 0){
			engine.setMinimumRate(mxUDPMinimumRate);
		}

		engine.setAggressiveness(mxAggressiveness);

		engine.setLogging(mxLogging);

		if (mxTrace != null && mxTrace.length() > 0){
			engine.setTrace(mxTrace);
		}

		if (mxURLList != null && mxURLList.length() > 0){
			engine.setUrlList(mxURLList);
		}

		engine.setAgentList(mxAgentList);
		engine.setUser(mxAgentUser);
		engine.setPassword(mxAgentPwd.toCharArray());
		engine.setDestination(mxAgentDest);
		engine.setFiles(fileNames);
		engine.addTransferListener(listener);

	}catch(Exception e){
System.out.println("Exception setting CTE Engine properties: " + e.toString());
	}

 

Setters/Getters

Knowing that this applet will be used later on by our HTML/JavaScript and Flex examples, we have also exposed all of these same configuration options as setters and getters using the following code:

public String getAgentList(){
		return mxAgentList;
	}
	public void setAgentList(String sAgentList){
		mxAgentList = sAgentList;
	}

	public String getAgentFallbackURLList(){
		return mxURLList;
	}
	public void setAgentFallbackURLList(String sURLList){
		mxURLList = sURLList;
	}

	public String getAgentUser(){
		return mxAgentUser;
	}
	public void setAgentUser(String sUser){
		mxAgentUser = sUser;
	}

	public String getAgentPwd(){
		return mxAgentPwd;
	}
	public void setAgentPwd(String sPwd){
		mxAgentPwd = sPwd;
	}

	public String getAgentSourceDir(){
		return mxSourceDir;
	}
	public void setAgentSourceDir(String sDir){
		mxSourceDir = sDir;
	}

	public String getAgentDestDir(){
		return mxDestinationDir;
	}
	public void setAgentDestDir(String sDir){
		mxDestinationDir = sDir;
	}

	public String getAgentCert(){
		return mxAgentCert;
	}
	public void setAgentCert(String sCert){
		mxAgentCert = sCert;
	}

	public String getAgentLogging(){
		return mxLogging;
	}
	public void setAgentLogging(String sLogging){
		mxLogging = sLogging;
	}

	public String getAgentTransport(){
		return mxTransport;
	}
	public void setAgentTransport(String sTransport){
		mxTransport = sTransport;
	}

	public long getAgentTargetRate(){
		return mxTargetRate;
	}
	public void setAgentTargetRate(long lTargetRate){
		mxTargetRate = lTargetRate;
	}

	public int getAgentAggressiveness(){
		return mxAggressiveness;
	}
	public void setAgentAggressiveness(int iAgressiveness){
		mxAggressiveness = iAgressiveness;
	}

	public String getAgentTrace(){
		return mxTrace;
	}
	public void setAgentTrace(String sTrace){
		mxTrace = sTrace;
	}

	public long getAgentBandwidthThrottle(){
		return mxBandwidthThrottle;
	}
	public void setAgentBandwidthThrottle(long throttle){
		mxBandwidthThrottle = throttle;
	}

	public int getAgentRestarts(){
		return mxRestarts;
	}
	public void setAgentRestarts(int restarts){
		mxRestarts = restarts;
	}

	public int getAgentStreams(){
		return mxStreams;
	}
	public void setAgentStreams(int streams){
		mxStreams = streams;
	}

	public int getAgentRetries(){
		return mxRetries;
	}
	public void setAgentRetries(int retries){
		mxRetries = retries;
	}

	public String getAgentEncryption(){
		return mxEncryption;
	}
	public void setAgentEncryption(String encryption){
		mxEncryption = encryption;
	}

	public String getAgentDirHandlingMode(){
		return mxDirHandlingMode;
	}
	public void setAgentDirHandlingMode(String mode){
		mxDirHandlingMode = mode;
	}

	public long getAgentUDPMinimumRate(){
		return mxUDPMinimumRate;
	}
	public void setAgentUDPMinimumRate(long rate){
		mxUDPMinimumRate = rate;
}

Selecting Files and Directories

The Java applet uses both of the two built-in file choosers:

AWT

The AWT file chooser visually looks like the native file browser of the client OS. It has certain limitations, but it is used in our samples for cases where you want to limit the user to selecting only a single file. Invoking the AWT file chooser is done as follows:

java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<File>(){
	public File run(){
		try{
			Frame frame = new Frame() ;
			FileDialog fd = new FileDialog(frame, "Open", FileDialog.LOAD) ;
			fd.setVisible(true);
			String filename = fd.getFile();
			String filePath = fd.getDirectory();
			if (!filePath.endsWith(File.separator)){
				filePath += File.separatorChar;
			}
			String fullPath = fd.getDirectory() + fd.getFile();

			if (filename != null) {
				//return the selected files on a new thread
				//so not to block the UI processing
				ReturnSelectedFilesHandler rh = new ReturnSelectedFilesHandler();

				String[] args = new String[1];
				args[0] = fullPath;

				rh.args = args;
				new Thread(rh).start();
			}else{
				System.out.println("chooseFileForUI - user canceled file selection");
			}
		}catch (Exception e){
			System.out.println("chooseFileForUI - exception: " + e.toString());
		}
		File selFile = null; //doPrivilidged requires a return of type File
		return selFile;
	}
});

static class ReturnSelectedFilesHandler implements Runnable{
	public String sRet;
	public String[] args;

	public void run(){
		//if the UI subscribed the the onFilesSelected event
		//we make the call to the event handler in the UI
		if (onFilesSelectedHandler != null && onFilesSelectedHandler.length() > 0){
			sRet = win.call(onFilesSelectedHandler, args).toString();
		}
	}
	}

 

Swing

The Swing file chooser is the more typical "Java" grey file chooser. While it has a distinct look-and-feel, it provides the most flexibility. In our sample we use it to allow for multiple files to be selected and the selection of directories. Invoking the Swing file chooser is done as follows:

java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<File>(){
	public File run(){
		try{
			JFileChooser fc = new JFileChooser();
			if (directoriesOnly){
				fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
				fc.setMultiSelectionEnabled(false);
			}else{
				fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
				fc.setMultiSelectionEnabled(true);
			}

			int returnVal = fc.showOpenDialog(null);
			if (returnVal == JFileChooser.APPROVE_OPTION){
				String selFiles = "";

				if (directoriesOnly){
					File selDir = fc.getSelectedFile();
					selFiles = selDir.getPath();
				}else{
					File[] selectedFiles = fc.getSelectedFiles();
					for (int i = 0; i < selectedFiles.length; i++){
						if (i > 0){
							selFiles += "||";
						}
						selFiles += selectedFiles[i].getPath();
					}
				}

				String[] args = new String[1];
				args[0] = selFiles;

				if (directoriesOnly){
					//return the selected folder on a new thread
					//so not to block the UI processing
					ReturnSelectedFolderHandler rh = new ReturnSelectedFolderHandler();
					rh.args = args;
					new Thread(rh).start();
				}else{
					//return the selected files on a new thread
					//so not to block the UI processing
					ReturnSelectedFilesHandler rh = new ReturnSelectedFilesHandler();
					rh.args = args;
					new Thread(rh).start();
				}
			}else{
				System.out.println("chooseFileForUI - user canceled file selection");
			}

		}catch (Exception e){
			System.out.println("chooseFileForUI - exception: " + e.toString());
		}
		File selFile = null; //requires a return of type File
		return selFile;
	}
});

static class ReturnSelectedFilesHandler implements Runnable{
	public String sRet;
	public String[] args;

	public void run(){
		//if the UI subscribed the the onFilesSelected event
		//we make the call to the event handler in the UI
		if (onFilesSelectedHandler != null && onFilesSelectedHandler.length() > 0){
			sRet = win.call(onFilesSelectedHandler, args).toString();
		}
	}
}

static class ReturnSelectedFolderHandler implements Runnable{
	public String sRet;
	public String[] args;

	public void run({
		//if the UI subscribed the the onFolderSelected event
		//we make the call to the event handler in the UI
		if (onFolderSelectedHandler != null && onFolderSelectedHandler.length() > 0){
			sRet = win.call(onFolderSelectedHandler, args).toString();
		}
		//sRet = win.call("folderSelected", args).toString();
	}
}

 

Starting a Transfer

The Java applet allows for two kinds of transfers Upload and Download. The option is set via the TransferMode property as either TransferMode.SEND or TransferMode.Receive values respectively. In a receive operation the destination folder for the downloaded file(s) is set via the setDestination property setter of the Transfer Engine. All Transfer Engine properties are set from variables, the transfer process is initiated and progress is monitored and reported back to the UI via the submitFiles method:

public boolean submitFiles(String[] fileNames)
	{
		boolean bSuccess = true;

		//assign self as the listener
		CTETransfer listener = new CTETransfer();

		//set engine properties
		try
		{
			engine.setCertificate(mxAgentCert);

			engine.setMode(mxTransferMode);

			if (mxTransport.equalsIgnoreCase("Transport.TCP"))
			{
				engine.setTransport(Transport.TCP);
			}
			else if (mxTransport.equalsIgnoreCase("Transport.UDP"))
			{
				engine.setTransport(Transport.UDP);
			}
			else if (mxTransport.equalsIgnoreCase("Transport.UDP_THEN_TCP"))
			{
				engine.setTransport(Transport.UDP_THEN_TCP);
			}

			if (mxTargetRate > 0)
			{
				engine.setTargetRate(mxTargetRate);
			}

			if (mxBandwidthThrottle > 0)
			{
				engine.setBandwidthThrottle(mxBandwidthThrottle);
			}

			if (mxRestarts > 0)
			{
				engine.setRestarts(mxRestarts);
			}

			if (mxStreams > 0)
			{
				engine.setNumberOfStreams(mxStreams);
			}

			if (mxRetries > 0)
			{
				engine.setRetries(mxRetries);
			}

			if (mxEncryption != null && mxEncryption.length() > 0)
			{
				if (mxEncryption.equals("STRONG"))
				{
					engine.setEncryption(SSLMode.STRONG);
				}
				if (mxEncryption.equals("SIGNED"))
				{
					engine.setEncryption(SSLMode.NONE);
				}
				if (mxEncryption.equals("UNSIGNED"))
				{
					engine.setEncryption(SSLMode.UNSIGNED);
				}
			}

			if (mxDirHandlingMode != null && mxDirHandlingMode.length() > 0)
			{
				if (mxDirHandlingMode.equals("PATH"))
				{
					engine.setDirectoryHandlingMode(DirectoryHandlingMode.PATH);
				}
				if (mxDirHandlingMode.equals("FLAT"))
				{
					engine.setDirectoryHandlingMode(DirectoryHandlingMode.FLAT);
				}
				if (mxDirHandlingMode.equals("FULL"))
				{
					engine.setDirectoryHandlingMode(DirectoryHandlingMode.FULL);
				}
			}

			if (mxUDPMinimumRate > 0)
			{
				engine.setMinimumRate(mxUDPMinimumRate);
			}

			engine.setAggressiveness(mxAggressiveness);

			engine.setLogging(mxLogging);

			if (mxTrace != null && mxTrace.length() > 0)
			{
				engine.setTrace(mxTrace);
			}

			if (mxURLList != null && mxURLList.length() > 0)
			{
				engine.setUrlList(mxURLList);
			}

			engine.setAgentList(mxAgentList);
			engine.setUser(mxAgentUser);
			engine.setPassword(mxAgentPwd.toCharArray());
			engine.setDestination(mxAgentDest);
			engine.setFiles(fileNames);
			engine.addTransferListener(listener);


			//sanity check
			if (debugMode)
			{
			System.out.println("CTETransfer.submitFiles - mxTransport = " + mxTransport);
			System.out.println("CTETransfer.submitFiles - mxTargetRate = " + mxTargetRate);
			System.out.println("CTETransfer.submitFiles - mxAggressiveness = " + mxAggressiveness);
			System.out.println("CTETransfer.submitFiles - mxLogging = " + mxLogging);
			System.out.println("CTETransfer.submitFiles - mxTrace = " + mxTrace);
			System.out.println("CTETransfer.submitFiles - mxAgentList = " + mxAgentList);
			System.out.println("CTETransfer.submitFiles - mxAgentUser = " + mxAgentUser);
			System.out.println("CTETransfer.submitFiles - mxAgentPwd = " + mxAgentPwd);
			System.out.println("CTETransfer.submitFiles - mxAgentDest = " + mxAgentDest);
			System.out.println("CTETransfer.submitFiles = mxURLList = " + mxURLList);
			}
		}
		catch(Exception e)
		{
			if (debugMode)
			{
				System.out.println("Exception setting Signiant properties: " + e.toString());
			}
			return false;
		}

		try
		{
			engine.startTransfer();

			//inform controller applet which transport protocol is being used
			CTEApplet.transportProtocolChange(engine.getTransport().toString());

		}
		catch (Exception e)
		{
			System.err.println("Exception caught initiating transfer: " + e.getMessage ());
			return false;
		}

		try
		{
			//start the progress loop to report completion status and transfer
			//rate to controller applet
			startProgressLoop();
		}
		catch (Exception ex)
		{
			if (debugMode)
			{
				System.out.println("Exception in stats loop: " + ex.toString());
			}
		}

		for (int i = 0; i < listener.files.size(); i++)
		{
			FileTransfer f = listener.files.elementAt(i);

			String fileStatus = "File: " + f.getFileString();

			switch (f.getState())
			{
			case PENDING:
				fileStatus += " did not complete";
				break;
			case SKIPPED_DENIED:
				fileStatus += " transfer denied";
				break;
			case SKIPPED_EXISTS:
				fileStatus += " was already present on the remote system";
				break;
			case TRANSFERRED:
				fileStatus += " was successfully transferred";
				bSuccess = true;
				break;
			case ERROR:
				fileStatus += " encountered an error during transfer";
				bSuccess = false;
				break;
			}

			if (debugMode)
			{
				System.out.println(fileStatus);
			}
		}

		CompletionStatus status = engine.getCompletionStatus();
		switch (status)
		{
		case SUCCESSFUL:
			if (debugMode)
			{
				System.out.println("engine.completionStatus = SUCCESSFUL");
			}
			bSuccess = true;
			break;
		case WARNING:
			if (debugMode)
			{
				System.out.println("engine.completionStatus = WARNING");
			}
			bSuccess = true;
		case FAILURE:
			if (debugMode)
			{
				System.out.println("engine.completionStatus = FAILURE");
				System.out.println("The following errors occurred during the transfer: ");
				for (int i = 0; i < listener.errors.size(); i++){
					System.out.println(" "+listener.errors.elementAt(i++));
				}
			}
			bSuccess = false;
			break;
		}

		return bSuccess;
	}

 

Monitoring Progress

Engine progress is interrogated in a while (!engine.isCompleted) loop. In this example the loop is run in a separate thread so that the UI (browser) can get some CPU cycles to repaint:

private static void startProgressLoop(){
		try{
			LoopHandler lh = new LoopHandler();
			new Thread(lh).start();
		}catch (Exception e){
			System.out.println("Exception caught in startProgressLoop: "
			+ e.toString());
		}
	}


	static class LoopHandler implements Runnable{
		public void run(){
			while (!engine.isCompleted()){
				if (isPaused){
					//static var set when UI requests a pause
					break;
				}
				try { Thread.sleep(200); } catch (Exception discard) {}
				// Wait for the engine to load files prior to emitting any statistics
				if(engine.getTotalBytesToTransfer() == 0){
					continue;
				}
				//calculate the completion percentage
				long percentComplete = (engine.getBytesSkipped()+engine.getBytesTransferred())
				* 100 / engine.getTotalBytesToTransfer();
				//put current transfer rate in var
				long transferRateKb = engine.getTransferRate() / 1024;

				//only report if vals changed
				if (percentComplete != lastProgress){
					System.out.println("CTETransfer.submitFiles - progress: "
					+ percentComplete + "% - " + transferRateKb + " KBytes/s \r" );

					//pass status to applet class
					CTEApplet.percentXferComplete(percentComplete, transferRateKb);

					lastProgress = percentComplete;
				}
			}
		}
	}

 

Callbacks

The following defines the callbacks that the Java applet will make and their respective signatures:

onProgress

/*
	 * percentXferComplete method
	 *
	 * called by the transfer class providing the percentage of completion
	 * and the transfer rate of a running transfer
	 *
	 * if the onProgress event is subscribed to by the UI a callback is
	 * made on a new thread with the current status
	 */
	public static void percentXferComplete(long percentComplete, long transferRateKb)
	{
		try
		{
			if (debugMode)
			{
				System.out.println("CTEApplet.percentXferComplete - percentComplete = "
				+ percentComplete + ", transfer rate = " + transferRateKb + " KB/s");
			}

			//if the UI subscribed the the onProgress event
			//we make the call to the event handler in the UI
			if (onProgressEventHandler != null && onProgressEventHandler.length() > 0)
			{
				String[] args = {String.valueOf(percentComplete),String.valueOf(transferRateKb)};

				ProgressHandler ph = new ProgressHandler();
				ph.args = args;
				new Thread(ph).start();
			}

		}
		catch (Exception e)
		{
			if (debugMode)
			{
				System.out.println("Exception caught in updateProgress: " + e.toString());
			}
		}
	}

	/*
	 * ProgressHandler class
	 *
	 * makes the call to the UI with the completion percentage and transfer rate
	 */
	static class ProgressHandler implements Runnable
	{
		public String[] args;

		public void run()
		{
			win.call(onProgressEventHandler, args);
		}
	}

onStatus

/*
	 * updateStatus method
	 *
	 * called by the transfer class when the engine statusUpdate event is fired
	 *
	 * if the onStatus event is subscribed to by the UI a callback is
	 * made on a new thread with the current status
	 */
	public static void updateStatus(String status)
	{
		if (onStatusEventHandler != null && onStatusEventHandler.length() > 0)
		{
			submissionStatusHandler ssh = new submissionStatusHandler();
			String[] args = {status};
			ssh.status = args;
			new Thread(ssh).start();
		}
	}

	/*
	 * SubmissionStatusHandler class
	 *
	 * makes the call to the UI with the status reported by the engine statusUpdate event
	 */
	static class submissionStatusHandler implements Runnable
	{
		public String[] status;

		public void run()
		{
			win.call(onStatusEventHandler, status);
		}
	}

onComplete

/*
	 * completionStatus method
	 *
	 * called by the transfer class when the engine postTransfer event is fired
	 *
	 * if a log file was created for the transfer the path is returned to the UI
	 *
	 * if the onCompletion event is subscribed to by the UI a callback is
	 * made on a new thread with the status message
	 */
	public static void completionStatus(String status)
	{
		CompletionStatusHandler csh = new CompletionStatusHandler();
		String[] args = {status};
		csh.status = args;
		new Thread(csh).start();
	}

	/*
	 * CompletionStatusHandler class
	 *
	 * makes the call to the UI with the status reported by the engine postTransfer event
	 */
	static class CompletionStatusHandler implements Runnable
	{
		public String[] status;

		public void run()
		{
			if (onLogfileEventHandler != null && onLogfileEventHandler.length() > 0)
			{
				if (logFileExists(localLogfile))
				{
					String[]logfileArr = {localLogfile};
					win.call(onLogfileEventHandler, logfileArr);
				}
			}
			if (onCompleteEventHandler != null && onCompleteEventHandler.length() > 0)
			{
				win.call(onCompleteEventHandler, status);
			}
		}
	}

onPause

/*
	 * pauseTransfer method
	 *
	 * makes a call, on a new thread via the PauseHandler class,
	 * to the engine to pause the transfer
	 */
	public boolean pauseTransfer()
	{
		try
		{
			PauseHandler ph = new PauseHandler();
			new Thread(ph).start();

			//if the UI subscribed the the onPause event
			//we make the call to the event handler in the UI
			if (onPauseEventHandler != null && onPauseEventHandler.length() > 0)
			{
				win.eval(onPauseEventHandler);
			}
			return true;
		}
		catch (Exception e)
		{
			if (debugMode)
			{
				System.out.println("Exception caught in pauseTransfer: " + e.toString());
			}
			return false;
		}
	}

	/*
	 * PauseHandler class
	 *
	 * makes a call to the transfer engine to pause a running transfer
	 * on a new thread with security access privileges
	 */
	static class PauseHandler implements Runnable
	{
		public void run()
		{
		try
		{
			java.security.AccessController.doPrivileged(
				new java.security.PrivilegedAction<String>()
				{
					public String run()
					{
						//make the call and evaluate the return
						if (xfer.pauseTransfer())
						{
							if (debugMode)
							{
								System.out.println("transfer paused");
							}
							return "true";
						}
						else
						{
							if (debugMode)
							{
								System.out.println("unable to pause transfer");
							}
							return "false";
						}
					}
				}
			);

		}
		catch (Exception ex)
		{
			if (debugMode)
			{
				System.out.println("Exception caught in PauseHandler.run: " + ex.toString());
			}
		}
		}
	}

onResume

/*
	 * resumeTransfer method
	 *
	 * makes a call, on a new thread via the ResumeHandler class,
	 * to the engine to resume a paused transfer
	 */
	public boolean resumeTransfer()
	{
		try
		{
			ResumeHandler rh = new ResumeHandler();
			new Thread(rh).start();

			//if the UI subscribed the the onResume event
			//we make the call to the event handler in the UI
			if (onResumeEventHandler != null && onResumeEventHandler.length() > 0)
			{
				win.eval(onResumeEventHandler);
			}
			return true;
		}
		catch (Exception e)
		{
			if (debugMode)
			{
				System.out.println("Exception caught in resumeTransfer: " + e.toString());
			}
			return false;
		}
	}

	/*
	 * ResumeHandler class
	 *
	 * makes a call to the transfer engine to resume a paused transfer
	 * on a new thread with security access privileges
	 */
	static class ResumeHandler implements Runnable
	{
		public void run()
		{
		try
		{
			java.security.AccessController.doPrivileged(
				new java.security.PrivilegedAction<String>()
				{
					public String run()
					{
						//make the call and evaluate the return
						if (xfer.resumeTransfer())
						{
							if (debugMode)
							{
								System.out.println("transfer resumed");
							}
							return "true";
						}
						else
						{
						if (debugMode)
							{
								System.out.println("unable to resume transfer");
							}
							return "false";
						}
					}
				}
			);

		}
		catch (Exception ex)
		{
			if (debugMode)
			{
				System.out.println("Exception caught in ResumeHandler.run: " + ex.toString());
			}
		}
		}
	}

onCancel

/*
	 * cancelTransfer method
	 *
	 * makes a call, on a new thread via the CancelHandler class,
	 * to the engine to cancel a running transfer
	 */
	public boolean cancelTransfer()
	{
		try
		{

			CancelHandler ch = new CancelHandler();
			new Thread(ch).start();

			//if the UI subscribed the the onCancel event
			//we make the call to the event handler in the UI
			if (onCancelEventHandler != null && onCancelEventHandler.length() > 0)
			{
				win.eval(onCancelEventHandler);
			}

			return true;

		}
		catch (Exception e)
		{
			if (debugMode)
			{
				System.out.println("Exception caught in resumeTransfer: " + e.toString());
			}
			return false;
		}
	}

	/*
	 * CancelHandler class
	 *
	 * makes a call to the transfer engine to cancel a running transfer
	 * on a new thread with security access privileges
	 */
	static class CancelHandler implements Runnable
	{
		public void run()
		{
		try
		{
			java.security.AccessController.doPrivileged(
				new java.security.PrivilegedAction<String>()
				{
					public String run()
					{
						//make the call and evaluate the return
						if (xfer.cancelTransfer())
						{
							System.out.println("transfer cancelled");
							return "true";
						}
						else
						{
							System.out.println("unable to cancel transfer");
							return "false";
						}
					}
				}
			);

		}
		catch (Exception ex)
		{
			if (debugMode)
			{
				System.out.println("Exception caught in CancelHandler.run: " + ex.toString());
			}
		}
		}
	}

onLogFile

/*
	 * completionStatus method
	 *
	 * called by the transfer class when the engine postTransfer event is fired
	 *
	 * if a log file was created for the transfer the path is returned to the UI
	 *
	 * if the onCompletion event is subscribed to by the UI a callback is
	 * made on a new thread with the status message
	 */
	public static void completionStatus(String status)
	{
		CompletionStatusHandler csh = new CompletionStatusHandler();
		String[] args = {status};
		csh.status = args;
		new Thread(csh).start();
	}

	/*
	 * CompletionStatusHandler class
	 *
	 * makes the call to the UI with the status reported by the engine postTransfer event
	 */
	static class CompletionStatusHandler implements Runnable
	{
		public String[] status;

		public void run()
		{
			if (onLogfileEventHandler != null && onLogfileEventHandler.length() > 0)
			{
				if (logFileExists(localLogfile))
				{
					String[]logfileArr = {localLogfile};
					win.call(onLogfileEventHandler, logfileArr);
				}
			}
			if (onCompleteEventHandler != null && onCompleteEventHandler.length() > 0)
			{
				win.call(onCompleteEventHandler, status);
			}
		}
	}

onConnection

/*
	 * connectionStatus method
	 *
	 * called by the transfer class when the engine connectionStatusChange event is fired
	 *
	 * if the onConnection event is subscribed to by the UI a callback is
	 * made on a new thread with the current status
	 */
	public static void connectionStatus(String status, String sessionId)
	{
		if (onConnectionEventHandler != null && onConnectionEventHandler.length() > 0)
		{
			ConnectionStatusHandler csh = new ConnectionStatusHandler();
			String[] args = {status, sessionId};
			csh.status = args;
			new Thread(csh).start();
		}
	}

	/*
	 * ConnectionStatusHandler class
	 *
	 * makes the call to the UI with the status reported by the engine connectionStatusChange event
	 */
	static class ConnectionStatusHandler implements Runnable
	{
		public String[] status;

		public void run()
		{
			win.call(onConnectionEventHandler, status);
		}
	}

onProtocolChange

/*
	 * transportProtocolChange method
	 *
	 * called by the transfer class immediately following the start
	 * of a transfer reporting the transport protocol being used
	 *
	 * if the onProtocolChange event is subscribed to by the UI a callback is
	 * made on a new thread reporting the current protocol
	 */
	public static void transportProtocolChange(String protocol)
	{
		if (onProtocolChangeHandler != null && onProtocolChangeHandler.length() > 0)
		{
			TransportProtocolHandler tph = new TransportProtocolHandler();
			String[] args = {protocol};
			tph.protocol = args;
			new Thread(tph).start();
		}
	}

	/*
	 * TransportProtocolHandler class
	 *
	 * makes the call to the UI with the transport protocol reported
	 * by the engine when a transfer is started
	 */
	static class TransportProtocolHandler implements Runnable
	{
		public String[] protocol;

		public void run()
		{
			win.call(onProtocolChangeHandler, protocol);
		}
	}

onFilesSelected

public void chooseFilesForUI(String isMultiSelect, String isDirectoriesOnly)
	{
.
.
.
if (filename != null) {
	if (debugMode)
	{
		System.out.println("chooseFileForUI calling selectedFiles in parent with args: " + fullPath);
	}

	//return the selected files on a new thread
	//so not to block the UI processing
	ReturnSelectedFilesHandler rh = new ReturnSelectedFilesHandler();

	String[] args = new String[1];
	args[0] = fullPath;

	rh.args = args;
	new Thread(rh).start();
	}
	else
	{
		if (debugMode)
			{
				System.out.println("chooseFileForUI - user canceled file selection");
			}
	}

	/*
	 * ReturnSelectedFilesHandler class
	 *
	 * returns the selected files to the UI
	 * on a separate thread to prevent blocking
	 */
	static class ReturnSelectedFilesHandler implements Runnable
	{
		public String sRet;
		public String[] args;

		public void run()
		{
			//if the UI subscribed the the onFilesSelected event
			//we make the call to the event handler in the UI
			if (onFilesSelectedHandler != null && onFilesSelectedHandler.length() > 0)
			{
				sRet = win.call(onFilesSelectedHandler, args).toString();
			}
		}
	}

onFolderSelected

public void chooseFilesForUI(String isMultiSelect, String isDirectoriesOnly)
	{
if (directoriesOnly)
	{
		//return the selected folder on a new thread
		//so not to block the UI processing
		ReturnSelectedFolderHandler rh = new ReturnSelectedFolderHandler();
		rh.args = args;
		new Thread(rh).start();
	}
	else
	{
		//return the selected files on a new thread
		//so not to block the UI processing
		ReturnSelectedFilesHandler rh = new ReturnSelectedFilesHandler();
		rh.args = args;
		new Thread(rh).start();
	}

	/*
	 * ReturnSelectedFolderHandler class
	 *
	 * returns the selected folder to the UI
	 * on a separate thread to prevent blocking
	 */
	static class ReturnSelectedFolderHandler implements Runnable
	{
		public String sRet;
		public String[] args;

		public void run()
		{
			//if the UI subscribed the the onFolderSelected event
			//we make the call to the event handler in the UI
			if (onFolderSelectedHandler != null && onFolderSelectedHandler.length() > 0)
			{
				sRet = win.call(onFolderSelectedHandler, args).toString();
			}
			//sRet = win.call("folderSelected", args).toString();
		}
	}

Signing the Applet

Operating System security restrictions prevent an unsigned applet from accessing the local file system. To allow an end user to decide to allow an applet access the file system an applet can be signed using the jarsigner java command and a keystore file (which can be created via the keytool java command).

For more information on jar signing, go to: http://introcs.cs.princeton.edu/java/85application/jar/sign.html

HTML Implementation

The HTML/JavaScript implementation takes advantage of the Java applet we built in the previous section to provide a completely customizable user interface.

Embedding the Applet

Our sample applet is embedded like any other Java applet with the addition of being able to specify several callback functions and an external configuration file. 

Embedding the applet is broken in to two main parts: an <embed> element for browsers like Firefox and Safari, and the <object> tag for Internet Explorer.  In each case, the callback and configuration file settings need to be made, however, they are done slightly differently.  Let's look at the <embed> first:

<comment>
<embed type="application/x-java-applet;version=1.5" id="CTEApplet"
width="1" height="1" pluginspage="https://java.sun.com/j2se/1.5.0/download.html"
java_code="CTEApplet.class"
java_archive="CTEAppletSigned.jar,webclient.jar" onCancel="onCancelHandler"
onComplete="onCompleteHandler" onLogfile="onLogfileHandler" onConnection="onConnectionHandler"
onPause="onPauseHandler"
onResume="onResumeHandler" onProgress="onProgressHandler" onStatus="onStatusHandler"
onProtocolChange="onProtocolChangeHandler" onFilesSelected="onFilesSelectedHandler"
onFolderSelected="onFolderSelectedHandler" configFile="CTE_Config.txt"
autostart="-1"
MAYSCRIPT
scriptable>
</embed>
</comment>

The embed element is placed between a <comment> tag so that it is ignored by Internet Explorer. The <object> tag, while a different syntax, is:

<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
id="CTEApplet" name="CTEApplet" width="1" height="1"
codebase="https://java.sun.com/update/1.5.0/jinstall-1_5_0_03-windowsi586. cab#Version=1,5,0,0">
<param name="code" value="CTEApplet.class" >
<param name="java_code" value="CTEApplet.class">
<param name="java_archive" value="CTEAppletSigned.jar,webclient.jar">
<param name="onCancel" value="onCancelHandler">
<param name="onComplete" value="onCompleteHandler">
<param name="onLogfile" value="onLogfileHandler">
<param name="onConnection" value="onConnectionHandler">
<param name="onPause" value="onPauseHandler">
<param name="onResume" value="onResumeHandler">
<param name="onProgress" value="onProgressHandler">
<param name="onStatus" value="onStatusHandler">
<param name="onProtocolChange" value="onProtocolChangeHandler">
<param name="onFilesSelected" value="onFilesSelectedHandler">
<param name="onFolderSelected" value="onFolderSelectedHandler">
<param name="configFile" value="CTE_Config.txt">
<param name="type" value="application/x-java-applet;version=1.5">
<param name="MAYSCRIPT">
<param name="scriptable">
</object>

Setting Up Callbacks

Both the <embed> and <object> tags above specify callback methods for the applet to call when certain "events" occur.  For example, when a transfer completes, the onComplete event fires, and the specified JavaScript function will be invoked.  In our sample, we named each of our JavaScript functions as handlers for the specific event.  For example, onCompleteHandler.

 The syntax for setting up the callbacks is different for the <embed> and <object> tags.  For the <embed>, the callbacks are defined as name/value pairs as attributes of the <embed> element itself: 

onComplete="onCompleteHandler"

The <object> outer-element has the <param> inner-element to define the callbacks:

<param name="onComplete" value="onCompleteHandler">

As part of the <embed> and <object> elements above, we are merely specifying which JavaScript functions we want called when specific events occur - we have not created the JavaScript functions themselves.  See JavaScript below for details.

Note:  Not every callback function needs to be specified, if it is not necessary for your specific implementation.  For example, if you do not need to let end-users see the log file that is generated, then you do not need to specify the onLogfile callback function.

Specifying an External Configuration File

One of the features of the applet that we built above is that it can load an external configuration file to specify all of the CTE settings.  While not secure (all the values are stored in plain text), it is a convenient way to try different settings without having to recompile the applet or edit the HTML file. 

Just as with the callbacks, the <embed> and <object> elements specify the configuration file differently.  For the <embed>, it is another name/value pair attribute:

configFile="CTE_Config.txt"

For the <object> element, it is specified as follows:

<param name="configFile" value="CTE_Config.txt">

Initialization

In our sample HTML we have an initialization function that is invoked when the HTML page loads.  This is specified as follows: 

<body onload="initialize()">

This is useful to set up any initial display conditions or initializing variables.  In addition, we used it to show that you can set any of the CTE configuration settings in JavaScript as well (not just via the external configuration file). 

Here's our initialize function:

function initialize() {
	document.transferForm.rate.value='0';
	document.transferForm.message.value='Preparing Transfer';
	document.transferForm.protocol.value='';

	//transfer control property setters/getters that can be controlled by the client:

	//The following two lines allow you to set/get the list of agents
    //that are used as the endpoint for a transfer
	//document.getElementById("CTEApplet").setAgentList(agentList);
	//agentList=document.getElementById("CTEApplet").getAgentList();

	//The following two lines allow you to set/get the agent username
	//document.getElementById("CTEApplet").setAgentUser(userName);
	//userName=getAgentUser();

	//The following two lines allow you to set/get the agent password
	//document.getElementById("CTEApplet").setAgentPwd(userPwd);
	//userPwd=getAgentPwd();

	//The following two lines allow you to set/get the source directory.
    //We do not use this setting in this implementation.
    //Instead we pass in the full path to every file.
	//document.getElementById("CTEApplet").setAgentSourceDir(directory);
	//directory=getAgentSourceDir()

	//The following two lines allow you to set/get the destination directory
    //for an upload or download
	//document.getElementById("CTEApplet").setAgentDestDir(destDir);
	//destDir=getAgentDestDir();

	//The following two lines allow you to set/get the certificate string.
    //This is required to set a secure connection with the agent.
    //The certificate is obtainable via the Manager.
	//document.getElementById("CTEApplet").setAgentCert(agentCert);
	//agentCert=getAgentCert();

	//The following two lines allow you to set/get the logging level - defaults to INFO.
    //In this implementation, we set the valus to FINE so that a log is
    //created for all transfers.  Valid values are: OFF, NONE, INFO, FINE, FINER, FINEST
	//document.getElementById("CTEApplet").setAgentLogging(loggingLevel);
	//loggingLevel=getAgentLogging();

	//The following two lines allow you to set/get the transport protocol.
    //Valid values are: NONE, TCP, UDP, UDP_THEN_TCP
	//document.getElementById("CTEApplet").setAgentTransport(transportProtocol);
	//transportPortocol=getAgentTransport();

	//The following two lines allow you to set/get the desired target rate - defaults to 0
	//document.getElementById("CTEApplet").setAgentTargetRate(targetRate);
	//targetRate=getAgentTargetRate();

	//The following two lines allow you to set/get the desired aggressiveness -
    //defaults to 2 (medium).  Valid values are: 1 (low), 2 (medium), 3 (high)
	//document.getElementById("CTEApplet").setAgentAggressiveness(agressiveness);
	//aggressiveness=getAgentAggressiveness();

	//The following two lines allow you to set/get the tracing on the agent side.
    //Use "-trace" to turn tracing on (there is a performance impact).
	//document.getElementById("CTEApplet").setAgentTrace(trace);
	//trace=getAgentTrace();
	return;
	};

Setting Configuration Parameters

In our applet, we created a number of setter/getter functions so that all of configuration settings can be made or viewed in JavaScript. The following is a list of all the setters and getters:

Setters

setAgentList(agentList)
setAgentUser(userName)
setAgentPwd(userPwd)
setAgentSourceDir(directory)
setAgentDestDir(destDir);
setAgentCert(agentCert)
setAgentLogging(loggingLevel)
setAgentTransport(transportProtocol)
setAgentTargetRate(targetRate)
setAgentAggressiveness(agressiveness)
setAgentTrace(trace)

Getters

getAgentList()
getAgentUser()
getAgentPwd()
getAgentSourceDir()
getAgentDestDir();
getAgentCert()
getAgentLogging()
getAgentTransport()
getAgentTargetRate()
getAgentAggressiveness()
getAgentTrace()

For example, to reference getAgentTransport, use the following syntax: 

document.getElementById("CTEApplet").getAgentCert()

We reference the applet itself using: document.getElementById("CTEApplet") 

where "CTEApplet" is the ID value we set in the <embed> and <object> elements.  This is followed by the name of the setter/getter function.

Display/User Interaction Elements

There are a number of display elements in our HTML example that allow us to showcase the most common use cases for the CTE: upload and download of files.  It is important to note that these display elements could be anything that works for your particular implementation.  For the most part we used standard HTML form elements for sample purposes, but they could have just as easily been custom image buttons or other more graphical elements. 

To highlight the various components, we have divided our user interface in to three distinct areas using <div> tags.  They are:

  • Upload

  • Download

  • Transfer

Divs

<div> elements allow us to group various components and hide/show them as necessary.  In our sample, we have the following three <div> sections:

Upload

The upload div has all the components for selecting files to upload to the remote Signiant Agent.  In our sample, we take advantage of the two file chooser user interfaces we provide in the Java applet: AWT and Swing.  If the user selects "Single File" the "Browse" button will call the AWT file chooser.  Selecting "Multi File" and clicking the "Browse" button will display the Swing file chooser and allow the user to select multiple files for upload.

<div id="upload">
	<form name="uploadForm">
		<label>File Selection Type:</label><input name="fileSelect"
         id="radio1" type="radio" value="single" checked>
         <span style="vertical-align:middle;">Single File </span></input>
        <input name="fileSelect" type="radio" value="multi">
        <span style="vertical-align:middle;">Multi File </span></input>
        <input name="browse" type="button" value="Browse"
         onclick="fileBrowser('file')" /><br  />
		<label>File(s) Selected:</label><textarea name="filesSelected"
         cols="100" readonly="readonly"></textarea>
	</form>
	</div>

Download

The download div allows the user to select a local directory as the target to download files to, as well as type the name of the remote file to download.  In this case, the Swing file chooser is set to only allow the user to select a directory.

<div id="download">
	<form name="downloadForm">
		<label>File to Download:</label><input name="downloadFile"
         type="text" size="100" /> <br />
		<label>Target Directory:</label><input name="trgtDir" type="text"
         size="100" />
         <input name="browse" type="button" value="Browse" onclick="fileBrowser('dir')" />
	</form>
	</div>

Transfer

The transfer div contains all of the information components for monitoring a transfer in progress.  This includes displaying any messages returned by the CTE, dynamically displaying the current transfer rate being achieved, and a progress bar to show the percent of the file uploaded/downloaded.

<div id="transfer">
	<form name="transferForm">
		<label>Transfer Protocol:</label><input name="protocol"
         type="text" value="UDP" size="10" readonly="readonly" /> <br  />
		<label>Last Message:</label><textarea name="message" cols="100"
         readonly="readonly">Transfer initiated</textarea> <br  />
        <label>Transfer Rate:</label><input name="rate" type="text" value="5 MBps"
         readonly="readonly" /><br />
		<label>Transfer Status:</label><span class="progressBar"
         id="transferProgress">0</span><br />
		<p style="left:170px; position:relative"><input name="interrupt"
         type="button" value="Pause" onclick="interruptTransfer()" />
        <input name="cancel" type="button" value="Cancel" onclick="cancelTransfer()" />
        <input name="viewLog" type="button" value="View Log" onclick="viewLogfile()"
         disabled="disabled" /></p>
    </form>
</div>

Dropdown List

A dropdown list at the top of the page allows the user to choose whether an upload or download will be performed. Besides showing and hiding the respective div, the selection will also be passed to the CTE. The HTML is as follows:

<p>
<label>Transfer Type:</label>
<select name="TransType" size="1" onchange="showHide(this[this.selectedIndex].value)">
  <option value="upload">Upload</option>
  <option value="download">Download</option>
</select>
</p>

Buttons

Standard form buttons are used for users to initiate actions within the user interface. The following buttons are defined in our sample:

Browse

We have two different browse buttons defined in our user interface.  One to select file(s) and the other to select a directory.  However, the same JavaScript function is called in both cases by simply passing in "file" or "dir" respectively.  The HTML for the Browse buttons is:

<input name="browse" type="button" value="Browse" onclick="fileBrowser('file')" />

and

<input name="browse" type="button" value="Browse" onclick="fileBrowser('dir')" />

Start Transfer

The start transfer button is used to actually kick off the CTE transfer.  The HTML for the start transfer button is:

<input name="startTrans" type="button" value="Start Transfer" onclick="startTrans()" />

Pause/Resume Transfer

The pause/resume button is only available while a transfer is in progress.  The same button is repurposed to resume a paused transfer.  The HTML for the pause/resume button is:

<input name="interrupt" type="button" value="Pause" onclick="interruptTransfer()" />

Cancel Transfer

The cancel button is only available while a transfer is in progress.  The HTML for the cancel button is:
<input name="cancel" type="button" value="Cancel" onclick="cancelTransfer()" />

View Log

The view log button is only enabled when the onLogfile event is fired and a log file has been created.  The HTML for the view log button is:

<input name="viewLog" type="button" value="View Log" onclick="viewLogfile()"
disabled="disabled" />

Progress Bar

For our sample, we used a JavaScript progress bar from Bram Van Damme.  There are two .js files that are required for this progress bar as follows:

<script type="text/javascript" src="js/prototype.js"></script>
            <script type="text/javascript" src="js/jsProgressBarHandler.js"></script>

The progress bar is then declared as follows:

<span class="progressBar" id="transferProgress">0</span>
For more information on the progress bar and documentation, go to: http://www.bram.us/projects/js_bramus/jsprogressbarhandler/

Text Fields

We chose to use standard form input and textareas to display file selection choices and status messages. There are several different text fields in our user interface, but the following two types are representative:

<input name="rate" type="text" value="" readonly="readonly" />

and

<textarea name="filesSelected" cols="100" readonly="readonly">
</textarea>

JavaScript

In our Flex sample, the JavaScript acts as a go-between for the Flash movie and the Java applet. Most of the logic, therefore, is contained in the ActionScript not in the JavaScript. The following shows each of the JavaScript pass-through functions called by user actions and the callbacks:

Starting a Transfer

When a user clicks the "Start Transfer" button in the Flash movie, ActionScript code is executed that eventually calls the following JavaScript function:

//Called when the "Start Transfer" button is clicked.  Initiates the actual transfer.
function startTrans(transferType,files,targetDir) {
	if (transferType == 'Upload') {
		//Call to upload files
		var bSuccess = document.getElementById("CTEApplet").uploadFiles(files);
	} else if (transferType == "Download") {
		//Call to download files
		var bSuccess = document.getElementById("CTEApplet").downloadFiles(files, targetDir);
	}
	return;
};

The same JavaScript function is called for both uploads and downloads, but different methods of the applet are called depending on the value of the transferType parameter that is passed in from the ActionScript. Both applet calls return a Boolean value that we store in the variable "bSuccess". For uploads, the call is:

var bSuccess = document.getElementById("CTEApplet").uploadFiles(files);

The variable "files" that is passed as a parameter of uploadFiles is passed in from the ActionScript. The code for this is described below in the ActionScript section.

For downloads, the call is:

var bSuccess = document.getElementById("CTEApplet").downloadFiles(files, targetDir);

The "files" and "targetDir" parameters that are passed to downloadFiles are passed in from the ActionScript. The code for this is described below in the ActionScript section.

Selecting Files/Directories

There are two parts to selecting files and directories in our sample. The first is when the user clicks on one of the "Browse" buttons, and the second is when the user finishes selecting the file(s) or directory in the file chooser. There are two separate JavaScript functions to handle these two events. When the user clicks on a "Browse" button in the Flash movie, ActionScript code is executed that eventually calls the following JavaScript function:

function fileBrowser(multiFile,directory) {
    alert('browse clicked');
	document.getElementById("CTEApplet").chooseFilesForUI(multiFile, directory);
	return;
};

Regardless of whether a single file, multifile or directory is being requested, the same applet method "chooseFilesForUI" is called. Based on the two Boolean parameters that are passed in, the appropriate file chooser is displayed with the appropriate configuration. The first Boolean determines whether it is a single file or multi-file. Pass "false" for single file or "true" for multi file. The second Boolean is "false" if a file is being selected or "true" if only a directory can be selected. In this case, the "multiFile" and "directory" parameters are passed in to the JavaScript from the ActionScript code.

There are two different callback functions that are called depending on whether the user selected files or selected a directory.  Once a user has interacted with the file chooser and made their selection, the applet fires the "onFilesSelected" or "onFolderSelected" event.  As you recall from our applet <embed> tag above, we set the callback for that event to be "onFilesSelectedHandler" and "onFolderSelectedHandler" respectively.  The onFilesSelectedHandler is as follows:

function onFilesSelectedHandler(sFiles) {
	var files=sFiles + "";
	//a limitation of Javascript prevents referencing
	//the passed arg directly
	thisMovie("Main").onFilesSelectedHandler(files);
	//Calls the onFilesSelectedHandler in Flash movie
	return;
};

The applet will pass back the file(s) selected in the sFiles parameter. If more than one file was selected, each file will be delimited by a "||". As stated earlier, the JavaScript merely acts as a go-between, and, in this case, calls an ActionScript function and passes up the parameters from the applet. The onFolderSelectedHandler function is as follows:

function onFolderSelectedHandler(sFolder) {
	var folder = sFolder + "";
	//a limitation of javascript prevents referencing the
	//passed arg directly in split or indexOf functions
	thisMovie("Main").onFolderSelectedHandler(folder);
	//Calls the onFolderSelectedHandler in Flash movie
	return;
};

The full path of the directory selected is returned in the sFolder parameter, and forwards it on to the ActionScript method.

Interrupting/Resuming a Transfer

There are two parts to pausing and resuming a transfer. The first is when the user clicks on the "Pause/Resume" button in the Flash movie, and the second is when the applet acknowledges that the pause/resume has occurred. There are two separate JavaScript functions to handle these two events. When the user clicks on the "Pause/Resume" button in the Flash movie, ActionScript code is executed that eventually calls the following JavaScript function:

function interruptTransfer(interruptType) {
	if (interruptType == 'Pause') {
		document.getElementById("CTEApplet").pauseTransfer();
	//Pauses the transfer
	} else {
		document.getElementById("CTEApplet").resumeTransfer();
	//Resumes the transfer
	}
	return;
};

The "interruptType" is passed in from the ActionScript, and either "pauseTransfer()" or "resumeTransfer()" is called, which in turn calls an ActionScript function. When the applet acknowledges that the CTE has paused or resumed a transfer, the appropriate callback is made (as set in the embed/object tag). For pause, the "onPauseHandler" is as follows:

function onPauseHandler() {
	thisMovie("Main").onPauseHandler();
	//Calls the onPauseHandler in Flash movie
	return;
};

For resume, the "onResumeHandler" is as follows:

function onResumeHandler() {
	thisMovie("Main").onResumeHandler(files);
	//Calls the onResumeHandler in Flash movie
	return;
};

The CTE will also fire the "onComplete" event when a transfer is paused giving a false indication that the transfer has completed successfully.

Canceling a Transfer

There are two parts to canceling a transfer. The first is when the user clicks on the "Cancel" button in the Flash movie, and the second is when the applet acknowledges that the cancel has occurred. There are two separate JavaScript functions to handle these two events. When the user clicks on the "Cancel" button in the Flash movie, ActionScript code is executed that eventually calls the following JavaScript function:

function cancelTransfer() {
	document.getElementById("CTEApplet").cancelTransfer();
	return;
};

When the applet acknowledges the transfer cancellation request, the "onCancelHandler" callback is made (as set in the embed/object tag) which in turn calls an ActionScript function:

function onCancelHandler() {
	thisMovie("Main").onCancelHandler();
	//Calls the onCancelHandler in Flash movie
	return;
};

The CTE engine.isCancelled() method proved unreliable to determine if the cancellation actually occurred. The CTE will also fire the "onComplete" event when a transfer is canceled.

View Log

The "View Log" button is disabled until the "onLogFile" event is fired. This event is only fired when a log file has been created by the CTE. Typically, a log file will only be created if an error occurs with the transfer or if the logging parameter is set to a level of FINE, FINER or FINEST. The JavaScript function, onLogFileHandler, is as follows:

function onLogfileHandler(sLogfilePath) {
	var logfilePath=sLogfilePath+"";
	thisMovie("Main").onLogfileHandler(logfilePath);
	//Calls the onLogfileHandler in Flash movie
	return;
};

Once the "View Log" button has been enabled in the Flash movie, if the user clicks on it, ActionScript code is executed that eventually calls the following JavaScript function:

function viewLogfile(logfilePath) {
	alert('The log file is located at: '+logfilePath);
	//Displays the full path of the logfile
	window.open(logfilePath,"Log File");
	//May not work in all browsers, but it is meant to open a
	//new browser window with the contents of the log file.
	return;
};

In addition to displaying the full path to the log file in an alert message, the function attempts to open the log file in a separate browser window. The "logfilePath" parameter is passed in from the ActionScript. Not all browsers support opening of the log file in a browser window. In those cases, only the alert message will be displayed.

Transfer Callbacks

As the CTE is transferring, the applet will make various callbacks to return status, progress, completion, connections, and protocol changes.  Each of these callbacks is specified in the <embed> and <object> elements described above.

onProgressHandler

The "onProgress" event is fired throughout the course of a transfer. In our sample, the JavaScript function "onProgressHandler" is the callback made by the applet. The JavaScript is as follows:

function onProgressHandler(sPercentComplete, sTransferRate) {
	var percentComplete=sPercentComplete+"";
	var transferRate=sTransferRate+"";
	thisMovie("Main").onLogfileHandler(percentComplete,transferRate);
	//Calls the onLogfileHandler in Flash movie
	return;
};

This function has two parameters sent from the applet: percentComplete and transferRate. Both of these parameters are passed to the ActionScript function.

onStatusHandler

The "onStatus" event is fired when the status of the transfer changes. In our sample, the JavaScript function "onStatusHandler" is the callback made by the applet. The JavaScript is as follows:

function onStatusHandler(sStatus) {
	var status = sStatus + "";
	thisMovie("Main").onStatusHandler(status);
	//Calls the onLogfileHandler in Flash movie
	return;
};

This function has a single parameter, status, which is passed to the ActionScript function.

onCompleteHandler

The "onComplete" event is fired when the CTE considers the transfer to be completed. In our sample, the JavaScript function "onCompleteHandler" is the callback made by the applet. The JavaScript is as follows:

function onCompleteHandler(sStatus) {
	var status=sStatus+"";
	thisMovie("Main").onCompleteHandler(status);
	//Calls the onCompleteHandler in Flash movie
	return;
};

This function has a single parameter, status, which is passed to the ActionScript function.

Important: The "onComplete" event is fired when a transfer is paused or canceled as well as when it has completed successfully.

onConnectionHandler

The "onConnection" event is fired when the CTE negotiates an initial connection with the remote Agent.  In our sample, the JavaScript function "onConnectionHandler" is the callback made by the applet.  The JavaScript is as follows:

function onConnectionHandler(sStatus, sSessionId) {
	var status=sStatus+"";
	var sessionID=sSessionID+"";
	thisMovie("Main").onLogfileHandler(status,sessionID);
	//Calls the onConnectionHandler in Flash movie
	return;
};

This function has two parameters: "status" and "sessionId".  Both of these parameters are passed to the ActionScript function, however, SessionId is not used in this sample. While not used in this sample, every transfer is associated with a unique session ID that could be used for monitoring and correlation.

onProtocolChangeHandler

The "onProtocolChange" event is fired when the CTE changes the protocol used for the transfer.  For example, if the CTE needs to use TCP instead of UDP.  In our sample, the JavaScript function "onProtocolChangeHandler" is the callback made by the applet.  The JavaScript is as follows:

function onProtocolChangeHandler(sProtocol) {
	var protocol = sProtocol + "";
	thisMovie("Main").onProtocolChangeHandler(protocol);
   //Calls the onLogfileHandler in Flash movie
	return;
	};

This function has a single parameter, protocol, is passed to the ActionScript function.


ActionScript

In our Flex sample, all of the real logic is in the ActionScript rather than the JavaScript which merely acts as a pass-through. As shown above, each of the JavaScript functions has a corresponding ActionScript function. For clarity, we have named those functions the same.

Starting a Transfer

The following ActionScript code is executed when the user clicks on the "Start Transfer" button in the Flash movie:

protected function startTransfer_clickHandler(event:MouseEvent):void
{
	//Create the listeners for the Javascript callbacks that are fired after a transfer has been initiated
	ExternalInterface.addCallback("onPauseHandler",onPauseHandler);
	ExternalInterface.addCallback("onResumeHandler",onResumeHandler);
	ExternalInterface.addCallback("onCancelHandler",onCancelHandler);
	ExternalInterface.addCallback("onCompleteHandler",onCompleteHandler);
	ExternalInterface.addCallback("onLogfileHandler",onLogfileHandler);
	ExternalInterface.addCallback("onConnectionHandler",onConnectionHandler);
	ExternalInterface.addCallback("onProgressHandler",onProgressHandler);
	ExternalInterface.addCallback("onStatusHandler",onStatusHandler);
	ExternalInterface.addCallback("onProtocolChangeHandler",onProtocolChangeHandler);

	transfer.y=startTransfer.y+25;
     //move the transfer container under the Start Transfer button
	transfer.visible=true;
      //display the transfer container and all its fields
	if (transferType.selectedItem=="Download"){
		var pattern:RegExp = /\\/g;
         //create a regular expression to globally replace the backslashes
		gselectedFiles=fileToDownload.text;
         //if a download, set the value of the global variable to be the file to download
		gselectedFiles=gselectedFiles.replace(pattern,"/");
         //Replace backslashes with forward-slashes as the backslashes get stripped when passed to Javascript
				}
	ExternalInterface.call("startTrans",transferType.selectedItem,gselectedFiles,targetDir.text);
   //call the startTrans Javascript function passing in the type of transfer, files to transfer, and the target directory for downloads
}

The first thing this function does is to setup callbacks for JavaScript for all of the callbacks from the applet. After unhiding the "transfer" Border Container, the corresponding JavaScript function, startTrans, is called to ultimately call the applet function to initiate the transfer.

Selecting Files/Directories

There are two parts to selecting files and directories in our sample. The first is when the user clicks on one of the "Browse" buttons, and the second is when the user finishes selecting the file(s) or directory in the file chooser. There are two separate ActionScript functions to handle these two events. When the user clicks on a "Browse" button in the Flash movie, the following ActionScript function is executed:

protected function browseButton_clickHandler(typeBrowse:String):void
			{
//Create the listeners for the Javascript callbacks that are fired after a file or folder is selected
ExternalInterface.addCallback("onFilesSelectedHandler",onFilesSelectedHandler);
	ExternalInterface.addCallback("onFolderSelectedHandler",onFolderSelectedHandler);

	//Check the type of browsing we're doing
	if (typeBrowse == "file")
	{
		if (fileSelection.selectedValue == "Single File")
         //check the value of the radio button
		{
			ExternalInterface.call("fileBrowser","false","false");
            //call the fileBrowser Javascript function passing in false for
            //multiple files and false for a directory
		} else {
			ExternalInterface.call("fileBrowser","true","false");
            //call the fileBrowser Javascript function passing in true for
            //multiple files and false for a directory
		}
	} else {
		ExternalInterface.call("fileBrowser","false","true");
         //call the fileBrowser Javascript function passing in false
         //for multiple files and true for a directory
	}
	}

Regardless of whether a single file, multifile or directory is being requested, the same JavaScript function "fileBrowser" is called. The two Boolean values are passed in so that the appropriate file chooser is displayed. See the fileBrowser JavaScript function above for more details. There are two different callback functions that are called depending on whether the user selected files or selected a directory. These are described in the JavaScript section above. The onFilesSelectedHandler is as follows:

public function onFilesSelectedHandler(sFiles:String):void
{
	transfer.visible=false; //reset the transfer container to invisible
	gselectedFiles=sFiles; //assign the returned list of files to the global variable
	var pattern:RegExp = /\\/g; //create a regular expression to globally replace the backslashes
	gselectedFiles=gselectedFiles.replace(pattern,"/"); //Replace backslashes with forward-slashes as the backslashes get stripped when passed to Javascript

	if (sFiles.indexOf('||') == -1) //Files are '||' delimeted.
    If no || is found, then only a single file was selected
	{
		filesSelected.text=sFiles;
         //Sets the text in the "File(s) Selected" form field
		filesSelected.heightInLines=1;
         //Sets the number of rows to 1 for display purposes
	}
	else
	{
		var splitStr:Array = sFiles.split("||");
         //Splits on the || to get the individual files selected
		var fileStr:String = "";
		var i:int;
		for (i = 0; i < splitStr.length; i++) //loops through all the files selected
		{
			fileStr += splitStr[i];
			if (i < splitStr.length - 1)
			{
				fileStr += "\n"; //add a linefeed between each file
			}
		}
		filesSelected.text=fileStr; //Sets the text in the "File(s) Selected" form field
		filesSelected.heightInLines=splitStr.length;
         //Sets the number of rows to the number of files for display purposes

	}
	filesSelected.validateSize(true);
   //forces Flash to update the height property.
   //Without this call, the height is not changed after setting the heightInLines property.

	upload.height=filesSelected.getExplicitOrMeasuredHeight()+filesSelected.y+10;
      //change the overall height of the upload container

	startTransfer.top=upload.y+upload.getExplicitOrMeasuredHeight()+5;
      //move the Start Transfer button to be under the upload container
	return;
	}

The JavaScript will pass back the file(s) selected in the sFiles parameter. If more than one file was selected, each file will be delimited by a "||". As stated earlier, the JavaScript merely acts as a go-between, and it passes up the parameter from the applet. The onFolderSelectedHandler function is as follows:

public function onFolderSelectedHandler(sFolder:String):void
{
	targetDir.text=sFolder;
	return;
}

The full path of the directory selected is passed in from the JavaScript and the Text Input component, targetDir, is updated with the value.

Interrupting/Resuming a Transfer

There are two parts to pausing and resuming a transfer. The first is when the user clicks on the "Pause/Resume" button in the Flash movie, and the second is when the applet acknowledges that the pause/resume has occurred. There are two separate ActionScript functions to handle these two events. When the user clicks on the "Pause/Resume" button in the Flash movie, the following ActionScript function is called:

protected function interrupt_clickHandler(event:MouseEvent):void
{
	if (interrupt.label == "Pause")
	{
		interrupt.label="Resume";
		ExternalInterface.call("interruptTransfer","Pause");
         //call the interruptTransfer Javascript function to pause the transfer
	} else {
		interrupt.label="Pause";
		ExternalInterface.call("interruptTransfer","Resume");
         //call the interruptTransfer Javascript function to resume the transfer
	}
	}

The ActionScript determines the current button state, and calls the corresponding JavaScript function passing in the state.  The JavaScript then calls a method in the applet, which, in turn, makes a callback to a separate JavaScript function (see Javascript above).  For pause, the JavaScript calls the following ActionScript function:

public function onPauseHandler():void
{
	message.text="Transfer Paused.";
     //updates the last message field
	return;
	}

For resume, the JavaScript call the following ActionScript function:

public function onResumeHandler():void
{
	message.text="Transfer Resumed.";
   //updates the last message field
	return;
}

Canceling a Transfer

There are two parts to canceling a transfer. The first is when the user clicks Cancel in the Flash movie, and the second is when the applet acknowledges that the cancel has occurred. There are two separate ActionScript functions to handle these two events. When the user clicks Cancel in the Flash movie, the following ActionScript function is called:

protected function cancel_clickHandler(event:MouseEvent):void
{
	ExternalInterface.call("cancelTransfer","");
   //call the cancelTransfer Javascript function
   }

The ActionScript calls the corresponding JavaScript function "cancelTransfer". cancelTransfer calls a method in the applet to actually cancel the transfer. When the transfer is canceled, the applet calls back to a JavaScript function, which, in turn, call the following ActionScript function:

public function onCancelHandler():void
{
	message.text="Transfer Canceled.";
   //updates the last message field
	return;
}

View Log

The View Log button is disabled until the "onLogFile" event is fired. This event is only fired when a log file has been created by the CTE. Typically, a log file will only be created if an error occurs with the transfer or if the logging parameter is set to a level of FINE, FINER or FINEST. The JavaScript function, onLogFileHandler, described above calls the following ActionScript function:

public function onLogfileHandler(logfilePath:String):void
{
	glogfilePath=logfilePath;
   //assigns the passed in logfilePath to the global variable
	viewLog.enabled=true;
   //enables the view log button
	return;
	}

Once the View Log button has been enabled in the Flash movie, if the user clicks on it, the following ActionScript function is called:

protected function viewLog_clickHandler(event:MouseEvent):void
{
	ExternalInterface.call("viewLogfile",glogfilePath);
   //call the viewLogfile Javascript function passing in the global
   //variable containing the logfile path
}

This function then forwards the click by calling the JavaScript function, viewLogfile, described above passing in the path to the logfilePath.

Transfer Callbacks

As described above, while the CTE is transferring, the applet will make various callbacks to return status, progress, completion, connections, and protocol changes.  Each of these callbacks is specified in the <embed> and <object> elements described above.  These callbacks are made to JavaScript functions which, in turn, call the following ActionScript functions.

onProgressHandler

The "onProgress" event is fired throughout the course of a transfer. The ActionScript function is as follows:

public function onProgressHandler(percentComplete:String, transferRate:String):void
{
	transferRateField.text=transferRate;
   //upadates the transfer rate field with the passed in transfer rate value
	progressBar.setProgress(parseInt(percentComplete),100);
   //updates the progress bar
	progressBar.label=percentComplete+"%";
	return;
}

The two passed in parameters are: percentComplete and transferRate. The transferRateField is update with the value of transferRate. The percentComplete string is first converted to an integer and used to set the progress of the progressBar.

onStatusHandler

The "onStatus" event is fired when the status of the transfer changes. The ActionScript function is as follows:

public function onStatusHandler(status:String):void
{
	message.text=status;
	return;
}

This function has a single parameter, status, which is mapped directly to the value of the text field "message".

onCompleteHandler

The "onComplete" event is fired when the CTE considers the transfer to be completed. The ActionScript function is as follows:

public function onCompleteHandler(status:String):void
{
	message.text="Transfer Complete - status = "+status;
	return;
}

This function has a single parameter, status, which is mapped directly to the value of the text field "message".

onConnectionHandler

The "onConnection" event is fired when the CTE negotiates an initial connection with the remote Agent. The ActionScript function is as follows:

public function onConnectionHandler(status:String, sessionID:String):void
{
	message.text=status;
	return;
}

This function has two parameters: "status" and "sessionId". Status is mapped directly to the value of the text field "message". While not used in this sample, every transfer is associated with a unique session ID that could be used for monitoring and correlation.

onProtocolChangeHandler

The "onProtocolChange" event is fired when the CTE changes the protocol used for the transfer. For example, if the CTE needs to use TCP instead of UDP. The ActionScript function is as follows:

public function onProtocolChangeHandler(protocol:String):void
{
	protocolField.text=protocol;
	return;
}

This function has a single parameter, protocol, is mapped directly to the value of the text field protocolField.


Flex Implementation

The Flex implementation takes advantage of both the Java applet and the HTML/JavaScript we built in the prior sections to provide a completely customizable user interface.

While the Flex implementation is built on the HTML/JavaScript implementation, it is not an exact duplication.  As such, this section is written to be completely independent of the HTML/JavaScript chapter.

Embedding the Applet

Our sample applet is embedded like any other Java applet with the addition of being able to specify several callback functions and an external configuration file. 

Embedding the applet is broken in to two main parts: an <embed> element for browsers like Firefox and Safari, and the <object> tag for Internet Explorer.  In each case, the callback and configuration file settings need to be made, however, they are done slightly differently.  Let's look at the <embed> first:

<comment>
<embed type="application/x-java-applet;version=1.5" id="CTEApplet"
width="1" height="1" pluginspage="https://java.sun.com/j2se/1.5.0/download.html"
java_code="CTEApplet.class"
java_archive="CTEAppletSigned.jar,webclient.jar" onCancel="onCancelHandler"
onComplete="onCompleteHandler" onLogfile="onLogfileHandler" onConnection="onConnectionHandler"
onPause="onPauseHandler"
onResume="onResumeHandler" onProgress="onProgressHandler" onStatus="onStatusHandler"
onProtocolChange="onProtocolChangeHandler" onFilesSelected="onFilesSelectedHandler"
onFolderSelected="onFolderSelectedHandler" configFile="CTE_Config.txt"
autostart="-1"
MAYSCRIPT
scriptable>
</embed>
</comment>

The embed element is placed between a <comment> tag so that it is ignored by Internet Explorer. The <object> tag, while a different syntax, is:

<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
id="CTEApplet" name="CTEApplet" width="1" height="1"
codebase="https://java.sun.com/update/1.5.0/jinstall-1_5_0_03-windowsi586. cab#Version=1,5,0,0">
<param name="code" value="CTEApplet.class" >
<param name="java_code" value="CTEApplet.class">
<param name="java_archive" value="CTEAppletSigned.jar,webclient.jar">
<param name="onCancel" value="onCancelHandler">
<param name="onComplete" value="onCompleteHandler">
<param name="onLogfile" value="onLogfileHandler">
<param name="onConnection" value="onConnectionHandler">
<param name="onPause" value="onPauseHandler">
<param name="onResume" value="onResumeHandler">
<param name="onProgress" value="onProgressHandler">
<param name="onStatus" value="onStatusHandler">
<param name="onProtocolChange" value="onProtocolChangeHandler">
<param name="onFilesSelected" value="onFilesSelectedHandler">
<param name="onFolderSelected" value="onFolderSelectedHandler">
<param name="configFile" value="CTE_Config.txt">
<param name="type" value="application/x-java-applet;version=1.5">
<param name="MAYSCRIPT">
<param name="scriptable">
</object>

Setting Up Callbacks

Both the <embed> and <object> tags above specify callback methods for the applet to call when certain "events" occur.  For example, when a transfer completes, the onComplete event fires, and the specified JavaScript function will be invoked.  In our sample, we named each of our JavaScript functions as handlers for the specific event.  For example, onCompleteHandler.

 The syntax for setting up the callbacks is different for the <embed> and <object> tags.  For the <embed>, the callbacks are defined as name/value pairs as attributes of the <embed> element itself: 

onComplete="onCompleteHandler"

The <object> outer-element has the <param> inner-element to define the callbacks:

<param name="onComplete" value="onCompleteHandler">

As part of the <embed> and <object> elements above, we are merely specifying which JavaScript functions we want called when specific events occur - we have not created the JavaScript functions themselves.  See JavaScript below for details.

Note:  Not every callback function needs to be specified, if it is not necessary for your specific implementation.  For example, if you do not need to let end-users see the log file that is generated, then you do not need to specify the onLogfile callback function.

Specifying an External Configuration File

One of the features of the applet that we built above is that it can load an external configuration file to specify all of the CTE settings.  While not secure (all the values are stored in plain text), it is a convenient way to try different settings without having to recompile the applet or edit the HTML file. 

Just as with the callbacks, the <embed> and <object> elements specify the configuration file differently.  For the <embed>, it is another name/value pair attribute:

configFile="CTE_Config.txt"

For the <object> element, it is specified as follows:

<param name="configFile" value="CTE_Config.txt">

Embedding the Flash Movie

Embedding a Flash movie within an HTML page can be done in a number of ways. The best practice prescribed by Adobe includes additional functionality than simply an embed such as checking for Flash Player versions and ensuring that the player is installed. In fact, when compiling your application in Flex, it will generate the container HTML file that includes all of that functionality.

In our example, we have simply taken the relevant HTML from that generated file and merged it with our additions. The following is placed in the header:

<script type="text/javascript" src="swfobject.js"></script>
<script type="text/javascript">
    <!-- For version detection, set to min. required Flash Player version, or 0 (or 0.0.0), for no version detection. -->
    var swfVersionStr = "10.0.0";
    <!-- To use express install, set to playerProductInstall.swf, otherwise the empty string. -->
    var xiSwfUrlStr = "playerProductInstall.swf";
    var flashvars = {};
    var params = {};
    params.quality = "high";
    params.bgcolor = "#ffffff";
    params.allowscriptaccess = "sameDomain";
    params.allowfullscreen = "true";
    var attributes = {};
    attributes.id = "Main";
    attributes.name = "Main";
    attributes.align = "middle";
    swfobject.embedSWF(
        "Main.swf", "flashContent",
        "100%", "100%",
        swfVersionStr, xiSwfUrlStr,
        flashvars, params, attributes);
	<!-- JavaScript enabled so display the flashContent div in case it is not replaced with a swf object. -->
	swfobject.createCSS("#flashContent", "display:block;text-align:left;");
</script>

The following JavaScript function is used to get the reference to the Flash movie object:

function thisMovie(movieName) {
     if (navigator.appName.indexOf("Microsoft") != -1) {
         return window[movieName];
     } else {
         return document[movieName];
     }
     return;
 };

The following is placed in the body of the HTML and is what actually embeds the Flash Player in the page:

<!-- embedded Flash Player -->
        <!-- SWFObject's dynamic embed method replaces this alternative HTML content with Flash
            content when enough JavaScript and Flash plug-in support is available.
            The div is initially hidden so that it doesn't show when JavaScript is disabled.
		-->
        <div id="flashContent">
        	<p>
	        	To view this page ensure that Adobe Flash Player version
				10.0.0 or greater is installed.
			</p>
			<script type="text/javascript">
				var pageHost = ((document.location.protocol == "https:") ? "https://" :	"http://");
				document.write("<a href='http://www.adobe.com/go/getflashplayer'>
                <img src='" + pageHost + "www.adobe.com/images/shared/download_buttons/
                  get_flash_player.gif' alt='Get Adobe Flash player' /></a>" );
			</script>
        </div>

       	<noscript>
            <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%"
            height="100%" id="Main">
                <param name="movie" value="Main.swf" />
                <param name="quality" value="high" />
                <param name="bgcolor" value="#ffffff" />
                <param name="allowScriptAccess" value="sameDomain" />
                <param name="allowFullScreen" value="true" />
                <!--[if !IE]>-->
                <object type="application/x-shockwave-flash" data="Main.swf" width="100%" height="100%">
                    <param name="quality" value="high" />
                    <param name="bgcolor" value="#ffffff" />
                    <param name="allowScriptAccess" value="sameDomain" />
                    <param name="allowFullScreen" value="true" />
                <!--<![endif]-->
                <!--[if gte IE 6]>-->
                	<p>
                		Either scripts and active content are not permitted to run or
                        Adobe Flash Player version 10.0.0 or greater is not installed.
                	</p>
                <!--<![endif]-->
                    <a href="http://www.adobe.com/go/getflashplayer">
                        <img src="http://www.adobe.com/images/shared/download_buttons/
                        get_flash_player.gif" alt="Get Adobe Flash Player" />
                    </a>
                <!--[if !IE]>-->
                </object>
                <!--<![endif]-->
            </object>
	    </noscript>

Initialization

In our sample HTML we have an initialization function that is invoked when the HTML page loads.  This is specified as:

<body onload="initialize()">

This is useful to set up any initial display conditions or initializing variables.  In addition, we used it to show that you can set any of the CTE configuration settings in JavaScript as well (not just via the external configuration file). 

Here's our initialize function:

function initialize() {
	document.transferForm.rate.value='0';
	document.transferForm.message.value='Preparing Transfer';
	document.transferForm.protocol.value='';

	//transfer control property setters/getters that can be controlled by the client:

	//The following two lines allow you to set/get the list of agents
    //that are used as the endpoint for a transfer
	//document.getElementById("CTEApplet").setAgentList(agentList);
	//agentList=document.getElementById("CTEApplet").getAgentList();

	//The following two lines allow you to set/get the agent username
	//document.getElementById("CTEApplet").setAgentUser(userName);
	//userName=getAgentUser();

	//The following two lines allow you to set/get the agent password
	//document.getElementById("CTEApplet").setAgentPwd(userPwd);
	//userPwd=getAgentPwd();

	//The following two lines allow you to set/get the source directory.
    //We do not use this setting in this implementation.
    //Instead we pass in the full path to every file.
	//document.getElementById("CTEApplet").setAgentSourceDir(directory);
	//directory=getAgentSourceDir()

	//The following two lines allow you to set/get the destination directory
    //for an upload or download
	//document.getElementById("CTEApplet").setAgentDestDir(destDir);
	//destDir=getAgentDestDir();

	//The following two lines allow you to set/get the certificate string.
    //This is required to set a secure connection with the agent.
    //The certificate is obtainable via the Manager.
	//document.getElementById("CTEApplet").setAgentCert(agentCert);
	//agentCert=getAgentCert();

	//The following two lines allow you to set/get the logging level - defaults to INFO.
    //In this implementation, we set the valus to FINE so that a log is
    //created for all transfers.  Valid values are: OFF, NONE, INFO, FINE, FINER, FINEST
	//document.getElementById("CTEApplet").setAgentLogging(loggingLevel);
	//loggingLevel=getAgentLogging();

	//The following two lines allow you to set/get the transport protocol.
    //Valid values are: NONE, TCP, UDP, UDP_THEN_TCP
	//document.getElementById("CTEApplet").setAgentTransport(transportProtocol);
	//transportPortocol=getAgentTransport();

	//The following two lines allow you to set/get the desired target rate - defaults to 0
	//document.getElementById("CTEApplet").setAgentTargetRate(targetRate);
	//targetRate=getAgentTargetRate();

	//The following two lines allow you to set/get the desired aggressiveness -
    //defaults to 2 (medium).  Valid values are: 1 (low), 2 (medium), 3 (high)
	//document.getElementById("CTEApplet").setAgentAggressiveness(agressiveness);
	//aggressiveness=getAgentAggressiveness();

	//The following two lines allow you to set/get the tracing on the agent side.
    //Use "-trace" to turn tracing on (there is a performance impact).
	//document.getElementById("CTEApplet").setAgentTrace(trace);
	//trace=getAgentTrace();
	return;
	};

Setting Configuration Parameters

In our applet, we created a number of setter/getter functions so that all of configuration settings can be made or viewed in JavaScript. The following is a list of all the setters and getters:

Setters

setAgentList(agentList)
setAgentUser(userName)
setAgentPwd(userPwd)
setAgentSourceDir(directory)
setAgentDestDir(destDir);
setAgentCert(agentCert)
setAgentLogging(loggingLevel)
setAgentTransport(transportProtocol)
setAgentTargetRate(targetRate)
setAgentAggressiveness(agressiveness)
setAgentTrace(trace)

Getters

getAgentList()
getAgentUser()
getAgentPwd()
getAgentSourceDir()
getAgentDestDir();
getAgentCert()
getAgentLogging()
getAgentTransport()
getAgentTargetRate()
getAgentAggressiveness()
getAgentTrace()

For example, to reference getAgentTransport, use the following syntax:

document.getElementById("CTEApplet").getAgentCert()

We reference the applet itself using: document.getElementById("CTEApplet") 

where "CTEApplet" is the ID value we set in the <embed> and <object> elements.  This is followed by the name of the setter/getter function.

Display/User Interaction Elements

There are a number of display elements in our Flex example that allow us to showcase the most common use cases for the CTE: upload and download of files. It is important to note that these display elements could be anything that works for your particular implementation. We used standard Flex components for sample purposes, but they could have just as easily been custom skinned buttons or other more customized elements.

To highlight the various components, we have divided our user interface in to three distinct areas using BorderContainers. They are:

  • Upload

  • Download

  • Transfer

Border Containers

BorderContainer components allow us to group various components and hide/show them as necessary. In our sample, we have the following three Border Containers:

Upload

The upload Border Container has all the components for selecting files to upload to the remote Signiant Agent. In our sample, we take advantage of the two file chooser user interfaces we provide in the Java applet: AWT and Swing. If the user selects Single File the Browse button calls the AWT file chooser. Selecting Multi File and clicking Browse displays the Swing file chooser and allows the user to select multiple files for upload.

<s:BorderContainer x="49" y="51" width="746" height="210" borderVisible="false" id="upload"
                  visible="true" autoLayout="true">
		<s:Label x="10" y="15" text="File Selection Type:"/>
		<s:RadioButton x="144" y="10" label="Single File" id="radio1" groupName="fileSelection" selected="true"/>
		<s:RadioButton x="236" y="10" label="Multi File" id="radio2" groupName="fileSelection"/>
		<s:Button x="316" y="9" label="Browse" click="browseButton_clickHandler('file')"/>
		<s:TextArea x="144" y="50" width="596" editable="false" id="filesSelected"/>
		<s:Label x="24" y="50" text="File(s) Selected:"/>
	</s:BorderContainer>

Download

The download Border Container allows the user to select a local directory as the target to download files to, as well as type the name of the remote file to download. In this case, the Swing file chooser is set to only allow the user to select a directory.

<s:BorderContainer x="49" y="51" width="746" height="72" borderVisible="false" id="download" visible="false">
		<s:Label x="20" y="13" text="File to Download:" textAlign="right"/>
		<s:Button x="666" y="44" label="Browse" click="browseButton_clickHandler('dir')"/>
		<s:Label x="24" y="50" text="Target Directory:"/>
		<s:TextInput x="144" y="10" width="592" id="fileToDownload"/>
		<s:TextInput x="144" y="44" width="510" id="targetDir"/>
	</s:BorderContainer>

Transfer

The transfer Border Container contains all of the information components for monitoring a transfer in progress. This includes displaying any messages returned by the CTE, dynamically displaying the current transfer rate being achieved, and a progress bar to show the percent of the file uploaded/downloaded.

<s:BorderContainer x="48" y="366" width="746" height="275" borderVisible="false" id="transfer" visible="false" autoLayout="true">
		<s:Label x="15" y="15" text="Transfer Protocol:"/>
		<s:Button x="143" y="193" label="Pause" id="interrupt" click="interrupt_clickHandler(event)"/>
		<s:Button x="220" y="193" label="Cancel" id="cancel" click="cancel_clickHandler(event)"/>
		<s:Button x="298" y="193" label="View Log" id="viewLog" enabled="false" click="viewLog_clickHandler(event)"/>
		<s:TextArea x="144" y="44" width="596" editable="false" height="77" id="message"/>
		<s:Label x="31" y="44" text="Last Message:"/>
		<s:TextInput x="144" y="10" id="protocolField"/>
		<s:Label x="33" y="136" text="Transfer Rate:"/>
		<s:Label x="25" y="168" text="Transfer Status:"/>
		<s:TextInput x="143" y="130" id="transferRateField"/>
		<mx:ProgressBar x="142" y="166" labelPlacement="center" label="0%" mode="manual" id="progressBar"/>
	</s:BorderContainer>

Dropdown List

A dropdown list at the top of the page allows the user to choose whether an upload or download will be performed. Besides showing and hiding the respective div, the selection will also be passed to the CTE. The Flex code is:

<s:DropDownList x="196" y="25" change="transferType_changeHandler(event)" id="transferType" labelField="type" selectedIndex="0">

<mx:ArrayCollection>
			<fx:String>Upload</fx:String>
			<fx:String>Download</fx:String>
		</mx:ArrayCollection>
	</s:DropDownList>

Buttons

Standard form buttons are used for users to initiate actions within the user interface. The following buttons are defined in our sample:

Browse

We have two different browse buttons defined in our user interface. One to select file(s) and the other to select a directory. However, the same ActionScript function is called in both cases by simply passing in "file" or "dir" respectively. The Flex code for the Browse buttons is:

<s:Button x="316" y="9" label="Browse" click="browseButton_clickHandler('file')"/>

and

<s:Button x="666" y="44" label="Browse" click="browseButton_clickHandler('dir')"/>

Start Transfer

The start transfer button is used to actually kick off the CTE transfer.  The Flex code for the start transfer button is:

<s:Button x="193" y="266" label="Start Transfer" id="startTransfer" click="startTransfer_clickHandler(event)" />

Pause/Resume Transfer

The pause/resume button is only available while a transfer is in progress.  The same button is repurposed to resume a paused transfer.  The Flex code for the pause/resume button is:

<s:Button x="143" y="193" label="Pause" id="interrupt" click="interrupt_clickHandler(event)"/>

Cancel Transfer

The cancel button is only available while a transfer is in progress.  The Flex code for the cancel button is:

<s:Button x="220" y="193" label="Cancel" id="cancel" click="cancel_clickHandler(event)"/>

View Log

The view log button is only enabled when the onLogfile event is fired and a log file has been created.  The Flex code for the view log button is:

<s:Button x="298" y="193" label="View Log" id="viewLog" enabled="false" click="viewLog_clickHandler(event)"/>

Progress Bar

In our sample we use a standard Flex progress bar component as follows:

<mx:ProgressBar x="142" y="166" labelPlacement="center" label="0%" mode="manual" id="progressBar"/>

Text Fields

We chose to use standard Flex input and textareas to display file selection choices and status messages. There are several different text fields in our user interface, but the following two types are representative:

<s:TextInput x="143" y="130" id="transferRateField"/>

and

<s:TextArea x="144" y="50" width="596" editable="false" id="filesSelected"/>


C# Implementation

This chapter focuses on the creation of a Windows Forms application using C#.  The existing webclient.jar is converted to a digital link library (dll) using IKVM.net (www.ikvm.net/index.html) so that it can be used in a Visual Studio application. (see Converting webclient.jar to a .dll for more information on how to use IKVM)

It is assumed that the reader has launched Visual Studio 2005 (or equivalent) and has created a new Windows Forms project.

A compiled version of the application described in this chapter as well as the converted webclient.dll and assorted ikvm dll's required to run the application are available from Signiant.

Imports

In the interest of clarity this example will contain three classes: Form1 - contains the UI controls and logic, CTETransfer contains the code that interacts with the Content Transfer Engine and CTEListener implements the TransferListener protocol to receive status message callbacks from the CTE.  

The first step after creating your new project is to add the converted Signiant webclient.dll and various IKVM dll's as project references. The class that will make calls to the webclient methods (e.g.: CTETransfer) and the listener that will receive callbacks from the CTE will import the com.signiant.interactivetransfer.engine namespace in the webclient.dll via "using" directives. Also the java.lang.Exception must be mapped to a field (e.g.: exception) to compile the webclient.dll conversion.

The following namespaces are also imported via "using" directives in the CTETransfer and CTEListener classes:

System;
System.Collections.Generic;
System.Windows.Forms;
System.Diagnostics;
System.Configuration;
System.Threading;
System.IO;
System.Runtime.CompilerServices;
System.Runtime.InteropServices;

The class that will facilitate the UI (e.g.: Form1) will import the following namespaces:

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Diagnostics;
System.Drawing;
System.Text;
System.Windows.Forms;

This project will also define its own namespace as CTETransferClient.

Setting Configuration Parameters

There are a number of configuration parameters that can be set prior to initiating a transport with CTE. The following parameters are available:

Parameter Description
Transfer The type of transfer for this configuration. Choose from "Send" (files are transferred to the selected remote agent) or "Receive" (files are transferred from the selected remote agent).
User The name of the user as which the transfer is running on the Content Transfer Engine-enabled server. With Local authentication, the transfer uses the username and password of the user on the Content Transfer Engine-enabled server. With SOAP authentication, the username and password are passed to the SOAP server, effectively ignored by the Content Transfer Engine-enabled server, since the transfer runs as the default user. The SOAP server can use the username and password to perform authentication.

Note that if you use the %user% and %password% keywords for the Fallback URL List (see above), the user and password you specify here and in the following field are substituted. With FTP transfers, you need to specify the user name and password here for the Fallback URL List, otherwise "anonymous" is assumed.

Password The password for the user as which the transfer is running on the Content Transfer Engine-enabled server. With Local authentication, the transfer uses the username and password of the user on the Content Transfer Engine- enabled server. With SOAP authentication, the username and password are passed to the SOAP server, effectively ignored by the Content Transfer Engine-enabled server, since the transfer runs as the default user. The SOAP server can use the username and password to perform authentication. The field displays the password masked as asterisks.
Directory Handling Mode Specifies the way the directory structure will be replicated on transfer. Choose from the following:
  • Full (replicates the parent directory, as well as the specified directory. For example, if the specified directory is "\my files", on transfer, the directory structure would include the parent, similar to "c:\parent directory\my files").
  • Flat (all the files appear in a single root directory, regardless of sub- directory structure on the source)
  • Path (the directory structure is replicated from the specified directory downwards. For example, "\my files\new\file 1, file 2")
C.A. Certificate Displays the CA certificate the agent uses for server authentication. The certificate that appears by default is the one generated by the Manager. You may want to use a different CA certificate if you are using a different Certificate Authority. The certificate must be from the Manager from which the agent was installed. To extract a CA, follow these steps:
  1. Login as root/Administrator to a host that is populated with the Manager trusted CA whose certificate you are trying to obtain. For example, if you are interested in obtaining a Manager trusted CA certificate for the site MySite.com, then logon to an agent that belongs to MySite.com (i.e., MyHost01.MySite.com).
  2. Run the administrative command: dds_cert extract. This will extract certificates from the credentials store, and among them will exist the Manager trusted CA certificate named ddsCA_cert.pem.
  3. Copy ddsCA_cert.pem to a location where agents wishing to import it into their local credentials store can access it.
  4. Logout
  5. Paste the contents of the ddsCA_cert.pem file in the CA Certificate field. OR e. Click Browse to select the location of the CA cert file.
Number of Retries The number of times the source attempts to transfer a file after an initial failure, such as "file in use". Default value is 3. A value of 0 indicates no retries. Maximum value you can specify is 99.
Number of Restarts Specifies the maximum number of times the SDK attempts to restart a transfer after a recoverable failure such as a network outage. If set to zero ("0"), no transfer agent restarts are attempted. Maximum value you can specify is 99. The default value is 3.
Transport The method to use for transferring the files. Choose from the following: WAN Accelerator then Parallel Streams - Use the WAN accelerator (UDP) initially, but if communication cannot be established, try parallel streams (TCP). WAN Accelerator - Use the WAN Accelerator (UDP) only. Parallel Streams - Use Parallel Streams (TCP) only. Note that if you specify a Fallback URL List (see above), then the transport will go to the fallback option if none of the transport option(s) specified here works. If a fallback URL is specified, but an agent is not, this field is ignored.
Number of Streams Specifies the number of files to transfer at one time. The default (and maximum) value is 4. Specifying zero will cause an error. The default value becomes the lower of the number of files to be transferred or the number of streams specified. For example, if you specify two files, only two streams are used.
Bandwidth Limit Limit this job to the amount of bandwidth specified. You set this limit in bytes, kilobytes or megabytes per second to a maximum of whatever the CPU can handle, or a percentage of a selected network connection (for example, 75% of 128 Kbytes/s). A bandwidth level below 128 Kbytes/sec is not supported, as performance is not reliable. Note that if you specify a Target Transfer Rate for WAN Accelerator parameters (see below), the Bandwidth Limit setting is not the maximum bandwidth limit. The Target Transfer rate becomes the de facto maximum bandwidth rate, as it provides a ceiling for the transfer rate, factoring in retransmission rate and network sharing. (For example, if the transfer had to go below the bandwidth limit to allow for shared network usage, once the network is less busy, the transfer may go above the Bandwidth Limit, to a maximum of the specified Target Transfer Rate until the transmission averages to the specified Bandwidth Limit, at which point the bandwidth limit becomes effective again.)

Note that bandwidth throttles may also be employed by other network devices and policies (e.g., QoS), therefore, a bandwidth throttle (or target maximum) defined here may not be achievable. If you are having difficulty achieving a particular bandwidth target, ensure that other policies are not impacting your ability to reach the desired throughput. Note also that bandwidth throttles and pause/resume are honored for all transfers, regardless of protocol. Some FTP servers cannot support resume. If the FTP server advertises that it cannot, the user is informed immediately, and pause/resume will cause the current transfer to start over at the beginning.

Encrypt Allows users to specify the encryption level from the following values: "Strong encryption", "No encryption, signed" or "No encryption, unsigned". The default value is "Strong encryption". Note that server authentication is always used regardless of the encryption level specified. The "No encryption, signed" option transfers unencrypted (plain text) data, but includes the SSL protocol's message digest calculation and digital signing to ensure data stream integrity.

The "No encryption, unsigned" option allows the transfer of data, after the initial SSL authentication of the endpoints to proceed without encryption, message digest computation or digital signing. This mode of operation is only for raw performance, since it makes no guarantee of the integrity of the data stream other than what is provided by the network channel. The underlying TCP protocol can guarantee the integrity of messages across each single network hop, but has no facilities for detecting a man-in-the-middle attack.

Agent List A space-separated list of the agent(s) with which the client can communicate, as well as any relay agents those agents may need. When more than one agent is listed, the transfer engine attempts to launch a transfer on all of the agents at the same time. The first agent with which it connects continues with the transfer. All other connections immediately end. If the agent has a relay, use the following format: agent1[relay1 relay2(portForRelay2)] agent2[relay3(port) relay4] agent3 agent4 Note that port numbers are optional, but the square brackets are required. Both the source and the relay agent must use the same port number. In addition, you must enable the Content Transfer Engine SDK on the relay agent. 
Fallback URL List This is a list of URLs that the transfer will attempt to use once the agent list is exhausted, or if it has not been specified. The following protocols are supported: UDP (WAN Acceleration), TCP (parallel streams), HTTP (parallel streams over HTTP), or FTP. Use the following format for the fallback URL list, separating each URL with a new line "\n" or the pipe "|" character. With the pipe character as separator, the Content Transfer Engine SDK tries all of the addresses at the same time and uses the quickest to respond. URLs separated by new lines are processed in the order in which they appear:protocol://[username@ | username:password@]host[:port]/path [ | ] [ protocol://host/path]Protocol is one of: mxtcp (TCP authentication), mxwan (WAN acceleration), HTTP or FTP. The host and path are implementation dependent, and the port is an optional value that may be specified in order to use a non-standard port. The user name and password are optional and if specified, can either be clear text, or the keywords %user% and %password%, which are substituted for the currently-supplied Content Transfer Engine SDK user name and password.

Note that the native Content Transfer Engine SDK format is as follows:protocol://target.host@relay.host:portWhere "protocol" can be "mxtcp" for a parallel streams (TCP) transfer, or "mxwan" for a udp transfer, "target.host" is the name passed to the relay (if present), "relay.host" is the agent to which a direct connection is made, and "port" is the port on which the agent is listening. Note that with FTP transfers, you need to use the user name and password in the Fallback URL List, otherwise "anonymous" is assumed. For sample Fallback URL implementations, see Sample Fallback URL Implementations.

Logging Specifies the level of detail that appears in the log files for a job run. Choose from "None", "Info", "Fine", "Finer", or "Finest". "Finest" provides the greatest amount of detail, while "Info" provides the least. The default is "Info". Note "finest" logging may have a significant impact on performance.
Trace Whether to create a trace file for the connection on the agent. Use in consultation with a technical support person to define agent trace flags.
WAN Acceleration Specific Parameters
Target Transfer Rate The rate at which the agent should attempt to send data. Typically this is the maximum speed of the network (i.e., 100Mbps, 10Mbps, and so on). You no longer have to set a definitive target transfer rate, as the transfer will now attempt to discover the target throughput by analyzing packet loss and adjusting its transmission rate accordingly. Each transfer will continually adjust itself to maximize its throughput across all network access points between the source and the target. You may specify the target transfer rate for use under conditions where you know the actual link throughput in advance. If specified, the transfer uses the target transfer rate as a bandwidth ceiling. It is the rate of transfer including retransmission. Note that if you plan to set a Minimum Transfer Rate (see below) you must specify a Target Transfer Rate that is larger than the Minimum Transfer Rate you specify.
Minimum Transfer Rate The minimum rate at which the transfer should send the data. Note that the Minimum Transfer Rate works only if you set a Target Transfer Rate (see above), and if that Target Transfer Rate value is larger than the specified minimum transfer rate. Limits the slowest transmission rate to which the transfer can back off, regardless of packet loss. Be careful when setting the Minimum Transfer Rate variable, as it can flood the network.
Aggressiveness Indicates how sensitive the job will be to other network traffic when running. "High" aggressiveness yields the least to other network traffic. Choose from one of the following:

High: The agent will always attempt to send data at the Target Transfer rate (specified or dynamically calculated) and not share with other network traffic. It is recommended that you specify a Target Transfer rate with the High aggressiveness setting. If you do not, it is possible for the transfer to drive other traffic off the network, possibly resulting in a denial of service. By setting a reasonable target transfer rate, this setting can be most effective at overcoming packet loss on less reliable networks.

Medium: The agent will always attempt to send data at the Target transfer rate (specified or dynamically calculated), however, if other traffic is detected, the agent will share the network resources but never fall below the Minimum Transfer Rate (if specified). Use the Medium aggressiveness setting as the default setting, along with specifying a Target Transfer rate value where possible. Setting the target achieves a balance between network sharing and overcoming packet loss.

Low: The agent will always attempt to send data at the Target Transfer Rate (specified or dynamically calculated), however, if other traffic is detected, the agent will drop its transfer rate more quickly and attempt to creep back up to the Target Transfer rate more slowly. Use the Low aggressiveness setting for clean networks (little or no packet loss between endpoints) where sharing the network is the primary goal of the transfer.

Agents involved in a Content Transfer Engine SDK transfer must be configured as Content Transfer Engine-enabled servers. Make sure the agents you specify are properly configured.

In our C# application example, we'll create variables and public scoped setters and getters for each of these parameters, exposing them externally so that they can be set outside of the application itself (see Setters/Getters below). Additionally these will be initially configured, where appropriate, via the App.config configuration file.

External Configuration File

In our C# implementation, we utilize the App.config configuration file which is loaded on start-up.  The App.config configuration file is in XML format and as such allows for easy updates.  A sample App.config configuration file is provided in the Appendix of this document.

The following code loads the configuration file contents into local variables:

private void readConfigSettings()
{
    try
    {
        //parse settings in App.config to class scope variables
        mxAgentList = ConfigurationSettings.AppSettings["mxAgentList"].ToString();
        mxAgentUser = ConfigurationSettings.AppSettings["mxAgentUser"].ToString();
        mxAgentPwd = ConfigurationSettings.AppSettings["mxAgentPwd"].ToString();
        mxAgentURLList = ConfigurationSettings.AppSettings["mxAgentURLList"].ToString();
        mxAgentDestDir = ConfigurationSettings.AppSettings["mxDestinationDir"].ToString();
        mxAgentCert = ConfigurationSettings.AppSettings["mxAgentCert"].ToString();
        mxLogging = ConfigurationSettings.AppSettings["mxLogging"].ToString();
        mxTransport = ConfigurationSettings.AppSettings["mxTransport"].ToString();
        mxTargetRate = long.Parse(ConfigurationSettings.AppSettings["mxTargetRate"].ToString());
        mxAggressiveness = int.Parse(ConfigurationSettings.AppSettings["mxAggressiveness"].ToString());
        mxTrace = ConfigurationSettings.AppSettings["mxTrace"].ToString();
        mxBandwidthThrottle = long.Parse(ConfigurationSettings.AppSettings["mxBandwidthThrottle"].ToString());
        mxRestarts = int.Parse(ConfigurationSettings.AppSettings["mxRestarts"].ToString());
        mxStreams = int.Parse(ConfigurationSettings.AppSettings["mxStreams"].ToString());
        mxRetries = int.Parse(ConfigurationSettings.AppSettings["mxRetries"].ToString());
        mxEncryption = ConfigurationSettings.AppSettings["mxEncryption"].ToString();
        mxDirHandlingMode = ConfigurationSettings.AppSettings["mxDirHandlingMode"].ToString();
        mxUDPMinimumRate = long.Parse(ConfigurationSettings.AppSettings["mxUDPMinimumRate"].ToString());

    }
    catch (Exception e)
    {
        Debug.WriteLine("Exception in readConfigSettings: " + e.ToString());
    }
}

Once the configuration file has been loaded and the values parsed to local variables, the following code is used to set the corresponding parameters in the Signiant CTE:

engine.setAgentList(mxAgentList);
engine.setUser(mxAgentUser);
engine.setPassword(mxAgentPwd.ToCharArray());

engine.setCertificate(mxAgentCert);
engine.setMode(mxTransferMode);

if (mxTransport.Equals("Transport.TCP"))
{
    engine.setTransport(Transport.TCP);
}
else if (mxTransport.Equals("Transport.UDP"))
{
    engine.setTransport(Transport.UDP);
}
else if (mxTransport.Equals("Transport.UDP_THEN_TCP"))
{
    engine.setTransport(Transport.UDP_THEN_TCP);
}

if (mxTargetRate > 0)
{
    engine.setTargetRate(mxTargetRate);
}

if (mxBandwidthThrottle > 0)
{
    engine.setBandwidthThrottle(mxBandwidthThrottle);
}

if (mxRestarts > 0)
{
    engine.setRestarts(mxRestarts);
}

if (mxStreams > 0)
{
    engine.setNumberOfStreams(mxStreams);
}

if (mxRetries > 0)
{
    engine.setRetries(mxRetries);
}

if (mxEncryption != null && mxEncryption.Length > 0)
{
    if (mxEncryption.Equals("STRONG"))
    {
        engine.setEncryption(com.signiant.mobilize.ddsclient.connection.Port.SSLMode.STRONG);
    }
    if (mxEncryption.Equals("SIGNED"))
    {
        engine.setEncryption(com.signiant.interactivetransfer.engine.Port.SSLMode.NONE);
    }
    if (mxEncryption.Equals("UNSIGNED"))
    {
        engine.setEncryption(com.signiant.mobilize.ddsclient.connection.Port.SSLMode.UNSIGNED);
    }
}

if (mxDirHandlingMode != null && mxDirHandlingMode.Length > 0)
{
    if (mxDirHandlingMode.Equals("PATH"))
    {
        engine.setDirectoryHandlingMode(com.signiant.interactivetransfer.engine.FileTransfer.DirectoryHandlingMode.PATH);
    }
    if (mxDirHandlingMode.Equals("FLAT"))
    {
        engine.setDirectoryHandlingMode(com.signiant.interactivetransfer.engine.FileTransfer.DirectoryHandlingMode.FLAT);
    }
    if (mxDirHandlingMode.Equals("FULL"))
    {
        engine.setDirectoryHandlingMode(com.signiant.interactivetransfer.engine.FileTransfer.DirectoryHandlingMode.FULL);
    }
}

if (mxUDPMinimumRate > 0)
{
    engine.setMinimumRate(mxUDPMinimumRate);
}

engine.setAggressiveness(mxAggressiveness);

engine.setLogging(mxLogging);

if (mxTrace != null && mxTrace.Length > 0)
{
    engine.setTrace(mxTrace);
}

if (mxAgentURLList != null && mxAgentURLList.Length > 0)
{
    engine.setUrlList(mxAgentURLList);
}

engine.setDestination(mxAgentDestDir);
engine.setFiles(fileNames);
engine.addTransferListener(listener);

Setters/Getters

With the Windows Forms application UI model the form can control many aspects of a file transfer and so we have also exposed all of these same configuration options as setters and getters using the following code:

public String MxAgentList
{
    get { return mxAgentList; }
    set { mxAgentList = value; }
}

public String MxAgentUser
{
    get { return mxAgentUser; }
    set { mxAgentUser = value; }
}

public String MxAgentPwd
{
    get { return mxAgentPwd; }
    set { mxAgentPwd = value; }
}

public String MxAgentURLList
{
    get { return mxAgentURLList; }
    set { mxAgentURLList = value; }
}

public String MxAgentDestDir
{
    get { return mxAgentDestDir; }
    set { mxAgentDestDir = value; }
}

public String MxAgentCert
{
    get { return mxAgentCert; }
    set { mxAgentCert = value; }
}

public String MxLogging
{
    get { return mxLogging; }
    set { mxLogging = value; }
}

public String MxTransport
{
    get { return mxTransport; }
    set { mxTransport = value; }
}

public long MxTargetRate
{
    get { return mxTargetRate; }
    set { mxTargetRate = value; }
}

public int MxAggressiveness
{
    get { return mxAggressiveness; }
    set { mxAggressiveness = value; }
}

public String MxTrace
{
    get { return mxTrace; }
    set { mxTrace = value; }
}

public long MxBandwidthThrottle
{
    get { return mxBandwidthThrottle; }
    set { mxBandwidthThrottle = value; }
}

public int MxRestarts
{
    get { return mxRestarts; }
    set { mxRestarts = value; }
}

public int MxStreams
{
    get { return mxStreams; }
    set { mxStreams = value; }
}

public int MxRetries
{
    get { return mxRetries; }
    set { mxRetries = value; }
}

public String MxEncryption
{
    get { return mxEncryption; }
    set { mxEncryption = value; }
}

public String MxDirHandlingMode
{
    get { return mxDirHandlingMode; }
    set { mxDirHandlingMode = value; }
}

public long MxUDPMinimumRate
{
    get { return mxUDPMinimumRate; }
    set { mxUDPMinimumRate = value; }
}

public void setTransferMode(TransferModeEnum mode)
{
    if (mode.Equals(TransferModeEnum.SEND))
    {
        mxTransferMode = TransferMode.SEND;
    }
    else if (mode.Equals(TransferModeEnum.RECEIVE))
    {
        mxTransferMode = TransferMode.RECEIVE;
    }
}
public TransferModeEnum getTransferMode()
{
    if (mxTransferMode.equals(TransferMode.SEND))
    {
        return TransferModeEnum.SEND;
    }
    else if (mxTransferMode.equals(TransferMode.RECEIVE))
    {
        return TransferModeEnum.RECEIVE;
    }
    else
    {
        return TransferModeEnum.NONE;
    }
}

Selecting Files and Directories

The C# application uses an OpenFileDialog and a FolderBrowserDialog depending on the selection of File(s) or Folder radio buttons in a file Upload operation and the FolderBrowserDialog for the selection of the target folder in a Download operation.

Upload

The user can choose between selecting one or more files to upload or an entire folder as:

private void btnFileOpen_Click(object sender, EventArgs e)
{
    //File(s) radio button checked

    if (radioButton1.Checked)
    {
        OpenFileDialog dlg = new OpenFileDialog();
        dlg.Multiselect = true;
        dlg.SupportMultiDottedExtensions = true;
        dlg.Title = "Select File(s) to Transfer";

        if (dlg.ShowDialog() == DialogResult.OK)
        {
            lstFiles.Items.Clear();

            btnStartTransfer.Enabled = true;

            filesArray = new String[dlg.SafeFileNames.Length];

            for (int i = 0; i < dlg.FileNames.Length; i++)
            {
                filesArray[i] = dlg.FileNames[i];

                lstFiles.Items.Add(dlg.FileNames[i]);
                //lstFiles.Items.Add(dlg.SafeFileNames[i]);
            }
        }
    }
    else
    {
	  //Folder radio button checked
        FolderBrowserDialog fbd = new FolderBrowserDialog();
        if (fbd.ShowDialog() == DialogResult.OK)
        {
            lstFiles.Items.Clear();

            btnStartTransfer.Enabled = true;

            filesArray = new String[1];
            filesArray[0] = fbd.SelectedPath;

            lstFiles.Items.Add(fbd.SelectedPath);
        }
    }
}

Download

The user selects a destination (target) folder for a download op as:

private void btnPickTargetFolder_Click(object sender, EventArgs e)
{
    FolderBrowserDialog fbd = new FolderBrowserDialog();
    if (fbd.ShowDialog() == DialogResult.OK)
    {
        lblTargetFolder.Text = fbd.SelectedPath;

        xfer.MxAgentDestDir = fbd.SelectedPath;
    }

    btnStartTransfer.Enabled = (txtSourceFile.Text.Length > 0 && lblTargetFolder.Text.Length > 0);
}

Starting a Transfer

The C# application allows for two kinds of transfers Upload and Download. The selection is made via a tab control and the option is set in the Transfer Engine via the TransferMode property as either TransferMode.SEND or TransferMode.Receive values respectively. In a receive operation the destination folder for the downloaded file(s) is set via the setDestination property setter of the Transfer Engine. All Transfer Engine properties are set from variables, the transfer process is initiated and progress is monitored and reported back to the UI via the submitFiles method:

public bool submitFiles(String[] fileNames)
{
    bool bSuccess = true;

    CTEListener listener = new CTEListener(Parent);

    try
    {
        //readConfigSettings();

        if (mxTransferMode.equals(TransferMode.SEND))
        {
            if ((!File.Exists(fileNames[0])) && (!Directory.Exists(fileNames[0])))
            {
                Debug.WriteLine("can't find input file or folder: " + fileNames[0]);
                return false;
            }
        }

	//setEngineProperties

     //start the transfer op
    engine.startTransfer();

    Parent.transportProtocolChange(engine.getTransport().toString());

    //monitor transfer progress
    enterProgressLoop();

}
catch (Exception e)
{
    Debug.WriteLine("Exception in submitFiles: " + e.ToString());
    return false;
}

Monitoring Progress

Engine progress is interrogated in a while (!engine.isCompleted) loop. In this example Application.DoEvents() is called in the loop so that the UI can get some CPU cycles to process listener callbacks and repaint the form:

private static void enterProgressLoop()
{
    while (!engine.isCompleted())
    {
        if (isPaused)
        {
            break;
        }

        try
        {
            Thread.Sleep(200);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Exception sleeping thread: " + ex.ToString());
        }

        if (engine.getTotalBytesToTransfer() == 0)
        {
            continue;
        }
        long percentComplete = (engine.getBytesSkipped() + engine.getBytesTransferred()) * 100 / engine.getTotalBytesToTransfer();
        long transferRateKb = engine.getTransferRate() / 1024;

        if (percentComplete != lastProgress)
        {
            Debug.WriteLine("transfer progress: " + percentComplete + "% - " + transferRateKb + " KBytes/s");

            Parent.updateProgress(percentComplete);
            Parent.updateTransferRate(transferRateKb);
            Parent.updateStatus("transferring file(s)");

            Application.DoEvents();

            lastProgress = percentComplete;
        }
        //give some cycles to paint UI and respond to button clicks
        Application.DoEvents();
    }
}

Callbacks

The following callbacks are made to the UI form during and after a transfer:

In Process

Parent.updateProgress(percentComplete);

Parent.updateTransferRate(transferRateKb);

Parent.updateStatus("transferring file(s)");

Transfer Control Methods/Responses

public bool cancelTransfer()
{
    try
    {
        engine.cancelTransfer();
        Parent.updateStatus("transfer cancelled");
        return true;
    }
    catch (Exception ex)
    {
        Debug.WriteLine("exception cancelling transfer: " + ex.ToString());
        return false;
    }
}

public bool pauseTransfer()
{
    try
    {
        engine.shutdownTransfer();
        isPaused = true;
        Parent.updateStatus("transfer paused");
        return true;
    }
    catch (Exception ex)
    {
        Debug.WriteLine("exception pausing transfer: " + ex.ToString());
        return false;
    }
}

public bool resumeTransfer()
{
    try
    {
        engine.restartTransfer();
        isPaused = false;
        Parent.updateStatus("transfer resumed");
        enterProgressLoop();
        return true;
    }
    catch (Exception ex)
    {
        Debug.WriteLine("exception resuming transfer: " + ex.ToString());
        return false;
    }
}

On Completion

CompletionStatus compStatus = engine.getCompletionStatus();
String statusMsg = "";

if (compStatus.equals(CompletionStatus.SUCCESSFUL))
{
    bSuccess = true;
    statusMsg = "SUCCESS";
}
else if (compStatus.equals(CompletionStatus.WARNING))
{
    bSuccess = true;
    statusMsg = "WARNING";
}
else if (compStatus.equals(CompletionStatus.FAILURE))
{
    bSuccess = false;
    statusMsg = "FAILURE";
}

Parent.onCompleteEventHandler(statusMsg);

Post Transfer

public void postTransfer(CompletionStatus status)
{
    Debug.WriteLine("postTransfer.CompletionStatus = " + status.toString());
    Parent.onPostTransferEventHandler(status.toString());
}

Converting webclient.jar to a .dll

We used the open source IKVM.net tools to convert the CTE webclient.jar to a digital link library (dll) for consumption by a Visual Studio application. complete instructions and the source for IKVM can be found at http://www.ikvm.net/home.

After unzipping IKVM.net the following command was used at the Windows Command Prompt to generate the webclient.dll file from the webclient.jar file:

Ikvmc webclient.jar -target:library

IKVM.net adds a java layer to a .net application via inclusion of dll references. The following dll's from the IKVM.net source were referenced in our C# Windows Forms example application (note: it is likely not all of the following references were required - no attempt was made to narrow the inclusion to only those necessary):

ICSharpCode.SharpZipLib.dll
IKVM.AWT.WinForms.dll
IKVM.OpenJDK.Beans.dll
IKVM.OpenJDK.Charsets.dll
IKVM.OpenJDK.Cobra.dll
IKVM.OpenJDK.Core.dll
IKVM.OpenJDK.Jdbc.dll
IKVM.OpenJDK.Management.dll
IKVM.OpenJDK.Media.dll
IKVM.OpenJDK.Misc.dll
IKVM.OpenJDK.Naming.dll
IKVM.OpenJDK.Remoting.dll
IKVM.OpenJDK.Security.dll
IKVM.OpenJDK.SwingAWT.dll
IKVM.OpenJDK.Text.dll
IKVM.OpenJDK.Util.dll
IKVM.OpenJDK.XML.API.dll
IKVM.OpenJDK.XML.Bind.dll
IKVM.OpenJDK.XML.Crypto.dll
IKVM.OpenJDK.XML.Parse.dll
IKVM.OpenJDK.XML.RelaxNG.dll
IKVM.OpenJDK.XML.Transform.dll
IKVM.OpenJDK.XML.WebServices.dll
IKVM.OpenJDK.XML.XPath.dll
IKVM.Reflection.Emt
IKVM.Reflection.Emt.MdbWriter.dll
IKVM.Reflection.Emt.PdbWriter.dll
IKVM.Runtime.dll
IKVM.Runtime.JNI.dll
ikvmstub.dll
JVM.dll

Sample Configuration File

The following is a sample configuration file:

mxAgentList=myAgent
mxAgentUser=administrator
mxAgentPwd=password
mxSourceDir=/Users/jdoe/Documents/Images
mxDestinationDir=c:\\signiant_xfers
mxAgentCert=-----BEGIN CERTIFICATE-----\n
MIIFEjCCA/qgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBnzELMAkGA1UEBhMCVVMx\n
EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCUNhbGFiYXNhczEXMBUGA1UE\n
ChMOdGhyZWVsZWdnZWRkb2cxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0\
neTEuMCwGA1UEAxMlc3pyb21iYS14cHMgRFRNIENlcnRpZmljYXRlIEF1dGhvcml0\n
eTAeFw0xMDAzMTcwNzAwMDFaFw0zNTAzMTcwNzAwMDFaMIGfMQswCQYDVQQGEwJV\n
UzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJQ2FsYWJhc2FzMRcwFQYD\n
VQQKEw50aHJlZWxlZ2dlZGRvZzEeMBwGA1UECxMVQ2VydGlmaWNhdGUgQXV0aG9y\n
aXR5MS4wLAYDVQQDEyVzenJvbWJhLXhwcyBEVE0gQ2VydGlmaWNhdGUgQXV0aG9y\n
aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA80u7Hl3IxujDOMA2\n
nyNHnqwrVB1SznGciNUe5fyQa2MJmHcqiEFPxwtAdcnwgBHklWouC9wgJNeaVv/v\n
7Cq9+bYig1A0uaTIB3Bu8tQETP8YClRz8/JhuFdes1LFqbts/Tpn5doo4z/2HUrX\n
16sRzlz6cj/Bv12x0oo1vdcviu8jRW1bc3ou+gNhQxoOR+973Qk0y5wBvQCfOATz\n
8qXODESb/z1surCvL2EZMj3BFQ8YYkChzvVS3GJmoun1ab7JqIop8hsIGCllcL9k\n
SqeNhl81IW2CPtAqTHoJ9vn29ZieuV/ZUfXCjQxsoTSKSsG2j1n8hqdoCX3hRSrh\n
Fd8qCwIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zARBglghkgBhvhCAQEE\n
BAMCAgQwCwYDVR0PBAQDAgEGMDAGCWCGSAGG+EIBDQQjFiFUcnVzdGVkIEREUyBD\n
ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHQYDVR0OBBYEFNo5o+5ea0sNMlW/75VgGJCv\n
2AcJMIHMBgNVHSMEgcQwgcGAFNo5o+5ea0sNMlW/75VgGJCv2AcJoYGlpIGiMIGf\n
MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJQ2Fs\n
YWJhc2FzMRcwFQYDVQQKEw50aHJlZWxlZ2dlZGRvZzEeMBwGA1UECxMVQ2VydGlm\n
aWNhdGUgQXV0aG9yaXR5MS4wLAYDVQQDEyVzenJvbWJhLXhwcyBEVE0gQ2VydGlm\n
aWNhdGUgQXV0aG9yaXR5ggEAMA0GCSqGSIb3DQEBBQUAA4IBAQCKrjLp+y8xHDa0\n
VnL3w4QKJFLhIOGerhWx+gIJE12X+btE1Kl9YMMfdCTEF47Vm1JzMUgBHhNIXrs4\n
vSToJbw7uWDxM7Skxk9LLjbkX7ks0IkA46gzyaqjTgxPHeuD3vWuPUinHEgRIMqg\n
gVjdN5kl81oEN53kFAR71lf4wAtfeWlowxxH7ZCG7HFOGb0/yT9QJa0nUES5UoD6\n
FVJjRgm9/8o7YqAx5jU1TcTrXzv8yAsCSHk7hf+WN40OMJ1Nrd5uRLkKorvVug+Z\n
4ndK+2b4JpPkwNkpexBLZ/xwOBb1EVIR3PIXTVfC6IWDXHxhwvcfy9po/MgFHP9x\n
K+4jSsfj\n
-----END CERTIFICATE-----
mxLogging=FINE
mxTransport=Transport.UDP
mxTargetRate=0
mxAggressiveness=2
mxTrace=-trace
mxBandwidthThrottle=625000
mxRestarts=3
mxStreams=4
mxRetries=3
mxEncryption=UNSIGNED
mxDirHandlingMode=PATH
mxUDPMinimumRate=375000
mxFallbackURLList=mxwan://srv6.somewhere.com|mxtcp://srv6.somewhere.com|http://srv6.somewhere.com:8080