Azure Webjobs - Use INameResolver with TimerTrigger Function - azure-webjobs

I've tried to configure a job with a simple function with a TimerTrigger.
public class Processor
{
/// <summary>
/// Initializes a new instance of the <see cref="Processor"/> class.
/// </summary>
public Processor()
{
}
/// <summary>
/// Process the Leads to Marketo.
/// </summary>
[Disable("Processor.Disable")]
public async Task ProcessMessages([TimerTrigger("%Processor.TimerTrigger%")] TimerInfo timerInfo, TextWriter log)
{
// TODO : remove
await Task.FromResult(0);
}
}
My settings are defined in my app.config file:
<add key="Processor.TimerTrigger" value="00:01:00" />
<add key="Processor.Disable" value="false" />
When Starting my webjob, I've configure the job to use INameResolver and timertrigger:
static void Main()
{
// Configure the job host
var config = new JobHostConfiguration
{
NameResolver = new ConfigNameResolver() // Resolve name from the config file.
};
config.UseTimers();
var host = new JobHost(config);
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
When executing the line host.RunAndBlock(), I've got this exception :
Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexingException: Error indexing method 'ProcessMessages' ---> System.FormatException: String was not recognized as a valid TimeSpan.
I've put a break point in the class that implements the INameResolver interface but never hit.
Is there any way to configure a NameResolver with TimerTrigger ?
Thanks.

TimerTrigger does not currently support INameResolver. Please open an issue in the public repo here and we'll add that support. The other extension bindings support INameResolver. If it's important to you, we can get out a pre-release build for you to use/verify ahead of the actual next release.

Confirmation that INameResolver is now supported in Timer Triggers using the technique in the original question and a resolver that looks like this:
public class ConfigNameResolver : INameResolver
{
public string Resolve(string name)
{
return ConfigurationManager.AppSettings.Get(name);
}
}

Related

SQSlistener not receiving messages

I am able to send messages to SQS queue from my springboot but not able to receive using sqslistener annotation, can someone help?
public void send(String message) {
queueMessagingTemplate.convertAndSend("test-queue", MessageBuilder.withPayload(message).build());
}
#SqsListener(value = "test-queue", deletionPolicy = SqsMessageDeletionPolicy.NEVER)
public void receive(String message)
{
System.out.println("message: " + message);
}
I have verified send by goign to AWS console, i can see my messages in queue, but they are not coming to receive method.
config:
#Bean
public AmazonSQSAsyncClient amazonSQSAsyncClient()
{
AmazonSQSAsyncClient amazonSQSAsyncClient= new AmazonSQSAsyncClient(amazonAWSCredentials());
if (!StringUtils.isEmpty(amazonSqsEndpoint)) {
amazonSQSAsyncClient.setEndpoint(amazonSqsEndpoint);
}
}
#Bean
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory() {
SimpleMessageListenerContainerFactory msgListenerContainerFactory = new SimpleMessageListenerContainerFactory();
msgListenerContainerFactory.setAmazonSqs(amazonSQSAsyncClient());
return msgListenerContainerFactory;
}
#Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs) {
return new QueueMessagingTemplate(amazonSQSAsyncClient());
}
#Bean
public BasicAWSCredentials amazonAWSCredentials() {
return new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey);
}
In my case i was missing an annotation #EnableSqs in config class
Turns out i had a typo in the queue names in SQS console and my code, my bad.
Though it is late, it may help someone. In my case, I used gradle with these configuration
implementation "io.awspring.cloud:spring-cloud-starter-aws-messaging:2.3.0"
implementation "io.awspring.cloud:spring-cloud-aws-dependencies:2.3.0"
It doesn't work , below works perfectly for me:
compile "io.awspring.cloud:spring-cloud-starter-aws-messaging:2.3.0"
compile "io.awspring.cloud:spring-cloud-aws-dependencies:2.3.0"
In my case, the wrong region is specified. For some reason, sending didn't complain & pushed the message to the queue, but the listener is not getting called. Fixing the region in the application.yml file solved the issue.
In my case QueueMessageHandler bean wasn't initialized.
In the documentation they are using maven dependency:
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-messaging</artifactId>
<version>{spring-cloud-version}</version>
</dependency>
But in my case I'm using gradle, and also require dependency:
implementation(platform("io.awspring.cloud:spring-cloud-aws-dependencies:2.4.2"))
implementation("io.awspring.cloud:spring-cloud-aws-messaging")
implementation("io.awspring.cloud:spring-cloud-aws-autoconfigure")
Or you could define QueueMessageHandler in your own config file
#Bean
public QueueMessageHandler queueMessageHandler(QueueMessageHandlerFactory factory) {
return factory.createQueueMessageHandler();
}

