How to get the thin client URI of an Alfresco folder/document? - alfresco

A thin client URI is a web address that you can type to see details about a file or folder, on a nice web user interface.
For instance, my Android app uses Alfresco's CMIS API, but for complex operations (eg. to start a workflow on this file), you could click on a link and it would bring you to the fully-fledged web interface (provided by the Alfresco server).
How to calculate this thin client URI, for any Alfresco folder/document?
A good start is to use the thinClientURI feature of the CMIS protocol. Unfortunately it only work for the root of the repository.
A perfect algorithm would show Alfresco Share nodes in their Alfresco Share site, rather than in Share's generic Repository Browser.

Alfresco does have a little known feature to do just what you need! I believe it was implemented in Enterprise 4.0.3ish, ready for 4.1, and the main use of it so far is in Cloud Sync.
The webscript you're looking for is org.alfresco.repository.site.site-share-view-url.get and it is exposed as /api/sites/shareUrl?nodeRef=nodeRef . It returns a simple bit of JSON, such as:
{
"site": "alfresco-test",
"url": "https:\/\/my.alfresco.com\/share\/test.com\/page\/site\/alfresco-test\/document-details?nodeRef=workspace:\/\/SpacesStore\/aae3b33fd-23d4-4091-ae64-44a8e332091341"
}
(The above example is taken from the Alfresco cloud version, but it should be present in late 4.0 enterprise releases, enterprise 4.1, and community + enterprise 4.2 onwards)
If you want to see what kinds of content it supports, your best bet is to look at the java class which powers it, org.alfresco.repo.web.scripts.site.SiteShareViewUrlGet
However, one slight restriction is that it only supports nodes that are located within sites. If you have a non-site node, you'll have to calculate a repository browser URL for it yourself...

Below is my current implementation.
It is (very) far from perfect, as it only works for folders, and only in Alfresco Share.
string suffix1 = "alfresco/cmisatom";
string suffix2 = "alfresco/service/cmis";
if (repo.Address.AbsoluteUri.EndsWith(suffix1) || repo.Address.AbsoluteUri.EndsWith(suffix2))
{
// Detect suffix length.
int suffixLength = 0;
if (repo.Address.AbsoluteUri.EndsWith(suffix1))
suffixLength = suffix1.Length;
if (repo.Address.AbsoluteUri.EndsWith(suffix2))
suffixLength = suffix2.Length;
string root = repo.Address.AbsoluteUri.Substring(0, repo.Address.AbsoluteUri.Length - suffixLength);
if (repo.RemotePath.StartsWith("/Sites"))
{
// Case of Alfresco Share.
// Example RemotePath: /Sites/thesite
// Result: http://server/share/page/site/thesite/documentlibrary
// Example RemotePath: /Sites/thesite/documentLibrary/somefolder/anotherfolder
// Result: http://server/share/page/site/thesite/documentlibrary#filter=path|%2Fsomefolder%2Fanotherfolder
// Example RemotePath: /Sites/s1/documentLibrary/éß和ệ
// Result: http://server/share/page/site/s1/documentlibrary#filter=path|%2F%25E9%25DF%25u548C%25u1EC7
// Example RemotePath: /Sites/s1/documentLibrary/a#bc/éß和ệ
// Result: http://server/share/page/site/thesite/documentlibrary#filter=path%7C%2Fa%2523bc%2F%25E9%25DF%25u548C%25u1EC7%7C
string path = repo.RemotePath.Substring("/Sites/".Length);
if (path.Contains("documentLibrary"))
{
int firstSlashPosition = path.IndexOf('/');
string siteName = path.Substring(0, firstSlashPosition);
string pathWithinSite = path.Substring(firstSlashPosition + "/documentLibrary".Length);
string escapedPathWithinSite = HttpUtility.UrlEncode(pathWithinSite);
string reescapedPathWithinSite = HttpUtility.UrlEncode(escapedPathWithinSite);
string sharePath = reescapedPathWithinSite.Replace("%252f", "%2F");
return root + "share/page/site/" + siteName + "/documentlibrary#filter=path|" + sharePath;
}
else
{
// Site name only.
return root + "share/page/site/" + path + "/documentlibrary";
}
}
else
{
// Case of Alfresco Web Client. Difficult to build a direct URL, so return root.
return root;
}
}

