URL Schemes

General Description

Remoting SDK Client Channels and Remote Services (and by extension Data Abstract's Remote Data Adapters can use URL schemes to automatically determine the right parameters to establish a connection to the server, as specified in RFCs 1808, 1738, and 2732. These parameters include channel/protocol type, host name or IP address (both IPv4 and IPv6 are supported), port and message type identifier (dispatcher). In the generalized form, the URL can be written as:

protocol://host:port/message

The meaning of the URL parts and their possible values are:

  • protocol – The protocol used to communicate with the server. Currently the following protocols are supported (note: the actual protocol support may be limited on ceratin platforms):
Value Channel
http:// Plain HTTP channel
https:// Plain HTTP channel, SSL-enabled
superhttp:// Super HTTP channel
superhttps:// Super HTTP channel, SSL-enabled
tcp:// Plain TCP channel
tcps:// Plain TCP channel, SSL-enabled
supertcp:// Super TCP channel
supertcps:// Super TCP channel, SSL-enabled
  • host – Host name or IP address to connect to. IPv6 addresses (which contain colons) must be surrounded with square brackets. Square brackets are also allowed for IPv4 addresses and host names, but not required.
  • port – A decimal number specifying the TCP port to connect to.
  • message - A string identifier of the message type used to communicate with the server. Currently supported message types are:
Value Message
bin RO binary message
soap SOAP Message
json JSON Message
post Post Message
xmlrpc XML-RPC Message

Usage

 

Delphi

URLs are represented using the TROUri class. But for convenience, all operations are overloaded to accept URLs as a string, as well. The TROUri class allows to parse string URLs and get or set URL parts using the set of properties.

fCurrentTargetUri := TROUri.Create;
fCurrentTargetUri.Protocol := 'http';
fCurrentTargetUri.Host := 'localhost';
fCurrentTargetUri.Port := 8089;
fCurrentTargetUri.Path := 'bin';
{ An alternative way }
fCurrentTargetUri := TROUri.Create;
fCurrentTargetUri.URI := 'http://localhost:8089/bin';
{ One more alternative way }
fCurrentTargetUri := TROUri.Create('http://localhost:8089/bin');

To take advantage of URL Schemes, you can use the TROUri object to create the appropriate messages, channels and proxies. In case of the TROTransportChannel, the channel is created with all required target-related properties (e.g. TargetUrl, host name and port number), already set up.

var
  channel: TROTransportChannel;
  message: TROMessage;
  uri: TROUri;
  { Types below are defined in the corresponding _Intf unit }
  proxy: TYourServiceProxy;
  proxyIntf: IYourService;
begin
  uri := TROUri.Create('http://localhost:8089/bin');
  message := TROMessage.MessageMatchingTargetUri(uri);
  channel := TROTransportChannel.ChannelMatchingTargetUri(uri);
    
  { Or even fully ready proxy object in one line of code }
  proxy := TYourServiceProxy.Create(uri);
  proxyIntf := CoYourService.Create(uri);
end;    

You can also use TROUri to set up the TRORemoteService object. It is not required to create the channel and message to use with the RemoteService, as they are created automatically by the call below:

var
  uri: TROUri;
begin
  // ...
  RemoteService.SetUpForTargetUrl(uri);
end;    


It is important to understand how the RO SDK chooses which class to use when it needs to create a channel for a protocol (for example, we have 3 different HTTP client channel implementations to choose from). The TROUrlSchemaParser class is responsible for holding associations between protocol name (e.g. http) and client channel class. This class is used via the ROUrlSchemaParser as singleton. The initialization of these associations are performed in the initialization sections of the corresponding units. For example, the Indy HTTP channel unit uROIndyHTTPChannel.pas:

initialization
  RegisterTransportChannelClass(TROIndyHTTPChannel);
  ROUrlSchemaParser.ProtocolHandler[URL_PROTOCOL_HTTP] := TROIndyHTTPChannel;
  ROUrlSchemaParser.ProtocolHandler[URL_PROTOCOL_HTTPS] := TROIndyHTTPChannel;

Similar code is written for Synapse based channels. The only difference is that it checks if the association exists. So Indy is preferred over Synapse by default, if you use both in your project.
Any of these associations can be overridden by user code.


 

.NET

URLs are represented using the System.Uri class. But for convenience, all operations are overloaded to accept URLs as string, as well.
To take advantage of URL Schemes, use the Uri object to create the appropriate messages, channels and proxies. The ClientChannel is created with all required target-related properties (e.g. TragetUrl, host name and port number), already set up.

System.Uri uri = new System.Uri("http://localhost:8099/bin");

// Creating a message and channel
RemObjects.SDK.IMesssage message = RemObjects.SDK.Message.MessageMatchingTargetUri(uri);
RemObjects.SDK.IClientChannel channel = RemObjects.SDK.ClientChannel.ChannelMatchingTargetUri(uri);

// Or a proxy with a single line of code
var proxy = new YourServiceServer.YourService_Proxy(url);
var proxyIntf = CoYourService.Create(url);


Inside the RO SDK library, associations between protocol (message) name and the implementing class is stored inside the ClientChannelTypeManager and MessageTypeManager static classes. Defaults for which class to use are controlled with attributes. For example, the declaration of the IpHttpClientChannel class looks like this:

[ClientChannel, Default, Protocol("http"), Protocol("https")]
public class IpHttpClientChannel : IpClientChannel, IHttpClientChannel, IRodlAccess
{
  // ...
}

Two Protocol attributes make this channel the default choice for both HTTP and HTTPS protocols. Notice: Internet Pack based channels are used by default for all protocols. If you need to change the default setting, you can use the appropriate method of the ClientChannelTypeManager class:

public static void OverrideClientChannelForProtocol(String protocol, Type type)


 

Cocoa

RO and DA for Cocoa APIs use the Foundation NSURL class to work with URLs. When dealing with URLs in string form, they can be converted to an NSRUL by calling:

NSURL *targetUrl = [NSURL urlWithString:@"superhttp://192.168.1.9:8099/bin"]

For Remoting SDK use, you can instantiate your proxy (or asynchronous proxy) objects using the proxyWithURL: or alloc/initWithURL: messages:

ConcreteProxy *proxy = [MyService_Proxy proxyWithURL:targetUrl];

or

ConcreteProxy *proxy = [[MyService_Proxy alloc] initWithURL:targetUrl];

For Data Abstract, you can similarly use adapterWithTargetURL: or alloc/initWithTargetURL: to create a DARemoteDataAdapter. The RDA will automatically create the underlying Remote Service, Client Channel and Message, and initialize them with DataService and LoginService as service names.

If you are working on a lower level, you can also manually create Client Channel or Message for a specific URL, using the following static methods:

ROClientChannel *channel = [ROClientChannel channelWithTargetURL:targetURL];
ROMessage *message = [ROMessage messageMatchingTargetURL:targetURL];

Note that calling the channelWithTargetURL: and messageMatchingTargetURL: class methods on a concrete channel and message class will create an instance of the respective concrete channel class, possibly disregarding the protocol. Also note that passing a URL with an unsupported or unknown protocol type will throw an exception. Unknown path values will default to ROBinMessage.

Note for iOS users
SOAP messaging support (ROSoapMessage instances creation) is not possible under iOS, unless the libxml2 is linked. Please set up the necessary linker options in your project if you plan to support SOAP messaging.



 

Java

RO and DA for Java APIs use the URI class from the java.net namespace, which can be initialized with a string, for example. The URI can be passed your Remoting SDK proxy objects constructors:

 

var lService := new MyService_Proxy(new URI("http://127.0.0.1:8099/bin"));

 

MyService_Proxy lService = new MyService_Proxy(new URI("http://127.0.0.1:8099/bin"));

For Data Abstract, you can use the RemoteDataAdapter.create() static methods to prepare a new instance of the adapter with message, client channel, and service instances created under the hood.

 

var adapter := RemoteDataAdapter.create(new URI("http://127.0.0.1:8099/bin"));

 

RemoteDataAdapter adapter = RemoteDataAdapter.create(new URI("http://127.0.0.1:8099/bin"));

By default, DataService and LoginService names are used to talk to data and login services, respectively. To customize names, you can use the following create() overloads:

 

var adapter := RemoteDataAdapter.create(new URI("http://127.0.0.1:8099/bin"), "MyDataService");
// or
var adapter := RemoteDataAdapter.create(new URI("http://127.0.0.1:8099/bin"), "MyDataService", "MyLoginService");

 

RemoteDataAdapter adapter = RemoteDataAdapter.create(new URI("http://127.0.0.1:8099/bin"), "MyDataService");
// or
RemoteDataAdapter adapter = RemoteDataAdapter.create(new URI("http://127.0.0.1:8099/bin"), "MyDataService", "MyLoginService");

 

JavaScript

RO

var svc = new MyService("http://127.0.0.1:8099/bin");
var svc = new MyService(channel, message); //old api


DA

By default, DataService, LoginService and Bin2DataStreamer are used;

var rda = new RemObjects.DataAbstract.RemoteDataAdapter("http://127.0.0.1:8099/bin");
var rda = new RemObjects.DataAbstract.RemoteDataAdapter("http://127.0.0.1:8099/bin", RemObjects.DataAbstract.JSONDataStreamer); //streamer class may be passed to other overloads too
var rda = new RemObjects.DataAbstract.RemoteDataAdapter("http://127.0.0.1:8099/bin", "MyDataService");
var rda = new RemObjects.DataAbstract.RemoteDataAdapter("http://127.0.0.1:8099/bin", "MyDataService", "MyLoginService");