#region Disclaimer / License
// Copyright (C) 2009, Kenneth Skovhede
// http://www.hexad.dk, opensource@hexad.dk
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Net;
using System.Xml;
using System.Collections.Specialized;
using System.Collections;
using OSGeo.MapGuide.MaestroAPI.Services;
using OSGeo.MapGuide.ObjectModels.Common;
using System.ComponentModel;
using OSGeo.MapGuide.ObjectModels.MapDefinition;
using OSGeo.MapGuide.MaestroAPI.Mapping;
using OSGeo.MapGuide.ObjectModels.ApplicationDefinition;
using OSGeo.MapGuide.MaestroAPI.Commands;
using OSGeo.MapGuide.MaestroAPI.Resource;
using OSGeo.MapGuide.MaestroAPI.CoordinateSystem;
using OSGeo.MapGuide.MaestroAPI.Serialization;
using OSGeo.MapGuide.MaestroAPI.Exceptions;
using OSGeo.MapGuide.MaestroAPI.Http;
using System.IO;
using OSGeo.MapGuide.ObjectModels.Capabilities;
using System.Text;
using System.Collections.Generic;
using OSGeo.MapGuide.ObjectModels.ApplicationDefinition_1_0_0;
using OSGeo.MapGuide.MaestroAPI.Http.Commands;
using OSGeo.MapGuide.MaestroAPI.Schema;
using OSGeo.MapGuide.MaestroAPI.Feature;
using System.Drawing;
using OSGeo.MapGuide.ObjectModels.FeatureSource;
using OSGeo.MapGuide.MaestroAPI.SchemaOverrides;
using System.Diagnostics;
namespace OSGeo.MapGuide.MaestroAPI
{
///
/// Primary http based connection to the MapGuide Server
///
public class HttpServerConnection : MgServerConnectionBase,
IServerConnection,
IDisposable,
IFeatureService,
IResourceService,
ITileService,
IMappingService,
IDrawingService,
IFusionService,
ISiteService
{
private RequestBuilder m_reqBuilder;
//These only change after server reboot, so it is probably safe to cache them
private FeatureProviderRegistry m_featureProviders = null; //SHARED
private Hashtable m_cachedProviderCapabilities = null; //SHARED
private Version m_siteVersion; //SHARED
private bool mAnonymousUser = false;
internal HttpServerConnection()
: base()
{
m_cachedProviderCapabilities = new Hashtable();
}
internal HttpServerConnection(RequestBuilder builder)
: this()
{
m_reqBuilder = builder;
}
public override NameValueCollection CloneParameters
{
get
{
var nvc = new NameValueCollection();
nvc["Url"] = this.BaseURL;
nvc[CommandLineArguments.Provider] = this.ProviderName;
nvc[CommandLineArguments.Session] = this.SessionID;
return nvc;
}
}
public override string ProviderName
{
get { return "Maestro.Http"; }
}
///
/// Gets whether this connection was initialised with an Anonymous login. If it was, it will return true.
/// If this was not, or it was initialised from an existing session id, then it will return false.
///
public bool IsAnonymous
{
get
{
lock (SyncRoot)
{
return mAnonymousUser;
}
}
}
public const string PARAM_URL = "Url";
public const string PARAM_SESSION = "SessionId";
public const string PARAM_LOCALE = "Locale";
public const string PARAM_UNTESTED = "AllowUntestedVersion";
public const string PARAM_USERNAME = "Username";
public const string PARAM_PASSWORD = "Password";
private ICredentials _cred;
private void InitConnection(Uri hosturl, string sessionid, string locale, bool allowUntestedVersion)
{
m_reqBuilder = new RequestBuilder(hosturl, locale, sessionid);
string req = m_reqBuilder.GetSiteVersion();
SiteVersion sv = null;
try
{
sv = (SiteVersion)DeserializeObject(typeof(SiteVersion), OpenRead(req));
}
catch (Exception ex)
{
sv = null;
bool ok = false;
try
{
//Retry, and append missing path, if applicable
if (!hosturl.ToString().EndsWith("/mapagent/mapagent.fcgi"))
{
string tmp = hosturl.ToString();
if (!tmp.EndsWith("/"))
tmp += "/";
hosturl = new Uri(tmp + "mapagent/mapagent.fcgi");
m_reqBuilder = new RequestBuilder(hosturl, locale, sessionid);
req = m_reqBuilder.GetSiteVersion();
sv = (SiteVersion)DeserializeObject(typeof(SiteVersion), OpenRead(req));
ok = true;
}
}
catch { }
if (!ok) //Report original error
throw new Exception("Failed to connect, perhaps session is expired?\nExtended error info: " + NestedExceptionMessageProcessor.GetFullMessage(ex), ex);
}
if (!allowUntestedVersion)
ValidateVersion(sv);
lock (SyncRoot)
{
m_siteVersion = new Version(sv.Version);
}
}
private void InitConnection(Uri hosturl, string username, string password, string locale, bool allowUntestedVersion)
{
m_reqBuilder = new RequestBuilder(hosturl, locale);
mAnonymousUser = (username == "Anonymous");
_cred = new NetworkCredential(username, password);
string req = m_reqBuilder.CreateSession();
m_username = username;
m_password = password;
try
{
this.RestartSession();
}
catch (Exception ex)
{
throw new Exception("Failed to connect, please check network connection and login information.\nExtended error info: " + NestedExceptionMessageProcessor.GetFullMessage(ex), ex);
}
if (!allowUntestedVersion)
ValidateVersion(this.SiteVersion);
m_username = username;
m_password = password;
}
//This is the constructor used by ConnectionProviderRegistry.CreateConnection
internal HttpServerConnection(NameValueCollection initParams)
: this()
{
if (initParams[PARAM_URL] == null)
throw new ArgumentException("Missing required connection parameter: " + PARAM_URL);
string locale = null;
bool allowUntestedVersion = true;
if (initParams[PARAM_LOCALE] != null)
locale = initParams[PARAM_LOCALE];
if (initParams[PARAM_UNTESTED] != null)
bool.TryParse(initParams[PARAM_UNTESTED], out allowUntestedVersion);
if (initParams[PARAM_SESSION] != null)
{
string sessionid = initParams[PARAM_SESSION];
InitConnection(new Uri(initParams[PARAM_URL]), sessionid, locale, allowUntestedVersion);
}
else //Assuming username/password combination
{
string pwd = initParams[PARAM_PASSWORD] ?? string.Empty;
if (initParams[PARAM_USERNAME] == null)
throw new ArgumentException("Missing required connection parameter: " + PARAM_USERNAME);
InitConnection(new Uri(initParams[PARAM_URL]), initParams[PARAM_USERNAME], pwd, locale, allowUntestedVersion);
}
}
[Obsolete("This will be removed in the future. Use ConnectionProviderRegistry.CreateConnection() instead")]
public HttpServerConnection(Uri hosturl, string sessionid, string locale, bool allowUntestedVersion)
: this()
{
InitConnection(hosturl, sessionid, locale, allowUntestedVersion);
}
[Obsolete("This will be removed in the future. Use ConnectionProviderRegistry.CreateConnection() instead")]
public HttpServerConnection(Uri hosturl, string username, string password, string locale, bool allowUntestedVersion)
: this()
{
InitConnection(hosturl, username, password, locale, allowUntestedVersion);
}
public override string SessionID
{
get { return m_reqBuilder.SessionID; }
}
public override ResourceList GetRepositoryResources(string startingpoint, string type, int depth, bool computeChildren)
{
string req = m_reqBuilder.EnumerateResources(startingpoint, depth, type, computeChildren);
//TODO: Cache?
return (ResourceList)DeserializeObject(typeof(ResourceList), this.OpenRead(req));
}
public override FeatureProviderRegistryFeatureProvider[] FeatureProviders
{
get
{
string req = m_reqBuilder.GetFeatureProviders();
lock (SyncRoot)
{
if (m_featureProviders == null)
m_featureProviders = (FeatureProviderRegistry)DeserializeObject(typeof(FeatureProviderRegistry), this.OpenRead(req));
}
var providers = new FeatureProviderRegistryFeatureProvider[m_featureProviders.FeatureProvider.Count];
int i = 0;
foreach (var p in m_featureProviders.FeatureProvider)
{
providers[i] = p;
i++;
}
return providers;
}
}
public override string TestConnection(string featuresource)
{
string req = m_reqBuilder.TestConnection(featuresource);
string result = string.Empty;
try
{
byte[] x = this.DownloadData(req);
//UGLY: Prune out the '\0' chars
List bytes = new List();
for (int i = 0; i < x.Length; i++)
{
if (x[i] > 0)
{
bytes.Add(x[i]);
}
else
{
break;
}
}
result = Encoding.UTF8.GetString(bytes.ToArray());
}
catch (WebException wex)
{
LogFailedRequest(wex);
if (wex.Response != null)
{
try
{
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
Utility.CopyStream(wex.Response.GetResponseStream(), ms);
result = System.Text.Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length);
}
if (result.ToLower().IndexOf("") > 0)
result = result.Substring(result.ToLower().IndexOf("") + 6);
if (result.ToLower().IndexOf("") > 0)
result = result.Substring(0, result.ToLower().IndexOf(""));
return result;
}
catch
{
}
}
if (wex.InnerException == null)
return wex.Message;
else
return wex.InnerException.Message;
}
catch (Exception ex)
{
result = NestedExceptionMessageProcessor.GetFullMessage(ex);
}
return result;
}
public string TestConnection(string providername, NameValueCollection parameters)
{
string req = m_reqBuilder.TestConnection(providername, parameters);
//System.IO.MemoryStream msx = new System.IO.MemoryStream();
//System.Net.WebRequest reqp = m_reqBuilder.TestConnectionPost(providername, parameters, msx);
try
{
/*msx.Position = 0;
Utility.CopyStream(msx, reqp.GetRequestStream());
reqp.GetRequestStream().Flush();
int f = reqp.GetResponse().GetResponseStream().ReadByte();*/
byte[] x = this.DownloadData(req);
}
catch (WebException wex)
{
LogFailedRequest(wex);
if (wex.Response != null)
{
try
{
string result = "";
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
Utility.CopyStream(wex.Response.GetResponseStream(), ms);
result = System.Text.Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length);
}
if (result.ToLower().IndexOf("") > 0)
result = result.Substring(result.ToLower().IndexOf("") + 6);
if (result.ToLower().IndexOf("") > 0)
result = result.Substring(0, result.ToLower().IndexOf(""));
return result;
}
catch
{
}
}
if (wex.InnerException == null)
return wex.Message;
else
return wex.InnerException.Message;
}
catch (Exception ex)
{
return NestedExceptionMessageProcessor.GetFullMessage(ex);
}
return string.Empty;
}
public override System.IO.Stream GetResourceData(string resourceID, string dataname)
{
string req = m_reqBuilder.GetResourceData(resourceID, dataname);
return this.OpenRead(req);
/*
System.IO.MemoryStream ms = new System.IO.MemoryStream();
Utility.CopyStream(this.OpenRead(req), ms);
#if DEBUG_LASTMESSAGE
using (System.IO.Stream s = System.IO.File.Open("lastSave.xml", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
Utility.CopyStream(ms, s);
#endif
ms.Position = 0;
return ms;
*/
}
public override Stream GetResourceXmlData(string resourceID)
{
ResourceIdentifier.Validate(resourceID, ResourceTypes.FeatureSource);
string req = m_reqBuilder.GetResourceContent(resourceID);
return this.OpenRead(req);
}
public override void SetResourceData(string resourceid, string dataname, ResourceDataType datatype, System.IO.Stream stream, Utility.StreamCopyProgressDelegate callback)
{
//Protect against session expired
if (this.m_autoRestartSession && m_username != null && m_password != null)
this.DownloadData(m_reqBuilder.GetSiteVersion());
//Use the old code path if stream is under 50MB (implying seekable too)
if (stream.CanSeek && stream.Length < 50 * 1024 * 1024)
{
#if DEBUG_LASTMESSAGE
using (System.IO.Stream s = System.IO.File.Open("lastSaveData.bin", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
Utility.CopyStream(stream, s);
#endif
if (stream.CanSeek)
stream.Position = 0;
System.IO.MemoryStream outStream = new System.IO.MemoryStream();
#if DEBUG_LASTMESSAGE
try
{
#endif
System.Net.WebRequest req = m_reqBuilder.SetResourceData(resourceid, dataname, datatype, outStream, stream, callback);
req.Credentials = _cred;
outStream.Position = 0;
//TODO: We need a progress bar for the upload....
req.Timeout = 1000 * 60 * 15;
using (System.IO.Stream rs = req.GetRequestStream())
{
Utility.CopyStream(outStream, rs);
rs.Flush();
}
using (System.IO.Stream resp = req.GetResponse().GetResponseStream())
{
//Do nothing... there is no return value
}
#if DEBUG_LASTMESSAGE
}
catch
{
using (System.IO.Stream s = System.IO.File.OpenWrite("lastPost.txt"))
Utility.CopyStream(outStream, s);
throw;
}
#endif
}
else
{
//Dump to temp file
string tmp = Path.GetTempFileName();
try
{
using (var fw = File.OpenWrite(tmp))
{
Utility.CopyStream(stream, fw);
}
var fi = new FileInfo(tmp);
NameValueCollection nvc = m_reqBuilder.SetResourceDataParams(resourceid, dataname, datatype);
nvc.Add("DATALENGTH", fi.Length.ToString());
HttpUploadFile(m_reqBuilder.HostURI, tmp, "DATA", "application/octet-stream", nvc, callback);
}
finally
{
if (File.Exists(tmp))
{
try
{
File.Delete(tmp);
Debug.WriteLine("Deleted: " + tmp);
}
catch { }
}
}
}
}
//Source: http://stackoverflow.com/questions/566462/upload-files-with-httpwebrequest-multipart-form-data
private void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc, Utility.StreamCopyProgressDelegate callback)
{
Debug.WriteLine(string.Format("Uploading {0} to {1}", file, url));
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url);
wr.ContentType = "multipart/form-data; boundary=" + boundary;
wr.Method = "POST";
//DO NOT BUFFER. Otherwise this will still OOM on really large files
wr.AllowWriteStreamBuffering = false;
wr.KeepAlive = true;
wr.Credentials = _cred;
//Pre-compute request body size
{
long contentLength = 0L;
foreach (string key in nvc.Keys)
{
string formitem = string.Format(formdataTemplate, key, nvc[key]);
byte[] formitembytes = System.Text.Encoding.ASCII.GetBytes(formitem);
contentLength += formitembytes.Length;
contentLength += boundarybytes.Length;
}
contentLength += boundarybytes.Length;
string header = string.Format(headerTemplate, paramName, file, contentType);
byte[] headerbytes = System.Text.Encoding.ASCII.GetBytes(header);
contentLength += headerbytes.Length;
var fi = new FileInfo(file);
contentLength += fi.Length;
contentLength += trailer.Length;
wr.ContentLength = contentLength;
}
using (Stream rs = wr.GetRequestStream())
{
foreach (string key in nvc.Keys)
{
rs.Write(boundarybytes, 0, boundarybytes.Length);
string formitem = string.Format(formdataTemplate, key, nvc[key]);
byte[] formitembytes = System.Text.Encoding.ASCII.GetBytes(formitem);
rs.Write(formitembytes, 0, formitembytes.Length);
}
rs.Write(boundarybytes, 0, boundarybytes.Length);
string header = string.Format(headerTemplate, paramName, file, contentType);
byte[] headerbytes = System.Text.Encoding.ASCII.GetBytes(header);
rs.Write(headerbytes, 0, headerbytes.Length);
FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
/*
byte[] buffer = new byte[4096];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
rs.Write(buffer, 0, bytesRead);
}
*/
Utility.CopyStream(fileStream, rs, callback, 1024);
fileStream.Close();
rs.Write(trailer, 0, trailer.Length);
rs.Close();
WebResponse wresp = null;
try
{
wresp = wr.GetResponse();
Stream stream2 = wresp.GetResponseStream();
StreamReader reader2 = new StreamReader(stream2);
Debug.WriteLine(string.Format("File uploaded, server response is: {0}", reader2.ReadToEnd()));
}
catch (Exception ex)
{
Debug.WriteLine("Error uploading file: " + ex.ToString());
if (wresp != null)
{
wresp.Close();
wresp = null;
}
}
finally
{
wr = null;
}
}
}
public override void SetResourceXmlData(string resourceid, System.IO.Stream content, System.IO.Stream header)
{
bool exists = ResourceExists(resourceid);
System.IO.MemoryStream outStream = new System.IO.MemoryStream();
#if DEBUG_LASTMESSAGE
try
{
#endif
//Protect against session expired
if (this.m_autoRestartSession && m_username != null && m_password != null)
this.DownloadData(m_reqBuilder.GetSiteVersion());
System.Net.WebRequest req = m_reqBuilder.SetResource(resourceid, outStream, content, header);
req.Credentials = _cred;
outStream.Position = 0;
try
{
using (System.IO.Stream rs = req.GetRequestStream())
{
Utility.CopyStream(outStream, rs);
rs.Flush();
}
var wresp = req.GetResponse();
if (wresp is HttpWebResponse)
{
HttpWebResponse httpresp = (HttpWebResponse)wresp;
LogResponse(httpresp);
}
using (System.IO.Stream resp = wresp.GetResponseStream())
{
//Do nothing... there is no return value
}
}
catch (Exception ex)
{
if (typeof(WebException).IsAssignableFrom(ex.GetType()))
LogFailedRequest((WebException)ex);
Exception ex2 = Utility.ThrowAsWebException(ex);
if (ex2 != ex)
throw ex2;
else
throw;
}
#if DEBUG_LASTMESSAGE
}
catch
{
if (outStream.CanSeek)
outStream.Position = 0;
using (System.IO.Stream s = System.IO.File.OpenWrite("lastPost.txt"))
Utility.CopyStream(outStream, s);
throw;
}
#endif
if (exists)
OnResourceUpdated(resourceid);
else
OnResourceAdded(resourceid);
}
private void LogResponse(HttpWebResponse resp)
{
OnRequestDispatched(string.Format("{0:d} {1} {2} {3}", resp.StatusCode, resp.StatusDescription, resp.Method, GetResponseString(resp)));
}
private string GetResponseString(HttpWebResponse resp)
{
if (resp.Method == "GET")
return resp.ResponseUri.AbsolutePath + resp.ResponseUri.Query;
else
return resp.ResponseUri.AbsolutePath;
}
private void LogFailedRequest(WebException ex)
{
var resp = ex.Response as HttpWebResponse;
if (resp != null)
{
OnRequestDispatched(string.Format("{0:d} {1} {2} {3}", resp.StatusCode, resp.StatusDescription, resp.Method, GetResponseString(resp)));
}
}
public IReader ExecuteSqlQuery(string featureSourceID, string sql)
{
ResourceIdentifier.Validate(featureSourceID, ResourceTypes.FeatureSource);
string req = m_reqBuilder.ExecuteSqlQuery(featureSourceID, sql);
return new XmlSqlResultReader(this.OpenRead(req));
}
public IFeatureReader QueryFeatureSource(string resourceID, string schema, string query)
{
return QueryFeatureSource(resourceID, schema, query, null);
}
public IFeatureReader QueryFeatureSource(string resourceID, string schema)
{
return QueryFeatureSource(resourceID, schema, null, null);
}
public IFeatureReader QueryFeatureSource(string resourceID, string schema, string query, string[] columns)
{
return QueryFeatureSource(resourceID, schema, query, columns, null);
}
public IFeatureReader QueryFeatureSource(string resourceID, string schema, string query, string[] columns, NameValueCollection computedProperties)
{
return (IFeatureReader)QueryFeatureSourceCore(false, resourceID, schema, query, columns, computedProperties);
}
private IReader QueryFeatureSourceCore(bool aggregate, string resourceID, string schema, string query, string[] columns, NameValueCollection computedProperties)
{
//The request may execeed the url limit of the server, especially when using GeomFromText('...')
ResourceIdentifier.Validate(resourceID, ResourceTypes.FeatureSource);
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.Net.WebRequest req = m_reqBuilder.SelectFeatures(aggregate, resourceID, schema, query, columns, computedProperties, ms);
req.Timeout = 200 * 1000;
ms.Position = 0;
#if DEBUG
string xq = m_reqBuilder.reqAsUrl(resourceID, schema, query, columns);
#endif
try
{
using (System.IO.Stream rs = req.GetRequestStream())
{
Utility.CopyStream(ms, rs);
rs.Flush();
}
var resp = (HttpWebResponse)req.GetResponse();
LogResponse(resp);
if (aggregate)
return new XmlDataReader(resp.GetResponseStream());
else
return new XmlFeatureReader(resp.GetResponseStream());
}
catch (Exception ex)
{
if (typeof(WebException).IsAssignableFrom(ex.GetType()))
LogFailedRequest((WebException)ex);
try
{
if (this.IsSessionExpiredException(ex) && this.AutoRestartSession && this.RestartSession(false))
return this.QueryFeatureSource(resourceID, schema, query, columns);
}
catch
{
//Throw the original exception, not the secondary one
}
Exception ex2 = Utility.ThrowAsWebException(ex);
if (ex2 != ex)
throw ex2;
else
throw;
}
}
public override IReader AggregateQueryFeatureSource(string resourceID, string schema, string filter, string[] columns)
{
return QueryFeatureSourceCore(true, resourceID, schema, filter, columns, null);
}
public override IReader AggregateQueryFeatureSource(string resourceID, string schema, string filter, NameValueCollection aggregateFunctions)
{
return QueryFeatureSourceCore(true, resourceID, schema, filter, null, aggregateFunctions);
}
public override FeatureSourceDescription DescribeFeatureSource(string resourceID)
{
ResourceIdentifier.Validate(resourceID, ResourceTypes.FeatureSource);
string req = m_reqBuilder.DescribeSchema(resourceID);
try
{
return new FeatureSourceDescription(this.OpenRead(req));
}
catch (Exception ex)
{
if (typeof(WebException).IsAssignableFrom(ex.GetType()))
LogFailedRequest((WebException)ex);
try
{
if (this.IsSessionExpiredException(ex) && this.AutoRestartSession && this.RestartSession(false))
return this.DescribeFeatureSource(resourceID);
}
catch
{
//Throw the original exception, not the secondary one
}
Exception ex2 = Utility.ThrowAsWebException(ex);
if (ex2 != ex)
throw ex2;
else
throw;
}
}
public override FeatureSchema DescribeFeatureSource(string resourceID, string schema)
{
ResourceIdentifier.Validate(resourceID, ResourceTypes.FeatureSource);
string req = m_reqBuilder.DescribeSchema(resourceID, schema);
try
{
var fsd = new FeatureSourceDescription(this.OpenRead(req));
return fsd.Schemas[0];
}
catch (Exception ex)
{
if (typeof(WebException).IsAssignableFrom(ex.GetType()))
LogFailedRequest((WebException)ex);
try
{
if (this.IsSessionExpiredException(ex) && this.AutoRestartSession && this.RestartSession(false))
return this.DescribeFeatureSource(resourceID, schema);
}
catch
{
//Throw the original exception, not the secondary one
}
Exception ex2 = Utility.ThrowAsWebException(ex);
if (ex2 != ex)
throw ex2;
else
throw;
}
}
public void DeleteResourceData(string resourceID, string dataname)
{
string req = m_reqBuilder.DeleteResourceData(resourceID, dataname);
using (System.IO.Stream resp = this.OpenRead(req))
resp.ReadByte();
//Do nothing... there is no return value
}
public ResourceDataList EnumerateResourceData(string resourceID)
{
string req = m_reqBuilder.EnumerateResourceData(resourceID);
using (System.IO.Stream resp = this.OpenRead(req))
return (ResourceDataList)DeserializeObject(typeof(ResourceDataList), resp);
}
public override void DeleteResource(string resourceID)
{
string req = m_reqBuilder.DeleteResource(resourceID);
using (System.IO.Stream resp = this.OpenRead(req))
resp.ReadByte();
//Do nothing... there is no return value
OnResourceDeleted(resourceID);
}
public override Version SiteVersion
{
get
{
lock (SyncRoot)
{
return m_siteVersion;
}
}
}
//For unit testing purposes
internal void SetSiteVersion(Version v)
{
lock (SyncRoot)
{
m_siteVersion = v;
}
}
private ICoordinateSystemCatalog m_coordsys = null;
//TODO: Figure out a strategy for cache invalidation
//TODO: Figure out if this can work with MapGuide EP 1.0 (just exclude it?)
public ICoordinateSystemCatalog CoordinateSystemCatalog
{
get
{
if (this.SiteVersion < OSGeo.MapGuide.MaestroAPI.SiteVersions.GetVersion(OSGeo.MapGuide.MaestroAPI.KnownSiteVersions.MapGuideOS1_1))
return null;
else
{
if (m_coordsys == null)
m_coordsys = new HttpCoordinateSystemCatalog(this, m_reqBuilder);
return m_coordsys;
}
}
}
public System.IO.Stream ExecuteOperation(System.Collections.Specialized.NameValueCollection param)
{
return this.OpenRead(m_reqBuilder.BuildRequest(param));
}
///
/// Returns the Uri for the mapagent
///
public string ServerURI { get { return m_reqBuilder.HostURI; } }
///
/// Gets a string that can be used to identify the server by a user
///
public string DisplayName
{
get
{
string s = m_reqBuilder.HostURI;
if (s.ToLower().EndsWith("/mapagent/mapagent.fcgi"))
s = s.Substring(0, s.Length - "/mapagent/mapagent.fcgi".Length);
else if (s.ToLower().EndsWith("/mapagent/mapagent.exe"))
s = s.Substring(0, s.Length - "/mapagent/mapagent.exe".Length);
/*if (m_wc.Credentials as NetworkCredential != null)
s += " [" + (m_wc.Credentials as NetworkCredential).UserName + "]"; */
return s + " (v" + this.SiteVersion.ToString() + ")";
}
}
public override ResourceReferenceList EnumerateResourceReferences(string resourceid)
{
string req = m_reqBuilder.EnumerateResourceReferences(resourceid);
using (System.IO.Stream resp = this.OpenRead(req))
return (ResourceReferenceList)DeserializeObject(typeof(ResourceReferenceList), resp);
}
public override void CopyResource(string oldpath, string newpath, bool overwrite)
{
bool exists = ResourceExists(newpath);
string req = m_reqBuilder.CopyResource(oldpath, newpath, overwrite);
using (System.IO.Stream resp = this.OpenRead(req))
resp.ReadByte();
//Do nothing... there is no return value
if (exists)
OnResourceUpdated(newpath);
else
OnResourceAdded(newpath);
//HACK: the COPYRESOURCE call does not update timestamps of the target
//if it already exists.
Touch(newpath);
}
public override void CopyFolder(string oldpath, string newpath, bool overwrite)
{
oldpath = FixAndValidateFolderPath(oldpath);
newpath = FixAndValidateFolderPath(newpath);
bool exists = ResourceExists(newpath);
string req = m_reqBuilder.CopyResource(oldpath, newpath, overwrite);
using (System.IO.Stream resp = this.OpenRead(req))
resp.ReadByte();
//Do nothing... there is no return value
if (exists)
OnResourceUpdated(newpath);
else
OnResourceAdded(newpath);
}
public override void MoveResource(string oldpath, string newpath, bool overwrite)
{
bool exists = ResourceExists(newpath);
string req = m_reqBuilder.MoveResource(oldpath, newpath, overwrite);
using (System.IO.Stream resp = this.OpenRead(req))
resp.ReadByte();
//Do nothing... there is no return value
OnResourceDeleted(oldpath);
if (exists)
OnResourceUpdated(newpath);
else
OnResourceAdded(newpath);
}
public override void MoveFolder(string oldpath, string newpath, bool overwrite)
{
oldpath = FixAndValidateFolderPath(oldpath);
newpath = FixAndValidateFolderPath(newpath);
bool exists = ResourceExists(newpath);
string req = m_reqBuilder.MoveResource(oldpath, newpath, overwrite);
using (System.IO.Stream resp = this.OpenRead(req))
resp.ReadByte();
//Do nothing... there is no return value
OnResourceDeleted(oldpath);
if (exists)
OnResourceUpdated(newpath);
else
OnResourceAdded(newpath);
}
public override System.IO.Stream RenderDynamicOverlay(RuntimeMap map, MapSelection selection, string format, bool keepSelection)
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.Net.WebRequest req = m_reqBuilder.GetDynamicMapOverlayImage(map.Name, (selection == null ? string.Empty : selection.ToXml()), format, ms);
//Maksim reported that the rendering times out frequently, so now we wait 5 minutes
req.Timeout = 5 * 60 * 1000;
using (System.IO.Stream rs = req.GetRequestStream())
{
Utility.CopyStream(ms, rs);
rs.Flush();
var resp = req.GetResponse();
var hwr = resp as HttpWebResponse;
if (hwr != null)
LogResponse(hwr);
return resp.GetResponseStream();
}
}
public Stream RenderMapLegend(RuntimeMap map, int width, int height, System.Drawing.Color backgroundColor, string format)
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
string req = m_reqBuilder.RenderMapLegend(map.Name, width, height, ColorTranslator.ToHtml(backgroundColor), format);
return this.OpenRead(req);
}
public override System.IO.Stream RenderRuntimeMap(string resourceId, double x, double y, double scale, int width, int height, int dpi, string format, bool clip)
{
ResourceIdentifier.Validate(resourceId, ResourceTypes.RuntimeMap);
string mapname = resourceId.Substring(resourceId.IndexOf("//") + 2);
mapname = mapname.Substring(0, mapname.LastIndexOf("."));
#if DEBUG
string s = m_reqBuilder.GetMapImageUrl(mapname, format, null, x, y, scale, dpi, width, height, clip, null, null, null, null);
return new System.IO.MemoryStream(this.DownloadData(s));
#else
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.Net.WebRequest req = m_reqBuilder.GetMapImage(mapname, format, null, x, y, scale, dpi, width, height, clip, null, null, null, null, ms);
//Maksim reported that the rendering times out frequently, so now we wait 5 minutes
req.Timeout = 5 * 60 * 1000;
using(System.IO.Stream rs = req.GetRequestStream())
{
Utility.CopyStream(ms, rs);
rs.Flush();
var resp = req.GetResponse();
var hwr = resp as HttpWebResponse;
if (hwr != null)
LogResponse(hwr);
return resp.GetResponseStream();
}
#endif
}
public override System.IO.Stream RenderRuntimeMap(string resourceId, double x1, double y1, double x2, double y2, int width, int height, int dpi, string format, bool clip)
{
ResourceIdentifier.Validate(resourceId, ResourceTypes.RuntimeMap);
string mapname = resourceId.Substring(resourceId.IndexOf("//") + 2);
mapname = mapname.Substring(0, mapname.LastIndexOf("."));
#if DEBUG
string s = m_reqBuilder.GetMapImageUrl(mapname, format, null, x1, y1, x2, y2, dpi, width, height, clip, null, null, null, null);
return new System.IO.MemoryStream(this.DownloadData(s));
#else
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.Net.WebRequest req = m_reqBuilder.GetMapImage(mapname, format, null, x1, y1, x2, y2, dpi, width, height, clip, null, null, null, null, ms);
//Maksim reported that the rendering times out frequently, so now we wait 5 minutes
req.Timeout = 5 * 60 * 1000;
using(System.IO.Stream rs = req.GetRequestStream())
{
Utility.CopyStream(ms, rs);
rs.Flush();
var resp = req.GetResponse();
var hwr = resp as HttpWebResponse;
if (hwr != null)
LogResponse(hwr);
return resp.GetResponseStream();
}
#endif
}
public override bool IsSessionExpiredException(Exception ex)
{
if (ex != null && ex.GetType() == typeof(System.Net.WebException))
{
System.Net.WebException wex = (System.Net.WebException)ex;
if (wex.Message.ToLower().IndexOf("session expired") >= 0 || wex.Message.ToLower().IndexOf("session not found") >= 0 || wex.Message.ToLower().IndexOf("mgsessionexpiredexception") >= 0)
return true;
}
return false;
}
///
/// Returns the avalible application templates on the server
///
/// The avalible application templates on the server
public IApplicationDefinitionTemplateInfoSet GetApplicationTemplates()
{
//TODO: Caching these should be safe
return (IApplicationDefinitionTemplateInfoSet)base.DeserializeObject(typeof(ApplicationDefinitionTemplateInfoSet), this.OpenRead(m_reqBuilder.EnumerateApplicationTemplates()));
}
///
/// Returns the avalible application widgets on the server
///
/// The avalible application widgets on the server
public IApplicationDefinitionWidgetInfoSet GetApplicationWidgets()
{
//TODO: Caching these should be safe
return (IApplicationDefinitionWidgetInfoSet)base.DeserializeObject(typeof(ApplicationDefinitionWidgetInfoSet), this.OpenRead(m_reqBuilder.EnumerateApplicationWidgets()));
}
///
/// Returns the avalible widget containers on the server
///
/// The avalible widget containers on the server
public IApplicationDefinitionContainerInfoSet GetApplicationContainers()
{
//TODO: Caching these should be safe
return (IApplicationDefinitionContainerInfoSet)base.DeserializeObject(typeof(ApplicationDefinitionContainerInfoSet), this.OpenRead(m_reqBuilder.EnumerateApplicationContainers()));
}
///
/// Returns the spatial info for a given featuresource
///
/// The ID of the resource to query
/// Query only active items
/// A list of spatial contexts
public override FdoSpatialContextList GetSpatialContextInfo(string resourceID, bool activeOnly)
{
string req = m_reqBuilder.GetSpatialContextInfo(resourceID, activeOnly);
FdoSpatialContextList o = (FdoSpatialContextList)DeserializeObject(typeof(FdoSpatialContextList), this.OpenRead(req));
return o;
}
///
/// Gets the names of the identity properties from a feature
///
/// The resourceID for the FeatureSource
/// The classname of the feature, including schema
/// A string array with the found identities
public override string[] GetIdentityProperties(string resourceID, string classname)
{
string[] parts = classname.Split(':');
string req;
if (parts.Length == 2)
req = m_reqBuilder.GetIdentityProperties(resourceID, parts[0], parts[1]);
else if (parts.Length == 1)
req = m_reqBuilder.GetIdentityProperties(resourceID, null, parts[0]);
else
throw new Exception("Unable to parse classname into class and schema: " + classname);
XmlDocument doc = new XmlDocument();
doc.Load(this.OpenRead(req));
XmlNodeList lst = doc.SelectNodes("/PropertyDefinitions/PropertyDefinition/Name");
string[] ids = new string[lst.Count];
for(int i = 0; i < lst.Count; i++)
ids[i] = lst[i].InnerText;
return ids;
}
private readonly object SyncRoot = new object();
///
/// Restarts the server session, and creates a new session ID
///
/// If set to true, the call throws an exception if the call failed
/// True if the creation succeed, false otherwise
public override bool RestartSession(bool throwException)
{
if (m_username == null || m_password == null)
if (throwException)
throw new Exception("Cannot recreate session, because connection was not opened with username and password");
else
return false;
Uri hosturl = new Uri(m_reqBuilder.HostURI);
string locale = m_reqBuilder.Locale;
try
{
RequestBuilder reqb = new RequestBuilder(hosturl, locale);
WebClient wc = new WebClient();
wc.Credentials = new NetworkCredential(m_username, m_password);
string req = reqb.CreateSession();
try
{
reqb.SessionID = System.Text.Encoding.Default.GetString(wc.DownloadData(req));
if (reqb.SessionID.IndexOf("<") >= 0)
throw new Exception("Invalid server token recieved: " + reqb.SessionID);
}
catch (Exception ex)
{
reqb.SessionID = null;
bool ok = false;
try
{
//Retry, and append missing path, if applicable
if (!hosturl.ToString().EndsWith("/mapagent/mapagent.fcgi"))
{
string tmp = hosturl.ToString();
if (!tmp.EndsWith("/"))
tmp += "/";
hosturl = new Uri(tmp + "mapagent/mapagent.fcgi");
reqb = new RequestBuilder(hosturl, locale);
req = reqb.CreateSession();
reqb.SessionID = System.Text.Encoding.Default.GetString(wc.DownloadData(req));
if (reqb.SessionID.IndexOf("<") >= 0)
throw new Exception("Invalid server token recieved: " + reqb.SessionID);
ok = true;
}
}
catch {}
if (!ok)
{
if (throwException) //Report original error
{
if (ex is WebException) //These exceptions, we just want the underlying message. No need for 50 bajillion nested exceptions
throw;
else //We don't know what this could be so grab everything
throw new Exception("Failed to connect, perhaps session is expired?\nExtended error info: " + ex.Message, ex);
}
else
return false;
}
}
//Reset cached items
lock (SyncRoot)
{
m_siteVersion = new Version(((SiteVersion)DeserializeObject(typeof(SiteVersion), wc.OpenRead(reqb.GetSiteVersion()))).Version);
m_featureProviders = null;
m_cachedProviderCapabilities = null;
m_reqBuilder = reqb;
}
return true;
}
catch
{
if (throwException)
throw;
else
return false;
}
}
///
/// Downloads data as a byte array. Wrapper function that automatically recreates the session if it has expired.
///
/// The request URI
/// The data at the given location
internal byte[] DownloadData(string req)
{
string prev_session = m_reqBuilder.SessionID;
try
{
var httpreq = HttpWebRequest.Create(req);
var httpresp = (HttpWebResponse)httpreq.GetResponse();
LogResponse(httpresp);
using (var st = httpresp.GetResponseStream())
{
using (var ms = new MemoryStream())
{
Utility.CopyStream(st, ms);
return ms.GetBuffer();
}
}
}
catch (Exception ex)
{
if (typeof(WebException).IsAssignableFrom(ex.GetType()))
LogFailedRequest((WebException)ex);
if (!this.m_autoRestartSession || !this.IsSessionExpiredException(ex) || !this.RestartSession(false))
{
Exception ex2 = Utility.ThrowAsWebException(ex);
if (ex2 != ex)
throw ex2;
else
throw;
}
else
{
//Do not try more than once
req = req.Replace(prev_session, m_reqBuilder.SessionID);
var httpreq = HttpWebRequest.Create(req);
var httpresp = httpreq.GetResponse();
using (var st = httpresp.GetResponseStream())
{
using (var ms = new MemoryStream())
{
Utility.CopyStream(st, ms);
return ms.GetBuffer();
}
}
}
}
}
///
/// Opens a stream for reading. Wrapper function that automatically recreates the session if it has expired.
///
/// The request URI
/// The data at the given location
internal System.IO.Stream OpenRead(string req)
{
string prev_session = m_reqBuilder.SessionID;
try
{
var httpreq = HttpWebRequest.Create(req);
if (_cred != null)
httpreq.Credentials = _cred;
var httpresp = (HttpWebResponse)httpreq.GetResponse();
LogResponse(httpresp);
return httpresp.GetResponseStream();
}
catch (Exception ex)
{
if (typeof(WebException).IsAssignableFrom(ex.GetType()))
LogFailedRequest((WebException)ex);
var sessionRecreated = false;
if (this.IsSessionExpiredException(ex))
sessionRecreated = this.RestartSession(false);
if (!this.m_autoRestartSession || !this.IsSessionExpiredException(ex) || !sessionRecreated)
{
Exception ex2 = Utility.ThrowAsWebException(ex);
if (ex2 != ex)
throw ex2;
else
throw;
}
else
{
req = req.Replace(prev_session, m_reqBuilder.SessionID);
var httpreq = HttpWebRequest.Create(req);
if (_cred != null)
httpreq.Credentials = _cred;
var httpresp = httpreq.GetResponse();
return httpresp.GetResponseStream();
}
}
}
///
/// Enumerates all unmanaged folders, meaning alias'ed folders
///
/// The type of data to return
/// A filter applied to the items
/// True if the list should contains recursive results
/// The path to retrieve the data from
/// A list of unmanaged data
public override UnmanagedDataList EnumerateUnmanagedData(string startpath, string filter, bool recursive, UnmanagedDataTypes type)
{
string req = m_reqBuilder.EnumerateUnmanagedData(startpath, filter, recursive, type);
System.IO.MemoryStream ms = new System.IO.MemoryStream();
using(System.IO.Stream s = this.OpenRead(req))
Utility.CopyStream(s, ms);
ms.Position = 0;
return (UnmanagedDataList)DeserializeObject(typeof(UnmanagedDataList), ms);
}
///
/// Gets the base url, ea.: http://localhost/mapguide/
///
public string BaseURL
{
get
{
string baseurl = this.ServerURI;
if (baseurl.ToLower().EndsWith("/mapagent.fcgi"))
baseurl = baseurl.Substring(0, baseurl.Length - "mapagent.fcgi".Length);
if (baseurl.ToLower().EndsWith("/mapagent/"))
baseurl = baseurl.Substring(0, baseurl.Length - "mapagent/".Length);
else if (baseurl.ToLower().EndsWith("/mapagent"))
baseurl = baseurl.Substring(0, baseurl.Length - "mapagent".Length);
return baseurl;
}
}
///
/// Renders a minature bitmap of the layers style
///
/// The scale for the bitmap to match
/// The layer the image should represent
/// If the layer is themed, this gives the theme index, otherwise set to 0
/// The geometry type, 1 for point, 2 for line, 3 for area, 4 for composite
/// The minature bitmap
public override System.Drawing.Image GetLegendImage(double scale, string layerdefinition, int themeIndex, int type, int width, int height, string format)
{
string param = m_reqBuilder.GetLegendImage(scale, layerdefinition, themeIndex, type, width, height, format);
return new System.Drawing.Bitmap(this.OpenRead(param));
}
///
/// Upload a MapGuide Package file to the server
///
/// Name of the file to upload
/// A callback argument used to display progress. May be null.
public override void UploadPackage(string filename, Utility.StreamCopyProgressDelegate callback)
{
try
{
using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))
{
System.Net.WebRequest req = m_reqBuilder.ApplyPackage(fs, callback);
req.Credentials = _cred;
req.GetRequestStream().Flush();
req.GetRequestStream().Close();
byte[] buf = new byte[1];
System.IO.Stream s = req.GetResponse().GetResponseStream();
s.Read(buf, 0, 1);
s.Close();
}
}
catch (Exception ex)
{
if (typeof(WebException).IsAssignableFrom(ex.GetType()))
LogFailedRequest((WebException)ex);
Exception ex2 = Utility.ThrowAsWebException(ex);
if (ex2 != ex)
throw ex2;
else
throw;
}
}
public override void UpdateRepository(string resourceId, ResourceFolderHeaderType header)
{
try
{
System.Net.WebRequest req = m_reqBuilder.UpdateRepository(resourceId, this.SerializeObject(header));
req.Credentials = _cred;
req.GetRequestStream().Flush();
req.GetRequestStream().Close();
byte[] buf = new byte[1];
System.IO.Stream s = req.GetResponse().GetResponseStream();
s.Read(buf, 0, 1);
s.Close();
}
catch (Exception ex)
{
if (typeof(WebException).IsAssignableFrom(ex.GetType()))
LogFailedRequest((WebException)ex);
Exception ex2 = Utility.ThrowAsWebException(ex);
if (ex2 != ex)
throw ex2;
else
throw;
}
}
public override object GetFolderOrResourceHeader(string resourceID)
{
string req = m_reqBuilder.GetResourceHeader(resourceID);
using(System.IO.Stream s = this.OpenRead(req))
if (ResourceIdentifier.IsFolderResource(resourceID))
return this.DeserializeObject(s);
else
return this.DeserializeObject(s);
}
///
/// Gets a list of users in a group
///
/// The group to retrieve the users from
/// The list of users
public override UserList EnumerateUsers(string group)
{
if (m_cachedUserList == null)
{
string req = m_reqBuilder.EnumerateUsers(group);
using (System.IO.Stream s = this.OpenRead(req))
m_cachedUserList = this.DeserializeObject(s);
}
return m_cachedUserList;
}
///
/// Gets a list of all groups on the server
///
/// The list of groups
public override GroupList EnumerateGroups()
{
if (m_cachedGroupList == null)
{
string req = m_reqBuilder.EnumerateGroups();
using (System.IO.Stream s = this.OpenRead(req))
m_cachedGroupList = this.DeserializeObject(s);
}
return m_cachedGroupList;
}
public override bool ResourceExists(string resourceid)
{
try
{
string req = m_reqBuilder.ResourceExists(resourceid);
using (System.IO.Stream s = this.OpenRead(req))
using (System.IO.StreamReader sr = new System.IO.StreamReader(s))
return sr.ReadToEnd().Trim().Equals("true", StringComparison.InvariantCultureIgnoreCase);
}
catch (Exception ex)
{
try { return base.ResourceExists(resourceid); }
catch { throw ex; } //Throw original error
}
}
public string[] GetConnectionPropertyValues(string providerName, string propertyName, string partialConnectionString)
{
string req = m_reqBuilder.GetConnectionPropertyValues(providerName, propertyName, partialConnectionString);
using (System.IO.Stream s = this.OpenRead(req))
{
OSGeo.MapGuide.ObjectModels.Common.StringCollection strc = this.DeserializeObject(s);
return strc.Item.ToArray();
}
}
#region IDisposable Members
public override void Dispose()
{
lock (SyncRoot)
{
if (m_featureProviders != null)
m_featureProviders = null;
if (m_cachedProviderCapabilities != null)
m_cachedProviderCapabilities = null;
}
}
#endregion
public override System.IO.Stream GetTile(string mapdefinition, string baselayergroup, int col, int row, int scaleindex, string format)
{
string req = string.Empty;
if (mAnonymousUser)
req = m_reqBuilder.GetTileAnonymous(mapdefinition, baselayergroup, row, col, scaleindex, format);
else
req = m_reqBuilder.GetTile(mapdefinition, baselayergroup, row, col, scaleindex, format, _cred == null);
return this.OpenRead(req);
}
///
/// Gets or sets the agent reported to MapGuide.
/// Free form text, will appear in the log files.
/// Default is MapGuide Maestro API
///
public string UserAgent
{
get
{
if (m_reqBuilder != null)
return m_reqBuilder.UserAgent;
return string.Empty;
}
set
{
if (m_reqBuilder != null)
m_reqBuilder.UserAgent = value;
}
}
public bool SupportsResourcePreviews
{
get { return true; }
}
public IFeatureService FeatureService
{
get { return this; }
}
public IResourceService ResourceService
{
get { return this; }
}
public IConnectionCapabilities Capabilities
{
get { return new HttpCapabilities(this); }
}
public IService GetService(int serviceType)
{
ServiceType st = (ServiceType)serviceType;
switch (st)
{
case ServiceType.Drawing:
case ServiceType.Feature:
case ServiceType.Mapping:
case ServiceType.Resource:
case ServiceType.Tile:
case ServiceType.Site:
return this;
case ServiceType.Fusion:
if (this.SiteVersion >= new Version(2, 0))
return this;
break;
}
throw new UnsupportedServiceTypeException(st);
}
public const string PROP_USER_AGENT = "UserAgent";
public const string PROP_BASE_URL = "BaseUrl";
public override string[] GetCustomPropertyNames()
{
return new string[] { PROP_USER_AGENT, PROP_BASE_URL };
}
///
/// Gets or sets the number of worker threads to spawn when initializing
/// a runtime map
///
public int RuntimeMapWorkerCount
{
get;
set;
}
public override Type GetCustomPropertyType(string name)
{
if (name == PROP_USER_AGENT)
return typeof(string);
else if (name == PROP_BASE_URL)
return typeof(string);
else
throw new CustomPropertyNotFoundException();
}
public override void SetCustomProperty(string name, object value)
{
if (name == PROP_USER_AGENT)
this.UserAgent = value.ToString();
else
throw new CustomPropertyNotFoundException();
}
public override object GetCustomProperty(string name)
{
if (name == PROP_USER_AGENT)
return this.UserAgent;
else if (name == PROP_BASE_URL)
return this.BaseURL;
else
throw new CustomPropertyNotFoundException();
}
protected override IServerConnection GetInterface()
{
return this;
}
public System.IO.Stream DescribeDrawing(string resourceID)
{
string req = m_reqBuilder.DescribeDrawing(resourceID);
return this.OpenRead(resourceID);
}
public string[] EnumerateDrawingLayers(string resourceID, string sectionName)
{
string req = m_reqBuilder.EnumerateDrawingLayers(resourceID, sectionName);
using (System.IO.Stream s = this.OpenRead(req))
{
var list = this.DeserializeObject(s);
//Workaround for #1727
var dict = new Dictionary();
foreach (var it in list.Item)
{
if (!dict.ContainsKey(it))
dict.Add(it, it);
}
return new List(dict.Values).ToArray();
}
}
public DrawingSectionResourceList EnumerateDrawingSectionResources(string resourceID, string sectionName)
{
string req = m_reqBuilder.EnumerateDrawingSectionResources(resourceID, sectionName);
using (System.IO.Stream s = this.OpenRead(req))
return this.DeserializeObject(s);
}
public DrawingSectionList EnumerateDrawingSections(string resourceID)
{
string req = m_reqBuilder.EnumerateDrawingSections(resourceID);
using (System.IO.Stream s = this.OpenRead(req))
return this.DeserializeObject(s);
}
public string GetDrawingCoordinateSpace(string resourceID)
{
string req = m_reqBuilder.GetDrawingCoordinateSpace(resourceID);
using (System.IO.StreamReader s = new System.IO.StreamReader(this.OpenRead(req)))
return s.ReadToEnd();
}
public System.IO.Stream GetDrawing(string resourceID)
{
string req = m_reqBuilder.GetDrawing(resourceID);
return this.OpenRead(req);
}
public System.IO.Stream GetLayer(string resourceID, string sectionName, string layerName)
{
string req = m_reqBuilder.GetDrawingLayer(resourceID, sectionName, layerName);
return this.OpenRead(req);
}
public System.IO.Stream GetSection(string resourceID, string sectionName)
{
string req = m_reqBuilder.GetDrawingSection(resourceID, sectionName);
return this.OpenRead(req);
}
public System.IO.Stream GetSectionResource(string resourceID, string resourceName)
{
string req = m_reqBuilder.GetDrawingSectionResource(resourceID, resourceName);
return this.OpenRead(req);
}
public override DataStoreList EnumerateDataStores(string providerName, string partialConnString)
{
string req = m_reqBuilder.EnumerateDataStores(providerName, partialConnString);
using (System.IO.Stream s = this.OpenRead(req))
{
var list = this.DeserializeObject(s);
return list;
}
}
public FdoProviderCapabilities GetProviderCapabilities(string provider)
{
if (m_cachedProviderCapabilities == null)
m_cachedProviderCapabilities = new Hashtable();
if (m_cachedProviderCapabilities.ContainsKey(provider))
return (FdoProviderCapabilities)m_cachedProviderCapabilities[provider];
string req = m_reqBuilder.GetProviderCapabilities(provider);
//TODO: Cache?
FdoProviderCapabilities o = (FdoProviderCapabilities)DeserializeObject(typeof(FdoProviderCapabilities), this.OpenRead(req));
return o;
}
public override ILongTransactionList GetLongTransactions(string resourceId, bool activeOnly)
{
string req = m_reqBuilder.GetLongTransactions(resourceId, activeOnly);
return DeserializeObject(this.OpenRead(req));
}
public override ConfigurationDocument GetSchemaMapping(string provider, string partialConnString)
{
string req = m_reqBuilder.GetSchemaMapping(provider, partialConnString);
return ConfigurationDocument.Load(this.OpenRead(req));
}
public override bool MoveFolderWithReferences(string oldpath, string newpath, LengthyOperationCallBack callback, LengthyOperationProgressCallBack progress)
{
if (this.SiteVersion >= new Version(2, 2)) //new way
{
//Unfortunately because this is all batched server-side, there is no
//meaningful way to track progress
LengthyOperationProgressArgs la = new LengthyOperationProgressArgs("Moving resource...", -1); //LOCALIZEME
if (progress != null)
progress(this, la);
oldpath = FixAndValidateFolderPath(oldpath);
newpath = FixAndValidateFolderPath(newpath);
string req = m_reqBuilder.MoveResource(oldpath, newpath, true);
req += "&CASCADE=1";
using (System.IO.Stream resp = this.OpenRead(req))
resp.ReadByte();
return true;
}
else //old way
{
return base.MoveFolderWithReferences(oldpath, newpath, callback, progress);
}
}
public override bool MoveResourceWithReferences(string oldpath, string newpath, LengthyOperationCallBack callback, LengthyOperationProgressCallBack progress)
{
if (this.SiteVersion >= new Version(2, 2)) //new way
{
//Unfortunately because this is all batched server-side, there is no
//meaningful way to track progress
LengthyOperationProgressArgs la = new LengthyOperationProgressArgs("Moving resource...", -1); //LOCALIZEME
if (progress != null)
progress(this, la);
string req = m_reqBuilder.MoveResource(oldpath, newpath, true);
req += "&CASCADE=1";
using (System.IO.Stream resp = this.OpenRead(req))
resp.ReadByte();
return true;
}
else //old way
{
return base.MoveResourceWithReferences(oldpath, newpath, callback, progress);
}
}
public override string QueryMapFeatures(string runtimeMapName, string wkt, bool persist, QueryMapFeaturesLayerAttributes attributes, bool raw)
{
//The request may execeed the url limit of the server, when large geometries
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.Net.WebRequest req = m_reqBuilder.QueryMapFeatures(runtimeMapName, persist, wkt, ms, attributes);
req.Timeout = 200 * 1000;
ms.Position = 0;
using (System.IO.Stream rs = req.GetRequestStream())
{
Utility.CopyStream(ms, rs);
rs.Flush();
}
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
#if DEBUG
System.IO.MemoryStream xms = new System.IO.MemoryStream();
Utility.CopyStream(req.GetResponse().GetResponseStream(), xms);
xms.Position = 0;
string f = System.Text.Encoding.UTF8.GetString(xms.ToArray());
if (raw)
return f;
xms.Position = 0;
doc.Load(xms);
#else
if (raw)
{
System.IO.MemoryStream xms = new System.IO.MemoryStream();
Utility.CopyStream(req.GetResponse().GetResponseStream(), xms);
return System.Text.Encoding.UTF8.GetString(xms.ToArray());
}
doc.Load(req.GetResponse().GetResponseStream());
#endif
if (doc.SelectSingleNode("FeatureInformation/FeatureSet") != null && doc["FeatureInformation"]["FeatureSet"].ChildNodes.Count > 0)
return "" + doc["FeatureInformation"]["FeatureSet"].InnerXml + "";
else
return "";
}
public override string[] GetSchemas(string resourceId)
{
var req = m_reqBuilder.GetSchemas(resourceId);
using (var s = this.OpenRead(req))
{
var sc = this.DeserializeObject(s);
return sc.Item.ToArray();
}
}
public override string[] GetClassNames(string resourceId, string schemaName)
{
var req = m_reqBuilder.GetClassNames(resourceId, schemaName);
using (var s = this.OpenRead(req))
{
var sc = this.DeserializeObject(s);
return sc.Item.ToArray();
}
}
public override IServerConnection Clone()
{
if (this.IsAnonymous)
return new HttpServerConnection(new Uri(this.ServerURI), "Anonymous", "", null, true);
else
return new HttpServerConnection(new Uri(this.ServerURI), this.SessionID, null, true);
}
public override SiteInformation GetSiteInfo()
{
var req = m_reqBuilder.GetSiteInfo();
using (var s = this.OpenRead(req))
{
return this.DeserializeObject(s);
}
}
public override ICommand CreateCommand(int cmdType)
{
CommandType ct = (CommandType)cmdType;
if (ct == CommandType.GetResourceContents)
return new HttpGetResourceContents(this);
else if (ct == CommandType.GetFdoCacheInfo)
return new HttpGetFdoCacheInfo(this);
return base.CreateCommand(cmdType);
}
internal FdoCacheInfo GetFdoCacheInfo()
{
var req = m_reqBuilder.GetFdoCacheInfo();
using (var s = this.OpenRead(req))
{
var info = this.DeserializeObject(s);
return info;
}
}
}
}