Related

NSFileProtectionComplete doesn't encrypt the core data file

I am using Xcode 7.3 for iOS 9.3 to try and encrypt a Core Data file. I am trying to use NSPersistentStoreFileProtectionKey and set it to NSFileProtectionComplete to enable the encryption. It is not working for some reason and I can always see the .sqlite file generated by the app and browse through the content in sqlitebrowser or iexplorer. Here is my code :
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
let dict: [NSObject : AnyObject] = [
NSPersistentStoreFileProtectionKey : NSFileProtectionComplete
]
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: dict)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
do {
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
try NSFileManager.defaultManager().setAttributes([NSFileProtectionKey : NSFileProtectionComplete], ofItemAtPath: url.path!)
} catch {
}
do {
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite-wal")
try NSFileManager.defaultManager().setAttributes([NSFileProtectionKey : NSFileProtectionComplete], ofItemAtPath: url.path!)
// try print(NSFileManager.defaultManager().attributesOfFileSystemForPath(String(url)))
} catch {
}
do {
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite-shm")
try NSFileManager.defaultManager().setAttributes([NSFileProtectionKey : NSFileProtectionComplete], ofItemAtPath: url.path!)
// try print(NSFileManager.defaultManager().attributesOfFileSystemForPath(String(url)))
} catch {
}
return coordinator
}()
I have also enabled Data Protection for my target in the "Capabilities". I have regenerated the provisioning profile from the Apple Developer portal and am using that with Enabled Data Protection.
I am also using the following code to check the file attributes of .sqlite , .sqlite-wal and .sqlite-shm files. NSFileProtectionKey is correctly set for all 3 of them.
func checkProtectionForLocalDb(atDir : String){
let fileManager = NSFileManager.defaultManager()
let enumerator: NSDirectoryEnumerator = fileManager.enumeratorAtPath(atDir)!
for path in enumerator {
let attr : NSDictionary = enumerator.fileAttributes!
print(attr)
}
}
I also tried disabling the Journal mode to prevent -wal and -shm files from being created. But I can still read the .sqlite file. Even though the attributes read NSFileProtectionComplete.
As described in the Apple Documentation at Apple Docs under "Protecting Data using On Disk Encryption", I tried to check whether the value of variable protectedDataAvailable changes as shown in the code below
public func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSThread.sleepForTimeInterval(10)
sleep(10)
let dataAvailable : Bool = UIApplication.sharedApplication().protectedDataAvailable
print("Protected Data Available : " + String(dataAvailable))
}
If I check the value without the delay it's set to true but after adding the delay it's set to false. This is kind of encouraging, however, right after, when I download the container, to show the content, it still has .sqlite file that still shows the content when opened in sqlitebrowser.
Ok, I finally understand this.
Using Xcode 7.3.1
Enabling File Protection
Enable File Protection using the Capabilities tab on your app target
If you do not want the default NSFileProtectionComplete, change this setting in the developer portal under your app id
Make sure Xcode has the new provisioning profile this creates.
For protecting files your app creates, that's it.
To protect Core Data, you need to add the NSPersistentStoreFileProtectionKey: NSFileProtectionComplete option to your persistent store.
Example:
var options: [NSObject : AnyObject] = [NSMigratePersistentStoresAutomaticallyOption: true,
NSPersistentStoreFileProtectionKey: NSFileProtectionComplete,
NSInferMappingModelAutomaticallyOption: true]
do {
try coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options)
Testing File Protection
I am not able to test this using a non-jailbroken device connected to a computer. Every attempt to access the device this way requires that I "trust" the computer and I believe that trusted computers are always able to read the phone's data ("Trusted computers can sync with your iOS device, create backups, and access your device's photos, videos, contacts, and other content" - https://support.apple.com/en-us/HT202778). I think the other answers on SO referencing this technique are no longer valid with more recent versions of iOS. Indeed, I am always able to download the container using XCode and view the app's data using iPhone Explorer. So how to test...
1 - Create an archive and ensure that it is has the proper entitlements by running the following on the .app file from the command line:
codesign -d --entitlements :- <path_to_app_binary>
You should see a key/value pair that represents your Data Protection level. In this example, NSFileProtectionComplete:
<key>com.apple.developer.default-data-protection</key>
<string>NSFileProtectionComplete</string>
In addition, I used the following two techniques to satisfy myself that the data protection is indeed working. They both require code changes.
2 - Add some code to verify that the proper NSFileProtectionKey is being set on your files and/or core data store:
NSFileManager.defaultManager().attributesOfItemAtPath(dbPath.path!)
If I print this out on one of my files I get:
["NSFileCreationDate": 2016-10-14 02:06:39 +0000, "NSFileGroupOwnerAccountName": mobile, "NSFileType": NSFileTypeRegular, "NSFileSystemNumber": 16777218, "NSFileOwnerAccountName": mobile, "NSFileReferenceCount": 1, "NSFileModificationDate": 2016-10-14 02:06:39 +0000, "NSFileExtensionHidden": 0, "NSFileSize": 81920, "NSFileGroupOwnerAccountID": 501, "NSFileOwnerAccountID": 501, "NSFilePosixPermissions": 420, "NSFileProtectionKey": NSFileProtectionComplete, "NSFileSystemFileNumber": 270902]
Note the "NSFileProtectionKey": "NSFileProtectionComplete" pair.
3 - Modify the following code and hook it up to some button in your app.
#IBAction func settingButtonTouch(sender: AnyObject) {
updateTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self,
selector: #selector(TabbedOverviewViewController.runTest), userInfo: nil, repeats: true)
registerBackgroundTask()
}
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
var updateTimer: NSTimer?
func registerBackgroundTask() {
backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler {
[unowned self] in
self.endBackgroundTask()
}
assert(backgroundTask != UIBackgroundTaskInvalid)
}
func endBackgroundTask() {
NSLog("Background task ended.")
UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}
func runTest() {
switch UIApplication.sharedApplication().applicationState {
case .Active:
NSLog("App is active.")
checkFiles()
case .Background:
NSLog("App is backgrounded.")
checkFiles()
case .Inactive:
break
}
}
func checkFiles() {
// attempt to access a protected resource, i.e. a core data store or file
}
When you tap the button this code begins executing the checkFiles method every .5 seconds. This should run indefinitely with the app in the foreground or background - until you lock your phone. At that point it should reliably fail after roughly 10 seconds - exactly as described in the description of NSFileProtectionComplete.
We need to understand how Data Protection works.
Actually, you don't even need to enable it. Starting with iOS7, the default protection level is “File Protection Complete until first user authentication.”
This means that the files are not accessible until the user unlocks the device for the first time. After that, the files remain accessible even when the device is locked and until it shuts down or reboots.
The other thing is that you're going to see the app's data on a trusted computer always - regardless of the Data Protection level setting.
However, the data can’t be accessed if somebody tries to read them from the flash drive directly. The purpose of Data Protection is to ensure that sensitive data can’t be extracted from a password-protected device’s storage.
After running this code, I could still access and read the contents written to protectedFileURL, even after locking the device.
do {
try data.write(to: protectedFileURL, options: .completeFileProtectionUnlessOpen)
} catch {
print(error)
}
But that's normal since I ran iExplorer on a trusted computer.
And for the same reason, it's fine if you see your sqlite file.
The situation is different if your device gets lost or stolen. A hacker won't be able to read the sqlite file since it's encrypted. Well, unless he guesses your passcode somehow.
Swift 5.0 & Xcode 11:
Enable "Data Protection" in "Capabilities".
Use the following code to protect a file or folder at a specific path:
// Protects a file or folder + excludes it from backup.
// - parameter path: Path component of the file.
// - parameter fileProtectionType: `FileProtectionType`.
// - returns: True, when protected successful.
static func protectFileOrFolderAtPath(_ path: String, fileProtectionType: FileProtectionType) -> Bool {
guard FileManager.default.fileExists(atPath: path) else { return false }
let fileProtectionAttrs = [FileAttributeKey.protectionKey: fileProtectionType]
do {
try FileManager.default.setAttributes(fileProtectionAttrs, ofItemAtPath: path)
return true
} catch {
assertionFailure("Failed protecting path with error: \(error).")
return false
}
}
(Optional) Use the following code to check whether the file or folder at the specific path is protected (note: This only works on physical devices):
/// Returns true, when the file at the provided path is protected.
/// - parameter path: Path of the file to check.
/// - note: Returns true, for simulators. Simulators do not have hardware file encryption. This feature is only available for real devices.
static func isFileProtectedAtPath(_ path: String) -> Bool {
guard !Environment.isSimulator else { return true } // file protection does not work on simulator!
do {
let attributes = try FileManager.default.attributesOfItem(atPath: path)
if attributes.contains(where: { $0.key == .protectionKey }) {
return true
} else {
return false
}
} catch {
assertionFailure(String(describing: error))
return false
}
}
Rather than encrypt a file at the local level I set NSFileProtectionComplete for the app as a whole.
Create the file 'entitlements.plist' in your apps root folder with the following content.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>DataProtectionClass</key>
<string>NSFileProtectionComplete</string>
</dict>
</plist>
Then if you haven't already done so already (this could be the problem with your file level encryption) enable Data Protection in your apps capabilities.

