I'm using the ASP.NET MVC 4 bundling and minifying features in the Microsoft.AspNet.Web.Optimization namespace (e.g. #Styles.Render("~/content/static/css")).
I'd like to use it in combination with a Windows Azure CDN.
I looked into writing a custom BundleTransform but the content is not optimized there yet.
I also looked into parsing and uploading the optimized stream on runtime but that feels like a hack to me and I don't really like it:
#StylesCdn.Render(Url.AbsoluteContent(
Styles.Url("~/content/static/css").ToString()
));
public static IHtmlString Render(string absolutePath)
{
// get the version hash
string versionHash = HttpUtility.ParseQueryString(
new Uri(absolutePath).Query
).Get("v");
// only parse and upload to CDN if version hash is different
if (versionHash != _versionHash)
{
_versionHash = versionHash;
WebClient client = new WebClient();
Stream stream = client.OpenRead(absolutePath);
UploadStreamToAzureCdn(stream);
}
var styleSheetLink = String.Format(
"<link href=\"{0}://{1}/{2}/{3}?v={4}\" rel=\"stylesheet\" type=\"text/css\" />",
cdnEndpointProtocol, cdnEndpointUrl, cdnContainer, cdnCssFileName, versionHash
);
return new HtmlString(styleSheetLink);
}
How can I upload the bundled and minified versions automatically to my Windows Azure CDN?
Following Hao's advice I Extended Bundle and IBundleTransform.
Adding AzureScriptBundle or AzureStyleBundle to bundles;
bundles.Add(new AzureScriptBundle("~/bundles/modernizr.js", "cdn").Include("~/Scripts/vendor/modernizr.custom.68789.js"));
Results in;
<script src="//127.0.0.1:10000/devstoreaccount1/cdn/modernizr.js?v=g-XPguHFgwIb6tGNcnvnI_VY_ljCYf2BDp_NS5X7sAo1"></script>
If CdnHost isn't set it will use the Uri of the blob instead of the CDN.
Class
using System;
using System.Text;
using System.Web;
using System.Web.Optimization;
using System.Security.Cryptography;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
namespace SiegeEngineWebRole.BundleExtentions
{
public class AzureScriptBundle : Bundle
{
public AzureScriptBundle(string virtualPath, string containerName, string cdnHost = "")
: base(virtualPath, null, new IBundleTransform[] { new JsMinify(), new AzureBlobUpload { ContainerName = containerName, CdnHost = cdnHost } })
{
ConcatenationToken = ";";
}
}
public class AzureStyleBundle : Bundle
{
public AzureStyleBundle(string virtualPath, string containerName, string cdnHost = "")
: base(virtualPath, null, new IBundleTransform[] { new CssMinify(), new AzureBlobUpload { ContainerName = containerName, CdnHost = cdnHost } })
{
}
}
public class AzureBlobUpload : IBundleTransform
{
public string ContainerName { get; set; }
public string CdnHost { get; set; }
static AzureBlobUpload()
{
}
public virtual void Process(BundleContext context, BundleResponse response)
{
var file = VirtualPathUtility.GetFileName(context.BundleVirtualPath);
if (!context.BundleCollection.UseCdn)
{
return;
}
if (string.IsNullOrWhiteSpace(ContainerName))
{
throw new Exception("ContainerName Not Set");
}
var conn = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"));
var blob = conn.CreateCloudBlobClient()
.GetContainerReference(ContainerName)
.GetBlobReference(file);
blob.Properties.ContentType = response.ContentType;
blob.UploadText(response.Content);
var uri = string.IsNullOrWhiteSpace(CdnHost) ? blob.Uri.AbsoluteUri.Replace("http:", "").Replace("https:", "") : string.Format("//{0}/{1}/{2}", CdnHost, ContainerName, file);
using (var hashAlgorithm = CreateHashAlgorithm())
{
var hash = HttpServerUtility.UrlTokenEncode(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(response.Content)));
context.BundleCollection.GetBundleFor(context.BundleVirtualPath).CdnPath = string.Format("{0}?v={1}", uri, hash);
}
}
private static SHA256 CreateHashAlgorithm()
{
if (CryptoConfig.AllowOnlyFipsAlgorithms)
{
return new SHA256CryptoServiceProvider();
}
return new SHA256Managed();
}
}
}
So there isn't a great way to do this currently. The longer term workflow we are envisioning is adding build-time bundling support. Then you would run a build task (or run an exe if you prefer) to generate the bundles and then be able to upload these to the AzureCDN. Finally, you just turn on UseCDN on the BundleCollection, and the Script/Style helpers would just automatically switch to rendering out links to your AzureCDN with proper fallback to your local bundles.
For the short term, what I think you are trying to do is upload your bundle to the AzureCDN when the bundle is first constructed?
A BundleTransform is one way to do it I guess, its a bit of a hack, but you could add a BundleTransform last in your bundle. Since its last, the BundleResponse.Content is effectively the final bundle response. At that point in time you can upload it to your CDN. Does that make sense?
You can define origin domain as Azure's website (this probably was added long after the original question).
Once you have CDN endpoint, you will need to allow query string for it and then you can reference directly to bundles via CDN:
<link href="//az888888.vo.msecnd.net/Content/css-common?v=ioYVnAg-Q3qYl3Pmki-qdKwT20ESkdREhi4DsEehwCY1" rel="stylesheet"/>
I've also created this helper to append CDN host name:
public static IHtmlString RenderScript(string virtualPath)
{
if (HttpContext.Current.IsDebuggingEnabled)
return Scripts.Render(virtualPath);
else
return new HtmlString(String.Format(
CultureInfo.InvariantCulture,
Scripts.DefaultTagFormat,
"//CDN_HOST" + Scripts.Url(virtualPath).ToHtmlString()));
}
For #manishKungwani requested in previous comment. Just set UseCdn and then use the
cdnHost overload to crate the bundle. I used this to put in an AWS CloudFront domain (xxx.cloudfront.net) but in hindsight it should have been named more generically to use with any other CDN provider.
public class CloudFrontScriptBundle : Bundle
{
public CloudFrontScriptBundle(string virtualPath, string cdnHost = "")
: base(virtualPath, null, new IBundleTransform[] { new JsMinify(), new CloudFrontBundleTransformer { CdnHost = cdnHost } })
{
ConcatenationToken = ";";
}
}
public class CloudFrontStyleBundle : Bundle
{
public CloudFrontStyleBundle(string virtualPath, string cdnHost = "")
: base(virtualPath, null, new IBundleTransform[] { new CssMinify(), new CloudFrontBundleTransformer { CdnHost = cdnHost } })
{
}
}
public class CloudFrontBundleTransformer : IBundleTransform
{
public string CdnHost { get; set; }
static CloudFrontBundleTransformer()
{
}
public virtual void Process(BundleContext context, BundleResponse response)
{
if (context.BundleCollection.UseCdn && !String.IsNullOrWhiteSpace(CdnHost))
{
var virtualFileName = VirtualPathUtility.GetFileName(context.BundleVirtualPath);
var virtualDirectory = VirtualPathUtility.GetDirectory(context.BundleVirtualPath);
if (!String.IsNullOrEmpty(virtualDirectory))
virtualDirectory = virtualDirectory.Trim('~');
var uri = string.Format("//{0}{1}{2}", CdnHost, virtualDirectory, virtualFileName);
using (var hashAlgorithm = CreateHashAlgorithm())
{
var hash = HttpServerUtility.UrlTokenEncode(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(response.Content)));
context.BundleCollection.GetBundleFor(context.BundleVirtualPath).CdnPath = string.Format("{0}?v={1}", uri, hash);
}
}
}
private static SHA256 CreateHashAlgorithm()
{
if (CryptoConfig.AllowOnlyFipsAlgorithms)
{
return new SHA256CryptoServiceProvider();
}
return new SHA256Managed();
}
}
Related
I extended the class OrmLiteAuthRepository
In the app host i inject it into the container. I test it using requiredrole controller and it never calls the methods for my custom security checks.
Even though i get redirect to http://localhost:5000/?redirect=%2fRequiresRole#f=Unauthorized
SO i just verified the the main simple contrustor is called when teh application starts. So it is using my clss. but not calling the get\haspermission methods.
[RequiredRole("TheRole")]
public class RequiresRoleController : ServiceStackController
{
public ActionResult Index()
{
var session = SessionAs<CustomUserSession>();
return View(session);
}
}
using System;
using System.Collections.Generic;
using cbw.service.interfaces.Services;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.Data;
using ServiceStack.Host;
using ServiceStack.Messaging;
namespace cbw.mvc.web.service.Providers
{
public class MyOrmLiteAuthRepository : OrmLiteAuthRepository
{
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory) : base(dbFactory) { }
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory, string namedConnnection = null)
: base(dbFactory, namedConnnection)
{
DbFactory = dbFactory;
NamedConnnection = namedConnnection;
}
public IDbConnectionFactory DbFactory { get; set; }
public string NamedConnnection { get; set; }
public override ICollection<string> GetPermissions(string userAuthId)
{
var permissions = base.GetPermissions(userAuthId);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
permissions = ss.UserPermissions(Convert.ToInt32(userAuthId));
}
return permissions;
}
public override bool HasPermission(string userAuthId, string permission)
{
var hasPermission = base.HasPermission(userAuthId, permission);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
hasPermission = ss.UserHasPermInRoleOrGroup(permission, Convert.ToInt32(userAuthId));
}
return hasPermission;
}
}
}
Apphost.cs
using System;
using System.Net;
using cbw.mvc.web.service.Providers;
using cbw.service.interfaces.Services;
using cbw.service.interfaces.Validators;
using cbw.service.models.Models;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.OrmLite;
using ServiceStack.Caching;
using ServiceStack.Data;
using ServiceStack.Mvc;
using ServiceStack.Text;
using ServiceStack.Validation;
namespace cbw.mvc.web.service
{
public class AppHost : AppHostBase
{
public AppHost() : base("ServiceStack + .NET Core", typeof(StartupService).Assembly) { }
public override void Configure(Funq.Container container)
{
Plugins.Add(new RazorFormat());
//Works but recommend handling 404 at end of .NET Core pipeline
//this.CustomErrorHttpHandlers[HttpStatusCode.NotFound] = new RazorHandler("/notfound");
this.CustomErrorHttpHandlers[HttpStatusCode.Unauthorized] = new RazorHandler("/login");
//To include null values in the json globally
JsConfig.IncludeNullValues = true;
//This is mandate. We need "IncludeNullValuesInDictionaries = true" to include null values
JsConfig.IncludeNullValuesInDictionaries = true;
//To automatically wired up for you on all HTTP Verbs (GET, POST, etc)
//And built-in endpoints, i.e. JSON, XML, JSV, HTML, CSV, SOAP
Plugins.Add(new CorsFeature());
//To add registration feature
Plugins.Add(new RegistrationFeature());
//To add validation feature
Plugins.Add(new ValidationFeature());
container.RegisterValidators(
typeof(InsertCompanyValidator).Assembly,
typeof(UpdateCompanyValidator).Assembly,
typeof(DeleteCompanyValidator).Assembly
);
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[]
{
new CredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
new BasicAuthProvider(), //Sign-in with HTTP Basic Auth
new DigestAuthProvider(AppSettings), //Sign-in with HTTP Digest Auth
new TwitterAuthProvider(AppSettings), //Sign-in with Twitter
new FacebookAuthProvider(AppSettings), //Sign-in with Facebook
new GithubAuthProvider(AppSettings), //Sign-in with GitHub OAuth Provider
new GoogleAuthProvider(AppSettings), //Sign-in with Google OAuth Provider
new YandexAuthProvider(AppSettings), //Sign-in with Yandex OAuth Provider
new VkAuthProvider(AppSettings), //Sign-in with VK.com OAuth Provider
})
{
HtmlRedirect = "/",
//IncludeRegistrationService = true,
});
// i.e. Register in Memory Cache Client
//AutoQuery
Plugins.Add(new AutoQueryFeature { MaxLimit = 100000 });
container.Register<IAuthRepository>(c =>
new MyOrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())
{
UseDistinctRoleTables = AppSettings.Get("UseDistinctRoleTables", true),
});
;
bool ShouldWipeAndReloadDb = false;
var environmentVariable = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (environmentVariable == "LocalMemory" || environmentVariable == "LocalSQLServer")
{
ShouldWipeAndReloadDb = true;
//Init auth tables
container.Resolve<IAuthRepository>().InitSchema();
}
var authRepo = (MyOrmLiteAuthRepository)container.Resolve<IAuthRepository>();
//Wipe and reload if using in memory SQL
if (ShouldWipeAndReloadDb)
{
DatabaseInitService dis = new DatabaseInitService();
dis.ResetDatabase();
SessionService.ResetUsers(authRepo);
dis.InitializeTablesAndData();
}
}
}
}
You’re using [RequiredRole] but you’re only overriding the HasPermission/GetPermissions APIs, you need to have overridden the HasRole/GetRoles APIs instead.
How do you model bind an array from the URI with GET in ASP.NET Core 1 Web API (implicitly or explicitly)?
In ASP.NET Web API pre Core 1, this worked:
[HttpGet]
public void Method([FromUri] IEnumerable<int> ints) { ... }
How do you do this in ASP.NET Web API Core 1 (aka ASP.NET 5 aka ASP.NET vNext)? The docs have nothing.
The FromUriAttribute class combines the FromRouteAttribute and FromQueryAttribute classes. Depending the configuration of your routes / the request being sent, you should be able to replace your attribute with one of those.
However, there is a shim available which will give you the FromUriAttribute class. Install the "Microsoft.AspNet.Mvc.WebApiCompatShim" NuGet package through the package explorer, or add it directly to your project.json file:
"dependencies": {
"Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-rc1-final"
}
While it is a little old, I've found that this article does a pretty good job of explaining some of the changes.
Binding
If you're looking to bind comma separated values for the array ("/api/values?ints=1,2,3"), you will need a custom binder just as before. This is an adapted version of Mrchief's solution for use in ASP.NET Core.
public class CommaDelimitedArrayModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext.ModelMetadata.IsEnumerableType)
{
var key = bindingContext.ModelName;
var value = bindingContext.ValueProvider.GetValue(key).ToString();
if (!string.IsNullOrWhiteSpace(value))
{
var elementType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];
var converter = TypeDescriptor.GetConverter(elementType);
var values = value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => converter.ConvertFromString(x.Trim()))
.ToArray();
var typedValues = Array.CreateInstance(elementType, values.Length);
values.CopyTo(typedValues, 0);
bindingContext.Result = ModelBindingResult.Success(typedValues);
}
else
{
// change this line to null if you prefer nulls to empty arrays
bindingContext.Result = ModelBindingResult.Success(Array.CreateInstance(bindingContext.ModelType.GetElementType(), 0));
}
return TaskCache.CompletedTask;
}
return TaskCache.CompletedTask;
}
}
You can either specify the model binder to be used for all collections in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc().AddMvcOptions(opts =>
{
opts.ModelBinders.Insert(0, new CommaDelimitedArrayModelBinder());
});
}
Or specify it once in your API call:
[HttpGet]
public void Method([ModelBinder(BinderType = typeof(CommaDelimitedArrayModelBinder))] IEnumerable<int> ints)
ASP.NET Core 1.1 Answer
#WillRay's answer is a little outdated. I have written an 'IModelBinder' and 'IModelBinderProvider'. The first can be used with the [ModelBinder(BinderType = typeof(DelimitedArrayModelBinder))] attribute, while the second can be used to apply the model binder globally as I've show below.
.AddMvc(options =>
{
// Add to global model binders so you don't need to use the [ModelBinder] attribute.
var arrayModelBinderProvider = options.ModelBinderProviders.OfType<ArrayModelBinderProvider>().First();
options.ModelBinderProviders.Insert(
options.ModelBinderProviders.IndexOf(arrayModelBinderProvider),
new DelimitedArrayModelBinderProvider());
})
public class DelimitedArrayModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.IsEnumerableType && !context.Metadata.ElementMetadata.IsComplexType)
{
return new DelimitedArrayModelBinder();
}
return null;
}
}
public class DelimitedArrayModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var modelName = bindingContext.ModelName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
var values = valueProviderResult
.ToString()
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var elementType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];
if (values.Length == 0)
{
bindingContext.Result = ModelBindingResult.Success(Array.CreateInstance(elementType, 0));
}
else
{
var converter = TypeDescriptor.GetConverter(elementType);
var typedArray = Array.CreateInstance(elementType, values.Length);
try
{
for (int i = 0; i < values.Length; ++i)
{
var value = values[i];
var convertedValue = converter.ConvertFromString(value);
typedArray.SetValue(convertedValue, i);
}
}
catch (Exception exception)
{
bindingContext.ModelState.TryAddModelError(
modelName,
exception,
bindingContext.ModelMetadata);
}
bindingContext.Result = ModelBindingResult.Success(typedArray);
}
return Task.CompletedTask;
}
}
There are some changes in the .NET Core 3.
Microsoft has split out the functionality from the AddMvc method (source).
As AddMvc also includes support for View Controllers, Razor Views and etc. If you don't need to use them in your project (like in an API), you might consider using services.AddControllers() which is for Web API controllers.
So, updated code will look like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddMvcOptions(opt =>
{
var mbp = opt.ModelBinderProviders.OfType<ArrayModelBinderProvider>().First();
opt.ModelBinderProviders.Insert(opt.ModelBinderProviders.IndexOf(mbp), new DelimitedArrayModelBinderProvider());
});
}
ConfigurationManager has AppSettings name-value collection but CloudConfigurationManager has only GetSetting(string) method where you can get the config settings 1 by 1 if you know the key.
Is there a way to get the whole config of the role runtime?
The root cause is that I want to make strong typed configuration in order to abstract it away and make my code more testable. Using CloudConfigurationManager directly is implicit dependency which I want to remove with an abstraction which I want to stub in tests. So I find this practical. Which brings me to my question.
I do not want to use library like fx.configuration.azure because I will have to carry its dependency altogether because it requires inheritance of a base class.
AFAIK, there's no direct method available which will give you this information.
However there's a workaround that you can use. It involves making use of Service Management API's Get Deployment operation. This operation will return an XML and one of the element there is Configuration which contains your service configuration file in Base64 encoded format. You can read this element, convert it into string and parse the XML to get to ConfigurationSettings elements. It's child elements contains all the settings.
For this, you could either write your own wrapper over Service Management REST API or make use of Azure Management Library.
UPDATE
So here's a sample code for listing all configuration settings from Service Configuration File using Azure Management Library. It's a simple console app hacked together in very short amount of time thus has a lot of scope of improvement :). For management certificate, I have used the data from Publish Setting File.
You just have to install Azure Management Library Nuget Package in your console application:
Install-Package Microsoft.WindowsAzure.Management.Libraries
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management.Compute;
using System.Security.Cryptography.X509Certificates;
using System.Xml.Linq;
namespace ReadConfigurationSettingsUsingAzureManagementLibrary
{
class Program
{
static string subscriptionId = "<subscription-id>";
static string managementCertContents = "<Base64 Encoded Management Certificate String from Publish Setting File>";//Certificate string from Azure Publish Settings file
static string cloudServiceName = "<your cloud service name>";
static string ns = "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration";
static void Main(string[] args)
{
var managementCetificate = new X509Certificate2(Convert.FromBase64String(managementCertContents));
var credentials = new CertificateCloudCredentials(subscriptionId, managementCetificate);
var computeManagementClient = new ComputeManagementClient(credentials);
var response = computeManagementClient.HostedServices.GetDetailed(cloudServiceName);
var deployment = response.Deployments.FirstOrDefault(d => d.DeploymentSlot == Microsoft.WindowsAzure.Management.Compute.Models.DeploymentSlot.Production);
if (deployment != null)
{
var config = deployment.Configuration;
XElement configXml = XElement.Parse(config);
var roles = configXml.Descendants(XName.Get("Role", ns));
foreach (var role in roles)
{
Console.WriteLine(role.Attribute("name").Value);
Console.WriteLine("-----------------------------");
var configurationSettings = role.Element(XName.Get("ConfigurationSettings", ns));
foreach (var element in configurationSettings.Elements(XName.Get("Setting", ns)))
{
var settingName = element.Attribute("name").Value;
var settingValue = element.Attribute("value").Value;
Console.WriteLine(string.Format("{0} = {1}", settingName, settingValue));
}
Console.WriteLine("==========================================");
}
}
Console.ReadLine();
}
}
}
Here is an updated implementation which takes care if you are running in emulator or not and if you are running in local web server or not. After returning the dictionary it can be easily abstracted away from the whole application by Castle.DictionaryAdapter. I shared the code as template project on GitHub here. Here is an excerpt:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.Xml.Linq;
using Castle.Components.DictionaryAdapter;
using Core.Configuration.Interfaces;
using Microsoft.Azure;
using Microsoft.WindowsAzure.Management.Compute;
using Microsoft.WindowsAzure.Management.Compute.Models;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace Core.Configuration
{
public class AzureServiceConfigurationProvider : IAzureServiceConfigurationProvider
{
private readonly string _subscriptionId;
// The Base64 Encoded Management Certificate string from Azure Publish Settings file
// download from https://manage.windowsazure.com/publishsettings/index
private readonly string _managementCertContents;
private readonly string _cloudServiceName;
private readonly string _serviceConfigurationNamespace;
public DefaultAzureServiceConfigurationProvider(IWebConfigSettings webConfigSettings)
{
_subscriptionId = webConfigSettings.SubscriptionId;
_managementCertContents = webConfigSettings.ManagementCertContents;
_cloudServiceName = webConfigSettings.CloudServiceName;
_serviceConfigurationNamespace = webConfigSettings.ServiceConfigurationNamespace;
}
public Dictionary<string, Dictionary<string, string>> GetConfigRaw()
{
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->Start");
var configuration = new Dictionary<string, Dictionary<string, string>>();
var configXml = GetConfigXml();
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->XmlExtracted");
var roles = configXml.Descendants(XName.Get("Role", _serviceConfigurationNamespace));
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->Roles : ");
foreach(var role in roles)
{
var roleConfiguration = new Dictionary<string, string>();
var roleName = role.Attribute("name").Value;
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->RoleName : " + roleName);
var configurationSettings = role.Element(XName.Get("ConfigurationSettings", _serviceConfigurationNamespace));
if (configurationSettings == null)
{
throw new InvalidOperationException("configurationSettings is null");
}
foreach(var element in configurationSettings.Elements(XName.Get("Setting", _serviceConfigurationNamespace)))
{
var settingName = element.Attribute("name").Value;
var settingValue = element.Attribute("value").Value;
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->settingName : " + settingName + " settingValue : " + settingValue);
roleConfiguration.Add(settingName, settingValue);
}
configuration.Add(roleName, roleConfiguration);
}
return configuration;
}
public IAzureServiceConfiguration GetConfig()
{
var configFactory = new DictionaryAdapterFactory();
IAzureServiceConfiguration config;
try
{
var rawAzureServiceConfig = GetConfigRaw();
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfig :");
var rawAzureWebServiceConfig = rawAzureServiceConfig["Core.Web"];
config = configFactory.GetAdapter<IAzureServiceConfiguration>(rawAzureWebServiceConfig);
config = ComplementConfigurationFromConfigurationManager(config);
}
catch(Exception exception)
{
// happens in some projects when using Full Emulator
// so we fallback to cloudconfigurationmanager
// this is not bad since we have isolated it in configuration assembly
Trace.WriteLine(exception.Message);
Trace.WriteLine(exception.StackTrace);
Hashtable hashConfig = GetConfigFromConfigurationManager();
config = configFactory.GetAdapter<IAzureServiceConfiguration>(hashConfig);
}
return config;
}
private IAzureServiceConfiguration ComplementConfigurationFromConfigurationManager(IAzureServiceConfiguration config)
{
Trace.WriteLine("Complementing configuration");
var azureConfigType = config.GetType();
foreach(PropertyInfo property in config.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
var xmlConfigValue = CloudConfigurationManager.GetSetting(property.Name);
var liveConfigPropValue = (string)azureConfigType.GetProperty(property.Name).GetValue(config, null);
if(string.IsNullOrEmpty(liveConfigPropValue))
{
Trace.WriteLine(property.Name + " in live config is empty. Complementing with '" + xmlConfigValue + "' from ConfigurationManager.");
property.SetValue(config, xmlConfigValue);
}
// do something with the property
}
return config;
}
private Hashtable GetConfigFromConfigurationManager()
{
Hashtable hashConfig = new Hashtable();
var configProperties = typeof(IAzureServiceConfiguration).GetProperties();
foreach(PropertyInfo prop in configProperties)
{
hashConfig.Add(prop.Name, CloudConfigurationManager.GetSetting(prop.Name));
}
return hashConfig;
}
private XElement GetConfigXml()
{
XElement configXml = null;
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml");
if(!RoleEnvironment.IsAvailable/*as local web project*/ || RoleEnvironment.IsEmulated /*as azure emulator project*/)
{
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml->!RoleEnvironment.IsAvailable || RoleEnvironment.IsEmulated");
try
{
var localConfigFile =
new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).Parent.EnumerateFiles(
"*Local.cscfg", SearchOption.AllDirectories).FirstOrDefault();
XmlDocument doc = new XmlDocument();
doc.Load(localConfigFile.FullName);
configXml = XElement.Parse(doc.InnerXml);
}
catch(Exception exception) // happens in some projects when using Full Emulator
{
Trace.WriteLine(exception.Message);
Trace.WriteLine(exception.StackTrace);
throw; // intended - just marking - will catch it above
}
}
else
{
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml->RoleEnvironment ->in cloud");
var managementCertificate = new X509Certificate2(Convert.FromBase64String(_managementCertContents));
var credentials = new CertificateCloudCredentials(_subscriptionId, managementCertificate);
var computeManagementClient = new ComputeManagementClient(credentials);
var response = computeManagementClient.HostedServices.GetDetailed(_cloudServiceName);
var deployment = response.Deployments.FirstOrDefault(d => d.DeploymentSlot == DeploymentSlot.Production);
if(deployment != null)
{
var config = deployment.Configuration;
configXml = XElement.Parse(config);
}
}
return configXml;
}
}
internal static class TypeHelpers
{
public static bool IsNumber(this object value)
{
return value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal;
}
public static bool IsString(this object value)
{
return value is string;
}
}
}
I am using svnkit-1.3.5.jar in my application. On one of my screens on clicking a button I need to display a jQuery dialog box containing list of folders present at a particular path in SVN. Does svnkit provide any method that retrieves all folder names present at a specific location? How do I achieve this in java?
Here is the code i use for the same purpose (uses svnkit library). Modified version of #mstrap's code for better clarity.
public static String NAME = "svnusername";
public static String PASSWORD = "svnpass";
public final String TRUNK_VERSION_PATH = "svn://192.168.1.1/path";
public static List<String> apiVersions;
public List<String> getApiVersion() {
logger.info("Getting API Version list....");
apiVersions = new ArrayList<String>();
SVNURL repositoryURL = null;
try {
repositoryURL = SVNURL.parseURIEncoded(TRUNK_VERSION_PATH);
} catch (SVNException e) {
logger.error(e);
}
SVNRevision revision = SVNRevision.HEAD;
SvnOperationFactory operationFactory = new SvnOperationFactory();
operationFactory.setAuthenticationManager(new BasicAuthenticationManager(NAME, PASSWORD));
SvnList list = operationFactory.createList();
list.setDepth(SVNDepth.IMMEDIATES);
list.setRevision(revision);
list.addTarget(SvnTarget.fromURL(repositoryURL, revision));
list.setReceiver(new ISvnObjectReceiver<SVNDirEntry>() {
public void receive(SvnTarget target, SVNDirEntry object) throws SVNException {
String name = object.getRelativePath();
if(name!=null && !name.isEmpty()){
apiVersions.add(name);
}
}
});
try {
list.run();
} catch (SVNException ex) {
logger.error(ex);
}
return apiVersions;
}
Cheers!!
final URL url = ...
final SVNRevision revision = ...
final SvnOperationFactory operationFactory = ...
final SvnList list = operationFactory.createList();
list.setDepth(SVNDepth.IMMEDIATES);
list.setRevision(revision);
list.addTarget(SvnTarget.fromURL(url, revision);
list.setReceiver(new ISvnObjectReceiver<SVNDirEntry>() {
public void receive(SvnTarget target, SVNDirEntry object) throws SVNException {
final String name = object.getRelativePath();
System.out.println(name);
}
});
list.run();
I'm trying to write a target for NLog to send messages out to connected clients using SignalR.
Here's what I have now. What I'm wondering is should I be using resolving the ConnectionManager like this -or- somehow obtain a reference to the hub (SignalrTargetHub) and call a SendMessage method on it?
Are there performance ramifications for either?
[Target("Signalr")]
public class SignalrTarget:TargetWithLayout
{
public SignalR.IConnectionManager ConnectionManager { get; set; }
public SignalrTarget()
{
ConnectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
}
protected override void Write(NLog.LogEventInfo logEvent)
{
dynamic clients = GetClients();
var logEventObject = new
{
Message = this.Layout.Render(logEvent),
Level = logEvent.Level.Name,
TimeStamp = logEvent.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss.fff")
};
clients.onLoggedEvent(logEventObject);
}
private dynamic GetClients()
{
return ConnectionManager.GetClients<SignalrTargetHub>();
}
}
I ended up with the basic the same basic structure that I started with. Just a few tweaks to get the information I needed.
Added exception details.
Html encoded the final message.
[Target("Signalr")]
public class SignalrTarget:TargetWithLayout
{
protected override void Write(NLog.LogEventInfo logEvent)
{
var sb = new System.Text.StringBuilder();
sb.Append(this.Layout.Render(logEvent));
if (logEvent.Exception != null)
sb.AppendLine().Append(logEvent.Exception.ToString());
var message = HttpUtility.HtmlEncode(sb.ToString());
var logEventObject = new
{
Message = message,
Logger = logEvent.LoggerName,
Level = logEvent.Level.Name,
TimeStamp = logEvent.TimeStamp.ToString("HH:mm:ss.fff")
};
GetClients().onLoggedEvent(logEventObject);
}
private dynamic GetClients()
{
return AspNetHost.DependencyResolver.Resolve<IConnectionManager>().GetClients<SignalrTargetHub>();
}
}
In my simple testing it's working well. Still remains to be seen if this adds any significant load when under stress.