/// <summary> /// Checks whether the given ExternalResourceReference is formatted correctly for this server. /// The format should match one of the formats created in the SetupBrowserData method. /// </summary> public bool IsResourceWellFormed(ExternalResourceReference extRef) { if (extRef.ServerId != GetServerId()) { return(false); } if (!extRef.HasValidDisplayPath()) { return(false); } // Either the ExternalResourceReference has a valid database key (German/French keynote case) ... IDictionary <string, string> refMap = extRef.GetReferenceInformation(); if (refMap.ContainsKey(RefMapDBKeyEntry)) { return(KeynotesDatabase.IsValidDBKey(refMap[RefMapDBKeyEntry])); } else if (refMap.ContainsKey(RefMapLinkPathEntry)) // ... OR it is a Revit link file { return(File.Exists(GetFullServerLinkFilePath(extRef))); } // ... OR it is a keynote file (non- French/German cases). return(File.Exists(GetFullServerKeynoteFilePath(extRef))); }
public bool IsResourceWellFormed(ExternalResourceReference extRef) { bool valid = false; try { if (extRef.ServerId != GetServerId()) { return(false); } if (!extRef.HasValidDisplayPath()) { return(false); } IDictionary <string, string> refMap = extRef.GetReferenceInformation(); if (refMap.ContainsKey("VersionNumber")) { valid = KeynoteDatabase.IsValidRevitVersion(refMap["VersionNumber"]); } } catch (Exception ex) { string message = ex.Message; } return(valid); }
public String GetInSessionPath(ExternalResourceReference resourceReference, String originalPath) { string file = resourceReference.GetReferenceInformation()["File"]; DBUtils.DBItem dbItem = DBUtils.FindDBItem(file); return(ServerName + "://" + FileUtils.GetRelativePath(Application.BaseFolder, dbItem.Path)); }
/// <summary> /// Loads a specified Revit link external resource. /// </summary> /// <param name="resourceReference">An ExternalResourceReference identifying which particular resource to load.</param> /// <param name="loadContent">An ExternalResourceLoadContent object that will be populated with load data by the /// server. There are different subclasses of ExternalResourceLoadContent for different ExternalResourceTypes.</param> private void LoadRevitLink(ExternalResourceReference resourceReference, ExternalResourceLoadContent loadContent) { LinkLoadContent linkLoadContent = (LinkLoadContent)loadContent; if (linkLoadContent == null) { throw new ArgumentException("Wrong type of ExternalResourceLoadContent (expecting a LinkLoadContent) for Revit links.", "loadContent"); } try { // Copy the file from the path under the server "root" folder to a secret "cache" folder on the users machine String fullCachedPath = GetFullLinkCachedFilePath(resourceReference); String cacheFolder = System.IO.Path.GetDirectoryName(fullCachedPath); if (!System.IO.Directory.Exists(cacheFolder)) { System.IO.Directory.CreateDirectory(cacheFolder); } String serverLinkPath = GetFullServerLinkFilePath(resourceReference); System.IO.File.Copy(serverLinkPath, fullCachedPath, true); // Overwrite ModelPath linksPath = ModelPathUtils.ConvertUserVisiblePathToModelPath(fullCachedPath); linkLoadContent.SetLinkDataPath(linksPath); loadContent.LoadStatus = ExternalResourceLoadStatus.Success; } catch (System.Exception) { } }
public String GetInSessionPath(ExternalResourceReference resourceReference, String originalPath) { string file = resourceReference.GetReferenceInformation()["File"]; DBUtils.DBItem dbItem = DBUtils.FindDBItem(file); return(GetPath(dbItem)); }
/// <summary> /// When the user updates the shared coordinates in the link model, Revit calls this /// method. In this implementation, the updated local version of the link on the user's /// machine is uploaded (copied) back to the server location. /// </summary> public void OnLocalLinkSharedCoordinatesSaved(ExternalResourceReference changedReference) { string localLinkPath = SampleExternalResourceDBServer.GetFullLinkCachedFilePath(changedReference); string fullServerPath = SampleExternalResourceDBServer.GetFullServerLinkFilePath(changedReference); String serverDirectoryName = System.IO.Path.GetDirectoryName(fullServerPath); if (!System.IO.Directory.Exists(serverDirectoryName)) { System.IO.Directory.CreateDirectory(serverDirectoryName); } System.IO.File.Copy(localLinkPath, fullServerPath, true); // Overwrite }
/// <summary> /// Computes the full path of a ExternalResourceReference's keynote data file location on the server. /// </summary> /// <param name="extRef">An ExternalResourceReference for a keynote data file stored on this server.</param> /// <returns>A string representing the full path to the Revit link resource on the server drive.</returns> private String GetFullServerKeynoteFilePath(ExternalResourceReference extRef) { String inSessionPath = extRef.InSessionPath; String serverName = GetName(); // As an alternative to using the inSessionPath to determine the actual file path, the // preferred method is to store the relative server path of the file in the // ExternalResourceReference's referenceInformation (as is done for links). // This would be particularly true if you were overriding the default in-session path in // the GetInSessionPath() method. String serverKeynoteFilePath = inSessionPath.Replace(serverName + "://", RootFolder + "\\"); return(serverKeynoteFilePath); }
/// <summary> /// Indicates whether the given version of a resource is the most current /// version of the data. /// </summary> /// <param name="extRef">The ExternalResourceReference to check.</param> /// <returns>An enum indicating whether the resource is current, out of date, or of unknown status</returns> public ResourceVersionStatus GetResourceVersionStatus(ExternalResourceReference extRef) { // Determine whether currently loaded version is out of date, and return appropriate status. String currentlyLoadedVersion = extRef.Version; if (currentlyLoadedVersion == String.Empty) { return(ResourceVersionStatus.Unknown); } return(currentlyLoadedVersion == GetCurrentlyAvailableResourceVersion(extRef) ? ResourceVersionStatus.Current : ResourceVersionStatus.OutOfDate); }
public void LoadResource(Guid loadRequestId, ExternalResourceType resourceType, ExternalResourceReference resourceReference, ExternalResourceLoadContext loadContext, ExternalResourceLoadContent content) { LinkLoadContent linkLoadContent = (LinkLoadContent)content; string file = resourceReference.GetReferenceInformation()["File"]; DBUtils.DBItem dbItem = DBUtils.FindDBItem(file); ModelPath linksPath = ModelPathUtils.ConvertUserVisiblePathToModelPath(dbItem.Path); linkLoadContent.SetLinkDataPath(linksPath); content.LoadStatus = ExternalResourceLoadStatus.Success; return; }
public void LoadResource(Guid loadRequestId, ExternalResourceType resourceType, ExternalResourceReference resourceReference, ExternalResourceLoadContext loadContext, ExternalResourceLoadContent content) { KeyBasedTreeEntriesLoadContent kdrlc = (KeyBasedTreeEntriesLoadContent)content; string tableName = resourceReference.GetReferenceInformation()["TableName"]; DBUtils.GetAllDBItems(tableName).ForEach(item => { kdrlc.AddEntry(new KeynoteEntry(item.Key, item.ParentKey, item.Text)); }); kdrlc.BuildEntries(); kdrlc.LoadStatus = ExternalResourceLoadStatus.Success; return; }
/// <summary> /// <para>Computes the full path on the end user's local drive where an /// ExternalResourceReference's link model will be copied when the user loads the /// link from the server.</para> ///<para>The paths of the local copies relative to this cache root folder will be the /// same as the paths of the original server copies relative to the Revit links /// to the root folder on the (compare this method with the GetFullServerLinkFilePath method).</para> /// </summary> /// <param name="resource">An ExternalResourceReference for a Revit link stored on this server.</param> /// <returns>A string representing the full path to the link model on the end user's local drive.</returns> public static String GetFullLinkCachedFilePath(ExternalResourceReference resource) { if (resource == null) { return(""); } IDictionary <String, String> refMap = resource.GetReferenceInformation(); if (!refMap.ContainsKey(RefMapLinkPathEntry)) { return(""); } return(LocalLinkCacheFolder + resource.GetReferenceInformation()[RefMapLinkPathEntry].Replace("/", "\\")); }
public void ReloadNotes() { KeynoteTable knTable = KeynoteTable.GetKeynoteTable(this.docu); KeyBasedTreeEntries kntableEntries = knTable.GetKeyBasedTreeEntries(); ModelPath p = ModelPathUtils.ConvertUserVisiblePathToModelPath(dialog.tempFilePath); KeyBasedTreeEntriesLoadResults loadResults = new KeyBasedTreeEntriesLoadResults(); ExternalResourceReference s = ExternalResourceReference.CreateLocalResource(this.docu, ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable, p, PathType.Absolute); Transaction t = new Transaction(this.docu, "Reload"); t.Start(); knTable.LoadFrom(s, loadResults); t.Commit(); //ExternalResourceReference exRef = new ExternalResourceReference( //Transaction tr = new Transaction(this.docu, "Reload"); //tr.Start(); }
private string GetCurrentAvailableResourceVersion(ExternalResourceReference extResRef) { string databaseVersion = ""; try { IDictionary <string, string> refMap = extResRef.GetReferenceInformation(); if (refMap.ContainsKey("VersionNumber")) { return(KeynoteDatabase.GetCurrentVersion()); } } catch (Exception ex) { string message = ex.Message; } return(databaseVersion); }
/// <summary> /// Returns a string specifying the version of an external resource available from the server. /// </summary> /// <param name="extResRef">The ExternalResourceReference of the resource whose version is requested.</param> /// <returns>A string containing the version of the specified resource.</returns> private String GetCurrentlyAvailableResourceVersion(ExternalResourceReference extResRef) { IDictionary <string, string> refMap = extResRef.GetReferenceInformation(); if (refMap.ContainsKey(RefMapDBKeyEntry)) { return(KeynotesDatabase.CurrentVersion); } else if (refMap.ContainsKey(RefMapLinkPathEntry)) // ... OR it is a Revit link file { String serverLinkPath = GetFullServerLinkFilePath(extResRef); return(GetFileVersion(serverLinkPath)); } // ... OR it is a keynote file (non- French/German cases). String serverKeynoteFilePath = GetFullServerKeynoteFilePath(extResRef); return(GetFileVersion(serverKeynoteFilePath)); }
/// <summary> /// Loads the keynote data resources, either from the fictitious French/German database, or from a file. /// </summary> /// <param name="resourceReference">An ExternalResourceReference identifying which particular resource to load.</param> /// <param name="loadContent">An ExternalResourceLoadContent object that will be populated with load data by the /// server. There are different subclasses of ExternalResourceLoadContent for different ExternalResourceTypes.</param> private void LoadKeynoteDataResource(ExternalResourceReference resourceReference, ExternalResourceLoadContent loadContent) { KeyBasedTreeEntriesLoadContent kdrlc = (KeyBasedTreeEntriesLoadContent)loadContent; if (kdrlc == null) { throw new ArgumentException("Wrong type of ExternalResourceLoadContent (expecting a KeyBasedTreeEntriesLoadContent) for keynote data.", "loadContent"); } kdrlc.Reset(); // Either the ExternalResourceReference has a valid database key (German/French case) ... IDictionary <string, string> refMap = resourceReference.GetReferenceInformation(); if (refMap.ContainsKey(RefMapDBKeyEntry)) { try { KeynotesDatabase.LoadKeynoteEntries(refMap[RefMapDBKeyEntry], ref kdrlc); kdrlc.BuildEntries(); loadContent.LoadStatus = ExternalResourceLoadStatus.Success; } catch (ArgumentOutOfRangeException) { } catch (ArgumentNullException) { } } else { // ... OR it is a real file (all other cases). String serverKeynoteFilePath = GetFullServerKeynoteFilePath(resourceReference); bool doesReadingSucceed = KeynoteEntries.LoadKeynoteEntriesFromFile(serverKeynoteFilePath, kdrlc); if (doesReadingSucceed) { kdrlc.BuildEntries(); loadContent.LoadStatus = ExternalResourceLoadStatus.Success; } } }
private void LoadKeynoteDataResource(ExternalResourceReference resourceReference, ExternalResourceLoadContent loadContent) { try { KeyBasedTreeEntriesLoadContent entriesContent = (KeyBasedTreeEntriesLoadContent)loadContent; if (null == entriesContent) { throw new ArgumentException("Wrong type of ExternalResourceLoadContent (expecting a KeyBasedTreeEntriesLoadContent) for keynote data.", "loadContent"); } entriesContent.Reset(); IDictionary <string, string> refMap = resourceReference.GetReferenceInformation(); if (refMap.ContainsKey("VersionNumber") && refMap.ContainsKey("ProjectName")) { KeynoteDatabase.LoadKeynoteEntries(refMap, ref entriesContent); entriesContent.BuildEntries(); loadContent.LoadStatus = ExternalResourceLoadStatus.Success; } } catch (Exception ex) { string message = ex.Message; } }
/// <summary> /// Link in the new created document to parent document. /// </summary> /// <param name="originalIFCFileName">The full path to the original IFC file. Same as baseLocalFileName if the IFC file is not on a server.</param> /// <param name="baseLocalFileName">The full path to the IFC file on disk.</param> /// <param name="ifcDocument">The newly imported IFC file document.</param> /// <param name="originalDocument">The document to contain the IFC link.</param> /// <param name="useExistingType">True if the RevitLinkType already exists.</param> /// <param name="doSave">True if we should save the document. This should only be false if we are reusing a cached document.</param> /// <returns>The element id of the RevitLinkType for this link operation.</returns> public static ElementId LinkInFile(string originalIFCFileName, string baseLocalFileName, Document ifcDocument, Document originalDocument, bool useExistingType, bool doSave) { bool saveSucceded = true; string fileName = GenerateRevitFileName(baseLocalFileName); if (doSave) { SaveAsOptions saveAsOptions = new SaveAsOptions(); saveAsOptions.OverwriteExistingFile = true; try { ifcDocument.SaveAs(fileName, saveAsOptions); } catch { saveSucceded = false; } if (!saveSucceded) { try { string tempPathDir = Path.GetTempPath(); string fileNameOnly = Path.GetFileName(fileName); string intermediateFileName = tempPathDir + fileNameOnly; ifcDocument.SaveAs(tempPathDir + fileNameOnly, saveAsOptions); File.Copy(intermediateFileName, fileName); Application application = ifcDocument.Application; ifcDocument.Close(false); ifcDocument = application.OpenDocumentFile(fileName); File.Delete(intermediateFileName); saveSucceded = true; } catch (Exception ex) { // We still want to close the document to prevent having a corrupt model in memory. saveSucceded = false; Importer.TheLog.LogError(-1, ex.Message, false); } } } if (!ifcDocument.IsLinked) { ifcDocument.Close(false); } ElementId revitLinkTypeId = ElementId.InvalidElementId; if (!saveSucceded) { return(revitLinkTypeId); } bool doReloadFrom = useExistingType && !Importer.TheOptions.CreateLinkInstanceOnly; if (Importer.TheOptions.RevitLinkFileName != null) { FilePath originalRevitFilePath = new FilePath(Importer.TheOptions.RevitLinkFileName); revitLinkTypeId = RevitLinkType.GetTopLevelLink(originalDocument, originalRevitFilePath); } ModelPath path = ModelPathUtils.ConvertUserVisiblePathToModelPath(originalIFCFileName); // Relative path type only works if the model isn't in the cloud. As such, we'll try again if the // routine returns an exception. ExternalResourceReference ifcResource = null; for (int ii = 0; ii < 2; ii++) { PathType pathType = (ii == 0) ? PathType.Relative : PathType.Absolute; try { ifcResource = ExternalResourceReference.CreateLocalResource(originalDocument, ExternalResourceTypes.BuiltInExternalResourceTypes.IFCLink, path, pathType); break; } catch { ifcResource = null; } } if (ifcResource == null) { Importer.TheLog.LogError(-1, "Couldn't create local IFC cached file. Aborting import.", true); } if (!doReloadFrom) { Transaction linkTransaction = new Transaction(originalDocument); linkTransaction.Start(Resources.IFCLinkFile); try { if (revitLinkTypeId == ElementId.InvalidElementId) { RevitLinkOptions options = new RevitLinkOptions(true); LinkLoadResult loadResult = RevitLinkType.CreateFromIFC(originalDocument, ifcResource, fileName, false, options); if ((loadResult != null) && (loadResult.ElementId != ElementId.InvalidElementId)) { revitLinkTypeId = loadResult.ElementId; } } if (revitLinkTypeId != ElementId.InvalidElementId) { RevitLinkInstance.Create(originalDocument, revitLinkTypeId); } Importer.PostDelayedLinkErrors(originalDocument); linkTransaction.Commit(); } catch (Exception ex) { linkTransaction.RollBack(); throw ex; } } else // reload from { // For the reload from case, we expect the transaction to have been created in the UI. if (revitLinkTypeId != ElementId.InvalidElementId) { RevitLinkType existingRevitLinkType = originalDocument.GetElement(revitLinkTypeId) as RevitLinkType; if (existingRevitLinkType != null) { existingRevitLinkType.UpdateFromIFC(originalDocument, ifcResource, fileName, false); } } } return(revitLinkTypeId); }
/// <summary> /// Servers can override the name for UI purposes, but here we just return the names that we /// used when we first created the Resources in SetupBrowserData(). /// </summary> public String GetInSessionPath(ExternalResourceReference err, String savedPath) { return(savedPath); }
/// <summary> /// Loads the resources. /// </summary> /// <param name="loadRequestId">A GUID that uniquely identifies this resource load request from Revit.</param> /// <param name="resourceType">The type of external resource that Revit is asking the server to load.</param> /// <param name="resourceReference">An ExternalResourceReference identifying which particular resource to load.</param> /// <param name="loadContext">Context information, including the name of Revit document that is calling the server, /// </param>the resource that is currently loaded and the type of load operation (automatic or user-driven). /// <param name="loadContent">An ExternalResourceLoadContent object that will be populated with load data by the /// server. There are different subclasses of ExternalResourceLoadContent for different ExternalResourceTypes.</param> public void LoadResource(Guid loadRequestId, ExternalResourceType resourceType, ExternalResourceReference resourceReference, ExternalResourceLoadContext loadContext, ExternalResourceLoadContent loadContent) { loadContent.LoadStatus = ExternalResourceLoadStatus.Failure; // The following checks can help with testing. However, they should not be necessary, since Revit checks all input paramters // before calling this method. if (loadRequestId == null) { throw new ArgumentNullException("loadRequestId"); } ; if (resourceType == null) { throw new ArgumentNullException("resourceType"); } ; if (resourceReference == null) { throw new ArgumentNullException("resourceReference"); } ; if (loadContext == null) { throw new ArgumentNullException("loadContext"); } ; if (loadContent == null) { throw new ArgumentNullException("loadContent"); } ; if (!SupportsExternalResourceType(resourceType)) { throw new ArgumentOutOfRangeException("resourceType", "The specified resource type is not supported by this server."); } // The server indicates what version of the resource is being loaded. loadContent.Version = GetCurrentlyAvailableResourceVersion(resourceReference); // resourceReference is for Keynote Data if (resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable) { LoadKeynoteDataResource(resourceReference, loadContent); } else // resourceReference is a Revit Link { LoadRevitLink(resourceReference, loadContent); } }
public ResourceVersionStatus GetResourceVersionStatus(ExternalResourceReference reference) { throw new NotImplementedException(); }
public string GetInSessionPath(ExternalResourceReference reference, string originalDisplayPath) { return(originalDisplayPath); }
public void LoadResource(Guid loadRequestId, ExternalResourceType resourceType, ExternalResourceReference desiredResource, ExternalResourceLoadContext loadContext, ExternalResourceLoadContent loadResults) { try { loadResults.LoadStatus = ExternalResourceLoadStatus.Failure; if (loadRequestId == null) { throw new ArgumentNullException("loadRequestId"); } ; if (resourceType == null) { throw new ArgumentNullException("resourceType"); } ; if (desiredResource == null) { throw new ArgumentNullException("resourceReference"); } ; if (loadContext == null) { throw new ArgumentNullException("loadContext"); } ; if (loadResults == null) { throw new ArgumentNullException("loadContent"); } ; if (!SupportsExternalResourceType(resourceType)) { throw new ArgumentOutOfRangeException("resourceType", "The specified resource type is not supported by this server."); } loadResults.Version = GetCurrentAvailableResourceVersion(desiredResource); if (resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable) { LoadKeynoteDataResource(desiredResource, loadResults); } } catch (Exception ex) { string message = ex.Message; } }
/// <summary> /// This implementation simply retrieves the same local file name that the server /// uses when first copying the link document to the user's machine. /// </summary> public string GetLocalPathForOpen(ExternalResourceReference desiredReference) { return(SampleExternalResourceDBServer.GetFullLinkCachedFilePath(desiredReference)); }
public bool IsResourceWellFormed(ExternalResourceReference extRef) => true;
public String GetInSessionPath(ExternalResourceReference resourceReference, String originalPath) { return(ServerName + "://" + resourceReference.GetReferenceInformation()["TableName"] + ".txt"); }
public ResourceVersionStatus GetResourceVersionStatus(ExternalResourceReference err) => ResourceVersionStatus.OutOfDate;
/// <summary> /// Reports the results of loads from the DB server (SampleExternalResourceServer). /// This method should be implemented to provide UI to communicate success or failure /// of a particular external resource load operation to the user. /// </summary> /// <param name="doc">The Revit model into which the External Resource was loaded. /// </param> /// <param name="loadDataList">Contains a list of ExternalResourceLoadData with results /// for all external resources loaded by the DB server. It is possible for the DB server /// to have loaded more than one resource (for example, loading several linked files /// when a host file is opened by the user). /// </param> public void HandleLoadResourceResults(Document doc, IList <ExternalResourceLoadData> loadDataList) { foreach (ExternalResourceLoadData data in loadDataList) { ExternalResourceType resourceType = data.ExternalResourceType; // This message will be posted in a dialog box at the end of this method. String myMessage = String.Empty; ExternalResourceLoadContext loadContext = data.GetLoadContext(); ExternalResourceReference desiredRef = data.GetExternalResourceReference(); ExternalResourceReference currentlyLoadedRef = loadContext.GetCurrentlyLoadedReference(); LoadOperationType loadType = loadContext.LoadOperationType; switch (loadType) { case LoadOperationType.Automatic: myMessage = "This is an Automatic load operation. "; break; case LoadOperationType.Explicit: myMessage = "This is an Explicit load operation. "; break; default: myMessage = "There is no load type information!! "; break; } bool bUnrecognizedStatus = false; if (data.LoadStatus == ExternalResourceLoadStatus.ResourceAlreadyCurrent) { if (data.GetLoadContext().LoadOperationType == LoadOperationType.Explicit) { string resourcePath = currentlyLoadedRef.InSessionPath; myMessage += "\n No new changes to load for link: " + resourcePath; } else { continue; } } else if (data.LoadStatus == ExternalResourceLoadStatus.Uninitialized) { myMessage += "\n The load status is uninitialized - this generally shouldn't happen"; } else if (data.LoadStatus == ExternalResourceLoadStatus.Failure) { myMessage += "\n The load failed and the reason is unknown."; } else if (data.LoadStatus == ExternalResourceLoadStatus.Success) { if (resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable) { string resourcePath = data.GetExternalResourceReference().InSessionPath; myMessage += "\n Version " + data.GetLoadContent().Version + " of keynote data \'" + resourcePath + "\' has been loaded successfully"; } else if (resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.RevitLink) { string resourcePath = data.GetExternalResourceReference().InSessionPath; LinkLoadContent ldrlc = (LinkLoadContent)(data.GetLoadContent()); string destinationPath = ModelPathUtils.ConvertModelPathToUserVisiblePath(ldrlc.GetLinkDataPath()); myMessage += "\n Version " + data.GetLoadContent().Version + " of the file: " + resourcePath + " has been downloaded into the cached folder: " + destinationPath + " for this Revit Link."; } } else { myMessage += "Unrecognized external resource load status."; bUnrecognizedStatus = true; } if (!bUnrecognizedStatus && resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.RevitLink) { // For Revit links, the UI server can also obtain a RevitLinkLoadResult which contains detailed // information about the status of the attempt to load the local copy of the link into Revit. LinkLoadContent ldrlc = (LinkLoadContent)(data.GetLoadContent()); LinkLoadResult loadResult = ldrlc.GetLinkLoadResult(); if (loadResult != null) { myMessage += "\n LinkLoadResultType: " + loadResult.LoadResult.ToString("g"); } } System.Windows.Forms.MessageBox.Show(myMessage, "UI Server for SDK Sample External Resource Server"); } }