NexusTargetMappingAuthorizationFilter

I've got a weird one (to me): Using Nexus 2.11.4-01 and a another piece of software (Talend) is interfacing with it.
When Talend tries to talk to Nexus it throws an error, looks like it's trying to hit a URL of the form http://servername:8081/nexus/service/local/repositories/scratch/content which throws a 403 when browsed to with Chrome.
The Nexus logs show:
2015-09-07 15:47:30,396+0000 WARN [qtp131312334-65] admin org.sonatype.nexus.security.filter.authz.NexusTargetMappingAuthorizationFilter - Cannot translate request to Nexus repository path, expected pattern /service/local/repositories/([^/]*)/content/(.*), request: GET http://servername:8081/nexus/service/local/repositories/scratch/content
For any repo that I try, now "scratch" should match the pattern and the source here (for Nexus 2.11.3 admittedly) which I found via some googleing suggests it should work too:
http://grepcode.com/file/repo1.maven.org/maven2/org.sonatype.nexus/nexus-core/2.11.3-01/org/sonatype/nexus/security/filter/authz/NexusTargetMappingAuthorizationFilter.java
private String getResourceStorePath(final ServletRequest request) {
String path = WebUtils.getPathWithinApplication((HttpServletRequest) request);
if (getPathPrefix() != null) {
final Pattern p = getPathPrefixPattern();
final Matcher m = p.matcher(path);
if (m.matches()) {
path = getPathReplacement();
// TODO: hardcoded currently
if (path.contains("#1")) {
path = path.replaceAll("#1", Matcher.quoteReplacement(m.group(1)));
}
if (path.contains("#2")) {
path = path.replaceAll("#2", Matcher.quoteReplacement(m.group(2)));
}
// and so on... this will be reworked to be dynamic
}
else {
// what happens here: router requests are formed as: /KIND/ID/REPO_PATH
// where KIND = {"repositories", "groups", ...}, ID is a repo ID, and REPO_PATH is a repository path
// being here, means we could not even match anything of these, usually having newline in string
// as that's the only thing the "dotSTAR" regex would not match (it would match any other character)
log.warn(formatMessage(request, "Cannot translate request to Nexus repository path, expected pattern {}"), p);
return null;
}
}
return path;
}
So my question is what am I doing wrong, what am I missing?
The solution is that the version of Nexus shipped with Talend 5.6 (and that it is written to interface with) is pretty ancient and that the newer versions of Nexus use a different interface.