Sitecore Experience Analytics Graphs Issue

After upgrading my site from 7.1 to 8.1 I have the following error message appears when opneing any page in the expierence analytics:
"The 'Graph Name' graph cannot be displayed due to a server error. Contact you system administrator."
The following call show 500 error on the browser console:
"http://sitename/sitecore/api/ao/aggregates/all/DC0DB760B0F54690B9EB1BBF7A4F7BD1/all?&dateGrouping=collapsed&&keyTop=8&keyOrderBy=valuePerVisit-Desc&dateFrom=07-04-2016&dateTo=05-07-2016&keyGrouping=by-key"
I checked the log files and there is no server error logged there!
More information:
The error message:
"ValueFactory attempted to access the Value property of this instance."
Also
" at System.Lazy`1.CreateValue() at System.Lazy`1.LazyInitValue() at System.Web.Http.Dispatcher.DefaultHttpControllerSelector.GetControllerMapping() at System.Web.Http.Routing.AttributeRoutingMapper.AddRouteEntries(SubRouteCollection collector, HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider) at System.Web.Http.Routing.AttributeRoutingMapper.<>c__DisplayClass2.<>c__DisplayClass4.<MapAttributeRoutes>b__1() at System.Web.Http.Routing.RouteCollectionRoute.EnsureInitialized(Func`1 initializer) at System.Web.Http.Routing.AttributeRoutingMapper.<>c__DisplayClass2.<MapAttributeRoutes>b__0(HttpConfiguration config) at System.Web.Http.HttpConfiguration.ApplyControllerSettings(HttpControllerSettings settings, HttpConfiguration configuration) at System.Web.Http.Controllers.HttpControllerDescriptor.InvokeAttributesOnControllerType(HttpControllerDescriptor controllerDescriptor, Type type) at System.Web.Http.Controllers.HttpControllerDescriptor..ctor(HttpConfiguration configuration, String controllerName, Type controllerType) at System.Web.Http.Dispatcher.DefaultHttpControllerSelector.InitializeControllerInfoCache() at System.Lazy`1.CreateValue() at System.Lazy`1.LazyInitValue() at System.Web.Http.Dispatcher.DefaultHttpControllerSelector.GetControllerMapping() at System.Web.Http.Routing.AttributeRoutingMapper.AddRouteEntries(SubRouteCollection collector, HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider)
at System.Web.Http.Routing.AttributeRoutingMapper.<>c__DisplayClass2.<>c__DisplayClass4.<MapAttributeRoutes>b__1() at System.Web.Http.Routing.RouteCollectionRoute.EnsureInitialized(Func`1 initializer) at System.Web.Http.Routing.AttributeRoutingMapper.<>c__DisplayClass2.<MapAttributeRoutes>b__0(HttpConfiguration config) at
System.Web.Http.HttpConfiguration.ApplyControllerSettings(HttpControllerSettings settings, HttpConfiguration configuration) at
System.Web.Http.Controllers.HttpControllerDescriptor.InvokeAttributesOnControllerType(HttpControllerDescriptor controllerDescriptor, Type type) at
System.Web.Http.Controllers.HttpControllerDescriptor..ctor(HttpConfiguration configuration, String controllerName, Type controllerType) at
Sitecore.Services.Infrastructure.Web.Http.Dispatcher.NamespaceHttpControllerSelector.InitializeControllerDictionary() at System.Lazy`1.CreateValue()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Lazy`1.get_Value() at Sitecore.Services.Infrastructure.Web.Http.Dispatcher.NamespaceHttpControllerSelector.FindMatchingController(String namespaceName, String controllerName) at Sitecore.Services.Infrastructure.Web.Http.Dispatcher.NamespaceHttpControllerSelector.SelectController(HttpRequestMessage request) at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
Any ideas?
The cause of this error is you might be using Web Api in your code. To integrate web api with site core you need to extend your global.asax as below
public class GlobalExtended : Sitecore.Web.Application
{
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(ConfigureRoutes);
}
public static void ConfigureRoutes(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApiRoute",
"api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
GlobalConfiguration.Configuration.MapHttpAttributeRoutes();
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
}
}
You can go through below url for detailed explanation
https://sitecorecommerce.wordpress.com/2014/11/30/webapi-attribute-routing-is-not-working-with-sitecore-7-5/
http://blog.krusen.dk/web-api-attribute-routing-in-sitecore-7-5-and-later/
Sitecore support provided the cause and solution for this and thought will add it in case same issue happened with someone else:
Cause:
It looks like the issue is caused by a conflict in a Web API configuration
As far as I can see, the following code is executed during the application start:
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
System.Web.Http.GlobalConfiguration.Configure(MyDll.WebApiConfig.Register);
}
Solution:
As an alternative approach, this code can be moved to the "initialize" pipeline to run on application startup.
In case if custom code is run after the default Sitecore.ExperienceAnalytics.Api.Pipelines.Initialize.WebApiInitializer processor, the Experience Analytics configuration will be loaded first.
For example:
1) Create the "initialize" pipeline processor
internal class WebApiInitializer
{
public void Process(PipelineArgs args)
{
System.Web.Http.GlobalConfiguration.Configure(Register);
}
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
}
2) Create a config file and place in into the Include/Z.MapRoutes fodler (so it will be loaded last):
<configuration xmlns:x="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<initialize>
<processor type="HttpAttributeRouting.WebApiInitializer, HttpAttributeRouting" x:after="processor[position()=last()]" />
</initialize>
</pipelines>
</sitecore>
</configuration>

How can you call a URL without opening a browser in C/Side?

We are using C/Side on one server to try to call a URL to a PHP script on another server without opening a browser window. We want the script to run as a background process. So far, everything we've tried opens a browser. Any suggestions?
I used HttpClient to do this. But it's not that straight forward in Nav, since you cannot call an asynchronous method and assign its return value to a variable. So I created a wrapper class in c#.
public class NavHttpClient
{
private HttpResponseMessage responseMsg;
public HttpResponseMessage ResponseMsg
{
get { return responseMsg; }
}
public async Task GetAsync(string requestUri)
{
HttpClient client = new HttpClient();
responseMsg = await client.GetAsync(requestUri);
}
}
Then I exported this class in a class library. Now I can use it Nav this way:
NavWebClient : DotNet NavWebClient.NavHttpClient.'NavWebClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
NavWebClient := NavWebClient.NavHttpClient;
NavWebClient.GetAsync('http://localhost').Wait;
IF NavWebClient.ResponseMsg.IsSuccessStatusCode THEN
...
Is this solution suits your needs? Inside there is link to codeunit for Nav 2013 that allows to work with web services in Nav style.

Accessing Session in HttpRequest Pipelines

I'm trying to access a query string parameter and save it to a Session variable. Since the solution I'm working on has several base layouts, the simplest approach would be to add this to a pipeline handler. However, my code is failing because args.Context.Session is null:
public class SaveQueryStringToSession : HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
Assert.ArgumentNotNull((object)args, "args");
string queryString = WebUtil.GetQueryString("parm1");
if (queryString.Length <= 0)
return;
args.Context.Session["parm1"] = queryString;
}
}
This occurs when this method is inserted into either the HttpRequestBegin or HttpRequestEnd pipeline. Curious to know why, and if there is a standard workaround or pattern to use here. (Yes, I will add a null check. No need to point that out.)
I'm running Sitecore Sitecore.NET 6.4.1 (rev. 110720) on IIS 7.5 (Integrated .Net 2.0)
Possibly relevant links:
What is the first global.asax event that Session is accessible assuming the context handler is IRequiresSessionState or IReadOnlySessionState?
http://intothecore.cassidy.dk/2009/02/session-state-and-integrated-pipeline.html
The HttpRequestBegin pipeline is wired up to the HttpApplication.BeginRequest event, and this event is fired before the HttpSession object has been instantiated. Using the HttpRequestEnd pipeline does not work because the HttpSession object has already been disposed by the time the HttpApplication.EndRequest event is fired.
The session becomes available after the PostAcquireRequestState event is fired. To intercept this, create a class that implements IHttpModule, and add it to the <httpModules> element in Web.config. The HttpModule code will need to check if the request requires session state, as attempting to read the session for a static resource request will throw an exception.
Here is HttpModule code that accesses the Session and QueryString:
public class MyHttpModule :IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAcquireRequestState += RequestHandler;
}
public void Dispose()
{
//
}
public void RequestHandler(object sender, EventArgs e)
{
var app = (HttpApplication) sender;
if (app.Context.Handler is IRequiresSessionState)
{
var session = app.Session;
var queryString = app.Request.QueryString["test"];
session["test"] = queryString;
}
}
}
It is worth noting that Sitecore's HttpRequestBegin and HttpRequestEnd pipelines are wired to ASP.NET via an HttpModule:
<add type="Sitecore.Nexus.Web.HttpModule,Sitecore.Nexus"
name="SitecoreHttpModule" />
Thanks to #ddysart for pointing me in the right direction, and to this answer for the correct event to listen for.
Actually instead of httpRequestBegin or HttpRequestEnd you can use httpRequestProcessed during which sitecore process the HttpRequest so you can access the Session.
You will be able to use the same code you have provided earlier.
public class SaveQueryStringToSession : HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
Assert.ArgumentNotNull((object)args, "args");
string queryString = WebUtil.GetQueryString("parm1");
if (queryString.Length <= 0)
return;
args.Context.Session["parm1"] = queryString;
}
}

ASP.NET Universal Providers - Roleprovider does not cache roles in cookie

Ironically my role provider does not cache the roles in a cookie anymore. That was working earlier. Unfortunately i have noticed that only now, so i cannot say what causes the problem. But i think it has to do with the update to the new version 1.2 of the universal providers (released on 16th august).
My config for the roleprovider looks like:
<roleManager enabled="true" cacheRolesInCookie="true" cookieName="X_Roles"
cookiePath="/" cookieProtection="All" cookieRequireSSL="true" cookieSlidingExpiration="true" cookieTimeout="1440"
createPersistentCookie="false" domain="" maxCachedResults="25" defaultProvider="XManager_RoleProvider">
<providers>
<clear/>
<add name="XManager_RoleProvider" type="ManagersX.XManager_RoleProvider, AssemblyX"
connectionStringName="XEntities" applicationName="/" rolesTableName="Roles" roleMembershipsTableName="Users_Roles"/>
</providers>
</roleManager>
Everything is working fine with the rolemanager (loginviews, menu with sitemaptrimming etc.), but it is only not caching the roles anymore. The membership provider, sessionstate etc. are also working fine and the cookies of them are set correctly.
All properties of the static Roles-class are correctly set and everything in Httpcontext (IsSecureConnection etc.) is also correct.
The roles cookie was set earlier, but not anymore. I hope anybody can help me with my problem.
Thanks in advance.
Best Regards,
HeManNew
UPDATE:
Has nobody got the same problem or a tip for me, please?
Below are the details of the Custom Role Provider I wrote that uses proper caching and doesn't hit the database on each page load.
============= My Code-Behind file ===============
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Web.Caching;
using System.Web.Security;
namespace MyProject.Providers
{
public class CustomRoleProvider : RoleProvider
{
#region Properties
private static readonly object LockObject = new object();
private int _cacheTimeoutInMinutes = 0;
#endregion
#region Overrides of RoleProvider
public override void Initialize(string name, NameValueCollection config)
{
// Set Properties
ApplicationName = config["applicationName"];
_cacheTimeoutInMinutes = Convert.ToInt32(config["cacheTimeoutInMinutes"]);
// Call base method
base.Initialize(name, config);
}
/// <summary>
/// Gets a value indicating whether the specified user is in the specified role for the configured applicationName.
/// </summary>
/// <returns>
/// true if the specified user is in the specified role for the configured applicationName; otherwise, false.
/// </returns>
/// <param name="username">The user name to search for.</param><param name="roleName">The role to search in.</param>
public override bool IsUserInRole(string username, string roleName)
{
// Get Roles
var userRoles = GetRolesForUser(username);
// Return if exists
return userRoles.Contains(roleName);
}
/// <summary>
/// Gets a list of the roles that a specified user is in for the configured applicationName.
/// </summary>
/// <returns>
/// A string array containing the names of all the roles that the specified user is in for the configured applicationName.
/// </returns>
/// <param name="username">The user to return a list of roles for.</param>
public override string[] GetRolesForUser(string username)
{
// Return if User is not authenticated
if (!HttpContext.Current.User.Identity.IsAuthenticated) return null;
// Return if present in Cache
var cacheKey = string.format("UserRoles_{0}", username);
if (HttpRuntime.Cache[cacheKey] != null) return (string[]) HttpRuntime.Cache[cacheKey];
// Vars
var userRoles = new List<string>();
var sqlParams = new List<SqlParameter>
{
new SqlParameter("#ApplicationName", ApplicationName),
new SqlParameter("#UserName", username)
};
lock (LockObject)
{
// Run Stored Proc << Replace this block with your own Database Call Methods >>
using (IDataReader dr =
BaseDatabase.ExecuteDataReader("aspnet_UsersInRoles_GetRolesForUser", sqlParams.ToArray(),
Constants.DatabaseConnectionName) as SqlDataReader)
{
while (dr.Read())
{
userRoles.Add(dr["RoleName"].ToString());
}
}
}
// Store in Cache and expire after set minutes
HttpRuntime.Cache.Insert(cacheKey, userRoles.ToArray(), null,
DateTime.Now.AddMinutes(_cacheTimeoutInMinutes), Cache.NoSlidingExpiration);
// Return
return userRoles.ToArray();
}
/// <summary>
/// Gets or sets the name of the application to store and retrieve role information for.
/// </summary>
/// <returns>
/// The name of the application to store and retrieve role information for.
/// </returns>
public override sealed string ApplicationName { get; set; }
// I skipped the other methods as they do not apply to this scenario
#endregion
}
}
============= End of My Code-Behind file ===============
============= My Web.Config file =======================
<roleManager enabled="true" defaultProvider="CustomRoleManager">
<providers>
<clear />
<add name="SqlRoleManager" type="System.Web.Security.SqlRoleProvider" connectionStringName="AspnetDbConnection" applicationName="MyApplication"/>
<add name="CustomRoleManager" type="MyProject.Providers.CustomRoleProvider" connectionStringName="AspnetDbConnection" applicationName="MyApplication" cacheTimeoutInMinutes="30" />
</providers>
</roleManager>
============= End of My Web.Config file ================
The cache is set to expire automatically after every 30 minutes. You can modify this as you deem fit.
Cheers.
I was having the same issue, but I was able to find a MS KB article that seems to have fixed it. I installed the patch and the cookie reappeared.
http://support.microsoft.com/kb/2750147
See the section: ASP.Net Issue 4.
Hopefully that helps someone else!