Running Angular and Web API in Visual Studio with IIS Express

I have a Visual Studio 2013 solution with a Web API project and a Web UI project (using Angular). I am using IIS Express.
Is there a way to set these projects up so that the Angular code can call the Web API project without hard-coding in the localhost and port number?
return $http.get("http://localhost:1561/api/products")
.then(function (response) {
return response.data;
});
If I hard-code localhost:1561 instead of just using the "/api/products" style I have to manually change the code before deploying to production and change it back to run it during development.
Is there an easier way?
Thanks!
var rootPath;
if (location.hostname === 'localhost') {
rootPath = 'http://localhost:1561';
} else {
rootPath = 'http://' + location.hostname;
}
return $http.get(rootPath + '/api/products')
.then(function (response) {
return response.data;
});
I actually wrote a simple utility method that returns the absolute URL so in your code you just have to type the relative URL. Below is this method which you should be able to call from your JS passing the relative URL (i.e. /api/products) or by converting it into a helper extension method...
// Code
public static string ToAbsoluteUrl(string relativeUrl)
{
if (string.IsNullOrEmpty(relativeUrl))
return relativeUrl;
if (HttpContext.Current == null)
return relativeUrl;
if (relativeUrl.StartsWith("/"))
relativeUrl = relativeUrl.Insert(0, "~");
if (!relativeUrl.StartsWith("~/"))
relativeUrl = relativeUrl.Insert(0, "~/");
var url = HttpContext.Current.Request.Url;
var port = url.Port != 80 ? (":" + url.Port) : String.Empty;
return String.Format("{0}://{1}{2}{3}",
url.Scheme, url.Host, port, VirtualPathUtility.ToAbsolute(relativeUrl));
}
From what I understand, it is not possible to do what I was attempting to do. Here are the options:
1) Use IIS on the local machine. That way you can set up the paths/virtual directories as necessary. This also makes it easier to call the API from other browsers during debugging. Note: This does require that you then run Visual Studio in admin mode from this point forward.
2) Put the two projects into one. For me this was not a valid option because I am need to deliver the UI code completely separate from the API code.
Hope this helps others trying to work with Angular and Web API.

How to specify credentials from a Java Web Service in PTC Windchill PDMLink

I am currently investigating the possibility of using a Java Web Service (as described by the Info*Engine documentation of Windchill) in order to retrieve information regarding parts. I am using Windchill version 10.1.
I have successfully deployed a web service, which I consume in a .Net application. Calls which do not try to access Windchill information complete successfully. However, when trying to retrieve part information, I get a wt.method.AuthenticationException.
Here is the code that runs within the webService (The web service method simply calls this method)
public static String GetOnePart(String partNumber) throws WTException
{
WTPart part=null;
RemoteMethodServer server = RemoteMethodServer.getDefault();
server.setUserName("theUsername");
server.setPassword("thePassword");
try {
QuerySpec qspec= new QuerySpec(WTPart.class);
qspec.appendWhere(new SearchCondition(WTPart.class,WTPart.NUMBER,SearchCondition.LIKE,partNumber),new int[]{0,1});
// This fails.
QueryResult qr=PersistenceHelper.manager.find((StatementSpec)qspec);
while(qr.hasMoreElements())
{
part=(WTPart) qr.nextElement();
partName = part.getName();
}
} catch (AuthenticationException e) {
// Exception caught here.
partName = e.toString();
}
return partName;
}
This code works in a command line application deployed on the server, but fails with a wt.method.AuthenticationException when performed from within the web service. I feel it fails because the use of RemoteMethodServer is not what I should be doing since the web service is within the MethodServer.
Anyhow, if anyone knows how to do this, it would be awesome.
A bonus question would be how to log from within the web service, and how to configure this logging.
Thank you.
You don't need to authenticate on the server side with this code
RemoteMethodServer server = RemoteMethodServer.getDefault();
server.setUserName("theUsername");
server.setPassword("thePassword");
If you have followed the documentation (windchill help center), your web service should be something annotated with #WebServices and #WebMethod(operationName="getOnePart") and inherit com.ptc.jws.servlet.JaxWsService
Also you have to take care to the policy used during deployment.
The default ant script is configured with
security.policy=userNameAuthSymmetricKeys
So you need to manage it when you consume your ws with .Net.
For logging events, you just need to call the log4j logger instantiated by default with $log.debug("Hello")
You can't pre-authenticate server side.
You can write the auth into your client tho. Not sure what the .Net equivilent is, but this works for Java clients:
private static final String USERNAME = "admin";
private static final String PASSWORD = "password";
static {
java.net.Authenticator.setDefault(new java.net.Authenticator() {
#Override
protected java.net.PasswordAuthentication getPasswordAuthentication() {
return new java.net.PasswordAuthentication(USERNAME, PASSWORD.toCharArray());
}
});
}

WCF Client Proxies, Client/Channel Caching in ASP.Net - Code Review

long time ASP.Net interface developer being asked to learn WCF, looking for some education on more architecture related fronts - as its not my strong suit but I'm having to deal.
In our current ASMX world we adopted a model of creating ServiceManager static classes for our interaction with web services. We're starting to migrate to WCF, attempting to follow the same model. At first I was dealing with performance problems, but I've tweaked a bit and we're running smoothly now, but I'm questioning my tactics. Here's a simplified version (removed error handling, caching, object manipulation, etc.) of what we're doing:
public static class ContentManager
{
private static StoryManagerClient _clientProxy = null;
const string _contentServiceResourceCode = "StorySvc";
// FOR CACHING
const int _getStoriesTTL = 300;
private static Dictionary<string, GetStoriesCacheItem> _getStoriesCache = new Dictionary<string, GetStoriesCacheItem>();
private static ReaderWriterLockSlim _cacheLockStories = new ReaderWriterLockSlim();
public static Story[] GetStories(string categoryGuid)
{
// OMITTED - if category is cached and not expired, return from cache
// get endpoint address from FinderClient (ResourceManagement SVC)
UrlResource ur = FinderClient.GetUrlResource(_contentServiceResourceCode);
// Get proxy
StoryManagerClient svc = GetStoryServiceClient(ur.Url);
// create request params
GetStoriesRequest request = new GetStoriesRequest{}; // SIMPLIFIED
Manifest manifest = new Manifest{}; // SIMPLIFIED
// execute GetStories at WCF service
try
{
GetStoriesResponse response = svc.GetStories(manifest, request);
}
catch (Exception)
{
if (svc.State == CommunicationState.Faulted)
{
svc.Abort();
}
throw;
}
// OMITTED - do stuff with response, cache if needed
// return....
}
internal static StoryManagerClient GetStoryServiceClient(string endpointAddress)
{
if (_clientProxy == null)
_clientProxy = new StoryManagerClient(GetServiceBinding(_contentServiceResourceCode), new EndpointAddress(endpointAddress));
return _clientProxy;
}
public static Binding GetServiceBinding(string bindingSettingName)
{
// uses Finder service to load a binding object - our alternative to definition in web.config
}
public static void PreloadContentServiceClient()
{
// get finder location
UrlResource ur = FinderClient.GetUrlResource(_contentServiceResourceCode);
// preload proxy
GetStoryServiceClient(ur.Url);
}
}
We're running smoothly now with round-trip calls completing in the 100ms range. Creating the PreloadContentServiceClient() method and adding to our global.asax got that "first call" performance down to that same level. And you might want to know we're using the DataContractSerializer, and the "Add Service Reference" method.
I've done a lot of reading on static classes, singletons, shared data contract assemblies, how to use the ChannelFactory pattern and a whole bunch of other things that I could do to our usage model...admittedly, some of its gone over my head. And, like I said, we seem to be running smoothly. I know I'm not seeing the big picture, though. Can someone tell me what I've ended up here with regards to channel pooling, proxy failures, etc. and why I should head down the ChannelFactory path? My gut says to just do it, but my head can't comprehend why...
Thanks!
ChannelFactory is typically used when you aren't using Add Service Reference - you have the contract via a shared assembly not generated via a WSDL. Add Service Reference uses ClientBase which is essentially creating the WCF channel for you behind the scenes.
When you are dealing with REST-ful services, WebChannelFactory provides a service-client like interface based off the shared assembly contract. You can't use Add Service Reference if your service only supports a REST-ful endpoint binding.
The only difference to you is preference - do you need full access the channel for custom behaviors, bindings, etc. or does Add Service Reference + SOAP supply you with enough of an interface for your needs.

Resources